]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/serial/serial_uniphier.c
74547eb692b19eedfbe9de2aabb30aafaebe3595
[karo-tx-uboot.git] / drivers / serial / serial_uniphier.c
1 /*
2  * Copyright (C) 2012-2015 Panasonic Corporation
3  * Copyright (C) 2015      Socionext Inc.
4  *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5  *
6  * SPDX-License-Identifier:     GPL-2.0+
7  */
8
9 #include <linux/serial_reg.h>
10 #include <asm/io.h>
11 #include <asm/errno.h>
12 #include <dm/device.h>
13 #include <dm/platform_data/serial-uniphier.h>
14 #include <mapmem.h>
15 #include <serial.h>
16 #include <fdtdec.h>
17
18 /*
19  * Note: Register map is slightly different from that of 16550.
20  */
21 struct uniphier_serial {
22         u32 rx;                 /* In:  Receive buffer */
23 #define tx rx                   /* Out: Transmit buffer */
24         u32 ier;                /* Interrupt Enable Register */
25         u32 iir;                /* In: Interrupt ID Register */
26         u32 char_fcr;           /* Charactor / FIFO Control Register */
27         u32 lcr_mcr;            /* Line/Modem Control Register */
28 #define LCR_SHIFT       8
29 #define LCR_MASK        (0xff << (LCR_SHIFT))
30         u32 lsr;                /* In: Line Status Register */
31         u32 msr;                /* In: Modem Status Register */
32         u32 __rsv0;
33         u32 __rsv1;
34         u32 dlr;                /* Divisor Latch Register */
35 };
36
37 struct uniphier_serial_private_data {
38         struct uniphier_serial __iomem *membase;
39 };
40
41 #define uniphier_serial_port(dev)       \
42         ((struct uniphier_serial_private_data *)dev_get_priv(dev))->membase
43
44 static int uniphier_serial_setbrg(struct udevice *dev, int baudrate)
45 {
46         struct uniphier_serial_platform_data *plat = dev_get_platdata(dev);
47         struct uniphier_serial __iomem *port = uniphier_serial_port(dev);
48         const unsigned int mode_x_div = 16;
49         unsigned int divisor;
50
51         divisor = DIV_ROUND_CLOSEST(plat->uartclk, mode_x_div * baudrate);
52
53         writel(divisor, &port->dlr);
54
55         return 0;
56 }
57
58 static int uniphier_serial_getc(struct udevice *dev)
59 {
60         struct uniphier_serial __iomem *port = uniphier_serial_port(dev);
61
62         if (!(readl(&port->lsr) & UART_LSR_DR))
63                 return -EAGAIN;
64
65         return readl(&port->rx);
66 }
67
68 static int uniphier_serial_putc(struct udevice *dev, const char c)
69 {
70         struct uniphier_serial __iomem *port = uniphier_serial_port(dev);
71
72         if (!(readl(&port->lsr) & UART_LSR_THRE))
73                 return -EAGAIN;
74
75         writel(c, &port->tx);
76
77         return 0;
78 }
79
80 static int uniphier_serial_pending(struct udevice *dev, bool input)
81 {
82         struct uniphier_serial __iomem *port = uniphier_serial_port(dev);
83
84         if (input)
85                 return readl(&port->lsr) & UART_LSR_DR;
86         else
87                 return !(readl(&port->lsr) & UART_LSR_THRE);
88 }
89
90 static int uniphier_serial_probe(struct udevice *dev)
91 {
92         u32 tmp;
93         struct uniphier_serial_private_data *priv = dev_get_priv(dev);
94         struct uniphier_serial_platform_data *plat = dev_get_platdata(dev);
95         struct uniphier_serial __iomem *port;
96
97         port = map_sysmem(plat->base, sizeof(struct uniphier_serial));
98         if (!port)
99                 return -ENOMEM;
100
101         priv->membase = port;
102
103         tmp = readl(&port->lcr_mcr);
104         tmp &= ~LCR_MASK;
105         tmp |= UART_LCR_WLEN8 << LCR_SHIFT;
106         writel(tmp, &port->lcr_mcr);
107
108         return 0;
109 }
110
111 static int uniphier_serial_remove(struct udevice *dev)
112 {
113         unmap_sysmem(uniphier_serial_port(dev));
114
115         return 0;
116 }
117
118 #ifdef CONFIG_OF_CONTROL
119 static const struct udevice_id uniphier_uart_of_match[] = {
120         { .compatible = "socionext,uniphier-uart" },
121         { /* sentinel */ }
122 };
123
124 static int uniphier_serial_ofdata_to_platdata(struct udevice *dev)
125 {
126         struct uniphier_serial_platform_data *plat = dev_get_platdata(dev);
127         DECLARE_GLOBAL_DATA_PTR;
128
129         plat->base = fdtdec_get_addr(gd->fdt_blob, dev->of_offset, "reg");
130         plat->uartclk = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
131                                        "clock-frequency", 0);
132
133         return 0;
134 }
135 #endif
136
137 static const struct dm_serial_ops uniphier_serial_ops = {
138         .setbrg = uniphier_serial_setbrg,
139         .getc = uniphier_serial_getc,
140         .putc = uniphier_serial_putc,
141         .pending = uniphier_serial_pending,
142 };
143
144 U_BOOT_DRIVER(uniphier_serial) = {
145         .name = DRIVER_NAME,
146         .id = UCLASS_SERIAL,
147         .of_match = of_match_ptr(uniphier_uart_of_match),
148         .ofdata_to_platdata = of_match_ptr(uniphier_serial_ofdata_to_platdata),
149         .probe = uniphier_serial_probe,
150         .remove = uniphier_serial_remove,
151         .priv_auto_alloc_size = sizeof(struct uniphier_serial_private_data),
152         .platdata_auto_alloc_size =
153                                 sizeof(struct uniphier_serial_platform_data),
154         .ops = &uniphier_serial_ops,
155         .flags = DM_FLAG_PRE_RELOC,
156 };