1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Moxa C101 synchronous serial card driver for Linux |
4 | * |
5 | * Copyright (C) 2000-2003 Krzysztof Halasa <khc@pm.waw.pl> |
6 | * |
7 | * For information see <https://www.kernel.org/pub/linux/utils/net/hdlc/> |
8 | * |
9 | * Sources of information: |
10 | * Hitachi HD64570 SCA User's Manual |
11 | * Moxa C101 User's Manual |
12 | */ |
13 | |
14 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
15 | |
16 | #include <linux/module.h> |
17 | #include <linux/kernel.h> |
18 | #include <linux/capability.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/types.h> |
21 | #include <linux/string.h> |
22 | #include <linux/errno.h> |
23 | #include <linux/init.h> |
24 | #include <linux/netdevice.h> |
25 | #include <linux/hdlc.h> |
26 | #include <linux/delay.h> |
27 | #include <asm/io.h> |
28 | |
29 | #include "hd64570.h" |
30 | |
31 | static const char *version = "Moxa C101 driver version: 1.15" ; |
32 | static const char *devname = "C101" ; |
33 | |
34 | #undef DEBUG_PKT |
35 | #define DEBUG_RINGS |
36 | |
37 | #define C101_PAGE 0x1D00 |
38 | #define C101_DTR 0x1E00 |
39 | #define C101_SCA 0x1F00 |
40 | #define C101_WINDOW_SIZE 0x2000 |
41 | #define C101_MAPPED_RAM_SIZE 0x4000 |
42 | |
43 | #define RAM_SIZE (256 * 1024) |
44 | #define TX_RING_BUFFERS 10 |
45 | #define RX_RING_BUFFERS ((RAM_SIZE - C101_WINDOW_SIZE) / \ |
46 | (sizeof(pkt_desc) + HDLC_MAX_MRU) - TX_RING_BUFFERS) |
47 | |
48 | #define CLOCK_BASE 9830400 /* 9.8304 MHz */ |
49 | #define PAGE0_ALWAYS_MAPPED |
50 | |
51 | static char *hw; /* pointer to hw=xxx command line string */ |
52 | |
53 | typedef struct card_s { |
54 | struct net_device *dev; |
55 | spinlock_t lock; /* TX lock */ |
56 | u8 __iomem *win0base; /* ISA window base address */ |
57 | u32 phy_winbase; /* ISA physical base address */ |
58 | sync_serial_settings settings; |
59 | int rxpart; /* partial frame received, next frame invalid*/ |
60 | unsigned short encoding; |
61 | unsigned short parity; |
62 | u16 rx_ring_buffers; /* number of buffers in a ring */ |
63 | u16 tx_ring_buffers; |
64 | u16 buff_offset; /* offset of first buffer of first channel */ |
65 | u16 rxin; /* rx ring buffer 'in' pointer */ |
66 | u16 txin; /* tx ring buffer 'in' and 'last' pointers */ |
67 | u16 txlast; |
68 | u8 rxs, txs, tmc; /* SCA registers */ |
69 | u8 irq; /* IRQ (3-15) */ |
70 | u8 page; |
71 | |
72 | struct card_s *next_card; |
73 | } card_t; |
74 | |
75 | typedef card_t port_t; |
76 | |
77 | static card_t *first_card; |
78 | static card_t **new_card = &first_card; |
79 | |
80 | #define sca_in(reg, card) readb((card)->win0base + C101_SCA + (reg)) |
81 | #define sca_out(value, reg, card) writeb(value, (card)->win0base + C101_SCA + (reg)) |
82 | #define sca_inw(reg, card) readw((card)->win0base + C101_SCA + (reg)) |
83 | |
84 | /* EDA address register must be set in EDAL, EDAH order - 8 bit ISA bus */ |
85 | #define sca_outw(value, reg, card) do { \ |
86 | writeb(value & 0xFF, (card)->win0base + C101_SCA + (reg)); \ |
87 | writeb((value >> 8) & 0xFF, (card)->win0base + C101_SCA + (reg + 1));\ |
88 | } while (0) |
89 | |
90 | #define port_to_card(port) (port) |
91 | #define log_node(port) (0) |
92 | #define phy_node(port) (0) |
93 | #define winsize(card) (C101_WINDOW_SIZE) |
94 | #define win0base(card) ((card)->win0base) |
95 | #define winbase(card) ((card)->win0base + 0x2000) |
96 | #define get_port(card, port) (card) |
97 | static void sca_msci_intr(port_t *port); |
98 | |
99 | static inline u8 sca_get_page(card_t *card) |
100 | { |
101 | return card->page; |
102 | } |
103 | |
104 | static inline void openwin(card_t *card, u8 page) |
105 | { |
106 | card->page = page; |
107 | writeb(val: page, addr: card->win0base + C101_PAGE); |
108 | } |
109 | |
110 | #include "hd64570.c" |
111 | |
112 | static inline void set_carrier(port_t *port) |
113 | { |
114 | if (!(sca_in(MSCI1_OFFSET + ST3, port) & ST3_DCD)) |
115 | netif_carrier_on(dev: port_to_dev(port)); |
116 | else |
117 | netif_carrier_off(dev: port_to_dev(port)); |
118 | } |
119 | |
120 | static void sca_msci_intr(port_t *port) |
121 | { |
122 | u8 stat = sca_in(MSCI0_OFFSET + ST1, port); /* read MSCI ST1 status */ |
123 | |
124 | /* Reset MSCI TX underrun and CDCD (ignored) status bit */ |
125 | sca_out(stat & (ST1_UDRN | ST1_CDCD), MSCI0_OFFSET + ST1, port); |
126 | |
127 | if (stat & ST1_UDRN) { |
128 | /* TX Underrun error detected */ |
129 | port_to_dev(port)->stats.tx_errors++; |
130 | port_to_dev(port)->stats.tx_fifo_errors++; |
131 | } |
132 | |
133 | stat = sca_in(MSCI1_OFFSET + ST1, port); /* read MSCI1 ST1 status */ |
134 | /* Reset MSCI CDCD status bit - uses ch#2 DCD input */ |
135 | sca_out(stat & ST1_CDCD, MSCI1_OFFSET + ST1, port); |
136 | |
137 | if (stat & ST1_CDCD) |
138 | set_carrier(port); |
139 | } |
140 | |
141 | static void c101_set_iface(port_t *port) |
142 | { |
143 | u8 rxs = port->rxs & CLK_BRG_MASK; |
144 | u8 txs = port->txs & CLK_BRG_MASK; |
145 | |
146 | switch (port->settings.clock_type) { |
147 | case CLOCK_INT: |
148 | rxs |= CLK_BRG_RX; /* TX clock */ |
149 | txs |= CLK_RXCLK_TX; /* BRG output */ |
150 | break; |
151 | |
152 | case CLOCK_TXINT: |
153 | rxs |= CLK_LINE_RX; /* RXC input */ |
154 | txs |= CLK_BRG_TX; /* BRG output */ |
155 | break; |
156 | |
157 | case CLOCK_TXFROMRX: |
158 | rxs |= CLK_LINE_RX; /* RXC input */ |
159 | txs |= CLK_RXCLK_TX; /* RX clock */ |
160 | break; |
161 | |
162 | default: /* EXTernal clock */ |
163 | rxs |= CLK_LINE_RX; /* RXC input */ |
164 | txs |= CLK_LINE_TX; /* TXC input */ |
165 | } |
166 | |
167 | port->rxs = rxs; |
168 | port->txs = txs; |
169 | sca_out(rxs, MSCI1_OFFSET + RXS, port); |
170 | sca_out(txs, MSCI1_OFFSET + TXS, port); |
171 | sca_set_port(port); |
172 | } |
173 | |
174 | static int c101_open(struct net_device *dev) |
175 | { |
176 | port_t *port = dev_to_port(dev); |
177 | int result; |
178 | |
179 | result = hdlc_open(dev); |
180 | if (result) |
181 | return result; |
182 | |
183 | writeb(val: 1, addr: port->win0base + C101_DTR); |
184 | sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */ |
185 | sca_open(dev); |
186 | /* DCD is connected to port 2 !@#$%^& - disable MSCI0 CDCD interrupt */ |
187 | sca_out(IE1_UDRN, MSCI0_OFFSET + IE1, port); |
188 | sca_out(IE0_TXINT, MSCI0_OFFSET + IE0, port); |
189 | |
190 | set_carrier(port); |
191 | |
192 | /* enable MSCI1 CDCD interrupt */ |
193 | sca_out(IE1_CDCD, MSCI1_OFFSET + IE1, port); |
194 | sca_out(IE0_RXINTA, MSCI1_OFFSET + IE0, port); |
195 | sca_out(0x48, IER0, port); /* TXINT #0 and RXINT #1 */ |
196 | c101_set_iface(port); |
197 | return 0; |
198 | } |
199 | |
200 | static int c101_close(struct net_device *dev) |
201 | { |
202 | port_t *port = dev_to_port(dev); |
203 | |
204 | sca_close(dev); |
205 | writeb(val: 0, addr: port->win0base + C101_DTR); |
206 | sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port); |
207 | hdlc_close(dev); |
208 | return 0; |
209 | } |
210 | |
211 | static int c101_siocdevprivate(struct net_device *dev, struct ifreq *ifr, |
212 | void __user *data, int cmd) |
213 | { |
214 | #ifdef DEBUG_RINGS |
215 | port_t *port = dev_to_port(dev); |
216 | |
217 | if (cmd == SIOCDEVPRIVATE) { |
218 | sca_dump_rings(dev); |
219 | printk(KERN_DEBUG "MSCI1: ST: %02x %02x %02x %02x\n" , |
220 | sca_in(MSCI1_OFFSET + ST0, port), |
221 | sca_in(MSCI1_OFFSET + ST1, port), |
222 | sca_in(MSCI1_OFFSET + ST2, port), |
223 | sca_in(MSCI1_OFFSET + ST3, port)); |
224 | return 0; |
225 | } |
226 | #endif |
227 | |
228 | return -EOPNOTSUPP; |
229 | } |
230 | |
231 | static int c101_ioctl(struct net_device *dev, struct if_settings *ifs) |
232 | { |
233 | const size_t size = sizeof(sync_serial_settings); |
234 | sync_serial_settings new_line; |
235 | sync_serial_settings __user *line = ifs->ifs_ifsu.sync; |
236 | port_t *port = dev_to_port(dev); |
237 | |
238 | switch (ifs->type) { |
239 | case IF_GET_IFACE: |
240 | ifs->type = IF_IFACE_SYNC_SERIAL; |
241 | if (ifs->size < size) { |
242 | ifs->size = size; /* data size wanted */ |
243 | return -ENOBUFS; |
244 | } |
245 | if (copy_to_user(to: line, from: &port->settings, n: size)) |
246 | return -EFAULT; |
247 | return 0; |
248 | |
249 | case IF_IFACE_SYNC_SERIAL: |
250 | if (!capable(CAP_NET_ADMIN)) |
251 | return -EPERM; |
252 | |
253 | if (copy_from_user(to: &new_line, from: line, n: size)) |
254 | return -EFAULT; |
255 | |
256 | if (new_line.clock_type != CLOCK_EXT && |
257 | new_line.clock_type != CLOCK_TXFROMRX && |
258 | new_line.clock_type != CLOCK_INT && |
259 | new_line.clock_type != CLOCK_TXINT) |
260 | return -EINVAL; /* No such clock setting */ |
261 | |
262 | if (new_line.loopback != 0 && new_line.loopback != 1) |
263 | return -EINVAL; |
264 | |
265 | memcpy(&port->settings, &new_line, size); /* Update settings */ |
266 | c101_set_iface(port); |
267 | return 0; |
268 | |
269 | default: |
270 | return hdlc_ioctl(dev, ifs); |
271 | } |
272 | } |
273 | |
274 | static void c101_destroy_card(card_t *card) |
275 | { |
276 | readb(addr: card->win0base + C101_PAGE); /* Resets SCA? */ |
277 | |
278 | if (card->irq) |
279 | free_irq(card->irq, card); |
280 | |
281 | if (card->win0base) { |
282 | iounmap(addr: card->win0base); |
283 | release_mem_region(card->phy_winbase, C101_MAPPED_RAM_SIZE); |
284 | } |
285 | |
286 | free_netdev(dev: card->dev); |
287 | |
288 | kfree(objp: card); |
289 | } |
290 | |
291 | static const struct net_device_ops c101_ops = { |
292 | .ndo_open = c101_open, |
293 | .ndo_stop = c101_close, |
294 | .ndo_start_xmit = hdlc_start_xmit, |
295 | .ndo_siocwandev = c101_ioctl, |
296 | .ndo_siocdevprivate = c101_siocdevprivate, |
297 | }; |
298 | |
299 | static int __init c101_run(unsigned long irq, unsigned long winbase) |
300 | { |
301 | struct net_device *dev; |
302 | hdlc_device *hdlc; |
303 | card_t *card; |
304 | int result; |
305 | |
306 | if (irq < 3 || irq > 15 || irq == 6) /* FIXME */ { |
307 | pr_err("invalid IRQ value\n" ); |
308 | return -ENODEV; |
309 | } |
310 | |
311 | if (winbase < 0xC0000 || winbase > 0xDFFFF || (winbase & 0x3FFF) != 0) { |
312 | pr_err("invalid RAM value\n" ); |
313 | return -ENODEV; |
314 | } |
315 | |
316 | card = kzalloc(size: sizeof(card_t), GFP_KERNEL); |
317 | if (!card) |
318 | return -ENOBUFS; |
319 | |
320 | card->dev = alloc_hdlcdev(priv: card); |
321 | if (!card->dev) { |
322 | pr_err("unable to allocate memory\n" ); |
323 | kfree(objp: card); |
324 | return -ENOBUFS; |
325 | } |
326 | |
327 | if (request_irq(irq, handler: sca_intr, flags: 0, name: devname, dev: card)) { |
328 | pr_err("could not allocate IRQ\n" ); |
329 | c101_destroy_card(card); |
330 | return -EBUSY; |
331 | } |
332 | card->irq = irq; |
333 | |
334 | if (!request_mem_region(winbase, C101_MAPPED_RAM_SIZE, devname)) { |
335 | pr_err("could not request RAM window\n" ); |
336 | c101_destroy_card(card); |
337 | return -EBUSY; |
338 | } |
339 | card->phy_winbase = winbase; |
340 | card->win0base = ioremap(offset: winbase, C101_MAPPED_RAM_SIZE); |
341 | if (!card->win0base) { |
342 | pr_err("could not map I/O address\n" ); |
343 | c101_destroy_card(card); |
344 | return -EFAULT; |
345 | } |
346 | |
347 | card->tx_ring_buffers = TX_RING_BUFFERS; |
348 | card->rx_ring_buffers = RX_RING_BUFFERS; |
349 | card->buff_offset = C101_WINDOW_SIZE; /* Bytes 1D00-1FFF reserved */ |
350 | |
351 | readb(addr: card->win0base + C101_PAGE); /* Resets SCA? */ |
352 | udelay(100); |
353 | writeb(val: 0, addr: card->win0base + C101_PAGE); |
354 | writeb(val: 0, addr: card->win0base + C101_DTR); /* Power-up for RAM? */ |
355 | |
356 | sca_init(card, wait_states: 0); |
357 | |
358 | dev = port_to_dev(port: card); |
359 | hdlc = dev_to_hdlc(dev); |
360 | |
361 | spin_lock_init(&card->lock); |
362 | dev->irq = irq; |
363 | dev->mem_start = winbase; |
364 | dev->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1; |
365 | dev->tx_queue_len = 50; |
366 | dev->netdev_ops = &c101_ops; |
367 | hdlc->attach = sca_attach; |
368 | hdlc->xmit = sca_xmit; |
369 | card->settings.clock_type = CLOCK_EXT; |
370 | |
371 | result = register_hdlc_device(dev); |
372 | if (result) { |
373 | pr_warn("unable to register hdlc device\n" ); |
374 | c101_destroy_card(card); |
375 | return result; |
376 | } |
377 | |
378 | sca_init_port(port: card); /* Set up C101 memory */ |
379 | set_carrier(card); |
380 | |
381 | netdev_info(dev, format: "Moxa C101 on IRQ%u, using %u TX + %u RX packets rings\n" , |
382 | card->irq, card->tx_ring_buffers, card->rx_ring_buffers); |
383 | |
384 | *new_card = card; |
385 | new_card = &card->next_card; |
386 | return 0; |
387 | } |
388 | |
389 | static int __init c101_init(void) |
390 | { |
391 | if (!hw) { |
392 | #ifdef MODULE |
393 | pr_info("no card initialized\n" ); |
394 | #endif |
395 | return -EINVAL; /* no parameters specified, abort */ |
396 | } |
397 | |
398 | pr_info("%s\n" , version); |
399 | |
400 | do { |
401 | unsigned long irq, ram; |
402 | |
403 | irq = simple_strtoul(hw, &hw, 0); |
404 | |
405 | if (*hw++ != ',') |
406 | break; |
407 | ram = simple_strtoul(hw, &hw, 0); |
408 | |
409 | if (*hw == ':' || *hw == '\x0') |
410 | c101_run(irq, winbase: ram); |
411 | |
412 | if (*hw == '\x0') |
413 | return first_card ? 0 : -EINVAL; |
414 | } while (*hw++ == ':'); |
415 | |
416 | pr_err("invalid hardware parameters\n" ); |
417 | return first_card ? 0 : -EINVAL; |
418 | } |
419 | |
420 | static void __exit c101_cleanup(void) |
421 | { |
422 | card_t *card = first_card; |
423 | |
424 | while (card) { |
425 | card_t *ptr = card; |
426 | |
427 | card = card->next_card; |
428 | unregister_hdlc_device(dev: port_to_dev(port: ptr)); |
429 | c101_destroy_card(card: ptr); |
430 | } |
431 | } |
432 | |
433 | module_init(c101_init); |
434 | module_exit(c101_cleanup); |
435 | |
436 | MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>" ); |
437 | MODULE_DESCRIPTION("Moxa C101 serial port driver" ); |
438 | MODULE_LICENSE("GPL v2" ); |
439 | module_param(hw, charp, 0444); |
440 | MODULE_PARM_DESC(hw, "irq,ram:irq,..." ); |
441 | |