162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2022 Davicom Semiconductor,Inc.
462306a36Sopenharmony_ci * Davicom DM9051 SPI Fast Ethernet Linux driver
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/etherdevice.h>
862306a36Sopenharmony_ci#include <linux/ethtool.h>
962306a36Sopenharmony_ci#include <linux/interrupt.h>
1062306a36Sopenharmony_ci#include <linux/iopoll.h>
1162306a36Sopenharmony_ci#include <linux/irq.h>
1262306a36Sopenharmony_ci#include <linux/mii.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/netdevice.h>
1562306a36Sopenharmony_ci#include <linux/phy.h>
1662306a36Sopenharmony_ci#include <linux/regmap.h>
1762306a36Sopenharmony_ci#include <linux/skbuff.h>
1862306a36Sopenharmony_ci#include <linux/spinlock.h>
1962306a36Sopenharmony_ci#include <linux/spi/spi.h>
2062306a36Sopenharmony_ci#include <linux/types.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "dm9051.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define DRVNAME_9051	"dm9051"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/**
2762306a36Sopenharmony_ci * struct rx_ctl_mach - rx activities record
2862306a36Sopenharmony_ci * @status_err_counter: rx status error counter
2962306a36Sopenharmony_ci * @large_err_counter: rx get large packet length error counter
3062306a36Sopenharmony_ci * @rx_err_counter: receive packet error counter
3162306a36Sopenharmony_ci * @tx_err_counter: transmit packet error counter
3262306a36Sopenharmony_ci * @fifo_rst_counter: reset operation counter
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * To keep track for the driver operation statistics
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_cistruct rx_ctl_mach {
3762306a36Sopenharmony_ci	u16				status_err_counter;
3862306a36Sopenharmony_ci	u16				large_err_counter;
3962306a36Sopenharmony_ci	u16				rx_err_counter;
4062306a36Sopenharmony_ci	u16				tx_err_counter;
4162306a36Sopenharmony_ci	u16				fifo_rst_counter;
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/**
4562306a36Sopenharmony_ci * struct dm9051_rxctrl - dm9051 driver rx control
4662306a36Sopenharmony_ci * @hash_table: Multicast hash-table data
4762306a36Sopenharmony_ci * @rcr_all: KS_RXCR1 register setting
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * The settings needs to control the receive filtering
5062306a36Sopenharmony_ci * such as the multicast hash-filter and the receive register settings
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_cistruct dm9051_rxctrl {
5362306a36Sopenharmony_ci	u16				hash_table[4];
5462306a36Sopenharmony_ci	u8				rcr_all;
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/**
5862306a36Sopenharmony_ci * struct dm9051_rxhdr - rx packet data header
5962306a36Sopenharmony_ci * @headbyte: lead byte equal to 0x01 notifies a valid packet
6062306a36Sopenharmony_ci * @status: status bits for the received packet
6162306a36Sopenharmony_ci * @rxlen: packet length
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * The Rx packed, entered into the FIFO memory, start with these
6462306a36Sopenharmony_ci * four bytes which is the Rx header, followed by the ethernet
6562306a36Sopenharmony_ci * packet data and ends with an appended 4-byte CRC data.
6662306a36Sopenharmony_ci * Both Rx packet and CRC data are for check purpose and finally
6762306a36Sopenharmony_ci * are dropped by this driver
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_cistruct dm9051_rxhdr {
7062306a36Sopenharmony_ci	u8				headbyte;
7162306a36Sopenharmony_ci	u8				status;
7262306a36Sopenharmony_ci	__le16				rxlen;
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/**
7662306a36Sopenharmony_ci * struct board_info - maintain the saved data
7762306a36Sopenharmony_ci * @spidev: spi device structure
7862306a36Sopenharmony_ci * @ndev: net device structure
7962306a36Sopenharmony_ci * @mdiobus: mii bus structure
8062306a36Sopenharmony_ci * @phydev: phy device structure
8162306a36Sopenharmony_ci * @txq: tx queue structure
8262306a36Sopenharmony_ci * @regmap_dm: regmap for register read/write
8362306a36Sopenharmony_ci * @regmap_dmbulk: extra regmap for bulk read/write
8462306a36Sopenharmony_ci * @rxctrl_work: Work queue for updating RX mode and multicast lists
8562306a36Sopenharmony_ci * @tx_work: Work queue for tx packets
8662306a36Sopenharmony_ci * @pause: ethtool pause parameter structure
8762306a36Sopenharmony_ci * @spi_lockm: between threads lock structure
8862306a36Sopenharmony_ci * @reg_mutex: regmap access lock structure
8962306a36Sopenharmony_ci * @bc: rx control statistics structure
9062306a36Sopenharmony_ci * @rxhdr: rx header structure
9162306a36Sopenharmony_ci * @rctl: rx control setting structure
9262306a36Sopenharmony_ci * @msg_enable: message level value
9362306a36Sopenharmony_ci * @imr_all: to store operating imr value for register DM9051_IMR
9462306a36Sopenharmony_ci * @lcr_all: to store operating rcr value for register DM9051_LMCR
9562306a36Sopenharmony_ci *
9662306a36Sopenharmony_ci * The saved data variables, keep up to date for retrieval back to use
9762306a36Sopenharmony_ci */
9862306a36Sopenharmony_cistruct board_info {
9962306a36Sopenharmony_ci	u32				msg_enable;
10062306a36Sopenharmony_ci	struct spi_device		*spidev;
10162306a36Sopenharmony_ci	struct net_device		*ndev;
10262306a36Sopenharmony_ci	struct mii_bus			*mdiobus;
10362306a36Sopenharmony_ci	struct phy_device		*phydev;
10462306a36Sopenharmony_ci	struct sk_buff_head		txq;
10562306a36Sopenharmony_ci	struct regmap			*regmap_dm;
10662306a36Sopenharmony_ci	struct regmap			*regmap_dmbulk;
10762306a36Sopenharmony_ci	struct work_struct		rxctrl_work;
10862306a36Sopenharmony_ci	struct work_struct		tx_work;
10962306a36Sopenharmony_ci	struct ethtool_pauseparam	pause;
11062306a36Sopenharmony_ci	struct mutex			spi_lockm;
11162306a36Sopenharmony_ci	struct mutex			reg_mutex;
11262306a36Sopenharmony_ci	struct rx_ctl_mach		bc;
11362306a36Sopenharmony_ci	struct dm9051_rxhdr		rxhdr;
11462306a36Sopenharmony_ci	struct dm9051_rxctrl		rctl;
11562306a36Sopenharmony_ci	u8				imr_all;
11662306a36Sopenharmony_ci	u8				lcr_all;
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int dm9051_set_reg(struct board_info *db, unsigned int reg, unsigned int val)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	int ret;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, reg, val);
12462306a36Sopenharmony_ci	if (ret < 0)
12562306a36Sopenharmony_ci		netif_err(db, drv, db->ndev, "%s: error %d set reg %02x\n",
12662306a36Sopenharmony_ci			  __func__, ret, reg);
12762306a36Sopenharmony_ci	return ret;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int dm9051_update_bits(struct board_info *db, unsigned int reg, unsigned int mask,
13162306a36Sopenharmony_ci			      unsigned int val)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	int ret;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	ret = regmap_update_bits(db->regmap_dm, reg, mask, val);
13662306a36Sopenharmony_ci	if (ret < 0)
13762306a36Sopenharmony_ci		netif_err(db, drv, db->ndev, "%s: error %d update bits reg %02x\n",
13862306a36Sopenharmony_ci			  __func__, ret, reg);
13962306a36Sopenharmony_ci	return ret;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/* skb buffer exhausted, just discard the received data
14362306a36Sopenharmony_ci */
14462306a36Sopenharmony_cistatic int dm9051_dumpblk(struct board_info *db, u8 reg, size_t count)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	struct net_device *ndev = db->ndev;
14762306a36Sopenharmony_ci	unsigned int rb;
14862306a36Sopenharmony_ci	int ret;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* no skb buffer,
15162306a36Sopenharmony_ci	 * both reg and &rb must be noinc,
15262306a36Sopenharmony_ci	 * read once one byte via regmap_read
15362306a36Sopenharmony_ci	 */
15462306a36Sopenharmony_ci	do {
15562306a36Sopenharmony_ci		ret = regmap_read(db->regmap_dm, reg, &rb);
15662306a36Sopenharmony_ci		if (ret < 0) {
15762306a36Sopenharmony_ci			netif_err(db, drv, ndev, "%s: error %d dumping read reg %02x\n",
15862306a36Sopenharmony_ci				  __func__, ret, reg);
15962306a36Sopenharmony_ci			break;
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci	} while (--count);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return ret;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic int dm9051_set_regs(struct board_info *db, unsigned int reg, const void *val,
16762306a36Sopenharmony_ci			   size_t val_count)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	int ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	ret = regmap_bulk_write(db->regmap_dmbulk, reg, val, val_count);
17262306a36Sopenharmony_ci	if (ret < 0)
17362306a36Sopenharmony_ci		netif_err(db, drv, db->ndev, "%s: error %d bulk writing regs %02x\n",
17462306a36Sopenharmony_ci			  __func__, ret, reg);
17562306a36Sopenharmony_ci	return ret;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic int dm9051_get_regs(struct board_info *db, unsigned int reg, void *val,
17962306a36Sopenharmony_ci			   size_t val_count)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	int ret;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	ret = regmap_bulk_read(db->regmap_dmbulk, reg, val, val_count);
18462306a36Sopenharmony_ci	if (ret < 0)
18562306a36Sopenharmony_ci		netif_err(db, drv, db->ndev, "%s: error %d bulk reading regs %02x\n",
18662306a36Sopenharmony_ci			  __func__, ret, reg);
18762306a36Sopenharmony_ci	return ret;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int dm9051_write_mem(struct board_info *db, unsigned int reg, const void *buff,
19162306a36Sopenharmony_ci			    size_t len)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	int ret;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	ret = regmap_noinc_write(db->regmap_dm, reg, buff, len);
19662306a36Sopenharmony_ci	if (ret < 0)
19762306a36Sopenharmony_ci		netif_err(db, drv, db->ndev, "%s: error %d noinc writing regs %02x\n",
19862306a36Sopenharmony_ci			  __func__, ret, reg);
19962306a36Sopenharmony_ci	return ret;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int dm9051_read_mem(struct board_info *db, unsigned int reg, void *buff,
20362306a36Sopenharmony_ci			   size_t len)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	ret = regmap_noinc_read(db->regmap_dm, reg, buff, len);
20862306a36Sopenharmony_ci	if (ret < 0)
20962306a36Sopenharmony_ci		netif_err(db, drv, db->ndev, "%s: error %d noinc reading regs %02x\n",
21062306a36Sopenharmony_ci			  __func__, ret, reg);
21162306a36Sopenharmony_ci	return ret;
21262306a36Sopenharmony_ci}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci/* waiting tx-end rather than tx-req
21562306a36Sopenharmony_ci * got faster
21662306a36Sopenharmony_ci */
21762306a36Sopenharmony_cistatic int dm9051_nsr_poll(struct board_info *db)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	unsigned int mval;
22062306a36Sopenharmony_ci	int ret;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	ret = regmap_read_poll_timeout(db->regmap_dm, DM9051_NSR, mval,
22362306a36Sopenharmony_ci				       mval & (NSR_TX2END | NSR_TX1END), 1, 20);
22462306a36Sopenharmony_ci	if (ret == -ETIMEDOUT)
22562306a36Sopenharmony_ci		netdev_err(db->ndev, "timeout in checking for tx end\n");
22662306a36Sopenharmony_ci	return ret;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int dm9051_epcr_poll(struct board_info *db)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	unsigned int mval;
23262306a36Sopenharmony_ci	int ret;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	ret = regmap_read_poll_timeout(db->regmap_dm, DM9051_EPCR, mval,
23562306a36Sopenharmony_ci				       !(mval & EPCR_ERRE), 100, 10000);
23662306a36Sopenharmony_ci	if (ret == -ETIMEDOUT)
23762306a36Sopenharmony_ci		netdev_err(db->ndev, "eeprom/phy in processing get timeout\n");
23862306a36Sopenharmony_ci	return ret;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int dm9051_irq_flag(struct board_info *db)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct spi_device *spi = db->spidev;
24462306a36Sopenharmony_ci	int irq_type = irq_get_trigger_type(spi->irq);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (irq_type)
24762306a36Sopenharmony_ci		return irq_type;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return IRQF_TRIGGER_LOW;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic unsigned int dm9051_intcr_value(struct board_info *db)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	return (dm9051_irq_flag(db) == IRQF_TRIGGER_LOW) ?
25562306a36Sopenharmony_ci		INTCR_POL_LOW : INTCR_POL_HIGH;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int dm9051_set_fcr(struct board_info *db)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	u8 fcr = 0;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (db->pause.rx_pause)
26362306a36Sopenharmony_ci		fcr |= FCR_BKPM | FCR_FLCE;
26462306a36Sopenharmony_ci	if (db->pause.tx_pause)
26562306a36Sopenharmony_ci		fcr |= FCR_TXPEN;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	return dm9051_set_reg(db, DM9051_FCR, fcr);
26862306a36Sopenharmony_ci}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_cistatic int dm9051_set_recv(struct board_info *db)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	int ret;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	ret = dm9051_set_regs(db, DM9051_MAR, db->rctl.hash_table, sizeof(db->rctl.hash_table));
27562306a36Sopenharmony_ci	if (ret)
27662306a36Sopenharmony_ci		return ret;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return dm9051_set_reg(db, DM9051_RCR, db->rctl.rcr_all); /* enable rx */
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic int dm9051_core_reset(struct board_info *db)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	int ret;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	db->bc.fifo_rst_counter++;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_NCR, NCR_RST); /* NCR reset */
28862306a36Sopenharmony_ci	if (ret)
28962306a36Sopenharmony_ci		return ret;
29062306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_MBNDRY, MBNDRY_BYTE); /* MemBound */
29162306a36Sopenharmony_ci	if (ret)
29262306a36Sopenharmony_ci		return ret;
29362306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_PPCR, PPCR_PAUSE_COUNT); /* Pause Count */
29462306a36Sopenharmony_ci	if (ret)
29562306a36Sopenharmony_ci		return ret;
29662306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_LMCR, db->lcr_all); /* LEDMode1 */
29762306a36Sopenharmony_ci	if (ret)
29862306a36Sopenharmony_ci		return ret;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return dm9051_set_reg(db, DM9051_INTCR, dm9051_intcr_value(db));
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int dm9051_update_fcr(struct board_info *db)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	u8 fcr = 0;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (db->pause.rx_pause)
30862306a36Sopenharmony_ci		fcr |= FCR_BKPM | FCR_FLCE;
30962306a36Sopenharmony_ci	if (db->pause.tx_pause)
31062306a36Sopenharmony_ci		fcr |= FCR_TXPEN;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return dm9051_update_bits(db, DM9051_FCR, FCR_RXTX_BITS, fcr);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic int dm9051_disable_interrupt(struct board_info *db)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	return dm9051_set_reg(db, DM9051_IMR, IMR_PAR); /* disable int */
31862306a36Sopenharmony_ci}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int dm9051_enable_interrupt(struct board_info *db)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	return dm9051_set_reg(db, DM9051_IMR, db->imr_all); /* enable int */
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic int dm9051_stop_mrcmd(struct board_info *db)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	return dm9051_set_reg(db, DM9051_ISR, ISR_STOP_MRCMD); /* to stop mrcmd */
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int dm9051_clear_interrupt(struct board_info *db)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	return dm9051_update_bits(db, DM9051_ISR, ISR_CLR_INT, ISR_CLR_INT);
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int dm9051_eeprom_read(struct board_info *db, int offset, u8 *to)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	int ret;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_EPAR, offset);
34062306a36Sopenharmony_ci	if (ret)
34162306a36Sopenharmony_ci		return ret;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_EPCR, EPCR_ERPRR);
34462306a36Sopenharmony_ci	if (ret)
34562306a36Sopenharmony_ci		return ret;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	ret = dm9051_epcr_poll(db);
34862306a36Sopenharmony_ci	if (ret)
34962306a36Sopenharmony_ci		return ret;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_EPCR, 0);
35262306a36Sopenharmony_ci	if (ret)
35362306a36Sopenharmony_ci		return ret;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return regmap_bulk_read(db->regmap_dmbulk, DM9051_EPDRL, to, 2);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic int dm9051_eeprom_write(struct board_info *db, int offset, u8 *data)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	int ret;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_EPAR, offset);
36362306a36Sopenharmony_ci	if (ret)
36462306a36Sopenharmony_ci		return ret;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	ret = regmap_bulk_write(db->regmap_dmbulk, DM9051_EPDRL, data, 2);
36762306a36Sopenharmony_ci	if (ret < 0)
36862306a36Sopenharmony_ci		return ret;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_EPCR, EPCR_WEP | EPCR_ERPRW);
37162306a36Sopenharmony_ci	if (ret)
37262306a36Sopenharmony_ci		return ret;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	ret = dm9051_epcr_poll(db);
37562306a36Sopenharmony_ci	if (ret)
37662306a36Sopenharmony_ci		return ret;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return regmap_write(db->regmap_dm, DM9051_EPCR, 0);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic int dm9051_phyread(void *context, unsigned int reg, unsigned int *val)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct board_info *db = context;
38462306a36Sopenharmony_ci	int ret;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_EPAR, DM9051_PHY | reg);
38762306a36Sopenharmony_ci	if (ret)
38862306a36Sopenharmony_ci		return ret;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_EPCR, EPCR_ERPRR | EPCR_EPOS);
39162306a36Sopenharmony_ci	if (ret)
39262306a36Sopenharmony_ci		return ret;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	ret = dm9051_epcr_poll(db);
39562306a36Sopenharmony_ci	if (ret)
39662306a36Sopenharmony_ci		return ret;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_EPCR, 0);
39962306a36Sopenharmony_ci	if (ret)
40062306a36Sopenharmony_ci		return ret;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	/* this is a 4 bytes data, clear to zero since following regmap_bulk_read
40362306a36Sopenharmony_ci	 * only fill lower 2 bytes
40462306a36Sopenharmony_ci	 */
40562306a36Sopenharmony_ci	*val = 0;
40662306a36Sopenharmony_ci	return regmap_bulk_read(db->regmap_dmbulk, DM9051_EPDRL, val, 2);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic int dm9051_phywrite(void *context, unsigned int reg, unsigned int val)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct board_info *db = context;
41262306a36Sopenharmony_ci	int ret;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_EPAR, DM9051_PHY | reg);
41562306a36Sopenharmony_ci	if (ret)
41662306a36Sopenharmony_ci		return ret;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	ret = regmap_bulk_write(db->regmap_dmbulk, DM9051_EPDRL, &val, 2);
41962306a36Sopenharmony_ci	if (ret < 0)
42062306a36Sopenharmony_ci		return ret;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	ret = regmap_write(db->regmap_dm, DM9051_EPCR, EPCR_EPOS | EPCR_ERPRW);
42362306a36Sopenharmony_ci	if (ret)
42462306a36Sopenharmony_ci		return ret;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	ret = dm9051_epcr_poll(db);
42762306a36Sopenharmony_ci	if (ret)
42862306a36Sopenharmony_ci		return ret;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	return regmap_write(db->regmap_dm, DM9051_EPCR, 0);
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int dm9051_mdio_read(struct mii_bus *bus, int addr, int regnum)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct board_info *db = bus->priv;
43662306a36Sopenharmony_ci	unsigned int val = 0xffff;
43762306a36Sopenharmony_ci	int ret;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (addr == DM9051_PHY_ADDR) {
44062306a36Sopenharmony_ci		ret = dm9051_phyread(db, regnum, &val);
44162306a36Sopenharmony_ci		if (ret)
44262306a36Sopenharmony_ci			return ret;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return val;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int dm9051_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct board_info *db = bus->priv;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (addr == DM9051_PHY_ADDR)
45362306a36Sopenharmony_ci		return dm9051_phywrite(db, regnum, val);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	return -ENODEV;
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic void dm9051_reg_lock_mutex(void *dbcontext)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct board_info *db = dbcontext;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	mutex_lock(&db->reg_mutex);
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic void dm9051_reg_unlock_mutex(void *dbcontext)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	struct board_info *db = dbcontext;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	mutex_unlock(&db->reg_mutex);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic struct regmap_config regconfigdm = {
47362306a36Sopenharmony_ci	.reg_bits = 8,
47462306a36Sopenharmony_ci	.val_bits = 8,
47562306a36Sopenharmony_ci	.max_register = 0xff,
47662306a36Sopenharmony_ci	.reg_stride = 1,
47762306a36Sopenharmony_ci	.cache_type = REGCACHE_NONE,
47862306a36Sopenharmony_ci	.read_flag_mask = 0,
47962306a36Sopenharmony_ci	.write_flag_mask = DM_SPI_WR,
48062306a36Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_LITTLE,
48162306a36Sopenharmony_ci	.lock = dm9051_reg_lock_mutex,
48262306a36Sopenharmony_ci	.unlock = dm9051_reg_unlock_mutex,
48362306a36Sopenharmony_ci};
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic struct regmap_config regconfigdmbulk = {
48662306a36Sopenharmony_ci	.reg_bits = 8,
48762306a36Sopenharmony_ci	.val_bits = 8,
48862306a36Sopenharmony_ci	.max_register = 0xff,
48962306a36Sopenharmony_ci	.reg_stride = 1,
49062306a36Sopenharmony_ci	.cache_type = REGCACHE_NONE,
49162306a36Sopenharmony_ci	.read_flag_mask = 0,
49262306a36Sopenharmony_ci	.write_flag_mask = DM_SPI_WR,
49362306a36Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_LITTLE,
49462306a36Sopenharmony_ci	.lock = dm9051_reg_lock_mutex,
49562306a36Sopenharmony_ci	.unlock = dm9051_reg_unlock_mutex,
49662306a36Sopenharmony_ci	.use_single_read = true,
49762306a36Sopenharmony_ci	.use_single_write = true,
49862306a36Sopenharmony_ci};
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_cistatic int dm9051_map_init(struct spi_device *spi, struct board_info *db)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	/* create two regmap instances,
50362306a36Sopenharmony_ci	 * split read/write and bulk_read/bulk_write to individual regmap
50462306a36Sopenharmony_ci	 * to resolve regmap execution confliction problem
50562306a36Sopenharmony_ci	 */
50662306a36Sopenharmony_ci	regconfigdm.lock_arg = db;
50762306a36Sopenharmony_ci	db->regmap_dm = devm_regmap_init_spi(db->spidev, &regconfigdm);
50862306a36Sopenharmony_ci	if (IS_ERR(db->regmap_dm))
50962306a36Sopenharmony_ci		return PTR_ERR(db->regmap_dm);
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	regconfigdmbulk.lock_arg = db;
51262306a36Sopenharmony_ci	db->regmap_dmbulk = devm_regmap_init_spi(db->spidev, &regconfigdmbulk);
51362306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(db->regmap_dmbulk);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic int dm9051_map_chipid(struct board_info *db)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct device *dev = &db->spidev->dev;
51962306a36Sopenharmony_ci	unsigned short wid;
52062306a36Sopenharmony_ci	u8 buff[6];
52162306a36Sopenharmony_ci	int ret;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	ret = dm9051_get_regs(db, DM9051_VIDL, buff, sizeof(buff));
52462306a36Sopenharmony_ci	if (ret < 0)
52562306a36Sopenharmony_ci		return ret;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	wid = get_unaligned_le16(buff + 2);
52862306a36Sopenharmony_ci	if (wid != DM9051_ID) {
52962306a36Sopenharmony_ci		dev_err(dev, "chipid error as %04x !\n", wid);
53062306a36Sopenharmony_ci		return -ENODEV;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	dev_info(dev, "chip %04x found\n", wid);
53462306a36Sopenharmony_ci	return 0;
53562306a36Sopenharmony_ci}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci/* Read DM9051_PAR registers which is the mac address loaded from EEPROM while power-on
53862306a36Sopenharmony_ci */
53962306a36Sopenharmony_cistatic int dm9051_map_etherdev_par(struct net_device *ndev, struct board_info *db)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
54262306a36Sopenharmony_ci	int ret;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	ret = dm9051_get_regs(db, DM9051_PAR, addr, sizeof(addr));
54562306a36Sopenharmony_ci	if (ret < 0)
54662306a36Sopenharmony_ci		return ret;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	if (!is_valid_ether_addr(addr)) {
54962306a36Sopenharmony_ci		eth_hw_addr_random(ndev);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		ret = dm9051_set_regs(db, DM9051_PAR, ndev->dev_addr, sizeof(ndev->dev_addr));
55262306a36Sopenharmony_ci		if (ret < 0)
55362306a36Sopenharmony_ci			return ret;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci		dev_dbg(&db->spidev->dev, "Use random MAC address\n");
55662306a36Sopenharmony_ci		return 0;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	eth_hw_addr_set(ndev, addr);
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/* ethtool-ops
56462306a36Sopenharmony_ci */
56562306a36Sopenharmony_cistatic void dm9051_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	strscpy(info->driver, DRVNAME_9051, sizeof(info->driver));
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic void dm9051_set_msglevel(struct net_device *ndev, u32 value)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	db->msg_enable = value;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic u32 dm9051_get_msglevel(struct net_device *ndev)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	return db->msg_enable;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic int dm9051_get_eeprom_len(struct net_device *dev)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	return 128;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic int dm9051_get_eeprom(struct net_device *ndev,
59062306a36Sopenharmony_ci			     struct ethtool_eeprom *ee, u8 *data)
59162306a36Sopenharmony_ci{
59262306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
59362306a36Sopenharmony_ci	int offset = ee->offset;
59462306a36Sopenharmony_ci	int len = ee->len;
59562306a36Sopenharmony_ci	int i, ret;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if ((len | offset) & 1)
59862306a36Sopenharmony_ci		return -EINVAL;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	ee->magic = DM_EEPROM_MAGIC;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	for (i = 0; i < len; i += 2) {
60362306a36Sopenharmony_ci		ret = dm9051_eeprom_read(db, (offset + i) / 2, data + i);
60462306a36Sopenharmony_ci		if (ret)
60562306a36Sopenharmony_ci			break;
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci	return ret;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_cistatic int dm9051_set_eeprom(struct net_device *ndev,
61162306a36Sopenharmony_ci			     struct ethtool_eeprom *ee, u8 *data)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
61462306a36Sopenharmony_ci	int offset = ee->offset;
61562306a36Sopenharmony_ci	int len = ee->len;
61662306a36Sopenharmony_ci	int i, ret;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	if ((len | offset) & 1)
61962306a36Sopenharmony_ci		return -EINVAL;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	if (ee->magic != DM_EEPROM_MAGIC)
62262306a36Sopenharmony_ci		return -EINVAL;
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	for (i = 0; i < len; i += 2) {
62562306a36Sopenharmony_ci		ret = dm9051_eeprom_write(db, (offset + i) / 2, data + i);
62662306a36Sopenharmony_ci		if (ret)
62762306a36Sopenharmony_ci			break;
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci	return ret;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic void dm9051_get_pauseparam(struct net_device *ndev,
63362306a36Sopenharmony_ci				  struct ethtool_pauseparam *pause)
63462306a36Sopenharmony_ci{
63562306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	*pause = db->pause;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_cistatic int dm9051_set_pauseparam(struct net_device *ndev,
64162306a36Sopenharmony_ci				 struct ethtool_pauseparam *pause)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	db->pause = *pause;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	if (pause->autoneg == AUTONEG_DISABLE)
64862306a36Sopenharmony_ci		return dm9051_update_fcr(db);
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	phy_set_sym_pause(db->phydev, pause->rx_pause, pause->tx_pause,
65162306a36Sopenharmony_ci			  pause->autoneg);
65262306a36Sopenharmony_ci	phy_start_aneg(db->phydev);
65362306a36Sopenharmony_ci	return 0;
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic const struct ethtool_ops dm9051_ethtool_ops = {
65762306a36Sopenharmony_ci	.get_drvinfo = dm9051_get_drvinfo,
65862306a36Sopenharmony_ci	.get_link_ksettings = phy_ethtool_get_link_ksettings,
65962306a36Sopenharmony_ci	.set_link_ksettings = phy_ethtool_set_link_ksettings,
66062306a36Sopenharmony_ci	.get_msglevel = dm9051_get_msglevel,
66162306a36Sopenharmony_ci	.set_msglevel = dm9051_set_msglevel,
66262306a36Sopenharmony_ci	.nway_reset = phy_ethtool_nway_reset,
66362306a36Sopenharmony_ci	.get_link = ethtool_op_get_link,
66462306a36Sopenharmony_ci	.get_eeprom_len = dm9051_get_eeprom_len,
66562306a36Sopenharmony_ci	.get_eeprom = dm9051_get_eeprom,
66662306a36Sopenharmony_ci	.set_eeprom = dm9051_set_eeprom,
66762306a36Sopenharmony_ci	.get_pauseparam = dm9051_get_pauseparam,
66862306a36Sopenharmony_ci	.set_pauseparam = dm9051_set_pauseparam,
66962306a36Sopenharmony_ci};
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic int dm9051_all_start(struct board_info *db)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	int ret;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	/* GPR power on of the internal phy
67662306a36Sopenharmony_ci	 */
67762306a36Sopenharmony_ci	ret = dm9051_set_reg(db, DM9051_GPR, 0);
67862306a36Sopenharmony_ci	if (ret)
67962306a36Sopenharmony_ci		return ret;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/* dm9051 chip registers could not be accessed within 1 ms
68262306a36Sopenharmony_ci	 * after GPR power on, delay 1 ms is essential
68362306a36Sopenharmony_ci	 */
68462306a36Sopenharmony_ci	msleep(1);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	ret = dm9051_core_reset(db);
68762306a36Sopenharmony_ci	if (ret)
68862306a36Sopenharmony_ci		return ret;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	return dm9051_enable_interrupt(db);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_cistatic int dm9051_all_stop(struct board_info *db)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	int ret;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	/* GPR power off of the internal phy,
69862306a36Sopenharmony_ci	 * The internal phy still could be accessed after this GPR power off control
69962306a36Sopenharmony_ci	 */
70062306a36Sopenharmony_ci	ret = dm9051_set_reg(db, DM9051_GPR, GPR_PHY_OFF);
70162306a36Sopenharmony_ci	if (ret)
70262306a36Sopenharmony_ci		return ret;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	return dm9051_set_reg(db, DM9051_RCR, RCR_RX_DISABLE);
70562306a36Sopenharmony_ci}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci/* fifo reset while rx error found
70862306a36Sopenharmony_ci */
70962306a36Sopenharmony_cistatic int dm9051_all_restart(struct board_info *db)
71062306a36Sopenharmony_ci{
71162306a36Sopenharmony_ci	struct net_device *ndev = db->ndev;
71262306a36Sopenharmony_ci	int ret;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	ret = dm9051_core_reset(db);
71562306a36Sopenharmony_ci	if (ret)
71662306a36Sopenharmony_ci		return ret;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	ret = dm9051_enable_interrupt(db);
71962306a36Sopenharmony_ci	if (ret)
72062306a36Sopenharmony_ci		return ret;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	netdev_dbg(ndev, " rxstatus_Er & rxlen_Er %d, RST_c %d\n",
72362306a36Sopenharmony_ci		   db->bc.status_err_counter + db->bc.large_err_counter,
72462306a36Sopenharmony_ci		   db->bc.fifo_rst_counter);
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	ret = dm9051_set_recv(db);
72762306a36Sopenharmony_ci	if (ret)
72862306a36Sopenharmony_ci		return ret;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	return dm9051_set_fcr(db);
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci/* read packets from the fifo memory
73462306a36Sopenharmony_ci * return value,
73562306a36Sopenharmony_ci *  > 0 - read packet number, caller can repeat the rx operation
73662306a36Sopenharmony_ci *    0 - no error, caller need stop further rx operation
73762306a36Sopenharmony_ci *  -EBUSY - read data error, caller escape from rx operation
73862306a36Sopenharmony_ci */
73962306a36Sopenharmony_cistatic int dm9051_loop_rx(struct board_info *db)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	struct net_device *ndev = db->ndev;
74262306a36Sopenharmony_ci	unsigned int rxbyte;
74362306a36Sopenharmony_ci	int ret, rxlen;
74462306a36Sopenharmony_ci	struct sk_buff *skb;
74562306a36Sopenharmony_ci	u8 *rdptr;
74662306a36Sopenharmony_ci	int scanrr = 0;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	do {
74962306a36Sopenharmony_ci		ret = dm9051_read_mem(db, DM_SPI_MRCMDX, &rxbyte, 2);
75062306a36Sopenharmony_ci		if (ret)
75162306a36Sopenharmony_ci			return ret;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci		if ((rxbyte & GENMASK(7, 0)) != DM9051_PKT_RDY)
75462306a36Sopenharmony_ci			break; /* exhaust-empty */
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci		ret = dm9051_read_mem(db, DM_SPI_MRCMD, &db->rxhdr, DM_RXHDR_SIZE);
75762306a36Sopenharmony_ci		if (ret)
75862306a36Sopenharmony_ci			return ret;
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci		ret = dm9051_stop_mrcmd(db);
76162306a36Sopenharmony_ci		if (ret)
76262306a36Sopenharmony_ci			return ret;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci		rxlen = le16_to_cpu(db->rxhdr.rxlen);
76562306a36Sopenharmony_ci		if (db->rxhdr.status & RSR_ERR_BITS || rxlen > DM9051_PKT_MAX) {
76662306a36Sopenharmony_ci			netdev_dbg(ndev, "rxhdr-byte (%02x)\n",
76762306a36Sopenharmony_ci				   db->rxhdr.headbyte);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci			if (db->rxhdr.status & RSR_ERR_BITS) {
77062306a36Sopenharmony_ci				db->bc.status_err_counter++;
77162306a36Sopenharmony_ci				netdev_dbg(ndev, "check rxstatus-error (%02x)\n",
77262306a36Sopenharmony_ci					   db->rxhdr.status);
77362306a36Sopenharmony_ci			} else {
77462306a36Sopenharmony_ci				db->bc.large_err_counter++;
77562306a36Sopenharmony_ci				netdev_dbg(ndev, "check rxlen large-error (%d > %d)\n",
77662306a36Sopenharmony_ci					   rxlen, DM9051_PKT_MAX);
77762306a36Sopenharmony_ci			}
77862306a36Sopenharmony_ci			return dm9051_all_restart(db);
77962306a36Sopenharmony_ci		}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		skb = dev_alloc_skb(rxlen);
78262306a36Sopenharmony_ci		if (!skb) {
78362306a36Sopenharmony_ci			ret = dm9051_dumpblk(db, DM_SPI_MRCMD, rxlen);
78462306a36Sopenharmony_ci			if (ret)
78562306a36Sopenharmony_ci				return ret;
78662306a36Sopenharmony_ci			return scanrr;
78762306a36Sopenharmony_ci		}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		rdptr = skb_put(skb, rxlen - 4);
79062306a36Sopenharmony_ci		ret = dm9051_read_mem(db, DM_SPI_MRCMD, rdptr, rxlen);
79162306a36Sopenharmony_ci		if (ret) {
79262306a36Sopenharmony_ci			db->bc.rx_err_counter++;
79362306a36Sopenharmony_ci			dev_kfree_skb(skb);
79462306a36Sopenharmony_ci			return ret;
79562306a36Sopenharmony_ci		}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci		ret = dm9051_stop_mrcmd(db);
79862306a36Sopenharmony_ci		if (ret) {
79962306a36Sopenharmony_ci			dev_kfree_skb(skb);
80062306a36Sopenharmony_ci			return ret;
80162306a36Sopenharmony_ci		}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci		skb->protocol = eth_type_trans(skb, db->ndev);
80462306a36Sopenharmony_ci		if (db->ndev->features & NETIF_F_RXCSUM)
80562306a36Sopenharmony_ci			skb_checksum_none_assert(skb);
80662306a36Sopenharmony_ci		netif_rx(skb);
80762306a36Sopenharmony_ci		db->ndev->stats.rx_bytes += rxlen;
80862306a36Sopenharmony_ci		db->ndev->stats.rx_packets++;
80962306a36Sopenharmony_ci		scanrr++;
81062306a36Sopenharmony_ci	} while (!ret);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	return scanrr;
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci/* transmit a packet,
81662306a36Sopenharmony_ci * return value,
81762306a36Sopenharmony_ci *   0 - succeed
81862306a36Sopenharmony_ci *  -ETIMEDOUT - timeout error
81962306a36Sopenharmony_ci */
82062306a36Sopenharmony_cistatic int dm9051_single_tx(struct board_info *db, u8 *buff, unsigned int len)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	int ret;
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	ret = dm9051_nsr_poll(db);
82562306a36Sopenharmony_ci	if (ret)
82662306a36Sopenharmony_ci		return ret;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	ret = dm9051_write_mem(db, DM_SPI_MWCMD, buff, len);
82962306a36Sopenharmony_ci	if (ret)
83062306a36Sopenharmony_ci		return ret;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	ret = dm9051_set_regs(db, DM9051_TXPLL, &len, 2);
83362306a36Sopenharmony_ci	if (ret < 0)
83462306a36Sopenharmony_ci		return ret;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	return dm9051_set_reg(db, DM9051_TCR, TCR_TXREQ);
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic int dm9051_loop_tx(struct board_info *db)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	struct net_device *ndev = db->ndev;
84262306a36Sopenharmony_ci	int ntx = 0;
84362306a36Sopenharmony_ci	int ret;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	while (!skb_queue_empty(&db->txq)) {
84662306a36Sopenharmony_ci		struct sk_buff *skb;
84762306a36Sopenharmony_ci		unsigned int len;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		skb = skb_dequeue(&db->txq);
85062306a36Sopenharmony_ci		if (skb) {
85162306a36Sopenharmony_ci			ntx++;
85262306a36Sopenharmony_ci			ret = dm9051_single_tx(db, skb->data, skb->len);
85362306a36Sopenharmony_ci			len = skb->len;
85462306a36Sopenharmony_ci			dev_kfree_skb(skb);
85562306a36Sopenharmony_ci			if (ret < 0) {
85662306a36Sopenharmony_ci				db->bc.tx_err_counter++;
85762306a36Sopenharmony_ci				return 0;
85862306a36Sopenharmony_ci			}
85962306a36Sopenharmony_ci			ndev->stats.tx_bytes += len;
86062306a36Sopenharmony_ci			ndev->stats.tx_packets++;
86162306a36Sopenharmony_ci		}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		if (netif_queue_stopped(ndev) &&
86462306a36Sopenharmony_ci		    (skb_queue_len(&db->txq) < DM9051_TX_QUE_LO_WATER))
86562306a36Sopenharmony_ci			netif_wake_queue(ndev);
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	return ntx;
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_cistatic irqreturn_t dm9051_rx_threaded_irq(int irq, void *pw)
87262306a36Sopenharmony_ci{
87362306a36Sopenharmony_ci	struct board_info *db = pw;
87462306a36Sopenharmony_ci	int result, result_tx;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	mutex_lock(&db->spi_lockm);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	result = dm9051_disable_interrupt(db);
87962306a36Sopenharmony_ci	if (result)
88062306a36Sopenharmony_ci		goto out_unlock;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	result = dm9051_clear_interrupt(db);
88362306a36Sopenharmony_ci	if (result)
88462306a36Sopenharmony_ci		goto out_unlock;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	do {
88762306a36Sopenharmony_ci		result = dm9051_loop_rx(db); /* threaded irq rx */
88862306a36Sopenharmony_ci		if (result < 0)
88962306a36Sopenharmony_ci			goto out_unlock;
89062306a36Sopenharmony_ci		result_tx = dm9051_loop_tx(db); /* more tx better performance */
89162306a36Sopenharmony_ci		if (result_tx < 0)
89262306a36Sopenharmony_ci			goto out_unlock;
89362306a36Sopenharmony_ci	} while (result > 0);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	dm9051_enable_interrupt(db);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* To exit and has mutex unlock while rx or tx error
89862306a36Sopenharmony_ci	 */
89962306a36Sopenharmony_ciout_unlock:
90062306a36Sopenharmony_ci	mutex_unlock(&db->spi_lockm);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	return IRQ_HANDLED;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cistatic void dm9051_tx_delay(struct work_struct *work)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	struct board_info *db = container_of(work, struct board_info, tx_work);
90862306a36Sopenharmony_ci	int result;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	mutex_lock(&db->spi_lockm);
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	result = dm9051_loop_tx(db);
91362306a36Sopenharmony_ci	if (result < 0)
91462306a36Sopenharmony_ci		netdev_err(db->ndev, "transmit packet error\n");
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	mutex_unlock(&db->spi_lockm);
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_cistatic void dm9051_rxctl_delay(struct work_struct *work)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	struct board_info *db = container_of(work, struct board_info, rxctrl_work);
92262306a36Sopenharmony_ci	struct net_device *ndev = db->ndev;
92362306a36Sopenharmony_ci	int result;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	mutex_lock(&db->spi_lockm);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	result = dm9051_set_regs(db, DM9051_PAR, ndev->dev_addr, sizeof(ndev->dev_addr));
92862306a36Sopenharmony_ci	if (result < 0)
92962306a36Sopenharmony_ci		goto out_unlock;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	dm9051_set_recv(db);
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	/* To has mutex unlock and return from this function if regmap function fail
93462306a36Sopenharmony_ci	 */
93562306a36Sopenharmony_ciout_unlock:
93662306a36Sopenharmony_ci	mutex_unlock(&db->spi_lockm);
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci/* Open network device
94062306a36Sopenharmony_ci * Called when the network device is marked active, such as a user executing
94162306a36Sopenharmony_ci * 'ifconfig up' on the device
94262306a36Sopenharmony_ci */
94362306a36Sopenharmony_cistatic int dm9051_open(struct net_device *ndev)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
94662306a36Sopenharmony_ci	struct spi_device *spi = db->spidev;
94762306a36Sopenharmony_ci	int ret;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	db->imr_all = IMR_PAR | IMR_PRM;
95062306a36Sopenharmony_ci	db->lcr_all = LMCR_MODE1;
95162306a36Sopenharmony_ci	db->rctl.rcr_all = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;
95262306a36Sopenharmony_ci	memset(db->rctl.hash_table, 0, sizeof(db->rctl.hash_table));
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	ndev->irq = spi->irq; /* by dts */
95562306a36Sopenharmony_ci	ret = request_threaded_irq(spi->irq, NULL, dm9051_rx_threaded_irq,
95662306a36Sopenharmony_ci				   dm9051_irq_flag(db) | IRQF_ONESHOT,
95762306a36Sopenharmony_ci				   ndev->name, db);
95862306a36Sopenharmony_ci	if (ret < 0) {
95962306a36Sopenharmony_ci		netdev_err(ndev, "failed to get irq\n");
96062306a36Sopenharmony_ci		return ret;
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	phy_support_sym_pause(db->phydev);
96462306a36Sopenharmony_ci	phy_start(db->phydev);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	/* flow control parameters init */
96762306a36Sopenharmony_ci	db->pause.rx_pause = true;
96862306a36Sopenharmony_ci	db->pause.tx_pause = true;
96962306a36Sopenharmony_ci	db->pause.autoneg = AUTONEG_DISABLE;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	if (db->phydev->autoneg)
97262306a36Sopenharmony_ci		db->pause.autoneg = AUTONEG_ENABLE;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	ret = dm9051_all_start(db);
97562306a36Sopenharmony_ci	if (ret) {
97662306a36Sopenharmony_ci		phy_stop(db->phydev);
97762306a36Sopenharmony_ci		free_irq(spi->irq, db);
97862306a36Sopenharmony_ci		return ret;
97962306a36Sopenharmony_ci	}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	netif_wake_queue(ndev);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	return 0;
98462306a36Sopenharmony_ci}
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci/* Close network device
98762306a36Sopenharmony_ci * Called to close down a network device which has been active. Cancel any
98862306a36Sopenharmony_ci * work, shutdown the RX and TX process and then place the chip into a low
98962306a36Sopenharmony_ci * power state while it is not being used
99062306a36Sopenharmony_ci */
99162306a36Sopenharmony_cistatic int dm9051_stop(struct net_device *ndev)
99262306a36Sopenharmony_ci{
99362306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
99462306a36Sopenharmony_ci	int ret;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	ret = dm9051_all_stop(db);
99762306a36Sopenharmony_ci	if (ret)
99862306a36Sopenharmony_ci		return ret;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	flush_work(&db->tx_work);
100162306a36Sopenharmony_ci	flush_work(&db->rxctrl_work);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	phy_stop(db->phydev);
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	free_irq(db->spidev->irq, db);
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	netif_stop_queue(ndev);
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	skb_queue_purge(&db->txq);
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	return 0;
101262306a36Sopenharmony_ci}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci/* event: play a schedule starter in condition
101562306a36Sopenharmony_ci */
101662306a36Sopenharmony_cistatic netdev_tx_t dm9051_start_xmit(struct sk_buff *skb, struct net_device *ndev)
101762306a36Sopenharmony_ci{
101862306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	skb_queue_tail(&db->txq, skb);
102162306a36Sopenharmony_ci	if (skb_queue_len(&db->txq) > DM9051_TX_QUE_HI_WATER)
102262306a36Sopenharmony_ci		netif_stop_queue(ndev); /* enforce limit queue size */
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	schedule_work(&db->tx_work);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	return NETDEV_TX_OK;
102762306a36Sopenharmony_ci}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci/* event: play with a schedule starter
103062306a36Sopenharmony_ci */
103162306a36Sopenharmony_cistatic void dm9051_set_rx_mode(struct net_device *ndev)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
103462306a36Sopenharmony_ci	struct dm9051_rxctrl rxctrl;
103562306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
103662306a36Sopenharmony_ci	u8 rcr = RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN;
103762306a36Sopenharmony_ci	u32 hash_val;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	memset(&rxctrl, 0, sizeof(rxctrl));
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/* rx control */
104262306a36Sopenharmony_ci	if (ndev->flags & IFF_PROMISC) {
104362306a36Sopenharmony_ci		rcr |= RCR_PRMSC;
104462306a36Sopenharmony_ci		netdev_dbg(ndev, "set_multicast rcr |= RCR_PRMSC, rcr= %02x\n", rcr);
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	if (ndev->flags & IFF_ALLMULTI) {
104862306a36Sopenharmony_ci		rcr |= RCR_ALL;
104962306a36Sopenharmony_ci		netdev_dbg(ndev, "set_multicast rcr |= RCR_ALLMULTI, rcr= %02x\n", rcr);
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	rxctrl.rcr_all = rcr;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	/* broadcast address */
105562306a36Sopenharmony_ci	rxctrl.hash_table[0] = 0;
105662306a36Sopenharmony_ci	rxctrl.hash_table[1] = 0;
105762306a36Sopenharmony_ci	rxctrl.hash_table[2] = 0;
105862306a36Sopenharmony_ci	rxctrl.hash_table[3] = 0x8000;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	/* the multicast address in Hash Table : 64 bits */
106162306a36Sopenharmony_ci	netdev_for_each_mc_addr(ha, ndev) {
106262306a36Sopenharmony_ci		hash_val = ether_crc_le(ETH_ALEN, ha->addr) & GENMASK(5, 0);
106362306a36Sopenharmony_ci		rxctrl.hash_table[hash_val / 16] |= BIT(0) << (hash_val % 16);
106462306a36Sopenharmony_ci	}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	/* schedule work to do the actual set of the data if needed */
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	if (memcmp(&db->rctl, &rxctrl, sizeof(rxctrl))) {
106962306a36Sopenharmony_ci		memcpy(&db->rctl, &rxctrl, sizeof(rxctrl));
107062306a36Sopenharmony_ci		schedule_work(&db->rxctrl_work);
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci/* event: write into the mac registers and eeprom directly
107562306a36Sopenharmony_ci */
107662306a36Sopenharmony_cistatic int dm9051_set_mac_address(struct net_device *ndev, void *p)
107762306a36Sopenharmony_ci{
107862306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
107962306a36Sopenharmony_ci	int ret;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	ret = eth_prepare_mac_addr_change(ndev, p);
108262306a36Sopenharmony_ci	if (ret < 0)
108362306a36Sopenharmony_ci		return ret;
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	eth_commit_mac_addr_change(ndev, p);
108662306a36Sopenharmony_ci	return dm9051_set_regs(db, DM9051_PAR, ndev->dev_addr, sizeof(ndev->dev_addr));
108762306a36Sopenharmony_ci}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_cistatic const struct net_device_ops dm9051_netdev_ops = {
109062306a36Sopenharmony_ci	.ndo_open = dm9051_open,
109162306a36Sopenharmony_ci	.ndo_stop = dm9051_stop,
109262306a36Sopenharmony_ci	.ndo_start_xmit = dm9051_start_xmit,
109362306a36Sopenharmony_ci	.ndo_set_rx_mode = dm9051_set_rx_mode,
109462306a36Sopenharmony_ci	.ndo_validate_addr = eth_validate_addr,
109562306a36Sopenharmony_ci	.ndo_set_mac_address = dm9051_set_mac_address,
109662306a36Sopenharmony_ci};
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_cistatic void dm9051_operation_clear(struct board_info *db)
109962306a36Sopenharmony_ci{
110062306a36Sopenharmony_ci	db->bc.status_err_counter = 0;
110162306a36Sopenharmony_ci	db->bc.large_err_counter = 0;
110262306a36Sopenharmony_ci	db->bc.rx_err_counter = 0;
110362306a36Sopenharmony_ci	db->bc.tx_err_counter = 0;
110462306a36Sopenharmony_ci	db->bc.fifo_rst_counter = 0;
110562306a36Sopenharmony_ci}
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_cistatic int dm9051_mdio_register(struct board_info *db)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	struct spi_device *spi = db->spidev;
111062306a36Sopenharmony_ci	int ret;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	db->mdiobus = devm_mdiobus_alloc(&spi->dev);
111362306a36Sopenharmony_ci	if (!db->mdiobus)
111462306a36Sopenharmony_ci		return -ENOMEM;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	db->mdiobus->priv = db;
111762306a36Sopenharmony_ci	db->mdiobus->read = dm9051_mdio_read;
111862306a36Sopenharmony_ci	db->mdiobus->write = dm9051_mdio_write;
111962306a36Sopenharmony_ci	db->mdiobus->name = "dm9051-mdiobus";
112062306a36Sopenharmony_ci	db->mdiobus->phy_mask = (u32)~BIT(1);
112162306a36Sopenharmony_ci	db->mdiobus->parent = &spi->dev;
112262306a36Sopenharmony_ci	snprintf(db->mdiobus->id, MII_BUS_ID_SIZE,
112362306a36Sopenharmony_ci		 "dm9051-%s.%u", dev_name(&spi->dev), spi_get_chipselect(spi, 0));
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	ret = devm_mdiobus_register(&spi->dev, db->mdiobus);
112662306a36Sopenharmony_ci	if (ret)
112762306a36Sopenharmony_ci		dev_err(&spi->dev, "Could not register MDIO bus\n");
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	return ret;
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic void dm9051_handle_link_change(struct net_device *ndev)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	phy_print_status(db->phydev);
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	/* only write pause settings to mac. since mac and phy are integrated
113962306a36Sopenharmony_ci	 * together, such as link state, speed and duplex are sync already
114062306a36Sopenharmony_ci	 */
114162306a36Sopenharmony_ci	if (db->phydev->link) {
114262306a36Sopenharmony_ci		if (db->phydev->pause) {
114362306a36Sopenharmony_ci			db->pause.rx_pause = true;
114462306a36Sopenharmony_ci			db->pause.tx_pause = true;
114562306a36Sopenharmony_ci		}
114662306a36Sopenharmony_ci		dm9051_update_fcr(db);
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci}
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci/* phy connect as poll mode
115162306a36Sopenharmony_ci */
115262306a36Sopenharmony_cistatic int dm9051_phy_connect(struct board_info *db)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	char phy_id[MII_BUS_ID_SIZE + 3];
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT,
115762306a36Sopenharmony_ci		 db->mdiobus->id, DM9051_PHY_ADDR);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	db->phydev = phy_connect(db->ndev, phy_id, dm9051_handle_link_change,
116062306a36Sopenharmony_ci				 PHY_INTERFACE_MODE_MII);
116162306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(db->phydev);
116262306a36Sopenharmony_ci}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_cistatic int dm9051_probe(struct spi_device *spi)
116562306a36Sopenharmony_ci{
116662306a36Sopenharmony_ci	struct device *dev = &spi->dev;
116762306a36Sopenharmony_ci	struct net_device *ndev;
116862306a36Sopenharmony_ci	struct board_info *db;
116962306a36Sopenharmony_ci	int ret;
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	ndev = devm_alloc_etherdev(dev, sizeof(struct board_info));
117262306a36Sopenharmony_ci	if (!ndev)
117362306a36Sopenharmony_ci		return -ENOMEM;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	SET_NETDEV_DEV(ndev, dev);
117662306a36Sopenharmony_ci	dev_set_drvdata(dev, ndev);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	db = netdev_priv(ndev);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	db->msg_enable = 0;
118162306a36Sopenharmony_ci	db->spidev = spi;
118262306a36Sopenharmony_ci	db->ndev = ndev;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	ndev->netdev_ops = &dm9051_netdev_ops;
118562306a36Sopenharmony_ci	ndev->ethtool_ops = &dm9051_ethtool_ops;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	mutex_init(&db->spi_lockm);
118862306a36Sopenharmony_ci	mutex_init(&db->reg_mutex);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	INIT_WORK(&db->rxctrl_work, dm9051_rxctl_delay);
119162306a36Sopenharmony_ci	INIT_WORK(&db->tx_work, dm9051_tx_delay);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	ret = dm9051_map_init(spi, db);
119462306a36Sopenharmony_ci	if (ret)
119562306a36Sopenharmony_ci		return ret;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	ret = dm9051_map_chipid(db);
119862306a36Sopenharmony_ci	if (ret)
119962306a36Sopenharmony_ci		return ret;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	ret = dm9051_map_etherdev_par(ndev, db);
120262306a36Sopenharmony_ci	if (ret < 0)
120362306a36Sopenharmony_ci		return ret;
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	ret = dm9051_mdio_register(db);
120662306a36Sopenharmony_ci	if (ret)
120762306a36Sopenharmony_ci		return ret;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	ret = dm9051_phy_connect(db);
121062306a36Sopenharmony_ci	if (ret)
121162306a36Sopenharmony_ci		return ret;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	dm9051_operation_clear(db);
121462306a36Sopenharmony_ci	skb_queue_head_init(&db->txq);
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	ret = devm_register_netdev(dev, ndev);
121762306a36Sopenharmony_ci	if (ret) {
121862306a36Sopenharmony_ci		phy_disconnect(db->phydev);
121962306a36Sopenharmony_ci		return dev_err_probe(dev, ret, "device register failed");
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	return 0;
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_cistatic void dm9051_drv_remove(struct spi_device *spi)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	struct device *dev = &spi->dev;
122862306a36Sopenharmony_ci	struct net_device *ndev = dev_get_drvdata(dev);
122962306a36Sopenharmony_ci	struct board_info *db = to_dm9051_board(ndev);
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	phy_disconnect(db->phydev);
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_cistatic const struct of_device_id dm9051_match_table[] = {
123562306a36Sopenharmony_ci	{ .compatible = "davicom,dm9051" },
123662306a36Sopenharmony_ci	{}
123762306a36Sopenharmony_ci};
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic const struct spi_device_id dm9051_id_table[] = {
124062306a36Sopenharmony_ci	{ "dm9051", 0 },
124162306a36Sopenharmony_ci	{}
124262306a36Sopenharmony_ci};
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic struct spi_driver dm9051_driver = {
124562306a36Sopenharmony_ci	.driver = {
124662306a36Sopenharmony_ci		.name = DRVNAME_9051,
124762306a36Sopenharmony_ci		.of_match_table = dm9051_match_table,
124862306a36Sopenharmony_ci	},
124962306a36Sopenharmony_ci	.probe = dm9051_probe,
125062306a36Sopenharmony_ci	.remove = dm9051_drv_remove,
125162306a36Sopenharmony_ci	.id_table = dm9051_id_table,
125262306a36Sopenharmony_ci};
125362306a36Sopenharmony_cimodule_spi_driver(dm9051_driver);
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ciMODULE_AUTHOR("Joseph CHANG <joseph_chang@davicom.com.tw>");
125662306a36Sopenharmony_ciMODULE_DESCRIPTION("Davicom DM9051 network SPI driver");
125762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1258