diff options
Diffstat (limited to 'drivers/ps3/ps3-vuart.c')
-rw-r--r-- | drivers/ps3/ps3-vuart.c | 1270 |
1 files changed, 1270 insertions, 0 deletions
diff --git a/drivers/ps3/ps3-vuart.c b/drivers/ps3/ps3-vuart.c new file mode 100644 index 000000000000..bb8d5b1eec90 --- /dev/null +++ b/drivers/ps3/ps3-vuart.c | |||
@@ -0,0 +1,1270 @@ | |||
1 | /* | ||
2 | * PS3 virtual uart | ||
3 | * | ||
4 | * Copyright (C) 2006 Sony Computer Entertainment Inc. | ||
5 | * Copyright 2006 Sony Corp. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; version 2 of the License. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/workqueue.h> | ||
25 | #include <linux/bitops.h> | ||
26 | #include <asm/ps3.h> | ||
27 | |||
28 | #include <asm/firmware.h> | ||
29 | #include <asm/lv1call.h> | ||
30 | |||
31 | #include "vuart.h" | ||
32 | |||
33 | MODULE_AUTHOR("Sony Corporation"); | ||
34 | MODULE_LICENSE("GPL v2"); | ||
35 | MODULE_DESCRIPTION("PS3 vuart"); | ||
36 | |||
37 | /** | ||
38 | * vuart - An inter-partition data link service. | ||
39 | * port 0: PS3 AV Settings. | ||
40 | * port 2: PS3 System Manager. | ||
41 | * | ||
42 | * The vuart provides a bi-directional byte stream data link between logical | ||
43 | * partitions. Its primary role is as a communications link between the guest | ||
44 | * OS and the system policy module. The current HV does not support any | ||
45 | * connections other than those listed. | ||
46 | */ | ||
47 | |||
48 | enum {PORT_COUNT = 3,}; | ||
49 | |||
50 | enum vuart_param { | ||
51 | PARAM_TX_TRIGGER = 0, | ||
52 | PARAM_RX_TRIGGER = 1, | ||
53 | PARAM_INTERRUPT_MASK = 2, | ||
54 | PARAM_RX_BUF_SIZE = 3, /* read only */ | ||
55 | PARAM_RX_BYTES = 4, /* read only */ | ||
56 | PARAM_TX_BUF_SIZE = 5, /* read only */ | ||
57 | PARAM_TX_BYTES = 6, /* read only */ | ||
58 | PARAM_INTERRUPT_STATUS = 7, /* read only */ | ||
59 | }; | ||
60 | |||
61 | enum vuart_interrupt_bit { | ||
62 | INTERRUPT_BIT_TX = 0, | ||
63 | INTERRUPT_BIT_RX = 1, | ||
64 | INTERRUPT_BIT_DISCONNECT = 2, | ||
65 | }; | ||
66 | |||
67 | enum vuart_interrupt_mask { | ||
68 | INTERRUPT_MASK_TX = 1, | ||
69 | INTERRUPT_MASK_RX = 2, | ||
70 | INTERRUPT_MASK_DISCONNECT = 4, | ||
71 | }; | ||
72 | |||
73 | /** | ||
74 | * struct ps3_vuart_port_priv - private vuart device data. | ||
75 | */ | ||
76 | |||
77 | struct ps3_vuart_port_priv { | ||
78 | u64 interrupt_mask; | ||
79 | |||
80 | struct { | ||
81 | spinlock_t lock; | ||
82 | struct list_head head; | ||
83 | } tx_list; | ||
84 | struct { | ||
85 | struct ps3_vuart_work work; | ||
86 | unsigned long bytes_held; | ||
87 | spinlock_t lock; | ||
88 | struct list_head head; | ||
89 | } rx_list; | ||
90 | struct ps3_vuart_stats stats; | ||
91 | }; | ||
92 | |||
93 | static struct ps3_vuart_port_priv *to_port_priv( | ||
94 | struct ps3_system_bus_device *dev) | ||
95 | { | ||
96 | BUG_ON(!dev); | ||
97 | BUG_ON(!dev->driver_priv); | ||
98 | return (struct ps3_vuart_port_priv *)dev->driver_priv; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * struct ports_bmp - bitmap indicating ports needing service. | ||
103 | * | ||
104 | * A 256 bit read only bitmap indicating ports needing service. Do not write | ||
105 | * to these bits. Must not cross a page boundary. | ||
106 | */ | ||
107 | |||
108 | struct ports_bmp { | ||
109 | u64 status; | ||
110 | u64 unused[3]; | ||
111 | } __attribute__ ((aligned (32))); | ||
112 | |||
113 | #define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__) | ||
114 | static void __maybe_unused _dump_ports_bmp( | ||
115 | const struct ports_bmp* bmp, const char* func, int line) | ||
116 | { | ||
117 | pr_debug("%s:%d: ports_bmp: %016lxh\n", func, line, bmp->status); | ||
118 | } | ||
119 | |||
120 | #define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__) | ||
121 | static void __maybe_unused _dump_port_params(unsigned int port_number, | ||
122 | const char* func, int line) | ||
123 | { | ||
124 | #if defined(DEBUG) | ||
125 | static const char *strings[] = { | ||
126 | "tx_trigger ", | ||
127 | "rx_trigger ", | ||
128 | "interrupt_mask ", | ||
129 | "rx_buf_size ", | ||
130 | "rx_bytes ", | ||
131 | "tx_buf_size ", | ||
132 | "tx_bytes ", | ||
133 | "interrupt_status", | ||
134 | }; | ||
135 | int result; | ||
136 | unsigned int i; | ||
137 | u64 value; | ||
138 | |||
139 | for (i = 0; i < ARRAY_SIZE(strings); i++) { | ||
140 | result = lv1_get_virtual_uart_param(port_number, i, &value); | ||
141 | |||
142 | if (result) { | ||
143 | pr_debug("%s:%d: port_%u: %s failed: %s\n", func, line, | ||
144 | port_number, strings[i], ps3_result(result)); | ||
145 | continue; | ||
146 | } | ||
147 | pr_debug("%s:%d: port_%u: %s = %lxh\n", | ||
148 | func, line, port_number, strings[i], value); | ||
149 | } | ||
150 | #endif | ||
151 | } | ||
152 | |||
153 | struct vuart_triggers { | ||
154 | unsigned long rx; | ||
155 | unsigned long tx; | ||
156 | }; | ||
157 | |||
158 | int ps3_vuart_get_triggers(struct ps3_system_bus_device *dev, | ||
159 | struct vuart_triggers *trig) | ||
160 | { | ||
161 | int result; | ||
162 | unsigned long size; | ||
163 | unsigned long val; | ||
164 | |||
165 | result = lv1_get_virtual_uart_param(dev->port_number, | ||
166 | PARAM_TX_TRIGGER, &trig->tx); | ||
167 | |||
168 | if (result) { | ||
169 | dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n", | ||
170 | __func__, __LINE__, ps3_result(result)); | ||
171 | return result; | ||
172 | } | ||
173 | |||
174 | result = lv1_get_virtual_uart_param(dev->port_number, | ||
175 | PARAM_RX_BUF_SIZE, &size); | ||
176 | |||
177 | if (result) { | ||
178 | dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n", | ||
179 | __func__, __LINE__, ps3_result(result)); | ||
180 | return result; | ||
181 | } | ||
182 | |||
183 | result = lv1_get_virtual_uart_param(dev->port_number, | ||
184 | PARAM_RX_TRIGGER, &val); | ||
185 | |||
186 | if (result) { | ||
187 | dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n", | ||
188 | __func__, __LINE__, ps3_result(result)); | ||
189 | return result; | ||
190 | } | ||
191 | |||
192 | trig->rx = size - val; | ||
193 | |||
194 | dev_dbg(&dev->core, "%s:%d: tx %lxh, rx %lxh\n", __func__, __LINE__, | ||
195 | trig->tx, trig->rx); | ||
196 | |||
197 | return result; | ||
198 | } | ||
199 | |||
200 | int ps3_vuart_set_triggers(struct ps3_system_bus_device *dev, unsigned int tx, | ||
201 | unsigned int rx) | ||
202 | { | ||
203 | int result; | ||
204 | unsigned long size; | ||
205 | |||
206 | result = lv1_set_virtual_uart_param(dev->port_number, | ||
207 | PARAM_TX_TRIGGER, tx); | ||
208 | |||
209 | if (result) { | ||
210 | dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n", | ||
211 | __func__, __LINE__, ps3_result(result)); | ||
212 | return result; | ||
213 | } | ||
214 | |||
215 | result = lv1_get_virtual_uart_param(dev->port_number, | ||
216 | PARAM_RX_BUF_SIZE, &size); | ||
217 | |||
218 | if (result) { | ||
219 | dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n", | ||
220 | __func__, __LINE__, ps3_result(result)); | ||
221 | return result; | ||
222 | } | ||
223 | |||
224 | result = lv1_set_virtual_uart_param(dev->port_number, | ||
225 | PARAM_RX_TRIGGER, size - rx); | ||
226 | |||
227 | if (result) { | ||
228 | dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n", | ||
229 | __func__, __LINE__, ps3_result(result)); | ||
230 | return result; | ||
231 | } | ||
232 | |||
233 | dev_dbg(&dev->core, "%s:%d: tx %xh, rx %xh\n", __func__, __LINE__, | ||
234 | tx, rx); | ||
235 | |||
236 | return result; | ||
237 | } | ||
238 | |||
239 | static int ps3_vuart_get_rx_bytes_waiting(struct ps3_system_bus_device *dev, | ||
240 | u64 *bytes_waiting) | ||
241 | { | ||
242 | int result; | ||
243 | |||
244 | result = lv1_get_virtual_uart_param(dev->port_number, | ||
245 | PARAM_RX_BYTES, bytes_waiting); | ||
246 | |||
247 | if (result) | ||
248 | dev_dbg(&dev->core, "%s:%d: rx_bytes failed: %s\n", | ||
249 | __func__, __LINE__, ps3_result(result)); | ||
250 | |||
251 | dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, | ||
252 | *bytes_waiting); | ||
253 | return result; | ||
254 | } | ||
255 | |||
256 | /** | ||
257 | * ps3_vuart_set_interrupt_mask - Enable/disable the port interrupt sources. | ||
258 | * @dev: The struct ps3_system_bus_device instance. | ||
259 | * @bmp: Logical OR of enum vuart_interrupt_mask values. A zero bit disables. | ||
260 | */ | ||
261 | |||
262 | static int ps3_vuart_set_interrupt_mask(struct ps3_system_bus_device *dev, | ||
263 | unsigned long mask) | ||
264 | { | ||
265 | int result; | ||
266 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
267 | |||
268 | dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, mask); | ||
269 | |||
270 | priv->interrupt_mask = mask; | ||
271 | |||
272 | result = lv1_set_virtual_uart_param(dev->port_number, | ||
273 | PARAM_INTERRUPT_MASK, priv->interrupt_mask); | ||
274 | |||
275 | if (result) | ||
276 | dev_dbg(&dev->core, "%s:%d: interrupt_mask failed: %s\n", | ||
277 | __func__, __LINE__, ps3_result(result)); | ||
278 | |||
279 | return result; | ||
280 | } | ||
281 | |||
282 | static int ps3_vuart_get_interrupt_status(struct ps3_system_bus_device *dev, | ||
283 | unsigned long *status) | ||
284 | { | ||
285 | int result; | ||
286 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
287 | u64 tmp; | ||
288 | |||
289 | result = lv1_get_virtual_uart_param(dev->port_number, | ||
290 | PARAM_INTERRUPT_STATUS, &tmp); | ||
291 | |||
292 | if (result) | ||
293 | dev_dbg(&dev->core, "%s:%d: interrupt_status failed: %s\n", | ||
294 | __func__, __LINE__, ps3_result(result)); | ||
295 | |||
296 | *status = tmp & priv->interrupt_mask; | ||
297 | |||
298 | dev_dbg(&dev->core, "%s:%d: m %lxh, s %lxh, m&s %lxh\n", | ||
299 | __func__, __LINE__, priv->interrupt_mask, tmp, *status); | ||
300 | |||
301 | return result; | ||
302 | } | ||
303 | |||
304 | int ps3_vuart_enable_interrupt_tx(struct ps3_system_bus_device *dev) | ||
305 | { | ||
306 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
307 | |||
308 | return (priv->interrupt_mask & INTERRUPT_MASK_TX) ? 0 | ||
309 | : ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | ||
310 | | INTERRUPT_MASK_TX); | ||
311 | } | ||
312 | |||
313 | int ps3_vuart_enable_interrupt_rx(struct ps3_system_bus_device *dev) | ||
314 | { | ||
315 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
316 | |||
317 | return (priv->interrupt_mask & INTERRUPT_MASK_RX) ? 0 | ||
318 | : ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | ||
319 | | INTERRUPT_MASK_RX); | ||
320 | } | ||
321 | |||
322 | int ps3_vuart_enable_interrupt_disconnect(struct ps3_system_bus_device *dev) | ||
323 | { | ||
324 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
325 | |||
326 | return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0 | ||
327 | : ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | ||
328 | | INTERRUPT_MASK_DISCONNECT); | ||
329 | } | ||
330 | |||
331 | int ps3_vuart_disable_interrupt_tx(struct ps3_system_bus_device *dev) | ||
332 | { | ||
333 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
334 | |||
335 | return (priv->interrupt_mask & INTERRUPT_MASK_TX) | ||
336 | ? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | ||
337 | & ~INTERRUPT_MASK_TX) : 0; | ||
338 | } | ||
339 | |||
340 | int ps3_vuart_disable_interrupt_rx(struct ps3_system_bus_device *dev) | ||
341 | { | ||
342 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
343 | |||
344 | return (priv->interrupt_mask & INTERRUPT_MASK_RX) | ||
345 | ? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | ||
346 | & ~INTERRUPT_MASK_RX) : 0; | ||
347 | } | ||
348 | |||
349 | int ps3_vuart_disable_interrupt_disconnect(struct ps3_system_bus_device *dev) | ||
350 | { | ||
351 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
352 | |||
353 | return (priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) | ||
354 | ? ps3_vuart_set_interrupt_mask(dev, priv->interrupt_mask | ||
355 | & ~INTERRUPT_MASK_DISCONNECT) : 0; | ||
356 | } | ||
357 | |||
358 | /** | ||
359 | * ps3_vuart_raw_write - Low level write helper. | ||
360 | * @dev: The struct ps3_system_bus_device instance. | ||
361 | * | ||
362 | * Do not call ps3_vuart_raw_write directly, use ps3_vuart_write. | ||
363 | */ | ||
364 | |||
365 | static int ps3_vuart_raw_write(struct ps3_system_bus_device *dev, | ||
366 | const void* buf, unsigned int bytes, unsigned long *bytes_written) | ||
367 | { | ||
368 | int result; | ||
369 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
370 | |||
371 | result = lv1_write_virtual_uart(dev->port_number, | ||
372 | ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_written); | ||
373 | |||
374 | if (result) { | ||
375 | dev_dbg(&dev->core, "%s:%d: lv1_write_virtual_uart failed: " | ||
376 | "%s\n", __func__, __LINE__, ps3_result(result)); | ||
377 | return result; | ||
378 | } | ||
379 | |||
380 | priv->stats.bytes_written += *bytes_written; | ||
381 | |||
382 | dev_dbg(&dev->core, "%s:%d: wrote %lxh/%xh=>%lxh\n", __func__, __LINE__, | ||
383 | *bytes_written, bytes, priv->stats.bytes_written); | ||
384 | |||
385 | return result; | ||
386 | } | ||
387 | |||
388 | /** | ||
389 | * ps3_vuart_raw_read - Low level read helper. | ||
390 | * @dev: The struct ps3_system_bus_device instance. | ||
391 | * | ||
392 | * Do not call ps3_vuart_raw_read directly, use ps3_vuart_read. | ||
393 | */ | ||
394 | |||
395 | static int ps3_vuart_raw_read(struct ps3_system_bus_device *dev, void *buf, | ||
396 | unsigned int bytes, unsigned long *bytes_read) | ||
397 | { | ||
398 | int result; | ||
399 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
400 | |||
401 | dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes); | ||
402 | |||
403 | result = lv1_read_virtual_uart(dev->port_number, | ||
404 | ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_read); | ||
405 | |||
406 | if (result) { | ||
407 | dev_dbg(&dev->core, "%s:%d: lv1_read_virtual_uart failed: %s\n", | ||
408 | __func__, __LINE__, ps3_result(result)); | ||
409 | return result; | ||
410 | } | ||
411 | |||
412 | priv->stats.bytes_read += *bytes_read; | ||
413 | |||
414 | dev_dbg(&dev->core, "%s:%d: read %lxh/%xh=>%lxh\n", __func__, __LINE__, | ||
415 | *bytes_read, bytes, priv->stats.bytes_read); | ||
416 | |||
417 | return result; | ||
418 | } | ||
419 | |||
420 | /** | ||
421 | * ps3_vuart_clear_rx_bytes - Discard bytes received. | ||
422 | * @dev: The struct ps3_system_bus_device instance. | ||
423 | * @bytes: Max byte count to discard, zero = all pending. | ||
424 | * | ||
425 | * Used to clear pending rx interrupt source. Will not block. | ||
426 | */ | ||
427 | |||
428 | void ps3_vuart_clear_rx_bytes(struct ps3_system_bus_device *dev, | ||
429 | unsigned int bytes) | ||
430 | { | ||
431 | int result; | ||
432 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
433 | u64 bytes_waiting; | ||
434 | void* tmp; | ||
435 | |||
436 | result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes_waiting); | ||
437 | |||
438 | BUG_ON(result); | ||
439 | |||
440 | bytes = bytes ? min(bytes, (unsigned int)bytes_waiting) : bytes_waiting; | ||
441 | |||
442 | dev_dbg(&dev->core, "%s:%d: %u\n", __func__, __LINE__, bytes); | ||
443 | |||
444 | if (!bytes) | ||
445 | return; | ||
446 | |||
447 | /* Add some extra space for recently arrived data. */ | ||
448 | |||
449 | bytes += 128; | ||
450 | |||
451 | tmp = kmalloc(bytes, GFP_KERNEL); | ||
452 | |||
453 | if (!tmp) | ||
454 | return; | ||
455 | |||
456 | ps3_vuart_raw_read(dev, tmp, bytes, &bytes_waiting); | ||
457 | |||
458 | kfree(tmp); | ||
459 | |||
460 | /* Don't include these bytes in the stats. */ | ||
461 | |||
462 | priv->stats.bytes_read -= bytes_waiting; | ||
463 | } | ||
464 | EXPORT_SYMBOL_GPL(ps3_vuart_clear_rx_bytes); | ||
465 | |||
466 | /** | ||
467 | * struct list_buffer - An element for a port device fifo buffer list. | ||
468 | */ | ||
469 | |||
470 | struct list_buffer { | ||
471 | struct list_head link; | ||
472 | const unsigned char *head; | ||
473 | const unsigned char *tail; | ||
474 | unsigned long dbg_number; | ||
475 | unsigned char data[]; | ||
476 | }; | ||
477 | |||
478 | /** | ||
479 | * ps3_vuart_write - the entry point for writing data to a port | ||
480 | * @dev: The struct ps3_system_bus_device instance. | ||
481 | * | ||
482 | * If the port is idle on entry as much of the incoming data is written to | ||
483 | * the port as the port will accept. Otherwise a list buffer is created | ||
484 | * and any remaning incoming data is copied to that buffer. The buffer is | ||
485 | * then enqueued for transmision via the transmit interrupt. | ||
486 | */ | ||
487 | |||
488 | int ps3_vuart_write(struct ps3_system_bus_device *dev, const void *buf, | ||
489 | unsigned int bytes) | ||
490 | { | ||
491 | static unsigned long dbg_number; | ||
492 | int result; | ||
493 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
494 | unsigned long flags; | ||
495 | struct list_buffer *lb; | ||
496 | |||
497 | dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, | ||
498 | bytes, bytes); | ||
499 | |||
500 | spin_lock_irqsave(&priv->tx_list.lock, flags); | ||
501 | |||
502 | if (list_empty(&priv->tx_list.head)) { | ||
503 | unsigned long bytes_written; | ||
504 | |||
505 | result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written); | ||
506 | |||
507 | spin_unlock_irqrestore(&priv->tx_list.lock, flags); | ||
508 | |||
509 | if (result) { | ||
510 | dev_dbg(&dev->core, | ||
511 | "%s:%d: ps3_vuart_raw_write failed\n", | ||
512 | __func__, __LINE__); | ||
513 | return result; | ||
514 | } | ||
515 | |||
516 | if (bytes_written == bytes) { | ||
517 | dev_dbg(&dev->core, "%s:%d: wrote %xh bytes\n", | ||
518 | __func__, __LINE__, bytes); | ||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | bytes -= bytes_written; | ||
523 | buf += bytes_written; | ||
524 | } else | ||
525 | spin_unlock_irqrestore(&priv->tx_list.lock, flags); | ||
526 | |||
527 | lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL); | ||
528 | |||
529 | if (!lb) { | ||
530 | return -ENOMEM; | ||
531 | } | ||
532 | |||
533 | memcpy(lb->data, buf, bytes); | ||
534 | lb->head = lb->data; | ||
535 | lb->tail = lb->data + bytes; | ||
536 | lb->dbg_number = ++dbg_number; | ||
537 | |||
538 | spin_lock_irqsave(&priv->tx_list.lock, flags); | ||
539 | list_add_tail(&lb->link, &priv->tx_list.head); | ||
540 | ps3_vuart_enable_interrupt_tx(dev); | ||
541 | spin_unlock_irqrestore(&priv->tx_list.lock, flags); | ||
542 | |||
543 | dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n", | ||
544 | __func__, __LINE__, lb->dbg_number, bytes); | ||
545 | |||
546 | return 0; | ||
547 | } | ||
548 | EXPORT_SYMBOL_GPL(ps3_vuart_write); | ||
549 | |||
550 | /** | ||
551 | * ps3_vuart_queue_rx_bytes - Queue waiting bytes into the buffer list. | ||
552 | * @dev: The struct ps3_system_bus_device instance. | ||
553 | * @bytes_queued: Number of bytes queued to the buffer list. | ||
554 | * | ||
555 | * Must be called with priv->rx_list.lock held. | ||
556 | */ | ||
557 | |||
558 | static int ps3_vuart_queue_rx_bytes(struct ps3_system_bus_device *dev, | ||
559 | u64 *bytes_queued) | ||
560 | { | ||
561 | static unsigned long dbg_number; | ||
562 | int result; | ||
563 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
564 | struct list_buffer *lb; | ||
565 | u64 bytes; | ||
566 | |||
567 | *bytes_queued = 0; | ||
568 | |||
569 | result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes); | ||
570 | BUG_ON(result); | ||
571 | |||
572 | if (result) | ||
573 | return -EIO; | ||
574 | |||
575 | if (!bytes) | ||
576 | return 0; | ||
577 | |||
578 | /* Add some extra space for recently arrived data. */ | ||
579 | |||
580 | bytes += 128; | ||
581 | |||
582 | lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC); | ||
583 | |||
584 | if (!lb) | ||
585 | return -ENOMEM; | ||
586 | |||
587 | ps3_vuart_raw_read(dev, lb->data, bytes, &bytes); | ||
588 | |||
589 | lb->head = lb->data; | ||
590 | lb->tail = lb->data + bytes; | ||
591 | lb->dbg_number = ++dbg_number; | ||
592 | |||
593 | list_add_tail(&lb->link, &priv->rx_list.head); | ||
594 | priv->rx_list.bytes_held += bytes; | ||
595 | |||
596 | dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %lxh bytes\n", | ||
597 | __func__, __LINE__, lb->dbg_number, bytes); | ||
598 | |||
599 | *bytes_queued = bytes; | ||
600 | |||
601 | return 0; | ||
602 | } | ||
603 | |||
604 | /** | ||
605 | * ps3_vuart_read - The entry point for reading data from a port. | ||
606 | * | ||
607 | * Queue data waiting at the port, and if enough bytes to satisfy the request | ||
608 | * are held in the buffer list those bytes are dequeued and copied to the | ||
609 | * caller's buffer. Emptied list buffers are retiered. If the request cannot | ||
610 | * be statified by bytes held in the list buffers -EAGAIN is returned. | ||
611 | */ | ||
612 | |||
613 | int ps3_vuart_read(struct ps3_system_bus_device *dev, void *buf, | ||
614 | unsigned int bytes) | ||
615 | { | ||
616 | int result; | ||
617 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
618 | unsigned long flags; | ||
619 | struct list_buffer *lb, *n; | ||
620 | unsigned long bytes_read; | ||
621 | |||
622 | dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, | ||
623 | bytes, bytes); | ||
624 | |||
625 | spin_lock_irqsave(&priv->rx_list.lock, flags); | ||
626 | |||
627 | /* Queue rx bytes here for polled reads. */ | ||
628 | |||
629 | while (priv->rx_list.bytes_held < bytes) { | ||
630 | u64 tmp; | ||
631 | |||
632 | result = ps3_vuart_queue_rx_bytes(dev, &tmp); | ||
633 | if (result || !tmp) { | ||
634 | dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n", | ||
635 | __func__, __LINE__, | ||
636 | bytes - priv->rx_list.bytes_held); | ||
637 | spin_unlock_irqrestore(&priv->rx_list.lock, flags); | ||
638 | return -EAGAIN; | ||
639 | } | ||
640 | } | ||
641 | |||
642 | list_for_each_entry_safe(lb, n, &priv->rx_list.head, link) { | ||
643 | bytes_read = min((unsigned int)(lb->tail - lb->head), bytes); | ||
644 | |||
645 | memcpy(buf, lb->head, bytes_read); | ||
646 | buf += bytes_read; | ||
647 | bytes -= bytes_read; | ||
648 | priv->rx_list.bytes_held -= bytes_read; | ||
649 | |||
650 | if (bytes_read < lb->tail - lb->head) { | ||
651 | lb->head += bytes_read; | ||
652 | dev_dbg(&dev->core, "%s:%d: buf_%lu: dequeued %lxh " | ||
653 | "bytes\n", __func__, __LINE__, lb->dbg_number, | ||
654 | bytes_read); | ||
655 | spin_unlock_irqrestore(&priv->rx_list.lock, flags); | ||
656 | return 0; | ||
657 | } | ||
658 | |||
659 | dev_dbg(&dev->core, "%s:%d: buf_%lu: free, dequeued %lxh " | ||
660 | "bytes\n", __func__, __LINE__, lb->dbg_number, | ||
661 | bytes_read); | ||
662 | |||
663 | list_del(&lb->link); | ||
664 | kfree(lb); | ||
665 | } | ||
666 | |||
667 | spin_unlock_irqrestore(&priv->rx_list.lock, flags); | ||
668 | return 0; | ||
669 | } | ||
670 | EXPORT_SYMBOL_GPL(ps3_vuart_read); | ||
671 | |||
672 | /** | ||
673 | * ps3_vuart_work - Asynchronous read handler. | ||
674 | */ | ||
675 | |||
676 | static void ps3_vuart_work(struct work_struct *work) | ||
677 | { | ||
678 | struct ps3_system_bus_device *dev = | ||
679 | ps3_vuart_work_to_system_bus_dev(work); | ||
680 | struct ps3_vuart_port_driver *drv = | ||
681 | ps3_system_bus_dev_to_vuart_drv(dev); | ||
682 | |||
683 | BUG_ON(!drv); | ||
684 | drv->work(dev); | ||
685 | } | ||
686 | |||
687 | int ps3_vuart_read_async(struct ps3_system_bus_device *dev, unsigned int bytes) | ||
688 | { | ||
689 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
690 | unsigned long flags; | ||
691 | |||
692 | if (priv->rx_list.work.trigger) { | ||
693 | dev_dbg(&dev->core, "%s:%d: warning, multiple calls\n", | ||
694 | __func__, __LINE__); | ||
695 | return -EAGAIN; | ||
696 | } | ||
697 | |||
698 | BUG_ON(!bytes); | ||
699 | |||
700 | PREPARE_WORK(&priv->rx_list.work.work, ps3_vuart_work); | ||
701 | |||
702 | spin_lock_irqsave(&priv->rx_list.lock, flags); | ||
703 | if (priv->rx_list.bytes_held >= bytes) { | ||
704 | dev_dbg(&dev->core, "%s:%d: schedule_work %xh bytes\n", | ||
705 | __func__, __LINE__, bytes); | ||
706 | schedule_work(&priv->rx_list.work.work); | ||
707 | spin_unlock_irqrestore(&priv->rx_list.lock, flags); | ||
708 | return 0; | ||
709 | } | ||
710 | |||
711 | priv->rx_list.work.trigger = bytes; | ||
712 | spin_unlock_irqrestore(&priv->rx_list.lock, flags); | ||
713 | |||
714 | dev_dbg(&dev->core, "%s:%d: waiting for %u(%xh) bytes\n", __func__, | ||
715 | __LINE__, bytes, bytes); | ||
716 | |||
717 | return 0; | ||
718 | } | ||
719 | EXPORT_SYMBOL_GPL(ps3_vuart_read_async); | ||
720 | |||
721 | void ps3_vuart_cancel_async(struct ps3_system_bus_device *dev) | ||
722 | { | ||
723 | to_port_priv(dev)->rx_list.work.trigger = 0; | ||
724 | } | ||
725 | EXPORT_SYMBOL_GPL(ps3_vuart_cancel_async); | ||
726 | |||
727 | /** | ||
728 | * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler | ||
729 | * | ||
730 | * Services the transmit interrupt for the port. Writes as much data from the | ||
731 | * buffer list as the port will accept. Retires any emptied list buffers and | ||
732 | * adjusts the final list buffer state for a partial write. | ||
733 | */ | ||
734 | |||
735 | static int ps3_vuart_handle_interrupt_tx(struct ps3_system_bus_device *dev) | ||
736 | { | ||
737 | int result = 0; | ||
738 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
739 | unsigned long flags; | ||
740 | struct list_buffer *lb, *n; | ||
741 | unsigned long bytes_total = 0; | ||
742 | |||
743 | dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); | ||
744 | |||
745 | spin_lock_irqsave(&priv->tx_list.lock, flags); | ||
746 | |||
747 | list_for_each_entry_safe(lb, n, &priv->tx_list.head, link) { | ||
748 | |||
749 | unsigned long bytes_written; | ||
750 | |||
751 | result = ps3_vuart_raw_write(dev, lb->head, lb->tail - lb->head, | ||
752 | &bytes_written); | ||
753 | |||
754 | if (result) { | ||
755 | dev_dbg(&dev->core, | ||
756 | "%s:%d: ps3_vuart_raw_write failed\n", | ||
757 | __func__, __LINE__); | ||
758 | break; | ||
759 | } | ||
760 | |||
761 | bytes_total += bytes_written; | ||
762 | |||
763 | if (bytes_written < lb->tail - lb->head) { | ||
764 | lb->head += bytes_written; | ||
765 | dev_dbg(&dev->core, | ||
766 | "%s:%d cleared buf_%lu, %lxh bytes\n", | ||
767 | __func__, __LINE__, lb->dbg_number, | ||
768 | bytes_written); | ||
769 | goto port_full; | ||
770 | } | ||
771 | |||
772 | dev_dbg(&dev->core, "%s:%d free buf_%lu\n", __func__, __LINE__, | ||
773 | lb->dbg_number); | ||
774 | |||
775 | list_del(&lb->link); | ||
776 | kfree(lb); | ||
777 | } | ||
778 | |||
779 | ps3_vuart_disable_interrupt_tx(dev); | ||
780 | port_full: | ||
781 | spin_unlock_irqrestore(&priv->tx_list.lock, flags); | ||
782 | dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n", | ||
783 | __func__, __LINE__, bytes_total); | ||
784 | return result; | ||
785 | } | ||
786 | |||
787 | /** | ||
788 | * ps3_vuart_handle_interrupt_rx - third stage receive interrupt handler | ||
789 | * | ||
790 | * Services the receive interrupt for the port. Creates a list buffer and | ||
791 | * copies all waiting port data to that buffer and enqueues the buffer in the | ||
792 | * buffer list. Buffer list data is dequeued via ps3_vuart_read. | ||
793 | */ | ||
794 | |||
795 | static int ps3_vuart_handle_interrupt_rx(struct ps3_system_bus_device *dev) | ||
796 | { | ||
797 | int result; | ||
798 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
799 | unsigned long flags; | ||
800 | u64 bytes; | ||
801 | |||
802 | dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); | ||
803 | |||
804 | spin_lock_irqsave(&priv->rx_list.lock, flags); | ||
805 | result = ps3_vuart_queue_rx_bytes(dev, &bytes); | ||
806 | |||
807 | if (result) { | ||
808 | spin_unlock_irqrestore(&priv->rx_list.lock, flags); | ||
809 | return result; | ||
810 | } | ||
811 | |||
812 | if (priv->rx_list.work.trigger && priv->rx_list.bytes_held | ||
813 | >= priv->rx_list.work.trigger) { | ||
814 | dev_dbg(&dev->core, "%s:%d: schedule_work %lxh bytes\n", | ||
815 | __func__, __LINE__, priv->rx_list.work.trigger); | ||
816 | priv->rx_list.work.trigger = 0; | ||
817 | schedule_work(&priv->rx_list.work.work); | ||
818 | } | ||
819 | |||
820 | spin_unlock_irqrestore(&priv->rx_list.lock, flags); | ||
821 | return result; | ||
822 | } | ||
823 | |||
824 | static int ps3_vuart_handle_interrupt_disconnect( | ||
825 | struct ps3_system_bus_device *dev) | ||
826 | { | ||
827 | dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); | ||
828 | BUG_ON("no support"); | ||
829 | return -1; | ||
830 | } | ||
831 | |||
832 | /** | ||
833 | * ps3_vuart_handle_port_interrupt - second stage interrupt handler | ||
834 | * | ||
835 | * Services any pending interrupt types for the port. Passes control to the | ||
836 | * third stage type specific interrupt handler. Returns control to the first | ||
837 | * stage handler after one iteration. | ||
838 | */ | ||
839 | |||
840 | static int ps3_vuart_handle_port_interrupt(struct ps3_system_bus_device *dev) | ||
841 | { | ||
842 | int result; | ||
843 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
844 | unsigned long status; | ||
845 | |||
846 | result = ps3_vuart_get_interrupt_status(dev, &status); | ||
847 | |||
848 | if (result) | ||
849 | return result; | ||
850 | |||
851 | dev_dbg(&dev->core, "%s:%d: status: %lxh\n", __func__, __LINE__, | ||
852 | status); | ||
853 | |||
854 | if (status & INTERRUPT_MASK_DISCONNECT) { | ||
855 | priv->stats.disconnect_interrupts++; | ||
856 | result = ps3_vuart_handle_interrupt_disconnect(dev); | ||
857 | if (result) | ||
858 | ps3_vuart_disable_interrupt_disconnect(dev); | ||
859 | } | ||
860 | |||
861 | if (status & INTERRUPT_MASK_TX) { | ||
862 | priv->stats.tx_interrupts++; | ||
863 | result = ps3_vuart_handle_interrupt_tx(dev); | ||
864 | if (result) | ||
865 | ps3_vuart_disable_interrupt_tx(dev); | ||
866 | } | ||
867 | |||
868 | if (status & INTERRUPT_MASK_RX) { | ||
869 | priv->stats.rx_interrupts++; | ||
870 | result = ps3_vuart_handle_interrupt_rx(dev); | ||
871 | if (result) | ||
872 | ps3_vuart_disable_interrupt_rx(dev); | ||
873 | } | ||
874 | |||
875 | return 0; | ||
876 | } | ||
877 | |||
878 | struct vuart_bus_priv { | ||
879 | struct ports_bmp *bmp; | ||
880 | unsigned int virq; | ||
881 | struct semaphore probe_mutex; | ||
882 | int use_count; | ||
883 | struct ps3_system_bus_device *devices[PORT_COUNT]; | ||
884 | } static vuart_bus_priv; | ||
885 | |||
886 | /** | ||
887 | * ps3_vuart_irq_handler - first stage interrupt handler | ||
888 | * | ||
889 | * Loops finding any interrupting port and its associated instance data. | ||
890 | * Passes control to the second stage port specific interrupt handler. Loops | ||
891 | * until all outstanding interrupts are serviced. | ||
892 | */ | ||
893 | |||
894 | static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private) | ||
895 | { | ||
896 | struct vuart_bus_priv *bus_priv = _private; | ||
897 | |||
898 | BUG_ON(!bus_priv); | ||
899 | |||
900 | while (1) { | ||
901 | unsigned int port; | ||
902 | |||
903 | dump_ports_bmp(bus_priv->bmp); | ||
904 | |||
905 | port = (BITS_PER_LONG - 1) - __ilog2(bus_priv->bmp->status); | ||
906 | |||
907 | if (port == BITS_PER_LONG) | ||
908 | break; | ||
909 | |||
910 | BUG_ON(port >= PORT_COUNT); | ||
911 | BUG_ON(!bus_priv->devices[port]); | ||
912 | |||
913 | ps3_vuart_handle_port_interrupt(bus_priv->devices[port]); | ||
914 | } | ||
915 | |||
916 | return IRQ_HANDLED; | ||
917 | } | ||
918 | |||
919 | static int ps3_vuart_bus_interrupt_get(void) | ||
920 | { | ||
921 | int result; | ||
922 | |||
923 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
924 | |||
925 | vuart_bus_priv.use_count++; | ||
926 | |||
927 | BUG_ON(vuart_bus_priv.use_count > 2); | ||
928 | |||
929 | if (vuart_bus_priv.use_count != 1) { | ||
930 | return 0; | ||
931 | } | ||
932 | |||
933 | BUG_ON(vuart_bus_priv.bmp); | ||
934 | |||
935 | vuart_bus_priv.bmp = kzalloc(sizeof(struct ports_bmp), GFP_KERNEL); | ||
936 | |||
937 | if (!vuart_bus_priv.bmp) { | ||
938 | pr_debug("%s:%d: kzalloc failed.\n", __func__, __LINE__); | ||
939 | result = -ENOMEM; | ||
940 | goto fail_bmp_malloc; | ||
941 | } | ||
942 | |||
943 | result = ps3_vuart_irq_setup(PS3_BINDING_CPU_ANY, vuart_bus_priv.bmp, | ||
944 | &vuart_bus_priv.virq); | ||
945 | |||
946 | if (result) { | ||
947 | pr_debug("%s:%d: ps3_vuart_irq_setup failed (%d)\n", | ||
948 | __func__, __LINE__, result); | ||
949 | result = -EPERM; | ||
950 | goto fail_alloc_irq; | ||
951 | } | ||
952 | |||
953 | result = request_irq(vuart_bus_priv.virq, ps3_vuart_irq_handler, | ||
954 | IRQF_DISABLED, "vuart", &vuart_bus_priv); | ||
955 | |||
956 | if (result) { | ||
957 | pr_debug("%s:%d: request_irq failed (%d)\n", | ||
958 | __func__, __LINE__, result); | ||
959 | goto fail_request_irq; | ||
960 | } | ||
961 | |||
962 | pr_debug(" <- %s:%d: ok\n", __func__, __LINE__); | ||
963 | return result; | ||
964 | |||
965 | fail_request_irq: | ||
966 | ps3_vuart_irq_destroy(vuart_bus_priv.virq); | ||
967 | vuart_bus_priv.virq = NO_IRQ; | ||
968 | fail_alloc_irq: | ||
969 | kfree(vuart_bus_priv.bmp); | ||
970 | vuart_bus_priv.bmp = NULL; | ||
971 | fail_bmp_malloc: | ||
972 | vuart_bus_priv.use_count--; | ||
973 | pr_debug(" <- %s:%d: failed\n", __func__, __LINE__); | ||
974 | return result; | ||
975 | } | ||
976 | |||
977 | static int ps3_vuart_bus_interrupt_put(void) | ||
978 | { | ||
979 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
980 | |||
981 | vuart_bus_priv.use_count--; | ||
982 | |||
983 | BUG_ON(vuart_bus_priv.use_count < 0); | ||
984 | |||
985 | if (vuart_bus_priv.use_count != 0) | ||
986 | return 0; | ||
987 | |||
988 | free_irq(vuart_bus_priv.virq, &vuart_bus_priv); | ||
989 | |||
990 | ps3_vuart_irq_destroy(vuart_bus_priv.virq); | ||
991 | vuart_bus_priv.virq = NO_IRQ; | ||
992 | |||
993 | kfree(vuart_bus_priv.bmp); | ||
994 | vuart_bus_priv.bmp = NULL; | ||
995 | |||
996 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
997 | return 0; | ||
998 | } | ||
999 | |||
1000 | static int ps3_vuart_probe(struct ps3_system_bus_device *dev) | ||
1001 | { | ||
1002 | int result; | ||
1003 | struct ps3_vuart_port_driver *drv; | ||
1004 | struct ps3_vuart_port_priv *priv = NULL; | ||
1005 | |||
1006 | dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); | ||
1007 | |||
1008 | drv = ps3_system_bus_dev_to_vuart_drv(dev); | ||
1009 | |||
1010 | dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__, | ||
1011 | drv->core.core.name); | ||
1012 | |||
1013 | BUG_ON(!drv); | ||
1014 | |||
1015 | if (dev->port_number >= PORT_COUNT) { | ||
1016 | BUG(); | ||
1017 | return -EINVAL; | ||
1018 | } | ||
1019 | |||
1020 | down(&vuart_bus_priv.probe_mutex); | ||
1021 | |||
1022 | result = ps3_vuart_bus_interrupt_get(); | ||
1023 | |||
1024 | if (result) | ||
1025 | goto fail_setup_interrupt; | ||
1026 | |||
1027 | if (vuart_bus_priv.devices[dev->port_number]) { | ||
1028 | dev_dbg(&dev->core, "%s:%d: port busy (%d)\n", __func__, | ||
1029 | __LINE__, dev->port_number); | ||
1030 | result = -EBUSY; | ||
1031 | goto fail_busy; | ||
1032 | } | ||
1033 | |||
1034 | vuart_bus_priv.devices[dev->port_number] = dev; | ||
1035 | |||
1036 | /* Setup dev->driver_priv. */ | ||
1037 | |||
1038 | dev->driver_priv = kzalloc(sizeof(struct ps3_vuart_port_priv), | ||
1039 | GFP_KERNEL); | ||
1040 | |||
1041 | if (!dev->driver_priv) { | ||
1042 | result = -ENOMEM; | ||
1043 | goto fail_dev_malloc; | ||
1044 | } | ||
1045 | |||
1046 | priv = to_port_priv(dev); | ||
1047 | |||
1048 | INIT_LIST_HEAD(&priv->tx_list.head); | ||
1049 | spin_lock_init(&priv->tx_list.lock); | ||
1050 | |||
1051 | INIT_LIST_HEAD(&priv->rx_list.head); | ||
1052 | spin_lock_init(&priv->rx_list.lock); | ||
1053 | |||
1054 | INIT_WORK(&priv->rx_list.work.work, NULL); | ||
1055 | priv->rx_list.work.trigger = 0; | ||
1056 | priv->rx_list.work.dev = dev; | ||
1057 | |||
1058 | /* clear stale pending interrupts */ | ||
1059 | |||
1060 | ps3_vuart_clear_rx_bytes(dev, 0); | ||
1061 | |||
1062 | ps3_vuart_set_interrupt_mask(dev, INTERRUPT_MASK_RX); | ||
1063 | |||
1064 | ps3_vuart_set_triggers(dev, 1, 1); | ||
1065 | |||
1066 | if (drv->probe) | ||
1067 | result = drv->probe(dev); | ||
1068 | else { | ||
1069 | result = 0; | ||
1070 | dev_info(&dev->core, "%s:%d: no probe method\n", __func__, | ||
1071 | __LINE__); | ||
1072 | } | ||
1073 | |||
1074 | if (result) { | ||
1075 | dev_dbg(&dev->core, "%s:%d: drv->probe failed\n", | ||
1076 | __func__, __LINE__); | ||
1077 | goto fail_probe; | ||
1078 | } | ||
1079 | |||
1080 | up(&vuart_bus_priv.probe_mutex); | ||
1081 | |||
1082 | return result; | ||
1083 | |||
1084 | fail_probe: | ||
1085 | ps3_vuart_set_interrupt_mask(dev, 0); | ||
1086 | kfree(dev->driver_priv); | ||
1087 | dev->driver_priv = NULL; | ||
1088 | fail_dev_malloc: | ||
1089 | vuart_bus_priv.devices[dev->port_number] = NULL; | ||
1090 | fail_busy: | ||
1091 | ps3_vuart_bus_interrupt_put(); | ||
1092 | fail_setup_interrupt: | ||
1093 | up(&vuart_bus_priv.probe_mutex); | ||
1094 | dev_dbg(&dev->core, "%s:%d: failed\n", __func__, __LINE__); | ||
1095 | return result; | ||
1096 | } | ||
1097 | |||
1098 | /** | ||
1099 | * ps3_vuart_cleanup - common cleanup helper. | ||
1100 | * @dev: The struct ps3_system_bus_device instance. | ||
1101 | * | ||
1102 | * Cleans interrupts and HV resources. Must be called with | ||
1103 | * vuart_bus_priv.probe_mutex held. Used by ps3_vuart_remove and | ||
1104 | * ps3_vuart_shutdown. After this call, polled reading will still work. | ||
1105 | */ | ||
1106 | |||
1107 | static int ps3_vuart_cleanup(struct ps3_system_bus_device *dev) | ||
1108 | { | ||
1109 | dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); | ||
1110 | |||
1111 | ps3_vuart_cancel_async(dev); | ||
1112 | ps3_vuart_set_interrupt_mask(dev, 0); | ||
1113 | ps3_vuart_bus_interrupt_put(); | ||
1114 | return 0; | ||
1115 | } | ||
1116 | |||
1117 | /** | ||
1118 | * ps3_vuart_remove - Completely clean the device instance. | ||
1119 | * @dev: The struct ps3_system_bus_device instance. | ||
1120 | * | ||
1121 | * Cleans all memory, interrupts and HV resources. After this call the | ||
1122 | * device can no longer be used. | ||
1123 | */ | ||
1124 | |||
1125 | static int ps3_vuart_remove(struct ps3_system_bus_device *dev) | ||
1126 | { | ||
1127 | struct ps3_vuart_port_priv *priv = to_port_priv(dev); | ||
1128 | struct ps3_vuart_port_driver *drv; | ||
1129 | |||
1130 | BUG_ON(!dev); | ||
1131 | |||
1132 | down(&vuart_bus_priv.probe_mutex); | ||
1133 | |||
1134 | dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, | ||
1135 | dev->match_id); | ||
1136 | |||
1137 | if (!dev->core.driver) { | ||
1138 | dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, | ||
1139 | __LINE__); | ||
1140 | up(&vuart_bus_priv.probe_mutex); | ||
1141 | return 0; | ||
1142 | } | ||
1143 | |||
1144 | drv = ps3_system_bus_dev_to_vuart_drv(dev); | ||
1145 | |||
1146 | BUG_ON(!drv); | ||
1147 | |||
1148 | if (drv->remove) { | ||
1149 | drv->remove(dev); | ||
1150 | } else { | ||
1151 | dev_dbg(&dev->core, "%s:%d: no remove method\n", __func__, | ||
1152 | __LINE__); | ||
1153 | BUG(); | ||
1154 | } | ||
1155 | |||
1156 | ps3_vuart_cleanup(dev); | ||
1157 | |||
1158 | vuart_bus_priv.devices[dev->port_number] = NULL; | ||
1159 | kfree(priv); | ||
1160 | priv = NULL; | ||
1161 | |||
1162 | dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); | ||
1163 | up(&vuart_bus_priv.probe_mutex); | ||
1164 | return 0; | ||
1165 | } | ||
1166 | |||
1167 | /** | ||
1168 | * ps3_vuart_shutdown - Cleans interrupts and HV resources. | ||
1169 | * @dev: The struct ps3_system_bus_device instance. | ||
1170 | * | ||
1171 | * Cleans interrupts and HV resources. After this call the | ||
1172 | * device can still be used in polling mode. This behavior required | ||
1173 | * by sys-manager to be able to complete the device power operation | ||
1174 | * sequence. | ||
1175 | */ | ||
1176 | |||
1177 | static int ps3_vuart_shutdown(struct ps3_system_bus_device *dev) | ||
1178 | { | ||
1179 | struct ps3_vuart_port_driver *drv; | ||
1180 | |||
1181 | BUG_ON(!dev); | ||
1182 | |||
1183 | down(&vuart_bus_priv.probe_mutex); | ||
1184 | |||
1185 | dev_dbg(&dev->core, " -> %s:%d: match_id %d\n", __func__, __LINE__, | ||
1186 | dev->match_id); | ||
1187 | |||
1188 | if (!dev->core.driver) { | ||
1189 | dev_dbg(&dev->core, "%s:%d: no driver bound\n", __func__, | ||
1190 | __LINE__); | ||
1191 | up(&vuart_bus_priv.probe_mutex); | ||
1192 | return 0; | ||
1193 | } | ||
1194 | |||
1195 | drv = ps3_system_bus_dev_to_vuart_drv(dev); | ||
1196 | |||
1197 | BUG_ON(!drv); | ||
1198 | |||
1199 | if (drv->shutdown) | ||
1200 | drv->shutdown(dev); | ||
1201 | else if (drv->remove) { | ||
1202 | dev_dbg(&dev->core, "%s:%d: no shutdown, calling remove\n", | ||
1203 | __func__, __LINE__); | ||
1204 | drv->remove(dev); | ||
1205 | } else { | ||
1206 | dev_dbg(&dev->core, "%s:%d: no shutdown method\n", __func__, | ||
1207 | __LINE__); | ||
1208 | BUG(); | ||
1209 | } | ||
1210 | |||
1211 | ps3_vuart_cleanup(dev); | ||
1212 | |||
1213 | dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); | ||
1214 | |||
1215 | up(&vuart_bus_priv.probe_mutex); | ||
1216 | return 0; | ||
1217 | } | ||
1218 | |||
1219 | static int __init ps3_vuart_bus_init(void) | ||
1220 | { | ||
1221 | pr_debug("%s:%d:\n", __func__, __LINE__); | ||
1222 | |||
1223 | if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) | ||
1224 | return -ENODEV; | ||
1225 | |||
1226 | init_MUTEX(&vuart_bus_priv.probe_mutex); | ||
1227 | |||
1228 | return 0; | ||
1229 | } | ||
1230 | |||
1231 | static void __exit ps3_vuart_bus_exit(void) | ||
1232 | { | ||
1233 | pr_debug("%s:%d:\n", __func__, __LINE__); | ||
1234 | } | ||
1235 | |||
1236 | core_initcall(ps3_vuart_bus_init); | ||
1237 | module_exit(ps3_vuart_bus_exit); | ||
1238 | |||
1239 | /** | ||
1240 | * ps3_vuart_port_driver_register - Add a vuart port device driver. | ||
1241 | */ | ||
1242 | |||
1243 | int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv) | ||
1244 | { | ||
1245 | int result; | ||
1246 | |||
1247 | pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.core.name); | ||
1248 | |||
1249 | BUG_ON(!drv->core.match_id); | ||
1250 | BUG_ON(!drv->core.core.name); | ||
1251 | |||
1252 | drv->core.probe = ps3_vuart_probe; | ||
1253 | drv->core.remove = ps3_vuart_remove; | ||
1254 | drv->core.shutdown = ps3_vuart_shutdown; | ||
1255 | |||
1256 | result = ps3_system_bus_driver_register(&drv->core); | ||
1257 | return result; | ||
1258 | } | ||
1259 | EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register); | ||
1260 | |||
1261 | /** | ||
1262 | * ps3_vuart_port_driver_unregister - Remove a vuart port device driver. | ||
1263 | */ | ||
1264 | |||
1265 | void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv) | ||
1266 | { | ||
1267 | pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.core.name); | ||
1268 | ps3_system_bus_driver_unregister(&drv->core); | ||
1269 | } | ||
1270 | EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_unregister); | ||