1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/device.h> |
3 | #include <linux/kernel.h> |
4 | #include <linux/module.h> |
5 | #include <linux/io.h> |
6 | #include <linux/mcb.h> |
7 | #include <linux/serial.h> |
8 | #include <linux/serial_core.h> |
9 | #include <linux/serial_8250.h> |
10 | |
11 | #define MEN_UART_ID_Z025 0x19 |
12 | #define MEN_UART_ID_Z057 0x39 |
13 | #define MEN_UART_ID_Z125 0x7d |
14 | |
15 | /* |
16 | * IP Cores Z025 and Z057 can have up to 4 UART |
17 | * The UARTs available are stored in a global |
18 | * register saved in physical address + 0x40 |
19 | * Is saved as follows: |
20 | * |
21 | * 7 0 |
22 | * +------+-------+-------+-------+-------+-------+-------+-------+ |
23 | * |UART4 | UART3 | UART2 | UART1 | U4irq | U3irq | U2irq | U1irq | |
24 | * +------+-------+-------+-------+-------+-------+-------+-------+ |
25 | */ |
26 | #define MEN_UART1_MASK 0x01 |
27 | #define MEN_UART2_MASK 0x02 |
28 | #define MEN_UART3_MASK 0x04 |
29 | #define MEN_UART4_MASK 0x08 |
30 | |
31 | #define MEN_Z125_UARTS_AVAILABLE 0x01 |
32 | |
33 | #define MEN_Z025_MAX_UARTS 4 |
34 | #define MEN_UART_MEM_SIZE 0x10 |
35 | #define MEM_UART_REGISTER_SIZE 0x01 |
36 | #define MEN_Z025_REGISTER_OFFSET 0x40 |
37 | |
38 | #define MEN_UART1_OFFSET 0 |
39 | #define MEN_UART2_OFFSET (MEN_UART1_OFFSET + MEN_UART_MEM_SIZE) |
40 | #define MEN_UART3_OFFSET (MEN_UART2_OFFSET + MEN_UART_MEM_SIZE) |
41 | #define MEN_UART4_OFFSET (MEN_UART3_OFFSET + MEN_UART_MEM_SIZE) |
42 | |
43 | #define MEN_READ_REGISTER(addr) readb(addr) |
44 | |
45 | #define MAX_PORTS 4 |
46 | |
47 | struct serial_8250_men_mcb_data { |
48 | int num_ports; |
49 | int line[MAX_PORTS]; |
50 | unsigned int offset[MAX_PORTS]; |
51 | }; |
52 | |
53 | /* |
54 | * The Z125 16550-compatible UART has no fixed base clock assigned |
55 | * So, depending on the board we're on, we need to adjust the |
56 | * parameter in order to really set the correct baudrate, and |
57 | * do so if possible without user interaction |
58 | */ |
59 | static u32 men_lookup_uartclk(struct mcb_device *mdev) |
60 | { |
61 | /* use default value if board is not available below */ |
62 | u32 clkval = 1041666; |
63 | |
64 | dev_info(&mdev->dev, "%s on board %s\n" , |
65 | dev_name(&mdev->dev), |
66 | mdev->bus->name); |
67 | if (strncmp(mdev->bus->name, "F075" , 4) == 0) |
68 | clkval = 1041666; |
69 | else if (strncmp(mdev->bus->name, "F216" , 4) == 0) |
70 | clkval = 1843200; |
71 | else if (strncmp(mdev->bus->name, "F210" , 4) == 0) |
72 | clkval = 115200; |
73 | else if (strstr(mdev->bus->name, "215" )) |
74 | clkval = 1843200; |
75 | else |
76 | dev_info(&mdev->dev, |
77 | "board not detected, using default uartclk\n" ); |
78 | |
79 | clkval = clkval << 4; |
80 | |
81 | return clkval; |
82 | } |
83 | |
84 | static int read_uarts_available_from_register(struct resource *mem_res, |
85 | u8 *uarts_available) |
86 | { |
87 | void __iomem *mem; |
88 | int reg_value; |
89 | |
90 | if (!request_mem_region(mem_res->start + MEN_Z025_REGISTER_OFFSET, |
91 | MEM_UART_REGISTER_SIZE, KBUILD_MODNAME)) { |
92 | return -EBUSY; |
93 | } |
94 | |
95 | mem = ioremap(offset: mem_res->start + MEN_Z025_REGISTER_OFFSET, |
96 | MEM_UART_REGISTER_SIZE); |
97 | if (!mem) { |
98 | release_mem_region(mem_res->start + MEN_Z025_REGISTER_OFFSET, |
99 | MEM_UART_REGISTER_SIZE); |
100 | return -ENOMEM; |
101 | } |
102 | |
103 | reg_value = MEN_READ_REGISTER(mem); |
104 | |
105 | iounmap(addr: mem); |
106 | |
107 | release_mem_region(mem_res->start + MEN_Z025_REGISTER_OFFSET, |
108 | MEM_UART_REGISTER_SIZE); |
109 | |
110 | *uarts_available = reg_value >> 4; |
111 | |
112 | return 0; |
113 | } |
114 | |
115 | static int read_serial_data(struct mcb_device *mdev, |
116 | struct resource *mem_res, |
117 | struct serial_8250_men_mcb_data *serial_data) |
118 | { |
119 | u8 uarts_available; |
120 | int count = 0; |
121 | int mask; |
122 | int res; |
123 | int i; |
124 | |
125 | res = read_uarts_available_from_register(mem_res, uarts_available: &uarts_available); |
126 | if (res < 0) |
127 | return res; |
128 | |
129 | for (i = 0; i < MAX_PORTS; i++) { |
130 | mask = 0x1 << i; |
131 | switch (uarts_available & mask) { |
132 | case MEN_UART1_MASK: |
133 | serial_data->offset[count] = MEN_UART1_OFFSET; |
134 | count++; |
135 | break; |
136 | case MEN_UART2_MASK: |
137 | serial_data->offset[count] = MEN_UART2_OFFSET; |
138 | count++; |
139 | break; |
140 | case MEN_UART3_MASK: |
141 | serial_data->offset[count] = MEN_UART3_OFFSET; |
142 | count++; |
143 | break; |
144 | case MEN_UART4_MASK: |
145 | serial_data->offset[count] = MEN_UART4_OFFSET; |
146 | count++; |
147 | break; |
148 | default: |
149 | return -EINVAL; |
150 | } |
151 | } |
152 | |
153 | if (count <= 0 || count > MAX_PORTS) { |
154 | dev_err(&mdev->dev, "unexpected number of ports: %u\n" , |
155 | count); |
156 | return -ENODEV; |
157 | } |
158 | |
159 | serial_data->num_ports = count; |
160 | |
161 | return 0; |
162 | } |
163 | |
164 | static int init_serial_data(struct mcb_device *mdev, |
165 | struct resource *mem_res, |
166 | struct serial_8250_men_mcb_data *serial_data) |
167 | { |
168 | switch (mdev->id) { |
169 | case MEN_UART_ID_Z125: |
170 | serial_data->num_ports = 1; |
171 | serial_data->offset[0] = 0; |
172 | return 0; |
173 | case MEN_UART_ID_Z025: |
174 | case MEN_UART_ID_Z057: |
175 | return read_serial_data(mdev, mem_res, serial_data); |
176 | default: |
177 | dev_err(&mdev->dev, "no supported device!\n" ); |
178 | return -ENODEV; |
179 | } |
180 | } |
181 | |
182 | static int serial_8250_men_mcb_probe(struct mcb_device *mdev, |
183 | const struct mcb_device_id *id) |
184 | { |
185 | struct uart_8250_port uart; |
186 | struct serial_8250_men_mcb_data *data; |
187 | struct resource *mem; |
188 | int i; |
189 | int res; |
190 | |
191 | mem = mcb_get_resource(dev: mdev, IORESOURCE_MEM); |
192 | if (mem == NULL) |
193 | return -ENXIO; |
194 | |
195 | data = devm_kzalloc(dev: &mdev->dev, |
196 | size: sizeof(struct serial_8250_men_mcb_data), |
197 | GFP_KERNEL); |
198 | if (!data) |
199 | return -ENOMEM; |
200 | |
201 | res = init_serial_data(mdev, mem_res: mem, serial_data: data); |
202 | if (res < 0) |
203 | return res; |
204 | |
205 | dev_dbg(&mdev->dev, "found a 16z%03u with %u ports\n" , |
206 | mdev->id, data->num_ports); |
207 | |
208 | mcb_set_drvdata(dev: mdev, data); |
209 | |
210 | for (i = 0; i < data->num_ports; i++) { |
211 | memset(&uart, 0, sizeof(struct uart_8250_port)); |
212 | spin_lock_init(&uart.port.lock); |
213 | |
214 | uart.port.flags = UPF_SKIP_TEST | |
215 | UPF_SHARE_IRQ | |
216 | UPF_BOOT_AUTOCONF | |
217 | UPF_IOREMAP; |
218 | uart.port.iotype = UPIO_MEM; |
219 | uart.port.uartclk = men_lookup_uartclk(mdev); |
220 | uart.port.irq = mcb_get_irq(dev: mdev); |
221 | uart.port.mapbase = (unsigned long) mem->start |
222 | + data->offset[i]; |
223 | |
224 | /* ok, register the port */ |
225 | res = serial8250_register_8250_port(&uart); |
226 | if (res < 0) { |
227 | dev_err(&mdev->dev, "unable to register UART port\n" ); |
228 | return res; |
229 | } |
230 | |
231 | data->line[i] = res; |
232 | dev_info(&mdev->dev, "found MCB UART: ttyS%d\n" , data->line[i]); |
233 | } |
234 | |
235 | return 0; |
236 | } |
237 | |
238 | static void serial_8250_men_mcb_remove(struct mcb_device *mdev) |
239 | { |
240 | int i; |
241 | struct serial_8250_men_mcb_data *data = mcb_get_drvdata(dev: mdev); |
242 | |
243 | if (!data) |
244 | return; |
245 | |
246 | for (i = 0; i < data->num_ports; i++) |
247 | serial8250_unregister_port(line: data->line[i]); |
248 | } |
249 | |
250 | static const struct mcb_device_id serial_8250_men_mcb_ids[] = { |
251 | { .device = MEN_UART_ID_Z025 }, |
252 | { .device = MEN_UART_ID_Z057 }, |
253 | { .device = MEN_UART_ID_Z125 }, |
254 | { } |
255 | }; |
256 | MODULE_DEVICE_TABLE(mcb, serial_8250_men_mcb_ids); |
257 | |
258 | static struct mcb_driver mcb_driver = { |
259 | .driver = { |
260 | .name = "8250_men_mcb" , |
261 | }, |
262 | .probe = serial_8250_men_mcb_probe, |
263 | .remove = serial_8250_men_mcb_remove, |
264 | .id_table = serial_8250_men_mcb_ids, |
265 | }; |
266 | module_mcb_driver(mcb_driver); |
267 | |
268 | MODULE_LICENSE("GPL v2" ); |
269 | MODULE_DESCRIPTION("MEN 8250 UART driver" ); |
270 | MODULE_AUTHOR("Michael Moese <michael.moese@men.de" ); |
271 | MODULE_ALIAS("mcb:16z125" ); |
272 | MODULE_ALIAS("mcb:16z025" ); |
273 | MODULE_ALIAS("mcb:16z057" ); |
274 | MODULE_IMPORT_NS(MCB); |
275 | |