1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2022 Davicom Semiconductor,Inc. |
4 | * Davicom DM9051 SPI Fast Ethernet Linux driver |
5 | */ |
6 | |
7 | #include <linux/etherdevice.h> |
8 | #include <linux/ethtool.h> |
9 | #include <linux/interrupt.h> |
10 | #include <linux/iopoll.h> |
11 | #include <linux/irq.h> |
12 | #include <linux/mii.h> |
13 | #include <linux/module.h> |
14 | #include <linux/netdevice.h> |
15 | #include <linux/phy.h> |
16 | #include <linux/regmap.h> |
17 | #include <linux/skbuff.h> |
18 | #include <linux/spinlock.h> |
19 | #include <linux/spi/spi.h> |
20 | #include <linux/types.h> |
21 | |
22 | #include "dm9051.h" |
23 | |
24 | #define DRVNAME_9051 "dm9051" |
25 | |
26 | /** |
27 | * struct rx_ctl_mach - rx activities record |
28 | * @status_err_counter: rx status error counter |
29 | * @large_err_counter: rx get large packet length error counter |
30 | * @rx_err_counter: receive packet error counter |
31 | * @tx_err_counter: transmit packet error counter |
32 | * @fifo_rst_counter: reset operation counter |
33 | * |
34 | * To keep track for the driver operation statistics |
35 | */ |
36 | struct rx_ctl_mach { |
37 | u16 status_err_counter; |
38 | u16 large_err_counter; |
39 | u16 rx_err_counter; |
40 | u16 tx_err_counter; |
41 | u16 fifo_rst_counter; |
42 | }; |
43 | |
44 | /** |
45 | * struct dm9051_rxctrl - dm9051 driver rx control |
46 | * @hash_table: Multicast hash-table data |
47 | * @rcr_all: KS_RXCR1 register setting |
48 | * |
49 | * The settings needs to control the receive filtering |
50 | * such as the multicast hash-filter and the receive register settings |
51 | */ |
52 | struct dm9051_rxctrl { |
53 | u16 hash_table[4]; |
54 | u8 rcr_all; |
55 | }; |
56 | |
57 | /** |
58 | * struct dm9051_rxhdr - rx packet data header |
59 | * @headbyte: lead byte equal to 0x01 notifies a valid packet |
60 | * @status: status bits for the received packet |
61 | * @rxlen: packet length |
62 | * |
63 | * The Rx packed, entered into the FIFO memory, start with these |
64 | * four bytes which is the Rx header, followed by the ethernet |
65 | * packet data and ends with an appended 4-byte CRC data. |
66 | * Both Rx packet and CRC data are for check purpose and finally |
67 | * are dropped by this driver |
68 | */ |
69 | struct dm9051_rxhdr { |
70 | u8 headbyte; |
71 | u8 status; |
72 | __le16 rxlen; |
73 | }; |
74 | |
75 | /** |
76 | * struct board_info - maintain the saved data |
77 | * @spidev: spi device structure |
78 | * @ndev: net device structure |
79 | * @mdiobus: mii bus structure |
80 | * @phydev: phy device structure |
81 | * @txq: tx queue structure |
82 | * @regmap_dm: regmap for register read/write |
83 | * @regmap_dmbulk: extra regmap for bulk read/write |
84 | * @rxctrl_work: Work queue for updating RX mode and multicast lists |
85 | * @tx_work: Work queue for tx packets |
86 | * @pause: ethtool pause parameter structure |
87 | * @spi_lockm: between threads lock structure |
88 | * @reg_mutex: regmap access lock structure |
89 | * @bc: rx control statistics structure |
90 | * @rxhdr: rx header structure |
91 | * @rctl: rx control setting structure |
92 | * @msg_enable: message level value |
93 | * @imr_all: to store operating imr value for register DM9051_IMR |
94 | * @lcr_all: to store operating rcr value for register DM9051_LMCR |
95 | * |
96 | * The saved data variables, keep up to date for retrieval back to use |
97 | */ |
98 | struct board_info { |
99 | u32 msg_enable; |
100 | struct spi_device *spidev; |
101 | struct net_device *ndev; |
102 | struct mii_bus *mdiobus; |
103 | struct phy_device *phydev; |
104 | struct sk_buff_head txq; |
105 | struct regmap *regmap_dm; |
106 | struct regmap *regmap_dmbulk; |
107 | struct work_struct rxctrl_work; |
108 | struct work_struct tx_work; |
109 | struct ethtool_pauseparam pause; |
110 | struct mutex spi_lockm; |
111 | struct mutex reg_mutex; |
112 | struct rx_ctl_mach bc; |
113 | struct dm9051_rxhdr rxhdr; |
114 | struct dm9051_rxctrl rctl; |
115 | u8 imr_all; |
116 | u8 lcr_all; |
117 | }; |
118 | |
119 | static int dm9051_set_reg(struct board_info *db, unsigned int reg, unsigned int val) |
120 | { |
121 | int ret; |
122 | |
123 | ret = regmap_write(map: db->regmap_dm, reg, val); |
124 | if (ret < 0) |
125 | netif_err(db, drv, db->ndev, "%s: error %d set reg %02x\n" , |
126 | __func__, ret, reg); |
127 | return ret; |
128 | } |
129 | |
130 | static int dm9051_update_bits(struct board_info *db, unsigned int reg, unsigned int mask, |
131 | unsigned int val) |
132 | { |
133 | int ret; |
134 | |
135 | ret = regmap_update_bits(map: db->regmap_dm, reg, mask, val); |
136 | if (ret < 0) |
137 | netif_err(db, drv, db->ndev, "%s: error %d update bits reg %02x\n" , |
138 | __func__, ret, reg); |
139 | return ret; |
140 | } |
141 | |
142 | /* skb buffer exhausted, just discard the received data |
143 | */ |
144 | static int dm9051_dumpblk(struct board_info *db, u8 reg, size_t count) |
145 | { |
146 | struct net_device *ndev = db->ndev; |
147 | unsigned int rb; |
148 | int ret; |
149 | |
150 | /* no skb buffer, |
151 | * both reg and &rb must be noinc, |
152 | * read once one byte via regmap_read |
153 | */ |
154 | do { |
155 | ret = regmap_read(map: db->regmap_dm, reg, val: &rb); |
156 | if (ret < 0) { |
157 | netif_err(db, drv, ndev, "%s: error %d dumping read reg %02x\n" , |
158 | __func__, ret, reg); |
159 | break; |
160 | } |
161 | } while (--count); |
162 | |
163 | return ret; |
164 | } |
165 | |
166 | static int dm9051_set_regs(struct board_info *db, unsigned int reg, const void *val, |
167 | size_t val_count) |
168 | { |
169 | int ret; |
170 | |
171 | ret = regmap_bulk_write(map: db->regmap_dmbulk, reg, val, val_count); |
172 | if (ret < 0) |
173 | netif_err(db, drv, db->ndev, "%s: error %d bulk writing regs %02x\n" , |
174 | __func__, ret, reg); |
175 | return ret; |
176 | } |
177 | |
178 | static int dm9051_get_regs(struct board_info *db, unsigned int reg, void *val, |
179 | size_t val_count) |
180 | { |
181 | int ret; |
182 | |
183 | ret = regmap_bulk_read(map: db->regmap_dmbulk, reg, val, val_count); |
184 | if (ret < 0) |
185 | netif_err(db, drv, db->ndev, "%s: error %d bulk reading regs %02x\n" , |
186 | __func__, ret, reg); |
187 | return ret; |
188 | } |
189 | |
190 | static int dm9051_write_mem(struct board_info *db, unsigned int reg, const void *buff, |
191 | size_t len) |
192 | { |
193 | int ret; |
194 | |
195 | ret = regmap_noinc_write(map: db->regmap_dm, reg, val: buff, val_len: len); |
196 | if (ret < 0) |
197 | netif_err(db, drv, db->ndev, "%s: error %d noinc writing regs %02x\n" , |
198 | __func__, ret, reg); |
199 | return ret; |
200 | } |
201 | |
202 | static int dm9051_read_mem(struct board_info *db, unsigned int reg, void *buff, |
203 | size_t len) |
204 | { |
205 | int ret; |
206 | |
207 | ret = regmap_noinc_read(map: db->regmap_dm, reg, val: buff, val_len: len); |
208 | if (ret < 0) |
209 | netif_err(db, drv, db->ndev, "%s: error %d noinc reading regs %02x\n" , |
210 | __func__, ret, reg); |
211 | return ret; |
212 | } |
213 | |
214 | /* waiting tx-end rather than tx-req |
215 | * got faster |
216 | */ |
217 | static int dm9051_nsr_poll(struct board_info *db) |
218 | { |
219 | unsigned int mval; |
220 | int ret; |
221 | |
222 | ret = regmap_read_poll_timeout(db->regmap_dm, DM9051_NSR, mval, |
223 | mval & (NSR_TX2END | NSR_TX1END), 1, 20); |
224 | if (ret == -ETIMEDOUT) |
225 | netdev_err(dev: db->ndev, format: "timeout in checking for tx end\n" ); |
226 | return ret; |
227 | } |
228 | |
229 | static int dm9051_epcr_poll(struct board_info *db) |
230 | { |
231 | unsigned int mval; |
232 | int ret; |
233 | |
234 | ret = regmap_read_poll_timeout(db->regmap_dm, DM9051_EPCR, mval, |
235 | !(mval & EPCR_ERRE), 100, 10000); |
236 | if (ret == -ETIMEDOUT) |
237 | netdev_err(dev: db->ndev, format: "eeprom/phy in processing get timeout\n" ); |
238 | return ret; |
239 | } |
240 | |
241 | static int dm9051_irq_flag(struct board_info *db) |
242 | { |
243 | struct spi_device *spi = db->spidev; |
244 | int irq_type = irq_get_trigger_type(irq: spi->irq); |
245 | |
246 | if (irq_type) |
247 | return irq_type; |
248 | |
249 | return IRQF_TRIGGER_LOW; |
250 | } |
251 | |
252 | static unsigned int dm9051_intcr_value(struct board_info *db) |
253 | { |
254 | return (dm9051_irq_flag(db) == IRQF_TRIGGER_LOW) ? |
255 | INTCR_POL_LOW : INTCR_POL_HIGH; |
256 | } |
257 | |
258 | static int dm9051_set_fcr(struct board_info *db) |
259 | { |
260 | u8 fcr = 0; |
261 | |
262 | if (db->pause.rx_pause) |
263 | fcr |= FCR_BKPM | FCR_FLCE; |
264 | if (db->pause.tx_pause) |
265 | fcr |= FCR_TXPEN; |
266 | |
267 | return dm9051_set_reg(db, DM9051_FCR, val: fcr); |
268 | } |
269 | |
270 | static int dm9051_set_recv(struct board_info *db) |
271 | { |
272 | int ret; |
273 | |
274 | ret = dm9051_set_regs(db, DM9051_MAR, val: db->rctl.hash_table, val_count: sizeof(db->rctl.hash_table)); |
275 | if (ret) |
276 | return ret; |
277 | |
278 | return dm9051_set_reg(db, DM9051_RCR, val: db->rctl.rcr_all); /* enable rx */ |
279 | } |
280 | |
281 | static int dm9051_core_reset(struct board_info *db) |
282 | { |
283 | int ret; |
284 | |
285 | db->bc.fifo_rst_counter++; |
286 | |
287 | ret = regmap_write(map: db->regmap_dm, DM9051_NCR, NCR_RST); /* NCR reset */ |
288 | if (ret) |
289 | return ret; |
290 | ret = regmap_write(map: db->regmap_dm, DM9051_MBNDRY, MBNDRY_BYTE); /* MemBound */ |
291 | if (ret) |
292 | return ret; |
293 | ret = regmap_write(map: db->regmap_dm, DM9051_PPCR, PPCR_PAUSE_COUNT); /* Pause Count */ |
294 | if (ret) |
295 | return ret; |
296 | ret = regmap_write(map: db->regmap_dm, DM9051_LMCR, val: db->lcr_all); /* LEDMode1 */ |
297 | if (ret) |
298 | return ret; |
299 | |
300 | return dm9051_set_reg(db, DM9051_INTCR, val: dm9051_intcr_value(db)); |
301 | } |
302 | |
303 | static int dm9051_update_fcr(struct board_info *db) |
304 | { |
305 | u8 fcr = 0; |
306 | |
307 | if (db->pause.rx_pause) |
308 | fcr |= FCR_BKPM | FCR_FLCE; |
309 | if (db->pause.tx_pause) |
310 | fcr |= FCR_TXPEN; |
311 | |
312 | return dm9051_update_bits(db, DM9051_FCR, FCR_RXTX_BITS, val: fcr); |
313 | } |
314 | |
315 | static int dm9051_disable_interrupt(struct board_info *db) |
316 | { |
317 | return dm9051_set_reg(db, DM9051_IMR, IMR_PAR); /* disable int */ |
318 | } |
319 | |
320 | static int dm9051_enable_interrupt(struct board_info *db) |
321 | { |
322 | return dm9051_set_reg(db, DM9051_IMR, val: db->imr_all); /* enable int */ |
323 | } |
324 | |
325 | static int dm9051_stop_mrcmd(struct board_info *db) |
326 | { |
327 | return dm9051_set_reg(db, DM9051_ISR, ISR_STOP_MRCMD); /* to stop mrcmd */ |
328 | } |
329 | |
330 | static int dm9051_clear_interrupt(struct board_info *db) |
331 | { |
332 | return dm9051_update_bits(db, DM9051_ISR, ISR_CLR_INT, ISR_CLR_INT); |
333 | } |
334 | |
335 | static int dm9051_eeprom_read(struct board_info *db, int offset, u8 *to) |
336 | { |
337 | int ret; |
338 | |
339 | ret = regmap_write(map: db->regmap_dm, DM9051_EPAR, val: offset); |
340 | if (ret) |
341 | return ret; |
342 | |
343 | ret = regmap_write(map: db->regmap_dm, DM9051_EPCR, EPCR_ERPRR); |
344 | if (ret) |
345 | return ret; |
346 | |
347 | ret = dm9051_epcr_poll(db); |
348 | if (ret) |
349 | return ret; |
350 | |
351 | ret = regmap_write(map: db->regmap_dm, DM9051_EPCR, val: 0); |
352 | if (ret) |
353 | return ret; |
354 | |
355 | return regmap_bulk_read(map: db->regmap_dmbulk, DM9051_EPDRL, val: to, val_count: 2); |
356 | } |
357 | |
358 | static int dm9051_eeprom_write(struct board_info *db, int offset, u8 *data) |
359 | { |
360 | int ret; |
361 | |
362 | ret = regmap_write(map: db->regmap_dm, DM9051_EPAR, val: offset); |
363 | if (ret) |
364 | return ret; |
365 | |
366 | ret = regmap_bulk_write(map: db->regmap_dmbulk, DM9051_EPDRL, val: data, val_count: 2); |
367 | if (ret < 0) |
368 | return ret; |
369 | |
370 | ret = regmap_write(map: db->regmap_dm, DM9051_EPCR, EPCR_WEP | EPCR_ERPRW); |
371 | if (ret) |
372 | return ret; |
373 | |
374 | ret = dm9051_epcr_poll(db); |
375 | if (ret) |
376 | return ret; |
377 | |
378 | return regmap_write(map: db->regmap_dm, DM9051_EPCR, val: 0); |
379 | } |
380 | |
381 | static int dm9051_phyread(void *context, unsigned int reg, unsigned int *val) |
382 | { |
383 | struct board_info *db = context; |
384 | int ret; |
385 | |
386 | ret = regmap_write(map: db->regmap_dm, DM9051_EPAR, DM9051_PHY | reg); |
387 | if (ret) |
388 | return ret; |
389 | |
390 | ret = regmap_write(map: db->regmap_dm, DM9051_EPCR, EPCR_ERPRR | EPCR_EPOS); |
391 | if (ret) |
392 | return ret; |
393 | |
394 | ret = dm9051_epcr_poll(db); |
395 | if (ret) |
396 | return ret; |
397 | |
398 | ret = regmap_write(map: db->regmap_dm, DM9051_EPCR, val: 0); |
399 | if (ret) |
400 | return ret; |
401 | |
402 | /* this is a 4 bytes data, clear to zero since following regmap_bulk_read |
403 | * only fill lower 2 bytes |
404 | */ |
405 | *val = 0; |
406 | return regmap_bulk_read(map: db->regmap_dmbulk, DM9051_EPDRL, val, val_count: 2); |
407 | } |
408 | |
409 | static int dm9051_phywrite(void *context, unsigned int reg, unsigned int val) |
410 | { |
411 | struct board_info *db = context; |
412 | int ret; |
413 | |
414 | ret = regmap_write(map: db->regmap_dm, DM9051_EPAR, DM9051_PHY | reg); |
415 | if (ret) |
416 | return ret; |
417 | |
418 | ret = regmap_bulk_write(map: db->regmap_dmbulk, DM9051_EPDRL, val: &val, val_count: 2); |
419 | if (ret < 0) |
420 | return ret; |
421 | |
422 | ret = regmap_write(map: db->regmap_dm, DM9051_EPCR, EPCR_EPOS | EPCR_ERPRW); |
423 | if (ret) |
424 | return ret; |
425 | |
426 | ret = dm9051_epcr_poll(db); |
427 | if (ret) |
428 | return ret; |
429 | |
430 | return regmap_write(map: db->regmap_dm, DM9051_EPCR, val: 0); |
431 | } |
432 | |
433 | static int dm9051_mdio_read(struct mii_bus *bus, int addr, int regnum) |
434 | { |
435 | struct board_info *db = bus->priv; |
436 | unsigned int val = 0xffff; |
437 | int ret; |
438 | |
439 | if (addr == DM9051_PHY_ADDR) { |
440 | ret = dm9051_phyread(context: db, reg: regnum, val: &val); |
441 | if (ret) |
442 | return ret; |
443 | } |
444 | |
445 | return val; |
446 | } |
447 | |
448 | static int dm9051_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) |
449 | { |
450 | struct board_info *db = bus->priv; |
451 | |
452 | if (addr == DM9051_PHY_ADDR) |
453 | return dm9051_phywrite(context: db, reg: regnum, val); |
454 | |
455 | return -ENODEV; |
456 | } |
457 | |
458 | static void dm9051_reg_lock_mutex(void *dbcontext) |
459 | { |
460 | struct board_info *db = dbcontext; |
461 | |
462 | mutex_lock(&db->reg_mutex); |
463 | } |
464 | |
465 | static void dm9051_reg_unlock_mutex(void *dbcontext) |
466 | { |
467 | struct board_info *db = dbcontext; |
468 | |
469 | mutex_unlock(lock: &db->reg_mutex); |
470 | } |
471 | |
472 | static struct regmap_config regconfigdm = { |
473 | .reg_bits = 8, |
474 | .val_bits = 8, |
475 | .max_register = 0xff, |
476 | .reg_stride = 1, |
477 | .cache_type = REGCACHE_NONE, |
478 | .read_flag_mask = 0, |
479 | .write_flag_mask = DM_SPI_WR, |
480 | .val_format_endian = REGMAP_ENDIAN_LITTLE, |
481 | .lock = dm9051_reg_lock_mutex, |
482 | .unlock = dm9051_reg_unlock_mutex, |
483 | }; |
484 | |
485 | static struct regmap_config regconfigdmbulk = { |
486 | .reg_bits = 8, |
487 | .val_bits = 8, |
488 | .max_register = 0xff, |
489 | .reg_stride = 1, |
490 | .cache_type = REGCACHE_NONE, |
491 | .read_flag_mask = 0, |
492 | .write_flag_mask = DM_SPI_WR, |
493 | .val_format_endian = REGMAP_ENDIAN_LITTLE, |
494 | .lock = dm9051_reg_lock_mutex, |
495 | .unlock = dm9051_reg_unlock_mutex, |
496 | .use_single_read = true, |
497 | .use_single_write = true, |
498 | }; |
499 | |
500 | static int dm9051_map_init(struct spi_device *spi, struct board_info *db) |
501 | { |
502 | /* create two regmap instances, |
503 | * split read/write and bulk_read/bulk_write to individual regmap |
504 | * to resolve regmap execution confliction problem |
505 | */ |
506 | regconfigdm.lock_arg = db; |
507 | db->regmap_dm = devm_regmap_init_spi(db->spidev, ®configdm); |
508 | if (IS_ERR(ptr: db->regmap_dm)) |
509 | return PTR_ERR(ptr: db->regmap_dm); |
510 | |
511 | regconfigdmbulk.lock_arg = db; |
512 | db->regmap_dmbulk = devm_regmap_init_spi(db->spidev, ®configdmbulk); |
513 | return PTR_ERR_OR_ZERO(ptr: db->regmap_dmbulk); |
514 | } |
515 | |
516 | static int dm9051_map_chipid(struct board_info *db) |
517 | { |
518 | struct device *dev = &db->spidev->dev; |
519 | unsigned short wid; |
520 | u8 buff[6]; |
521 | int ret; |
522 | |
523 | ret = dm9051_get_regs(db, DM9051_VIDL, val: buff, val_count: sizeof(buff)); |
524 | if (ret < 0) |
525 | return ret; |
526 | |
527 | wid = get_unaligned_le16(p: buff + 2); |
528 | if (wid != DM9051_ID) { |
529 | dev_err(dev, "chipid error as %04x !\n" , wid); |
530 | return -ENODEV; |
531 | } |
532 | |
533 | dev_info(dev, "chip %04x found\n" , wid); |
534 | return 0; |
535 | } |
536 | |
537 | /* Read DM9051_PAR registers which is the mac address loaded from EEPROM while power-on |
538 | */ |
539 | static int dm9051_map_etherdev_par(struct net_device *ndev, struct board_info *db) |
540 | { |
541 | u8 addr[ETH_ALEN]; |
542 | int ret; |
543 | |
544 | ret = dm9051_get_regs(db, DM9051_PAR, val: addr, val_count: sizeof(addr)); |
545 | if (ret < 0) |
546 | return ret; |
547 | |
548 | if (!is_valid_ether_addr(addr)) { |
549 | eth_hw_addr_random(dev: ndev); |
550 | |
551 | ret = dm9051_set_regs(db, DM9051_PAR, val: ndev->dev_addr, val_count: sizeof(ndev->dev_addr)); |
552 | if (ret < 0) |
553 | return ret; |
554 | |
555 | dev_dbg(&db->spidev->dev, "Use random MAC address\n" ); |
556 | return 0; |
557 | } |
558 | |
559 | eth_hw_addr_set(dev: ndev, addr); |
560 | return 0; |
561 | } |
562 | |
563 | /* ethtool-ops |
564 | */ |
565 | static void dm9051_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) |
566 | { |
567 | strscpy(info->driver, DRVNAME_9051, sizeof(info->driver)); |
568 | } |
569 | |
570 | static void dm9051_set_msglevel(struct net_device *ndev, u32 value) |
571 | { |
572 | struct board_info *db = to_dm9051_board(ndev); |
573 | |
574 | db->msg_enable = value; |
575 | } |
576 | |
577 | static u32 dm9051_get_msglevel(struct net_device *ndev) |
578 | { |
579 | struct board_info *db = to_dm9051_board(ndev); |
580 | |
581 | return db->msg_enable; |
582 | } |
583 | |
584 | static int dm9051_get_eeprom_len(struct net_device *dev) |
585 | { |
586 | return 128; |
587 | } |
588 | |
589 | static int dm9051_get_eeprom(struct net_device *ndev, |
590 | struct ethtool_eeprom *ee, u8 *data) |
591 | { |
592 | struct board_info *db = to_dm9051_board(ndev); |
593 | int offset = ee->offset; |
594 | int len = ee->len; |
595 | int i, ret; |
596 | |
597 | if ((len | offset) & 1) |
598 | return -EINVAL; |
599 | |
600 | ee->magic = DM_EEPROM_MAGIC; |
601 | |
602 | for (i = 0; i < len; i += 2) { |
603 | ret = dm9051_eeprom_read(db, offset: (offset + i) / 2, to: data + i); |
604 | if (ret) |
605 | break; |
606 | } |
607 | return ret; |
608 | } |
609 | |
610 | static int dm9051_set_eeprom(struct net_device *ndev, |
611 | struct ethtool_eeprom *ee, u8 *data) |
612 | { |
613 | struct board_info *db = to_dm9051_board(ndev); |
614 | int offset = ee->offset; |
615 | int len = ee->len; |
616 | int i, ret; |
617 | |
618 | if ((len | offset) & 1) |
619 | return -EINVAL; |
620 | |
621 | if (ee->magic != DM_EEPROM_MAGIC) |
622 | return -EINVAL; |
623 | |
624 | for (i = 0; i < len; i += 2) { |
625 | ret = dm9051_eeprom_write(db, offset: (offset + i) / 2, data: data + i); |
626 | if (ret) |
627 | break; |
628 | } |
629 | return ret; |
630 | } |
631 | |
632 | static void dm9051_get_pauseparam(struct net_device *ndev, |
633 | struct ethtool_pauseparam *pause) |
634 | { |
635 | struct board_info *db = to_dm9051_board(ndev); |
636 | |
637 | *pause = db->pause; |
638 | } |
639 | |
640 | static int dm9051_set_pauseparam(struct net_device *ndev, |
641 | struct ethtool_pauseparam *pause) |
642 | { |
643 | struct board_info *db = to_dm9051_board(ndev); |
644 | |
645 | db->pause = *pause; |
646 | |
647 | if (pause->autoneg == AUTONEG_DISABLE) |
648 | return dm9051_update_fcr(db); |
649 | |
650 | phy_set_sym_pause(phydev: db->phydev, rx: pause->rx_pause, tx: pause->tx_pause, |
651 | autoneg: pause->autoneg); |
652 | phy_start_aneg(phydev: db->phydev); |
653 | return 0; |
654 | } |
655 | |
656 | static const struct ethtool_ops dm9051_ethtool_ops = { |
657 | .get_drvinfo = dm9051_get_drvinfo, |
658 | .get_link_ksettings = phy_ethtool_get_link_ksettings, |
659 | .set_link_ksettings = phy_ethtool_set_link_ksettings, |
660 | .get_msglevel = dm9051_get_msglevel, |
661 | .set_msglevel = dm9051_set_msglevel, |
662 | .nway_reset = phy_ethtool_nway_reset, |
663 | .get_link = ethtool_op_get_link, |
664 | .get_eeprom_len = dm9051_get_eeprom_len, |
665 | .get_eeprom = dm9051_get_eeprom, |
666 | .set_eeprom = dm9051_set_eeprom, |
667 | .get_pauseparam = dm9051_get_pauseparam, |
668 | .set_pauseparam = dm9051_set_pauseparam, |
669 | }; |
670 | |
671 | static int dm9051_all_start(struct board_info *db) |
672 | { |
673 | int ret; |
674 | |
675 | /* GPR power on of the internal phy |
676 | */ |
677 | ret = dm9051_set_reg(db, DM9051_GPR, val: 0); |
678 | if (ret) |
679 | return ret; |
680 | |
681 | /* dm9051 chip registers could not be accessed within 1 ms |
682 | * after GPR power on, delay 1 ms is essential |
683 | */ |
684 | msleep(msecs: 1); |
685 | |
686 | ret = dm9051_core_reset(db); |
687 | if (ret) |
688 | return ret; |
689 | |
690 | return dm9051_enable_interrupt(db); |
691 | } |
692 | |
693 | static int dm9051_all_stop(struct board_info *db) |
694 | { |
695 | int ret; |
696 | |
697 | /* GPR power off of the internal phy, |
698 | * The internal phy still could be accessed after this GPR power off control |
699 | */ |
700 | ret = dm9051_set_reg(db, DM9051_GPR, GPR_PHY_OFF); |
701 | if (ret) |
702 | return ret; |
703 | |
704 | return dm9051_set_reg(db, DM9051_RCR, RCR_RX_DISABLE); |
705 | } |
706 | |
707 | /* fifo reset while rx error found |
708 | */ |
709 | static int dm9051_all_restart(struct board_info *db) |
710 | { |
711 | struct net_device *ndev = db->ndev; |
712 | int ret; |
713 | |
714 | ret = dm9051_core_reset(db); |
715 | if (ret) |
716 | return ret; |
717 | |
718 | ret = dm9051_enable_interrupt(db); |
719 | if (ret) |
720 | return ret; |
721 | |
722 | netdev_dbg(ndev, " rxstatus_Er & rxlen_Er %d, RST_c %d\n" , |
723 | db->bc.status_err_counter + db->bc.large_err_counter, |
724 | db->bc.fifo_rst_counter); |
725 | |
726 | ret = dm9051_set_recv(db); |
727 | if (ret) |
728 | return ret; |
729 | |
730 | return dm9051_set_fcr(db); |
731 | } |
732 | |
733 | /* read packets from the fifo memory |
734 | * return value, |
735 | * > 0 - read packet number, caller can repeat the rx operation |
736 | * 0 - no error, caller need stop further rx operation |
737 | * -EBUSY - read data error, caller escape from rx operation |
738 | */ |
739 | static int dm9051_loop_rx(struct board_info *db) |
740 | { |
741 | struct net_device *ndev = db->ndev; |
742 | unsigned int rxbyte; |
743 | int ret, rxlen; |
744 | struct sk_buff *skb; |
745 | u8 *rdptr; |
746 | int scanrr = 0; |
747 | |
748 | do { |
749 | ret = dm9051_read_mem(db, DM_SPI_MRCMDX, buff: &rxbyte, len: 2); |
750 | if (ret) |
751 | return ret; |
752 | |
753 | if ((rxbyte & GENMASK(7, 0)) != DM9051_PKT_RDY) |
754 | break; /* exhaust-empty */ |
755 | |
756 | ret = dm9051_read_mem(db, DM_SPI_MRCMD, buff: &db->rxhdr, DM_RXHDR_SIZE); |
757 | if (ret) |
758 | return ret; |
759 | |
760 | ret = dm9051_stop_mrcmd(db); |
761 | if (ret) |
762 | return ret; |
763 | |
764 | rxlen = le16_to_cpu(db->rxhdr.rxlen); |
765 | if (db->rxhdr.status & RSR_ERR_BITS || rxlen > DM9051_PKT_MAX) { |
766 | netdev_dbg(ndev, "rxhdr-byte (%02x)\n" , |
767 | db->rxhdr.headbyte); |
768 | |
769 | if (db->rxhdr.status & RSR_ERR_BITS) { |
770 | db->bc.status_err_counter++; |
771 | netdev_dbg(ndev, "check rxstatus-error (%02x)\n" , |
772 | db->rxhdr.status); |
773 | } else { |
774 | db->bc.large_err_counter++; |
775 | netdev_dbg(ndev, "check rxlen large-error (%d > %d)\n" , |
776 | rxlen, DM9051_PKT_MAX); |
777 | } |
778 | return dm9051_all_restart(db); |
779 | } |
780 | |
781 | skb = dev_alloc_skb(length: rxlen); |
782 | if (!skb) { |
783 | ret = dm9051_dumpblk(db, DM_SPI_MRCMD, count: rxlen); |
784 | if (ret) |
785 | return ret; |
786 | return scanrr; |
787 | } |
788 | |
789 | rdptr = skb_put(skb, len: rxlen - 4); |
790 | ret = dm9051_read_mem(db, DM_SPI_MRCMD, buff: rdptr, len: rxlen); |
791 | if (ret) { |
792 | db->bc.rx_err_counter++; |
793 | dev_kfree_skb(skb); |
794 | return ret; |
795 | } |
796 | |
797 | ret = dm9051_stop_mrcmd(db); |
798 | if (ret) { |
799 | dev_kfree_skb(skb); |
800 | return ret; |
801 | } |
802 | |
803 | skb->protocol = eth_type_trans(skb, dev: db->ndev); |
804 | if (db->ndev->features & NETIF_F_RXCSUM) |
805 | skb_checksum_none_assert(skb); |
806 | netif_rx(skb); |
807 | db->ndev->stats.rx_bytes += rxlen; |
808 | db->ndev->stats.rx_packets++; |
809 | scanrr++; |
810 | } while (!ret); |
811 | |
812 | return scanrr; |
813 | } |
814 | |
815 | /* transmit a packet, |
816 | * return value, |
817 | * 0 - succeed |
818 | * -ETIMEDOUT - timeout error |
819 | */ |
820 | static int dm9051_single_tx(struct board_info *db, u8 *buff, unsigned int len) |
821 | { |
822 | int ret; |
823 | |
824 | ret = dm9051_nsr_poll(db); |
825 | if (ret) |
826 | return ret; |
827 | |
828 | ret = dm9051_write_mem(db, DM_SPI_MWCMD, buff, len); |
829 | if (ret) |
830 | return ret; |
831 | |
832 | ret = dm9051_set_regs(db, DM9051_TXPLL, val: &len, val_count: 2); |
833 | if (ret < 0) |
834 | return ret; |
835 | |
836 | return dm9051_set_reg(db, DM9051_TCR, TCR_TXREQ); |
837 | } |
838 | |
839 | static int dm9051_loop_tx(struct board_info *db) |
840 | { |
841 | struct net_device *ndev = db->ndev; |
842 | int ntx = 0; |
843 | int ret; |
844 | |
845 | while (!skb_queue_empty(list: &db->txq)) { |
846 | struct sk_buff *skb; |
847 | unsigned int len; |
848 | |
849 | skb = skb_dequeue(list: &db->txq); |
850 | if (skb) { |
851 | ntx++; |
852 | ret = dm9051_single_tx(db, buff: skb->data, len: skb->len); |
853 | len = skb->len; |
854 | dev_kfree_skb(skb); |
855 | if (ret < 0) { |
856 | db->bc.tx_err_counter++; |
857 | return 0; |
858 | } |
859 | ndev->stats.tx_bytes += len; |
860 | ndev->stats.tx_packets++; |
861 | } |
862 | |
863 | if (netif_queue_stopped(dev: ndev) && |
864 | (skb_queue_len(list_: &db->txq) < DM9051_TX_QUE_LO_WATER)) |
865 | netif_wake_queue(dev: ndev); |
866 | } |
867 | |
868 | return ntx; |
869 | } |
870 | |
871 | static irqreturn_t dm9051_rx_threaded_irq(int irq, void *pw) |
872 | { |
873 | struct board_info *db = pw; |
874 | int result, result_tx; |
875 | |
876 | mutex_lock(&db->spi_lockm); |
877 | |
878 | result = dm9051_disable_interrupt(db); |
879 | if (result) |
880 | goto out_unlock; |
881 | |
882 | result = dm9051_clear_interrupt(db); |
883 | if (result) |
884 | goto out_unlock; |
885 | |
886 | do { |
887 | result = dm9051_loop_rx(db); /* threaded irq rx */ |
888 | if (result < 0) |
889 | goto out_unlock; |
890 | result_tx = dm9051_loop_tx(db); /* more tx better performance */ |
891 | if (result_tx < 0) |
892 | goto out_unlock; |
893 | } while (result > 0); |
894 | |
895 | dm9051_enable_interrupt(db); |
896 | |
897 | /* To exit and has mutex unlock while rx or tx error |
898 | */ |
899 | out_unlock: |
900 | mutex_unlock(lock: &db->spi_lockm); |
901 | |
902 | return IRQ_HANDLED; |
903 | } |
904 | |
905 | static void dm9051_tx_delay(struct work_struct *work) |
906 | { |
907 | struct board_info *db = container_of(work, struct board_info, tx_work); |
908 | int result; |
909 | |
910 | mutex_lock(&db->spi_lockm); |
911 | |
912 | result = dm9051_loop_tx(db); |
913 | if (result < 0) |
914 | netdev_err(dev: db->ndev, format: "transmit packet error\n" ); |
915 | |
916 | mutex_unlock(lock: &db->spi_lockm); |
917 | } |
918 | |
919 | static void dm9051_rxctl_delay(struct work_struct *work) |
920 | { |
921 | struct board_info *db = container_of(work, struct board_info, rxctrl_work); |
922 | struct net_device *ndev = db->ndev; |
923 | int result; |
924 | |
925 | mutex_lock(&db->spi_lockm); |
926 | |
927 | result = dm9051_set_regs(db, DM9051_PAR, val: ndev->dev_addr, val_count: sizeof(ndev->dev_addr)); |
928 | if (result < 0) |
929 | goto out_unlock; |
930 | |
931 | dm9051_set_recv(db); |
932 | |
933 | /* To has mutex unlock and return from this function if regmap function fail |
934 | */ |
935 | out_unlock: |
936 | mutex_unlock(lock: &db->spi_lockm); |
937 | } |
938 | |
939 | /* Open network device |
940 | * Called when the network device is marked active, such as a user executing |
941 | * 'ifconfig up' on the device |
942 | */ |
943 | static int dm9051_open(struct net_device *ndev) |
944 | { |
945 | struct board_info *db = to_dm9051_board(ndev); |
946 | struct spi_device *spi = db->spidev; |
947 | int ret; |
948 | |
949 | db->imr_all = IMR_PAR | IMR_PRM; |
950 | db->lcr_all = LMCR_MODE1; |
951 | db->rctl.rcr_all = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN; |
952 | memset(db->rctl.hash_table, 0, sizeof(db->rctl.hash_table)); |
953 | |
954 | ndev->irq = spi->irq; /* by dts */ |
955 | ret = request_threaded_irq(irq: spi->irq, NULL, thread_fn: dm9051_rx_threaded_irq, |
956 | flags: dm9051_irq_flag(db) | IRQF_ONESHOT, |
957 | name: ndev->name, dev: db); |
958 | if (ret < 0) { |
959 | netdev_err(dev: ndev, format: "failed to get irq\n" ); |
960 | return ret; |
961 | } |
962 | |
963 | phy_support_sym_pause(phydev: db->phydev); |
964 | phy_start(phydev: db->phydev); |
965 | |
966 | /* flow control parameters init */ |
967 | db->pause.rx_pause = true; |
968 | db->pause.tx_pause = true; |
969 | db->pause.autoneg = AUTONEG_DISABLE; |
970 | |
971 | if (db->phydev->autoneg) |
972 | db->pause.autoneg = AUTONEG_ENABLE; |
973 | |
974 | ret = dm9051_all_start(db); |
975 | if (ret) { |
976 | phy_stop(phydev: db->phydev); |
977 | free_irq(spi->irq, db); |
978 | return ret; |
979 | } |
980 | |
981 | netif_wake_queue(dev: ndev); |
982 | |
983 | return 0; |
984 | } |
985 | |
986 | /* Close network device |
987 | * Called to close down a network device which has been active. Cancel any |
988 | * work, shutdown the RX and TX process and then place the chip into a low |
989 | * power state while it is not being used |
990 | */ |
991 | static int dm9051_stop(struct net_device *ndev) |
992 | { |
993 | struct board_info *db = to_dm9051_board(ndev); |
994 | int ret; |
995 | |
996 | ret = dm9051_all_stop(db); |
997 | if (ret) |
998 | return ret; |
999 | |
1000 | flush_work(work: &db->tx_work); |
1001 | flush_work(work: &db->rxctrl_work); |
1002 | |
1003 | phy_stop(phydev: db->phydev); |
1004 | |
1005 | free_irq(db->spidev->irq, db); |
1006 | |
1007 | netif_stop_queue(dev: ndev); |
1008 | |
1009 | skb_queue_purge(list: &db->txq); |
1010 | |
1011 | return 0; |
1012 | } |
1013 | |
1014 | /* event: play a schedule starter in condition |
1015 | */ |
1016 | static netdev_tx_t dm9051_start_xmit(struct sk_buff *skb, struct net_device *ndev) |
1017 | { |
1018 | struct board_info *db = to_dm9051_board(ndev); |
1019 | |
1020 | skb_queue_tail(list: &db->txq, newsk: skb); |
1021 | if (skb_queue_len(list_: &db->txq) > DM9051_TX_QUE_HI_WATER) |
1022 | netif_stop_queue(dev: ndev); /* enforce limit queue size */ |
1023 | |
1024 | schedule_work(work: &db->tx_work); |
1025 | |
1026 | return NETDEV_TX_OK; |
1027 | } |
1028 | |
1029 | /* event: play with a schedule starter |
1030 | */ |
1031 | static void dm9051_set_rx_mode(struct net_device *ndev) |
1032 | { |
1033 | struct board_info *db = to_dm9051_board(ndev); |
1034 | struct dm9051_rxctrl rxctrl; |
1035 | struct netdev_hw_addr *ha; |
1036 | u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN; |
1037 | u32 hash_val; |
1038 | |
1039 | memset(&rxctrl, 0, sizeof(rxctrl)); |
1040 | |
1041 | /* rx control */ |
1042 | if (ndev->flags & IFF_PROMISC) { |
1043 | rcr |= RCR_PRMSC; |
1044 | netdev_dbg(ndev, "set_multicast rcr |= RCR_PRMSC, rcr= %02x\n" , rcr); |
1045 | } |
1046 | |
1047 | if (ndev->flags & IFF_ALLMULTI) { |
1048 | rcr |= RCR_ALL; |
1049 | netdev_dbg(ndev, "set_multicast rcr |= RCR_ALLMULTI, rcr= %02x\n" , rcr); |
1050 | } |
1051 | |
1052 | rxctrl.rcr_all = rcr; |
1053 | |
1054 | /* broadcast address */ |
1055 | rxctrl.hash_table[0] = 0; |
1056 | rxctrl.hash_table[1] = 0; |
1057 | rxctrl.hash_table[2] = 0; |
1058 | rxctrl.hash_table[3] = 0x8000; |
1059 | |
1060 | /* the multicast address in Hash Table : 64 bits */ |
1061 | netdev_for_each_mc_addr(ha, ndev) { |
1062 | hash_val = ether_crc_le(ETH_ALEN, ha->addr) & GENMASK(5, 0); |
1063 | rxctrl.hash_table[hash_val / 16] |= BIT(0) << (hash_val % 16); |
1064 | } |
1065 | |
1066 | /* schedule work to do the actual set of the data if needed */ |
1067 | |
1068 | if (memcmp(p: &db->rctl, q: &rxctrl, size: sizeof(rxctrl))) { |
1069 | memcpy(&db->rctl, &rxctrl, sizeof(rxctrl)); |
1070 | schedule_work(work: &db->rxctrl_work); |
1071 | } |
1072 | } |
1073 | |
1074 | /* event: write into the mac registers and eeprom directly |
1075 | */ |
1076 | static int dm9051_set_mac_address(struct net_device *ndev, void *p) |
1077 | { |
1078 | struct board_info *db = to_dm9051_board(ndev); |
1079 | int ret; |
1080 | |
1081 | ret = eth_prepare_mac_addr_change(dev: ndev, p); |
1082 | if (ret < 0) |
1083 | return ret; |
1084 | |
1085 | eth_commit_mac_addr_change(dev: ndev, p); |
1086 | return dm9051_set_regs(db, DM9051_PAR, val: ndev->dev_addr, val_count: sizeof(ndev->dev_addr)); |
1087 | } |
1088 | |
1089 | static const struct net_device_ops dm9051_netdev_ops = { |
1090 | .ndo_open = dm9051_open, |
1091 | .ndo_stop = dm9051_stop, |
1092 | .ndo_start_xmit = dm9051_start_xmit, |
1093 | .ndo_set_rx_mode = dm9051_set_rx_mode, |
1094 | .ndo_validate_addr = eth_validate_addr, |
1095 | .ndo_set_mac_address = dm9051_set_mac_address, |
1096 | }; |
1097 | |
1098 | static void dm9051_operation_clear(struct board_info *db) |
1099 | { |
1100 | db->bc.status_err_counter = 0; |
1101 | db->bc.large_err_counter = 0; |
1102 | db->bc.rx_err_counter = 0; |
1103 | db->bc.tx_err_counter = 0; |
1104 | db->bc.fifo_rst_counter = 0; |
1105 | } |
1106 | |
1107 | static int dm9051_mdio_register(struct board_info *db) |
1108 | { |
1109 | struct spi_device *spi = db->spidev; |
1110 | int ret; |
1111 | |
1112 | db->mdiobus = devm_mdiobus_alloc(dev: &spi->dev); |
1113 | if (!db->mdiobus) |
1114 | return -ENOMEM; |
1115 | |
1116 | db->mdiobus->priv = db; |
1117 | db->mdiobus->read = dm9051_mdio_read; |
1118 | db->mdiobus->write = dm9051_mdio_write; |
1119 | db->mdiobus->name = "dm9051-mdiobus" ; |
1120 | db->mdiobus->phy_mask = (u32)~BIT(1); |
1121 | db->mdiobus->parent = &spi->dev; |
1122 | snprintf(buf: db->mdiobus->id, MII_BUS_ID_SIZE, |
1123 | fmt: "dm9051-%s.%u" , dev_name(dev: &spi->dev), spi_get_chipselect(spi, idx: 0)); |
1124 | |
1125 | ret = devm_mdiobus_register(&spi->dev, db->mdiobus); |
1126 | if (ret) |
1127 | dev_err(&spi->dev, "Could not register MDIO bus\n" ); |
1128 | |
1129 | return ret; |
1130 | } |
1131 | |
1132 | static void dm9051_handle_link_change(struct net_device *ndev) |
1133 | { |
1134 | struct board_info *db = to_dm9051_board(ndev); |
1135 | |
1136 | phy_print_status(phydev: db->phydev); |
1137 | |
1138 | /* only write pause settings to mac. since mac and phy are integrated |
1139 | * together, such as link state, speed and duplex are sync already |
1140 | */ |
1141 | if (db->phydev->link) { |
1142 | if (db->phydev->pause) { |
1143 | db->pause.rx_pause = true; |
1144 | db->pause.tx_pause = true; |
1145 | } |
1146 | dm9051_update_fcr(db); |
1147 | } |
1148 | } |
1149 | |
1150 | /* phy connect as poll mode |
1151 | */ |
1152 | static int dm9051_phy_connect(struct board_info *db) |
1153 | { |
1154 | char phy_id[MII_BUS_ID_SIZE + 3]; |
1155 | |
1156 | snprintf(buf: phy_id, size: sizeof(phy_id), PHY_ID_FMT, |
1157 | db->mdiobus->id, DM9051_PHY_ADDR); |
1158 | |
1159 | db->phydev = phy_connect(dev: db->ndev, bus_id: phy_id, handler: dm9051_handle_link_change, |
1160 | interface: PHY_INTERFACE_MODE_MII); |
1161 | return PTR_ERR_OR_ZERO(ptr: db->phydev); |
1162 | } |
1163 | |
1164 | static int dm9051_probe(struct spi_device *spi) |
1165 | { |
1166 | struct device *dev = &spi->dev; |
1167 | struct net_device *ndev; |
1168 | struct board_info *db; |
1169 | int ret; |
1170 | |
1171 | ndev = devm_alloc_etherdev(dev, sizeof(struct board_info)); |
1172 | if (!ndev) |
1173 | return -ENOMEM; |
1174 | |
1175 | SET_NETDEV_DEV(ndev, dev); |
1176 | dev_set_drvdata(dev, data: ndev); |
1177 | |
1178 | db = netdev_priv(dev: ndev); |
1179 | |
1180 | db->msg_enable = 0; |
1181 | db->spidev = spi; |
1182 | db->ndev = ndev; |
1183 | |
1184 | ndev->netdev_ops = &dm9051_netdev_ops; |
1185 | ndev->ethtool_ops = &dm9051_ethtool_ops; |
1186 | |
1187 | mutex_init(&db->spi_lockm); |
1188 | mutex_init(&db->reg_mutex); |
1189 | |
1190 | INIT_WORK(&db->rxctrl_work, dm9051_rxctl_delay); |
1191 | INIT_WORK(&db->tx_work, dm9051_tx_delay); |
1192 | |
1193 | ret = dm9051_map_init(spi, db); |
1194 | if (ret) |
1195 | return ret; |
1196 | |
1197 | ret = dm9051_map_chipid(db); |
1198 | if (ret) |
1199 | return ret; |
1200 | |
1201 | ret = dm9051_map_etherdev_par(ndev, db); |
1202 | if (ret < 0) |
1203 | return ret; |
1204 | |
1205 | ret = dm9051_mdio_register(db); |
1206 | if (ret) |
1207 | return ret; |
1208 | |
1209 | ret = dm9051_phy_connect(db); |
1210 | if (ret) |
1211 | return ret; |
1212 | |
1213 | dm9051_operation_clear(db); |
1214 | skb_queue_head_init(list: &db->txq); |
1215 | |
1216 | ret = devm_register_netdev(dev, ndev); |
1217 | if (ret) { |
1218 | phy_disconnect(phydev: db->phydev); |
1219 | return dev_err_probe(dev, err: ret, fmt: "device register failed" ); |
1220 | } |
1221 | |
1222 | return 0; |
1223 | } |
1224 | |
1225 | static void dm9051_drv_remove(struct spi_device *spi) |
1226 | { |
1227 | struct device *dev = &spi->dev; |
1228 | struct net_device *ndev = dev_get_drvdata(dev); |
1229 | struct board_info *db = to_dm9051_board(ndev); |
1230 | |
1231 | phy_disconnect(phydev: db->phydev); |
1232 | } |
1233 | |
1234 | static const struct of_device_id dm9051_match_table[] = { |
1235 | { .compatible = "davicom,dm9051" }, |
1236 | {} |
1237 | }; |
1238 | |
1239 | static const struct spi_device_id dm9051_id_table[] = { |
1240 | { "dm9051" , 0 }, |
1241 | {} |
1242 | }; |
1243 | |
1244 | static struct spi_driver dm9051_driver = { |
1245 | .driver = { |
1246 | .name = DRVNAME_9051, |
1247 | .of_match_table = dm9051_match_table, |
1248 | }, |
1249 | .probe = dm9051_probe, |
1250 | .remove = dm9051_drv_remove, |
1251 | .id_table = dm9051_id_table, |
1252 | }; |
1253 | module_spi_driver(dm9051_driver); |
1254 | |
1255 | MODULE_AUTHOR("Joseph CHANG <joseph_chang@davicom.com.tw>" ); |
1256 | MODULE_DESCRIPTION("Davicom DM9051 network SPI driver" ); |
1257 | MODULE_LICENSE("GPL" ); |
1258 | |