milbeaut_usio.c 16 KB

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