milbeaut_usio.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2018 Socionext Inc.
  4. */
  5. #if defined(CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
  6. #define SUPPORT_SYSRQ
  7. #endif
  8. #include <linux/clk.h>
  9. #include <linux/console.h>
  10. #include <linux/module.h>
  11. #include <linux/of_irq.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/serial_core.h>
  14. #include <linux/tty.h>
  15. #include <linux/tty_flip.h>
  16. #define USIO_NAME "mlb-usio-uart"
  17. #define USIO_UART_DEV_NAME "ttyUSI"
  18. static struct uart_port mlb_usio_ports[CONFIG_SERIAL_MILBEAUT_USIO_PORTS];
  19. #define RX 0
  20. #define TX 1
  21. static int mlb_usio_irq[CONFIG_SERIAL_MILBEAUT_USIO_PORTS][2];
  22. #define MLB_USIO_REG_SMR 0
  23. #define MLB_USIO_REG_SCR 1
  24. #define MLB_USIO_REG_ESCR 2
  25. #define MLB_USIO_REG_SSR 3
  26. #define MLB_USIO_REG_DR 4
  27. #define MLB_USIO_REG_BGR 6
  28. #define MLB_USIO_REG_FCR 12
  29. #define MLB_USIO_REG_FBYTE 14
  30. #define MLB_USIO_SMR_SOE BIT(0)
  31. #define MLB_USIO_SMR_SBL BIT(3)
  32. #define MLB_USIO_SCR_TXE BIT(0)
  33. #define MLB_USIO_SCR_RXE BIT(1)
  34. #define MLB_USIO_SCR_TBIE BIT(2)
  35. #define MLB_USIO_SCR_TIE BIT(3)
  36. #define MLB_USIO_SCR_RIE BIT(4)
  37. #define MLB_USIO_SCR_UPCL BIT(7)
  38. #define MLB_USIO_ESCR_L_8BIT 0
  39. #define MLB_USIO_ESCR_L_5BIT 1
  40. #define MLB_USIO_ESCR_L_6BIT 2
  41. #define MLB_USIO_ESCR_L_7BIT 3
  42. #define MLB_USIO_ESCR_P BIT(3)
  43. #define MLB_USIO_ESCR_PEN BIT(4)
  44. #define MLB_USIO_ESCR_FLWEN BIT(7)
  45. #define MLB_USIO_SSR_TBI BIT(0)
  46. #define MLB_USIO_SSR_TDRE BIT(1)
  47. #define MLB_USIO_SSR_RDRF BIT(2)
  48. #define MLB_USIO_SSR_ORE BIT(3)
  49. #define MLB_USIO_SSR_FRE BIT(4)
  50. #define MLB_USIO_SSR_PE BIT(5)
  51. #define MLB_USIO_SSR_REC BIT(7)
  52. #define MLB_USIO_SSR_BRK BIT(8)
  53. #define MLB_USIO_FCR_FE1 BIT(0)
  54. #define MLB_USIO_FCR_FE2 BIT(1)
  55. #define MLB_USIO_FCR_FCL1 BIT(2)
  56. #define MLB_USIO_FCR_FCL2 BIT(3)
  57. #define MLB_USIO_FCR_FSET BIT(4)
  58. #define MLB_USIO_FCR_FTIE BIT(9)
  59. #define MLB_USIO_FCR_FDRQ BIT(10)
  60. #define MLB_USIO_FCR_FRIIE BIT(11)
  61. static void mlb_usio_stop_tx(struct uart_port *port)
  62. {
  63. writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
  64. port->membase + MLB_USIO_REG_FCR);
  65. writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_TBIE,
  66. port->membase + MLB_USIO_REG_SCR);
  67. }
  68. static void mlb_usio_tx_chars(struct uart_port *port)
  69. {
  70. struct circ_buf *xmit = &port->state->xmit;
  71. int count;
  72. writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FTIE,
  73. port->membase + MLB_USIO_REG_FCR);
  74. writeb(readb(port->membase + MLB_USIO_REG_SCR) &
  75. ~(MLB_USIO_SCR_TIE | MLB_USIO_SCR_TBIE),
  76. port->membase + MLB_USIO_REG_SCR);
  77. if (port->x_char) {
  78. writew(port->x_char, port->membase + MLB_USIO_REG_DR);
  79. port->icount.tx++;
  80. port->x_char = 0;
  81. return;
  82. }
  83. if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
  84. mlb_usio_stop_tx(port);
  85. return;
  86. }
  87. count = port->fifosize -
  88. (readw(port->membase + MLB_USIO_REG_FBYTE) & 0xff);
  89. do {
  90. writew(xmit->buf[xmit->tail], port->membase + MLB_USIO_REG_DR);
  91. xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
  92. port->icount.tx++;
  93. if (uart_circ_empty(xmit))
  94. break;
  95. } while (--count > 0);
  96. writew(readw(port->membase + MLB_USIO_REG_FCR) & ~MLB_USIO_FCR_FDRQ,
  97. port->membase + MLB_USIO_REG_FCR);
  98. writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
  99. port->membase + MLB_USIO_REG_SCR);
  100. if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
  101. uart_write_wakeup(port);
  102. if (uart_circ_empty(xmit))
  103. mlb_usio_stop_tx(port);
  104. }
  105. static void mlb_usio_start_tx(struct uart_port *port)
  106. {
  107. u16 fcr = readw(port->membase + MLB_USIO_REG_FCR);
  108. writew(fcr | MLB_USIO_FCR_FTIE, port->membase + MLB_USIO_REG_FCR);
  109. if (!(fcr & MLB_USIO_FCR_FDRQ))
  110. return;
  111. writeb(readb(port->membase + MLB_USIO_REG_SCR) | MLB_USIO_SCR_TBIE,
  112. port->membase + MLB_USIO_REG_SCR);
  113. if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)
  114. mlb_usio_tx_chars(port);
  115. }
  116. static void mlb_usio_stop_rx(struct uart_port *port)
  117. {
  118. writeb(readb(port->membase + MLB_USIO_REG_SCR) & ~MLB_USIO_SCR_RIE,
  119. port->membase + MLB_USIO_REG_SCR);
  120. }
  121. static void mlb_usio_enable_ms(struct uart_port *port)
  122. {
  123. writeb(readb(port->membase + MLB_USIO_REG_SCR) |
  124. MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE,
  125. port->membase + MLB_USIO_REG_SCR);
  126. }
  127. static void mlb_usio_rx_chars(struct uart_port *port)
  128. {
  129. struct tty_port *ttyport = &port->state->port;
  130. unsigned long flag = 0;
  131. char ch = 0;
  132. u8 status;
  133. int max_count = 2;
  134. while (max_count--) {
  135. status = readb(port->membase + MLB_USIO_REG_SSR);
  136. if (!(status & MLB_USIO_SSR_RDRF))
  137. break;
  138. if (!(status & (MLB_USIO_SSR_ORE | MLB_USIO_SSR_FRE |
  139. MLB_USIO_SSR_PE))) {
  140. ch = readw(port->membase + MLB_USIO_REG_DR);
  141. flag = TTY_NORMAL;
  142. port->icount.rx++;
  143. if (uart_handle_sysrq_char(port, ch))
  144. continue;
  145. uart_insert_char(port, status, MLB_USIO_SSR_ORE,
  146. ch, flag);
  147. continue;
  148. }
  149. if (status & MLB_USIO_SSR_PE)
  150. port->icount.parity++;
  151. if (status & MLB_USIO_SSR_ORE)
  152. port->icount.overrun++;
  153. status &= port->read_status_mask;
  154. if (status & MLB_USIO_SSR_BRK) {
  155. flag = TTY_BREAK;
  156. ch = 0;
  157. } else
  158. if (status & MLB_USIO_SSR_PE) {
  159. flag = TTY_PARITY;
  160. ch = 0;
  161. } else
  162. if (status & MLB_USIO_SSR_FRE) {
  163. flag = TTY_FRAME;
  164. ch = 0;
  165. }
  166. if (flag)
  167. uart_insert_char(port, status, MLB_USIO_SSR_ORE,
  168. ch, flag);
  169. writeb(readb(port->membase + MLB_USIO_REG_SSR) |
  170. MLB_USIO_SSR_REC,
  171. port->membase + MLB_USIO_REG_SSR);
  172. max_count = readw(port->membase + MLB_USIO_REG_FBYTE) >> 8;
  173. writew(readw(port->membase + MLB_USIO_REG_FCR) |
  174. MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
  175. port->membase + MLB_USIO_REG_FCR);
  176. }
  177. tty_flip_buffer_push(ttyport);
  178. }
  179. static irqreturn_t mlb_usio_rx_irq(int irq, void *dev_id)
  180. {
  181. struct uart_port *port = dev_id;
  182. spin_lock(&port->lock);
  183. mlb_usio_rx_chars(port);
  184. spin_unlock(&port->lock);
  185. return IRQ_HANDLED;
  186. }
  187. static irqreturn_t mlb_usio_tx_irq(int irq, void *dev_id)
  188. {
  189. struct uart_port *port = dev_id;
  190. spin_lock(&port->lock);
  191. if (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI)
  192. mlb_usio_tx_chars(port);
  193. spin_unlock(&port->lock);
  194. return IRQ_HANDLED;
  195. }
  196. static unsigned int mlb_usio_tx_empty(struct uart_port *port)
  197. {
  198. return (readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TBI) ?
  199. TIOCSER_TEMT : 0;
  200. }
  201. static void mlb_usio_set_mctrl(struct uart_port *port, unsigned int mctrl)
  202. {
  203. }
  204. static unsigned int mlb_usio_get_mctrl(struct uart_port *port)
  205. {
  206. return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
  207. }
  208. static void mlb_usio_break_ctl(struct uart_port *port, int break_state)
  209. {
  210. }
  211. static int mlb_usio_startup(struct uart_port *port)
  212. {
  213. const char *portname = to_platform_device(port->dev)->name;
  214. unsigned long flags;
  215. int ret, index = port->line;
  216. unsigned char escr;
  217. ret = request_irq(mlb_usio_irq[index][RX], mlb_usio_rx_irq,
  218. 0, portname, port);
  219. if (ret)
  220. return ret;
  221. ret = request_irq(mlb_usio_irq[index][TX], mlb_usio_tx_irq,
  222. 0, portname, port);
  223. if (ret) {
  224. free_irq(mlb_usio_irq[index][RX], port);
  225. return ret;
  226. }
  227. escr = readb(port->membase + MLB_USIO_REG_ESCR);
  228. if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))
  229. escr |= MLB_USIO_ESCR_FLWEN;
  230. spin_lock_irqsave(&port->lock, flags);
  231. writeb(0, port->membase + MLB_USIO_REG_SCR);
  232. writeb(escr, port->membase + MLB_USIO_REG_ESCR);
  233. writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);
  234. writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);
  235. writew(0, port->membase + MLB_USIO_REG_FCR);
  236. writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2,
  237. port->membase + MLB_USIO_REG_FCR);
  238. writew(MLB_USIO_FCR_FE1 | MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
  239. port->membase + MLB_USIO_REG_FCR);
  240. writew(0, port->membase + MLB_USIO_REG_FBYTE);
  241. writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);
  242. writeb(MLB_USIO_SCR_TXE | MLB_USIO_SCR_RIE | MLB_USIO_SCR_TBIE |
  243. MLB_USIO_SCR_RXE, port->membase + MLB_USIO_REG_SCR);
  244. spin_unlock_irqrestore(&port->lock, flags);
  245. return 0;
  246. }
  247. static void mlb_usio_shutdown(struct uart_port *port)
  248. {
  249. int index = port->line;
  250. free_irq(mlb_usio_irq[index][RX], port);
  251. free_irq(mlb_usio_irq[index][TX], port);
  252. }
  253. static void mlb_usio_set_termios(struct uart_port *port,
  254. struct ktermios *termios, struct ktermios *old)
  255. {
  256. unsigned int escr, smr = MLB_USIO_SMR_SOE;
  257. unsigned long flags, baud, quot;
  258. switch (termios->c_cflag & CSIZE) {
  259. case CS5:
  260. escr = MLB_USIO_ESCR_L_5BIT;
  261. break;
  262. case CS6:
  263. escr = MLB_USIO_ESCR_L_6BIT;
  264. break;
  265. case CS7:
  266. escr = MLB_USIO_ESCR_L_7BIT;
  267. break;
  268. case CS8:
  269. default:
  270. escr = MLB_USIO_ESCR_L_8BIT;
  271. break;
  272. }
  273. if (termios->c_cflag & CSTOPB)
  274. smr |= MLB_USIO_SMR_SBL;
  275. if (termios->c_cflag & PARENB) {
  276. escr |= MLB_USIO_ESCR_PEN;
  277. if (termios->c_cflag & PARODD)
  278. escr |= MLB_USIO_ESCR_P;
  279. }
  280. /* Set hard flow control */
  281. if (of_property_read_bool(port->dev->of_node, "auto-flow-control") ||
  282. (termios->c_cflag & CRTSCTS))
  283. escr |= MLB_USIO_ESCR_FLWEN;
  284. baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk);
  285. if (baud > 1)
  286. quot = port->uartclk / baud - 1;
  287. else
  288. quot = 0;
  289. spin_lock_irqsave(&port->lock, flags);
  290. uart_update_timeout(port, termios->c_cflag, baud);
  291. port->read_status_mask = MLB_USIO_SSR_ORE | MLB_USIO_SSR_RDRF |
  292. MLB_USIO_SSR_TDRE;
  293. if (termios->c_iflag & INPCK)
  294. port->read_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;
  295. port->ignore_status_mask = 0;
  296. if (termios->c_iflag & IGNPAR)
  297. port->ignore_status_mask |= MLB_USIO_SSR_FRE | MLB_USIO_SSR_PE;
  298. if ((termios->c_iflag & IGNBRK) && (termios->c_iflag & IGNPAR))
  299. port->ignore_status_mask |= MLB_USIO_SSR_ORE;
  300. if ((termios->c_cflag & CREAD) == 0)
  301. port->ignore_status_mask |= MLB_USIO_SSR_RDRF;
  302. writeb(0, port->membase + MLB_USIO_REG_SCR);
  303. writeb(MLB_USIO_SCR_UPCL, port->membase + MLB_USIO_REG_SCR);
  304. writeb(MLB_USIO_SSR_REC, port->membase + MLB_USIO_REG_SSR);
  305. writew(0, port->membase + MLB_USIO_REG_FCR);
  306. writeb(smr, port->membase + MLB_USIO_REG_SMR);
  307. writeb(escr, port->membase + MLB_USIO_REG_ESCR);
  308. writew(quot, port->membase + MLB_USIO_REG_BGR);
  309. writew(0, port->membase + MLB_USIO_REG_FCR);
  310. writew(MLB_USIO_FCR_FCL1 | MLB_USIO_FCR_FCL2 | MLB_USIO_FCR_FE1 |
  311. MLB_USIO_FCR_FE2 | MLB_USIO_FCR_FRIIE,
  312. port->membase + MLB_USIO_REG_FCR);
  313. writew(0, port->membase + MLB_USIO_REG_FBYTE);
  314. writew(BIT(12), port->membase + MLB_USIO_REG_FBYTE);
  315. writeb(MLB_USIO_SCR_RIE | MLB_USIO_SCR_RXE | MLB_USIO_SCR_TBIE |
  316. MLB_USIO_SCR_TXE, port->membase + MLB_USIO_REG_SCR);
  317. spin_unlock_irqrestore(&port->lock, flags);
  318. }
  319. static const char *mlb_usio_type(struct uart_port *port)
  320. {
  321. return ((port->type == PORT_MLB_USIO) ? USIO_NAME : NULL);
  322. }
  323. static void mlb_usio_config_port(struct uart_port *port, int flags)
  324. {
  325. if (flags & UART_CONFIG_TYPE)
  326. port->type = PORT_MLB_USIO;
  327. }
  328. static const struct uart_ops mlb_usio_ops = {
  329. .tx_empty = mlb_usio_tx_empty,
  330. .set_mctrl = mlb_usio_set_mctrl,
  331. .get_mctrl = mlb_usio_get_mctrl,
  332. .stop_tx = mlb_usio_stop_tx,
  333. .start_tx = mlb_usio_start_tx,
  334. .stop_rx = mlb_usio_stop_rx,
  335. .enable_ms = mlb_usio_enable_ms,
  336. .break_ctl = mlb_usio_break_ctl,
  337. .startup = mlb_usio_startup,
  338. .shutdown = mlb_usio_shutdown,
  339. .set_termios = mlb_usio_set_termios,
  340. .type = mlb_usio_type,
  341. .config_port = mlb_usio_config_port,
  342. };
  343. #ifdef CONFIG_SERIAL_MILBEAUT_USIO_CONSOLE
  344. static void mlb_usio_console_putchar(struct uart_port *port, int c)
  345. {
  346. while (!(readb(port->membase + MLB_USIO_REG_SSR) & MLB_USIO_SSR_TDRE))
  347. cpu_relax();
  348. writew(c, port->membase + MLB_USIO_REG_DR);
  349. }
  350. static void mlb_usio_console_write(struct console *co, const char *s,
  351. unsigned int count)
  352. {
  353. struct uart_port *port = &mlb_usio_ports[co->index];
  354. uart_console_write(port, s, count, mlb_usio_console_putchar);
  355. }
  356. static int __init mlb_usio_console_setup(struct console *co, char *options)
  357. {
  358. struct uart_port *port;
  359. int baud = 115200;
  360. int parity = 'n';
  361. int flow = 'n';
  362. int bits = 8;
  363. if (co->index >= CONFIG_SERIAL_MILBEAUT_USIO_PORTS)
  364. return -ENODEV;
  365. port = &mlb_usio_ports[co->index];
  366. if (!port->membase)
  367. return -ENODEV;
  368. if (options)
  369. uart_parse_options(options, &baud, &parity, &bits, &flow);
  370. if (of_property_read_bool(port->dev->of_node, "auto-flow-control"))
  371. flow = 'r';
  372. return uart_set_options(port, co, baud, parity, bits, flow);
  373. }
  374. static struct uart_driver mlb_usio_uart_driver;
  375. static struct console mlb_usio_console = {
  376. .name = USIO_UART_DEV_NAME,
  377. .write = mlb_usio_console_write,
  378. .device = uart_console_device,
  379. .setup = mlb_usio_console_setup,
  380. .flags = CON_PRINTBUFFER,
  381. .index = -1,
  382. .data = &mlb_usio_uart_driver,
  383. };
  384. static int __init mlb_usio_console_init(void)
  385. {
  386. register_console(&mlb_usio_console);
  387. return 0;
  388. }
  389. console_initcall(mlb_usio_console_init);
  390. static void mlb_usio_early_console_write(struct console *co, const char *s,
  391. u_int count)
  392. {
  393. struct earlycon_device *dev = co->data;
  394. uart_console_write(&dev->port, s, count, mlb_usio_console_putchar);
  395. }
  396. static int __init mlb_usio_early_console_setup(struct earlycon_device *device,
  397. const char *opt)
  398. {
  399. if (!device->port.membase)
  400. return -ENODEV;
  401. device->con->write = mlb_usio_early_console_write;
  402. return 0;
  403. }
  404. OF_EARLYCON_DECLARE(mlb_usio, "socionext,milbeaut-usio-uart",
  405. mlb_usio_early_console_setup);
  406. #define USIO_CONSOLE (&mlb_usio_console)
  407. #else
  408. #define USIO_CONSOLE NULL
  409. #endif
  410. static struct uart_driver mlb_usio_uart_driver = {
  411. .owner = THIS_MODULE,
  412. .driver_name = USIO_NAME,
  413. .dev_name = USIO_UART_DEV_NAME,
  414. .cons = USIO_CONSOLE,
  415. .nr = CONFIG_SERIAL_MILBEAUT_USIO_PORTS,
  416. };
  417. static int mlb_usio_probe(struct platform_device *pdev)
  418. {
  419. struct clk *clk = devm_clk_get(&pdev->dev, NULL);
  420. struct uart_port *port;
  421. struct resource *res;
  422. int index = 0;
  423. int ret;
  424. if (IS_ERR(clk)) {
  425. dev_err(&pdev->dev, "Missing clock\n");
  426. return PTR_ERR(clk);
  427. }
  428. ret = clk_prepare_enable(clk);
  429. if (ret) {
  430. dev_err(&pdev->dev, "Clock enable failed: %d\n", ret);
  431. return ret;
  432. }
  433. of_property_read_u32(pdev->dev.of_node, "index", &index);
  434. port = &mlb_usio_ports[index];
  435. port->private_data = (void *)clk;
  436. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  437. if (res == NULL) {
  438. dev_err(&pdev->dev, "Missing regs\n");
  439. ret = -ENODEV;
  440. goto failed;
  441. }
  442. port->membase = devm_ioremap(&pdev->dev, res->start,
  443. resource_size(res));
  444. ret = platform_get_irq_byname(pdev, "rx");
  445. mlb_usio_irq[index][RX] = ret;
  446. ret = platform_get_irq_byname(pdev, "tx");
  447. mlb_usio_irq[index][TX] = ret;
  448. port->irq = mlb_usio_irq[index][RX];
  449. port->uartclk = clk_get_rate(clk);
  450. port->fifosize = 128;
  451. port->iotype = UPIO_MEM32;
  452. port->flags = UPF_BOOT_AUTOCONF | UPF_SPD_VHI;
  453. port->line = index;
  454. port->ops = &mlb_usio_ops;
  455. port->dev = &pdev->dev;
  456. ret = uart_add_one_port(&mlb_usio_uart_driver, port);
  457. if (ret) {
  458. dev_err(&pdev->dev, "Adding port failed: %d\n", ret);
  459. goto failed;
  460. }
  461. return 0;
  462. failed:
  463. clk_disable_unprepare(clk);
  464. return ret;
  465. }
  466. static int mlb_usio_remove(struct platform_device *pdev)
  467. {
  468. struct uart_port *port = &mlb_usio_ports[pdev->id];
  469. struct clk *clk = port->private_data;
  470. uart_remove_one_port(&mlb_usio_uart_driver, port);
  471. clk_disable_unprepare(clk);
  472. return 0;
  473. }
  474. static const struct of_device_id mlb_usio_dt_ids[] = {
  475. { .compatible = "socionext,milbeaut-usio-uart" },
  476. { /* sentinel */ }
  477. };
  478. MODULE_DEVICE_TABLE(of, mlb_usio_dt_ids);
  479. static struct platform_driver mlb_usio_driver = {
  480. .probe = mlb_usio_probe,
  481. .remove = mlb_usio_remove,
  482. .driver = {
  483. .name = USIO_NAME,
  484. .of_match_table = mlb_usio_dt_ids,
  485. },
  486. };
  487. static int __init mlb_usio_init(void)
  488. {
  489. int ret = uart_register_driver(&mlb_usio_uart_driver);
  490. if (ret) {
  491. pr_err("%s: uart registration failed: %d\n", __func__, ret);
  492. return ret;
  493. }
  494. ret = platform_driver_register(&mlb_usio_driver);
  495. if (ret) {
  496. uart_unregister_driver(&mlb_usio_uart_driver);
  497. pr_err("%s: drv registration failed: %d\n", __func__, ret);
  498. return ret;
  499. }
  500. return 0;
  501. }
  502. static void __exit mlb_usio_exit(void)
  503. {
  504. platform_driver_unregister(&mlb_usio_driver);
  505. uart_unregister_driver(&mlb_usio_uart_driver);
  506. }
  507. module_init(mlb_usio_init);
  508. module_exit(mlb_usio_exit);
  509. MODULE_AUTHOR("SOCIONEXT");
  510. MODULE_DESCRIPTION("MILBEAUT_USIO/UART Driver");
  511. MODULE_LICENSE("GPL");