1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * LiteX Liteeth Ethernet |
4 | * |
5 | * Copyright 2017 Joel Stanley <joel@jms.id.au> |
6 | * |
7 | */ |
8 | |
9 | #include <linux/etherdevice.h> |
10 | #include <linux/interrupt.h> |
11 | #include <linux/litex.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of_net.h> |
14 | #include <linux/platform_device.h> |
15 | |
16 | #define LITEETH_WRITER_SLOT 0x00 |
17 | #define LITEETH_WRITER_LENGTH 0x04 |
18 | #define LITEETH_WRITER_ERRORS 0x08 |
19 | #define LITEETH_WRITER_EV_STATUS 0x0C |
20 | #define LITEETH_WRITER_EV_PENDING 0x10 |
21 | #define LITEETH_WRITER_EV_ENABLE 0x14 |
22 | #define LITEETH_READER_START 0x18 |
23 | #define LITEETH_READER_READY 0x1C |
24 | #define LITEETH_READER_LEVEL 0x20 |
25 | #define LITEETH_READER_SLOT 0x24 |
26 | #define LITEETH_READER_LENGTH 0x28 |
27 | #define LITEETH_READER_EV_STATUS 0x2C |
28 | #define LITEETH_READER_EV_PENDING 0x30 |
29 | #define LITEETH_READER_EV_ENABLE 0x34 |
30 | #define LITEETH_PREAMBLE_CRC 0x38 |
31 | #define LITEETH_PREAMBLE_ERRORS 0x3C |
32 | #define LITEETH_CRC_ERRORS 0x40 |
33 | |
34 | #define LITEETH_PHY_CRG_RESET 0x00 |
35 | #define LITEETH_MDIO_W 0x04 |
36 | #define LITEETH_MDIO_R 0x0C |
37 | |
38 | #define DRV_NAME "liteeth" |
39 | |
40 | struct liteeth { |
41 | void __iomem *base; |
42 | struct net_device *netdev; |
43 | struct device *dev; |
44 | u32 slot_size; |
45 | |
46 | /* Tx */ |
47 | u32 tx_slot; |
48 | u32 num_tx_slots; |
49 | void __iomem *tx_base; |
50 | |
51 | /* Rx */ |
52 | u32 rx_slot; |
53 | u32 num_rx_slots; |
54 | void __iomem *rx_base; |
55 | }; |
56 | |
57 | static int liteeth_rx(struct net_device *netdev) |
58 | { |
59 | struct liteeth *priv = netdev_priv(dev: netdev); |
60 | struct sk_buff *skb; |
61 | unsigned char *data; |
62 | u8 rx_slot; |
63 | int len; |
64 | |
65 | rx_slot = litex_read8(reg: priv->base + LITEETH_WRITER_SLOT); |
66 | len = litex_read32(reg: priv->base + LITEETH_WRITER_LENGTH); |
67 | |
68 | if (len == 0 || len > 2048) |
69 | goto rx_drop; |
70 | |
71 | skb = netdev_alloc_skb_ip_align(dev: netdev, length: len); |
72 | if (!skb) { |
73 | netdev_err(dev: netdev, format: "couldn't get memory\n" ); |
74 | goto rx_drop; |
75 | } |
76 | |
77 | data = skb_put(skb, len); |
78 | memcpy_fromio(data, priv->rx_base + rx_slot * priv->slot_size, len); |
79 | skb->protocol = eth_type_trans(skb, dev: netdev); |
80 | |
81 | dev_sw_netstats_rx_add(dev: netdev, len); |
82 | |
83 | return netif_rx(skb); |
84 | |
85 | rx_drop: |
86 | netdev->stats.rx_dropped++; |
87 | netdev->stats.rx_errors++; |
88 | |
89 | return NET_RX_DROP; |
90 | } |
91 | |
92 | static irqreturn_t liteeth_interrupt(int irq, void *dev_id) |
93 | { |
94 | struct net_device *netdev = dev_id; |
95 | struct liteeth *priv = netdev_priv(dev: netdev); |
96 | u8 reg; |
97 | |
98 | reg = litex_read8(reg: priv->base + LITEETH_READER_EV_PENDING); |
99 | if (reg) { |
100 | if (netif_queue_stopped(dev: netdev)) |
101 | netif_wake_queue(dev: netdev); |
102 | litex_write8(reg: priv->base + LITEETH_READER_EV_PENDING, val: reg); |
103 | } |
104 | |
105 | reg = litex_read8(reg: priv->base + LITEETH_WRITER_EV_PENDING); |
106 | if (reg) { |
107 | liteeth_rx(netdev); |
108 | litex_write8(reg: priv->base + LITEETH_WRITER_EV_PENDING, val: reg); |
109 | } |
110 | |
111 | return IRQ_HANDLED; |
112 | } |
113 | |
114 | static int liteeth_open(struct net_device *netdev) |
115 | { |
116 | struct liteeth *priv = netdev_priv(dev: netdev); |
117 | int err; |
118 | |
119 | /* Clear pending events */ |
120 | litex_write8(reg: priv->base + LITEETH_WRITER_EV_PENDING, val: 1); |
121 | litex_write8(reg: priv->base + LITEETH_READER_EV_PENDING, val: 1); |
122 | |
123 | err = request_irq(irq: netdev->irq, handler: liteeth_interrupt, flags: 0, name: netdev->name, dev: netdev); |
124 | if (err) { |
125 | netdev_err(dev: netdev, format: "failed to request irq %d\n" , netdev->irq); |
126 | return err; |
127 | } |
128 | |
129 | /* Enable IRQs */ |
130 | litex_write8(reg: priv->base + LITEETH_WRITER_EV_ENABLE, val: 1); |
131 | litex_write8(reg: priv->base + LITEETH_READER_EV_ENABLE, val: 1); |
132 | |
133 | netif_carrier_on(dev: netdev); |
134 | netif_start_queue(dev: netdev); |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | static int liteeth_stop(struct net_device *netdev) |
140 | { |
141 | struct liteeth *priv = netdev_priv(dev: netdev); |
142 | |
143 | netif_stop_queue(dev: netdev); |
144 | netif_carrier_off(dev: netdev); |
145 | |
146 | litex_write8(reg: priv->base + LITEETH_WRITER_EV_ENABLE, val: 0); |
147 | litex_write8(reg: priv->base + LITEETH_READER_EV_ENABLE, val: 0); |
148 | |
149 | free_irq(netdev->irq, netdev); |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | static netdev_tx_t liteeth_start_xmit(struct sk_buff *skb, |
155 | struct net_device *netdev) |
156 | { |
157 | struct liteeth *priv = netdev_priv(dev: netdev); |
158 | void __iomem *txbuffer; |
159 | |
160 | if (!litex_read8(reg: priv->base + LITEETH_READER_READY)) { |
161 | if (net_ratelimit()) |
162 | netdev_err(dev: netdev, format: "LITEETH_READER_READY not ready\n" ); |
163 | |
164 | netif_stop_queue(dev: netdev); |
165 | |
166 | return NETDEV_TX_BUSY; |
167 | } |
168 | |
169 | /* Reject oversize packets */ |
170 | if (unlikely(skb->len > priv->slot_size)) { |
171 | if (net_ratelimit()) |
172 | netdev_err(dev: netdev, format: "tx packet too big\n" ); |
173 | |
174 | dev_kfree_skb_any(skb); |
175 | netdev->stats.tx_dropped++; |
176 | netdev->stats.tx_errors++; |
177 | |
178 | return NETDEV_TX_OK; |
179 | } |
180 | |
181 | txbuffer = priv->tx_base + priv->tx_slot * priv->slot_size; |
182 | memcpy_toio(txbuffer, skb->data, skb->len); |
183 | litex_write8(reg: priv->base + LITEETH_READER_SLOT, val: priv->tx_slot); |
184 | litex_write16(reg: priv->base + LITEETH_READER_LENGTH, val: skb->len); |
185 | litex_write8(reg: priv->base + LITEETH_READER_START, val: 1); |
186 | |
187 | dev_sw_netstats_tx_add(dev: netdev, packets: 1, len: skb->len); |
188 | |
189 | priv->tx_slot = (priv->tx_slot + 1) % priv->num_tx_slots; |
190 | dev_kfree_skb_any(skb); |
191 | |
192 | return NETDEV_TX_OK; |
193 | } |
194 | |
195 | static void |
196 | liteeth_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) |
197 | { |
198 | netdev_stats_to_stats64(stats64: stats, netdev_stats: &netdev->stats); |
199 | dev_fetch_sw_netstats(s: stats, netstats: netdev->tstats); |
200 | } |
201 | |
202 | static const struct net_device_ops liteeth_netdev_ops = { |
203 | .ndo_open = liteeth_open, |
204 | .ndo_stop = liteeth_stop, |
205 | .ndo_get_stats64 = liteeth_get_stats64, |
206 | .ndo_start_xmit = liteeth_start_xmit, |
207 | }; |
208 | |
209 | static void liteeth_setup_slots(struct liteeth *priv) |
210 | { |
211 | struct device_node *np = priv->dev->of_node; |
212 | int err; |
213 | |
214 | err = of_property_read_u32(np, propname: "litex,rx-slots" , out_value: &priv->num_rx_slots); |
215 | if (err) { |
216 | dev_dbg(priv->dev, "unable to get litex,rx-slots, using 2\n" ); |
217 | priv->num_rx_slots = 2; |
218 | } |
219 | |
220 | err = of_property_read_u32(np, propname: "litex,tx-slots" , out_value: &priv->num_tx_slots); |
221 | if (err) { |
222 | dev_dbg(priv->dev, "unable to get litex,tx-slots, using 2\n" ); |
223 | priv->num_tx_slots = 2; |
224 | } |
225 | |
226 | err = of_property_read_u32(np, propname: "litex,slot-size" , out_value: &priv->slot_size); |
227 | if (err) { |
228 | dev_dbg(priv->dev, "unable to get litex,slot-size, using 0x800\n" ); |
229 | priv->slot_size = 0x800; |
230 | } |
231 | } |
232 | |
233 | static int liteeth_probe(struct platform_device *pdev) |
234 | { |
235 | struct net_device *netdev; |
236 | void __iomem *buf_base; |
237 | struct liteeth *priv; |
238 | int irq, err; |
239 | |
240 | netdev = devm_alloc_etherdev(&pdev->dev, sizeof(*priv)); |
241 | if (!netdev) |
242 | return -ENOMEM; |
243 | |
244 | SET_NETDEV_DEV(netdev, &pdev->dev); |
245 | platform_set_drvdata(pdev, data: netdev); |
246 | |
247 | priv = netdev_priv(dev: netdev); |
248 | priv->netdev = netdev; |
249 | priv->dev = &pdev->dev; |
250 | |
251 | netdev->tstats = devm_netdev_alloc_pcpu_stats(&pdev->dev, |
252 | struct pcpu_sw_netstats); |
253 | if (!netdev->tstats) |
254 | return -ENOMEM; |
255 | |
256 | irq = platform_get_irq(pdev, 0); |
257 | if (irq < 0) |
258 | return irq; |
259 | netdev->irq = irq; |
260 | |
261 | priv->base = devm_platform_ioremap_resource_byname(pdev, name: "mac" ); |
262 | if (IS_ERR(ptr: priv->base)) |
263 | return PTR_ERR(ptr: priv->base); |
264 | |
265 | buf_base = devm_platform_ioremap_resource_byname(pdev, name: "buffer" ); |
266 | if (IS_ERR(ptr: buf_base)) |
267 | return PTR_ERR(ptr: buf_base); |
268 | |
269 | liteeth_setup_slots(priv); |
270 | |
271 | /* Rx slots */ |
272 | priv->rx_base = buf_base; |
273 | priv->rx_slot = 0; |
274 | |
275 | /* Tx slots come after Rx slots */ |
276 | priv->tx_base = buf_base + priv->num_rx_slots * priv->slot_size; |
277 | priv->tx_slot = 0; |
278 | |
279 | err = of_get_ethdev_address(np: pdev->dev.of_node, dev: netdev); |
280 | if (err) |
281 | eth_hw_addr_random(dev: netdev); |
282 | |
283 | netdev->netdev_ops = &liteeth_netdev_ops; |
284 | |
285 | err = register_netdev(dev: netdev); |
286 | if (err) { |
287 | dev_err(&pdev->dev, "Failed to register netdev %d\n" , err); |
288 | return err; |
289 | } |
290 | |
291 | netdev_info(dev: netdev, format: "irq %d slots: tx %d rx %d size %d\n" , |
292 | netdev->irq, priv->num_tx_slots, priv->num_rx_slots, priv->slot_size); |
293 | |
294 | return 0; |
295 | } |
296 | |
297 | static void liteeth_remove(struct platform_device *pdev) |
298 | { |
299 | struct net_device *netdev = platform_get_drvdata(pdev); |
300 | |
301 | unregister_netdev(dev: netdev); |
302 | } |
303 | |
304 | static const struct of_device_id liteeth_of_match[] = { |
305 | { .compatible = "litex,liteeth" }, |
306 | { } |
307 | }; |
308 | MODULE_DEVICE_TABLE(of, liteeth_of_match); |
309 | |
310 | static struct platform_driver liteeth_driver = { |
311 | .probe = liteeth_probe, |
312 | .remove_new = liteeth_remove, |
313 | .driver = { |
314 | .name = DRV_NAME, |
315 | .of_match_table = liteeth_of_match, |
316 | }, |
317 | }; |
318 | module_platform_driver(liteeth_driver); |
319 | |
320 | MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>" ); |
321 | MODULE_DESCRIPTION("LiteX Liteeth Ethernet driver" ); |
322 | MODULE_LICENSE("GPL" ); |
323 | |