1 /*
2  * (C) Copyright 2016 Stephen Warren <swarren@wwwdotorg.org>
3  *
4  * Derived from pl01x code:
5  *
6  * (C) Copyright 2000
7  * Rob Taylor, Flying Pig Systems. robt@flyingpig.com.
8  *
9  * (C) Copyright 2004
10  * ARM Ltd.
11  * Philippe Robin, <philippe.robin@arm.com>
12  *
13  * SPDX-License-Identifier:	GPL-2.0+
14  */
15 
16 /* Simple U-Boot driver for the BCM283x mini UART */
17 
18 #include <common.h>
19 #include <dm.h>
20 #include <errno.h>
21 #include <watchdog.h>
22 #include <asm/io.h>
23 #include <serial.h>
24 #include <dm/platform_data/serial_bcm283x_mu.h>
25 #include <linux/compiler.h>
26 #include <fdtdec.h>
27 
28 DECLARE_GLOBAL_DATA_PTR;
29 
30 struct bcm283x_mu_regs {
31 	u32 io;
32 	u32 iir;
33 	u32 ier;
34 	u32 lcr;
35 	u32 mcr;
36 	u32 lsr;
37 	u32 msr;
38 	u32 scratch;
39 	u32 cntl;
40 	u32 stat;
41 	u32 baud;
42 };
43 
44 #define BCM283X_MU_LCR_DATA_SIZE_8	3
45 
46 #define BCM283X_MU_LSR_TX_IDLE		BIT(6)
47 /* This actually means not full, but is named not empty in the docs */
48 #define BCM283X_MU_LSR_TX_EMPTY		BIT(5)
49 #define BCM283X_MU_LSR_RX_READY		BIT(0)
50 
51 struct bcm283x_mu_priv {
52 	struct bcm283x_mu_regs *regs;
53 };
54 
55 static int bcm283x_mu_serial_setbrg(struct udevice *dev, int baudrate)
56 {
57 	struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
58 	struct bcm283x_mu_priv *priv = dev_get_priv(dev);
59 	struct bcm283x_mu_regs *regs = priv->regs;
60 	u32 divider;
61 
62 	if (plat->disabled || plat->skip_init)
63 		return 0;
64 
65 	divider = plat->clock / (baudrate * 8);
66 
67 	writel(BCM283X_MU_LCR_DATA_SIZE_8, &regs->lcr);
68 	writel(divider - 1, &regs->baud);
69 
70 	return 0;
71 }
72 
73 static int bcm283x_mu_serial_probe(struct udevice *dev)
74 {
75 	struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
76 	struct bcm283x_mu_priv *priv = dev_get_priv(dev);
77 
78 	if (plat->disabled)
79 		return -ENODEV;
80 
81 	priv->regs = (struct bcm283x_mu_regs *)plat->base;
82 
83 	return 0;
84 }
85 
86 static int bcm283x_mu_serial_getc(struct udevice *dev)
87 {
88 	struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
89 	struct bcm283x_mu_priv *priv = dev_get_priv(dev);
90 	struct bcm283x_mu_regs *regs = priv->regs;
91 	u32 data;
92 
93 	if (plat->disabled)
94 		return -EAGAIN;
95 
96 	/* Wait until there is data in the FIFO */
97 	if (!(readl(&regs->lsr) & BCM283X_MU_LSR_RX_READY))
98 		return -EAGAIN;
99 
100 	data = readl(&regs->io);
101 
102 	return (int)data;
103 }
104 
105 static int bcm283x_mu_serial_putc(struct udevice *dev, const char data)
106 {
107 	struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
108 	struct bcm283x_mu_priv *priv = dev_get_priv(dev);
109 	struct bcm283x_mu_regs *regs = priv->regs;
110 
111 	if (plat->disabled)
112 		return 0;
113 
114 	/* Wait until there is space in the FIFO */
115 	if (!(readl(&regs->lsr) & BCM283X_MU_LSR_TX_EMPTY))
116 		return -EAGAIN;
117 
118 	/* Send the character */
119 	writel(data, &regs->io);
120 
121 	return 0;
122 }
123 
124 static int bcm283x_mu_serial_pending(struct udevice *dev, bool input)
125 {
126 	struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
127 	struct bcm283x_mu_priv *priv = dev_get_priv(dev);
128 	struct bcm283x_mu_regs *regs = priv->regs;
129 	unsigned int lsr;
130 
131 	if (plat->disabled)
132 		return 0;
133 
134 	lsr = readl(&regs->lsr);
135 
136 	if (input) {
137 		WATCHDOG_RESET();
138 		return (lsr & BCM283X_MU_LSR_RX_READY) ? 1 : 0;
139 	} else {
140 		return (lsr & BCM283X_MU_LSR_TX_IDLE) ? 0 : 1;
141 	}
142 }
143 
144 static const struct dm_serial_ops bcm283x_mu_serial_ops = {
145 	.putc = bcm283x_mu_serial_putc,
146 	.pending = bcm283x_mu_serial_pending,
147 	.getc = bcm283x_mu_serial_getc,
148 	.setbrg = bcm283x_mu_serial_setbrg,
149 };
150 
151 #if CONFIG_IS_ENABLED(OF_CONTROL)
152 static const struct udevice_id bcm283x_mu_serial_id[] = {
153 	{.compatible = "brcm,bcm2835-aux-uart"},
154 	{}
155 };
156 
157 static int bcm283x_mu_serial_ofdata_to_platdata(struct udevice *dev)
158 {
159 	struct bcm283x_mu_serial_platdata *plat = dev_get_platdata(dev);
160 	fdt_addr_t addr;
161 
162 	addr = dev_get_addr(dev);
163 	if (addr == FDT_ADDR_T_NONE)
164 		return -EINVAL;
165 
166 	plat->base = addr;
167 	plat->clock = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "clock", 1);
168 	plat->skip_init = fdtdec_get_bool(gd->fdt_blob, dev->of_offset,
169 	                                  "skip-init");
170 	plat->disabled = false;
171 	return 0;
172 }
173 #endif
174 
175 U_BOOT_DRIVER(serial_bcm283x_mu) = {
176 	.name = "serial_bcm283x_mu",
177 	.id = UCLASS_SERIAL,
178 	.of_match = of_match_ptr(bcm283x_mu_serial_id),
179 	.ofdata_to_platdata = of_match_ptr(bcm283x_mu_serial_ofdata_to_platdata),
180 	.platdata_auto_alloc_size = sizeof(struct bcm283x_mu_serial_platdata),
181 	.probe = bcm283x_mu_serial_probe,
182 	.ops = &bcm283x_mu_serial_ops,
183 	.flags = DM_FLAG_PRE_RELOC,
184 	.priv_auto_alloc_size = sizeof(struct bcm283x_mu_priv),
185 };
186