Commit | Line | Data |
---|---|---|
eb50fd3a | 1 | // SPDX-License-Identifier: GPL-2.0 |
79c822be | 2 | /* |
e68453ed | 3 | * UART driver for the Greybus "generic" UART module. |
79c822be GKH |
4 | * |
5 | * Copyright 2014 Google Inc. | |
a46e9671 | 6 | * Copyright 2014 Linaro Ltd. |
79c822be | 7 | * |
e68453ed GKH |
8 | * Heavily based on drivers/usb/class/cdc-acm.c and |
9 | * drivers/usb/serial/usb-serial.c. | |
79c822be | 10 | */ |
168db1cd | 11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
79c822be GKH |
12 | |
13 | #include <linux/kernel.h> | |
e68453ed | 14 | #include <linux/errno.h> |
79c822be | 15 | #include <linux/module.h> |
174cd4b1 | 16 | #include <linux/sched/signal.h> |
e68453ed | 17 | #include <linux/wait.h> |
79c822be | 18 | #include <linux/slab.h> |
e68453ed GKH |
19 | #include <linux/uaccess.h> |
20 | #include <linux/mutex.h> | |
79c822be GKH |
21 | #include <linux/tty.h> |
22 | #include <linux/serial.h> | |
23 | #include <linux/tty_driver.h> | |
24 | #include <linux/tty_flip.h> | |
ff45c265 | 25 | #include <linux/idr.h> |
7fabc884 MB |
26 | #include <linux/fs.h> |
27 | #include <linux/kdev_t.h> | |
8d6fbe9b AH |
28 | #include <linux/kfifo.h> |
29 | #include <linux/workqueue.h> | |
5dad5c31 | 30 | #include <linux/completion.h> |
ec0ad868 | 31 | #include <linux/greybus.h> |
e1e9dbdd | 32 | |
e54b106d | 33 | #include "gbphy.h" |
79c822be | 34 | |
4452b090 | 35 | #define GB_NUM_MINORS 16 /* 16 is more than enough */ |
7fabc884 | 36 | #define GB_NAME "ttyGB" |
79c822be | 37 | |
8d6fbe9b AH |
38 | #define GB_UART_WRITE_FIFO_SIZE PAGE_SIZE |
39 | #define GB_UART_WRITE_ROOM_MARGIN 1 /* leave some space in fifo */ | |
a8bc00fb | 40 | #define GB_UART_FIRMWARE_CREDITS 4096 |
5dad5c31 | 41 | #define GB_UART_CREDIT_WAIT_TIMEOUT_MSEC 10000 |
8d6fbe9b | 42 | |
79c822be | 43 | struct gb_tty { |
e54b106d | 44 | struct gbphy_device *gbphy_dev; |
79c822be | 45 | struct tty_port port; |
dd1c64ed | 46 | void *buffer; |
7f29aded | 47 | size_t buffer_payload_max; |
aed0bc6e | 48 | struct gb_connection *connection; |
1cfc667d | 49 | u16 cport_id; |
79c822be GKH |
50 | unsigned int minor; |
51 | unsigned char clocal; | |
e68453ed | 52 | bool disconnected; |
a18e1517 GKH |
53 | spinlock_t read_lock; |
54 | spinlock_t write_lock; | |
e68453ed GKH |
55 | struct async_icount iocount; |
56 | struct async_icount oldcount; | |
57 | wait_queue_head_t wioctl; | |
58 | struct mutex mutex; | |
ba4b099c BD |
59 | u8 ctrlin; /* input control lines */ |
60 | u8 ctrlout; /* output control lines */ | |
6064aeef | 61 | struct gb_uart_set_line_coding_request line_coding; |
8d6fbe9b AH |
62 | struct work_struct tx_work; |
63 | struct kfifo write_fifo; | |
64 | bool close_pending; | |
a8bc00fb | 65 | unsigned int credits; |
5dad5c31 | 66 | struct completion credits_complete; |
79c822be GKH |
67 | }; |
68 | ||
ff45c265 GKH |
69 | static struct tty_driver *gb_tty_driver; |
70 | static DEFINE_IDR(tty_minors); | |
71 | static DEFINE_MUTEX(table_lock); | |
4d980989 | 72 | |
c5f338c4 | 73 | static int gb_uart_receive_data_handler(struct gb_operation *op) |
4c025cf4 | 74 | { |
c5f338c4 | 75 | struct gb_connection *connection = op->connection; |
0ec30632 | 76 | struct gb_tty *gb_tty = gb_connection_get_data(connection); |
4c025cf4 | 77 | struct tty_port *port = &gb_tty->port; |
c5f338c4 JH |
78 | struct gb_message *request = op->request; |
79 | struct gb_uart_recv_data_request *receive_data; | |
4c025cf4 BD |
80 | u16 recv_data_size; |
81 | int count; | |
82 | unsigned long tty_flags = TTY_NORMAL; | |
83 | ||
c5f338c4 | 84 | if (request->payload_size < sizeof(*receive_data)) { |
e54b106d | 85 | dev_err(&gb_tty->gbphy_dev->dev, |
319b78c3 CL |
86 | "short receive-data request received (%zu < %zu)\n", |
87 | request->payload_size, sizeof(*receive_data)); | |
c5f338c4 JH |
88 | return -EINVAL; |
89 | } | |
90 | ||
91 | receive_data = op->request->payload; | |
4c025cf4 | 92 | recv_data_size = le16_to_cpu(receive_data->size); |
c5f338c4 JH |
93 | |
94 | if (recv_data_size != request->payload_size - sizeof(*receive_data)) { | |
e54b106d | 95 | dev_err(&gb_tty->gbphy_dev->dev, |
319b78c3 CL |
96 | "malformed receive-data request received (%u != %zu)\n", |
97 | recv_data_size, | |
98 | request->payload_size - sizeof(*receive_data)); | |
c5f338c4 JH |
99 | return -EINVAL; |
100 | } | |
101 | ||
102 | if (!recv_data_size) | |
4c025cf4 BD |
103 | return -EINVAL; |
104 | ||
105 | if (receive_data->flags) { | |
106 | if (receive_data->flags & GB_UART_RECV_FLAG_BREAK) | |
107 | tty_flags = TTY_BREAK; | |
108 | else if (receive_data->flags & GB_UART_RECV_FLAG_PARITY) | |
109 | tty_flags = TTY_PARITY; | |
110 | else if (receive_data->flags & GB_UART_RECV_FLAG_FRAMING) | |
111 | tty_flags = TTY_FRAME; | |
112 | ||
113 | /* overrun is special, not associated with a char */ | |
114 | if (receive_data->flags & GB_UART_RECV_FLAG_OVERRUN) | |
115 | tty_insert_flip_char(port, 0, TTY_OVERRUN); | |
116 | } | |
117 | count = tty_insert_flip_string_fixed_flag(port, receive_data->data, | |
118 | tty_flags, recv_data_size); | |
119 | if (count != recv_data_size) { | |
e54b106d | 120 | dev_err(&gb_tty->gbphy_dev->dev, |
4c025cf4 BD |
121 | "UART: RX 0x%08x bytes only wrote 0x%08x\n", |
122 | recv_data_size, count); | |
123 | } | |
124 | if (count) | |
125 | tty_flip_buffer_push(port); | |
126 | return 0; | |
127 | } | |
128 | ||
cb7f00ba | 129 | static int gb_uart_serial_state_handler(struct gb_operation *op) |
1c087015 BD |
130 | { |
131 | struct gb_connection *connection = op->connection; | |
0ec30632 | 132 | struct gb_tty *gb_tty = gb_connection_get_data(connection); |
1c087015 | 133 | struct gb_message *request = op->request; |
1c087015 | 134 | struct gb_uart_serial_state_request *serial_state; |
cb7f00ba JH |
135 | |
136 | if (request->payload_size < sizeof(*serial_state)) { | |
e54b106d | 137 | dev_err(&gb_tty->gbphy_dev->dev, |
319b78c3 CL |
138 | "short serial-state event received (%zu < %zu)\n", |
139 | request->payload_size, sizeof(*serial_state)); | |
cb7f00ba JH |
140 | return -EINVAL; |
141 | } | |
142 | ||
143 | serial_state = request->payload; | |
144 | gb_tty->ctrlin = serial_state->control; | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
a8bc00fb AH |
149 | static int gb_uart_receive_credits_handler(struct gb_operation *op) |
150 | { | |
151 | struct gb_connection *connection = op->connection; | |
152 | struct gb_tty *gb_tty = gb_connection_get_data(connection); | |
153 | struct gb_message *request = op->request; | |
154 | struct gb_uart_receive_credits_request *credit_request; | |
155 | unsigned long flags; | |
156 | unsigned int incoming_credits; | |
157 | int ret = 0; | |
158 | ||
159 | if (request->payload_size < sizeof(*credit_request)) { | |
160 | dev_err(&gb_tty->gbphy_dev->dev, | |
319b78c3 CL |
161 | "short receive_credits event received (%zu < %zu)\n", |
162 | request->payload_size, | |
163 | sizeof(*credit_request)); | |
a8bc00fb AH |
164 | return -EINVAL; |
165 | } | |
166 | ||
167 | credit_request = request->payload; | |
168 | incoming_credits = le16_to_cpu(credit_request->count); | |
169 | ||
170 | spin_lock_irqsave(&gb_tty->write_lock, flags); | |
171 | gb_tty->credits += incoming_credits; | |
172 | if (gb_tty->credits > GB_UART_FIRMWARE_CREDITS) { | |
173 | gb_tty->credits -= incoming_credits; | |
174 | ret = -EINVAL; | |
175 | } | |
176 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
177 | ||
178 | if (ret) { | |
179 | dev_err(&gb_tty->gbphy_dev->dev, | |
180 | "invalid number of incoming credits: %d\n", | |
181 | incoming_credits); | |
182 | return ret; | |
183 | } | |
184 | ||
185 | if (!gb_tty->close_pending) | |
186 | schedule_work(&gb_tty->tx_work); | |
187 | ||
188 | /* | |
189 | * the port the tty layer may be waiting for credits | |
190 | */ | |
191 | tty_port_tty_wakeup(&gb_tty->port); | |
192 | ||
5dad5c31 AH |
193 | if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) |
194 | complete(&gb_tty->credits_complete); | |
195 | ||
a8bc00fb AH |
196 | return ret; |
197 | } | |
198 | ||
7dbe1f49 | 199 | static int gb_uart_request_handler(struct gb_operation *op) |
cb7f00ba JH |
200 | { |
201 | struct gb_connection *connection = op->connection; | |
7dbe1f49 GKH |
202 | struct gb_tty *gb_tty = gb_connection_get_data(connection); |
203 | int type = op->type; | |
cb7f00ba | 204 | int ret; |
1c087015 BD |
205 | |
206 | switch (type) { | |
207 | case GB_UART_TYPE_RECEIVE_DATA: | |
c5f338c4 | 208 | ret = gb_uart_receive_data_handler(op); |
1c087015 BD |
209 | break; |
210 | case GB_UART_TYPE_SERIAL_STATE: | |
cb7f00ba | 211 | ret = gb_uart_serial_state_handler(op); |
1c087015 | 212 | break; |
a8bc00fb AH |
213 | case GB_UART_TYPE_RECEIVE_CREDITS: |
214 | ret = gb_uart_receive_credits_handler(op); | |
215 | break; | |
1c087015 | 216 | default: |
e54b106d | 217 | dev_err(&gb_tty->gbphy_dev->dev, |
b933fa4a | 218 | "unsupported unsolicited request: 0x%02x\n", type); |
1c087015 BD |
219 | ret = -EINVAL; |
220 | } | |
221 | ||
222 | return ret; | |
223 | } | |
224 | ||
8d6fbe9b | 225 | static void gb_uart_tx_write_work(struct work_struct *work) |
b4be4043 | 226 | { |
b4be4043 | 227 | struct gb_uart_send_data_request *request; |
8d6fbe9b AH |
228 | struct gb_tty *gb_tty; |
229 | unsigned long flags; | |
230 | unsigned int send_size; | |
563bd79b | 231 | int ret; |
b4be4043 | 232 | |
8d6fbe9b AH |
233 | gb_tty = container_of(work, struct gb_tty, tx_work); |
234 | request = gb_tty->buffer; | |
b4be4043 | 235 | |
8d6fbe9b AH |
236 | while (1) { |
237 | if (gb_tty->close_pending) | |
238 | break; | |
239 | ||
240 | spin_lock_irqsave(&gb_tty->write_lock, flags); | |
a8bc00fb AH |
241 | send_size = gb_tty->buffer_payload_max; |
242 | if (send_size > gb_tty->credits) | |
243 | send_size = gb_tty->credits; | |
244 | ||
8d6fbe9b | 245 | send_size = kfifo_out_peek(&gb_tty->write_fifo, |
319b78c3 CL |
246 | &request->data[0], |
247 | send_size); | |
8d6fbe9b AH |
248 | if (!send_size) { |
249 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
250 | break; | |
251 | } | |
252 | ||
a8bc00fb | 253 | gb_tty->credits -= send_size; |
8d6fbe9b AH |
254 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); |
255 | ||
256 | request->size = cpu_to_le16(send_size); | |
257 | ret = gb_operation_sync(gb_tty->connection, | |
258 | GB_UART_TYPE_SEND_DATA, | |
259 | request, sizeof(*request) + send_size, | |
260 | NULL, 0); | |
261 | if (ret) { | |
262 | dev_err(&gb_tty->gbphy_dev->dev, | |
263 | "send data error: %d\n", ret); | |
a8bc00fb AH |
264 | spin_lock_irqsave(&gb_tty->write_lock, flags); |
265 | gb_tty->credits += send_size; | |
266 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
8d6fbe9b AH |
267 | if (!gb_tty->close_pending) |
268 | schedule_work(work); | |
269 | return; | |
270 | } | |
271 | ||
272 | spin_lock_irqsave(&gb_tty->write_lock, flags); | |
273 | ret = kfifo_out(&gb_tty->write_fifo, &request->data[0], | |
274 | send_size); | |
275 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
276 | ||
277 | tty_port_tty_wakeup(&gb_tty->port); | |
278 | } | |
b4be4043 GKH |
279 | } |
280 | ||
9f240f20 | 281 | static int send_line_coding(struct gb_tty *tty) |
b4be4043 | 282 | { |
530430b7 | 283 | return gb_operation_sync(tty->connection, GB_UART_TYPE_SET_LINE_CODING, |
6064aeef JH |
284 | &tty->line_coding, sizeof(tty->line_coding), |
285 | NULL, 0); | |
b4be4043 GKH |
286 | } |
287 | ||
ba4b099c | 288 | static int send_control(struct gb_tty *gb_tty, u8 control) |
b4be4043 | 289 | { |
e51f1d1a | 290 | struct gb_uart_set_control_line_state_request request; |
b4be4043 | 291 | |
ba4b099c | 292 | request.control = control; |
62229a1b | 293 | return gb_operation_sync(gb_tty->connection, |
530430b7 | 294 | GB_UART_TYPE_SET_CONTROL_LINE_STATE, |
e51f1d1a | 295 | &request, sizeof(request), NULL, 0); |
b4be4043 GKH |
296 | } |
297 | ||
62229a1b | 298 | static int send_break(struct gb_tty *gb_tty, u8 state) |
b4be4043 | 299 | { |
e51f1d1a | 300 | struct gb_uart_set_break_request request; |
b4be4043 GKH |
301 | |
302 | if ((state != 0) && (state != 1)) { | |
e54b106d | 303 | dev_err(&gb_tty->gbphy_dev->dev, |
e51f1d1a | 304 | "invalid break state of %d\n", state); |
b4be4043 GKH |
305 | return -EINVAL; |
306 | } | |
307 | ||
e51f1d1a | 308 | request.state = state; |
a5192032 | 309 | return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_SEND_BREAK, |
e51f1d1a | 310 | &request, sizeof(request), NULL, 0); |
b4be4043 GKH |
311 | } |
312 | ||
5dad5c31 AH |
313 | static int gb_uart_wait_for_all_credits(struct gb_tty *gb_tty) |
314 | { | |
315 | int ret; | |
316 | ||
317 | if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) | |
318 | return 0; | |
319 | ||
320 | ret = wait_for_completion_timeout(&gb_tty->credits_complete, | |
321 | msecs_to_jiffies(GB_UART_CREDIT_WAIT_TIMEOUT_MSEC)); | |
322 | if (!ret) { | |
323 | dev_err(&gb_tty->gbphy_dev->dev, | |
324 | "time out waiting for credits\n"); | |
325 | return -ETIMEDOUT; | |
326 | } | |
327 | ||
328 | return 0; | |
329 | } | |
330 | ||
2b3b87f0 AH |
331 | static int gb_uart_flush(struct gb_tty *gb_tty, u8 flags) |
332 | { | |
333 | struct gb_uart_serial_flush_request request; | |
334 | ||
335 | request.flags = flags; | |
336 | return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_FLUSH_FIFOS, | |
337 | &request, sizeof(request), NULL, 0); | |
338 | } | |
ff45c265 | 339 | |
26746a36 | 340 | static struct gb_tty *get_gb_by_minor(unsigned int minor) |
ff45c265 GKH |
341 | { |
342 | struct gb_tty *gb_tty; | |
343 | ||
344 | mutex_lock(&table_lock); | |
345 | gb_tty = idr_find(&tty_minors, minor); | |
e68453ed GKH |
346 | if (gb_tty) { |
347 | mutex_lock(&gb_tty->mutex); | |
348 | if (gb_tty->disconnected) { | |
349 | mutex_unlock(&gb_tty->mutex); | |
350 | gb_tty = NULL; | |
351 | } else { | |
352 | tty_port_get(&gb_tty->port); | |
353 | mutex_unlock(&gb_tty->mutex); | |
354 | } | |
355 | } | |
ff45c265 GKH |
356 | mutex_unlock(&table_lock); |
357 | return gb_tty; | |
358 | } | |
359 | ||
360 | static int alloc_minor(struct gb_tty *gb_tty) | |
361 | { | |
362 | int minor; | |
363 | ||
364 | mutex_lock(&table_lock); | |
ff5f0b38 | 365 | minor = idr_alloc(&tty_minors, gb_tty, 0, GB_NUM_MINORS, GFP_KERNEL); |
ff45c265 | 366 | mutex_unlock(&table_lock); |
ff5f0b38 AE |
367 | if (minor >= 0) |
368 | gb_tty->minor = minor; | |
ff45c265 GKH |
369 | return minor; |
370 | } | |
371 | ||
372 | static void release_minor(struct gb_tty *gb_tty) | |
373 | { | |
ff5f0b38 AE |
374 | int minor = gb_tty->minor; |
375 | ||
376 | gb_tty->minor = 0; /* Maybe should use an invalid value instead */ | |
ff45c265 | 377 | mutex_lock(&table_lock); |
ff5f0b38 | 378 | idr_remove(&tty_minors, minor); |
ff45c265 GKH |
379 | mutex_unlock(&table_lock); |
380 | } | |
381 | ||
382 | static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty) | |
383 | { | |
384 | struct gb_tty *gb_tty; | |
385 | int retval; | |
386 | ||
387 | gb_tty = get_gb_by_minor(tty->index); | |
388 | if (!gb_tty) | |
389 | return -ENODEV; | |
390 | ||
391 | retval = tty_standard_install(driver, tty); | |
392 | if (retval) | |
393 | goto error; | |
394 | ||
395 | tty->driver_data = gb_tty; | |
396 | return 0; | |
397 | error: | |
398 | tty_port_put(&gb_tty->port); | |
399 | return retval; | |
400 | } | |
401 | ||
402 | static int gb_tty_open(struct tty_struct *tty, struct file *file) | |
403 | { | |
404 | struct gb_tty *gb_tty = tty->driver_data; | |
405 | ||
406 | return tty_port_open(&gb_tty->port, tty, file); | |
407 | } | |
408 | ||
409 | static void gb_tty_close(struct tty_struct *tty, struct file *file) | |
410 | { | |
411 | struct gb_tty *gb_tty = tty->driver_data; | |
412 | ||
413 | tty_port_close(&gb_tty->port, tty, file); | |
414 | } | |
415 | ||
416 | static void gb_tty_cleanup(struct tty_struct *tty) | |
417 | { | |
418 | struct gb_tty *gb_tty = tty->driver_data; | |
419 | ||
420 | tty_port_put(&gb_tty->port); | |
421 | } | |
422 | ||
423 | static void gb_tty_hangup(struct tty_struct *tty) | |
424 | { | |
425 | struct gb_tty *gb_tty = tty->driver_data; | |
426 | ||
427 | tty_port_hangup(&gb_tty->port); | |
428 | } | |
429 | ||
95713967 | 430 | static ssize_t gb_tty_write(struct tty_struct *tty, const u8 *buf, size_t count) |
a18e1517 | 431 | { |
b4be4043 | 432 | struct gb_tty *gb_tty = tty->driver_data; |
a18e1517 | 433 | |
8d6fbe9b | 434 | count = kfifo_in_spinlocked(&gb_tty->write_fifo, buf, count, |
319b78c3 | 435 | &gb_tty->write_lock); |
8d6fbe9b AH |
436 | if (count && !gb_tty->close_pending) |
437 | schedule_work(&gb_tty->tx_work); | |
438 | ||
439 | return count; | |
a18e1517 GKH |
440 | } |
441 | ||
03b3b1a2 | 442 | static unsigned int gb_tty_write_room(struct tty_struct *tty) |
a18e1517 | 443 | { |
dd1c64ed | 444 | struct gb_tty *gb_tty = tty->driver_data; |
8d6fbe9b AH |
445 | unsigned long flags; |
446 | int room; | |
a18e1517 | 447 | |
8d6fbe9b AH |
448 | spin_lock_irqsave(&gb_tty->write_lock, flags); |
449 | room = kfifo_avail(&gb_tty->write_fifo); | |
450 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
451 | ||
452 | room -= GB_UART_WRITE_ROOM_MARGIN; | |
453 | if (room < 0) | |
454 | return 0; | |
455 | ||
456 | return room; | |
a18e1517 GKH |
457 | } |
458 | ||
fff4ef17 | 459 | static unsigned int gb_tty_chars_in_buffer(struct tty_struct *tty) |
a18e1517 | 460 | { |
8d6fbe9b AH |
461 | struct gb_tty *gb_tty = tty->driver_data; |
462 | unsigned long flags; | |
fff4ef17 | 463 | unsigned int chars; |
8d6fbe9b AH |
464 | |
465 | spin_lock_irqsave(&gb_tty->write_lock, flags); | |
466 | chars = kfifo_len(&gb_tty->write_fifo); | |
a8bc00fb AH |
467 | if (gb_tty->credits < GB_UART_FIRMWARE_CREDITS) |
468 | chars += GB_UART_FIRMWARE_CREDITS - gb_tty->credits; | |
8d6fbe9b AH |
469 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); |
470 | ||
471 | return chars; | |
a18e1517 GKH |
472 | } |
473 | ||
e68453ed GKH |
474 | static int gb_tty_break_ctl(struct tty_struct *tty, int state) |
475 | { | |
b4be4043 | 476 | struct gb_tty *gb_tty = tty->driver_data; |
e68453ed | 477 | |
b4be4043 | 478 | return send_break(gb_tty, state ? 1 : 0); |
e68453ed GKH |
479 | } |
480 | ||
b4be4043 | 481 | static void gb_tty_set_termios(struct tty_struct *tty, |
a8c11c15 | 482 | const struct ktermios *termios_old) |
e68453ed | 483 | { |
6064aeef | 484 | struct gb_uart_set_line_coding_request newline; |
b4be4043 GKH |
485 | struct gb_tty *gb_tty = tty->driver_data; |
486 | struct ktermios *termios = &tty->termios; | |
ba4b099c | 487 | u8 newctrl = gb_tty->ctrlout; |
b4be4043 GKH |
488 | |
489 | newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); | |
62229a1b BD |
490 | newline.format = termios->c_cflag & CSTOPB ? |
491 | GB_SERIAL_2_STOP_BITS : GB_SERIAL_1_STOP_BITS; | |
b4be4043 GKH |
492 | newline.parity = termios->c_cflag & PARENB ? |
493 | (termios->c_cflag & PARODD ? 1 : 2) + | |
494 | (termios->c_cflag & CMSPAR ? 2 : 0) : 0; | |
495 | ||
3ec2ff37 | 496 | newline.data_bits = tty_get_char_size(termios->c_cflag); |
b4be4043 GKH |
497 | |
498 | /* FIXME: needs to clear unsupported bits in the termios */ | |
499 | gb_tty->clocal = ((termios->c_cflag & CLOCAL) != 0); | |
500 | ||
501 | if (C_BAUD(tty) == B0) { | |
502 | newline.rate = gb_tty->line_coding.rate; | |
e55c2520 | 503 | newctrl &= ~(GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); |
b4be4043 | 504 | } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { |
e55c2520 | 505 | newctrl |= (GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); |
b4be4043 GKH |
506 | } |
507 | ||
508 | if (newctrl != gb_tty->ctrlout) { | |
509 | gb_tty->ctrlout = newctrl; | |
510 | send_control(gb_tty, newctrl); | |
511 | } | |
512 | ||
e55c2520 | 513 | if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) |
34625c19 | 514 | newline.flow_control = GB_SERIAL_AUTO_RTSCTS_EN; |
e55c2520 | 515 | else |
34625c19 | 516 | newline.flow_control = 0; |
e55c2520 | 517 | |
b4b1b7ed | 518 | if (memcmp(&gb_tty->line_coding, &newline, sizeof(newline))) { |
b4be4043 | 519 | memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); |
9f240f20 | 520 | send_line_coding(gb_tty); |
b4be4043 | 521 | } |
e68453ed GKH |
522 | } |
523 | ||
524 | static int gb_tty_tiocmget(struct tty_struct *tty) | |
525 | { | |
b4be4043 | 526 | struct gb_tty *gb_tty = tty->driver_data; |
e68453ed | 527 | |
b4be4043 GKH |
528 | return (gb_tty->ctrlout & GB_UART_CTRL_DTR ? TIOCM_DTR : 0) | |
529 | (gb_tty->ctrlout & GB_UART_CTRL_RTS ? TIOCM_RTS : 0) | | |
530 | (gb_tty->ctrlin & GB_UART_CTRL_DSR ? TIOCM_DSR : 0) | | |
531 | (gb_tty->ctrlin & GB_UART_CTRL_RI ? TIOCM_RI : 0) | | |
532 | (gb_tty->ctrlin & GB_UART_CTRL_DCD ? TIOCM_CD : 0) | | |
533 | TIOCM_CTS; | |
e68453ed GKH |
534 | } |
535 | ||
536 | static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, | |
537 | unsigned int clear) | |
538 | { | |
b4be4043 | 539 | struct gb_tty *gb_tty = tty->driver_data; |
ba4b099c | 540 | u8 newctrl = gb_tty->ctrlout; |
e68453ed | 541 | |
b4be4043 GKH |
542 | set = (set & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | |
543 | (set & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); | |
544 | clear = (clear & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | | |
545 | (clear & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); | |
546 | ||
547 | newctrl = (newctrl & ~clear) | set; | |
548 | if (gb_tty->ctrlout == newctrl) | |
549 | return 0; | |
550 | ||
551 | gb_tty->ctrlout = newctrl; | |
552 | return send_control(gb_tty, newctrl); | |
e68453ed GKH |
553 | } |
554 | ||
a18e1517 GKH |
555 | static void gb_tty_throttle(struct tty_struct *tty) |
556 | { | |
557 | struct gb_tty *gb_tty = tty->driver_data; | |
980c7c50 GKH |
558 | unsigned char stop_char; |
559 | int retval; | |
560 | ||
561 | if (I_IXOFF(tty)) { | |
562 | stop_char = STOP_CHAR(tty); | |
563 | retval = gb_tty_write(tty, &stop_char, 1); | |
564 | if (retval <= 0) | |
565 | return; | |
566 | } | |
567 | ||
568 | if (tty->termios.c_cflag & CRTSCTS) { | |
569 | gb_tty->ctrlout &= ~GB_UART_CTRL_RTS; | |
570 | retval = send_control(gb_tty, gb_tty->ctrlout); | |
571 | } | |
a18e1517 GKH |
572 | } |
573 | ||
574 | static void gb_tty_unthrottle(struct tty_struct *tty) | |
575 | { | |
576 | struct gb_tty *gb_tty = tty->driver_data; | |
980c7c50 GKH |
577 | unsigned char start_char; |
578 | int retval; | |
a18e1517 | 579 | |
980c7c50 GKH |
580 | if (I_IXOFF(tty)) { |
581 | start_char = START_CHAR(tty); | |
582 | retval = gb_tty_write(tty, &start_char, 1); | |
583 | if (retval <= 0) | |
584 | return; | |
585 | } | |
a18e1517 | 586 | |
980c7c50 GKH |
587 | if (tty->termios.c_cflag & CRTSCTS) { |
588 | gb_tty->ctrlout |= GB_UART_CTRL_RTS; | |
589 | retval = send_control(gb_tty, gb_tty->ctrlout); | |
a18e1517 GKH |
590 | } |
591 | } | |
592 | ||
0aad5ad5 AV |
593 | static int get_serial_info(struct tty_struct *tty, |
594 | struct serial_struct *ss) | |
595 | { | |
596 | struct gb_tty *gb_tty = tty->driver_data; | |
597 | ||
0aad5ad5 | 598 | ss->line = gb_tty->minor; |
b71e571a | 599 | ss->close_delay = jiffies_to_msecs(gb_tty->port.close_delay) / 10; |
0aad5ad5 | 600 | ss->closing_wait = |
461ab807 | 601 | gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? |
b71e571a JH |
602 | ASYNC_CLOSING_WAIT_NONE : |
603 | jiffies_to_msecs(gb_tty->port.closing_wait) / 10; | |
604 | ||
3be03d42 | 605 | return 0; |
e68453ed GKH |
606 | } |
607 | ||
0aad5ad5 AV |
608 | static int set_serial_info(struct tty_struct *tty, |
609 | struct serial_struct *ss) | |
e68453ed | 610 | { |
0aad5ad5 | 611 | struct gb_tty *gb_tty = tty->driver_data; |
e68453ed GKH |
612 | unsigned int closing_wait; |
613 | unsigned int close_delay; | |
69f93abf | 614 | int retval = 0; |
e68453ed | 615 | |
b71e571a | 616 | close_delay = msecs_to_jiffies(ss->close_delay * 10); |
0aad5ad5 | 617 | closing_wait = ss->closing_wait == ASYNC_CLOSING_WAIT_NONE ? |
b71e571a JH |
618 | ASYNC_CLOSING_WAIT_NONE : |
619 | msecs_to_jiffies(ss->closing_wait * 10); | |
e68453ed GKH |
620 | |
621 | mutex_lock(&gb_tty->port.mutex); | |
622 | if (!capable(CAP_SYS_ADMIN)) { | |
623 | if ((close_delay != gb_tty->port.close_delay) || | |
624 | (closing_wait != gb_tty->port.closing_wait)) | |
625 | retval = -EPERM; | |
e68453ed GKH |
626 | } else { |
627 | gb_tty->port.close_delay = close_delay; | |
628 | gb_tty->port.closing_wait = closing_wait; | |
629 | } | |
630 | mutex_unlock(&gb_tty->port.mutex); | |
631 | return retval; | |
632 | } | |
633 | ||
634 | static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) | |
635 | { | |
636 | int retval = 0; | |
637 | DECLARE_WAITQUEUE(wait, current); | |
638 | struct async_icount old; | |
639 | struct async_icount new; | |
640 | ||
caaa8a83 | 641 | if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))) |
e68453ed GKH |
642 | return -EINVAL; |
643 | ||
644 | do { | |
2aae92bd | 645 | spin_lock_irq(&gb_tty->read_lock); |
e68453ed GKH |
646 | old = gb_tty->oldcount; |
647 | new = gb_tty->iocount; | |
648 | gb_tty->oldcount = new; | |
2aae92bd | 649 | spin_unlock_irq(&gb_tty->read_lock); |
e68453ed GKH |
650 | |
651 | if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) | |
652 | break; | |
653 | if ((arg & TIOCM_CD) && (old.dcd != new.dcd)) | |
654 | break; | |
655 | if ((arg & TIOCM_RI) && (old.rng != new.rng)) | |
656 | break; | |
657 | ||
658 | add_wait_queue(&gb_tty->wioctl, &wait); | |
659 | set_current_state(TASK_INTERRUPTIBLE); | |
660 | schedule(); | |
661 | remove_wait_queue(&gb_tty->wioctl, &wait); | |
662 | if (gb_tty->disconnected) { | |
663 | if (arg & TIOCM_CD) | |
664 | break; | |
caaa8a83 AE |
665 | retval = -ENODEV; |
666 | } else if (signal_pending(current)) { | |
667 | retval = -ERESTARTSYS; | |
e68453ed GKH |
668 | } |
669 | } while (!retval); | |
670 | ||
671 | return retval; | |
672 | } | |
673 | ||
424a4b59 JH |
674 | static int gb_tty_get_icount(struct tty_struct *tty, |
675 | struct serial_icounter_struct *icount) | |
e68453ed | 676 | { |
424a4b59 | 677 | struct gb_tty *gb_tty = tty->driver_data; |
e68453ed | 678 | |
424a4b59 JH |
679 | icount->dsr = gb_tty->iocount.dsr; |
680 | icount->rng = gb_tty->iocount.rng; | |
681 | icount->dcd = gb_tty->iocount.dcd; | |
682 | icount->frame = gb_tty->iocount.frame; | |
683 | icount->overrun = gb_tty->iocount.overrun; | |
684 | icount->parity = gb_tty->iocount.parity; | |
685 | icount->brk = gb_tty->iocount.brk; | |
e68453ed | 686 | |
424a4b59 | 687 | return 0; |
e68453ed GKH |
688 | } |
689 | ||
690 | static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, | |
691 | unsigned long arg) | |
692 | { | |
693 | struct gb_tty *gb_tty = tty->driver_data; | |
e68453ed GKH |
694 | |
695 | switch (cmd) { | |
e68453ed | 696 | case TIOCMIWAIT: |
199d68d4 | 697 | return wait_serial_change(gb_tty, arg); |
e68453ed GKH |
698 | } |
699 | ||
199d68d4 | 700 | return -ENOIOCTLCMD; |
e68453ed GKH |
701 | } |
702 | ||
5701cb8b | 703 | static void gb_tty_dtr_rts(struct tty_port *port, bool active) |
219ffcf3 AH |
704 | { |
705 | struct gb_tty *gb_tty; | |
706 | u8 newctrl; | |
707 | ||
708 | gb_tty = container_of(port, struct gb_tty, port); | |
709 | newctrl = gb_tty->ctrlout; | |
710 | ||
5701cb8b | 711 | if (active) |
219ffcf3 AH |
712 | newctrl |= (GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); |
713 | else | |
714 | newctrl &= ~(GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); | |
715 | ||
716 | gb_tty->ctrlout = newctrl; | |
717 | send_control(gb_tty, newctrl); | |
718 | } | |
79c822be | 719 | |
22b87087 AH |
720 | static int gb_tty_port_activate(struct tty_port *port, |
721 | struct tty_struct *tty) | |
722 | { | |
723 | struct gb_tty *gb_tty; | |
724 | ||
725 | gb_tty = container_of(port, struct gb_tty, port); | |
726 | ||
727 | return gbphy_runtime_get_sync(gb_tty->gbphy_dev); | |
728 | } | |
729 | ||
8d6fbe9b AH |
730 | static void gb_tty_port_shutdown(struct tty_port *port) |
731 | { | |
732 | struct gb_tty *gb_tty; | |
733 | unsigned long flags; | |
2b3b87f0 | 734 | int ret; |
8d6fbe9b AH |
735 | |
736 | gb_tty = container_of(port, struct gb_tty, port); | |
737 | ||
738 | gb_tty->close_pending = true; | |
739 | ||
740 | cancel_work_sync(&gb_tty->tx_work); | |
741 | ||
742 | spin_lock_irqsave(&gb_tty->write_lock, flags); | |
743 | kfifo_reset_out(&gb_tty->write_fifo); | |
744 | spin_unlock_irqrestore(&gb_tty->write_lock, flags); | |
745 | ||
5dad5c31 AH |
746 | if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) |
747 | goto out; | |
748 | ||
2b3b87f0 AH |
749 | ret = gb_uart_flush(gb_tty, GB_SERIAL_FLAG_FLUSH_TRANSMITTER); |
750 | if (ret) { | |
751 | dev_err(&gb_tty->gbphy_dev->dev, | |
752 | "error flushing transmitter: %d\n", ret); | |
753 | } | |
754 | ||
5dad5c31 AH |
755 | gb_uart_wait_for_all_credits(gb_tty); |
756 | ||
757 | out: | |
8d6fbe9b | 758 | gb_tty->close_pending = false; |
22b87087 AH |
759 | |
760 | gbphy_runtime_put_autosuspend(gb_tty->gbphy_dev); | |
8d6fbe9b AH |
761 | } |
762 | ||
92dc0b1f JH |
763 | static void gb_tty_port_destruct(struct tty_port *port) |
764 | { | |
765 | struct gb_tty *gb_tty = container_of(port, struct gb_tty, port); | |
766 | ||
767 | if (gb_tty->minor != GB_NUM_MINORS) | |
768 | release_minor(gb_tty); | |
769 | kfifo_free(&gb_tty->write_fifo); | |
770 | kfree(gb_tty->buffer); | |
771 | kfree(gb_tty); | |
772 | } | |
773 | ||
79c822be GKH |
774 | static const struct tty_operations gb_ops = { |
775 | .install = gb_tty_install, | |
776 | .open = gb_tty_open, | |
777 | .close = gb_tty_close, | |
778 | .cleanup = gb_tty_cleanup, | |
779 | .hangup = gb_tty_hangup, | |
780 | .write = gb_tty_write, | |
781 | .write_room = gb_tty_write_room, | |
782 | .ioctl = gb_tty_ioctl, | |
783 | .throttle = gb_tty_throttle, | |
784 | .unthrottle = gb_tty_unthrottle, | |
785 | .chars_in_buffer = gb_tty_chars_in_buffer, | |
786 | .break_ctl = gb_tty_break_ctl, | |
787 | .set_termios = gb_tty_set_termios, | |
788 | .tiocmget = gb_tty_tiocmget, | |
789 | .tiocmset = gb_tty_tiocmset, | |
424a4b59 | 790 | .get_icount = gb_tty_get_icount, |
327f2769 IV |
791 | .set_serial = set_serial_info, |
792 | .get_serial = get_serial_info, | |
79c822be GKH |
793 | }; |
794 | ||
4d79f431 | 795 | static const struct tty_port_operations gb_port_ops = { |
219ffcf3 | 796 | .dtr_rts = gb_tty_dtr_rts, |
22b87087 | 797 | .activate = gb_tty_port_activate, |
8d6fbe9b | 798 | .shutdown = gb_tty_port_shutdown, |
92dc0b1f | 799 | .destruct = gb_tty_port_destruct, |
219ffcf3 | 800 | }; |
79c822be | 801 | |
e54b106d SP |
802 | static int gb_uart_probe(struct gbphy_device *gbphy_dev, |
803 | const struct gbphy_device_id *id) | |
79c822be | 804 | { |
7dbe1f49 | 805 | struct gb_connection *connection; |
066f950c | 806 | size_t max_payload; |
a18e1517 GKH |
807 | struct gb_tty *gb_tty; |
808 | struct device *tty_dev; | |
79c822be | 809 | int retval; |
a18e1517 GKH |
810 | int minor; |
811 | ||
e54b106d SP |
812 | connection = gb_connection_create(gbphy_dev->bundle, |
813 | le16_to_cpu(gbphy_dev->cport_desc->id), | |
7dbe1f49 | 814 | gb_uart_request_handler); |
92dc0b1f JH |
815 | if (IS_ERR(connection)) |
816 | return PTR_ERR(connection); | |
f95ad78c | 817 | |
066f950c JH |
818 | max_payload = gb_operation_get_payload_size_max(connection); |
819 | if (max_payload < sizeof(struct gb_uart_send_data_request)) { | |
820 | retval = -EINVAL; | |
7dbe1f49 | 821 | goto exit_connection_destroy; |
066f950c JH |
822 | } |
823 | ||
92dc0b1f JH |
824 | gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); |
825 | if (!gb_tty) { | |
826 | retval = -ENOMEM; | |
827 | goto exit_connection_destroy; | |
828 | } | |
829 | ||
830 | tty_port_init(&gb_tty->port); | |
831 | gb_tty->port.ops = &gb_port_ops; | |
832 | gb_tty->minor = GB_NUM_MINORS; | |
833 | ||
066f950c | 834 | gb_tty->buffer_payload_max = max_payload - |
7f29aded | 835 | sizeof(struct gb_uart_send_data_request); |
dd1c64ed BD |
836 | |
837 | gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL); | |
838 | if (!gb_tty->buffer) { | |
55a8e355 | 839 | retval = -ENOMEM; |
92dc0b1f | 840 | goto exit_put_port; |
dd1c64ed BD |
841 | } |
842 | ||
8d6fbe9b AH |
843 | INIT_WORK(&gb_tty->tx_work, gb_uart_tx_write_work); |
844 | ||
845 | retval = kfifo_alloc(&gb_tty->write_fifo, GB_UART_WRITE_FIFO_SIZE, | |
319b78c3 | 846 | GFP_KERNEL); |
8d6fbe9b | 847 | if (retval) |
92dc0b1f | 848 | goto exit_put_port; |
8d6fbe9b | 849 | |
a8bc00fb | 850 | gb_tty->credits = GB_UART_FIRMWARE_CREDITS; |
5dad5c31 | 851 | init_completion(&gb_tty->credits_complete); |
a8bc00fb | 852 | |
a18e1517 | 853 | minor = alloc_minor(gb_tty); |
ff5f0b38 AE |
854 | if (minor < 0) { |
855 | if (minor == -ENOSPC) { | |
4fa58912 | 856 | dev_err(&gbphy_dev->dev, |
b4be4043 | 857 | "no more free minor numbers\n"); |
55f22911 | 858 | retval = -ENODEV; |
7dbe1f49 GKH |
859 | } else { |
860 | retval = minor; | |
ff5f0b38 | 861 | } |
92dc0b1f | 862 | goto exit_put_port; |
a18e1517 GKH |
863 | } |
864 | ||
865 | gb_tty->minor = minor; | |
a18e1517 GKH |
866 | spin_lock_init(&gb_tty->write_lock); |
867 | spin_lock_init(&gb_tty->read_lock); | |
e68453ed GKH |
868 | init_waitqueue_head(&gb_tty->wioctl); |
869 | mutex_init(&gb_tty->mutex); | |
a18e1517 | 870 | |
7dbe1f49 | 871 | gb_tty->connection = connection; |
e54b106d | 872 | gb_tty->gbphy_dev = gbphy_dev; |
7dbe1f49 | 873 | gb_connection_set_data(connection, gb_tty); |
e54b106d | 874 | gb_gbphy_set_data(gbphy_dev, gb_tty); |
7dbe1f49 GKH |
875 | |
876 | retval = gb_connection_enable_tx(connection); | |
877 | if (retval) | |
92dc0b1f | 878 | goto exit_put_port; |
7dbe1f49 | 879 | |
b4be4043 GKH |
880 | send_control(gb_tty, gb_tty->ctrlout); |
881 | ||
882 | /* initialize the uart to be 9600n81 */ | |
883 | gb_tty->line_coding.rate = cpu_to_le32(9600); | |
884 | gb_tty->line_coding.format = GB_SERIAL_1_STOP_BITS; | |
885 | gb_tty->line_coding.parity = GB_SERIAL_NO_PARITY; | |
11fca140 | 886 | gb_tty->line_coding.data_bits = 8; |
9f240f20 | 887 | send_line_coding(gb_tty); |
b4be4043 | 888 | |
7dbe1f49 GKH |
889 | retval = gb_connection_enable(connection); |
890 | if (retval) | |
891 | goto exit_connection_disable; | |
892 | ||
a18e1517 | 893 | tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, |
e54b106d | 894 | &gbphy_dev->dev); |
a18e1517 GKH |
895 | if (IS_ERR(tty_dev)) { |
896 | retval = PTR_ERR(tty_dev); | |
7dbe1f49 | 897 | goto exit_connection_disable; |
a18e1517 | 898 | } |
79c822be | 899 | |
22b87087 | 900 | gbphy_runtime_put_autosuspend(gbphy_dev); |
79c822be | 901 | return 0; |
7dbe1f49 GKH |
902 | |
903 | exit_connection_disable: | |
904 | gb_connection_disable(connection); | |
92dc0b1f JH |
905 | exit_put_port: |
906 | tty_port_put(&gb_tty->port); | |
7dbe1f49 GKH |
907 | exit_connection_destroy: |
908 | gb_connection_destroy(connection); | |
7dbe1f49 | 909 | |
a18e1517 | 910 | return retval; |
79c822be GKH |
911 | } |
912 | ||
e54b106d | 913 | static void gb_uart_remove(struct gbphy_device *gbphy_dev) |
79c822be | 914 | { |
e54b106d | 915 | struct gb_tty *gb_tty = gb_gbphy_get_data(gbphy_dev); |
7dbe1f49 | 916 | struct gb_connection *connection = gb_tty->connection; |
a18e1517 | 917 | struct tty_struct *tty; |
22b87087 AH |
918 | int ret; |
919 | ||
920 | ret = gbphy_runtime_get_sync(gbphy_dev); | |
921 | if (ret) | |
922 | gbphy_runtime_get_noresume(gbphy_dev); | |
a18e1517 | 923 | |
e68453ed GKH |
924 | mutex_lock(&gb_tty->mutex); |
925 | gb_tty->disconnected = true; | |
926 | ||
927 | wake_up_all(&gb_tty->wioctl); | |
e68453ed GKH |
928 | mutex_unlock(&gb_tty->mutex); |
929 | ||
a18e1517 GKH |
930 | tty = tty_port_tty_get(&gb_tty->port); |
931 | if (tty) { | |
932 | tty_vhangup(tty); | |
933 | tty_kref_put(tty); | |
934 | } | |
a18e1517 | 935 | |
7dbe1f49 | 936 | gb_connection_disable_rx(connection); |
a18e1517 GKH |
937 | tty_unregister_device(gb_tty_driver, gb_tty->minor); |
938 | ||
7dbe1f49 | 939 | gb_connection_disable(connection); |
7dbe1f49 | 940 | gb_connection_destroy(connection); |
92dc0b1f JH |
941 | |
942 | tty_port_put(&gb_tty->port); | |
79c822be GKH |
943 | } |
944 | ||
4d980989 | 945 | static int gb_tty_init(void) |
79c822be | 946 | { |
7fabc884 | 947 | int retval = 0; |
79c822be | 948 | |
f8089c0c | 949 | gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); |
7fabc884 | 950 | if (IS_ERR(gb_tty_driver)) { |
168db1cd | 951 | pr_err("Can not allocate tty driver\n"); |
7fabc884 MB |
952 | retval = -ENOMEM; |
953 | goto fail_unregister_dev; | |
954 | } | |
79c822be GKH |
955 | |
956 | gb_tty_driver->driver_name = "gb"; | |
7fabc884 | 957 | gb_tty_driver->name = GB_NAME; |
168db1cd GKH |
958 | gb_tty_driver->major = 0; |
959 | gb_tty_driver->minor_start = 0; | |
79c822be GKH |
960 | gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; |
961 | gb_tty_driver->subtype = SERIAL_TYPE_NORMAL; | |
962 | gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; | |
963 | gb_tty_driver->init_termios = tty_std_termios; | |
461ab807 GK |
964 | gb_tty_driver->init_termios.c_cflag = B9600 | CS8 | |
965 | CREAD | HUPCL | CLOCAL; | |
79c822be GKH |
966 | tty_set_operations(gb_tty_driver, &gb_ops); |
967 | ||
968 | retval = tty_register_driver(gb_tty_driver); | |
168db1cd GKH |
969 | if (retval) { |
970 | pr_err("Can not register tty driver: %d\n", retval); | |
7fabc884 | 971 | goto fail_put_gb_tty; |
168db1cd | 972 | } |
79c822be | 973 | |
7fabc884 MB |
974 | return 0; |
975 | ||
543b8ed2 | 976 | fail_put_gb_tty: |
9f90a4dd | 977 | tty_driver_kref_put(gb_tty_driver); |
543b8ed2 | 978 | fail_unregister_dev: |
79c822be GKH |
979 | return retval; |
980 | } | |
981 | ||
4d980989 | 982 | static void gb_tty_exit(void) |
79c822be | 983 | { |
79c822be | 984 | tty_unregister_driver(gb_tty_driver); |
9f90a4dd | 985 | tty_driver_kref_put(gb_tty_driver); |
5c1ac694 | 986 | idr_destroy(&tty_minors); |
79c822be | 987 | } |
3689f974 | 988 | |
e54b106d SP |
989 | static const struct gbphy_device_id gb_uart_id_table[] = { |
990 | { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_UART) }, | |
7dbe1f49 | 991 | { }, |
19d03dec | 992 | }; |
e54b106d | 993 | MODULE_DEVICE_TABLE(gbphy, gb_uart_id_table); |
19d03dec | 994 | |
e54b106d | 995 | static struct gbphy_driver uart_driver = { |
7dbe1f49 GKH |
996 | .name = "uart", |
997 | .probe = gb_uart_probe, | |
998 | .remove = gb_uart_remove, | |
999 | .id_table = gb_uart_id_table, | |
1000 | }; | |
73d292d8 | 1001 | |
66b9e09e | 1002 | static int gb_uart_driver_init(void) |
73d292d8 VK |
1003 | { |
1004 | int ret; | |
1005 | ||
1006 | ret = gb_tty_init(); | |
1007 | if (ret) | |
1008 | return ret; | |
1009 | ||
e54b106d | 1010 | ret = gb_gbphy_register(&uart_driver); |
73d292d8 VK |
1011 | if (ret) { |
1012 | gb_tty_exit(); | |
1013 | return ret; | |
1014 | } | |
1015 | ||
1016 | return 0; | |
1017 | } | |
66b9e09e | 1018 | module_init(gb_uart_driver_init); |
73d292d8 | 1019 | |
66b9e09e | 1020 | static void gb_uart_driver_exit(void) |
73d292d8 | 1021 | { |
e54b106d | 1022 | gb_gbphy_deregister(&uart_driver); |
73d292d8 VK |
1023 | gb_tty_exit(); |
1024 | } | |
66b9e09e VK |
1025 | |
1026 | module_exit(gb_uart_driver_exit); | |
baf41dbe | 1027 | MODULE_DESCRIPTION("UART driver for the Greybus 'generic' UART module"); |
66b9e09e | 1028 | MODULE_LICENSE("GPL v2"); |