diff options
Diffstat (limited to 'drivers/ps3')
-rw-r--r-- | drivers/ps3/Makefile | 1 | ||||
-rw-r--r-- | drivers/ps3/vuart.c | 965 | ||||
-rw-r--r-- | drivers/ps3/vuart.h | 94 |
3 files changed, 1060 insertions, 0 deletions
diff --git a/drivers/ps3/Makefile b/drivers/ps3/Makefile index b52d547b7a78..8433eb7562cb 100644 --- a/drivers/ps3/Makefile +++ b/drivers/ps3/Makefile | |||
@@ -1 +1,2 @@ | |||
1 | obj-y += system-bus.o | 1 | obj-y += system-bus.o |
2 | obj-$(CONFIG_PS3_VUART) += vuart.o | ||
diff --git a/drivers/ps3/vuart.c b/drivers/ps3/vuart.c new file mode 100644 index 000000000000..6974f65bcda5 --- /dev/null +++ b/drivers/ps3/vuart.c | |||
@@ -0,0 +1,965 @@ | |||
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 <asm/ps3.h> | ||
25 | |||
26 | #include <asm/lv1call.h> | ||
27 | #include <asm/bitops.h> | ||
28 | |||
29 | #include "vuart.h" | ||
30 | |||
31 | MODULE_AUTHOR("Sony Corporation"); | ||
32 | MODULE_LICENSE("GPL v2"); | ||
33 | MODULE_DESCRIPTION("ps3 vuart"); | ||
34 | |||
35 | /** | ||
36 | * vuart - An inter-partition data link service. | ||
37 | * port 0: PS3 AV Settings. | ||
38 | * port 2: PS3 System Manager. | ||
39 | * | ||
40 | * The vuart provides a bi-directional byte stream data link between logical | ||
41 | * partitions. Its primary role is as a communications link between the guest | ||
42 | * OS and the system policy module. The current HV does not support any | ||
43 | * connections other than those listed. | ||
44 | */ | ||
45 | |||
46 | enum {PORT_COUNT = 3,}; | ||
47 | |||
48 | enum vuart_param { | ||
49 | PARAM_TX_TRIGGER = 0, | ||
50 | PARAM_RX_TRIGGER = 1, | ||
51 | PARAM_INTERRUPT_MASK = 2, | ||
52 | PARAM_RX_BUF_SIZE = 3, /* read only */ | ||
53 | PARAM_RX_BYTES = 4, /* read only */ | ||
54 | PARAM_TX_BUF_SIZE = 5, /* read only */ | ||
55 | PARAM_TX_BYTES = 6, /* read only */ | ||
56 | PARAM_INTERRUPT_STATUS = 7, /* read only */ | ||
57 | }; | ||
58 | |||
59 | enum vuart_interrupt_bit { | ||
60 | INTERRUPT_BIT_TX = 0, | ||
61 | INTERRUPT_BIT_RX = 1, | ||
62 | INTERRUPT_BIT_DISCONNECT = 2, | ||
63 | }; | ||
64 | |||
65 | enum vuart_interrupt_mask { | ||
66 | INTERRUPT_MASK_TX = 1, | ||
67 | INTERRUPT_MASK_RX = 2, | ||
68 | INTERRUPT_MASK_DISCONNECT = 4, | ||
69 | }; | ||
70 | |||
71 | /** | ||
72 | * struct ports_bmp - bitmap indicating ports needing service. | ||
73 | * | ||
74 | * A 256 bit read only bitmap indicating ports needing service. Do not write | ||
75 | * to these bits. Must not cross a page boundary. | ||
76 | */ | ||
77 | |||
78 | struct ports_bmp { | ||
79 | u64 status; | ||
80 | u64 unused[3]; | ||
81 | } __attribute__ ((aligned (32))); | ||
82 | |||
83 | /* redefine dev_dbg to do a syntax check */ | ||
84 | |||
85 | #if !defined(DEBUG) | ||
86 | #undef dev_dbg | ||
87 | static inline int __attribute__ ((format (printf, 2, 3))) dev_dbg( | ||
88 | const struct device *_dev, const char *fmt, ...) {return 0;} | ||
89 | #endif | ||
90 | |||
91 | #define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__) | ||
92 | static void __attribute__ ((unused)) _dump_ports_bmp( | ||
93 | const struct ports_bmp* bmp, const char* func, int line) | ||
94 | { | ||
95 | pr_debug("%s:%d: ports_bmp: %016lxh\n", func, line, bmp->status); | ||
96 | } | ||
97 | |||
98 | static int ps3_vuart_match_id_to_port(enum ps3_match_id match_id, | ||
99 | unsigned int *port_number) | ||
100 | { | ||
101 | switch(match_id) { | ||
102 | case PS3_MATCH_ID_AV_SETTINGS: | ||
103 | *port_number = 0; | ||
104 | return 0; | ||
105 | case PS3_MATCH_ID_SYSTEM_MANAGER: | ||
106 | *port_number = 2; | ||
107 | return 0; | ||
108 | default: | ||
109 | WARN_ON(1); | ||
110 | *port_number = UINT_MAX; | ||
111 | return -EINVAL; | ||
112 | }; | ||
113 | } | ||
114 | |||
115 | #define dump_port_params(_b) _dump_port_params(_b, __func__, __LINE__) | ||
116 | static void __attribute__ ((unused)) _dump_port_params(unsigned int port_number, | ||
117 | const char* func, int line) | ||
118 | { | ||
119 | #if defined(DEBUG) | ||
120 | static const char *strings[] = { | ||
121 | "tx_trigger ", | ||
122 | "rx_trigger ", | ||
123 | "interrupt_mask ", | ||
124 | "rx_buf_size ", | ||
125 | "rx_bytes ", | ||
126 | "tx_buf_size ", | ||
127 | "tx_bytes ", | ||
128 | "interrupt_status", | ||
129 | }; | ||
130 | int result; | ||
131 | unsigned int i; | ||
132 | u64 value; | ||
133 | |||
134 | for (i = 0; i < ARRAY_SIZE(strings); i++) { | ||
135 | result = lv1_get_virtual_uart_param(port_number, i, &value); | ||
136 | |||
137 | if (result) { | ||
138 | pr_debug("%s:%d: port_%u: %s failed: %s\n", func, line, | ||
139 | port_number, strings[i], ps3_result(result)); | ||
140 | continue; | ||
141 | } | ||
142 | pr_debug("%s:%d: port_%u: %s = %lxh\n", | ||
143 | func, line, port_number, strings[i], value); | ||
144 | } | ||
145 | #endif | ||
146 | } | ||
147 | |||
148 | struct vuart_triggers { | ||
149 | unsigned long rx; | ||
150 | unsigned long tx; | ||
151 | }; | ||
152 | |||
153 | int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, | ||
154 | struct vuart_triggers *trig) | ||
155 | { | ||
156 | int result; | ||
157 | unsigned long size; | ||
158 | unsigned long val; | ||
159 | |||
160 | result = lv1_get_virtual_uart_param(dev->port_number, | ||
161 | PARAM_TX_TRIGGER, &trig->tx); | ||
162 | |||
163 | if (result) { | ||
164 | dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n", | ||
165 | __func__, __LINE__, ps3_result(result)); | ||
166 | return result; | ||
167 | } | ||
168 | |||
169 | result = lv1_get_virtual_uart_param(dev->port_number, | ||
170 | PARAM_RX_BUF_SIZE, &size); | ||
171 | |||
172 | if (result) { | ||
173 | dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n", | ||
174 | __func__, __LINE__, ps3_result(result)); | ||
175 | return result; | ||
176 | } | ||
177 | |||
178 | result = lv1_get_virtual_uart_param(dev->port_number, | ||
179 | PARAM_RX_TRIGGER, &val); | ||
180 | |||
181 | if (result) { | ||
182 | dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n", | ||
183 | __func__, __LINE__, ps3_result(result)); | ||
184 | return result; | ||
185 | } | ||
186 | |||
187 | trig->rx = size - val; | ||
188 | |||
189 | dev_dbg(&dev->core, "%s:%d: tx %lxh, rx %lxh\n", __func__, __LINE__, | ||
190 | trig->tx, trig->rx); | ||
191 | |||
192 | return result; | ||
193 | } | ||
194 | |||
195 | int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, | ||
196 | unsigned int rx) | ||
197 | { | ||
198 | int result; | ||
199 | unsigned long size; | ||
200 | |||
201 | result = lv1_set_virtual_uart_param(dev->port_number, | ||
202 | PARAM_TX_TRIGGER, tx); | ||
203 | |||
204 | if (result) { | ||
205 | dev_dbg(&dev->core, "%s:%d: tx_trigger failed: %s\n", | ||
206 | __func__, __LINE__, ps3_result(result)); | ||
207 | return result; | ||
208 | } | ||
209 | |||
210 | result = lv1_get_virtual_uart_param(dev->port_number, | ||
211 | PARAM_RX_BUF_SIZE, &size); | ||
212 | |||
213 | if (result) { | ||
214 | dev_dbg(&dev->core, "%s:%d: tx_buf_size failed: %s\n", | ||
215 | __func__, __LINE__, ps3_result(result)); | ||
216 | return result; | ||
217 | } | ||
218 | |||
219 | result = lv1_set_virtual_uart_param(dev->port_number, | ||
220 | PARAM_RX_TRIGGER, size - rx); | ||
221 | |||
222 | if (result) { | ||
223 | dev_dbg(&dev->core, "%s:%d: rx_trigger failed: %s\n", | ||
224 | __func__, __LINE__, ps3_result(result)); | ||
225 | return result; | ||
226 | } | ||
227 | |||
228 | dev_dbg(&dev->core, "%s:%d: tx %xh, rx %xh\n", __func__, __LINE__, | ||
229 | tx, rx); | ||
230 | |||
231 | return result; | ||
232 | } | ||
233 | |||
234 | static int ps3_vuart_get_rx_bytes_waiting(struct ps3_vuart_port_device *dev, | ||
235 | unsigned long *bytes_waiting) | ||
236 | { | ||
237 | int result = lv1_get_virtual_uart_param(dev->port_number, | ||
238 | PARAM_RX_BYTES, bytes_waiting); | ||
239 | |||
240 | if (result) | ||
241 | dev_dbg(&dev->core, "%s:%d: rx_bytes failed: %s\n", | ||
242 | __func__, __LINE__, ps3_result(result)); | ||
243 | |||
244 | dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, | ||
245 | *bytes_waiting); | ||
246 | return result; | ||
247 | } | ||
248 | |||
249 | static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev, | ||
250 | unsigned long mask) | ||
251 | { | ||
252 | int result; | ||
253 | |||
254 | dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, mask); | ||
255 | |||
256 | dev->interrupt_mask = mask; | ||
257 | |||
258 | result = lv1_set_virtual_uart_param(dev->port_number, | ||
259 | PARAM_INTERRUPT_MASK, dev->interrupt_mask); | ||
260 | |||
261 | if (result) | ||
262 | dev_dbg(&dev->core, "%s:%d: interrupt_mask failed: %s\n", | ||
263 | __func__, __LINE__, ps3_result(result)); | ||
264 | |||
265 | return result; | ||
266 | } | ||
267 | |||
268 | static int ps3_vuart_get_interrupt_mask(struct ps3_vuart_port_device *dev, | ||
269 | unsigned long *status) | ||
270 | { | ||
271 | int result = lv1_get_virtual_uart_param(dev->port_number, | ||
272 | PARAM_INTERRUPT_STATUS, status); | ||
273 | |||
274 | if (result) | ||
275 | dev_dbg(&dev->core, "%s:%d: interrupt_status failed: %s\n", | ||
276 | __func__, __LINE__, ps3_result(result)); | ||
277 | |||
278 | dev_dbg(&dev->core, "%s:%d: m %lxh, s %lxh, m&s %lxh\n", | ||
279 | __func__, __LINE__, dev->interrupt_mask, *status, | ||
280 | dev->interrupt_mask & *status); | ||
281 | |||
282 | return result; | ||
283 | } | ||
284 | |||
285 | int ps3_vuart_enable_interrupt_tx(struct ps3_vuart_port_device *dev) | ||
286 | { | ||
287 | return (dev->interrupt_mask & INTERRUPT_MASK_TX) ? 0 | ||
288 | : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask | ||
289 | | INTERRUPT_MASK_TX); | ||
290 | } | ||
291 | |||
292 | int ps3_vuart_enable_interrupt_rx(struct ps3_vuart_port_device *dev) | ||
293 | { | ||
294 | return (dev->interrupt_mask & INTERRUPT_MASK_RX) ? 0 | ||
295 | : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask | ||
296 | | INTERRUPT_MASK_RX); | ||
297 | } | ||
298 | |||
299 | int ps3_vuart_enable_interrupt_disconnect(struct ps3_vuart_port_device *dev) | ||
300 | { | ||
301 | return (dev->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0 | ||
302 | : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask | ||
303 | | INTERRUPT_MASK_DISCONNECT); | ||
304 | } | ||
305 | |||
306 | int ps3_vuart_disable_interrupt_tx(struct ps3_vuart_port_device *dev) | ||
307 | { | ||
308 | return (dev->interrupt_mask & INTERRUPT_MASK_TX) | ||
309 | ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask | ||
310 | & ~INTERRUPT_MASK_TX) : 0; | ||
311 | } | ||
312 | |||
313 | int ps3_vuart_disable_interrupt_rx(struct ps3_vuart_port_device *dev) | ||
314 | { | ||
315 | return (dev->interrupt_mask & INTERRUPT_MASK_RX) | ||
316 | ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask | ||
317 | & ~INTERRUPT_MASK_RX) : 0; | ||
318 | } | ||
319 | |||
320 | int ps3_vuart_disable_interrupt_disconnect(struct ps3_vuart_port_device *dev) | ||
321 | { | ||
322 | return (dev->interrupt_mask & INTERRUPT_MASK_DISCONNECT) | ||
323 | ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask | ||
324 | & ~INTERRUPT_MASK_DISCONNECT) : 0; | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * ps3_vuart_raw_write - Low level write helper. | ||
329 | * | ||
330 | * Do not call ps3_vuart_raw_write directly, use ps3_vuart_write. | ||
331 | */ | ||
332 | |||
333 | static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev, | ||
334 | const void* buf, unsigned int bytes, unsigned long *bytes_written) | ||
335 | { | ||
336 | int result; | ||
337 | |||
338 | dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes); | ||
339 | |||
340 | result = lv1_write_virtual_uart(dev->port_number, | ||
341 | ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_written); | ||
342 | |||
343 | if (result) { | ||
344 | dev_dbg(&dev->core, "%s:%d: lv1_write_virtual_uart failed: " | ||
345 | "%s\n", __func__, __LINE__, ps3_result(result)); | ||
346 | return result; | ||
347 | } | ||
348 | |||
349 | dev->stats.bytes_written += *bytes_written; | ||
350 | |||
351 | dev_dbg(&dev->core, "%s:%d: wrote %lxh/%xh=>%lxh\n", __func__, | ||
352 | __LINE__, *bytes_written, bytes, dev->stats.bytes_written); | ||
353 | |||
354 | return result; | ||
355 | } | ||
356 | |||
357 | /** | ||
358 | * ps3_vuart_raw_read - Low level read helper. | ||
359 | * | ||
360 | * Do not call ps3_vuart_raw_read directly, use ps3_vuart_read. | ||
361 | */ | ||
362 | |||
363 | static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf, | ||
364 | unsigned int bytes, unsigned long *bytes_read) | ||
365 | { | ||
366 | int result; | ||
367 | |||
368 | dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes); | ||
369 | |||
370 | result = lv1_read_virtual_uart(dev->port_number, | ||
371 | ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_read); | ||
372 | |||
373 | if (result) { | ||
374 | dev_dbg(&dev->core, "%s:%d: lv1_read_virtual_uart failed: %s\n", | ||
375 | __func__, __LINE__, ps3_result(result)); | ||
376 | return result; | ||
377 | } | ||
378 | |||
379 | dev->stats.bytes_read += *bytes_read; | ||
380 | |||
381 | dev_dbg(&dev->core, "%s:%d: read %lxh/%xh=>%lxh\n", __func__, __LINE__, | ||
382 | *bytes_read, bytes, dev->stats.bytes_read); | ||
383 | |||
384 | return result; | ||
385 | } | ||
386 | |||
387 | /** | ||
388 | * struct list_buffer - An element for a port device fifo buffer list. | ||
389 | */ | ||
390 | |||
391 | struct list_buffer { | ||
392 | struct list_head link; | ||
393 | const unsigned char *head; | ||
394 | const unsigned char *tail; | ||
395 | unsigned long dbg_number; | ||
396 | unsigned char data[]; | ||
397 | }; | ||
398 | |||
399 | /** | ||
400 | * ps3_vuart_write - the entry point for writing data to a port | ||
401 | * | ||
402 | * If the port is idle on entry as much of the incoming data is written to | ||
403 | * the port as the port will accept. Otherwise a list buffer is created | ||
404 | * and any remaning incoming data is copied to that buffer. The buffer is | ||
405 | * then enqueued for transmision via the transmit interrupt. | ||
406 | */ | ||
407 | |||
408 | int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, | ||
409 | unsigned int bytes) | ||
410 | { | ||
411 | static unsigned long dbg_number; | ||
412 | int result; | ||
413 | unsigned long flags; | ||
414 | struct list_buffer *lb; | ||
415 | |||
416 | dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, | ||
417 | bytes, bytes); | ||
418 | |||
419 | spin_lock_irqsave(&dev->tx_list.lock, flags); | ||
420 | |||
421 | if (list_empty(&dev->tx_list.head)) { | ||
422 | unsigned long bytes_written; | ||
423 | |||
424 | result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written); | ||
425 | |||
426 | spin_unlock_irqrestore(&dev->tx_list.lock, flags); | ||
427 | |||
428 | if (result) { | ||
429 | dev_dbg(&dev->core, | ||
430 | "%s:%d: ps3_vuart_raw_write failed\n", | ||
431 | __func__, __LINE__); | ||
432 | return result; | ||
433 | } | ||
434 | |||
435 | if (bytes_written == bytes) { | ||
436 | dev_dbg(&dev->core, "%s:%d: wrote %xh bytes\n", | ||
437 | __func__, __LINE__, bytes); | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | bytes -= bytes_written; | ||
442 | buf += bytes_written; | ||
443 | } else | ||
444 | spin_unlock_irqrestore(&dev->tx_list.lock, flags); | ||
445 | |||
446 | lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL); | ||
447 | |||
448 | if (!lb) { | ||
449 | return -ENOMEM; | ||
450 | } | ||
451 | |||
452 | memcpy(lb->data, buf, bytes); | ||
453 | lb->head = lb->data; | ||
454 | lb->tail = lb->data + bytes; | ||
455 | lb->dbg_number = ++dbg_number; | ||
456 | |||
457 | spin_lock_irqsave(&dev->tx_list.lock, flags); | ||
458 | list_add_tail(&lb->link, &dev->tx_list.head); | ||
459 | ps3_vuart_enable_interrupt_tx(dev); | ||
460 | spin_unlock_irqrestore(&dev->tx_list.lock, flags); | ||
461 | |||
462 | dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n", | ||
463 | __func__, __LINE__, lb->dbg_number, bytes); | ||
464 | |||
465 | return 0; | ||
466 | } | ||
467 | |||
468 | /** | ||
469 | * ps3_vuart_read - the entry point for reading data from a port | ||
470 | * | ||
471 | * If enough bytes to satisfy the request are held in the buffer list those | ||
472 | * bytes are dequeued and copied to the caller's buffer. Emptied list buffers | ||
473 | * are retiered. If the request cannot be statified by bytes held in the list | ||
474 | * buffers -EAGAIN is returned. | ||
475 | */ | ||
476 | |||
477 | int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, | ||
478 | unsigned int bytes) | ||
479 | { | ||
480 | unsigned long flags; | ||
481 | struct list_buffer *lb, *n; | ||
482 | unsigned long bytes_read; | ||
483 | |||
484 | dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, | ||
485 | bytes, bytes); | ||
486 | |||
487 | spin_lock_irqsave(&dev->rx_list.lock, flags); | ||
488 | |||
489 | if (dev->rx_list.bytes_held < bytes) { | ||
490 | spin_unlock_irqrestore(&dev->rx_list.lock, flags); | ||
491 | dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n", | ||
492 | __func__, __LINE__, bytes - dev->rx_list.bytes_held); | ||
493 | return -EAGAIN; | ||
494 | } | ||
495 | |||
496 | list_for_each_entry_safe(lb, n, &dev->rx_list.head, link) { | ||
497 | bytes_read = min((unsigned int)(lb->tail - lb->head), bytes); | ||
498 | |||
499 | memcpy(buf, lb->head, bytes_read); | ||
500 | buf += bytes_read; | ||
501 | bytes -= bytes_read; | ||
502 | dev->rx_list.bytes_held -= bytes_read; | ||
503 | |||
504 | if (bytes_read < lb->tail - lb->head) { | ||
505 | lb->head += bytes_read; | ||
506 | spin_unlock_irqrestore(&dev->rx_list.lock, flags); | ||
507 | |||
508 | dev_dbg(&dev->core, | ||
509 | "%s:%d: dequeued buf_%lu, %lxh bytes\n", | ||
510 | __func__, __LINE__, lb->dbg_number, bytes_read); | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | dev_dbg(&dev->core, "%s:%d free buf_%lu\n", __func__, __LINE__, | ||
515 | lb->dbg_number); | ||
516 | |||
517 | list_del(&lb->link); | ||
518 | kfree(lb); | ||
519 | } | ||
520 | spin_unlock_irqrestore(&dev->rx_list.lock, flags); | ||
521 | |||
522 | dev_dbg(&dev->core, "%s:%d: dequeued buf_%lu, %xh bytes\n", | ||
523 | __func__, __LINE__, lb->dbg_number, bytes); | ||
524 | |||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | /** | ||
529 | * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler | ||
530 | * | ||
531 | * Services the transmit interrupt for the port. Writes as much data from the | ||
532 | * buffer list as the port will accept. Retires any emptied list buffers and | ||
533 | * adjusts the final list buffer state for a partial write. | ||
534 | */ | ||
535 | |||
536 | static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev) | ||
537 | { | ||
538 | int result = 0; | ||
539 | unsigned long flags; | ||
540 | struct list_buffer *lb, *n; | ||
541 | unsigned long bytes_total = 0; | ||
542 | |||
543 | dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); | ||
544 | |||
545 | spin_lock_irqsave(&dev->tx_list.lock, flags); | ||
546 | |||
547 | list_for_each_entry_safe(lb, n, &dev->tx_list.head, link) { | ||
548 | |||
549 | unsigned long bytes_written; | ||
550 | |||
551 | result = ps3_vuart_raw_write(dev, lb->head, lb->tail - lb->head, | ||
552 | &bytes_written); | ||
553 | |||
554 | if (result) { | ||
555 | dev_dbg(&dev->core, | ||
556 | "%s:%d: ps3_vuart_raw_write failed\n", | ||
557 | __func__, __LINE__); | ||
558 | break; | ||
559 | } | ||
560 | |||
561 | bytes_total += bytes_written; | ||
562 | |||
563 | if (bytes_written < lb->tail - lb->head) { | ||
564 | lb->head += bytes_written; | ||
565 | dev_dbg(&dev->core, | ||
566 | "%s:%d cleared buf_%lu, %lxh bytes\n", | ||
567 | __func__, __LINE__, lb->dbg_number, | ||
568 | bytes_written); | ||
569 | goto port_full; | ||
570 | } | ||
571 | |||
572 | dev_dbg(&dev->core, "%s:%d free buf_%lu\n", __func__, __LINE__, | ||
573 | lb->dbg_number); | ||
574 | |||
575 | list_del(&lb->link); | ||
576 | kfree(lb); | ||
577 | } | ||
578 | |||
579 | ps3_vuart_disable_interrupt_tx(dev); | ||
580 | port_full: | ||
581 | spin_unlock_irqrestore(&dev->tx_list.lock, flags); | ||
582 | dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n", | ||
583 | __func__, __LINE__, bytes_total); | ||
584 | return result; | ||
585 | } | ||
586 | |||
587 | /** | ||
588 | * ps3_vuart_handle_interrupt_rx - third stage receive interrupt handler | ||
589 | * | ||
590 | * Services the receive interrupt for the port. Creates a list buffer and | ||
591 | * copies all waiting port data to that buffer and enqueues the buffer in the | ||
592 | * buffer list. Buffer list data is dequeued via ps3_vuart_read. | ||
593 | */ | ||
594 | |||
595 | static int ps3_vuart_handle_interrupt_rx(struct ps3_vuart_port_device *dev) | ||
596 | { | ||
597 | static unsigned long dbg_number; | ||
598 | int result = 0; | ||
599 | unsigned long flags; | ||
600 | struct list_buffer *lb; | ||
601 | unsigned long bytes; | ||
602 | |||
603 | dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); | ||
604 | |||
605 | result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes); | ||
606 | |||
607 | if (result) | ||
608 | return -EIO; | ||
609 | |||
610 | BUG_ON(!bytes); | ||
611 | |||
612 | /* add some extra space for recently arrived data */ | ||
613 | |||
614 | bytes += 128; | ||
615 | |||
616 | lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_ATOMIC); | ||
617 | |||
618 | if (!lb) | ||
619 | return -ENOMEM; | ||
620 | |||
621 | ps3_vuart_raw_read(dev, lb->data, bytes, &bytes); | ||
622 | |||
623 | lb->head = lb->data; | ||
624 | lb->tail = lb->data + bytes; | ||
625 | lb->dbg_number = ++dbg_number; | ||
626 | |||
627 | spin_lock_irqsave(&dev->rx_list.lock, flags); | ||
628 | list_add_tail(&lb->link, &dev->rx_list.head); | ||
629 | dev->rx_list.bytes_held += bytes; | ||
630 | spin_unlock_irqrestore(&dev->rx_list.lock, flags); | ||
631 | |||
632 | dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %lxh bytes\n", | ||
633 | __func__, __LINE__, lb->dbg_number, bytes); | ||
634 | |||
635 | return 0; | ||
636 | } | ||
637 | |||
638 | static int ps3_vuart_handle_interrupt_disconnect( | ||
639 | struct ps3_vuart_port_device *dev) | ||
640 | { | ||
641 | dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); | ||
642 | BUG_ON("no support"); | ||
643 | return -1; | ||
644 | } | ||
645 | |||
646 | /** | ||
647 | * ps3_vuart_handle_port_interrupt - second stage interrupt handler | ||
648 | * | ||
649 | * Services any pending interrupt types for the port. Passes control to the | ||
650 | * third stage type specific interrupt handler. Returns control to the first | ||
651 | * stage handler after one iteration. | ||
652 | */ | ||
653 | |||
654 | static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) | ||
655 | { | ||
656 | int result; | ||
657 | unsigned long status; | ||
658 | |||
659 | result = ps3_vuart_get_interrupt_mask(dev, &status); | ||
660 | |||
661 | if (result) | ||
662 | return result; | ||
663 | |||
664 | dev_dbg(&dev->core, "%s:%d: status: %lxh\n", __func__, __LINE__, | ||
665 | status); | ||
666 | |||
667 | if (status & INTERRUPT_MASK_DISCONNECT) { | ||
668 | dev->stats.disconnect_interrupts++; | ||
669 | result = ps3_vuart_handle_interrupt_disconnect(dev); | ||
670 | if (result) | ||
671 | ps3_vuart_disable_interrupt_disconnect(dev); | ||
672 | } | ||
673 | |||
674 | if (status & INTERRUPT_MASK_TX) { | ||
675 | dev->stats.tx_interrupts++; | ||
676 | result = ps3_vuart_handle_interrupt_tx(dev); | ||
677 | if (result) | ||
678 | ps3_vuart_disable_interrupt_tx(dev); | ||
679 | } | ||
680 | |||
681 | if (status & INTERRUPT_MASK_RX) { | ||
682 | dev->stats.rx_interrupts++; | ||
683 | result = ps3_vuart_handle_interrupt_rx(dev); | ||
684 | if (result) | ||
685 | ps3_vuart_disable_interrupt_rx(dev); | ||
686 | } | ||
687 | |||
688 | return 0; | ||
689 | } | ||
690 | |||
691 | struct vuart_private { | ||
692 | unsigned int in_use; | ||
693 | unsigned int virq; | ||
694 | struct ps3_vuart_port_device *devices[PORT_COUNT]; | ||
695 | const struct ports_bmp bmp; | ||
696 | }; | ||
697 | |||
698 | /** | ||
699 | * ps3_vuart_irq_handler - first stage interrupt handler | ||
700 | * | ||
701 | * Loops finding any interrupting port and its associated instance data. | ||
702 | * Passes control to the second stage port specific interrupt handler. Loops | ||
703 | * until all outstanding interrupts are serviced. | ||
704 | */ | ||
705 | |||
706 | static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private) | ||
707 | { | ||
708 | struct vuart_private *private; | ||
709 | |||
710 | BUG_ON(!_private); | ||
711 | private = (struct vuart_private *)_private; | ||
712 | |||
713 | while (1) { | ||
714 | unsigned int port; | ||
715 | |||
716 | dump_ports_bmp(&private->bmp); | ||
717 | |||
718 | port = (BITS_PER_LONG - 1) - __ilog2(private->bmp.status); | ||
719 | |||
720 | if (port == BITS_PER_LONG) | ||
721 | break; | ||
722 | |||
723 | BUG_ON(port >= PORT_COUNT); | ||
724 | BUG_ON(!private->devices[port]); | ||
725 | |||
726 | ps3_vuart_handle_port_interrupt(private->devices[port]); | ||
727 | } | ||
728 | |||
729 | return IRQ_HANDLED; | ||
730 | } | ||
731 | |||
732 | static int ps3_vuart_match(struct device *_dev, struct device_driver *_drv) | ||
733 | { | ||
734 | int result; | ||
735 | struct ps3_vuart_port_driver *drv = to_ps3_vuart_port_driver(_drv); | ||
736 | struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); | ||
737 | |||
738 | result = dev->match_id == drv->match_id; | ||
739 | |||
740 | dev_info(&dev->core, "%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__, | ||
741 | __LINE__, dev->match_id, dev->core.bus_id, drv->match_id, | ||
742 | drv->core.name, (result ? "match" : "miss")); | ||
743 | |||
744 | return result; | ||
745 | } | ||
746 | |||
747 | static struct vuart_private vuart_private; | ||
748 | |||
749 | static int ps3_vuart_probe(struct device *_dev) | ||
750 | { | ||
751 | int result; | ||
752 | unsigned long tmp; | ||
753 | struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); | ||
754 | struct ps3_vuart_port_driver *drv = | ||
755 | to_ps3_vuart_port_driver(_dev->driver); | ||
756 | |||
757 | dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); | ||
758 | |||
759 | BUG_ON(!drv); | ||
760 | |||
761 | result = ps3_vuart_match_id_to_port(dev->match_id, &dev->port_number); | ||
762 | |||
763 | if (result) { | ||
764 | dev_dbg(&dev->core, "%s:%d: unknown match_id (%d)\n", | ||
765 | __func__, __LINE__, dev->match_id); | ||
766 | result = -EINVAL; | ||
767 | goto fail_match; | ||
768 | } | ||
769 | |||
770 | if (vuart_private.devices[dev->port_number]) { | ||
771 | dev_dbg(&dev->core, "%s:%d: port busy (%d)\n", __func__, | ||
772 | __LINE__, dev->port_number); | ||
773 | result = -EBUSY; | ||
774 | goto fail_match; | ||
775 | } | ||
776 | |||
777 | vuart_private.devices[dev->port_number] = dev; | ||
778 | |||
779 | INIT_LIST_HEAD(&dev->tx_list.head); | ||
780 | spin_lock_init(&dev->tx_list.lock); | ||
781 | INIT_LIST_HEAD(&dev->rx_list.head); | ||
782 | spin_lock_init(&dev->rx_list.lock); | ||
783 | |||
784 | vuart_private.in_use++; | ||
785 | if (vuart_private.in_use == 1) { | ||
786 | result = ps3_alloc_vuart_irq((void*)&vuart_private.bmp.status, | ||
787 | &vuart_private.virq); | ||
788 | |||
789 | if (result) { | ||
790 | dev_dbg(&dev->core, | ||
791 | "%s:%d: ps3_alloc_vuart_irq failed (%d)\n", | ||
792 | __func__, __LINE__, result); | ||
793 | result = -EPERM; | ||
794 | goto fail_alloc_irq; | ||
795 | } | ||
796 | |||
797 | result = request_irq(vuart_private.virq, ps3_vuart_irq_handler, | ||
798 | IRQF_DISABLED, "vuart", &vuart_private); | ||
799 | |||
800 | if (result) { | ||
801 | dev_info(&dev->core, "%s:%d: request_irq failed (%d)\n", | ||
802 | __func__, __LINE__, result); | ||
803 | goto fail_request_irq; | ||
804 | } | ||
805 | } | ||
806 | |||
807 | ps3_vuart_set_interrupt_mask(dev, INTERRUPT_MASK_RX); | ||
808 | |||
809 | /* clear stale pending interrupts */ | ||
810 | ps3_vuart_get_interrupt_mask(dev, &tmp); | ||
811 | |||
812 | ps3_vuart_set_triggers(dev, 1, 1); | ||
813 | |||
814 | if (drv->probe) | ||
815 | result = drv->probe(dev); | ||
816 | else { | ||
817 | result = 0; | ||
818 | dev_info(&dev->core, "%s:%d: no probe method\n", __func__, | ||
819 | __LINE__); | ||
820 | } | ||
821 | |||
822 | if (result) { | ||
823 | dev_dbg(&dev->core, "%s:%d: drv->probe failed\n", | ||
824 | __func__, __LINE__); | ||
825 | goto fail_probe; | ||
826 | } | ||
827 | |||
828 | return result; | ||
829 | |||
830 | fail_probe: | ||
831 | fail_request_irq: | ||
832 | vuart_private.in_use--; | ||
833 | if (!vuart_private.in_use) { | ||
834 | ps3_free_vuart_irq(vuart_private.virq); | ||
835 | vuart_private.virq = NO_IRQ; | ||
836 | } | ||
837 | fail_alloc_irq: | ||
838 | fail_match: | ||
839 | dev_dbg(&dev->core, "%s:%d failed\n", __func__, __LINE__); | ||
840 | return result; | ||
841 | } | ||
842 | |||
843 | static int ps3_vuart_remove(struct device *_dev) | ||
844 | { | ||
845 | struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); | ||
846 | struct ps3_vuart_port_driver *drv = | ||
847 | to_ps3_vuart_port_driver(_dev->driver); | ||
848 | |||
849 | dev_dbg(&dev->core, "%s:%d: %s\n", __func__, __LINE__, | ||
850 | dev->core.bus_id); | ||
851 | |||
852 | BUG_ON(vuart_private.in_use < 1); | ||
853 | |||
854 | if (drv->remove) | ||
855 | drv->remove(dev); | ||
856 | else | ||
857 | dev_dbg(&dev->core, "%s:%d: %s no remove method\n", __func__, | ||
858 | __LINE__, dev->core.bus_id); | ||
859 | |||
860 | vuart_private.in_use--; | ||
861 | |||
862 | if (!vuart_private.in_use) { | ||
863 | free_irq(vuart_private.virq, &vuart_private); | ||
864 | ps3_free_vuart_irq(vuart_private.virq); | ||
865 | vuart_private.virq = NO_IRQ; | ||
866 | } | ||
867 | return 0; | ||
868 | } | ||
869 | |||
870 | /** | ||
871 | * ps3_vuart - The vuart instance. | ||
872 | * | ||
873 | * The vuart is managed as a bus that port devices connect to. | ||
874 | */ | ||
875 | |||
876 | struct bus_type ps3_vuart = { | ||
877 | .name = "ps3_vuart", | ||
878 | .match = ps3_vuart_match, | ||
879 | .probe = ps3_vuart_probe, | ||
880 | .remove = ps3_vuart_remove, | ||
881 | }; | ||
882 | |||
883 | int __init ps3_vuart_init(void) | ||
884 | { | ||
885 | int result; | ||
886 | |||
887 | pr_debug("%s:%d:\n", __func__, __LINE__); | ||
888 | result = bus_register(&ps3_vuart); | ||
889 | BUG_ON(result); | ||
890 | return result; | ||
891 | } | ||
892 | |||
893 | void __exit ps3_vuart_exit(void) | ||
894 | { | ||
895 | pr_debug("%s:%d:\n", __func__, __LINE__); | ||
896 | bus_unregister(&ps3_vuart); | ||
897 | } | ||
898 | |||
899 | core_initcall(ps3_vuart_init); | ||
900 | module_exit(ps3_vuart_exit); | ||
901 | |||
902 | /** | ||
903 | * ps3_vuart_port_release_device - Remove a vuart port device. | ||
904 | */ | ||
905 | |||
906 | static void ps3_vuart_port_release_device(struct device *_dev) | ||
907 | { | ||
908 | struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); | ||
909 | #if defined(DEBUG) | ||
910 | memset(dev, 0xad, sizeof(struct ps3_vuart_port_device)); | ||
911 | #endif | ||
912 | kfree(dev); | ||
913 | } | ||
914 | |||
915 | /** | ||
916 | * ps3_vuart_port_device_register - Add a vuart port device. | ||
917 | */ | ||
918 | |||
919 | int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev) | ||
920 | { | ||
921 | int result; | ||
922 | static unsigned int dev_count = 1; | ||
923 | |||
924 | dev->core.parent = NULL; | ||
925 | dev->core.bus = &ps3_vuart; | ||
926 | dev->core.release = ps3_vuart_port_release_device; | ||
927 | |||
928 | snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "vuart_%02x", | ||
929 | dev_count++); | ||
930 | |||
931 | dev_dbg(&dev->core, "%s:%d register\n", __func__, __LINE__); | ||
932 | |||
933 | result = device_register(&dev->core); | ||
934 | |||
935 | return result; | ||
936 | } | ||
937 | |||
938 | EXPORT_SYMBOL_GPL(ps3_vuart_port_device_register); | ||
939 | |||
940 | /** | ||
941 | * ps3_vuart_port_driver_register - Add a vuart port device driver. | ||
942 | */ | ||
943 | |||
944 | int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv) | ||
945 | { | ||
946 | int result; | ||
947 | |||
948 | pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.name); | ||
949 | drv->core.bus = &ps3_vuart; | ||
950 | result = driver_register(&drv->core); | ||
951 | return result; | ||
952 | } | ||
953 | |||
954 | EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register); | ||
955 | |||
956 | /** | ||
957 | * ps3_vuart_port_driver_unregister - Remove a vuart port device driver. | ||
958 | */ | ||
959 | |||
960 | void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv) | ||
961 | { | ||
962 | driver_unregister(&drv->core); | ||
963 | } | ||
964 | |||
965 | EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_unregister); | ||
diff --git a/drivers/ps3/vuart.h b/drivers/ps3/vuart.h new file mode 100644 index 000000000000..28fd89f0c8aa --- /dev/null +++ b/drivers/ps3/vuart.h | |||
@@ -0,0 +1,94 @@ | |||
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 | #if !defined(_PS3_VUART_H) | ||
22 | #define _PS3_VUART_H | ||
23 | |||
24 | struct ps3_vuart_stats { | ||
25 | unsigned long bytes_written; | ||
26 | unsigned long bytes_read; | ||
27 | unsigned long tx_interrupts; | ||
28 | unsigned long rx_interrupts; | ||
29 | unsigned long disconnect_interrupts; | ||
30 | }; | ||
31 | |||
32 | /** | ||
33 | * struct ps3_vuart_port_device - a device on a vuart port | ||
34 | */ | ||
35 | |||
36 | struct ps3_vuart_port_device { | ||
37 | enum ps3_match_id match_id; | ||
38 | struct device core; | ||
39 | |||
40 | /* private driver variables */ | ||
41 | unsigned int port_number; | ||
42 | unsigned long interrupt_mask; | ||
43 | struct { | ||
44 | spinlock_t lock; | ||
45 | struct list_head head; | ||
46 | } tx_list; | ||
47 | struct { | ||
48 | unsigned long bytes_held; | ||
49 | spinlock_t lock; | ||
50 | struct list_head head; | ||
51 | } rx_list; | ||
52 | struct ps3_vuart_stats stats; | ||
53 | }; | ||
54 | |||
55 | /** | ||
56 | * struct ps3_vuart_port_driver - a driver for a device on a vuart port | ||
57 | */ | ||
58 | |||
59 | struct ps3_vuart_port_driver { | ||
60 | enum ps3_match_id match_id; | ||
61 | struct device_driver core; | ||
62 | int (*probe)(struct ps3_vuart_port_device *); | ||
63 | int (*remove)(struct ps3_vuart_port_device *); | ||
64 | int (*tx_event)(struct ps3_vuart_port_device *dev); | ||
65 | int (*rx_event)(struct ps3_vuart_port_device *dev); | ||
66 | int (*disconnect_event)(struct ps3_vuart_port_device *dev); | ||
67 | /* int (*suspend)(struct ps3_vuart_port_device *, pm_message_t); */ | ||
68 | /* int (*resume)(struct ps3_vuart_port_device *); */ | ||
69 | }; | ||
70 | |||
71 | int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev); | ||
72 | int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv); | ||
73 | void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv); | ||
74 | int ps3_vuart_write(struct ps3_vuart_port_device *dev, | ||
75 | const void* buf, unsigned int bytes); | ||
76 | int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, | ||
77 | unsigned int bytes); | ||
78 | static inline struct ps3_vuart_port_driver *to_ps3_vuart_port_driver( | ||
79 | struct device_driver *_drv) | ||
80 | { | ||
81 | return container_of(_drv, struct ps3_vuart_port_driver, core); | ||
82 | } | ||
83 | static inline struct ps3_vuart_port_device *to_ps3_vuart_port_device( | ||
84 | struct device *_dev) | ||
85 | { | ||
86 | return container_of(_dev, struct ps3_vuart_port_device, core); | ||
87 | } | ||
88 | |||
89 | int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, | ||
90 | unsigned int bytes); | ||
91 | int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, | ||
92 | unsigned int bytes); | ||
93 | |||
94 | #endif | ||