1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* hplance.c : the Linux/hp300/lance ethernet driver |
3 | * |
4 | * Copyright (C) 05/1998 Peter Maydell <pmaydell@chiark.greenend.org.uk> |
5 | * Based on the Sun Lance driver and the NetBSD HP Lance driver |
6 | * Uses the generic 7990.c LANCE code. |
7 | */ |
8 | |
9 | #include <linux/module.h> |
10 | #include <linux/kernel.h> |
11 | #include <linux/types.h> |
12 | #include <linux/interrupt.h> |
13 | #include <linux/ioport.h> |
14 | #include <linux/string.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/init.h> |
17 | #include <linux/errno.h> |
18 | #include <linux/pgtable.h> |
19 | /* Used for the temporal inet entries and routing */ |
20 | #include <linux/socket.h> |
21 | #include <linux/route.h> |
22 | #include <linux/dio.h> |
23 | #include <linux/netdevice.h> |
24 | #include <linux/etherdevice.h> |
25 | #include <linux/skbuff.h> |
26 | |
27 | #include <asm/io.h> |
28 | |
29 | #include "hplance.h" |
30 | |
31 | /* We have 16392 bytes of RAM for the init block and buffers. This places |
32 | * an upper limit on the number of buffers we can use. NetBSD uses 8 Rx |
33 | * buffers and 2 Tx buffers, it takes (8 + 2) * 1544 bytes. |
34 | */ |
35 | #define LANCE_LOG_TX_BUFFERS 1 |
36 | #define LANCE_LOG_RX_BUFFERS 3 |
37 | |
38 | #include "7990.h" /* use generic LANCE code */ |
39 | |
40 | /* Our private data structure */ |
41 | struct hplance_private { |
42 | struct lance_private lance; |
43 | }; |
44 | |
45 | /* function prototypes... This is easy because all the grot is in the |
46 | * generic LANCE support. All we have to support is probing for boards, |
47 | * plus board-specific init, open and close actions. |
48 | * Oh, and we need to tell the generic code how to read and write LANCE registers... |
49 | */ |
50 | static int hplance_init_one(struct dio_dev *d, const struct dio_device_id *ent); |
51 | static void hplance_init(struct net_device *dev, struct dio_dev *d); |
52 | static void hplance_remove_one(struct dio_dev *d); |
53 | static void hplance_writerap(void *priv, unsigned short value); |
54 | static void hplance_writerdp(void *priv, unsigned short value); |
55 | static unsigned short hplance_readrdp(void *priv); |
56 | static int hplance_open(struct net_device *dev); |
57 | static int hplance_close(struct net_device *dev); |
58 | |
59 | static struct dio_device_id hplance_dio_tbl[] = { |
60 | { DIO_ID_LAN }, |
61 | { 0 } |
62 | }; |
63 | |
64 | static struct dio_driver hplance_driver = { |
65 | .name = "hplance" , |
66 | .id_table = hplance_dio_tbl, |
67 | .probe = hplance_init_one, |
68 | .remove = hplance_remove_one, |
69 | }; |
70 | |
71 | static const struct net_device_ops hplance_netdev_ops = { |
72 | .ndo_open = hplance_open, |
73 | .ndo_stop = hplance_close, |
74 | .ndo_start_xmit = lance_start_xmit, |
75 | .ndo_set_rx_mode = lance_set_multicast, |
76 | .ndo_validate_addr = eth_validate_addr, |
77 | .ndo_set_mac_address = eth_mac_addr, |
78 | #ifdef CONFIG_NET_POLL_CONTROLLER |
79 | .ndo_poll_controller = lance_poll, |
80 | #endif |
81 | }; |
82 | |
83 | /* Find all the HP Lance boards and initialise them... */ |
84 | static int hplance_init_one(struct dio_dev *d, const struct dio_device_id *ent) |
85 | { |
86 | struct net_device *dev; |
87 | int err = -ENOMEM; |
88 | |
89 | dev = alloc_etherdev(sizeof(struct hplance_private)); |
90 | if (!dev) |
91 | goto out; |
92 | |
93 | err = -EBUSY; |
94 | if (!request_mem_region(dio_resource_start(d), |
95 | dio_resource_len(d), d->name)) |
96 | goto out_free_netdev; |
97 | |
98 | hplance_init(dev, d); |
99 | err = register_netdev(dev); |
100 | if (err) |
101 | goto out_release_mem_region; |
102 | |
103 | dio_set_drvdata(d, data: dev); |
104 | |
105 | printk(KERN_INFO "%s: %s; select code %d, addr %pM, irq %d\n" , |
106 | dev->name, d->name, d->scode, dev->dev_addr, d->ipl); |
107 | |
108 | return 0; |
109 | |
110 | out_release_mem_region: |
111 | release_mem_region(dio_resource_start(d), dio_resource_len(d)); |
112 | out_free_netdev: |
113 | free_netdev(dev); |
114 | out: |
115 | return err; |
116 | } |
117 | |
118 | static void hplance_remove_one(struct dio_dev *d) |
119 | { |
120 | struct net_device *dev = dio_get_drvdata(d); |
121 | |
122 | unregister_netdev(dev); |
123 | release_mem_region(dio_resource_start(d), dio_resource_len(d)); |
124 | free_netdev(dev); |
125 | } |
126 | |
127 | /* Initialise a single lance board at the given DIO device */ |
128 | static void hplance_init(struct net_device *dev, struct dio_dev *d) |
129 | { |
130 | unsigned long va = (d->resource.start + DIO_VIRADDRBASE); |
131 | struct hplance_private *lp; |
132 | u8 addr[ETH_ALEN]; |
133 | int i; |
134 | |
135 | /* reset the board */ |
136 | out_8(va + DIO_IDOFF, 0xff); |
137 | udelay(100); /* ariba! ariba! udelay! udelay! */ |
138 | |
139 | /* Fill the dev fields */ |
140 | dev->base_addr = va; |
141 | dev->netdev_ops = &hplance_netdev_ops; |
142 | dev->dma = 0; |
143 | |
144 | for (i = 0; i < 6; i++) { |
145 | /* The NVRAM holds our ethernet address, one nibble per byte, |
146 | * at bytes NVRAMOFF+1,3,5,7,9... |
147 | */ |
148 | addr[i] = ((in_8(va + HPLANCE_NVRAMOFF + i*4 + 1) & 0xF) << 4) |
149 | | (in_8(va + HPLANCE_NVRAMOFF + i*4 + 3) & 0xF); |
150 | } |
151 | eth_hw_addr_set(dev, addr); |
152 | |
153 | lp = netdev_priv(dev); |
154 | lp->lance.name = d->name; |
155 | lp->lance.base = va; |
156 | lp->lance.init_block = (struct lance_init_block *)(va + HPLANCE_MEMOFF); /* CPU addr */ |
157 | lp->lance.lance_init_block = NULL; /* LANCE addr of same RAM */ |
158 | lp->lance.busmaster_regval = LE_C3_BSWP; /* we're bigendian */ |
159 | lp->lance.irq = d->ipl; |
160 | lp->lance.writerap = hplance_writerap; |
161 | lp->lance.writerdp = hplance_writerdp; |
162 | lp->lance.readrdp = hplance_readrdp; |
163 | lp->lance.lance_log_rx_bufs = LANCE_LOG_RX_BUFFERS; |
164 | lp->lance.lance_log_tx_bufs = LANCE_LOG_TX_BUFFERS; |
165 | lp->lance.rx_ring_mod_mask = RX_RING_MOD_MASK; |
166 | lp->lance.tx_ring_mod_mask = TX_RING_MOD_MASK; |
167 | } |
168 | |
169 | /* This is disgusting. We have to check the DIO status register for ack every |
170 | * time we read or write the LANCE registers. |
171 | */ |
172 | static void hplance_writerap(void *priv, unsigned short value) |
173 | { |
174 | struct lance_private *lp = (struct lance_private *)priv; |
175 | |
176 | do { |
177 | out_be16(lp->base + HPLANCE_REGOFF + LANCE_RAP, value); |
178 | } while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0); |
179 | } |
180 | |
181 | static void hplance_writerdp(void *priv, unsigned short value) |
182 | { |
183 | struct lance_private *lp = (struct lance_private *)priv; |
184 | |
185 | do { |
186 | out_be16(lp->base + HPLANCE_REGOFF + LANCE_RDP, value); |
187 | } while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0); |
188 | } |
189 | |
190 | static unsigned short hplance_readrdp(void *priv) |
191 | { |
192 | struct lance_private *lp = (struct lance_private *)priv; |
193 | __u16 value; |
194 | |
195 | do { |
196 | value = in_be16(lp->base + HPLANCE_REGOFF + LANCE_RDP); |
197 | } while ((in_8(lp->base + HPLANCE_STATUS) & LE_ACK) == 0); |
198 | return value; |
199 | } |
200 | |
201 | static int hplance_open(struct net_device *dev) |
202 | { |
203 | int status; |
204 | struct lance_private *lp = netdev_priv(dev); |
205 | |
206 | status = lance_open(dev); /* call generic lance open code */ |
207 | if (status) |
208 | return status; |
209 | /* enable interrupts at board level. */ |
210 | out_8(lp->base + HPLANCE_STATUS, LE_IE); |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | static int hplance_close(struct net_device *dev) |
216 | { |
217 | struct lance_private *lp = netdev_priv(dev); |
218 | |
219 | out_8(lp->base + HPLANCE_STATUS, 0); /* disable interrupts at boardlevel */ |
220 | lance_close(dev); |
221 | return 0; |
222 | } |
223 | |
224 | static int __init hplance_init_module(void) |
225 | { |
226 | return dio_register_driver(&hplance_driver); |
227 | } |
228 | |
229 | static void __exit hplance_cleanup_module(void) |
230 | { |
231 | dio_unregister_driver(&hplance_driver); |
232 | } |
233 | |
234 | module_init(hplance_init_module); |
235 | module_exit(hplance_cleanup_module); |
236 | |
237 | MODULE_LICENSE("GPL" ); |
238 | |