]>
Commit | Line | Data |
---|---|---|
142a20c3 MK |
1 | /* |
2 | * Qualcomm UART driver | |
3 | * | |
4 | * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> | |
5 | * | |
6 | * UART will work in Data Mover mode. | |
7 | * Based on Linux driver. | |
8 | * | |
9 | * SPDX-License-Identifier: GPL-2.0+ | |
10 | */ | |
11 | ||
12 | #include <common.h> | |
13 | #include <clk.h> | |
14 | #include <dm.h> | |
15 | #include <errno.h> | |
16 | #include <serial.h> | |
17 | #include <watchdog.h> | |
18 | #include <asm/io.h> | |
19 | #include <linux/compiler.h> | |
20 | ||
21 | /* Serial registers - this driver works in uartdm mode*/ | |
22 | ||
23 | #define UARTDM_DMRX 0x34 /* Max RX transfer length */ | |
24 | #define UARTDM_NCF_TX 0x40 /* Number of chars to TX */ | |
25 | ||
26 | #define UARTDM_RXFS 0x50 /* RX channel status register */ | |
27 | #define UARTDM_RXFS_BUF_SHIFT 0x7 /* Number of bytes in the packing buffer */ | |
28 | #define UARTDM_RXFS_BUF_MASK 0x7 | |
29 | ||
30 | #define UARTDM_SR 0xA4 /* Status register */ | |
31 | #define UARTDM_SR_RX_READY (1 << 0) /* Word is the receiver FIFO */ | |
32 | #define UARTDM_SR_TX_EMPTY (1 << 3) /* Transmitter underrun */ | |
33 | #define UARTDM_SR_UART_OVERRUN (1 << 4) /* Receive overrun */ | |
34 | ||
35 | #define UARTDM_CR 0xA8 /* Command register */ | |
36 | #define UARTDM_CR_CMD_RESET_ERR (3 << 4) /* Clear overrun error */ | |
37 | #define UARTDM_CR_CMD_RESET_STALE_INT (8 << 4) /* Clears stale irq */ | |
38 | #define UARTDM_CR_CMD_RESET_TX_READY (3 << 8) /* Clears TX Ready irq*/ | |
39 | #define UARTDM_CR_CMD_FORCE_STALE (4 << 8) /* Causes stale event */ | |
40 | #define UARTDM_CR_CMD_STALE_EVENT_DISABLE (6 << 8) /* Disable stale event */ | |
41 | ||
42 | #define UARTDM_IMR 0xB0 /* Interrupt mask register */ | |
43 | #define UARTDM_ISR 0xB4 /* Interrupt status register */ | |
44 | #define UARTDM_ISR_TX_READY 0x80 /* TX FIFO empty */ | |
45 | ||
46 | #define UARTDM_TF 0x100 /* UART Transmit FIFO register */ | |
47 | #define UARTDM_RF 0x140 /* UART Receive FIFO register */ | |
48 | ||
49 | ||
50 | DECLARE_GLOBAL_DATA_PTR; | |
51 | ||
52 | struct msm_serial_data { | |
53 | phys_addr_t base; | |
54 | unsigned chars_cnt; /* number of buffered chars */ | |
55 | uint32_t chars_buf; /* buffered chars */ | |
56 | }; | |
57 | ||
58 | static int msm_serial_fetch(struct udevice *dev) | |
59 | { | |
60 | struct msm_serial_data *priv = dev_get_priv(dev); | |
61 | unsigned sr; | |
62 | ||
63 | if (priv->chars_cnt) | |
64 | return priv->chars_cnt; | |
65 | ||
66 | /* Clear error in case of buffer overrun */ | |
67 | if (readl(priv->base + UARTDM_SR) & UARTDM_SR_UART_OVERRUN) | |
68 | writel(UARTDM_CR_CMD_RESET_ERR, priv->base + UARTDM_CR); | |
69 | ||
70 | /* We need to fetch new character */ | |
71 | sr = readl(priv->base + UARTDM_SR); | |
72 | ||
73 | if (sr & UARTDM_SR_RX_READY) { | |
74 | /* There are at least 4 bytes in fifo */ | |
75 | priv->chars_buf = readl(priv->base + UARTDM_RF); | |
76 | priv->chars_cnt = 4; | |
77 | } else { | |
78 | /* Check if there is anything in fifo */ | |
79 | priv->chars_cnt = readl(priv->base + UARTDM_RXFS); | |
80 | /* Extract number of characters in UART packing buffer*/ | |
81 | priv->chars_cnt = (priv->chars_cnt >> | |
82 | UARTDM_RXFS_BUF_SHIFT) & | |
83 | UARTDM_RXFS_BUF_MASK; | |
84 | if (!priv->chars_cnt) | |
85 | return 0; | |
86 | ||
87 | /* There is at least one charcter, move it to fifo */ | |
88 | writel(UARTDM_CR_CMD_FORCE_STALE, | |
89 | priv->base + UARTDM_CR); | |
90 | ||
91 | priv->chars_buf = readl(priv->base + UARTDM_RF); | |
92 | writel(UARTDM_CR_CMD_RESET_STALE_INT, | |
93 | priv->base + UARTDM_CR); | |
94 | writel(0x7, priv->base + UARTDM_DMRX); | |
95 | } | |
96 | ||
97 | return priv->chars_cnt; | |
98 | } | |
99 | ||
100 | static int msm_serial_getc(struct udevice *dev) | |
101 | { | |
102 | struct msm_serial_data *priv = dev_get_priv(dev); | |
103 | char c; | |
104 | ||
105 | if (!msm_serial_fetch(dev)) | |
106 | return -EAGAIN; | |
107 | ||
108 | c = priv->chars_buf & 0xFF; | |
109 | priv->chars_buf >>= 8; | |
110 | priv->chars_cnt--; | |
111 | ||
112 | return c; | |
113 | } | |
114 | ||
115 | static int msm_serial_putc(struct udevice *dev, const char ch) | |
116 | { | |
117 | struct msm_serial_data *priv = dev_get_priv(dev); | |
118 | ||
119 | if (!(readl(priv->base + UARTDM_SR) & UARTDM_SR_TX_EMPTY) && | |
120 | !(readl(priv->base + UARTDM_ISR) & UARTDM_ISR_TX_READY)) | |
121 | return -EAGAIN; | |
122 | ||
123 | writel(UARTDM_CR_CMD_RESET_TX_READY, priv->base + UARTDM_CR); | |
124 | ||
125 | writel(1, priv->base + UARTDM_NCF_TX); | |
126 | writel(ch, priv->base + UARTDM_TF); | |
127 | ||
128 | return 0; | |
129 | } | |
130 | ||
131 | static int msm_serial_pending(struct udevice *dev, bool input) | |
132 | { | |
133 | if (input) { | |
134 | if (msm_serial_fetch(dev)) | |
135 | return 1; | |
136 | } | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
141 | static const struct dm_serial_ops msm_serial_ops = { | |
142 | .putc = msm_serial_putc, | |
143 | .pending = msm_serial_pending, | |
144 | .getc = msm_serial_getc, | |
145 | }; | |
146 | ||
147 | static int msm_uart_clk_init(struct udevice *dev) | |
148 | { | |
149 | uint clk_rate = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, | |
150 | "clock-frequency", 115200); | |
151 | uint clkd[2]; /* clk_id and clk_no */ | |
152 | int clk_offset; | |
135aa950 SW |
153 | struct udevice *clk_dev; |
154 | struct clk clk; | |
142a20c3 MK |
155 | int ret; |
156 | ||
157 | ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "clock", clkd, | |
158 | 2); | |
159 | if (ret) | |
160 | return ret; | |
161 | ||
162 | clk_offset = fdt_node_offset_by_phandle(gd->fdt_blob, clkd[0]); | |
163 | if (clk_offset < 0) | |
164 | return clk_offset; | |
165 | ||
135aa950 | 166 | ret = uclass_get_device_by_of_offset(UCLASS_CLK, clk_offset, &clk_dev); |
142a20c3 MK |
167 | if (ret) |
168 | return ret; | |
169 | ||
135aa950 SW |
170 | clk.id = clkd[1]; |
171 | ret = clk_request(clk_dev, &clk); | |
172 | if (ret < 0) | |
173 | return ret; | |
174 | ||
175 | ret = clk_set_rate(&clk, clk_rate); | |
176 | clk_free(&clk); | |
142a20c3 MK |
177 | if (ret < 0) |
178 | return ret; | |
179 | ||
180 | return 0; | |
181 | } | |
182 | ||
183 | static int msm_serial_probe(struct udevice *dev) | |
184 | { | |
185 | struct msm_serial_data *priv = dev_get_priv(dev); | |
186 | ||
187 | msm_uart_clk_init(dev); /* Ignore return value and hope clock was | |
188 | properly initialized by earlier loaders */ | |
189 | ||
190 | if (readl(priv->base + UARTDM_SR) & UARTDM_SR_UART_OVERRUN) | |
191 | writel(UARTDM_CR_CMD_RESET_ERR, priv->base + UARTDM_CR); | |
192 | ||
193 | writel(0, priv->base + UARTDM_IMR); | |
194 | writel(UARTDM_CR_CMD_STALE_EVENT_DISABLE, priv->base + UARTDM_CR); | |
195 | msm_serial_fetch(dev); | |
196 | ||
197 | return 0; | |
198 | } | |
199 | ||
200 | static int msm_serial_ofdata_to_platdata(struct udevice *dev) | |
201 | { | |
202 | struct msm_serial_data *priv = dev_get_priv(dev); | |
203 | ||
204 | priv->base = dev_get_addr(dev); | |
205 | if (priv->base == FDT_ADDR_T_NONE) | |
206 | return -EINVAL; | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | static const struct udevice_id msm_serial_ids[] = { | |
212 | { .compatible = "qcom,msm-uartdm-v1.4" }, | |
213 | { } | |
214 | }; | |
215 | ||
216 | U_BOOT_DRIVER(serial_msm) = { | |
217 | .name = "serial_msm", | |
218 | .id = UCLASS_SERIAL, | |
219 | .of_match = msm_serial_ids, | |
220 | .ofdata_to_platdata = msm_serial_ofdata_to_platdata, | |
221 | .priv_auto_alloc_size = sizeof(struct msm_serial_data), | |
222 | .probe = msm_serial_probe, | |
223 | .ops = &msm_serial_ops, | |
224 | }; |