174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/*
274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  PS3 virtual uart
374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  Copyright (C) 2006 Sony Computer Entertainment Inc.
574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  Copyright 2006 Sony Corp.
674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  This program is free software; you can redistribute it and/or modify
874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  it under the terms of the GNU General Public License as published by
974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  the Free Software Foundation; version 2 of the License.
1074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
1174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  This program is distributed in the hope that it will be useful,
1274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  GNU General Public License for more details.
1574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
1674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  You should have received a copy of the GNU General Public License
1774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  along with this program; if not, write to the Free Software
1874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
2074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand#include <linux/kernel.h>
225a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
2374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand#include <linux/module.h>
2474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand#include <linux/interrupt.h>
25ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand#include <linux/workqueue.h>
261977f032722c27ee3730284582fd3991ad9ac81bJiri Slaby#include <linux/bitops.h>
2774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand#include <asm/ps3.h>
2874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2975c86e7422751c5be3caaf448d802839ec685725Geoff Levand#include <asm/firmware.h>
3074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand#include <asm/lv1call.h>
3174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand#include "vuart.h"
3374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff LevandMODULE_AUTHOR("Sony Corporation");
3574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff LevandMODULE_LICENSE("GPL v2");
3675c86e7422751c5be3caaf448d802839ec685725Geoff LevandMODULE_DESCRIPTION("PS3 vuart");
3774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
3974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * vuart - An inter-partition data link service.
4074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  port 0: PS3 AV Settings.
4174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *  port 2: PS3 System Manager.
4274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
4374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * The vuart provides a bi-directional byte stream data link between logical
4474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * partitions.  Its primary role is as a communications link between the guest
4574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * OS and the system policy module.  The current HV does not support any
4674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * connections other than those listed.
4774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
4874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
4974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandenum {PORT_COUNT = 3,};
5074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
5174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandenum vuart_param {
5274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	PARAM_TX_TRIGGER = 0,
5374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	PARAM_RX_TRIGGER = 1,
5474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	PARAM_INTERRUPT_MASK = 2,
5574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	PARAM_RX_BUF_SIZE = 3, /* read only */
5674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	PARAM_RX_BYTES = 4, /* read only */
5774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	PARAM_TX_BUF_SIZE = 5, /* read only */
5874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	PARAM_TX_BYTES = 6, /* read only */
5974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	PARAM_INTERRUPT_STATUS = 7, /* read only */
6074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand};
6174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
6274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandenum vuart_interrupt_bit {
6374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	INTERRUPT_BIT_TX = 0,
6474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	INTERRUPT_BIT_RX = 1,
6574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	INTERRUPT_BIT_DISCONNECT = 2,
6674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand};
6774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
6874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandenum vuart_interrupt_mask {
6974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	INTERRUPT_MASK_TX = 1,
7074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	INTERRUPT_MASK_RX = 2,
7174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	INTERRUPT_MASK_DISCONNECT = 4,
7274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand};
7374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
7474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
757626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * struct ps3_vuart_port_priv - private vuart device data.
767626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand */
777626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
787626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstruct ps3_vuart_port_priv {
797626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	u64 interrupt_mask;
807626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
817626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct {
827626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		spinlock_t lock;
837626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		struct list_head head;
847626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	} tx_list;
857626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct {
867626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		struct ps3_vuart_work work;
877626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		unsigned long bytes_held;
887626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		spinlock_t lock;
897626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		struct list_head head;
907626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	} rx_list;
917626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_stats stats;
927626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand};
937626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
947626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic struct ps3_vuart_port_priv *to_port_priv(
957626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_system_bus_device *dev)
967626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand{
977626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(!dev);
987626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(!dev->driver_priv);
997626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return (struct ps3_vuart_port_priv *)dev->driver_priv;
1007626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand}
1017626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
1027626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand/**
10374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * struct ports_bmp - bitmap indicating ports needing service.
10474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
10574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * A 256 bit read only bitmap indicating ports needing service.  Do not write
10674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * to these bits.  Must not cross a page boundary.
10774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
10874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
10974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandstruct ports_bmp {
11074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	u64 status;
11174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	u64 unused[3];
112d0e5c2185e1c6508894e1ee1a8e9bf79e009e427Geert Uytterhoeven} __attribute__((aligned(32)));
11374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
11474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand#define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__)
115848cfdc5c1cd2163ba0c9a6490d9adcb7a7c3518Geoff Levandstatic void __maybe_unused _dump_ports_bmp(
116d0e5c2185e1c6508894e1ee1a8e9bf79e009e427Geert Uytterhoeven	const struct ports_bmp *bmp, const char *func, int line)
11774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
118a9dad6e598155e2a548142336cd833e5360335d1Stephen Rothwell	pr_debug("%s:%d: ports_bmp: %016llxh\n", func, line, bmp->status);
11974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
12074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
12174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand#define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__)
122848cfdc5c1cd2163ba0c9a6490d9adcb7a7c3518Geoff Levandstatic void __maybe_unused _dump_port_params(unsigned int port_number,
123d0e5c2185e1c6508894e1ee1a8e9bf79e009e427Geert Uytterhoeven	const char *func, int line)
12474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
12574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand#if defined(DEBUG)
12674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	static const char *strings[] = {
12774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		"tx_trigger      ",
12874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		"rx_trigger      ",
12974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		"interrupt_mask  ",
13074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		"rx_buf_size     ",
13174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		"rx_bytes        ",
13274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		"tx_buf_size     ",
13374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		"tx_bytes        ",
13474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		"interrupt_status",
13574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	};
13674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result;
13774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned int i;
13874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	u64 value;
13974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
14074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	for (i = 0; i < ARRAY_SIZE(strings); i++) {
14174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		result = lv1_get_virtual_uart_param(port_number, i, &value);
14274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
14374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		if (result) {
14474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			pr_debug("%s:%d: port_%u: %s failed: %s\n", func, line,
14574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand				port_number, strings[i], ps3_result(result));
14674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			continue;
14774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		}
14874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		pr_debug("%s:%d: port_%u: %s = %lxh\n",
14974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			func, line, port_number, strings[i], value);
15074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
15174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand#endif
15274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
15374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
15474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandstruct vuart_triggers {
15574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long rx;
15674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long tx;
15774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand};
15874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
1597626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandint ps3_vuart_get_triggers(struct ps3_system_bus_device *dev,
16074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	struct vuart_triggers *trig)
16174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
16274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result;
163b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 size;
164b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 val;
165b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 tx;
16674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
1677626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = lv1_get_virtual_uart_param(dev->port_number,
168b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell		PARAM_TX_TRIGGER, &tx);
169b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	trig->tx = tx;
17074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
17174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result) {
17274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n",
17374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__func__, __LINE__, ps3_result(result));
17474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		return result;
17574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
17674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
1777626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = lv1_get_virtual_uart_param(dev->port_number,
17874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		PARAM_RX_BUF_SIZE, &size);
17974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
18074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result) {
18174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n",
18274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__func__, __LINE__, ps3_result(result));
18374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		return result;
18474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
18574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
1867626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = lv1_get_virtual_uart_param(dev->port_number,
18774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		PARAM_RX_TRIGGER, &val);
18874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
18974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result) {
19074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n",
19174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__func__, __LINE__, ps3_result(result));
19274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		return result;
19374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
19474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
19574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	trig->rx = size - val;
19674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
19774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d: tx %lxh, rx %lxh\n", __func__, __LINE__,
19874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		trig->tx, trig->rx);
19974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
20074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
20174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
20274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2037626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandint ps3_vuart_set_triggers(struct ps3_system_bus_device *dev, unsigned int tx,
20474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned int rx)
20574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
20674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result;
207b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	u64 size;
20874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2097626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = lv1_set_virtual_uart_param(dev->port_number,
21074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		PARAM_TX_TRIGGER, tx);
21174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
21274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result) {
21374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n",
21474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__func__, __LINE__, ps3_result(result));
21574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		return result;
21674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
21774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2187626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = lv1_get_virtual_uart_param(dev->port_number,
21974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		PARAM_RX_BUF_SIZE, &size);
22074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
22174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result) {
22274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n",
22374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__func__, __LINE__, ps3_result(result));
22474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		return result;
22574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
22674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2277626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = lv1_set_virtual_uart_param(dev->port_number,
22874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		PARAM_RX_TRIGGER, size - rx);
22974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
23074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result) {
23174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n",
23274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__func__, __LINE__, ps3_result(result));
23374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		return result;
23474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
23574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
23674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d: tx %xh, rx %xh\n", __func__, __LINE__,
23774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		tx, rx);
23874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
23974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
24074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
24174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2427626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_get_rx_bytes_waiting(struct ps3_system_bus_device *dev,
24375c86e7422751c5be3caaf448d802839ec685725Geoff Levand	u64 *bytes_waiting)
24474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
2457626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	int result;
2467626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
2477626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = lv1_get_virtual_uart_param(dev->port_number,
24874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		PARAM_RX_BYTES, bytes_waiting);
24974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
25074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result)
25174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: rx_bytes failed: %s\n",
25274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__func__, __LINE__, ps3_result(result));
25374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
254a9dad6e598155e2a548142336cd833e5360335d1Stephen Rothwell	dev_dbg(&dev->core, "%s:%d: %llxh\n", __func__, __LINE__,
25574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		*bytes_waiting);
25674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
25774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
25874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2597626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand/**
2607626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * ps3_vuart_set_interrupt_mask - Enable/disable the port interrupt sources.
2617626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * @dev: The struct ps3_system_bus_device instance.
2627626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * @bmp: Logical OR of enum vuart_interrupt_mask values. A zero bit disables.
2637626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand */
2647626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
2657626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_set_interrupt_mask(struct ps3_system_bus_device *dev,
26674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long mask)
26774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
26874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result;
2697626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
27074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
27174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, mask);
27274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2737626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	priv->interrupt_mask = mask;
27474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2757626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = lv1_set_virtual_uart_param(dev->port_number,
2767626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		PARAM_INTERRUPT_MASK, priv->interrupt_mask);
27774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
27874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result)
27974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: interrupt_mask failed: %s\n",
28074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__func__, __LINE__, ps3_result(result));
28174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
28274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
28374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
28474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2857626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_get_interrupt_status(struct ps3_system_bus_device *dev,
28674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long *status)
28774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
2887626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	int result;
2897626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
29075c86e7422751c5be3caaf448d802839ec685725Geoff Levand	u64 tmp;
2917626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
2927626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = lv1_get_virtual_uart_param(dev->port_number,
29375c86e7422751c5be3caaf448d802839ec685725Geoff Levand		PARAM_INTERRUPT_STATUS, &tmp);
29474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
29574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result)
29674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: interrupt_status failed: %s\n",
29774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__func__, __LINE__, ps3_result(result));
29874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
2997626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	*status = tmp & priv->interrupt_mask;
30075c86e7422751c5be3caaf448d802839ec685725Geoff Levand
301a9dad6e598155e2a548142336cd833e5360335d1Stephen Rothwell	dev_dbg(&dev->core, "%s:%d: m %llxh, s %llxh, m&s %lxh\n",
3027626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		__func__, __LINE__, priv->interrupt_mask, tmp, *status);
30374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
30474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
30574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
30674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3077626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandint ps3_vuart_enable_interrupt_tx(struct ps3_system_bus_device *dev)
30874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
3097626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
3107626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
3117626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return (priv->interrupt_mask & INTERRUPT_MASK_TX) ? 0
3127626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		: ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
31374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		| INTERRUPT_MASK_TX);
31474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
31574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3167626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandint ps3_vuart_enable_interrupt_rx(struct ps3_system_bus_device *dev)
31774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
3187626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
3197626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
3207626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return (priv->interrupt_mask & INTERRUPT_MASK_RX) ? 0
3217626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		: ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
32274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		| INTERRUPT_MASK_RX);
32374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
32474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3257626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandint ps3_vuart_enable_interrupt_disconnect(struct ps3_system_bus_device *dev)
32674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
3277626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
3287626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
3297626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0
3307626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		: ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
33174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		| INTERRUPT_MASK_DISCONNECT);
33274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
33374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3347626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandint ps3_vuart_disable_interrupt_tx(struct ps3_system_bus_device *dev)
33574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
3367626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
3377626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
3387626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return (priv->interrupt_mask & INTERRUPT_MASK_TX)
3397626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
34074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		& ~INTERRUPT_MASK_TX) : 0;
34174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
34274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3437626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandint ps3_vuart_disable_interrupt_rx(struct ps3_system_bus_device *dev)
34474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
3457626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
3467626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
3477626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return (priv->interrupt_mask & INTERRUPT_MASK_RX)
3487626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
34974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		& ~INTERRUPT_MASK_RX) : 0;
35074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
35174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3527626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandint ps3_vuart_disable_interrupt_disconnect(struct ps3_system_bus_device *dev)
35374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
3547626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
3557626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
3567626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT)
3577626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask
35874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		& ~INTERRUPT_MASK_DISCONNECT) : 0;
35974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
36074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
36174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
36274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * ps3_vuart_raw_write - Low level write helper.
3637626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * @dev: The struct ps3_system_bus_device instance.
36474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
36574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * Do not call ps3_vuart_raw_write directly, use ps3_vuart_write.
36674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
36774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3687626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_raw_write(struct ps3_system_bus_device *dev,
369b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	const void *buf, unsigned int bytes, u64 *bytes_written)
37074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
37174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result;
3727626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
37374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3747626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = lv1_write_virtual_uart(dev->port_number,
37574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_written);
37674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
37774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result) {
37874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: lv1_write_virtual_uart failed: "
37974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			"%s\n", __func__, __LINE__, ps3_result(result));
38074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		return result;
38174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
38274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3837626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	priv->stats.bytes_written += *bytes_written;
38474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
385b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	dev_dbg(&dev->core, "%s:%d: wrote %llxh/%xh=>%lxh\n", __func__, __LINE__,
3867626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		*bytes_written, bytes, priv->stats.bytes_written);
38774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
38874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
38974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
39074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
39174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
39274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * ps3_vuart_raw_read - Low level read helper.
3937626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * @dev: The struct ps3_system_bus_device instance.
39474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
39574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * Do not call ps3_vuart_raw_read directly, use ps3_vuart_read.
39674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
39774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
3987626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_raw_read(struct ps3_system_bus_device *dev, void *buf,
399b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	unsigned int bytes, u64 *bytes_read)
40074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
40174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result;
4027626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
40374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
40474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes);
40574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
4067626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = lv1_read_virtual_uart(dev->port_number,
40774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_read);
40874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
40974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result) {
41074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: lv1_read_virtual_uart failed: %s\n",
41174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__func__, __LINE__, ps3_result(result));
41274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		return result;
41374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
41474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
4157626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	priv->stats.bytes_read += *bytes_read;
41674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
417b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell	dev_dbg(&dev->core, "%s:%d: read %llxh/%xh=>%lxh\n", __func__, __LINE__,
4187626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		*bytes_read, bytes, priv->stats.bytes_read);
41974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
42074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
42174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
42274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
42374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
42475c86e7422751c5be3caaf448d802839ec685725Geoff Levand * ps3_vuart_clear_rx_bytes - Discard bytes received.
4257626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * @dev: The struct ps3_system_bus_device instance.
42675c86e7422751c5be3caaf448d802839ec685725Geoff Levand * @bytes: Max byte count to discard, zero = all pending.
42775c86e7422751c5be3caaf448d802839ec685725Geoff Levand *
42875c86e7422751c5be3caaf448d802839ec685725Geoff Levand * Used to clear pending rx interrupt source.  Will not block.
42975c86e7422751c5be3caaf448d802839ec685725Geoff Levand */
43075c86e7422751c5be3caaf448d802839ec685725Geoff Levand
4317626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandvoid ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev,
43275c86e7422751c5be3caaf448d802839ec685725Geoff Levand	unsigned int bytes)
43375c86e7422751c5be3caaf448d802839ec685725Geoff Levand{
43475c86e7422751c5be3caaf448d802839ec685725Geoff Levand	int result;
4357626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
43675c86e7422751c5be3caaf448d802839ec685725Geoff Levand	u64 bytes_waiting;
437d0e5c2185e1c6508894e1ee1a8e9bf79e009e427Geert Uytterhoeven	void *tmp;
43875c86e7422751c5be3caaf448d802839ec685725Geoff Levand
43975c86e7422751c5be3caaf448d802839ec685725Geoff Levand	result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes_waiting);
44075c86e7422751c5be3caaf448d802839ec685725Geoff Levand
44175c86e7422751c5be3caaf448d802839ec685725Geoff Levand	BUG_ON(result);
44275c86e7422751c5be3caaf448d802839ec685725Geoff Levand
44375c86e7422751c5be3caaf448d802839ec685725Geoff Levand	bytes = bytes ? min(bytes, (unsigned int)bytes_waiting) : bytes_waiting;
44475c86e7422751c5be3caaf448d802839ec685725Geoff Levand
44575c86e7422751c5be3caaf448d802839ec685725Geoff Levand	dev_dbg(&dev->core, "%s:%d: %u\n", __func__, __LINE__, bytes);
44675c86e7422751c5be3caaf448d802839ec685725Geoff Levand
44775c86e7422751c5be3caaf448d802839ec685725Geoff Levand	if (!bytes)
44875c86e7422751c5be3caaf448d802839ec685725Geoff Levand		return;
44975c86e7422751c5be3caaf448d802839ec685725Geoff Levand
45075c86e7422751c5be3caaf448d802839ec685725Geoff Levand	/* Add some extra space for recently arrived data. */
45175c86e7422751c5be3caaf448d802839ec685725Geoff Levand
45275c86e7422751c5be3caaf448d802839ec685725Geoff Levand	bytes += 128;
45375c86e7422751c5be3caaf448d802839ec685725Geoff Levand
45475c86e7422751c5be3caaf448d802839ec685725Geoff Levand	tmp = kmalloc(bytes, GFP_KERNEL);
45575c86e7422751c5be3caaf448d802839ec685725Geoff Levand
45675c86e7422751c5be3caaf448d802839ec685725Geoff Levand	if (!tmp)
45775c86e7422751c5be3caaf448d802839ec685725Geoff Levand		return;
45875c86e7422751c5be3caaf448d802839ec685725Geoff Levand
45975c86e7422751c5be3caaf448d802839ec685725Geoff Levand	ps3_vuart_raw_read(dev, tmp, bytes, &bytes_waiting);
46075c86e7422751c5be3caaf448d802839ec685725Geoff Levand
46175c86e7422751c5be3caaf448d802839ec685725Geoff Levand	kfree(tmp);
46275c86e7422751c5be3caaf448d802839ec685725Geoff Levand
46375c86e7422751c5be3caaf448d802839ec685725Geoff Levand	/* Don't include these bytes in the stats. */
46475c86e7422751c5be3caaf448d802839ec685725Geoff Levand
4657626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	priv->stats.bytes_read -= bytes_waiting;
46675c86e7422751c5be3caaf448d802839ec685725Geoff Levand}
4677626e78d29651d3075e88f233c0632867ea6a35cGeoff LevandEXPORT_SYMBOL_GPL(ps3_vuart_clear_rx_bytes);
46875c86e7422751c5be3caaf448d802839ec685725Geoff Levand
46975c86e7422751c5be3caaf448d802839ec685725Geoff Levand/**
47074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * struct list_buffer - An element for a port device fifo buffer list.
47174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
47274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
47374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandstruct list_buffer {
47474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	struct list_head link;
47574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	const unsigned char *head;
47674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	const unsigned char *tail;
47774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long dbg_number;
47874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned char data[];
47974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand};
48074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
48174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
48274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * ps3_vuart_write - the entry point for writing data to a port
4837626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * @dev: The struct ps3_system_bus_device instance.
48474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
48574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * If the port is idle on entry as much of the incoming data is written to
48674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * the port as the port will accept.  Otherwise a list buffer is created
48774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * and any remaning incoming data is copied to that buffer.  The buffer is
48874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * then enqueued for transmision via the transmit interrupt.
48974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
49074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
4917626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandint ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf,
49274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned int bytes)
49374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
49474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	static unsigned long dbg_number;
49574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result;
4967626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
49774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long flags;
49874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	struct list_buffer *lb;
49974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
50074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__,
50174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		bytes, bytes);
50274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
5037626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_lock_irqsave(&priv->tx_list.lock, flags);
50474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
5057626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (list_empty(&priv->tx_list.head)) {
506b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell		u64 bytes_written;
50774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
50874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written);
50974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
5107626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		spin_unlock_irqrestore(&priv->tx_list.lock, flags);
51174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
51274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		if (result) {
51374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			dev_dbg(&dev->core,
51474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand				"%s:%d: ps3_vuart_raw_write failed\n",
51574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand				__func__, __LINE__);
51674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			return result;
51774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		}
51874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
51974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		if (bytes_written == bytes) {
52074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			dev_dbg(&dev->core, "%s:%d: wrote %xh bytes\n",
52174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand				__func__, __LINE__, bytes);
52274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			return 0;
52374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		}
52474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
52574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		bytes -= bytes_written;
52674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		buf += bytes_written;
52774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	} else
5287626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		spin_unlock_irqrestore(&priv->tx_list.lock, flags);
52974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
53074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL);
53174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
532d0e5c2185e1c6508894e1ee1a8e9bf79e009e427Geert Uytterhoeven	if (!lb)
53374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		return -ENOMEM;
53474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
53574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	memcpy(lb->data, buf, bytes);
53674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	lb->head = lb->data;
53774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	lb->tail = lb->data + bytes;
53874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	lb->dbg_number = ++dbg_number;
53974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
5407626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_lock_irqsave(&priv->tx_list.lock, flags);
5417626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	list_add_tail(&lb->link, &priv->tx_list.head);
54274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	ps3_vuart_enable_interrupt_tx(dev);
5437626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_unlock_irqrestore(&priv->tx_list.lock, flags);
54474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
54574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n",
54674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		__func__, __LINE__, lb->dbg_number, bytes);
54774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
54874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return 0;
54974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
5507626e78d29651d3075e88f233c0632867ea6a35cGeoff LevandEXPORT_SYMBOL_GPL(ps3_vuart_write);
5517626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5527626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand/**
5537626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * ps3_vuart_queue_rx_bytes - Queue waiting bytes into the buffer list.
5547626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * @dev: The struct ps3_system_bus_device instance.
5557626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * @bytes_queued: Number of bytes queued to the buffer list.
5567626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand *
5577626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * Must be called with priv->rx_list.lock held.
5587626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand */
5597626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5607626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_queue_rx_bytes(struct ps3_system_bus_device *dev,
5617626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	u64 *bytes_queued)
5627626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand{
5637626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	static unsigned long dbg_number;
5647626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	int result;
5657626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
5667626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct list_buffer *lb;
5677626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	u64 bytes;
5687626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5697626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	*bytes_queued = 0;
5707626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5717626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes);
5727626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(result);
5737626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5747626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (result)
5757626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		return -EIO;
5767626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5777626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (!bytes)
5787626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		return 0;
5797626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5807626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	/* Add some extra space for recently arrived data. */
5817626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5827626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	bytes += 128;
5837626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5847626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC);
5857626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5867626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (!lb)
5877626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		return -ENOMEM;
5887626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5897626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	ps3_vuart_raw_read(dev, lb->data, bytes, &bytes);
5907626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5917626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	lb->head = lb->data;
5927626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	lb->tail = lb->data + bytes;
5937626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	lb->dbg_number = ++dbg_number;
5947626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
5957626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	list_add_tail(&lb->link, &priv->rx_list.head);
5967626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	priv->rx_list.bytes_held += bytes;
5977626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
598a9dad6e598155e2a548142336cd833e5360335d1Stephen Rothwell	dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %llxh bytes\n",
5997626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		__func__, __LINE__, lb->dbg_number, bytes);
6007626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
6017626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	*bytes_queued = bytes;
6027626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
6037626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return 0;
6047626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand}
60574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
60674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
6077626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * ps3_vuart_read - The entry point for reading data from a port.
60874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
6097626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * Queue data waiting at the port, and if enough bytes to satisfy the request
6107626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * are held in the buffer list those bytes are dequeued and copied to the
6117626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * caller's buffer.  Emptied list buffers are retiered.  If the request cannot
6127626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * be statified by bytes held in the list buffers -EAGAIN is returned.
61374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
61474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
6157626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandint ps3_vuart_read(struct ps3_system_bus_device *dev, void *buf,
61674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned int bytes)
61774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
6187626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	int result;
6197626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
62074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long flags;
62174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	struct list_buffer *lb, *n;
62274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long bytes_read;
62374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
62474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__,
62574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		bytes, bytes);
62674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
6277626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_lock_irqsave(&priv->rx_list.lock, flags);
62874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
6297626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	/* Queue rx bytes here for polled reads. */
6307626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
6317626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	while (priv->rx_list.bytes_held < bytes) {
6327626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		u64 tmp;
6337626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
6347626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		result = ps3_vuart_queue_rx_bytes(dev, &tmp);
6357626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		if (result || !tmp) {
6367626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n",
6377626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand				__func__, __LINE__,
6387626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand				bytes - priv->rx_list.bytes_held);
6397626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			spin_unlock_irqrestore(&priv->rx_list.lock, flags);
6407626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			return -EAGAIN;
6417626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		}
64274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
64374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
6447626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	list_for_each_entry_safe(lb, n, &priv->rx_list.head, link) {
64574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		bytes_read = min((unsigned int)(lb->tail - lb->head), bytes);
64674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
64774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		memcpy(buf, lb->head, bytes_read);
64874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		buf += bytes_read;
64974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		bytes -= bytes_read;
6507626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		priv->rx_list.bytes_held -= bytes_read;
65174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
65274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		if (bytes_read < lb->tail - lb->head) {
65374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			lb->head += bytes_read;
65475c86e7422751c5be3caaf448d802839ec685725Geoff Levand			dev_dbg(&dev->core, "%s:%d: buf_%lu: dequeued %lxh "
65575c86e7422751c5be3caaf448d802839ec685725Geoff Levand				"bytes\n", __func__, __LINE__, lb->dbg_number,
65675c86e7422751c5be3caaf448d802839ec685725Geoff Levand				bytes_read);
6577626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			spin_unlock_irqrestore(&priv->rx_list.lock, flags);
65874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			return 0;
65974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		}
66074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
66175c86e7422751c5be3caaf448d802839ec685725Geoff Levand		dev_dbg(&dev->core, "%s:%d: buf_%lu: free, dequeued %lxh "
66275c86e7422751c5be3caaf448d802839ec685725Geoff Levand			"bytes\n", __func__, __LINE__, lb->dbg_number,
66375c86e7422751c5be3caaf448d802839ec685725Geoff Levand			bytes_read);
66474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
66574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		list_del(&lb->link);
66674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		kfree(lb);
66774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
66874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
6697626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_unlock_irqrestore(&priv->rx_list.lock, flags);
67074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return 0;
67174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
6727626e78d29651d3075e88f233c0632867ea6a35cGeoff LevandEXPORT_SYMBOL_GPL(ps3_vuart_read);
67374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
6747626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand/**
6757626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * ps3_vuart_work - Asynchronous read handler.
6767626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand */
6777626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
6787626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic void ps3_vuart_work(struct work_struct *work)
6797626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand{
6807626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_system_bus_device *dev =
6817626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		ps3_vuart_work_to_system_bus_dev(work);
6827626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_driver *drv =
6837626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		ps3_system_bus_dev_to_vuart_drv(dev);
6847626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
6857626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(!drv);
6867626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	drv->work(dev);
6877626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand}
6887626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
6897626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandint ps3_vuart_read_async(struct ps3_system_bus_device *dev, unsigned int bytes)
690ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand{
6917626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
692ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand	unsigned long flags;
693ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand
6947626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (priv->rx_list.work.trigger) {
695ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand		dev_dbg(&dev->core, "%s:%d: warning, multiple calls\n",
696ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand			__func__, __LINE__);
697ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand		return -EAGAIN;
698ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand	}
699ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand
700ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand	BUG_ON(!bytes);
701ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand
7027626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	PREPARE_WORK(&priv->rx_list.work.work, ps3_vuart_work);
703ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand
7047626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_lock_irqsave(&priv->rx_list.lock, flags);
7057626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (priv->rx_list.bytes_held >= bytes) {
706ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand		dev_dbg(&dev->core, "%s:%d: schedule_work %xh bytes\n",
707ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand			__func__, __LINE__, bytes);
7087626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		schedule_work(&priv->rx_list.work.work);
7097626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		spin_unlock_irqrestore(&priv->rx_list.lock, flags);
710ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand		return 0;
711ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand	}
712ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand
7137626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	priv->rx_list.work.trigger = bytes;
7147626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_unlock_irqrestore(&priv->rx_list.lock, flags);
715ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand
716ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand	dev_dbg(&dev->core, "%s:%d: waiting for %u(%xh) bytes\n", __func__,
717ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand		__LINE__, bytes, bytes);
718ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand
719ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand	return 0;
720ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand}
7217626e78d29651d3075e88f233c0632867ea6a35cGeoff LevandEXPORT_SYMBOL_GPL(ps3_vuart_read_async);
722ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand
7237626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandvoid ps3_vuart_cancel_async(struct ps3_system_bus_device *dev)
724ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand{
7257626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	to_port_priv(dev)->rx_list.work.trigger = 0;
726ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand}
7277626e78d29651d3075e88f233c0632867ea6a35cGeoff LevandEXPORT_SYMBOL_GPL(ps3_vuart_cancel_async);
728ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand
72974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
73074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler
73174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
73274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * Services the transmit interrupt for the port.  Writes as much data from the
73374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * buffer list as the port will accept.  Retires any emptied list buffers and
73474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * adjusts the final list buffer state for a partial write.
73574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
73674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
7377626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_handle_interrupt_tx(struct ps3_system_bus_device *dev)
73874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
73974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result = 0;
7407626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
74174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long flags;
74274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	struct list_buffer *lb, *n;
74374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long bytes_total = 0;
74474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
74574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
74674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
7477626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_lock_irqsave(&priv->tx_list.lock, flags);
74874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
7497626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	list_for_each_entry_safe(lb, n, &priv->tx_list.head, link) {
75074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
751b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell		u64 bytes_written;
75274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
75374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		result = ps3_vuart_raw_write(dev, lb->head, lb->tail - lb->head,
75474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			&bytes_written);
75574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
75674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		if (result) {
75774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			dev_dbg(&dev->core,
75874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand				"%s:%d: ps3_vuart_raw_write failed\n",
75974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand				__func__, __LINE__);
76074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			break;
76174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		}
76274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
76374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		bytes_total += bytes_written;
76474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
76574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		if (bytes_written < lb->tail - lb->head) {
76674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			lb->head += bytes_written;
76774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			dev_dbg(&dev->core,
768b17b3df161814c43c03dbc8dbf8d32741bb30ba4Stephen Rothwell				"%s:%d cleared buf_%lu, %llxh bytes\n",
76974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand				__func__, __LINE__, lb->dbg_number,
77074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand				bytes_written);
77174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			goto port_full;
77274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		}
77374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
77474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d free buf_%lu\n", __func__, __LINE__,
77574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			lb->dbg_number);
77674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
77774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		list_del(&lb->link);
77874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		kfree(lb);
77974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
78074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
78174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	ps3_vuart_disable_interrupt_tx(dev);
78274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandport_full:
7837626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_unlock_irqrestore(&priv->tx_list.lock, flags);
78474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n",
78574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		__func__, __LINE__, bytes_total);
78674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
78774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
78874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
78974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
79074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * ps3_vuart_handle_interrupt_rx - third stage receive interrupt handler
79174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
79274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * Services the receive interrupt for the port.  Creates a list buffer and
79374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * copies all waiting port data to that buffer and enqueues the buffer in the
79474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * buffer list.  Buffer list data is dequeued via ps3_vuart_read.
79574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
79674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
7977626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_handle_interrupt_rx(struct ps3_system_bus_device *dev)
79874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
7997626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	int result;
8007626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
80174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long flags;
8027626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	u64 bytes;
80374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
80474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
80574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
8067626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_lock_irqsave(&priv->rx_list.lock, flags);
8077626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = ps3_vuart_queue_rx_bytes(dev, &bytes);
80874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
8097626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (result) {
8107626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		spin_unlock_irqrestore(&priv->rx_list.lock, flags);
8117626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		return result;
8127626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	}
81374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
8147626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (priv->rx_list.work.trigger && priv->rx_list.bytes_held
8157626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		>= priv->rx_list.work.trigger) {
816ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand		dev_dbg(&dev->core, "%s:%d: schedule_work %lxh bytes\n",
8177626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			__func__, __LINE__, priv->rx_list.work.trigger);
8187626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		priv->rx_list.work.trigger = 0;
8197626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		schedule_work(&priv->rx_list.work.work);
820ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand	}
8217626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
8227626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_unlock_irqrestore(&priv->rx_list.lock, flags);
8237626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return result;
82474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
82574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
82674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandstatic int ps3_vuart_handle_interrupt_disconnect(
8277626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_system_bus_device *dev)
82874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
82974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
83074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	BUG_ON("no support");
83174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return -1;
83274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
83374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
83474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
83574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * ps3_vuart_handle_port_interrupt - second stage interrupt handler
83674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
83774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * Services any pending interrupt types for the port.  Passes control to the
83874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * third stage type specific interrupt handler.  Returns control to the first
83974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * stage handler after one iteration.
84074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
84174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
8427626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_handle_port_interrupt(struct ps3_system_bus_device *dev)
84374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
84474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result;
8457626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
84674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned long status;
84774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
84875c86e7422751c5be3caaf448d802839ec685725Geoff Levand	result = ps3_vuart_get_interrupt_status(dev, &status);
84974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
85074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result)
85174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		return result;
85274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
85374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d: status: %lxh\n", __func__, __LINE__,
85474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		status);
85574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
85674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (status & INTERRUPT_MASK_DISCONNECT) {
8577626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		priv->stats.disconnect_interrupts++;
85874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		result = ps3_vuart_handle_interrupt_disconnect(dev);
85974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		if (result)
86074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			ps3_vuart_disable_interrupt_disconnect(dev);
86174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
86274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
86374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (status & INTERRUPT_MASK_TX) {
8647626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		priv->stats.tx_interrupts++;
86574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		result = ps3_vuart_handle_interrupt_tx(dev);
86674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		if (result)
86774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			ps3_vuart_disable_interrupt_tx(dev);
86874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
86974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
87074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (status & INTERRUPT_MASK_RX) {
8717626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		priv->stats.rx_interrupts++;
87274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		result = ps3_vuart_handle_interrupt_rx(dev);
87374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		if (result)
87474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			ps3_vuart_disable_interrupt_rx(dev);
87574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
87674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
87774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return 0;
87874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
87974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
88075c86e7422751c5be3caaf448d802839ec685725Geoff Levandstruct vuart_bus_priv {
8817626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ports_bmp *bmp;
88274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	unsigned int virq;
883c4e6752dd4f249d6797146d655973bc0bde4c26eGeoff Levand	struct mutex probe_mutex;
88475c86e7422751c5be3caaf448d802839ec685725Geoff Levand	int use_count;
8857626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_system_bus_device *devices[PORT_COUNT];
88675c86e7422751c5be3caaf448d802839ec685725Geoff Levand} static vuart_bus_priv;
88774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
88874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
88974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * ps3_vuart_irq_handler - first stage interrupt handler
89074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
89174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * Loops finding any interrupting port and its associated instance data.
89274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * Passes control to the second stage port specific interrupt handler.  Loops
89374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * until all outstanding interrupts are serviced.
89474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
89574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
89674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandstatic irqreturn_t ps3_vuart_irq_handler(int irq, void *_private)
89774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
8987626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct vuart_bus_priv *bus_priv = _private;
89974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
9007626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(!bus_priv);
90174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
90274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	while (1) {
90374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		unsigned int port;
90474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
9057626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		dump_ports_bmp(bus_priv->bmp);
90674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
9077626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		port = (BITS_PER_LONG - 1) - __ilog2(bus_priv->bmp->status);
90874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
90974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		if (port == BITS_PER_LONG)
91074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			break;
91174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
91274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		BUG_ON(port >= PORT_COUNT);
91375c86e7422751c5be3caaf448d802839ec685725Geoff Levand		BUG_ON(!bus_priv->devices[port]);
91474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
91575c86e7422751c5be3caaf448d802839ec685725Geoff Levand		ps3_vuart_handle_port_interrupt(bus_priv->devices[port]);
91674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
91774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
91874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return IRQ_HANDLED;
91974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
92074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
9217626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_bus_interrupt_get(void)
92274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
92374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result;
92474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
9257626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	pr_debug(" -> %s:%d\n", __func__, __LINE__);
9267626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9277626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	vuart_bus_priv.use_count++;
9287626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9297626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(vuart_bus_priv.use_count > 2);
9307626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
931d0e5c2185e1c6508894e1ee1a8e9bf79e009e427Geert Uytterhoeven	if (vuart_bus_priv.use_count != 1)
9327626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		return 0;
9337626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9347626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(vuart_bus_priv.bmp);
9357626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9367626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	vuart_bus_priv.bmp = kzalloc(sizeof(struct ports_bmp), GFP_KERNEL);
9377626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9387626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (!vuart_bus_priv.bmp) {
9397626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		pr_debug("%s:%d: kzalloc failed.\n", __func__, __LINE__);
9407626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		result = -ENOMEM;
9417626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		goto fail_bmp_malloc;
9427626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	}
9437626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9447626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = ps3_vuart_irq_setup(PS3_BINDING_CPU_ANY, vuart_bus_priv.bmp,
9457626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		&vuart_bus_priv.virq);
9467626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9477626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (result) {
9487626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		pr_debug("%s:%d: ps3_vuart_irq_setup failed (%d)\n",
9497626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			__func__, __LINE__, result);
9507626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		result = -EPERM;
9517626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		goto fail_alloc_irq;
9527626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	}
9537626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9547626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = request_irq(vuart_bus_priv.virq, ps3_vuart_irq_handler,
9556ea741a13690da9dbbac76175db8e313b0c4b817Yong Zhang		0, "vuart", &vuart_bus_priv);
95674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
9577626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (result) {
9587626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		pr_debug("%s:%d: request_irq failed (%d)\n",
9597626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			__func__, __LINE__, result);
9607626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		goto fail_request_irq;
9617626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	}
96274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
9637626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	pr_debug(" <- %s:%d: ok\n", __func__, __LINE__);
96474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
9657626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9667626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandfail_request_irq:
9677626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	ps3_vuart_irq_destroy(vuart_bus_priv.virq);
9687626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	vuart_bus_priv.virq = NO_IRQ;
9697626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandfail_alloc_irq:
9707626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	kfree(vuart_bus_priv.bmp);
9717626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	vuart_bus_priv.bmp = NULL;
9727626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandfail_bmp_malloc:
9737626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	vuart_bus_priv.use_count--;
9747626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	pr_debug(" <- %s:%d: failed\n", __func__, __LINE__);
9757626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return result;
9767626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand}
9777626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9787626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_bus_interrupt_put(void)
9797626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand{
9807626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	pr_debug(" -> %s:%d\n", __func__, __LINE__);
9817626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9827626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	vuart_bus_priv.use_count--;
9837626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9847626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(vuart_bus_priv.use_count < 0);
9857626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9867626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (vuart_bus_priv.use_count != 0)
9877626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		return 0;
9887626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9897626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	free_irq(vuart_bus_priv.virq, &vuart_bus_priv);
9907626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9917626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	ps3_vuart_irq_destroy(vuart_bus_priv.virq);
9927626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	vuart_bus_priv.virq = NO_IRQ;
9937626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9947626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	kfree(vuart_bus_priv.bmp);
9957626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	vuart_bus_priv.bmp = NULL;
9967626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
9977626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	pr_debug(" <- %s:%d\n", __func__, __LINE__);
9987626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return 0;
99974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
100074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
10017626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_probe(struct ps3_system_bus_device *dev)
100274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
100374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result;
10047626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_driver *drv;
10057626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = NULL;
100674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
100774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
100874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
10097626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	drv = ps3_system_bus_dev_to_vuart_drv(dev);
10107626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
10117626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__,
10127626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		drv->core.core.name);
10137626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
101474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	BUG_ON(!drv);
101574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
10167626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (dev->port_number >= PORT_COUNT) {
10177626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		BUG();
10187626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		return -EINVAL;
10197626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	}
102075c86e7422751c5be3caaf448d802839ec685725Geoff Levand
1021c4e6752dd4f249d6797146d655973bc0bde4c26eGeoff Levand	mutex_lock(&vuart_bus_priv.probe_mutex);
102275c86e7422751c5be3caaf448d802839ec685725Geoff Levand
10237626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = ps3_vuart_bus_interrupt_get();
102474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
10257626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (result)
10267626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		goto fail_setup_interrupt;
102774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
10287626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (vuart_bus_priv.devices[dev->port_number]) {
102974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: port busy (%d)\n", __func__,
10307626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			__LINE__, dev->port_number);
103174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		result = -EBUSY;
10327626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		goto fail_busy;
103374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
103474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
10357626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	vuart_bus_priv.devices[dev->port_number] = dev;
103675c86e7422751c5be3caaf448d802839ec685725Geoff Levand
10377626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	/* Setup dev->driver_priv. */
103875c86e7422751c5be3caaf448d802839ec685725Geoff Levand
10397626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	dev->driver_priv = kzalloc(sizeof(struct ps3_vuart_port_priv),
10407626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		GFP_KERNEL);
104175c86e7422751c5be3caaf448d802839ec685725Geoff Levand
10427626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (!dev->driver_priv) {
104375c86e7422751c5be3caaf448d802839ec685725Geoff Levand		result = -ENOMEM;
10447626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		goto fail_dev_malloc;
104575c86e7422751c5be3caaf448d802839ec685725Geoff Levand	}
104675c86e7422751c5be3caaf448d802839ec685725Geoff Levand
10477626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	priv = to_port_priv(dev);
104875c86e7422751c5be3caaf448d802839ec685725Geoff Levand
10497626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	INIT_LIST_HEAD(&priv->tx_list.head);
10507626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_lock_init(&priv->tx_list.lock);
105175c86e7422751c5be3caaf448d802839ec685725Geoff Levand
10527626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	INIT_LIST_HEAD(&priv->rx_list.head);
10537626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	spin_lock_init(&priv->rx_list.lock);
1054ea1547d31153f8c3bdd32646f17d096d3108c838Geoff Levand
10557626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	INIT_WORK(&priv->rx_list.work.work, NULL);
10567626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	priv->rx_list.work.trigger = 0;
10577626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	priv->rx_list.work.dev = dev;
105874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
105974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	/* clear stale pending interrupts */
106075c86e7422751c5be3caaf448d802839ec685725Geoff Levand
106175c86e7422751c5be3caaf448d802839ec685725Geoff Levand	ps3_vuart_clear_rx_bytes(dev, 0);
106275c86e7422751c5be3caaf448d802839ec685725Geoff Levand
106375c86e7422751c5be3caaf448d802839ec685725Geoff Levand	ps3_vuart_set_interrupt_mask(dev, INTERRUPT_MASK_RX);
106474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
106574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	ps3_vuart_set_triggers(dev, 1, 1);
106674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
106774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (drv->probe)
106874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		result = drv->probe(dev);
106974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	else {
107074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		result = 0;
107174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_info(&dev->core, "%s:%d: no probe method\n", __func__,
107274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__LINE__);
107374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
107474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
107574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	if (result) {
107674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		dev_dbg(&dev->core, "%s:%d: drv->probe failed\n",
107774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand			__func__, __LINE__);
107874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand		goto fail_probe;
107974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
108074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
1081c4e6752dd4f249d6797146d655973bc0bde4c26eGeoff Levand	mutex_unlock(&vuart_bus_priv.probe_mutex);
108275c86e7422751c5be3caaf448d802839ec685725Geoff Levand
108374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
108474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
108574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandfail_probe:
108675c86e7422751c5be3caaf448d802839ec685725Geoff Levand	ps3_vuart_set_interrupt_mask(dev, 0);
10877626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	kfree(dev->driver_priv);
10887626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	dev->driver_priv = NULL;
10897626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandfail_dev_malloc:
10907626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	vuart_bus_priv.devices[dev->port_number] = NULL;
10917626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandfail_busy:
10927626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	ps3_vuart_bus_interrupt_put();
10937626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandfail_setup_interrupt:
1094c4e6752dd4f249d6797146d655973bc0bde4c26eGeoff Levand	mutex_unlock(&vuart_bus_priv.probe_mutex);
10957626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	dev_dbg(&dev->core, "%s:%d: failed\n", __func__, __LINE__);
109674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
109774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
109874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
10997626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand/**
11007626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * ps3_vuart_cleanup - common cleanup helper.
11017626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * @dev: The struct ps3_system_bus_device instance.
11027626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand *
11037626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * Cleans interrupts and HV resources.  Must be called with
11047626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * vuart_bus_priv.probe_mutex held.  Used by ps3_vuart_remove and
11057626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * ps3_vuart_shutdown.  After this call, polled reading will still work.
11067626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand */
11077626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
11087626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_cleanup(struct ps3_system_bus_device *dev)
110974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
11107626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__);
11117626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
11127626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	ps3_vuart_cancel_async(dev);
11137626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	ps3_vuart_set_interrupt_mask(dev, 0);
11147626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	ps3_vuart_bus_interrupt_put();
11157626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return 0;
11167626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand}
11177626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
11187626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand/**
11197626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * ps3_vuart_remove - Completely clean the device instance.
11207626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * @dev: The struct ps3_system_bus_device instance.
11217626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand *
11227626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * Cleans all memory, interrupts and HV resources.  After this call the
11237626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * device can no longer be used.
11247626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand */
11257626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
11267626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_remove(struct ps3_system_bus_device *dev)
11277626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand{
11287626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_priv *priv = to_port_priv(dev);
11297626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_driver *drv;
11307626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
11317626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(!dev);
113274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
1133c4e6752dd4f249d6797146d655973bc0bde4c26eGeoff Levand	mutex_lock(&vuart_bus_priv.probe_mutex);
113475c86e7422751c5be3caaf448d802839ec685725Geoff Levand
11357626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__,
11367626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		dev->match_id);
113774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
11387626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (!dev->core.driver) {
11397626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,
11407626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			__LINE__);
1141c4e6752dd4f249d6797146d655973bc0bde4c26eGeoff Levand		mutex_unlock(&vuart_bus_priv.probe_mutex);
11427626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		return 0;
11437626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	}
114474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
11457626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	drv = ps3_system_bus_dev_to_vuart_drv(dev);
114674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
11477626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(!drv);
114874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
11497626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (drv->remove) {
11507626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		drv->remove(dev);
11517626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	} else {
11527626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		dev_dbg(&dev->core, "%s:%d: no remove method\n", __func__,
11537626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		__LINE__);
115475c86e7422751c5be3caaf448d802839ec685725Geoff Levand		BUG();
115574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	}
115675c86e7422751c5be3caaf448d802839ec685725Geoff Levand
11577626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	ps3_vuart_cleanup(dev);
11587626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
11597626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	vuart_bus_priv.devices[dev->port_number] = NULL;
11607626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	kfree(priv);
11617626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	priv = NULL;
116275c86e7422751c5be3caaf448d802839ec685725Geoff Levand
11637626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
1164c4e6752dd4f249d6797146d655973bc0bde4c26eGeoff Levand	mutex_unlock(&vuart_bus_priv.probe_mutex);
116574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return 0;
116674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
116774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
116874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
11697626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * ps3_vuart_shutdown - Cleans interrupts and HV resources.
11707626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * @dev: The struct ps3_system_bus_device instance.
117174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand *
11727626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * Cleans interrupts and HV resources.  After this call the
11737626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * device can still be used in polling mode.  This behavior required
11747626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * by sys-manager to be able to complete the device power operation
11757626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand * sequence.
117674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
117774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
11787626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int ps3_vuart_shutdown(struct ps3_system_bus_device *dev)
117974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
11807626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	struct ps3_vuart_port_driver *drv;
118174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
11827626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(!dev);
118375c86e7422751c5be3caaf448d802839ec685725Geoff Levand
1184c4e6752dd4f249d6797146d655973bc0bde4c26eGeoff Levand	mutex_lock(&vuart_bus_priv.probe_mutex);
118575c86e7422751c5be3caaf448d802839ec685725Geoff Levand
11867626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__,
11877626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		dev->match_id);
118875c86e7422751c5be3caaf448d802839ec685725Geoff Levand
11897626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (!dev->core.driver) {
11907626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__,
11917626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			__LINE__);
1192c4e6752dd4f249d6797146d655973bc0bde4c26eGeoff Levand		mutex_unlock(&vuart_bus_priv.probe_mutex);
11937626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		return 0;
11947626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	}
119574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
11967626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	drv = ps3_system_bus_dev_to_vuart_drv(dev);
119774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
11987626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(!drv);
119974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
12007626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (drv->shutdown)
12017626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		drv->shutdown(dev);
12027626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	else if (drv->remove) {
12037626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		dev_dbg(&dev->core, "%s:%d: no shutdown, calling remove\n",
12047626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			__func__, __LINE__);
12057626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		drv->remove(dev);
12067626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	} else {
12077626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		dev_dbg(&dev->core, "%s:%d: no shutdown method\n", __func__,
12087626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand			__LINE__);
12097626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		BUG();
12107626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	}
121174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
12127626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	ps3_vuart_cleanup(dev);
121375c86e7422751c5be3caaf448d802839ec685725Geoff Levand
12147626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
121575c86e7422751c5be3caaf448d802839ec685725Geoff Levand
1216c4e6752dd4f249d6797146d655973bc0bde4c26eGeoff Levand	mutex_unlock(&vuart_bus_priv.probe_mutex);
12177626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return 0;
121874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
121974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
12207626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic int __init ps3_vuart_bus_init(void)
122174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
12227626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	pr_debug("%s:%d:\n", __func__, __LINE__);
122375c86e7422751c5be3caaf448d802839ec685725Geoff Levand
12247626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
12257626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand		return -ENODEV;
122674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
1227c4e6752dd4f249d6797146d655973bc0bde4c26eGeoff Levand	mutex_init(&vuart_bus_priv.probe_mutex);
122874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
12297626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	return 0;
12307626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand}
123174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
12327626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandstatic void __exit ps3_vuart_bus_exit(void)
12337626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand{
12347626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	pr_debug("%s:%d:\n", __func__, __LINE__);
123574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
123674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
12377626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandcore_initcall(ps3_vuart_bus_init);
12387626e78d29651d3075e88f233c0632867ea6a35cGeoff Levandmodule_exit(ps3_vuart_bus_exit);
123974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
124074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
124174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * ps3_vuart_port_driver_register - Add a vuart port device driver.
124274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
124374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
124474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandint ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv)
124574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
124674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	int result;
124774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
12487626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.core.name);
12497626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
12507626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(!drv->core.match_id);
12517626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	BUG_ON(!drv->core.core.name);
12527626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
12537626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	drv->core.probe = ps3_vuart_probe;
12547626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	drv->core.remove = ps3_vuart_remove;
12557626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	drv->core.shutdown = ps3_vuart_shutdown;
12567626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand
12577626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	result = ps3_system_bus_driver_register(&drv->core);
125874e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand	return result;
125974e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
126074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff LevandEXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register);
126174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
126274e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand/**
126374e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand * ps3_vuart_port_driver_unregister - Remove a vuart port device driver.
126474e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand */
126574e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand
126674e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levandvoid ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv)
126774e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand{
12687626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.core.name);
12697626e78d29651d3075e88f233c0632867ea6a35cGeoff Levand	ps3_system_bus_driver_unregister(&drv->core);
127074e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff Levand}
127174e95d5de9d8eb243cda68b546bdb29f6ef0f01cGeoff LevandEXPORT_SYMBOL_GPL(ps3_vuart_port_driver_unregister);
1272