diff options
Diffstat (limited to 'drivers/net')
118 files changed, 24631 insertions, 4093 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 5823320b03..13e9ff6924 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config ARCH_HAS_FEC_IMX bool @@ -7,9 +8,6 @@ config HAS_AT91_ETHER config HAS_CS8900 bool -config HAS_DESIGNWARE_ETH - bool - config HAS_DM9000 bool @@ -31,7 +29,7 @@ config DRIVER_NET_AR231X config DRIVER_NET_ARC_EMAC bool "ARC Ethernet MAC driver" - depends on HAS_DMA + depends on HAS_DMA && 32BIT select PHYLIB help This option enables support for the ARC EMAC ethernet @@ -42,13 +40,15 @@ config DRIVER_NET_AT91_ETHER depends on HAS_AT91_ETHER select PHYLIB -config DRIVER_NET_CALXEDA_XGMAC - bool "Calxeda xgmac" - depends on HAS_DMA +config DRIVER_NET_BCMGENET + bool "BCMGENET V5 support" + select PHYLIB + help + This driver supports the BCMGENET Ethernet MAC. config DRIVER_NET_CS8900 bool "cs8900 ethernet driver" - depends on HAS_CS8900 + depends on HAS_CS8900 || COMPILE_TEST config DRIVER_NET_CPSW bool "CPSW ethernet driver" @@ -61,61 +61,88 @@ config DRIVER_NET_DAVINCI_EMAC select PHYLIB config DRIVER_NET_DESIGNWARE - bool "Designware Universal MAC1000 ethernet platform support" + bool "Designware DWMAC1000 Ethernet driver support" if COMPILE_TEST depends on HAS_DMA select PHYLIB help - This option enables support for the Synopsys - Designware Core Univesal MAC 10M/100M/1G ethernet IP. - -if DRIVER_NET_DESIGNWARE + This option is selected by platform glue drivers that contain + a DWMAC1000-compatible Ethernet IP. config DRIVER_NET_DESIGNWARE_GENERIC - bool "Designware Universal MAC ethernet generic driver" + bool "Generic Synopsis Designware Ethernet driver" + select DRIVER_NET_DESIGNWARE + depends on HAS_DMA help - This option enables support for the Synopsys - Designware Core Univesal MAC 10M/100M/1G ethernet IP on SoCFPGA. + This option enables support for the generic Synopsys + Designware Core Universal MAC 10M/100M/1G binding. Supported + are 3.70a and 3.72. Most integrations additionally require + access to platform-specific registers, e.g. for clocking. + If you are on such a platform, use the platform specific + driver instead. config DRIVER_NET_DESIGNWARE_SOCFPGA - bool "Designware Universal MAC ethernet driver for SoCFPGA platforms" - depends on ARCH_SOCFPGA + bool "SOCFPGA Designware Ethernet driver" + depends on HAS_DMA && (ARCH_SOCFPGA || COMPILE_TEST) + select DRIVER_NET_DESIGNWARE select MFD_SYSCON select RESET_CONTROLLER help This option enables support for the Synopsys - Designware Core Univesal MAC 10M/100M/1G ethernet IP on SoCFPGA. + Designware Core Universal MAC 10M/100M/1G Ethernet IP on SoCFPGA. -endif +config DRIVER_NET_DESIGNWARE_STARFIVE + bool "StarFive Designware Ethernet driver" + depends on HAS_DMA && (SOC_STARFIVE || COMPILE_TEST) + select DRIVER_NET_DESIGNWARE + select MFD_SYSCON + help + This option enables support for the Synopsys + Designware Core Universal MAC 10M/100M/1G Ethernet IP on StarFive. config DRIVER_NET_DESIGNWARE_EQOS - bool "Designware Designware Ethernet QoS support" - depends on HAS_DMA - depends on COMMON_CLK - depends on OFTREE + bool "Designware EQOS (GMAC4) Ethernet driver support" if COMPILE_TEST + depends on HAS_DMA && OFTREE select PHYLIB help This option enables support for the Synopsys Designware Ethernet Quality-of-Service (GMAC4). -if DRIVER_NET_DESIGNWARE_EQOS +config DRIVER_NET_DESIGNWARE_IMX8 + bool "Designware EQOS i.MX Ethernet driver" + depends on HAS_DMA && COMMON_CLK && OFTREE && (ARCH_IMX8M || ARCH_IMX93 || COMPILE_TEST) + select DRIVER_NET_DESIGNWARE_EQOS + select MFD_SYSCON + help + This option enables support for the Designware EQOS MAC implemented on + the NXP i.MX SoCs. config DRIVER_NET_DESIGNWARE_STM32 - bool "Designware EQOS STM32 driver" + bool "STM32 Designware Ethernet driver" + depends on HAS_DMA && COMMON_CLK && OFTREE && (ARCH_STM32MP || COMPILE_TEST) + select DRIVER_NET_DESIGNWARE_EQOS select MFD_SYSCON help - This option enables support for the ethernet MAC on the STM32MP platforms. + This option enables support for the Ethernet MAC on the STM32MP platforms. config DRIVER_NET_DESIGNWARE_TEGRA186 - bool "Designware Universal MAC ethernet driver for Tegra 186 platforms" + bool "Tegra 186/194 Designware Ethernet driver" + depends on HAS_DMA && COMMON_CLK && OFTREE && (ARCH_TEGRA || COMPILE_TEST) + select DRIVER_NET_DESIGNWARE_EQOS select RESET_CONTROLLER help - This option enables support for the ethernet MAC on the Tegra186 & 194. + This option enables support for the Ethernet MAC on the Tegra186 & 194. -endif +config DRIVER_NET_DESIGNWARE_ROCKCHIP + bool "Rockchip Designware Ethernet driver" + select DRIVER_NET_DESIGNWARE_EQOS + depends on HAS_DMA && COMMON_CLK && OFTREE && (ARCH_ROCKCHIP || COMPILE_TEST) + select MFD_SYSCON + help + This option enables support for the Ethernet MAC on different Rockchip SoCs config DRIVER_NET_DM9K bool "Davicom dm9k[E|A|B] ethernet driver" - depends on HAS_DM9000 + depends on HAS_DM9000 || COMPILE_TEST select PHYLIB config DRIVER_NET_E1000 @@ -157,9 +184,19 @@ config DRIVER_NET_ETHOC config DRIVER_NET_FEC_IMX bool "i.MX FEC Ethernet driver" - depends on ARCH_HAS_FEC_IMX + depends on ARCH_HAS_FEC_IMX || COMPILE_TEST + depends on HAS_DMA select PHYLIB +config DRIVER_NET_FSL_ENETC + bool "Freescale enetc ethernet driver" + select PHYLIB + depends on PCI + depends on HAS_DMA + help + This option enables support for the Freescale enetc core found + on Layerscape SoCs. + config DRIVER_NET_FSL_FMAN bool "Freescale fman ethernet driver" select PHYLIB @@ -182,7 +219,8 @@ config DRIVER_NET_KS8851_MLL config DRIVER_NET_MACB bool "macb Ethernet driver" - depends on HAS_MACB + depends on HAS_MACB || COMPILE_TEST + depends on HAS_DMA select PHYLIB config DRIVER_NET_MICREL @@ -212,7 +250,6 @@ config DRIVER_NET_ORION config DRIVER_NET_RTL8139 bool "RealTek RTL-8139 PCI Ethernet driver" depends on PCI - depends on MIPS select PHYLIB help This is a driver for the Fast Ethernet PCI network cards based on @@ -223,6 +260,7 @@ config DRIVER_NET_RTL8169 depends on PCI depends on HAS_DMA select PHYLIB + select REALTEK_PHY help This is a driver for the Fast Ethernet PCI network cards based on the RTL 8169 chips. @@ -243,11 +281,18 @@ config DRIVER_NET_SMC91111 config DRIVER_NET_TAP bool "tap Ethernet driver" - depends on LINUX + depends on SANDBOX config DRIVER_NET_EFI_SNP bool "EFI SNP ethernet driver" - depends on EFI_BOOTUP + depends on EFI_PAYLOAD + +config DRIVER_NET_VIRTIO + bool "virtio net driver" + depends on VIRTIO + help + This is the virtual net driver for virtio. It can be used with + QEMU based targets. config DRIVER_NET_AG71XX bool "Atheros AG71xx ethernet driver" @@ -256,24 +301,57 @@ config DRIVER_NET_AG71XX help This option enables support for Atheros AG71XX ethernet chip. -config DRIVER_NET_TSE - depends on NIOS2 - bool "Altera TSE ethernet driver" +config DRIVER_NET_LITEETH + bool "LiteX ethernet driver" select PHYLIB + select MDIO_BITBANG help - This option enables support for the Altera TSE MAC. - -config TSE_USE_DEDICATED_DESC_MEM - depends on DRIVER_NET_TSE - bool "Altera TSE uses dedicated descriptor memory" - help - This option tells the TSE driver to use an onchip memory - to store SGDMA descriptors. Descriptor memory is not - reserved with a malloc but directly mapped to the memory - address (defined in config.h) + This option enables support for the LiteX LiteEth + ethernet IP core. source "drivers/net/phy/Kconfig" source "drivers/net/usb/Kconfig" +menuconfig DSA + bool "Distributed Switch Architecture (DSA)" + select PHYLIB + +if DSA + +config DRIVER_NET_KSZ8873 + bool "KSZ8873 switch driver" + help + This option enables support for the Microchip KSZ8873 + switch chip. + +config DRIVER_NET_KSZ9477 + bool "KSZ9477 switch driver" + depends on SPI || I2C + select REGMAP_SPI if SPI + select REGMAP_I2C if I2C + help + This option enables support for the Microchip KSZ9477 + switch chip. + +config DRIVER_NET_SJA1105 + bool "NXP SJA1105 Ethernet switch family driver" + depends on SPI + select BITREV + help + This is the driver for the NXP SJA1105 automotive Ethernet switch + family. These are 5-port devices and are managed over an SPI + interface. Probing is handled based on OF bindings. The driver + supports the following revisions: + - SJA1105E (Gen. 1, No TT-Ethernet) + - SJA1105T (Gen. 1, TT-Ethernet) + - SJA1105P (Gen. 2, No SGMII, No TT-Ethernet) + - SJA1105Q (Gen. 2, No SGMII, TT-Ethernet) + - SJA1105R (Gen. 2, SGMII, No TT-Ethernet) + - SJA1105S (Gen. 2, SGMII, TT-Ethernet) + +source "drivers/net/realtek-dsa/Kconfig" + +endif + endmenu diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 656d45a868..207345cfa3 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -1,38 +1,49 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_DSA) += dsa.o obj-$(CONFIG_PHYLIB) += phy/ obj-$(CONFIG_NET_USB) += usb/ obj-$(CONFIG_DRIVER_NET_AR231X) += ar231x.o obj-$(CONFIG_DRIVER_NET_ARC_EMAC) += arc_emac.o obj-$(CONFIG_DRIVER_NET_AT91_ETHER) += at91_ether.o -obj-$(CONFIG_DRIVER_NET_CALXEDA_XGMAC) += xgmac.o +obj-$(CONFIG_DRIVER_NET_BCMGENET) += bcmgenet.o obj-$(CONFIG_DRIVER_NET_CS8900) += cs8900.o obj-$(CONFIG_DRIVER_NET_CPSW) += cpsw.o obj-$(CONFIG_DRIVER_NET_DAVINCI_EMAC) += davinci_emac.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE) += designware.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE_GENERIC) += designware_generic.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE_SOCFPGA) += designware_socfpga.o +obj-$(CONFIG_DRIVER_NET_DESIGNWARE_STARFIVE) += designware_starfive.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE_EQOS) += designware_eqos.o +obj-$(CONFIG_DRIVER_NET_DESIGNWARE_IMX8) += designware_imx.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE_STM32) += designware_stm32.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE_TEGRA186) += designware_tegra186.o +obj-$(CONFIG_DRIVER_NET_DESIGNWARE_ROCKCHIP) += designware_rockchip.o obj-$(CONFIG_DRIVER_NET_DM9K) += dm9k.o -obj-$(CONFIG_DRIVER_NET_E1000) += e1000/regio.o e1000/main.o e1000/eeprom.o +obj-$(CONFIG_DRIVER_NET_E1000) += e1000/ obj-$(CONFIG_DRIVER_NET_ENC28J60) += enc28j60.o obj-$(CONFIG_DRIVER_NET_EP93XX) += ep93xx.o obj-$(CONFIG_DRIVER_NET_ETHOC) += ethoc.o obj-$(CONFIG_DRIVER_NET_FEC_IMX) += fec_imx.o +obj-$(CONFIG_DRIVER_NET_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o obj-$(CONFIG_DRIVER_NET_FSL_FMAN) += fsl-fman.o obj-$(CONFIG_DRIVER_NET_GIANFAR) += gianfar.o obj-$(CONFIG_DRIVER_NET_KS8851_MLL) += ks8851_mll.o +obj-$(CONFIG_DRIVER_NET_KSZ8873) += ksz8873.o +obj-$(CONFIG_DRIVER_NET_KSZ9477) += ksz9477.o obj-$(CONFIG_DRIVER_NET_MACB) += macb.o obj-$(CONFIG_DRIVER_NET_MICREL) += ksz8864rmn.o obj-$(CONFIG_DRIVER_NET_MPC5200) += fec_mpc5200.o obj-$(CONFIG_DRIVER_NET_MVNETA) += mvneta.o obj-$(CONFIG_DRIVER_NET_ORION) += orion-gbe.o obj-$(CONFIG_DRIVER_NET_RTL8139) += rtl8139.o -obj-$(CONFIG_DRIVER_NET_RTL8169) += rtl8169.o +obj-$(CONFIG_DRIVER_NET_RTL8169) += r8169_main.o r8169_firmware.o r8169_phy_config.o +obj-$(CONFIG_DRIVER_NET_SJA1105) += sja1105.o obj-$(CONFIG_DRIVER_NET_SMC911X) += smc911x.o obj-$(CONFIG_DRIVER_NET_SMC91111) += smc91111.o obj-$(CONFIG_DRIVER_NET_TAP) += tap.o -obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o obj-$(CONFIG_DRIVER_NET_EFI_SNP) += efi-snp.o +obj-$(CONFIG_DRIVER_NET_VIRTIO) += virtio.o obj-$(CONFIG_DRIVER_NET_AG71XX) += ag71xx.o +obj-$(CONFIG_DRIVER_NET_LITEETH) += liteeth.o +obj-$(CONFIG_DRIVER_NET_DSA_REALTEK) += realtek-dsa/ diff --git a/drivers/net/ag71xx.c b/drivers/net/ag71xx.c index 70aaa60f1a..cf00c2eb09 100644 --- a/drivers/net/ag71xx.c +++ b/drivers/net/ag71xx.c @@ -211,7 +211,7 @@ typedef struct { #define MAX_WAIT 1000 struct ag71xx { - struct device_d *dev; + struct device *dev; struct eth_device netdev; void __iomem *regs; void __iomem *regs_gmac; @@ -287,7 +287,7 @@ static inline void ag71xx_wr(struct ag71xx *priv, int reg, u32 val) static int ag71xx_mii_wait(struct ag71xx *priv, int write) { - struct device_d *dev = priv->dev; + struct device *dev = priv->dev; uint64_t start; start = get_time_ns(); @@ -371,7 +371,7 @@ static int ag71xx_ether_get_ethaddr(struct eth_device *edev, unsigned char *adr) static void ag71xx_ether_halt(struct eth_device *edev) { struct ag71xx *priv = edev->priv; - struct device_d *dev = priv->dev; + struct device *dev = priv->dev; uint64_t start; ag71xx_wr(priv, AG71XX_REG_RX_CTRL, 0); @@ -403,7 +403,7 @@ static int ag71xx_ether_rx(struct eth_device *edev) rx_pkt = priv->rx_pkt[priv->next_rx]; /* invalidate */ - dma_sync_single_for_cpu((unsigned long)rx_pkt, pktlen, + dma_sync_single_for_cpu(priv->dev, (unsigned long)rx_pkt, pktlen, DMA_FROM_DEVICE); net_receive(edev, rx_pkt, pktlen - 4); @@ -425,13 +425,13 @@ static int ag71xx_ether_rx(struct eth_device *edev) static int ag71xx_ether_send(struct eth_device *edev, void *packet, int length) { struct ag71xx *priv = edev->priv; - struct device_d *dev = priv->dev; + struct device *dev = priv->dev; ag7240_desc_t *f = &priv->fifo_tx[priv->next_tx]; uint64_t start; int ret = 0; /* flush */ - dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE); + dma_sync_single_for_device(dev, (unsigned long)packet, length, DMA_TO_DEVICE); f->pkt_start_addr = virt_to_phys(packet); f->res1 = 0; @@ -441,7 +441,7 @@ static int ag71xx_ether_send(struct eth_device *edev, void *packet, int length) ag71xx_wr(priv, AG71XX_REG_TX_CTRL, TX_CTRL_TXE); /* flush again?! */ - dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE); + dma_sync_single_for_cpu(dev, (unsigned long)packet, length, DMA_TO_DEVICE); start = get_time_ns(); while (!f->is_empty) { @@ -491,7 +491,7 @@ static int ag71xx_ether_init(struct eth_device *edev) fr->next_desc = virt_to_phys(&priv->fifo_rx[(i + 1) % NO_OF_RX_FIFOS]); /* invalidate */ - dma_sync_single_for_device((unsigned long)rxbuf, MAX_RBUFF_SZ, + dma_sync_single_for_device(priv->dev, (unsigned long)rxbuf, MAX_RBUFF_SZ, DMA_FROM_DEVICE); rxbuf += MAX_RBUFF_SZ; @@ -549,7 +549,7 @@ static struct ag71xx_cfg ag71xx_cfg_ar9344_gmac0 = { .init_mii = ag71xx_ar9344_gmac0_mii_init, }; -static int ag71xx_probe(struct device_d *dev) +static int ag71xx_probe(struct device *dev) { void __iomem *regs, *regs_gmac; struct mii_bus *miibus; @@ -659,7 +659,7 @@ static int ag71xx_probe(struct device_d *dev) return 0; } -static void ag71xx_remove(struct device_d *dev) +static void ag71xx_remove(struct device *dev) { struct eth_device *edev = dev->priv; @@ -671,8 +671,9 @@ static __maybe_unused struct of_device_id ag71xx_dt_ids[] = { { .compatible = "qca,ar9344-gmac0", .data = &ag71xx_cfg_ar9344_gmac0, }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, ag71xx_dt_ids); -static struct driver_d ag71xx_driver = { +static struct driver ag71xx_driver = { .name = "ag71xx-gmac", .probe = ag71xx_probe, .remove = ag71xx_remove, diff --git a/drivers/net/altera_tse.c b/drivers/net/altera_tse.c deleted file mode 100644 index f1dfe5952c..0000000000 --- a/drivers/net/altera_tse.c +++ /dev/null @@ -1,563 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Altera TSE Network driver - * - * Copyright (C) 2008 Altera Corporation. - * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> - * Copyright (C) 2011 Franck JULLIEN, <elec4fun@gmail.com> - */ - -#include <common.h> -#include <dma.h> -#include <net.h> -#include <init.h> -#include <clock.h> -#include <linux/mii.h> -#include <linux/phy.h> -#include <linux/err.h> - -#include <io.h> -#include <asm/dma-mapping.h> - -#include "altera_tse.h" - -/* This is a generic routine that the SGDMA mode-specific routines - * call to populate a descriptor. - * arg1 :pointer to first SGDMA descriptor. - * arg2 :pointer to next SGDMA descriptor. - * arg3 :Address to where data to be written. - * arg4 :Address from where data to be read. - * arg5 :no of byte to transaction. - * arg6 :variable indicating to generate start of packet or not - * arg7 :read fixed - * arg8 :write fixed - * arg9 :read burst - * arg10 :write burst - * arg11 :atlantic_channel number - */ -static void alt_sgdma_construct_descriptor_burst( - struct alt_sgdma_descriptor *desc, - struct alt_sgdma_descriptor *next, - uint32_t *read_addr, - uint32_t *write_addr, - uint16_t length_or_eop, - uint8_t generate_eop, - uint8_t read_fixed, - uint8_t write_fixed_or_sop, - uint8_t read_burst, - uint8_t write_burst, - uint8_t atlantic_channel) -{ - uint32_t temp; - - /* - * Mark the "next" descriptor as "not" owned by hardware. This prevents - * The SGDMA controller from continuing to process the chain. This is - * done as a single IO write to bypass cache, without flushing - * the entire descriptor, since only the 8-bit descriptor status must - * be flushed. - */ - if (!next) - printf("Next descriptor not defined!!\n"); - - temp = readb(&next->descriptor_control); - writeb(temp & ~ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK, - &next->descriptor_control); - - writel((uint32_t)read_addr, &desc->source); - writel((uint32_t)write_addr, &desc->destination); - writel((uint32_t)next, &desc->next); - - writel(0, &desc->source_pad); - writel(0, &desc->destination_pad); - writel(0, &desc->next_pad); - writew(length_or_eop, &desc->bytes_to_transfer); - writew(0, &desc->actual_bytes_transferred); - writeb(0, &desc->descriptor_status); - - /* SGDMA burst not currently supported */ - writeb(0, &desc->read_burst); - writeb(0, &desc->write_burst); - - /* - * Set the descriptor control block as follows: - * - Set "owned by hardware" bit - * - Optionally set "generate EOP" bit - * - Optionally set the "read from fixed address" bit - * - Optionally set the "write to fixed address bit (which serves - * serves as a "generate SOP" control bit in memory-to-stream mode). - * - Set the 4-bit atlantic channel, if specified - * - * Note this step is performed after all other descriptor information - * has been filled out so that, if the controller already happens to be - * pointing at this descriptor, it will not run (via the "owned by - * hardware" bit) until all other descriptor has been set up. - */ - - writeb((ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK) | - (generate_eop ? ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK : 0) | - (read_fixed ? ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK : 0) | - (write_fixed_or_sop ? ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK : 0) | - (atlantic_channel ? ((atlantic_channel & 0x0F) << 3) : 0), - &desc->descriptor_control); -} - -static int alt_sgdma_do_sync_transfer(struct alt_sgdma_registers *dev, - struct alt_sgdma_descriptor *desc) -{ - uint32_t temp; - uint64_t start; - uint64_t tout; - - /* Wait for any pending transfers to complete */ - tout = ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT * MSECOND; - - start = get_time_ns(); - - while (readl(&dev->status) & ALT_SGDMA_STATUS_BUSY_MSK) { - if (is_timeout(start, tout)) { - debug("Timeout waiting sgdma in do sync!\n"); - break; - } - } - - /* - * Clear any (previous) status register information - * that might occlude our error checking later. - */ - writel(0xFF, &dev->status); - - /* Point the controller at the descriptor */ - writel((uint32_t)desc, &dev->next_descriptor_pointer); - debug("next desc in sgdma 0x%x\n", (uint32_t)dev->next_descriptor_pointer); - - /* - * Set up SGDMA controller to: - * - Disable interrupt generation - * - Run once a valid descriptor is written to controller - * - Stop on an error with any particular descriptor - */ - writel(ALT_SGDMA_CONTROL_RUN_MSK | ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK, - &dev->control); - - /* Wait for the descriptor (chain) to complete */ - debug("wait for sgdma...."); - start = get_time_ns(); - - while (readl(&dev->status) & ALT_SGDMA_STATUS_BUSY_MSK) { - if (is_timeout(start, tout)) { - debug("Timeout waiting sgdma in do sync!\n"); - break; - } - } - - debug("done\n"); - - /* Clear Run */ - temp = readl(&dev->control); - writel(temp & ~ALT_SGDMA_CONTROL_RUN_MSK, &dev->control); - - /* Get & clear status register contents */ - debug("tx sgdma status = 0x%x", readl(&dev->status)); - writel(0xFF, &dev->status); - - return 0; -} - -static int alt_sgdma_do_async_transfer(struct alt_sgdma_registers *dev, - struct alt_sgdma_descriptor *desc) -{ - uint64_t start; - uint64_t tout; - - /* Wait for any pending transfers to complete */ - tout = ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT * MSECOND; - - start = get_time_ns(); - - while (readl(&dev->status) & ALT_SGDMA_STATUS_BUSY_MSK) { - if (is_timeout(start, tout)) { - debug("Timeout waiting sgdma in do async!\n"); - break; - } - } - - /* - * Clear any (previous) status register information - * that might occlude our error checking later. - */ - writel(0xFF, &dev->status); - - /* Point the controller at the descriptor */ - writel((uint32_t)desc, &dev->next_descriptor_pointer); - - /* - * Set up SGDMA controller to: - * - Disable interrupt generation - * - Run once a valid descriptor is written to controller - * - Stop on an error with any particular descriptor - */ - writel(ALT_SGDMA_CONTROL_RUN_MSK | ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK, - &dev->control); - - return 0; -} - -static int tse_get_ethaddr(struct eth_device *edev, unsigned char *m) -{ - struct altera_tse_priv *priv = edev->priv; - struct alt_tse_mac *mac_dev = priv->tse_regs; - - m[5] = (readl(&mac_dev->mac_addr_1) >> 8) && 0xFF; - m[4] = (readl(&mac_dev->mac_addr_1)) && 0xFF; - m[3] = (readl(&mac_dev->mac_addr_0) >> 24) && 0xFF; - m[2] = (readl(&mac_dev->mac_addr_0) >> 16) && 0xFF; - m[1] = (readl(&mac_dev->mac_addr_0) >> 8) && 0xFF; - m[0] = (readl(&mac_dev->mac_addr_0)) && 0xFF; - - return 0; -} - -static int tse_set_ethaddr(struct eth_device *edev, const unsigned char *m) -{ - struct altera_tse_priv *priv = edev->priv; - struct alt_tse_mac *mac_dev = priv->tse_regs; - - debug("Setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n", - m[0], m[1], m[2], m[3], m[4], m[5]); - - writel(m[3] << 24 | m[2] << 16 | m[1] << 8 | m[0], &mac_dev->mac_addr_0); - writel((m[5] << 8 | m[4]) & 0xFFFF, &mac_dev->mac_addr_1); - - return 0; -} - -static int tse_phy_read(struct mii_bus *bus, int phy_addr, int reg) -{ - struct altera_tse_priv *priv = bus->priv; - struct alt_tse_mac *mac_dev = priv->tse_regs; - uint32_t *mdio_regs; - - writel(phy_addr, &mac_dev->mdio_phy1_addr); - - mdio_regs = (uint32_t *)&mac_dev->mdio_phy1; - - return readl(&mdio_regs[reg]) & 0xFFFF; -} - -static int tse_phy_write(struct mii_bus *bus, int phy_addr, int reg, u16 val) -{ - struct altera_tse_priv *priv = bus->priv; - struct alt_tse_mac *mac_dev = priv->tse_regs; - uint32_t *mdio_regs; - - writel(phy_addr, &mac_dev->mdio_phy1_addr); - - mdio_regs = (uint32_t *)&mac_dev->mdio_phy1; - - writel((uint32_t)val, &mdio_regs[reg]); - - return 0; -} - -static void tse_reset(struct eth_device *edev) -{ - /* stop sgdmas, disable tse receive */ - struct altera_tse_priv *priv = edev->priv; - struct alt_tse_mac *mac_dev = priv->tse_regs; - struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx_regs; - struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx_regs; - struct alt_sgdma_descriptor *rx_desc = priv->rx_desc; - struct alt_sgdma_descriptor *tx_desc = priv->tx_desc; - uint64_t start; - uint64_t tout; - - tout = ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT * MSECOND; - - /* clear rx desc & wait for sgdma to complete */ - writeb(0, &rx_desc->descriptor_control); - writel(0, &rx_sgdma->control); - - writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control); - writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control); - mdelay(100); - - start = get_time_ns(); - - while (readl(&rx_sgdma->status) & ALT_SGDMA_STATUS_BUSY_MSK) { - if (is_timeout(start, tout)) { - printf("Timeout waiting for rx sgdma!\n"); - writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control); - writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &rx_sgdma->control); - break; - } - } - - /* clear tx desc & wait for sgdma to complete */ - writeb(0, &tx_desc->descriptor_control); - writel(0, &tx_sgdma->control); - - writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control); - writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control); - mdelay(100); - - start = get_time_ns(); - - while (readl(&tx_sgdma->status) & ALT_SGDMA_STATUS_BUSY_MSK) { - if (is_timeout(start, tout)) { - printf("Timeout waiting for tx sgdma!\n"); - writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control); - writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK, &tx_sgdma->control); - break; - } - } - - /* reset the mac */ - writel(ALTERA_TSE_CMD_TX_ENA_MSK | ALTERA_TSE_CMD_RX_ENA_MSK | - ALTERA_TSE_CMD_SW_RESET_MSK, &mac_dev->command_config); - - start = get_time_ns(); - tout = ALT_TSE_SW_RESET_WATCHDOG_TOUT * MSECOND; - - while (readl(&mac_dev->command_config) & ALTERA_TSE_CMD_SW_RESET_MSK) { - if (is_timeout(start, tout)) { - printf("TSEMAC SW reset bit never cleared!\n"); - break; - } - } -} - -static int tse_eth_open(struct eth_device *edev) -{ - struct altera_tse_priv *priv = edev->priv; - int ret; - - ret = phy_device_connect(edev, priv->miibus, priv->phy_addr, NULL, 0, - PHY_INTERFACE_MODE_NA); - if (ret) - return ret; - - return 0; -} - -static int tse_eth_send(struct eth_device *edev, void *packet, int length) -{ - - struct altera_tse_priv *priv = edev->priv; - struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx_regs; - struct alt_sgdma_descriptor *tx_desc = priv->tx_desc; - struct alt_sgdma_descriptor *tx_desc_cur = tx_desc; - - flush_dcache_range((uint32_t)packet, (uint32_t)packet + length); - alt_sgdma_construct_descriptor_burst( - (struct alt_sgdma_descriptor *)&tx_desc[0], - (struct alt_sgdma_descriptor *)&tx_desc[1], - (uint32_t *)packet, /* read addr */ - (uint32_t *)0, /* */ - length, /* length or EOP ,will change for each tx */ - 0x1, /* gen eop */ - 0x0, /* read fixed */ - 0x1, /* write fixed or sop */ - 0x0, /* read burst */ - 0x0, /* write burst */ - 0x0 /* channel */ - ); - - alt_sgdma_do_sync_transfer(tx_sgdma, tx_desc_cur); - - return 0; -} - -static void tse_eth_halt(struct eth_device *edev) -{ - struct altera_tse_priv *priv = edev->priv; - struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx_regs; - struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx_regs; - - writel(0, &rx_sgdma->control); /* Stop the controller and reset settings */ - writel(0, &tx_sgdma->control); /* Stop the controller and reset settings */ -} - -static int tse_eth_rx(struct eth_device *edev) -{ - uint16_t packet_length = 0; - - struct altera_tse_priv *priv = edev->priv; - struct alt_sgdma_descriptor *rx_desc = priv->rx_desc; - struct alt_sgdma_descriptor *rx_desc_cur = rx_desc; - struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx_regs; - - if (rx_desc_cur->descriptor_status & - ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK) { - - packet_length = rx_desc->actual_bytes_transferred; - net_receive(edev, NetRxPackets[0], packet_length); - - /* Clear Run */ - rx_sgdma->control = (rx_sgdma->control & (~ALT_SGDMA_CONTROL_RUN_MSK)); - - /* start descriptor again */ - flush_dcache_range((uint32_t)(NetRxPackets[0]), (uint32_t)(NetRxPackets[0]) + PKTSIZE); - alt_sgdma_construct_descriptor_burst( - (struct alt_sgdma_descriptor *)&rx_desc[0], - (struct alt_sgdma_descriptor *)&rx_desc[1], - (uint32_t)0x0, /* read addr */ - (uint32_t *)NetRxPackets[0], /* */ - 0x0, /* length or EOP */ - 0x0, /* gen eop */ - 0x0, /* read fixed */ - 0x0, /* write fixed or sop */ - 0x0, /* read burst */ - 0x0, /* write burst */ - 0x0 /* channel */ - ); - - /* setup the sgdma */ - alt_sgdma_do_async_transfer(priv->sgdma_rx_regs, rx_desc); - } - - return 0; -} - -static int tse_init_dev(struct eth_device *edev) -{ - struct altera_tse_priv *priv = edev->priv; - struct alt_tse_mac *mac_dev = priv->tse_regs; - struct alt_sgdma_descriptor *tx_desc = priv->tx_desc; - struct alt_sgdma_descriptor *rx_desc = priv->rx_desc; - struct alt_sgdma_descriptor *rx_desc_cur; - - rx_desc_cur = rx_desc; - - tse_reset(edev); - - /* need to create sgdma */ - alt_sgdma_construct_descriptor_burst( - (struct alt_sgdma_descriptor *)&tx_desc[0], - (struct alt_sgdma_descriptor *)&tx_desc[1], - (uint32_t *)NULL, /* read addr */ - (uint32_t *)0, /* */ - 0, /* length or EOP ,will change for each tx */ - 0x1, /* gen eop */ - 0x0, /* read fixed */ - 0x1, /* write fixed or sop */ - 0x0, /* read burst */ - 0x0, /* write burst */ - 0x0 /* channel */ - ); - - flush_dcache_range((uint32_t)(NetRxPackets[0]), (uint32_t)(NetRxPackets[0]) + PKTSIZE); - alt_sgdma_construct_descriptor_burst( - (struct alt_sgdma_descriptor *)&rx_desc[0], - (struct alt_sgdma_descriptor *)&rx_desc[1], - (uint32_t)0x0, /* read addr */ - (uint32_t *)NetRxPackets[0], /* */ - 0x0, /* length or EOP */ - 0x0, /* gen eop */ - 0x0, /* read fixed */ - 0x0, /* write fixed or sop */ - 0x0, /* read burst */ - 0x0, /* write burst */ - 0x0 /* channel */ - ); - - /* start rx async transfer */ - alt_sgdma_do_async_transfer(priv->sgdma_rx_regs, rx_desc_cur); - - /* Initialize MAC registers */ - writel(PKTSIZE, &mac_dev->max_frame_length); - - /* NO Shift */ - writel(0, &mac_dev->rx_cmd_stat); - writel(0, &mac_dev->tx_cmd_stat); - - /* enable MAC */ - writel(ALTERA_TSE_CMD_TX_ENA_MSK | ALTERA_TSE_CMD_RX_ENA_MSK, &mac_dev->command_config); - - return 0; -} - -static int tse_probe(struct device_d *dev) -{ - struct resource *iores; - struct altera_tse_priv *priv; - struct mii_bus *miibus; - struct eth_device *edev; - struct alt_sgdma_descriptor *rx_desc; - struct alt_sgdma_descriptor *tx_desc; -#ifndef CONFIG_TSE_USE_DEDICATED_DESC_MEM - uint32_t dma_handle; -#endif - edev = xzalloc(sizeof(struct eth_device)); - priv = xzalloc(sizeof(struct altera_tse_priv)); - miibus = xzalloc(sizeof(struct mii_bus)); - - edev->priv = priv; - - edev->init = tse_init_dev; - edev->open = tse_eth_open; - edev->send = tse_eth_send; - edev->recv = tse_eth_rx; - edev->halt = tse_eth_halt; - edev->get_ethaddr = tse_get_ethaddr; - edev->set_ethaddr = tse_set_ethaddr; - edev->parent = dev; - -#ifdef CONFIG_TSE_USE_DEDICATED_DESC_MEM - iores = dev_request_mem_resource(dev, 3); - if (IS_ERR(iores)) - return PTR_ERR(iores); - tx_desc = IOMEM(iores->start); - rx_desc = tx_desc + 2; -#else - tx_desc = dma_alloc_coherent(sizeof(*tx_desc) * (3 + PKTBUFSRX), (dma_addr_t *)&dma_handle); - rx_desc = tx_desc + 2; - - if (!tx_desc) { - free(edev); - free(miibus); - return 0; - } -#endif - - memset(rx_desc, 0, (sizeof *rx_desc) * (PKTBUFSRX + 1)); - memset(tx_desc, 0, (sizeof *tx_desc) * 2); - - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - priv->tse_regs = IOMEM(iores->start); - iores = dev_request_mem_resource(dev, 1); - if (IS_ERR(iores)) - return PTR_ERR(iores); - priv->sgdma_rx_regs = IOMEM(iores->start); - - iores = dev_request_mem_resource(dev, 2); - if (IS_ERR(iores)) - return PTR_ERR(iores); - priv->sgdma_tx_regs = IOMEM(iores->start); - priv->rx_desc = rx_desc; - priv->tx_desc = tx_desc; - - priv->miibus = miibus; - - miibus->read = tse_phy_read; - miibus->write = tse_phy_write; - miibus->priv = priv; - miibus->parent = dev; - - if (dev->platform_data != NULL) - priv->phy_addr = *((int8_t *)(dev->platform_data)); - else - priv->phy_addr = -1; - - mdiobus_register(miibus); - - return eth_register(edev); -} - -static struct driver_d altera_tse_driver = { - .name = "altera_tse", - .probe = tse_probe, -}; -device_platform_driver(altera_tse_driver); diff --git a/drivers/net/altera_tse.h b/drivers/net/altera_tse.h deleted file mode 100644 index 7bff14de81..0000000000 --- a/drivers/net/altera_tse.h +++ /dev/null @@ -1,296 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Altera 10/100/1000 triple speed ethernet mac - * - * Copyright (C) 2008 Altera Corporation. - * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> - * Copyright (C) 2011 Franck JULLIEN <elec4fun@gmail.com> - */ -#ifndef _ALTERA_TSE_H_ -#define _ALTERA_TSE_H_ - -/* SGDMA Stuff */ -#define ALT_SGDMA_STATUS_ERROR_MSK (0x00000001) -#define ALT_SGDMA_STATUS_EOP_ENCOUNTERED_MSK (0x00000002) -#define ALT_SGDMA_STATUS_DESC_COMPLETED_MSK (0x00000004) -#define ALT_SGDMA_STATUS_CHAIN_COMPLETED_MSK (0x00000008) -#define ALT_SGDMA_STATUS_BUSY_MSK (0x00000010) - -#define ALT_SGDMA_CONTROL_IE_ERROR_MSK (0x00000001) -#define ALT_SGDMA_CONTROL_IE_EOP_ENCOUNTERED_MSK (0x00000002) -#define ALT_SGDMA_CONTROL_IE_DESC_COMPLETED_MSK (0x00000004) -#define ALT_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK (0x00000008) -#define ALT_SGDMA_CONTROL_IE_GLOBAL_MSK (0x00000010) -#define ALT_SGDMA_CONTROL_RUN_MSK (0x00000020) -#define ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK (0x00000040) -#define ALT_SGDMA_CONTROL_IE_MAX_DESC_PROCESSED_MSK (0x00000080) -#define ALT_SGDMA_CONTROL_MAX_DESC_PROCESSED_MSK (0x0000FF00) -#define ALT_SGDMA_CONTROL_SOFTWARERESET_MSK (0x00010000) -#define ALT_SGDMA_CONTROL_PARK_MSK (0x00020000) -#define ALT_SGDMA_CONTROL_CLEAR_INTERRUPT_MSK (0x80000000) - -#define ALTERA_TSE_SGDMA_INTR_MASK (ALT_SGDMA_CONTROL_IE_CHAIN_COMPLETED_MSK \ - | ALT_SGDMA_STATUS_DESC_COMPLETED_MSK \ - | ALT_SGDMA_CONTROL_IE_GLOBAL_MSK) - -/* - * Descriptor control bit masks & offsets - * - * Note: The control byte physically occupies bits [31:24] in memory. - * The following bit-offsets are expressed relative to the LSB of - * the control register bitfield. - */ -#define ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK (0x00000001) -#define ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK (0x00000002) -#define ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK (0x00000004) -#define ALT_SGDMA_DESCRIPTOR_CONTROL_ATLANTIC_CHANNEL_MSK (0x00000008) -#define ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK (0x00000080) - -/* - * Descriptor status bit masks & offsets - * - * Note: The status byte physically occupies bits [23:16] in memory. - * The following bit-offsets are expressed relative to the LSB of - * the status register bitfield. - */ -#define ALT_SGDMA_DESCRIPTOR_STATUS_E_CRC_MSK (0x00000001) -#define ALT_SGDMA_DESCRIPTOR_STATUS_E_PARITY_MSK (0x00000002) -#define ALT_SGDMA_DESCRIPTOR_STATUS_E_OVERFLOW_MSK (0x00000004) -#define ALT_SGDMA_DESCRIPTOR_STATUS_E_SYNC_MSK (0x00000008) -#define ALT_SGDMA_DESCRIPTOR_STATUS_E_UEOP_MSK (0x00000010) -#define ALT_SGDMA_DESCRIPTOR_STATUS_E_MEOP_MSK (0x00000020) -#define ALT_SGDMA_DESCRIPTOR_STATUS_E_MSOP_MSK (0x00000040) -#define ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK (0x00000080) -#define ALT_SGDMA_DESCRIPTOR_STATUS_ERROR_MSK (0x0000007F) - -/* - * The SGDMA controller buffer descriptor allocates - * 64 bits for each address. To support ANSI C, the - * struct implementing a descriptor places 32-bits - * of padding directly above each address; each pad must - * be cleared when initializing a descriptor. - */ - -/* - * Buffer Descriptor data structure - * - */ -struct alt_sgdma_descriptor { - unsigned int *source; /* the address of data to be read. */ - unsigned int source_pad; - - unsigned int *destination; /* the address to write data */ - unsigned int destination_pad; - - unsigned int *next; /* the next descriptor in the list. */ - unsigned int next_pad; - - unsigned short bytes_to_transfer; /* the number of bytes to transfer */ - unsigned char read_burst; - unsigned char write_burst; - - unsigned short actual_bytes_transferred;/* bytes transferred by DMA */ - unsigned char descriptor_status; - unsigned char descriptor_control; - -} __attribute__ ((packed, aligned(1))); - -/* SG-DMA Control/Status Slave registers map */ - -struct alt_sgdma_registers { - unsigned int status; - unsigned int status_pad[3]; - unsigned int control; - unsigned int control_pad[3]; - unsigned int next_descriptor_pointer; - unsigned int descriptor_pad[3]; -}; - -/* TSE Stuff */ -#define ALTERA_TSE_CMD_TX_ENA_MSK (0x00000001) -#define ALTERA_TSE_CMD_RX_ENA_MSK (0x00000002) -#define ALTERA_TSE_CMD_XON_GEN_MSK (0x00000004) -#define ALTERA_TSE_CMD_ETH_SPEED_MSK (0x00000008) -#define ALTERA_TSE_CMD_PROMIS_EN_MSK (0x00000010) -#define ALTERA_TSE_CMD_PAD_EN_MSK (0x00000020) -#define ALTERA_TSE_CMD_CRC_FWD_MSK (0x00000040) -#define ALTERA_TSE_CMD_PAUSE_FWD_MSK (0x00000080) -#define ALTERA_TSE_CMD_PAUSE_IGNORE_MSK (0x00000100) -#define ALTERA_TSE_CMD_TX_ADDR_INS_MSK (0x00000200) -#define ALTERA_TSE_CMD_HD_ENA_MSK (0x00000400) -#define ALTERA_TSE_CMD_EXCESS_COL_MSK (0x00000800) -#define ALTERA_TSE_CMD_LATE_COL_MSK (0x00001000) -#define ALTERA_TSE_CMD_SW_RESET_MSK (0x00002000) -#define ALTERA_TSE_CMD_MHASH_SEL_MSK (0x00004000) -#define ALTERA_TSE_CMD_LOOPBACK_MSK (0x00008000) -/* Bits (18:16) = address select */ -#define ALTERA_TSE_CMD_TX_ADDR_SEL_MSK (0x00070000) -#define ALTERA_TSE_CMD_MAGIC_ENA_MSK (0x00080000) -#define ALTERA_TSE_CMD_SLEEP_MSK (0x00100000) -#define ALTERA_TSE_CMD_WAKEUP_MSK (0x00200000) -#define ALTERA_TSE_CMD_XOFF_GEN_MSK (0x00400000) -#define ALTERA_TSE_CMD_CNTL_FRM_ENA_MSK (0x00800000) -#define ALTERA_TSE_CMD_NO_LENGTH_CHECK_MSK (0x01000000) -#define ALTERA_TSE_CMD_ENA_10_MSK (0x02000000) -#define ALTERA_TSE_CMD_RX_ERR_DISC_MSK (0x04000000) -/* Bits (30..27) reserved */ -#define ALTERA_TSE_CMD_CNT_RESET_MSK (0x80000000) - -#define ALTERA_TSE_TX_CMD_STAT_TX_SHIFT16 (0x00040000) -#define ALTERA_TSE_TX_CMD_STAT_OMIT_CRC (0x00020000) - -#define ALTERA_TSE_RX_CMD_STAT_RX_SHIFT16 (0x02000000) - -#define ALT_TSE_SW_RESET_WATCHDOG_CNTR 10000 -#define ALT_TSE_SGDMA_BUSY_WATCHDOG_CNTR 90000000 - -#define ALT_TSE_SW_RESET_WATCHDOG_TOUT 1 /* ms */ -#define ALT_TSE_SGDMA_BUSY_WATCHDOG_TOUT 5 /* ms */ - -struct alt_tse_mdio { - unsigned int control; /*PHY device operation control register */ - unsigned int status; /*PHY device operation status register */ - unsigned int phy_id1; /*Bits 31:16 of PHY identifier. */ - unsigned int phy_id2; /*Bits 15:0 of PHY identifier. */ - unsigned int auto_negotiation_advertisement; - unsigned int remote_partner_base_page_ability; - - unsigned int reg6; - unsigned int reg7; - unsigned int reg8; - unsigned int reg9; - unsigned int rega; - unsigned int regb; - unsigned int regc; - unsigned int regd; - unsigned int rege; - unsigned int regf; - unsigned int reg10; - unsigned int reg11; - unsigned int reg12; - unsigned int reg13; - unsigned int reg14; - unsigned int reg15; - unsigned int reg16; - unsigned int reg17; - unsigned int reg18; - unsigned int reg19; - unsigned int reg1a; - unsigned int reg1b; - unsigned int reg1c; - unsigned int reg1d; - unsigned int reg1e; - unsigned int reg1f; -}; - -/* MAC register Space */ - -struct alt_tse_mac { - unsigned int megacore_revision; - unsigned int scratch_pad; - unsigned int command_config; - unsigned int mac_addr_0; - unsigned int mac_addr_1; - unsigned int max_frame_length; - unsigned int pause_quanta; - unsigned int rx_sel_empty_threshold; - unsigned int rx_sel_full_threshold; - unsigned int tx_sel_empty_threshold; - unsigned int tx_sel_full_threshold; - unsigned int rx_almost_empty_threshold; - unsigned int rx_almost_full_threshold; - unsigned int tx_almost_empty_threshold; - unsigned int tx_almost_full_threshold; - unsigned int mdio_phy0_addr; - unsigned int mdio_phy1_addr; - - /* only if 100/1000 BaseX PCS, reserved otherwise */ - unsigned int reservedx44[5]; - - unsigned int reg_read_access_status; - unsigned int min_tx_ipg_length; - - /* IEEE 802.3 oEntity Managed Object Support */ - unsigned int aMACID_1; /*The MAC addresses */ - unsigned int aMACID_2; - unsigned int aFramesTransmittedOK; - unsigned int aFramesReceivedOK; - unsigned int aFramesCheckSequenceErrors; - unsigned int aAlignmentErrors; - unsigned int aOctetsTransmittedOK; - unsigned int aOctetsReceivedOK; - - /* IEEE 802.3 oPausedEntity Managed Object Support */ - unsigned int aTxPAUSEMACCtrlFrames; - unsigned int aRxPAUSEMACCtrlFrames; - - /* IETF MIB (MIB-II) Object Support */ - unsigned int ifInErrors; - unsigned int ifOutErrors; - unsigned int ifInUcastPkts; - unsigned int ifInMulticastPkts; - unsigned int ifInBroadcastPkts; - unsigned int ifOutDiscards; - unsigned int ifOutUcastPkts; - unsigned int ifOutMulticastPkts; - unsigned int ifOutBroadcastPkts; - - /* IETF RMON MIB Object Support */ - unsigned int etherStatsDropEvent; - unsigned int etherStatsOctets; - unsigned int etherStatsPkts; - unsigned int etherStatsUndersizePkts; - unsigned int etherStatsOversizePkts; - unsigned int etherStatsPkts64Octets; - unsigned int etherStatsPkts65to127Octets; - unsigned int etherStatsPkts128to255Octets; - unsigned int etherStatsPkts256to511Octets; - unsigned int etherStatsPkts512to1023Octets; - unsigned int etherStatsPkts1024to1518Octets; - - unsigned int etherStatsPkts1519toXOctets; - unsigned int etherStatsJabbers; - unsigned int etherStatsFragments; - - unsigned int reservedxE4; - - /*FIFO control register. */ - unsigned int tx_cmd_stat; - unsigned int rx_cmd_stat; - - unsigned int ipaccTxConf; - unsigned int ipaccRxConf; - unsigned int ipaccRxStat; - unsigned int ipaccRxStatSum; - - /*Multicast address resolution table */ - unsigned int hash_table[64]; - - /*Registers 0 to 31 within PHY device 0/1 */ - struct alt_tse_mdio mdio_phy0; - struct alt_tse_mdio mdio_phy1; - - /*4 Supplemental MAC Addresses */ - unsigned int supp_mac_addr_0_0; - unsigned int supp_mac_addr_0_1; - unsigned int supp_mac_addr_1_0; - unsigned int supp_mac_addr_1_1; - unsigned int supp_mac_addr_2_0; - unsigned int supp_mac_addr_2_1; - unsigned int supp_mac_addr_3_0; - unsigned int supp_mac_addr_3_1; - - unsigned int reservedx320[56]; -}; - -struct altera_tse_priv { - void __iomem *tse_regs; - void __iomem *sgdma_rx_regs; - void __iomem *sgdma_tx_regs; - void __iomem *rx_desc; - void __iomem *tx_desc; - int phy_addr; - struct mii_bus *miibus; -}; - -#endif /* _ALTERA_TSE_H_ */ diff --git a/drivers/net/ar231x.c b/drivers/net/ar231x.c index 48d41b8cb2..1af34a3117 100644 --- a/drivers/net/ar231x.c +++ b/drivers/net/ar231x.c @@ -357,7 +357,7 @@ static int ar231x_mdiibus_reset(struct mii_bus *bus) return 0; } -static int ar231x_eth_probe(struct device_d *dev) +static int ar231x_eth_probe(struct device *dev) { struct resource *iores; struct ar231x_eth_priv *priv; @@ -419,13 +419,9 @@ static int ar231x_eth_probe(struct device_d *dev) return 0; } -static struct driver_d ar231x_eth_driver = { +static struct driver ar231x_eth_driver = { .name = "ar231x_eth", .probe = ar231x_eth_probe, }; -static int ar231x_eth_driver_init(void) -{ - return platform_driver_register(&ar231x_eth_driver); -} -device_initcall(ar231x_eth_driver_init); +device_platform_driver(ar231x_eth_driver); diff --git a/drivers/net/arc_emac.c b/drivers/net/arc_emac.c index 28ec5e31fb..fa6e3955db 100644 --- a/drivers/net/arc_emac.c +++ b/drivers/net/arc_emac.c @@ -189,7 +189,7 @@ static int arc_emac_open(struct eth_device *edev) rxbd->data = cpu_to_le32(rxbuf); /* Return ownership to EMAC */ - dma_sync_single_for_device((unsigned long)rxbuf, PKTSIZE, + dma_sync_single_for_device(edev->parent, (unsigned long)rxbuf, PKTSIZE, DMA_FROM_DEVICE); rxbd->info = cpu_to_le32(FOR_EMAC | PKTSIZE); @@ -240,7 +240,7 @@ static int arc_emac_send(struct eth_device *edev, void *data, int length) length = EMAC_ZLEN; } - dma_sync_single_for_device((unsigned long)data, length, DMA_TO_DEVICE); + dma_sync_single_for_device(edev->parent, (unsigned long)data, length, DMA_TO_DEVICE); bd->data = cpu_to_le32(data); bd->info = cpu_to_le32(FOR_EMAC | FIRST_OR_LAST_MASK | length); @@ -249,7 +249,7 @@ static int arc_emac_send(struct eth_device *edev, void *data, int length) ret = wait_on_timeout(20 * MSECOND, (arc_reg_get(priv, R_STATUS) & TXINT_MASK) != 0); - dma_sync_single_for_cpu((unsigned long)data, length, DMA_TO_DEVICE); + dma_sync_single_for_cpu(edev->parent, (unsigned long)data, length, DMA_TO_DEVICE); if (ret) { dev_err(&edev->dev, "transmit timeout\n"); @@ -297,12 +297,12 @@ static int arc_emac_recv(struct eth_device *edev) pktlen = info & LEN_MASK; - dma_sync_single_for_cpu((unsigned long)rxbd->data, pktlen, + dma_sync_single_for_cpu(edev->parent, (unsigned long)rxbd->data, pktlen, DMA_FROM_DEVICE); net_receive(edev, (unsigned char *)rxbd->data, pktlen); - dma_sync_single_for_device((unsigned long)rxbd->data, pktlen, + dma_sync_single_for_device(edev->parent, (unsigned long)rxbd->data, pktlen, DMA_FROM_DEVICE); rxbd->info = cpu_to_le32(FOR_EMAC | PKTSIZE); @@ -381,7 +381,7 @@ static int arc_emac_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, #define DEFAULT_EMAC_CLOCK_FREQUENCY 50000000UL; -static int arc_emac_probe(struct device_d *dev) +static int arc_emac_probe(struct device *dev) { struct resource *iores; struct eth_device *edev; @@ -464,8 +464,9 @@ static __maybe_unused struct of_device_id arc_emac_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, arc_emac_dt_ids); -static struct driver_d arc_emac_driver = { +static struct driver arc_emac_driver = { .name = "arc-emac", .probe = arc_emac_probe, .of_compatible = DRV_OF_COMPAT(arc_emac_dt_ids), diff --git a/drivers/net/at91_ether.c b/drivers/net/at91_ether.c index e69a300a6b..0959a3c503 100644 --- a/drivers/net/at91_ether.c +++ b/drivers/net/at91_ether.c @@ -15,10 +15,10 @@ #include <xfuncs.h> #include <init.h> #include <asm/io.h> -#include <mach/hardware.h> -#include <mach/at91rm9200_emac.h> -#include <mach/board.h> -#include <generated/mach-types.h> +#include <mach/at91/hardware.h> +#include <mach/at91/at91rm9200_emac.h> +#include <mach/at91/board.h> +#include <asm/mach-types.h> #include <linux/clk.h> #include <linux/mii.h> #include <errno.h> @@ -186,7 +186,8 @@ static int at91_ether_send(struct eth_device *edev, void *packet, int length) { while (!(at91_emac_read(AT91_EMAC_TSR) & AT91_EMAC_TSR_BNQ)); - dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE); + dma_sync_single_for_device(edev->parent, (unsigned long)packet, + length, DMA_TO_DEVICE); /* Set address of the data in the Transmit Address register */ at91_emac_write(AT91_EMAC_TAR, (unsigned long) packet); @@ -198,7 +199,8 @@ static int at91_ether_send(struct eth_device *edev, void *packet, int length) at91_emac_write(AT91_EMAC_TSR, at91_emac_read(AT91_EMAC_TSR) | AT91_EMAC_TSR_COMP); - dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE); + dma_sync_single_for_cpu(edev->parent, (unsigned long)packet, + length, DMA_TO_DEVICE); return 0; } @@ -214,10 +216,10 @@ static int at91_ether_rx(struct eth_device *edev) size = rbfp->size & RBF_SIZE; - dma_sync_single_for_cpu((unsigned long)rbfp->addr, size, + dma_sync_single_for_cpu(edev->parent, (unsigned long)rbfp->addr, size, DMA_FROM_DEVICE); net_receive(edev, (unsigned char *)(rbfp->addr & RBF_ADDR), size); - dma_sync_single_for_device((unsigned long)rbfp->addr, size, + dma_sync_single_for_device(edev->parent, (unsigned long)rbfp->addr, size, DMA_FROM_DEVICE); rbfp->addr &= ~RBF_OWNER; @@ -284,7 +286,7 @@ static int at91_ether_init(struct eth_device *edev) return 0; } -static int at91_ether_probe(struct device_d *dev) +static int at91_ether_probe(struct device *dev) { unsigned int mac_cfg; struct ether_device *ether_dev; @@ -307,6 +309,7 @@ static int at91_ether_probe(struct device_d *dev) miibus = ðer_dev->miibus; edev->priv = ether_dev; + edev->parent = dev; edev->init = at91_ether_init; edev->open = at91_ether_open; edev->send = at91_ether_send; @@ -353,7 +356,7 @@ static int at91_ether_probe(struct device_d *dev) return 0; } -static struct driver_d at91_ether_driver = { +static struct driver at91_ether_driver = { .name = "at91_ether", .probe = at91_ether_probe, }; diff --git a/drivers/net/bcmgenet.c b/drivers/net/bcmgenet.c new file mode 100644 index 0000000000..9e0bacb31a --- /dev/null +++ b/drivers/net/bcmgenet.c @@ -0,0 +1,627 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Amit Singh Tomar <amittomer25@gmail.com> + * + * Driver for Broadcom GENETv5 Ethernet controller (as found on the RPi4) + * This driver is based on the Linux driver: + * drivers/net/ethernet/broadcom/genet/bcmgenet.c + * which is: Copyright (c) 2014-2017 Broadcom + * + * The hardware supports multiple queues (16 priority queues and one + * default queue), both for RX and TX. There are 256 DMA descriptors (both + * for TX and RX), and they live in MMIO registers. The hardware allows + * assigning descriptor ranges to queues, but we choose the most simple setup: + * All 256 descriptors are assigned to the default queue (#16). + * Also the Linux driver supports multiple generations of the MAC, whereas + * we only support v5, as used in the Raspberry Pi 4. + */ + +#include <common.h> +#include <dma.h> +#include <malloc.h> +#include <net.h> +#include <init.h> +#include <driver.h> +#include <io.h> +#include <clock.h> +#include <xfuncs.h> +#include <linux/phy.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <of_net.h> +#include <linux/iopoll.h> + +/* Register definitions derived from Linux source */ +#define SYS_REV_CTRL 0x00 + +#define SYS_PORT_CTRL 0x04 +#define PORT_MODE_EXT_GPHY 3 + +#define GENET_SYS_OFF 0x0000 +#define SYS_RBUF_FLUSH_CTRL (GENET_SYS_OFF + 0x08) +#define SYS_TBUF_FLUSH_CTRL (GENET_SYS_OFF + 0x0c) + +#define GENET_EXT_OFF 0x0080 +#define EXT_RGMII_OOB_CTRL (GENET_EXT_OFF + 0x0c) +#define RGMII_LINK BIT(4) +#define OOB_DISABLE BIT(5) +#define RGMII_MODE_EN BIT(6) +#define ID_MODE_DIS BIT(16) + +#define GENET_RBUF_OFF 0x0300 +#define RBUF_TBUF_SIZE_CTRL (GENET_RBUF_OFF + 0xb4) +#define RBUF_CTRL (GENET_RBUF_OFF + 0x00) +#define RBUF_ALIGN_2B BIT(1) + +#define GENET_UMAC_OFF 0x0800 +#define UMAC_MIB_CTRL (GENET_UMAC_OFF + 0x580) +#define UMAC_MAX_FRAME_LEN (GENET_UMAC_OFF + 0x014) +#define UMAC_MAC0 (GENET_UMAC_OFF + 0x00c) +#define UMAC_MAC1 (GENET_UMAC_OFF + 0x010) +#define UMAC_CMD (GENET_UMAC_OFF + 0x008) +#define MDIO_CMD (GENET_UMAC_OFF + 0x614) +#define UMAC_TX_FLUSH (GENET_UMAC_OFF + 0x334) +#define MDIO_START_BUSY BIT(29) +#define MDIO_READ_FAIL BIT(28) +#define MDIO_RD (2 << 26) +#define MDIO_WR BIT(26) +#define MDIO_PMD_SHIFT 21 +#define MDIO_PMD_MASK 0x1f +#define MDIO_REG_SHIFT 16 +#define MDIO_REG_MASK 0x1f + +#define CMD_TX_EN BIT(0) +#define CMD_RX_EN BIT(1) +#define UMAC_SPEED_10 0 +#define UMAC_SPEED_100 1 +#define UMAC_SPEED_1000 2 +#define UMAC_SPEED_2500 3 +#define CMD_SPEED_SHIFT 2 +#define CMD_SPEED_MASK 3 +#define CMD_SW_RESET BIT(13) +#define CMD_LCL_LOOP_EN BIT(15) +#define CMD_TX_EN BIT(0) +#define CMD_RX_EN BIT(1) + +#define MIB_RESET_RX BIT(0) +#define MIB_RESET_RUNT BIT(1) +#define MIB_RESET_TX BIT(2) + +/* total number of Buffer Descriptors, same for Rx/Tx */ +#define TOTAL_DESCS 256 +#define RX_DESCS TOTAL_DESCS +#define TX_DESCS TOTAL_DESCS + +#define DEFAULT_Q 0x10 + +#define ENET_MAX_MTU_SIZE 1536 + +/* Tx/Rx Dma Descriptor common bits */ +#define DMA_EN BIT(0) +#define DMA_RING_BUF_EN_SHIFT 0x01 +#define DMA_RING_BUF_EN_MASK 0xffff +#define DMA_BUFLENGTH_MASK 0x0fff +#define DMA_BUFLENGTH_SHIFT 16 +#define DMA_RING_SIZE_SHIFT 16 +#define DMA_OWN 0x8000 +#define DMA_EOP 0x4000 +#define DMA_SOP 0x2000 +#define DMA_WRAP 0x1000 +#define DMA_MAX_BURST_LENGTH 0x8 +/* Tx specific DMA descriptor bits */ +#define DMA_TX_UNDERRUN 0x0200 +#define DMA_TX_APPEND_CRC 0x0040 +#define DMA_TX_OW_CRC 0x0020 +#define DMA_TX_DO_CSUM 0x0010 +#define DMA_TX_QTAG_SHIFT 7 + +/* DMA rings size */ +#define DMA_RING_SIZE 0x40 +#define DMA_RINGS_SIZE (DMA_RING_SIZE * (DEFAULT_Q + 1)) + +/* DMA descriptor */ +#define DMA_DESC_LENGTH_STATUS 0x00 +#define DMA_DESC_ADDRESS_LO 0x04 +#define DMA_DESC_ADDRESS_HI 0x08 +#define DMA_DESC_SIZE 12 + +#define GENET_RX_OFF 0x2000 +#define GENET_RDMA_REG_OFF 0x2c00 +#define GENET_TX_OFF 0x4000 +#define GENET_TDMA_REG_OFF 0x4c00 + +#define DMA_FC_THRESH_HI (RX_DESCS >> 4) +#define DMA_FC_THRESH_LO 5 +#define DMA_FC_THRESH_VALUE ((DMA_FC_THRESH_LO << 16) | \ + DMA_FC_THRESH_HI) + +#define DMA_XOFF_THRESHOLD_SHIFT 16 + +#define TDMA_RING_REG_BASE 0x5000 +#define TDMA_READ_PTR (TDMA_RING_REG_BASE + 0x00) +#define TDMA_CONS_INDEX (TDMA_RING_REG_BASE + 0x08) +#define TDMA_PROD_INDEX (TDMA_RING_REG_BASE + 0x0c) +#define DMA_RING_BUF_SIZE 0x10 +#define DMA_START_ADDR 0x14 +#define DMA_END_ADDR 0x1c +#define DMA_MBUF_DONE_THRESH 0x24 +#define TDMA_FLOW_PERIOD (TDMA_RING_REG_BASE + 0x28) +#define TDMA_WRITE_PTR (TDMA_RING_REG_BASE + 0x2c) + +#define RDMA_RING_REG_BASE 0x3000 +#define RDMA_WRITE_PTR (RDMA_RING_REG_BASE + 0x00) +#define RDMA_PROD_INDEX (RDMA_RING_REG_BASE + 0x08) +#define RDMA_CONS_INDEX (RDMA_RING_REG_BASE + 0x0c) +#define RDMA_XON_XOFF_THRESH (RDMA_RING_REG_BASE + 0x28) +#define RDMA_READ_PTR (RDMA_RING_REG_BASE + 0x2c) + +#define TDMA_REG_BASE 0x5040 +#define RDMA_REG_BASE 0x3040 +#define DMA_RING_CFG 0x00 +#define DMA_CTRL 0x04 +#define DMA_SCB_BURST_SIZE 0x0c + +#define RX_BUF_LENGTH 2048 +#define RX_TOTAL_BUFSIZE (RX_BUF_LENGTH * RX_DESCS) +#define RX_BUF_OFFSET 2 + +struct bcmgenet_eth_priv { + char *rxbuffer; + void *mac_reg; + int tx_index; + int rx_index; + int c_index; + u32 interface; + struct mii_bus miibus; + struct eth_device edev; + struct device *dev; + unsigned char addr[6]; +}; + +static void bcmgenet_umac_reset(struct bcmgenet_eth_priv *priv) +{ + u32 reg; + + reg = readl(priv->mac_reg + SYS_RBUF_FLUSH_CTRL); + reg |= BIT(1); + writel(reg, (priv->mac_reg + SYS_RBUF_FLUSH_CTRL)); + udelay(10); + + reg &= ~BIT(1); + writel(reg, (priv->mac_reg + SYS_RBUF_FLUSH_CTRL)); + udelay(10); + + writel(0, (priv->mac_reg + SYS_RBUF_FLUSH_CTRL)); + udelay(10); + + writel(0, priv->mac_reg + UMAC_CMD); + + writel(CMD_SW_RESET | CMD_LCL_LOOP_EN, priv->mac_reg + UMAC_CMD); + udelay(2); + writel(0, priv->mac_reg + UMAC_CMD); + + /* clear tx/rx counter */ + writel(MIB_RESET_RX | MIB_RESET_TX | MIB_RESET_RUNT, + priv->mac_reg + UMAC_MIB_CTRL); + writel(0, priv->mac_reg + UMAC_MIB_CTRL); + + writel(ENET_MAX_MTU_SIZE, priv->mac_reg + UMAC_MAX_FRAME_LEN); + + /* init rx registers, enable ip header optimization */ + reg = readl(priv->mac_reg + RBUF_CTRL); + reg |= RBUF_ALIGN_2B; + writel(reg, (priv->mac_reg + RBUF_CTRL)); + + writel(1, (priv->mac_reg + RBUF_TBUF_SIZE_CTRL)); +} + +static int __bcmgenet_set_hwaddr(struct bcmgenet_eth_priv *priv) +{ + const unsigned char *addr = priv->addr; + u32 reg; + + reg = addr[0] << 24 | addr[1] << 16 | addr[2] << 8 | addr[3]; + writel_relaxed(reg, priv->mac_reg + UMAC_MAC0); + + reg = addr[4] << 8 | addr[5]; + writel_relaxed(reg, priv->mac_reg + UMAC_MAC1); + + return 0; +} + +static int bcmgenet_set_hwaddr(struct eth_device *dev, const unsigned char *addr) +{ + struct bcmgenet_eth_priv *priv = dev->priv; + + memcpy(priv->addr, addr, 6); + + __bcmgenet_set_hwaddr(priv); + + return 0; +} + +static int bcmgenet_get_hwaddr(struct eth_device *edev, unsigned char *mac) +{ + return -1; +} + +static void bcmgenet_disable_dma(struct bcmgenet_eth_priv *priv) +{ + clrbits_le32(priv->mac_reg + TDMA_REG_BASE + DMA_CTRL, DMA_EN); + clrbits_le32(priv->mac_reg + RDMA_REG_BASE + DMA_CTRL, DMA_EN); + + writel(1, priv->mac_reg + UMAC_TX_FLUSH); + udelay(10); + writel(0, priv->mac_reg + UMAC_TX_FLUSH); +} + +static void bcmgenet_enable_dma(struct bcmgenet_eth_priv *priv) +{ + u32 dma_ctrl = (1 << (DEFAULT_Q + DMA_RING_BUF_EN_SHIFT)) | DMA_EN; + + writel(dma_ctrl, priv->mac_reg + TDMA_REG_BASE + DMA_CTRL); + setbits_le32(priv->mac_reg + RDMA_REG_BASE + DMA_CTRL, dma_ctrl); +} + +static int bcmgenet_gmac_eth_send(struct eth_device *edev, void *packet, int length) +{ + struct bcmgenet_eth_priv *priv = edev->priv; + void *desc_base = priv->mac_reg + GENET_TX_OFF + priv->tx_index * DMA_DESC_SIZE; + u32 len_stat = length << DMA_BUFLENGTH_SHIFT; + u32 prod_index, cons; + u32 tries = 100; + dma_addr_t dma; + + prod_index = readl(priv->mac_reg + TDMA_PROD_INDEX); + + dma = dma_map_single(priv->dev, packet, length, DMA_TO_DEVICE); + if (dma_mapping_error(priv->dev, dma)) + return -EFAULT; + + len_stat |= 0x3f << DMA_TX_QTAG_SHIFT; + len_stat |= DMA_TX_APPEND_CRC | DMA_SOP | DMA_EOP; + + /* Set-up packet for transmission */ + writel(lower_32_bits(dma), (desc_base + DMA_DESC_ADDRESS_LO)); + writel(upper_32_bits(dma), (desc_base + DMA_DESC_ADDRESS_HI)); + writel(len_stat, (desc_base + DMA_DESC_LENGTH_STATUS)); + + /* Increment index and start transmission */ + if (++priv->tx_index >= TX_DESCS) + priv->tx_index = 0; + + prod_index++; + + /* Start Transmisson */ + writel(prod_index, priv->mac_reg + TDMA_PROD_INDEX); + + do { + cons = readl(priv->mac_reg + TDMA_CONS_INDEX); + } while ((cons & 0xffff) < prod_index && --tries); + + dma_unmap_single(priv->dev, dma, length, DMA_TO_DEVICE); + + if (!tries) { + dev_err(priv->dev, "sending timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int bcmgenet_gmac_eth_recv(struct eth_device *edev) +{ + struct bcmgenet_eth_priv *priv = edev->priv; + void *desc_base = priv->mac_reg + GENET_RX_OFF + priv->rx_index * DMA_DESC_SIZE; + u32 prod_index = readl(priv->mac_reg + RDMA_PROD_INDEX); + u32 length, addr_lo, addr_hi; + dma_addr_t addr; + + if (prod_index == priv->c_index) + return -EAGAIN; + + length = readl(desc_base + DMA_DESC_LENGTH_STATUS); + length = (length >> DMA_BUFLENGTH_SHIFT) & DMA_BUFLENGTH_MASK; + addr_lo = readl(desc_base + DMA_DESC_ADDRESS_LO); + addr_hi = readl(desc_base + DMA_DESC_ADDRESS_HI); + addr = (u64)addr_hi << 32 | addr_lo; + + dma_sync_single_for_cpu(priv->dev, addr, length, DMA_FROM_DEVICE); + + /* To cater for the IP header alignment the hardware does. + * This would actually not be needed if we don't program + * RBUF_ALIGN_2B + */ + net_receive(edev, (void *)addr + RX_BUF_OFFSET, length - RX_BUF_OFFSET); + + dma_sync_single_for_device(priv->dev, addr, length, DMA_FROM_DEVICE); + + /* Tell the MAC we have consumed that last receive buffer. */ + priv->c_index = (priv->c_index + 1) & 0xffff; + writel(priv->c_index, priv->mac_reg + RDMA_CONS_INDEX); + + /* Forward our descriptor pointer, wrapping around if needed. */ + if (++priv->rx_index >= RX_DESCS) + priv->rx_index = 0; + + return 0; +} + +static void rx_descs_init(struct bcmgenet_eth_priv *priv) +{ + char *rxbuffs = priv->rxbuffer; + u32 len_stat, i; + void *desc_base = priv->mac_reg + GENET_RX_OFF; + + len_stat = (RX_BUF_LENGTH << DMA_BUFLENGTH_SHIFT) | DMA_OWN; + + for (i = 0; i < RX_DESCS; i++) { + writel(lower_32_bits((uintptr_t)&rxbuffs[i * RX_BUF_LENGTH]), + desc_base + i * DMA_DESC_SIZE + DMA_DESC_ADDRESS_LO); + writel(upper_32_bits((uintptr_t)&rxbuffs[i * RX_BUF_LENGTH]), + desc_base + i * DMA_DESC_SIZE + DMA_DESC_ADDRESS_HI); + writel(len_stat, + desc_base + i * DMA_DESC_SIZE + DMA_DESC_LENGTH_STATUS); + } +} + +static void rx_ring_init(struct bcmgenet_eth_priv *priv) +{ + writel(DMA_MAX_BURST_LENGTH, + priv->mac_reg + RDMA_REG_BASE + DMA_SCB_BURST_SIZE); + + writel(0x0, priv->mac_reg + RDMA_RING_REG_BASE + DMA_START_ADDR); + writel(0x0, priv->mac_reg + RDMA_READ_PTR); + writel(0x0, priv->mac_reg + RDMA_WRITE_PTR); + writel(RX_DESCS * DMA_DESC_SIZE / 4 - 1, + priv->mac_reg + RDMA_RING_REG_BASE + DMA_END_ADDR); + + /* cannot init RDMA_PROD_INDEX to 0, so align RDMA_CONS_INDEX on it instead */ + priv->c_index = readl(priv->mac_reg + RDMA_PROD_INDEX); + writel(priv->c_index, priv->mac_reg + RDMA_CONS_INDEX); + priv->rx_index = priv->c_index; + priv->rx_index &= 0xff; + writel((RX_DESCS << DMA_RING_SIZE_SHIFT) | RX_BUF_LENGTH, + priv->mac_reg + RDMA_RING_REG_BASE + DMA_RING_BUF_SIZE); + writel(DMA_FC_THRESH_VALUE, priv->mac_reg + RDMA_XON_XOFF_THRESH); + writel(1 << DEFAULT_Q, priv->mac_reg + RDMA_REG_BASE + DMA_RING_CFG); +} + +static void tx_ring_init(struct bcmgenet_eth_priv *priv) +{ + writel(DMA_MAX_BURST_LENGTH, + priv->mac_reg + TDMA_REG_BASE + DMA_SCB_BURST_SIZE); + + writel(0x0, priv->mac_reg + TDMA_RING_REG_BASE + DMA_START_ADDR); + writel(0x0, priv->mac_reg + TDMA_READ_PTR); + writel(0x0, priv->mac_reg + TDMA_WRITE_PTR); + writel(TX_DESCS * DMA_DESC_SIZE / 4 - 1, + priv->mac_reg + TDMA_RING_REG_BASE + DMA_END_ADDR); + /* cannot init TDMA_CONS_INDEX to 0, so align TDMA_PROD_INDEX on it instead */ + priv->tx_index = readl(priv->mac_reg + TDMA_CONS_INDEX); + writel(priv->tx_index, priv->mac_reg + TDMA_PROD_INDEX); + priv->tx_index &= 0xFF; + writel(0x1, priv->mac_reg + TDMA_RING_REG_BASE + DMA_MBUF_DONE_THRESH); + writel(0x0, priv->mac_reg + TDMA_FLOW_PERIOD); + writel((TX_DESCS << DMA_RING_SIZE_SHIFT) | RX_BUF_LENGTH, + priv->mac_reg + TDMA_RING_REG_BASE + DMA_RING_BUF_SIZE); + + writel(1 << DEFAULT_Q, priv->mac_reg + TDMA_REG_BASE + DMA_RING_CFG); +} + +static void bcmgenet_adjust_link(struct eth_device *edev) +{ + struct bcmgenet_eth_priv *priv = edev->priv; + struct phy_device *phy_dev = edev->phydev; + u32 speed; + + switch (phy_dev->speed) { + case SPEED_1000: + speed = UMAC_SPEED_1000; + break; + case SPEED_100: + speed = UMAC_SPEED_100; + break; + case SPEED_10: + speed = UMAC_SPEED_10; + break; + default: + dev_err(priv->dev, "bcmgenet: Unsupported PHY speed: %d\n", phy_dev->speed); + return; + } + + clrsetbits_le32(priv->mac_reg + EXT_RGMII_OOB_CTRL, OOB_DISABLE, + RGMII_LINK | RGMII_MODE_EN); + + if (phy_dev->interface == PHY_INTERFACE_MODE_RGMII || + phy_dev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { + setbits_le32(priv->mac_reg + EXT_RGMII_OOB_CTRL, ID_MODE_DIS); + writel(PORT_MODE_EXT_GPHY, priv->mac_reg + SYS_PORT_CTRL); + } + + clrsetbits_le32(priv->mac_reg + UMAC_CMD, CMD_SPEED_MASK << CMD_SPEED_SHIFT, + speed << CMD_SPEED_SHIFT); +} + +static int bcmgenet_gmac_eth_start(struct eth_device *edev) +{ + struct bcmgenet_eth_priv *priv = edev->priv; + int ret; + + bcmgenet_umac_reset(priv); + + __bcmgenet_set_hwaddr(priv); + + /* Disable RX/TX DMA and flush TX queues */ + bcmgenet_disable_dma(priv); + + rx_ring_init(priv); + rx_descs_init(priv); + tx_ring_init(priv); + bcmgenet_enable_dma(priv); + + ret = phy_device_connect(edev, &priv->miibus, -1, + bcmgenet_adjust_link, 0, + priv->interface); + if (ret) + return ret; + + /* Enable Rx/Tx */ + setbits_le32(priv->mac_reg + UMAC_CMD, CMD_TX_EN | CMD_RX_EN); + + return 0; +} + +static void bcmgenet_mdio_start(struct bcmgenet_eth_priv *priv) +{ + setbits_le32(priv->mac_reg + MDIO_CMD, MDIO_START_BUSY); +} + +static int bcmgenet_mdio_write(struct mii_bus *bus, int addr, + int reg, u16 value) +{ + struct bcmgenet_eth_priv *priv = bus->priv; + u32 val; + + /* Prepare the read operation */ + val = MDIO_WR | (addr << MDIO_PMD_SHIFT) | + (reg << MDIO_REG_SHIFT) | (0xffff & value); + writel_relaxed(val, priv->mac_reg + MDIO_CMD); + + /* Start MDIO transaction */ + bcmgenet_mdio_start(priv); + + return readl_poll_timeout(priv->mac_reg + MDIO_CMD, reg, + !(reg & MDIO_START_BUSY), 20); +} + +static int bcmgenet_mdio_read(struct mii_bus *bus, int addr, int reg) +{ + struct bcmgenet_eth_priv *priv = bus->priv; + u32 val; + int ret; + + /* Prepare the read operation */ + val = MDIO_RD | (addr << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT); + writel_relaxed(val, priv->mac_reg + MDIO_CMD); + + /* Start MDIO transaction */ + bcmgenet_mdio_start(priv); + + ret = readl_poll_timeout(priv->mac_reg + MDIO_CMD, reg, + !(reg & MDIO_START_BUSY), 20); + if (ret) + return ret; + + val = readl_relaxed(priv->mac_reg + MDIO_CMD); + + return val & 0xffff; +} + +static int bcmgenet_probe(struct device *dev) +{ + struct resource *iores; + struct bcmgenet_eth_priv *priv; + u32 reg; + int ret; + u8 major; + struct eth_device *edev; + + priv = xzalloc(sizeof(*priv)); + edev = &priv->edev; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) { + ret = PTR_ERR(iores); + return ret; + } + priv->mac_reg = IOMEM(iores->start); + priv->dev = dev; + priv->rxbuffer = dma_alloc(RX_TOTAL_BUFSIZE); + + edev->open = bcmgenet_gmac_eth_start; + edev->send = bcmgenet_gmac_eth_send; + edev->recv = bcmgenet_gmac_eth_recv; + edev->get_ethaddr = bcmgenet_get_hwaddr; + edev->set_ethaddr = bcmgenet_set_hwaddr; + edev->parent = dev; + edev->priv = priv; + dev->priv = priv; + + /* Read GENET HW version */ + reg = readl_relaxed(priv->mac_reg + SYS_REV_CTRL); + major = (reg >> 24) & 0x0f; + if (major != 6) { + if (major == 5) + major = 4; + else if (major == 0) + major = 1; + + dev_err(priv->dev, "Unsupported GENETv%d.%d\n", major, (reg >> 16) & 0x0f); + return -ENODEV; + } + + ret = of_get_phy_mode(dev->of_node); + if (ret < 0) + priv->interface = PHY_INTERFACE_MODE_MII; + else + priv->interface = ret; + + writel(0, priv->mac_reg + SYS_RBUF_FLUSH_CTRL); + udelay(10); + /* disable MAC while updating its registers */ + writel(0, priv->mac_reg + UMAC_CMD); + /* issue soft reset with (rg)mii loopback to ensure a stable rxclk */ + writel(CMD_SW_RESET | CMD_LCL_LOOP_EN, priv->mac_reg + UMAC_CMD); + + priv->miibus.read = bcmgenet_mdio_read; + priv->miibus.write = bcmgenet_mdio_write; + + priv->miibus.priv = priv; + priv->miibus.parent = dev; + priv->miibus.dev.of_node + = of_get_compatible_child(dev->of_node, "brcm,genet-mdio-v5"); + + ret = mdiobus_register(&priv->miibus); + if (ret) + return ret; + + ret = eth_register(edev); + if (ret) + return ret; + + return 0; +} + +static void bcmgenet_gmac_eth_stop(struct bcmgenet_eth_priv *priv) +{ + clrbits_le32(priv->mac_reg + UMAC_CMD, CMD_TX_EN | CMD_RX_EN); + + bcmgenet_disable_dma(priv); +} + +static void bcmgenet_remove(struct device *dev) +{ + struct bcmgenet_eth_priv *priv = dev->priv; + + bcmgenet_gmac_eth_stop(priv); +} + +static struct of_device_id bcmgenet_ids[] = { + { + .compatible = "brcm,genet-v5", + }, { + .compatible = "brcm,bcm2711-genet-v5", + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, bcmgenet_ids); + +static struct driver bcmgenet_driver = { + .name = "brcm-genet", + .probe = bcmgenet_probe, + .remove = bcmgenet_remove, + .of_compatible = DRV_OF_COMPAT(bcmgenet_ids), +}; +device_platform_driver(bcmgenet_driver); diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index b9a6575009..31ca61a230 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -9,7 +9,6 @@ #include <command.h> #include <dma.h> -#include <net.h> #include <malloc.h> #include <net.h> #include <linux/phy.h> @@ -23,7 +22,7 @@ #include <asm/system.h> #include <linux/err.h> -#include <mach/cpsw.h> +#include <mach/omap/cpsw.h> #define CPSW_VERSION_1 0x19010a #define CPSW_VERSION_2 0x19010c @@ -52,6 +51,10 @@ #define CPDMA_DESC_EOP BIT(30) #define CPDMA_DESC_OWNER BIT(29) #define CPDMA_DESC_EOQ BIT(28) +#define CPDMA_DESC_TO_PORT_EN BIT(20) +#define CPDMA_FROM_TO_PORT_SHIFT 16 +#define CPDMA_RX_SOURCE_PORT(__status__) \ + (((__status__) >> CPDMA_FROM_TO_PORT_SHIFT) & 0x7) #define SLIVER_SIZE 0x40 @@ -161,16 +164,18 @@ enum cpsw_ale_port_state { /* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */ #define ALE_SECURE 1 #define ALE_BLOCKED 2 +#define ALE_VLAN 4 struct cpsw_slave { struct cpsw_slave_regs *regs; struct cpsw_sliver_regs *sliver; + int port_vlan; int slave_num; int phy_id; phy_interface_t phy_if; struct eth_device edev; struct cpsw_priv *cpsw; - struct device_d dev; + struct device dev; }; struct cpdma_desc { @@ -190,7 +195,7 @@ struct cpdma_chan { }; struct cpsw_priv { - struct device_d *dev; + struct device *dev; u32 version; struct cpsw_platform_data data; @@ -210,6 +215,8 @@ struct cpsw_priv { unsigned int slave_size; unsigned int sliver_ofs; + void *rx_buffer[PKTBUFSRX - 2]; + struct cpdma_desc *descs; struct cpdma_desc *desc_free; struct cpdma_chan rx_chan, tx_chan; @@ -218,7 +225,7 @@ struct cpsw_priv { }; struct cpsw_mdio_priv { - struct device_d *dev; + struct device *dev; struct mii_bus miibus; struct cpsw_mdio_regs *mdio_regs; }; @@ -258,6 +265,7 @@ static inline void cpsw_ale_set_##name(u32 *ale_entry, u32 value) \ } DEFINE_ALE_FIELD(entry_type, 60, 2) +DEFINE_ALE_FIELD(vlan_id, 48, 12) DEFINE_ALE_FIELD(mcast_state, 62, 2) DEFINE_ALE_FIELD(port_mask, 66, 3) DEFINE_ALE_FIELD(ucast_type, 62, 2) @@ -265,6 +273,10 @@ DEFINE_ALE_FIELD(port_num, 66, 2) DEFINE_ALE_FIELD(blocked, 65, 1) DEFINE_ALE_FIELD(secure, 64, 1) DEFINE_ALE_FIELD(mcast, 40, 1) +DEFINE_ALE_FIELD(vlan_untag, 24, 3) +DEFINE_ALE_FIELD(vlan_reg_mcast, 16, 3) +DEFINE_ALE_FIELD(vlan_unreg_mcast, 8, 3) +DEFINE_ALE_FIELD(vlan_member_list, 0, 3) static char ethbdaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -309,6 +321,23 @@ static int cpsw_ale_write(struct cpsw_priv *priv, int idx, u32 *ale_entry) return idx; } +static int cpsw_ale_match_vlan(struct cpsw_priv *priv, u16 vid) +{ + u32 ale_entry[ALE_ENTRY_WORDS]; + int type, idx; + + for (idx = 0; idx < priv->ale_entries; idx++) { + cpsw_ale_read(priv, idx, ale_entry); + type = cpsw_ale_get_entry_type(ale_entry); + if (type != ALE_TYPE_VLAN) + continue; + if (cpsw_ale_get_vlan_id(ale_entry) == vid) + return idx; + } + + return -ENOENT; +} + static int cpsw_ale_match_addr(struct cpsw_priv *priv, u8* addr) { u32 ale_entry[ALE_ENTRY_WORDS]; @@ -373,13 +402,47 @@ static int cpsw_ale_find_ageable(struct cpsw_priv *priv) return -ENOENT; } +static int cpsw_ale_add_vlan(struct cpsw_priv *priv, u16 vid, int port_mask, + int untag, int reg_mcast, int unreg_mcast) +{ + u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; + int idx; + + idx = cpsw_ale_match_vlan(priv, vid); + if (idx >= 0) + cpsw_ale_read(priv, idx, ale_entry); + + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN); + cpsw_ale_set_vlan_id(ale_entry, vid); + cpsw_ale_set_vlan_untag(ale_entry, untag); + cpsw_ale_set_vlan_reg_mcast(ale_entry, reg_mcast); + cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast); + cpsw_ale_set_vlan_member_list(ale_entry, port_mask); + + if (idx < 0) + idx = cpsw_ale_match_free(priv); + if (idx < 0) + idx = cpsw_ale_find_ageable(priv); + if (idx < 0) + return -ENOMEM; + + cpsw_ale_write(priv, idx, ale_entry); + return 0; +} + static int cpsw_ale_add_ucast(struct cpsw_priv *priv, u8 *addr, - int port, int flags) + int port, int flags, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; int idx; - cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + if (flags & ALE_VLAN) { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR); + cpsw_ale_set_vlan_id(ale_entry, vid); + } else { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + } + cpsw_ale_set_addr(ale_entry, addr); cpsw_ale_set_ucast_type(ale_entry, ALE_UCAST_PERSISTANT); cpsw_ale_set_secure(ale_entry, (flags & ALE_SECURE) ? 1 : 0); @@ -398,7 +461,8 @@ static int cpsw_ale_add_ucast(struct cpsw_priv *priv, u8 *addr, return 0; } -static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask) +static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask, + int flags, u16 vid, int mcast_state) { u32 ale_entry[ALE_ENTRY_WORDS] = {0, 0, 0}; int idx, mask; @@ -407,9 +471,14 @@ static int cpsw_ale_add_mcast(struct cpsw_priv *priv, u8 *addr, int port_mask) if (idx >= 0) cpsw_ale_read(priv, idx, ale_entry); - cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + if (flags & ALE_VLAN) { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_VLAN_ADDR); + cpsw_ale_set_vlan_id(ale_entry, vid); + } else { + cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_ADDR); + } cpsw_ale_set_addr(ale_entry, addr); - cpsw_ale_set_mcast_state(ale_entry, ALE_MCAST_FWD_2); + cpsw_ale_set_mcast_state(ale_entry, mcast_state); mask = cpsw_ale_get_port_mask(ale_entry); port_mask |= mask; @@ -514,7 +583,7 @@ static int cpsw_mdio_write(struct mii_bus *bus, int phy_id, int phy_reg, u16 val return 0; } -static int cpsw_mdio_probe(struct device_d *dev) +static int cpsw_mdio_probe(struct device *dev) { struct resource *iores; struct cpsw_mdio_priv *priv; @@ -524,7 +593,12 @@ static int cpsw_mdio_probe(struct device_d *dev) priv = xzalloc(sizeof(*priv)); + /* If we can't request I/O memory region, we'll assume parent did + * it for us + */ iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores) && PTR_ERR(iores) == -EBUSY) + iores = dev_get_resource(dev, IORESOURCE_MEM, 0); if (IS_ERR(iores)) return PTR_ERR(iores); priv->mdio_regs = IOMEM(iores->start); @@ -548,7 +622,7 @@ static int cpsw_mdio_probe(struct device_d *dev) * silicon. Since the effect of (b) was found to be largely * negligible, we keep things simple here. */ - udelay(1000); + udelay(2000); start = get_time_ns(); while (1) { @@ -581,8 +655,9 @@ static __maybe_unused struct of_device_id cpsw_mdio_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, cpsw_mdio_dt_ids); -static struct driver_d cpsw_mdio_driver = { +static struct driver cpsw_mdio_driver = { .name = "cpsw-mdio", .probe = cpsw_mdio_probe, .of_compatible = DRV_OF_COMPAT(cpsw_mdio_dt_ids), @@ -673,6 +748,7 @@ static inline u32 cpsw_get_slave_port(struct cpsw_priv *priv, u32 slave_num) static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv) { u32 slave_port; + u32 port_mask; dev_dbg(&slave->dev, "* %s\n", __func__); @@ -689,8 +765,22 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv) slave_port = cpsw_get_slave_port(priv, slave->slave_num); cpsw_ale_port_state(priv, slave_port, ALE_PORT_STATE_FORWARD); + port_mask = BIT(slave_port) | BIT(priv->host_port); + + /* set port_vlan to host_vlan */ + writel(BIT(slave->slave_num), &slave->regs->port_vlan); + slave->port_vlan = readl(&slave->regs->port_vlan); + slave->port_vlan &= 0xfff; + + /* add dual emac default entries */ + cpsw_ale_add_vlan(priv, slave->port_vlan, port_mask, + port_mask, port_mask, 0); /* add broadcast address */ - cpsw_ale_add_mcast(priv, ethbdaddr, 1 << slave_port); + cpsw_ale_add_mcast(priv, ethbdaddr, BIT(priv->host_port), ALE_VLAN, + slave->port_vlan, 0); + cpsw_ale_add_ucast(priv, priv->mac_addr, priv->host_port, + ALE_SECURE | ALE_VLAN, + slave->port_vlan); } static struct cpdma_desc *cpdma_desc_alloc(struct cpsw_priv *priv) @@ -714,7 +804,8 @@ static void cpdma_desc_free(struct cpsw_priv *priv, struct cpdma_desc *desc) } static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan, - void *buffer, int len) + void *sw_buffer, dma_addr_t hw_buffer, + int len, int port) { struct cpdma_desc *desc, *prev; u32 mode; @@ -728,11 +819,15 @@ static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan, mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP; + if (port) + mode |= CPDMA_DESC_TO_PORT_EN | + (port << CPDMA_FROM_TO_PORT_SHIFT); + writel(0, &desc->hw_next); - writel((u32)buffer, &desc->hw_buffer); + writel(hw_buffer, &desc->hw_buffer); writel(len, &desc->hw_len); writel(mode | len, &desc->hw_mode); - writel((u32)buffer, &desc->sw_buffer); + writel((u32)sw_buffer, &desc->sw_buffer); writel((u32)len, &desc->sw_len); if (!chan->head) { @@ -758,10 +853,11 @@ done: return 0; } -static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan, - void **buffer, int *len) +static int cpdma_process(struct cpsw_slave *slave, struct cpdma_chan *chan, + void **buffer, dma_addr_t *dma, int *len) { struct cpdma_desc *desc = chan->head; + struct cpsw_priv *priv = slave->cpsw; u32 status; if (!desc) @@ -772,6 +868,8 @@ static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan, if (len) *len = status & 0x7ff; + if (dma) + *dma = readl(&desc->hw_buffer); if (buffer) *buffer = (void *)readl(&desc->sw_buffer); @@ -783,6 +881,14 @@ static int cpdma_process(struct cpsw_priv *priv, struct cpdma_chan *chan, return -EBUSY; } + /* cpsw_send is cleaning finished descriptors on next send + * so we only have to check for rx channel here + */ + if (CPDMA_RX_SOURCE_PORT(status) != BIT(slave->slave_num) && + chan == &priv->rx_chan) { + return -ENOMSG; + } + chan->head = (void *)readl(&desc->hw_next); writel((u32)desc, chan->cp); @@ -801,15 +907,25 @@ static int cpsw_open(struct eth_device *edev) { struct cpsw_slave *slave = edev->priv; struct cpsw_priv *priv = slave->cpsw; - int i, ret; + int ret; dev_dbg(&slave->dev, "* %s\n", __func__); + cpsw_slave_init(slave, priv); + ret = phy_device_connect(edev, NULL, slave->phy_id, cpsw_adjust_link, 0, slave->phy_if); if (ret) return ret; + return 0; +} + +static int cpsw_setup(struct device *dev) +{ + struct cpsw_priv *priv = dev->priv; + int i, ret; + /* soft reset the controller and initialize priv */ soft_reset(priv, &priv->regs->soft_reset); @@ -817,7 +933,10 @@ static int cpsw_open(struct eth_device *edev) cpsw_ale_enable(priv, 1); cpsw_ale_clear(priv, 1); cpsw_ale_bypass(priv, 0); - cpsw_ale_vlan_aware(priv, 0); /* vlan unaware mode */ + cpsw_ale_vlan_aware(priv, 1); /* vlan aware mode */ + + /* dual mac mode in fifo */ + writel(BIT(16), &priv->host_port_regs->flow_thresh); /* setup host port priority mapping */ writel(0x76543210, &priv->host_port_regs->cpdma_tx_pri_map); @@ -831,12 +950,6 @@ static int cpsw_open(struct eth_device *edev) cpsw_ale_port_state(priv, priv->host_port, ALE_PORT_STATE_FORWARD); - cpsw_ale_add_ucast(priv, priv->mac_addr, priv->host_port, - ALE_SECURE); - cpsw_ale_add_mcast(priv, ethbdaddr, 1 << priv->host_port); - - cpsw_slave_init(slave, priv); - /* init descriptor pool */ for (i = 0; i < NUM_DESCS; i++) { u32 val = (i == (NUM_DESCS - 1)) ? 0 : (u32)&priv->descs[i + 1]; @@ -872,10 +985,17 @@ static int cpsw_open(struct eth_device *edev) /* submit rx descs */ for (i = 0; i < PKTBUFSRX - 2; i++) { - ret = cpdma_submit(priv, &priv->rx_chan, NetRxPackets[i], - PKTSIZE); + void *buffer = priv->rx_buffer[i]; + unsigned len = PKTSIZE; + dma_addr_t dma; + + dma = dma_map_single(priv->dev, buffer, len, DMA_FROM_DEVICE); + if (dma_mapping_error(priv->dev, dma)) + return -EFAULT; + + ret = cpdma_submit(priv, &priv->rx_chan, buffer, dma, len, 0); if (ret < 0) { - dev_err(&slave->dev, "error %d submitting rx desc\n", ret); + dev_err(dev, "error %d submitting rx desc\n", ret); break; } } @@ -904,19 +1024,21 @@ static int cpsw_send(struct eth_device *edev, void *packet, int length) { struct cpsw_slave *slave = edev->priv; struct cpsw_priv *priv = slave->cpsw; - void *buffer; - int ret, len; + dma_addr_t dma; + int ret; dev_dbg(&slave->dev, "* %s slave %d\n", __func__, slave->slave_num); /* first reap completed packets */ - while (cpdma_process(priv, &priv->tx_chan, &buffer, &len) >= 0); + while (cpdma_process(slave, &priv->tx_chan, NULL, NULL, NULL) >= 0) + ; dev_dbg(&slave->dev, "%s: %i bytes @ 0x%p\n", __func__, length, packet); - dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE); - ret = cpdma_submit(priv, &priv->tx_chan, packet, length); - dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE); + dma = dma_map_single(priv->dev, packet, length, DMA_TO_DEVICE); + ret = cpdma_submit(priv, &priv->tx_chan, packet, dma, + length, BIT(slave->slave_num)); + dma_unmap_single(priv->dev, dma, length, DMA_TO_DEVICE); return ret; } @@ -925,16 +1047,15 @@ static int cpsw_recv(struct eth_device *edev) { struct cpsw_slave *slave = edev->priv; struct cpsw_priv *priv = slave->cpsw; + dma_addr_t dma; void *buffer; int len; - while (cpdma_process(priv, &priv->rx_chan, &buffer, &len) >= 0) { - dma_sync_single_for_cpu((unsigned long)buffer, len, - DMA_FROM_DEVICE); + while (cpdma_process(slave, &priv->rx_chan, &buffer, &dma, &len) >= 0) { + dma_sync_single_for_cpu(priv->dev, dma, len, DMA_FROM_DEVICE); net_receive(edev, buffer, len); - dma_sync_single_for_device((unsigned long)buffer, len, - DMA_FROM_DEVICE); - cpdma_submit(priv, &priv->rx_chan, buffer, PKTSIZE); + dma_sync_single_for_device(priv->dev, dma, len, DMA_FROM_DEVICE); + cpdma_submit(priv, &priv->rx_chan, buffer, dma, PKTSIZE, 0); } return 0; @@ -954,7 +1075,7 @@ static int cpsw_slave_setup(struct cpsw_slave *slave, int slave_num, { void *regs = priv->regs; struct eth_device *edev = &slave->edev; - struct device_d *dev = &slave->dev; + struct device *dev = &slave->dev; int ret; edev->parent = dev; @@ -1015,7 +1136,7 @@ static struct cpsw_data cpsw1_data = { .cpdma_reg_ofs = 0x100, .state_ram_ofs = 0x200, .ale_reg_ofs = 0x600, - .slave_ofs = 0x050, + .slave_ofs = 0x058, .slave_size = 0x040, .sliver_ofs = 0x700, /* FIXME: mdio_reg_ofs and cppi_ram_ofs missing */ @@ -1026,7 +1147,7 @@ static struct cpsw_data cpsw2_data = { .cpdma_reg_ofs = 0x800, .state_ram_ofs = 0xa00, .ale_reg_ofs = 0xd00, - .slave_ofs = 0x200, + .slave_ofs = 0x208, .slave_size = 0x100, .sliver_ofs = 0xd80, .mdio_reg_ofs = 0x1000, @@ -1110,11 +1231,27 @@ static void cpsw_gmii_sel_am335x(struct cpsw_slave *slave) writel(reg, phy_sel_addr); } -static int cpsw_probe_dt(struct cpsw_priv *priv) +static void cpsw_add_slave(struct cpsw_slave *slave, struct device_node *child, int i) { - struct device_d *dev = priv->dev; - struct device_node *np = dev->device_node, *child; - struct device_node *physel; + uint32_t phy_id[2] = {-1, -1}; + int ret; + + if (!of_find_node_by_name_address(child, "fixed-link")) { + ret = of_property_read_u32_array(child, "phy_id", phy_id, 2); + if (!ret) + dev_warn(slave->cpsw->dev, "phy_id is deprecated, use phy-handle\n"); + } + + slave->dev.of_node = child; + slave->phy_id = phy_id[1]; + slave->phy_if = of_get_phy_mode(child); + slave->slave_num = i; +} + +static int cpsw_legacy_probe_dt(struct cpsw_priv *priv) +{ + struct device *dev = priv->dev; + struct device_node *np = dev->of_node, *child; int ret, i = 0; ret = of_property_read_u32(np, "slaves", &priv->num_slaves); @@ -1123,15 +1260,6 @@ static int cpsw_probe_dt(struct cpsw_priv *priv) priv->slaves = xzalloc(sizeof(struct cpsw_slave) * priv->num_slaves); - physel = of_find_compatible_node(NULL, NULL, "ti,am3352-phy-gmii-sel"); - if (!physel) { - dev_err(dev, "Cannot find ti,am3352-phy-gmii-sel node\n"); - return -EINVAL; - } - ret = cpsw_phy_sel_init(priv, physel); - if (ret) - return ret; - for_each_child_of_node(np, child) { if (of_device_is_compatible(child, "ti,davinci_mdio")) { ret = of_pinctrl_select_state_default(child); @@ -1140,34 +1268,78 @@ static int cpsw_probe_dt(struct cpsw_priv *priv) } if (i < priv->num_slaves && !strncmp(child->name, "slave", 5)) { - struct cpsw_slave *slave = &priv->slaves[i]; - uint32_t phy_id[2] = {-1, -1}; + cpsw_add_slave(&priv->slaves[i], child, i); + i++; + } + } - if (!of_find_node_by_name(child, "fixed-link")) { - ret = of_property_read_u32_array(child, "phy_id", phy_id, 2); - if (!ret) - dev_warn(dev, "phy_id is deprecated, use phy-handle\n"); - } + return 0; +} - slave->dev.device_node = child; - slave->phy_id = phy_id[1]; - slave->phy_if = of_get_phy_mode(child); - slave->slave_num = i; +static int cpsw_switch_probe_dt(struct cpsw_priv *priv) +{ + struct device *dev = priv->dev; + struct device_node *np = dev->of_node, *child; + struct device_node *ports = NULL; + int ret, i = 0; - i++; + for_each_child_of_node(np, child) { + if (of_device_is_compatible(child, "ti,davinci_mdio")) { + ret = of_pinctrl_select_state_default(child); + if (ret) + return ret; + } + + if (!strcmp(child->name, "ethernet-ports")) { + ports = child; + priv->num_slaves = of_get_available_child_count(ports); } } - for (i = 0; i < priv->num_slaves; i++) { - struct cpsw_slave *slave = &priv->slaves[i]; + if (!ports) + return -EINVAL; - cpsw_gmii_sel_am335x(slave); + priv->slaves = xzalloc(sizeof(struct cpsw_slave) * priv->num_slaves); + + for_each_available_child_of_node(ports, child) { + cpsw_add_slave(&priv->slaves[i], child, i); + i++; } return 0; } -static int cpsw_probe(struct device_d *dev) +static int cpsw_probe_dt(struct cpsw_priv *priv) +{ + struct device *dev = priv->dev; + struct device_node *physel; + int (*probe_slaves_dt)(struct cpsw_priv *priv); + int ret, i = 0; + + physel = of_find_compatible_node(NULL, NULL, "ti,am3352-phy-gmii-sel"); + if (!physel) { + dev_err(dev, "Cannot find ti,am3352-phy-gmii-sel node\n"); + return -EINVAL; + } + ret = cpsw_phy_sel_init(priv, physel); + if (ret) + return ret; + + probe_slaves_dt = device_get_match_data(dev); + if (!probe_slaves_dt) + return -EINVAL; + + ret = probe_slaves_dt(priv); + if (ret < 0) + return ret; + + for (i = 0; i < priv->num_slaves; i++) + cpsw_gmii_sel_am335x(&priv->slaves[i]); + + return 0; +} + +static int cpsw_probe(struct device *dev) { struct resource *iores; struct cpsw_platform_data *data = (struct cpsw_platform_data *)dev->platform_data; @@ -1178,19 +1350,23 @@ static int cpsw_probe(struct device_d *dev) dev_dbg(dev, "* %s\n", __func__); - ret = of_platform_populate(dev->device_node, NULL, dev); - if (ret) - return ret; - iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); regs = IOMEM(iores->start); + ret = of_platform_populate(dev->of_node, NULL, dev); + if (ret) + return ret; + priv = xzalloc(sizeof(*priv)); priv->dev = dev; - if (dev->device_node) { + ret = net_alloc_packets(priv->rx_buffer, ARRAY_SIZE(priv->rx_buffer)); + if (ret) + goto out; + + if (dev->of_node) { ret = cpsw_probe_dt(priv); if (ret) goto out; @@ -1241,6 +1417,8 @@ static int cpsw_probe(struct device_d *dev) dev->priv = priv; + cpsw_setup(dev); + return 0; out: free(priv->slaves); @@ -1249,7 +1427,7 @@ out: return ret; } -static void cpsw_remove(struct device_d *dev) +static void cpsw_remove(struct device *dev) { struct cpsw_priv *priv = dev->priv; int i; @@ -1265,13 +1443,16 @@ static void cpsw_remove(struct device_d *dev) static __maybe_unused struct of_device_id cpsw_dt_ids[] = { { - .compatible = "ti,cpsw", + .compatible = "ti,cpsw", .data = cpsw_legacy_probe_dt + }, { + .compatible = "ti,cpsw-switch", .data = cpsw_switch_probe_dt }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, cpsw_dt_ids); -static struct driver_d cpsw_driver = { +static struct driver cpsw_driver = { .name = "cpsw", .probe = cpsw_probe, .remove = cpsw_remove, diff --git a/drivers/net/cs8900.c b/drivers/net/cs8900.c index 75bbbd79e1..0dfd3f1303 100644 --- a/drivers/net/cs8900.c +++ b/drivers/net/cs8900.c @@ -180,6 +180,7 @@ struct cs8900_priv { void *regs; struct cs89x0_product *product; struct cs89x0_chip *chip; + void *rx_buf; }; /* Read a 16-bit value from PacketPage Memory using I/O Space operation */ @@ -294,13 +295,13 @@ static int cs8900_recv(struct eth_device *dev) status = readw(priv->regs + CS8900_RTDATA0); len = readw(priv->regs + CS8900_RTDATA0); - for (addr = (u16 *) NetRxPackets[0], i = len >> 1; i > 0; i--) { + for (addr = (u16 *)priv->rx_buf, i = len >> 1; i > 0; i--) { *addr++ = readw(priv->regs + CS8900_RTDATA0); } if (len & 1) { *addr++ = readw(priv->regs + CS8900_RTDATA0); } - net_receive(dev, NetRxPackets[0], len); + net_receive(dev, priv->rx_buf, len); return len; } @@ -349,7 +350,7 @@ static const char *yesno_str(int v) return v ? "yes" : "no"; } -static void cs8900_info(struct device_d *dev) +static void cs8900_info(struct device *dev) { struct eth_device *edev = dev_to_edev(dev); struct cs8900_priv *priv = (struct cs8900_priv *)edev->priv; @@ -424,7 +425,7 @@ static int cs8900_check_id(struct cs8900_priv *priv) return result; } -static int cs8900_probe(struct device_d *dev) +static int cs8900_probe(struct device *dev) { struct resource *iores; struct eth_device *edev; @@ -442,6 +443,8 @@ static int cs8900_probe(struct device_d *dev) return -1; } + priv->rx_buf = xmalloc(PKTSIZE); + edev = (struct eth_device *)xmalloc(sizeof(struct eth_device)); edev->priv = priv; @@ -460,7 +463,7 @@ static int cs8900_probe(struct device_d *dev) return 0; } -static struct driver_d cs8900_driver = { +static struct driver cs8900_driver = { .name = "cs8900", .probe = cs8900_probe, }; diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index ff35b746e2..04277e6924 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -31,12 +31,12 @@ #include <init.h> #include <asm/system.h> #include <linux/phy.h> -#include <mach/emac_defs.h> +#include <mach/omap/emac_defs.h> #include <of_net.h> #include "davinci_emac.h" struct davinci_emac_priv { - struct device_d *dev; + struct device *dev; struct eth_device edev; /* EMAC Addresses */ @@ -83,7 +83,7 @@ static inline void __iomem *HW_TO_BD(uint32_t x) #endif struct davinci_mdio_priv { - struct device_d *dev; + struct device *dev; struct mii_bus miibus; void __iomem *adap_mdio; /* = EMAC_MDIO_BASE_ADDR */ }; @@ -430,7 +430,7 @@ static int davinci_emac_send(struct eth_device *edev, void *packet, int length) EMAC_CPPI_OWNERSHIP_BIT | EMAC_CPPI_EOP_BIT), priv->emac_tx_desc + EMAC_DESC_PKT_FLAG_LEN); - dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE); + dma_sync_single_for_device(priv->dev, (unsigned long)packet, length, DMA_TO_DEVICE); /* Send the packet */ writel(BD_TO_HW(priv->emac_tx_desc), priv->adap_emac + EMAC_TX0HDP); @@ -448,7 +448,7 @@ static int davinci_emac_send(struct eth_device *edev, void *packet, int length) break; } } - dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE); + dma_sync_single_for_cpu(priv->dev, (unsigned long)packet, length, DMA_TO_DEVICE); dev_dbg(priv->dev, "- emac_send (ret_status %i)\n", ret_status); return ret_status; @@ -480,9 +480,9 @@ static int davinci_emac_recv(struct eth_device *edev) pkt = (unsigned char *)readl(rx_curr_desc + EMAC_DESC_BUFFER); len = readl(rx_curr_desc + EMAC_DESC_BUFF_OFF_LEN) & 0xffff; dev_dbg(priv->dev, "| emac_recv got packet (length %i)\n", len); - dma_sync_single_for_cpu((unsigned long)pkt, len, DMA_FROM_DEVICE); + dma_sync_single_for_cpu(priv->dev, (unsigned long)pkt, len, DMA_FROM_DEVICE); net_receive(edev, pkt, len); - dma_sync_single_for_device((unsigned long)pkt, len, DMA_FROM_DEVICE); + dma_sync_single_for_device(priv->dev, (unsigned long)pkt, len, DMA_FROM_DEVICE); ret = len; } @@ -533,13 +533,13 @@ out: return ret; } -static int davinci_emac_probe(struct device_d *dev) +static int davinci_emac_probe(struct device *dev) { struct resource *iores; struct davinci_emac_priv *priv; uint32_t ctrl_reg_offset; uint32_t ctrl_ram_offset; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; dev_dbg(dev, "+ emac_probe\n"); @@ -588,7 +588,7 @@ static int davinci_emac_probe(struct device_d *dev) return 0; } -static void davinci_emac_remove(struct device_d *dev) +static void davinci_emac_remove(struct device *dev) { struct davinci_emac_priv *priv = dev->priv; @@ -602,8 +602,9 @@ static __maybe_unused struct of_device_id davinci_emac_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, davinci_emac_dt_ids); -static struct driver_d davinci_emac_driver = { +static struct driver davinci_emac_driver = { .name = "davinci_emac", .probe = davinci_emac_probe, .remove = davinci_emac_remove, @@ -611,7 +612,7 @@ static struct driver_d davinci_emac_driver = { }; device_platform_driver(davinci_emac_driver); -static int davinci_mdio_probe(struct device_d *dev) +static int davinci_mdio_probe(struct device *dev) { struct resource *iores; struct davinci_mdio_priv *priv; @@ -658,8 +659,9 @@ static __maybe_unused struct of_device_id davinci_mdio_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, davinci_mdio_dt_ids); -static struct driver_d davinci_mdio_driver = { +static struct driver davinci_mdio_driver = { .name = "davinci_mdio", .probe = davinci_mdio_probe, .of_compatible = DRV_OF_COMPAT(davinci_mdio_dt_ids), diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 0ee6d3d78a..6936c844cd 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -104,15 +104,15 @@ static void tx_descs_init(struct eth_device *dev) { struct dw_eth_dev *priv = dev->priv; struct eth_dma_regs *dma_p = priv->dma_regs_p; - struct dmamacdescr *desc_table_p = &priv->tx_mac_descrtable[0]; + struct dmamacdescr *desc_table_p = &priv->tx_mac_descrtable_cpu[0]; char *txbuffs = &priv->txbuffs[0]; struct dmamacdescr *desc_p; u32 idx; for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) { desc_p = &desc_table_p[idx]; - desc_p->dmamac_addr = &txbuffs[idx * CONFIG_ETH_BUFSIZE]; - desc_p->dmamac_next = &desc_table_p[idx + 1]; + desc_p->dmamac_addr = virt_to_phys(&txbuffs[idx * CONFIG_ETH_BUFSIZE]); + desc_p->dmamac_next = tx_dma_addr(priv, &desc_table_p[idx + 1]); if (priv->enh_desc) { desc_p->txrx_status &= ~(DESC_ENH_TXSTS_TXINT | DESC_ENH_TXSTS_TXLAST | @@ -130,9 +130,9 @@ static void tx_descs_init(struct eth_device *dev) } /* Correcting the last pointer of the chain */ - desc_p->dmamac_next = &desc_table_p[0]; + desc_p->dmamac_next = tx_dma_addr(priv, &desc_table_p[0]); - writel((ulong)&desc_table_p[0], &dma_p->txdesclistaddr); + writel(desc_p->dmamac_next, &dma_p->txdesclistaddr); priv->tx_currdescnum = 0; } @@ -140,15 +140,15 @@ static void rx_descs_init(struct eth_device *dev) { struct dw_eth_dev *priv = dev->priv; struct eth_dma_regs *dma_p = priv->dma_regs_p; - struct dmamacdescr *desc_table_p = &priv->rx_mac_descrtable[0]; + struct dmamacdescr *desc_table_p = &priv->rx_mac_descrtable_cpu[0]; char *rxbuffs = &priv->rxbuffs[0]; struct dmamacdescr *desc_p; u32 idx; for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) { desc_p = &desc_table_p[idx]; - desc_p->dmamac_addr = &rxbuffs[idx * CONFIG_ETH_BUFSIZE]; - desc_p->dmamac_next = &desc_table_p[idx + 1]; + desc_p->dmamac_addr = virt_to_phys(&rxbuffs[idx * CONFIG_ETH_BUFSIZE]); + desc_p->dmamac_next = rx_dma_addr(priv, &desc_table_p[idx + 1]); desc_p->dmamac_cntl = MAC_MAX_FRAME_SZ; if (priv->enh_desc) @@ -156,15 +156,15 @@ static void rx_descs_init(struct eth_device *dev) else desc_p->dmamac_cntl |= DESC_RXCTRL_RXCHAIN; - dma_sync_single_for_cpu((unsigned long)desc_p->dmamac_addr, + dma_sync_single_for_cpu(dev->parent, desc_p->dmamac_addr, CONFIG_ETH_BUFSIZE, DMA_FROM_DEVICE); desc_p->txrx_status = DESC_RXSTS_OWNBYDMA; } /* Correcting the last pointer of the chain */ - desc_p->dmamac_next = &desc_table_p[0]; + desc_p->dmamac_next = rx_dma_addr(priv, &desc_table_p[0]); - writel((ulong)&desc_table_p[0], &dma_p->rxdesclistaddr); + writel(desc_p->dmamac_next, &dma_p->rxdesclistaddr); priv->rx_currdescnum = 0; } @@ -276,7 +276,7 @@ static int dwc_ether_send(struct eth_device *dev, void *packet, int length) struct dw_eth_dev *priv = dev->priv; struct eth_dma_regs *dma_p = priv->dma_regs_p; u32 owndma, desc_num = priv->tx_currdescnum; - struct dmamacdescr *desc_p = &priv->tx_mac_descrtable[desc_num]; + struct dmamacdescr *desc_p = &priv->tx_mac_descrtable_cpu[desc_num]; owndma = priv->enh_desc ? DESC_ENH_TXSTS_OWNBYDMA : DESC_TXSTS_OWNBYDMA; /* Check if the descriptor is owned by CPU */ @@ -285,8 +285,8 @@ static int dwc_ether_send(struct eth_device *dev, void *packet, int length) return -1; } - memcpy((void *)desc_p->dmamac_addr, packet, length); - dma_sync_single_for_device((unsigned long)desc_p->dmamac_addr, length, + memcpy(dmamac_addr(desc_p), packet, length); + dma_sync_single_for_device(dev->parent, desc_p->dmamac_addr, length, DMA_TO_DEVICE); if (priv->enh_desc) { @@ -314,7 +314,7 @@ static int dwc_ether_send(struct eth_device *dev, void *packet, int length) /* Start the transmission */ writel(POLL_DATA, &dma_p->txpolldemand); - dma_sync_single_for_cpu((unsigned long)desc_p->dmamac_addr, length, + dma_sync_single_for_cpu(dev->parent, desc_p->dmamac_addr, length, DMA_TO_DEVICE); return 0; @@ -324,7 +324,7 @@ static int dwc_ether_rx(struct eth_device *dev) { struct dw_eth_dev *priv = dev->priv; u32 desc_num = priv->rx_currdescnum; - struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num]; + struct dmamacdescr *desc_p = &priv->rx_mac_descrtable_cpu[desc_num]; u32 status = desc_p->txrx_status; int length = 0; @@ -358,10 +358,10 @@ static int dwc_ether_rx(struct eth_device *dev) length = (status & DESC_RXSTS_FRMLENMSK) >> DESC_RXSTS_FRMLENSHFT; - dma_sync_single_for_cpu((unsigned long)desc_p->dmamac_addr, + dma_sync_single_for_cpu(dev->parent, desc_p->dmamac_addr, length, DMA_FROM_DEVICE); - net_receive(dev, desc_p->dmamac_addr, length); - dma_sync_single_for_device((unsigned long)desc_p->dmamac_addr, + net_receive(dev, dmamac_addr(desc_p), length); + dma_sync_single_for_device(dev->parent, desc_p->dmamac_addr, length, DMA_FROM_DEVICE); ret = length; } @@ -410,7 +410,7 @@ static int dwc_ether_set_ethaddr(struct eth_device *dev, const unsigned char *ad return 0; } -static void dwc_version(struct device_d *dev, u32 hwid) +static void dwc_version(struct device *dev, u32 hwid) { u32 uid = ((hwid & 0x0000ff00) >> 8); u32 synid = (hwid & 0x000000ff); @@ -419,7 +419,7 @@ static void dwc_version(struct device_d *dev, u32 hwid) uid, synid); } -static int dwc_probe_dt(struct device_d *dev, struct dw_eth_dev *priv) +static int dwc_probe_dt(struct device *dev, struct dw_eth_dev *priv) { struct device_node *child; @@ -427,12 +427,12 @@ static int dwc_probe_dt(struct device_d *dev, struct dw_eth_dev *priv) return -ENODEV; priv->phy_addr = -1; - priv->interface = of_get_phy_mode(dev->device_node); + priv->interface = of_get_phy_mode(dev->of_node); /* Set MDIO bus device node, if present. */ - for_each_child_of_node(dev->device_node, child) { + for_each_child_of_node(dev->of_node, child) { if (of_device_is_compatible(child, "snps,dwmac-mdio")) { - priv->miibus.dev.device_node = child; + priv->miibus.dev.of_node = child; break; } } @@ -440,7 +440,7 @@ static int dwc_probe_dt(struct device_d *dev, struct dw_eth_dev *priv) return 0; } -struct dw_eth_dev *dwc_drv_probe(struct device_d *dev) +struct dw_eth_dev *dwc_drv_probe(struct device *dev) { struct resource *iores; struct dw_eth_dev *priv; @@ -451,17 +451,20 @@ struct dw_eth_dev *dwc_drv_probe(struct device_d *dev) int ret; struct dw_eth_drvdata *drvdata; + dma_set_mask(dev, DMA_BIT_MASK(32)); + priv = xzalloc(sizeof(struct dw_eth_dev)); ret = dev_get_drvdata(dev, (const void **)&drvdata); if (ret) return ERR_PTR(ret); - if (drvdata && drvdata->enh_desc) + if (drvdata) { priv->enh_desc = drvdata->enh_desc; - else + priv->fix_mac_speed = drvdata->fix_mac_speed; + } else { dev_warn(dev, "No drvdata specified\n"); - + } if (pdata) { priv->phy_addr = pdata->phy_addr; @@ -481,12 +484,21 @@ struct dw_eth_dev *dwc_drv_probe(struct device_d *dev) priv->mac_regs_p = base; dwc_version(dev, readl(&priv->mac_regs_p->version)); priv->dma_regs_p = base + DW_DMA_BASE_OFFSET; - priv->tx_mac_descrtable = dma_alloc_coherent( + + priv->tx_mac_descrtable_cpu = dma_alloc_coherent( CONFIG_TX_DESCR_NUM * sizeof(struct dmamacdescr), - DMA_ADDRESS_BROKEN); - priv->rx_mac_descrtable = dma_alloc_coherent( + &priv->tx_mac_descrtable_dev); + + if (dma_mapping_error(dev, priv->tx_mac_descrtable_dev)) + return ERR_PTR(-EFAULT); + + priv->rx_mac_descrtable_cpu = dma_alloc_coherent( CONFIG_RX_DESCR_NUM * sizeof(struct dmamacdescr), - DMA_ADDRESS_BROKEN); + &priv->rx_mac_descrtable_dev); + + if (dma_mapping_error(dev, priv->rx_mac_descrtable_dev)) + return ERR_PTR(-EFAULT); + priv->txbuffs = dma_alloc(TX_TOTAL_BUFSIZE); priv->rxbuffs = dma_alloc(RX_TOTAL_BUFSIZE); @@ -514,7 +526,7 @@ struct dw_eth_dev *dwc_drv_probe(struct device_d *dev) return priv; } -void dwc_drv_remove(struct device_d *dev) +void dwc_drv_remove(struct device *dev) { struct eth_device *edev = dev->priv; diff --git a/drivers/net/designware.h b/drivers/net/designware.h index 0a6a6bf1a4..5b587fa59e 100644 --- a/drivers/net/designware.h +++ b/drivers/net/designware.h @@ -8,6 +8,7 @@ #define __DESIGNWARE_ETH_H #include <net.h> +#include <linux/types.h> struct dw_eth_dev { struct eth_device netdev; @@ -18,8 +19,11 @@ struct dw_eth_dev { u32 tx_currdescnum; u32 rx_currdescnum; - struct dmamacdescr *tx_mac_descrtable; - struct dmamacdescr *rx_mac_descrtable; + struct dmamacdescr *tx_mac_descrtable_cpu; + struct dmamacdescr *rx_mac_descrtable_cpu; + + dma_addr_t tx_mac_descrtable_dev; + dma_addr_t rx_mac_descrtable_dev; u8 *txbuffs; u8 *rxbuffs; @@ -35,11 +39,26 @@ struct dw_eth_dev { struct dw_eth_drvdata { bool enh_desc; + void (*fix_mac_speed)(int speed); void *priv; }; -struct dw_eth_dev *dwc_drv_probe(struct device_d *dev); -void dwc_drv_remove(struct device_d *dev); +static inline dma_addr_t tx_dma_addr(struct dw_eth_dev *priv, + struct dmamacdescr *desc) +{ + return priv->tx_mac_descrtable_dev + + ((u8 *)desc - (u8 *)priv->tx_mac_descrtable_cpu); +} + +static inline dma_addr_t rx_dma_addr(struct dw_eth_dev *priv, + struct dmamacdescr *desc) +{ + return priv->rx_mac_descrtable_dev + + ((u8 *)desc - (u8 *)priv->rx_mac_descrtable_cpu); +} + +struct dw_eth_dev *dwc_drv_probe(struct device *dev); +void dwc_drv_remove(struct device *dev); #define CONFIG_TX_DESCR_NUM 16 #define CONFIG_RX_DESCR_NUM 16 @@ -138,10 +157,12 @@ struct eth_dma_regs { struct dmamacdescr { u32 txrx_status; u32 dmamac_cntl; - void *dmamac_addr; - struct dmamacdescr *dmamac_next; + u32 dmamac_addr; + u32 dmamac_next; }; +#define dmamac_addr(descr) (phys_to_virt((descr)->dmamac_addr)) + /* * txrx_status definitions */ diff --git a/drivers/net/designware_eqos.c b/drivers/net/designware_eqos.c index cb52f3942d..ccce51b6af 100644 --- a/drivers/net/designware_eqos.c +++ b/drivers/net/designware_eqos.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016, NVIDIA CORPORATION. * Copyright (c) 2019, Ahmad Fatoum, Pengutronix @@ -8,9 +8,12 @@ #include <common.h> #include <init.h> +#include <gpio.h> +#include <linux/gpio/consumer.h> #include <dma.h> #include <net.h> #include <of_net.h> +#include <of_gpio.h> #include <linux/iopoll.h> #include <linux/time.h> #include <linux/sizes.h> @@ -23,7 +26,8 @@ struct eqos_mac_regs { u32 config; /* 0x000 */ u32 ext_config; /* 0x004 */ - u32 unused_004[(0x070 - 0x008) / 4]; /* 0x008 */ + u32 packet_filter; /* 0x008 */ + u32 unused_004[(0x070 - 0x00C) / 4]; /* 0x00C */ u32 q0_tx_flow_ctrl; /* 0x070 */ u32 unused_070[(0x090 - 0x074) / 4]; /* 0x074 */ u32 rx_flow_ctrl; /* 0x090 */ @@ -59,6 +63,9 @@ struct eqos_mac_regs { #define EQOS_MAC_CONFIGURATION_TE BIT(1) #define EQOS_MAC_CONFIGURATION_RE BIT(0) +#define EQOS_MAC_PACKET_FILTER_PR BIT(0) /* Promiscuous mode */ +#define EQOS_MAC_PACKET_FILTER_PCF BIT(7) /* Pass Control Frames */ + #define EQOS_MAC_Q0_TX_FLOW_CTRL_PT_SHIFT 16 #define EQOS_MAC_Q0_TX_FLOW_CTRL_PT_MASK 0xffff #define EQOS_MAC_Q0_TX_FLOW_CTRL_TFE BIT(1) @@ -109,6 +116,8 @@ struct eqos_mtl_regs { #define EQOS_MTL_RXQ0_OPERATION_MODE_RFA_MASK 0x3f #define EQOS_MTL_RXQ0_OPERATION_MODE_EHFC BIT(7) #define EQOS_MTL_RXQ0_OPERATION_MODE_RSF BIT(5) +#define EQOS_MTL_RXQ0_OPERATION_MODE_FEP BIT(4) +#define EQOS_MTL_RXQ0_OPERATION_MODE_FUP BIT(3) #define EQOS_MTL_RXQ0_DEBUG_PRXQ_SHIFT 16 #define EQOS_MTL_RXQ0_DEBUG_PRXQ_MASK 0x7fff @@ -163,8 +172,6 @@ struct eqos_dma_regs { #define EQOS_DESCRIPTOR_SIZE (EQOS_DESCRIPTOR_WORDS * 4) /* We assume ARCH_DMA_MINALIGN >= 16; 16 is the EQOS HW minimum */ #define EQOS_DESCRIPTOR_ALIGN 64 -#define EQOS_DESCRIPTORS_TX 4 -#define EQOS_DESCRIPTORS_RX 4 #define EQOS_DESCRIPTORS_NUM (EQOS_DESCRIPTORS_TX + EQOS_DESCRIPTORS_RX) #define EQOS_DESCRIPTORS_SIZE ALIGN(EQOS_DESCRIPTORS_NUM * \ EQOS_DESCRIPTOR_SIZE, EQOS_DESCRIPTOR_ALIGN) @@ -189,6 +196,27 @@ struct eqos_desc { #define MII_BUSY (1 << 0) +static int eqos_phy_reset(struct device *dev, struct eqos *eqos) +{ + struct gpio_desc *phy_reset; + u32 delays[3] = { 0, 0, 0 }; + + phy_reset = gpiod_get_optional(dev, "snps,reset", GPIOF_OUT_INIT_ACTIVE); + if (IS_ERR(phy_reset)) { + dev_warn(dev, "Failed to get 'snps,reset' GPIO (ignored)\n"); + } else if (phy_reset) { + of_property_read_u32_array(dev->of_node, + "snps,reset-delays-us", + delays, ARRAY_SIZE(delays)); + + udelay(delays[1]); + gpiod_set_value(phy_reset, false); + udelay(delays[2]); + } + + return 0; +} + static int eqos_mdio_wait_idle(struct eqos *eqos) { u32 idle; @@ -321,6 +349,14 @@ int eqos_set_ethaddr(struct eth_device *edev, const unsigned char *mac) memcpy(eqos->macaddr, mac, ETH_ALEN); + if (!eqos->is_started) + return 0; + + /* mac_hi is only partially overwritten by the following code. Part of + * this variable is DCS (DMA Channel Select). If this variable is not + * zeroed, we may get some random DMA RX channel. + */ + mac_hi = 0; /* Update the MAC address */ memcpy(&mac_hi, &mac[4], 2); memcpy(&mac_lo, &mac[0], 4); @@ -331,6 +367,26 @@ int eqos_set_ethaddr(struct eth_device *edev, const unsigned char *mac) return 0; } +static int eqos_set_promisc(struct eth_device *edev, bool enable) +{ + struct eqos *eqos = edev->priv; + u32 mask; + + eqos->promisc_enabled = enable; + + if (!eqos->is_started) + return 0; + + mask = EQOS_MAC_PACKET_FILTER_PR; + + if (enable) + setbits_le32(&eqos->mac_regs->packet_filter, mask); + else + clrbits_le32(&eqos->mac_regs->packet_filter, mask); + + return 0; +} + /* Get PHY out of power saving mode. If this is needed elsewhere then * consider making it part of phy-core and adding a resume method to * the phy device ops. */ @@ -338,6 +394,10 @@ static int phy_resume(struct phy_device *phydev) { int bmcr; + // Bus will be NULL if a fixed-link is used. + if (!phydev->bus) + return 0; + bmcr = phy_read(phydev, MII_BMCR); if (bmcr < 0) return bmcr; @@ -354,12 +414,21 @@ static int eqos_start(struct eth_device *edev) { struct eqos *eqos = edev->priv; u32 val, tx_fifo_sz, rx_fifo_sz, tqs, rqs, pbl; - unsigned long last_rx_desc; + unsigned long last_rx_rf_desc; unsigned long rate; u32 mode_set; int ret; int i; + ret = phy_device_connect(edev, &eqos->miibus, eqos->phy_addr, + eqos->ops->adjust_link, 0, eqos->interface); + if (ret) + return ret; + + /* In some cases where PHY or DSA switch is the clock provider for + * EQOS, we need to probe and configure them before issuing software + * reset here. + */ setbits_le32(&eqos->dma_regs->mode, EQOS_DMA_MODE_SWR); ret = readl_poll_timeout(&eqos->dma_regs->mode, mode_set, @@ -370,8 +439,10 @@ static int eqos_start(struct eth_device *edev) return ret; } - /* Reset above clears MAC address */ + /* Reset above clears any previously made configuration */ + eqos->is_started = true; eqos_set_ethaddr(edev, eqos->macaddr); + eqos_set_promisc(edev, eqos->promisc_enabled); /* Required for accurate time keeping with EEE counters */ rate = eqos->ops->get_csr_clk_rate(eqos); @@ -379,17 +450,14 @@ static int eqos_start(struct eth_device *edev) val = (rate / USEC_PER_SEC) - 1; /* -1 because the data sheet says so */ writel(val, &eqos->mac_regs->us_tic_counter); - ret = phy_device_connect(edev, &eqos->miibus, eqos->phy_addr, - eqos->ops->adjust_link, 0, eqos->interface); - if (ret) - return ret; - /* Before we reset the mac, we must insure the PHY is not powered down * as the dw controller needs all clock domains to be running, including * the PHY clock, to come out of a mac reset. */ - ret = phy_resume(edev->phydev); - if (ret) - return ret; + if (edev->phydev) { + ret = phy_resume(edev->phydev); + if (ret) + return ret; + } /* Configure MTL */ @@ -405,7 +473,9 @@ static int eqos_start(struct eth_device *edev) /* Enable Store and Forward mode for RX, since no jumbo frame */ setbits_le32(&eqos->mtl_regs->rxq0_operation_mode, - EQOS_MTL_RXQ0_OPERATION_MODE_RSF); + EQOS_MTL_RXQ0_OPERATION_MODE_RSF | + EQOS_MTL_RXQ0_OPERATION_MODE_FEP | + EQOS_MTL_RXQ0_OPERATION_MODE_FUP); /* Transmit/Receive queue fifo size; use all RAM for 1 queue */ val = readl(&eqos->mac_regs->hw_feature1); @@ -554,9 +624,9 @@ static int eqos_start(struct eth_device *edev) eqos->tx_currdescnum = eqos->rx_currdescnum = 0; for (i = 0; i < EQOS_DESCRIPTORS_RX; i++) { - struct eqos_desc *rx_desc = &eqos->rx_descs[i]; + struct eqos_desc *rx_rf_desc = &eqos->rx_descs[i]; - writel(EQOS_DESC3_BUF1V | EQOS_DESC3_OWN, &rx_desc->des3); + writel(EQOS_DESC3_BUF1V | EQOS_DESC3_OWN, &rx_rf_desc->des3); } writel(0, &eqos->dma_regs->ch0_txdesc_list_haddress); @@ -586,12 +656,8 @@ static int eqos_start(struct eth_device *edev) * that's not distinguishable from none of the descriptors being * available. */ - last_rx_desc = (ulong)&eqos->rx_descs[(EQOS_DESCRIPTORS_RX - 1)]; - writel(last_rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer); - - barrier(); - - eqos->started = true; + last_rx_rf_desc = (ulong)&eqos->rx_descs[(EQOS_DESCRIPTORS_RX - 1)]; + writel(last_rx_rf_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer); return 0; } @@ -601,13 +667,6 @@ static void eqos_stop(struct eth_device *edev) struct eqos *eqos = edev->priv; int i; - if (!eqos->started) - return; - - eqos->started = false; - - barrier(); - /* Disable TX DMA */ clrbits_le32(&eqos->dma_regs->ch0_tx_control, EQOS_DMA_CH0_TX_CONTROL_ST); @@ -645,7 +704,6 @@ static void eqos_stop(struct eth_device *edev) static int eqos_send(struct eth_device *edev, void *packet, int length) { struct eqos *eqos = edev->priv; - struct device_d *dev = &eqos->netdev.dev; struct eqos_desc *tx_desc; dma_addr_t dma; u32 des3; @@ -655,8 +713,8 @@ static int eqos_send(struct eth_device *edev, void *packet, int length) eqos->tx_currdescnum++; eqos->tx_currdescnum %= EQOS_DESCRIPTORS_TX; - dma = dma_map_single(dev, packet, length, DMA_TO_DEVICE); - if (dma_mapping_error(dev, dma)) + dma = dma_map_single(edev->parent, packet, length, DMA_TO_DEVICE); + if (dma_mapping_error(edev->parent, dma)) return -EFAULT; tx_desc->des0 = (unsigned long)dma; @@ -675,7 +733,7 @@ static int eqos_send(struct eth_device *edev, void *packet, int length) !(des3 & EQOS_DESC3_OWN), 100 * USEC_PER_MSEC); - dma_unmap_single(dev, dma, length, DMA_TO_DEVICE); + dma_unmap_single(edev->parent, dma, length, DMA_TO_DEVICE); if (ret == -ETIMEDOUT) eqos_dbg(eqos, "TX timeout\n"); @@ -686,33 +744,51 @@ static int eqos_send(struct eth_device *edev, void *packet, int length) static int eqos_recv(struct eth_device *edev) { struct eqos *eqos = edev->priv; - struct eqos_desc *rx_desc; + struct eqos_desc *rx_wbf_desc, *rx_rf_desc; + dma_addr_t dma; void *frame; int length; - rx_desc = &eqos->rx_descs[eqos->rx_currdescnum]; - if (readl(&rx_desc->des3) & EQOS_DESC3_OWN) + /* We have two types of RX descriptors at some pointer: Read and + * Write-Back: + * All RX descriptors are prepared by the software and given to the + * DMA as "Normal" Descriptors with the content as shown in Receive + * Normal Descriptor (Read Format). The DMA reads this descriptor and + * after transferring a received packet (or part of) to the buffers + * indicated by the descriptor, the Rx DMA will close the descriptor + * with the corresponding packet status. The format of this status is + * given in the "Receive Normal Descriptor (Write-Back Format)" + */ + + /* Write-Back Format RX descriptor */ + rx_wbf_desc = &eqos->rx_descs[eqos->rx_currdescnum]; + if (readl(&rx_wbf_desc->des3) & EQOS_DESC3_OWN) return 0; - frame = phys_to_virt(rx_desc->des0); - length = rx_desc->des3 & 0x7fff; + dma = eqos->dma_rx_buf[eqos->rx_currdescnum]; + frame = phys_to_virt(dma); + length = rx_wbf_desc->des3 & 0x7fff; - dma_sync_single_for_cpu((unsigned long)frame, length, DMA_FROM_DEVICE); + dma_sync_single_for_cpu(edev->parent, (unsigned long)frame, + length, DMA_FROM_DEVICE); net_receive(edev, frame, length); - dma_sync_single_for_device((unsigned long)frame, length, DMA_FROM_DEVICE); - - rx_desc->des0 = (unsigned long)frame; - rx_desc->des1 = 0; - rx_desc->des2 = 0; + dma_sync_single_for_device(edev->parent, (unsigned long)frame, + length, DMA_FROM_DEVICE); + + /* Read Format RX descriptor */ + rx_rf_desc = &eqos->rx_descs[eqos->rx_currdescnum]; + rx_rf_desc->des0 = dma; + rx_rf_desc->des1 = 0; + rx_rf_desc->des2 = 0; /* * Make sure that if HW sees the _OWN write below, it will see all the * writes to the rest of the descriptor too. */ - rx_desc->des3 |= EQOS_DESC3_BUF1V; - rx_desc->des3 |= EQOS_DESC3_OWN; + rx_rf_desc->des3 |= EQOS_DESC3_BUF1V; + rx_rf_desc->des3 |= EQOS_DESC3_OWN; barrier(); - writel((ulong)rx_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer); + writel((ulong)rx_rf_desc, &eqos->dma_regs->ch0_rxdesc_tail_pointer); eqos->rx_currdescnum++; eqos->rx_currdescnum %= EQOS_DESCRIPTORS_RX; @@ -722,7 +798,7 @@ static int eqos_recv(struct eth_device *edev) static int eqos_init_resources(struct eqos *eqos) { - struct device_d *dev = eqos->netdev.parent; + struct eth_device *edev = &eqos->netdev; int ret = -ENOMEM; void *descs; void *p; @@ -740,16 +816,17 @@ static int eqos_init_resources(struct eqos *eqos) goto err_free_desc; for (i = 0; i < EQOS_DESCRIPTORS_RX; i++) { - struct eqos_desc *rx_desc = &eqos->rx_descs[i]; + struct eqos_desc *rx_rf_desc = &eqos->rx_descs[i]; dma_addr_t dma; - dma = dma_map_single(dev, p, EQOS_MAX_PACKET_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, dma)) { + dma = dma_map_single(edev->parent, p, EQOS_MAX_PACKET_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(edev->parent, dma)) { ret = -EFAULT; goto err_free_rx_bufs; } - rx_desc->des0 = dma; + rx_rf_desc->des0 = dma; + eqos->dma_rx_buf[i] = dma; p += EQOS_MAX_PACKET_SIZE; } @@ -765,7 +842,7 @@ err: return ret; } -static int eqos_init(struct device_d *dev, struct eqos *eqos) +static int eqos_init(struct device *dev, struct eqos *eqos) { int ret; @@ -781,25 +858,26 @@ static int eqos_init(struct device_d *dev, struct eqos *eqos) return ret; } -static void eqos_probe_dt(struct device_d *dev, struct eqos *eqos) +static void eqos_probe_dt(struct device *dev, struct eqos *eqos) { struct device_node *child; - eqos->interface = of_get_phy_mode(dev->device_node); + eqos->interface = of_get_phy_mode(dev->of_node); eqos->phy_addr = -1; /* Set MDIO bus device node, if present. */ - for_each_child_of_node(dev->device_node, child) { + for_each_child_of_node(dev->of_node, child) { if (of_device_is_compatible(child, "snps,dwmac-mdio") || (child->name && !of_node_cmp(child->name, "mdio"))) { - eqos->miibus.dev.device_node = child; + eqos->miibus.dev.of_node = child; break; } } } -int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv) +int eqos_probe(struct device *dev, const struct eqos_ops *ops, void *priv) { + struct device_node *np = dev->of_node; struct mii_bus *miibus; struct resource *iores; struct eqos *eqos; @@ -832,6 +910,7 @@ int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv) edev->halt = eqos_stop; edev->get_ethaddr = ops->get_ethaddr; edev->set_ethaddr = ops->set_ethaddr; + edev->set_promisc = eqos_set_promisc; miibus = &eqos->miibus; miibus->parent = edev->parent; @@ -839,10 +918,18 @@ int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv) miibus->write = eqos_mdio_write; miibus->priv = eqos; + miibus->dev.of_node = of_get_compatible_child(np, "snps,dwmac-mdio"); + if (!miibus->dev.of_node) + miibus->dev.of_node = of_get_child_by_name(np, "mdio"); + ret = eqos_init(dev, eqos); if (ret) return ret; + ret = eqos_phy_reset(dev, eqos); + if (ret) + return ret; + ret = mdiobus_register(miibus); if (ret) return ret; @@ -850,7 +937,7 @@ int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv) return eth_register(edev); } -void eqos_remove(struct device_d *dev) +void eqos_remove(struct device *dev) { struct eqos *eqos = dev->priv; diff --git a/drivers/net/designware_eqos.h b/drivers/net/designware_eqos.h index 30f4f02579..951565e8f9 100644 --- a/drivers/net/designware_eqos.h +++ b/drivers/net/designware_eqos.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2019 Ahmad Fatoum, Pengutronix */ @@ -10,7 +10,7 @@ struct eqos; struct eth_device; struct eqos_ops { - int (*init)(struct device_d *dev, struct eqos *priv); + int (*init)(struct device *dev, struct eqos *priv); int (*get_ethaddr)(struct eth_device *dev, unsigned char *mac); int (*set_ethaddr)(struct eth_device *edev, const unsigned char *mac); void (*adjust_link)(struct eth_device *edev); @@ -40,6 +40,9 @@ struct eqos_dma_regs; struct eqos_mac_regs; struct eqos_mtl_regs; +#define EQOS_DESCRIPTORS_TX 4 +#define EQOS_DESCRIPTORS_RX 64 + struct eqos { struct eth_device netdev; struct mii_bus miibus; @@ -49,6 +52,7 @@ struct eqos { u32 tx_currdescnum, rx_currdescnum; struct eqos_desc *tx_descs, *rx_descs; + dma_addr_t dma_rx_buf[EQOS_DESCRIPTORS_RX]; void __iomem *regs; struct eqos_mac_regs __iomem *mac_regs; @@ -60,13 +64,14 @@ struct eqos { const struct eqos_ops *ops; void *priv; - bool started; + + bool is_started; + bool promisc_enabled; }; -struct device_d; -int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv); -void eqos_remove(struct device_d *dev); -int eqos_reset(struct eqos *priv); +struct device; +int eqos_probe(struct device *dev, const struct eqos_ops *ops, void *priv); +void eqos_remove(struct device *dev); int eqos_get_ethaddr(struct eth_device *edev, unsigned char *mac); int eqos_set_ethaddr(struct eth_device *edev, const unsigned char *mac); diff --git a/drivers/net/designware_generic.c b/drivers/net/designware_generic.c index 809c7b7b69..fc9f0745f7 100644 --- a/drivers/net/designware_generic.c +++ b/drivers/net/designware_generic.c @@ -16,15 +16,12 @@ static struct dw_eth_drvdata dwmac_370a_drvdata = { .enh_desc = 1, }; -static int dwc_ether_probe(struct device_d *dev) +static int dwc_ether_probe(struct device *dev) { struct dw_eth_dev *dwc; dwc = dwc_drv_probe(dev); - if (IS_ERR(dwc)) - return PTR_ERR(dwc); - - return 0; + return PTR_ERR_OR_ZERO(dwc); } static __maybe_unused struct of_device_id dwc_ether_compatible[] = { @@ -38,8 +35,9 @@ static __maybe_unused struct of_device_id dwc_ether_compatible[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, dwc_ether_compatible); -static struct driver_d dwc_ether_driver = { +static struct driver dwc_ether_driver = { .name = "designware_eth", .probe = dwc_ether_probe, .remove = dwc_drv_remove, diff --git a/drivers/net/designware_imx.c b/drivers/net/designware_imx.c new file mode 100644 index 0000000000..c281d3b64b --- /dev/null +++ b/drivers/net/designware_imx.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <common.h> +#include <init.h> +#include <net.h> +#include <linux/regmap.h> +#include <mfd/syscon.h> +#include <linux/clk.h> + +#include "designware_eqos.h" + +#define GPR_ENET_QOS_INTF_MODE_MASK GENMASK(21, 16) +#define GPR_ENET_QOS_INTF_SEL_MII (0x0 << 16) +#define GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 16) +#define GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 16) +#define GPR_ENET_QOS_CLK_GEN_EN BIT(19) +#define GPR_ENET_QOS_CLK_TX_CLK_SEL BIT(20) +#define GPR_ENET_QOS_RGMII_EN BIT(21) + +#define MX93_GPR_ENET_QOS_INTF_MODE_MASK GENMASK(3, 0) +#define MX93_GPR_ENET_QOS_INTF_MASK GENMASK(3, 1) +#define MX93_GPR_ENET_QOS_INTF_SEL_MII (0x0 << 1) +#define MX93_GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 1) +#define MX93_GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 1) +#define MX93_GPR_ENET_QOS_CLK_GEN_EN (0x1 << 0) + +struct eqos_imx_soc_data { + int (*set_interface_mode)(struct eqos *eqos); + bool mac_rgmii_txclk_auto_adj; +}; + +struct eqos_imx_priv { + struct device *dev; + struct clk_bulk_data *clks; + int num_clks; + struct regmap *intf_regmap; + u32 intf_reg_off; + bool rmii_refclk_ext; + struct eqos_imx_soc_data *soc_data; +}; + +enum { CLK_STMMACETH, CLK_PCLK, CLK_PTP_REF, CLK_TX}; +static const struct clk_bulk_data imx_clks[] = { + [CLK_STMMACETH] = { .id = "stmmaceth" }, + [CLK_PCLK] = { .id = "pclk" }, + [CLK_PTP_REF] = { .id = "ptp_ref" }, + [CLK_TX] = { .id = "tx" }, +}; + +static unsigned long eqos_get_csr_clk_rate_imx(struct eqos *eqos) +{ + struct eqos_imx_priv *priv = eqos->priv; + + return clk_get_rate(priv->clks[CLK_PCLK].clk); +} + +static int eqos_set_txclk(struct eqos *eqos, int speed) +{ + struct eqos_imx_priv *priv = eqos->priv; + unsigned long rate; + int ret; + + switch (speed) { + case SPEED_10: + rate = 2500000; + break; + case SPEED_100: + rate = 25000000; + break; + case SPEED_1000: + rate = 125000000; + break; + default: + dev_err(priv->dev, "unknown speed value for GMAC speed=%d", speed); + return -EINVAL; + } + + ret = clk_set_rate(priv->clks[CLK_TX].clk, rate); + if (ret) + dev_err(priv->dev, "set TX clk rate %ld failed %d\n", rate, ret); + + return ret; +} + +static void eqos_adjust_link_imx(struct eth_device *edev) +{ + struct eqos *eqos = edev->priv; + struct eqos_imx_priv *priv = eqos->priv; + + if (!priv->soc_data->mac_rgmii_txclk_auto_adj) + eqos_set_txclk(eqos, edev->phydev->speed); + + eqos_adjust_link(edev); +} + +static int eqos_imx8mp_set_interface_mode(struct eqos *eqos) +{ + struct eqos_imx_priv *priv = eqos->priv; + int val; + + switch (eqos->interface) { + case PHY_INTERFACE_MODE_MII: + val = GPR_ENET_QOS_INTF_SEL_MII; + break; + case PHY_INTERFACE_MODE_RMII: + val = GPR_ENET_QOS_INTF_SEL_RMII; + val |= (priv->rmii_refclk_ext ? 0 : GPR_ENET_QOS_CLK_TX_CLK_SEL); + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + val = GPR_ENET_QOS_INTF_SEL_RGMII | + GPR_ENET_QOS_RGMII_EN; + break; + default: + dev_err(priv->dev, "no valid interface mode found!\n"); + return -EINVAL; + } + + val |= GPR_ENET_QOS_CLK_GEN_EN; + + return regmap_update_bits(priv->intf_regmap, priv->intf_reg_off, + GPR_ENET_QOS_INTF_MODE_MASK, val); +} + +static int eqos_imx93_set_interface_mode(struct eqos *eqos) +{ + struct eqos_imx_priv *priv = eqos->priv; + + int val; + + switch (eqos->interface) { + case PHY_INTERFACE_MODE_MII: + val = MX93_GPR_ENET_QOS_INTF_SEL_MII; + break; + case PHY_INTERFACE_MODE_RMII: + val = MX93_GPR_ENET_QOS_INTF_SEL_RMII; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + val = MX93_GPR_ENET_QOS_INTF_SEL_RGMII; + break; + default: + dev_dbg(priv->dev, "imx dwmac doesn't support %d interface\n", + eqos->interface); + return -EINVAL; + } + + val |= MX93_GPR_ENET_QOS_CLK_GEN_EN; + + return regmap_update_bits(priv->intf_regmap, priv->intf_reg_off, + MX93_GPR_ENET_QOS_INTF_MODE_MASK, val); +}; + +static int eqos_init_imx(struct device *dev, struct eqos *eqos) +{ + struct device_node *np = dev->device_node; + struct eqos_imx_priv *priv = eqos->priv; + int ret; + + priv->intf_regmap = syscon_regmap_lookup_by_phandle(np, "intf_mode"); + if (IS_ERR(priv->intf_regmap)) + return PTR_ERR(priv->intf_regmap); + + ret = of_property_read_u32_index(np, "intf_mode", 1, &priv->intf_reg_off); + if (ret) { + dev_err(dev, "Can't get intf mode reg offset (%d)\n", ret); + return ret; + } + + ret = priv->soc_data->set_interface_mode(eqos); + if (ret) + return ret; + + return 0; +} + +static struct eqos_ops imx_ops = { + .init = eqos_init_imx, + .get_ethaddr = eqos_get_ethaddr, + .set_ethaddr = eqos_set_ethaddr, + .adjust_link = eqos_adjust_link_imx, + .get_csr_clk_rate = eqos_get_csr_clk_rate_imx, + + .clk_csr = EQOS_MDIO_ADDR_CR_250_300, + .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB, +}; + +static int eqos_probe_imx(struct device *dev) +{ + struct device_node *np = dev->device_node; + struct eqos_imx_soc_data *soc_data; + struct eqos_imx_priv *priv; + int ret; + + ret = dev_get_drvdata(dev, (const void **)&soc_data); + if (ret) + return ret; + + priv = xzalloc(sizeof(*priv)); + + priv->soc_data = soc_data; + priv->dev = dev; + + if (of_get_property(np, "snps,rmii_refclk_ext", NULL)) + priv->rmii_refclk_ext = true; + + priv->num_clks = ARRAY_SIZE(imx_clks); + priv->clks = xmalloc(priv->num_clks * sizeof(*priv->clks)); + memcpy(priv->clks, imx_clks, sizeof imx_clks); + + ret = clk_bulk_get(dev, priv->num_clks, priv->clks); + if (ret) { + dev_err(dev, "Failed to get clks: %s\n", strerror(-ret)); + return ret; + } + + ret = clk_bulk_enable(priv->num_clks, priv->clks); + if (ret) { + dev_err(dev, "Failed to enable clks: %s\n", strerror(-ret)); + return ret; + } + + return eqos_probe(dev, &imx_ops, priv); +} + +static void eqos_remove_imx(struct device *dev) +{ + struct eqos *eqos = dev->priv; + struct eqos_imx_priv *priv = eqos->priv; + + eqos_remove(dev); + + clk_bulk_disable(priv->num_clks, priv->clks); + clk_bulk_put(priv->num_clks, priv->clks); +} + +static struct eqos_imx_soc_data imx93_soc_data = { + .set_interface_mode = eqos_imx93_set_interface_mode, + .mac_rgmii_txclk_auto_adj = true, +}; + +static struct eqos_imx_soc_data imx8mp_soc_data = { + .set_interface_mode = eqos_imx8mp_set_interface_mode, + .mac_rgmii_txclk_auto_adj = false, +}; + +static __maybe_unused struct of_device_id eqos_imx_ids[] = { + { + .compatible = "nxp,imx93-dwmac-eqos", + .data = &imx93_soc_data, + }, { + .compatible = "nxp,imx8mp-dwmac-eqos", + .data = &imx8mp_soc_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, eqos_imx_ids); + +static struct driver eqos_imx_driver = { + .name = "eqos-imx", + .probe = eqos_probe_imx, + .remove = eqos_remove_imx, + .of_compatible = DRV_OF_COMPAT(eqos_imx_ids), +}; +device_platform_driver(eqos_imx_driver); diff --git a/drivers/net/designware_rockchip.c b/drivers/net/designware_rockchip.c new file mode 100644 index 0000000000..04e2b7f12d --- /dev/null +++ b/drivers/net/designware_rockchip.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <common.h> +#include <init.h> +#include <dma.h> +#include <net.h> +#include <linux/regmap.h> +#include <of_net.h> +#include <mfd/syscon.h> +#include <linux/iopoll.h> +#include <linux/sizes.h> +#include <linux/time.h> +#include <linux/clk.h> + +#include "designware_eqos.h" + +struct rk_gmac_ops { + void (*set_to_rgmii)(struct eqos *eqos, + int tx_delay, int rx_delay); + void (*set_to_rmii)(struct eqos *eqos); + void (*set_rmii_speed)(struct eqos *eqos, int speed); + void (*set_rgmii_speed)(struct eqos *eqos, int speed); + void (*integrated_phy_powerup)(struct eqos *eqos); + const u32 *regs; +}; + +struct eqos_rk_gmac { + struct clk_bulk_data *clks; + int num_clks; + bool clock_input; + const struct rk_gmac_ops *ops; + struct regmap *grf; + int bus_id; + u32 tx_delay; + u32 rx_delay; + struct device *dev; +}; + +enum { + CLK_STMMACETH, + CLK_MAC_RX, + CLK_MAC_TX, + CLK_MAC_REFOUT, + CLK_MAC_ACLK, + CLK_MAC_PCLK, + CLK_MAC_SPEED, + CLK_PTP_REF, +}; + +static const struct clk_bulk_data rk_gmac_clks[] = { + [CLK_STMMACETH] = { .id = "stmmaceth" }, + [CLK_MAC_RX] = { .id = "mac_clk_rx" }, + [CLK_MAC_TX] = { .id = "mac_clk_tx" }, + [CLK_MAC_REFOUT] = { .id = "clk_mac_refout" }, + [CLK_MAC_ACLK] = { .id = "aclk_mac" }, + [CLK_MAC_PCLK] = { .id = "pclk_mac" }, + [CLK_MAC_SPEED] = { .id = "clk_mac_speed" }, + [CLK_PTP_REF] = { .id = "ptp_ref" }, +}; + +static inline struct eqos_rk_gmac *to_rk_gmac(struct eqos *eqos) +{ + return eqos->priv; +} + +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +#define GRF_BIT(nr) (BIT(nr) | BIT((nr) + 16)) +#define GRF_CLR_BIT(nr) (BIT((nr) + 16)) + +#define RK3568_GRF_GMAC0_CON0 0x0380 +#define RK3568_GRF_GMAC0_CON1 0x0384 +#define RK3568_GRF_GMAC1_CON0 0x0388 +#define RK3568_GRF_GMAC1_CON1 0x038c + +/* RK3568_GRF_GMAC0_CON1 && RK3568_GRF_GMAC1_CON1 */ +#define RK3568_GMAC_PHY_INTF_SEL_RGMII \ + (GRF_BIT(4) | GRF_CLR_BIT(5) | GRF_CLR_BIT(6)) +#define RK3568_GMAC_PHY_INTF_SEL_RMII \ + (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | GRF_BIT(6)) +#define RK3568_GMAC_FLOW_CTRL GRF_BIT(3) +#define RK3568_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3) +#define RK3568_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1) +#define RK3568_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(1) +#define RK3568_GMAC_TXCLK_DLY_ENABLE GRF_BIT(0) +#define RK3568_GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(0) + +/* RK3568_GRF_GMAC0_CON0 && RK3568_GRF_GMAC1_CON0 */ +#define RK3568_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 8) +#define RK3568_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) + +static unsigned long eqos_get_csr_clk_rate_rk_gmac(struct eqos *eqos) +{ + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + + return clk_get_rate(priv->clks[CLK_STMMACETH].clk); +} + +static void rk3568_set_to_rgmii(struct eqos *eqos, + int tx_delay, int rx_delay) +{ + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + struct device *dev = priv->dev; + u32 offset_con0, offset_con1; + + if (IS_ERR(priv->grf)) { + dev_err(dev, "Missing rockchip,grf property\n"); + return; + } + + offset_con0 = (priv->bus_id == 1) + ? RK3568_GRF_GMAC1_CON0 : RK3568_GRF_GMAC0_CON0; + offset_con1 = (priv->bus_id == 1) + ? RK3568_GRF_GMAC1_CON1 : RK3568_GRF_GMAC0_CON1; + + regmap_write(priv->grf, offset_con1, + RK3568_GMAC_PHY_INTF_SEL_RGMII | + RK3568_GMAC_RXCLK_DLY_ENABLE | + RK3568_GMAC_TXCLK_DLY_ENABLE); + + regmap_write(priv->grf, offset_con0, + RK3568_GMAC_CLK_RX_DL_CFG(rx_delay) | + RK3568_GMAC_CLK_TX_DL_CFG(tx_delay)); +} + +static void rk3568_set_to_rmii(struct eqos *eqos) +{ + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + struct device *dev = priv->dev; + u32 offset_con1; + + if (IS_ERR(priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + offset_con1 = (priv->bus_id == 1) + ? RK3568_GRF_GMAC1_CON1 : RK3568_GRF_GMAC0_CON1; + + regmap_write(priv->grf, offset_con1, + RK3568_GMAC_PHY_INTF_SEL_RMII); +} + +static void rk3568_set_gmac_speed(struct eqos *eqos, int speed) +{ + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + struct device *dev = priv->dev; + unsigned long rate; + int ret; + + switch (speed) { + case SPEED_10: + rate = 2500000; + break; + case SPEED_100: + rate = 25000000; + break; + case SPEED_1000: + rate = 125000000; + break; + default: + dev_err(dev, "unknown speed value for GMAC speed=%d", speed); + return; + } + + ret = clk_set_rate(priv->clks[CLK_MAC_SPEED].clk, rate); + if (ret) + dev_err(dev, "%s: set clk_mac_speed rate %ld failed %d\n", + __func__, rate, ret); +} + +static const struct rk_gmac_ops rk3568_ops = { + .set_to_rgmii = rk3568_set_to_rgmii, + .set_to_rmii = rk3568_set_to_rmii, + .set_rmii_speed = rk3568_set_gmac_speed, + .set_rgmii_speed = rk3568_set_gmac_speed, + .regs = (u32 []) { + 0xfe2a0000, /* gmac0 */ + 0xfe010000, /* gmac1 */ + 0x0, /* sentinel */ + }, +}; + +static int rk_gmac_powerup(struct eqos *eqos) +{ + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + struct device *dev = priv->dev; + + /*rmii or rgmii*/ + switch (eqos->interface) { + case PHY_INTERFACE_MODE_RGMII: + dev_dbg(dev, "init for RGMII\n"); + priv->ops->set_to_rgmii(eqos, priv->tx_delay, + priv->rx_delay); + break; + case PHY_INTERFACE_MODE_RGMII_ID: + dev_dbg(dev, "init for RGMII_ID\n"); + priv->ops->set_to_rgmii(eqos, 0, 0); + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + dev_dbg(dev, "init for RGMII_RXID\n"); + priv->ops->set_to_rgmii(eqos, priv->tx_delay, 0); + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + dev_dbg(dev, "init for RGMII_TXID\n"); + priv->ops->set_to_rgmii(eqos, 0, priv->rx_delay); + break; + case PHY_INTERFACE_MODE_RMII: + dev_dbg(dev, "init for RMII\n"); + priv->ops->set_to_rmii(eqos); + break; + default: + dev_err(dev, "NO interface defined!\n"); + } + + return 0; +} + +static void eqos_rk_adjust_link(struct eth_device *edev) +{ + struct eqos *eqos = edev->priv; + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + + if (phy_interface_mode_is_rgmii(eqos->interface)) + priv->ops->set_rgmii_speed(eqos, edev->phydev->speed); + else + priv->ops->set_rmii_speed(eqos, edev->phydev->speed); + + eqos_adjust_link(edev); +} + +static int eqos_init_rk_gmac(struct device *dev, struct eqos *eqos) +{ + struct device_node *np = dev->of_node; + struct eqos_rk_gmac *priv = to_rk_gmac(eqos); + int i = 0, ret; + const char *strings; + + priv->dev = dev; + + ret = of_property_read_string(np, "clock_in_out", &strings); + if (ret) { + dev_err(dev, "Can not read property: clock_in_out.\n"); + priv->clock_input = true; + } else { + dev_dbg(dev, "clock is %s\n", strings); + if (!strcmp(strings, "input")) + priv->clock_input = true; + else + priv->clock_input = false; + } + + priv->ops = device_get_match_data(dev); + + if (dev->num_resources > 0 && priv->ops->regs) { + while (priv->ops->regs[i]) { + if (priv->ops->regs[i] == dev->resource[0].start) { + priv->bus_id = i; + break; + } + i++; + } + } + + priv->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(priv->grf)) { + dev_err(dev, "unable to get grf"); + return PTR_ERR(priv->grf); + } + + priv->tx_delay = 0x30; + of_property_read_u32(np, "tx_delay", &priv->tx_delay); + priv->rx_delay = 0x10; + of_property_read_u32(np, "rx_delay", &priv->rx_delay); + + priv->num_clks = ARRAY_SIZE(rk_gmac_clks); + priv->clks = xmalloc(priv->num_clks * sizeof(*priv->clks)); + memcpy(priv->clks, rk_gmac_clks, sizeof rk_gmac_clks); + + ret = clk_bulk_get(dev, priv->num_clks, priv->clks); + if (ret) { + dev_err(dev, "Failed to get clks: %s\n", strerror(-ret)); + return ret; + } + + ret = clk_bulk_enable(priv->num_clks, priv->clks); + if (ret) { + dev_err(dev, "Failed to enable clks: %s\n", strerror(-ret)); + return ret; + } + + rk_gmac_powerup(eqos); + + return 0; +} + +static struct eqos_ops rk_gmac_ops = { + .init = eqos_init_rk_gmac, + .get_ethaddr = eqos_get_ethaddr, + .set_ethaddr = eqos_set_ethaddr, + .adjust_link = eqos_rk_adjust_link, + .get_csr_clk_rate = eqos_get_csr_clk_rate_rk_gmac, + + .clk_csr = EQOS_MDIO_ADDR_CR_250_300, + .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV, +}; + +static int rk_gmac_probe(struct device *dev) +{ + return eqos_probe(dev, &rk_gmac_ops, xzalloc(sizeof(struct eqos_rk_gmac))); +} + +static __maybe_unused struct of_device_id rk_gmac_compatible[] = { + { + .compatible = "rockchip,rk3568-gmac", + .data = &rk3568_ops, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, rk_gmac_compatible); + +static struct driver rk_gmac_driver = { + .name = "eqos-rockchip", + .probe = rk_gmac_probe, + .remove = eqos_remove, + .of_compatible = DRV_OF_COMPAT(rk_gmac_compatible), +}; +device_platform_driver(rk_gmac_driver); diff --git a/drivers/net/designware_socfpga.c b/drivers/net/designware_socfpga.c index d6c28af45e..a39c945c81 100644 --- a/drivers/net/designware_socfpga.c +++ b/drivers/net/designware_socfpga.c @@ -154,7 +154,8 @@ static int socfpga_gen10_set_phy_mode(struct socfpga_dwc_dev *dwc_dev) } -static int socfpga_dwc_probe_dt(struct device_d *dev, struct socfpga_dwc_dev *priv) +static int socfpga_dwc_probe_dt(struct device *dev, + struct socfpga_dwc_dev *priv) { u32 reg_offset, reg_shift; int ret; @@ -162,7 +163,7 @@ static int socfpga_dwc_probe_dt(struct device_d *dev, struct socfpga_dwc_dev *pr if (!IS_ENABLED(CONFIG_OFTREE)) return -ENODEV; - ret = of_property_read_u32_index(dev->device_node, "altr,sysmgr-syscon", + ret = of_property_read_u32_index(dev->of_node, "altr,sysmgr-syscon", 1, ®_offset); if (ret) { dev_err(dev, "Could not read reg_offset from sysmgr-syscon! Please update the devicetree.\n"); @@ -170,14 +171,15 @@ static int socfpga_dwc_probe_dt(struct device_d *dev, struct socfpga_dwc_dev *pr return -EINVAL; } - ret = of_property_read_u32_index(dev->device_node, "altr,sysmgr-syscon", + ret = of_property_read_u32_index(dev->of_node, "altr,sysmgr-syscon", 2, ®_shift); if (ret) { dev_err(dev, "Could not read reg_shift from sysmgr-syscon! Please update the devicetree.\n"); return -EINVAL; } - priv->f2h_ptp_ref_clk = of_property_read_bool(dev->device_node, "altr,f2h_ptp_ref_clk"); + priv->f2h_ptp_ref_clk = of_property_read_bool(dev->of_node, + "altr,f2h_ptp_ref_clk"); priv->reg_offset = reg_offset; priv->reg_shift = reg_shift; @@ -185,7 +187,7 @@ static int socfpga_dwc_probe_dt(struct device_d *dev, struct socfpga_dwc_dev *pr return 0; } -static int socfpga_dwc_ether_probe(struct device_d *dev) +static int socfpga_dwc_ether_probe(struct device *dev) { struct socfpga_dwc_dev *dwc_dev; struct dw_eth_dev *priv; @@ -215,7 +217,7 @@ static int socfpga_dwc_ether_probe(struct device_d *dev) dwc_dev->priv = priv; - dwc_dev->sys_mgr_base = syscon_base_lookup_by_phandle(dev->device_node, + dwc_dev->sys_mgr_base = syscon_base_lookup_by_phandle(dev->of_node, "altr,sysmgr-syscon"); if (IS_ERR(dwc_dev->sys_mgr_base)) { dev_err(dev, "Could not get sysmgr-syscon node\n"); @@ -259,8 +261,9 @@ static __maybe_unused struct of_device_id socfpga_dwc_ether_compatible[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, socfpga_dwc_ether_compatible); -static struct driver_d socfpga_dwc_ether_driver = { +static struct driver socfpga_dwc_ether_driver = { .name = "socfpga_designware_eth", .probe = socfpga_dwc_ether_probe, .remove = dwc_drv_remove, diff --git a/drivers/net/designware_starfive.c b/drivers/net/designware_starfive.c new file mode 100644 index 0000000000..aff2cc10e1 --- /dev/null +++ b/drivers/net/designware_starfive.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2021 Ahmad Fatoum, Pengutronix + */ + +#include <common.h> +#include <init.h> +#include <linux/reset.h> +#include <linux/regmap.h> +#include <linux/clk.h> +#include <mfd/syscon.h> +#include <soc/starfive/sysmain.h> +#include "designware.h" + +/* + * GMAC_GTXCLK + * bit name access default description + * [31] _gmac_gtxclk enable RW 0x0 "1:enable; 0:disable" + * [30] reserved - 0x0 reserved + * [29:8] reserved - 0x0 reserved + * [7:0] gmac_gtxclk ratio RW 0x4 divider value + * + * 1000M: gtxclk@125M => 500/125 = 0x4 + * 100M: gtxclk@25M => 500/25 = 0x14 + * 10M: gtxclk@2.5M => 500/2.5 = 0xc8 + */ + +#define CLKGEN_BASE 0x11800000 +#define CLKGEN_GMAC_GTXCLK_OFFSET 0x1EC +#define CLKGEN_GMAC_GTXCLK_ADDR (CLKGEN_BASE + CLKGEN_GMAC_GTXCLK_OFFSET) + + +#define CLKGEN_125M_DIV 0x4 +#define CLKGEN_25M_DIV 0x14 +#define CLKGEN_2_5M_DIV 0xc8 + +static void dwmac_fixed_speed(int speed) +{ + /* TODO: move this into clk driver */ + void __iomem *addr = IOMEM(CLKGEN_GMAC_GTXCLK_ADDR); + u32 value; + + value = readl(addr) & (~0x000000FF); + + switch (speed) { + case SPEED_1000: value |= CLKGEN_125M_DIV; break; + case SPEED_100: value |= CLKGEN_25M_DIV; break; + case SPEED_10: value |= CLKGEN_2_5M_DIV; break; + default: return; + } + + writel(value, addr); +} + +static struct dw_eth_drvdata starfive_drvdata = { + .enh_desc = 1, + .fix_mac_speed = dwmac_fixed_speed, +}; + +static int starfive_dwc_ether_probe(struct device *dev) +{ + struct dw_eth_dev *dwc; + struct regmap *regmap; + int ret; + struct clk_bulk_data clks[] = { + { .id = "stmmaceth" }, + { .id = "ptp_ref" }, + { .id = "tx" }, + }; + + regmap = syscon_regmap_lookup_by_phandle(dev->of_node, + "starfive,sysmain"); + if (IS_ERR(regmap)) { + dev_err(dev, "Could not get starfive,sysmain node\n"); + return PTR_ERR(regmap); + } + + ret = clk_bulk_get(dev, ARRAY_SIZE(clks), clks); + if (ret) + return ret; + + ret = clk_bulk_enable(ARRAY_SIZE(clks), clks); + if (ret < 0) + return ret; + + ret = device_reset(dev); + if (ret) + return ret; + + dwc = dwc_drv_probe(dev); + if (IS_ERR(dwc)) + return PTR_ERR(dwc); + + if (phy_interface_mode_is_rgmii(dwc->interface)) { + regmap_update_bits(regmap, SYSMAIN_GMAC_PHY_INTF_SEL, 0x7, 0x1); + regmap_write(regmap, SYSMAIN_GMAC_GTXCLK_DLYCHAIN_SEL, 0x4); + } + + return 0; +} + +static struct of_device_id starfive_dwc_ether_compatible[] = { + { .compatible = "starfive,stmmac", .data = &starfive_drvdata }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, starfive_dwc_ether_compatible); + +static struct driver starfive_dwc_ether_driver = { + .name = "starfive-designware_eth", + .probe = starfive_dwc_ether_probe, + .of_compatible = starfive_dwc_ether_compatible, +}; +device_platform_driver(starfive_dwc_ether_driver); diff --git a/drivers/net/designware_stm32.c b/drivers/net/designware_stm32.c index 2e2af8942d..54dabcc8d3 100644 --- a/drivers/net/designware_stm32.c +++ b/drivers/net/designware_stm32.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016, NVIDIA CORPORATION. * Copyright (c) 2019, Ahmad Fatoum, Pengutronix @@ -11,6 +11,7 @@ #include <net.h> #include <linux/clk.h> #include <mfd/syscon.h> +#include <linux/regmap.h> #include "designware_eqos.h" @@ -112,9 +113,9 @@ static int eqos_set_mode_stm32(struct eqos_stm32 *priv, phy_interface_t interfac return 0; } -static int eqos_init_stm32(struct device_d *dev, struct eqos *eqos) +static int eqos_init_stm32(struct device *dev, struct eqos *eqos) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct eqos_stm32 *priv = to_stm32(eqos); struct clk_bulk_data *eth_ck; int ret; @@ -126,14 +127,14 @@ static int eqos_init_stm32(struct device_d *dev, struct eqos *eqos) priv->eth_ref_clk_sel_reg = of_property_read_bool(np, "st,eth-ref-clk-sel"); - priv->regmap = syscon_regmap_lookup_by_phandle(dev->device_node, + priv->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "st,syscon"); if (IS_ERR(priv->regmap)) { dev_err(dev, "Could not get st,syscon node\n"); return PTR_ERR(priv->regmap); } - ret = of_property_read_u32_index(dev->device_node, "st,syscon", + ret = of_property_read_u32_index(dev->of_node, "st,syscon", 1, &priv->mode_reg); if (ret) { dev_err(dev, "Can't get sysconfig mode offset (%s)\n", @@ -163,14 +164,7 @@ static int eqos_init_stm32(struct device_d *dev, struct eqos *eqos) dev_dbg(dev, "No phy clock provided. Continuing without.\n"); } - ret = clk_bulk_enable(priv->num_clks, priv->clks); - if (ret < 0) { - eqos_err(eqos, "clk_bulk_enable() failed: %s\n", - strerror(-ret)); - return ret; - } - - return 0; + return clk_bulk_enable(priv->num_clks, priv->clks); } static struct eqos_ops stm32_ops = { @@ -184,12 +178,12 @@ static struct eqos_ops stm32_ops = { .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV, }; -static int eqos_probe_stm32(struct device_d *dev) +static int eqos_probe_stm32(struct device *dev) { return eqos_probe(dev, &stm32_ops, xzalloc(sizeof(struct eqos_stm32))); } -static void eqos_remove_stm32(struct device_d *dev) +static void eqos_remove_stm32(struct device *dev) { struct eqos_stm32 *priv = to_stm32(dev->priv); @@ -203,8 +197,9 @@ static const struct of_device_id eqos_stm32_ids[] = { { .compatible = "st,stm32mp1-dwmac" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, eqos_stm32_ids); -static struct driver_d eqos_stm32_driver = { +static struct driver eqos_stm32_driver = { .name = "eqos-stm32", .probe = eqos_probe_stm32, .remove = eqos_remove_stm32, diff --git a/drivers/net/designware_tegra186.c b/drivers/net/designware_tegra186.c index 5348f65c41..86f97e853e 100644 --- a/drivers/net/designware_tegra186.c +++ b/drivers/net/designware_tegra186.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016, NVIDIA CORPORATION. * Copyright (c) 2019, Ahmad Fatoum, Pengutronix @@ -195,7 +195,7 @@ static int eqos_set_ethaddr_tegra186(struct eth_device *edev, const unsigned cha * ported to some system where the expectation above is true. */ - if (!eqos->started) { + if (!edev->active) { memcpy(eqos->macaddr, mac, 6); return 0; } @@ -203,7 +203,7 @@ static int eqos_set_ethaddr_tegra186(struct eth_device *edev, const unsigned cha return eqos_set_ethaddr(edev, mac); } -static int eqos_init_tegra186(struct device_d *dev, struct eqos *eqos) +static int eqos_init_tegra186(struct device *dev, struct eqos *eqos) { struct eqos_tegra186 *priv = to_tegra186(eqos); int phy_reset; @@ -213,12 +213,11 @@ static int eqos_init_tegra186(struct device_d *dev, struct eqos *eqos) priv->rst = reset_control_get(dev, "eqos"); if (IS_ERR(priv->rst)) { - ret = PTR_ERR(priv->rst); - dev_err(dev, "reset_get_by_name(rst) failed: %s\n", strerror(-ret)); - return ret; + dev_err(dev, "reset_get_by_name(rst) failed: %pe\n", priv->rst); + return PTR_ERR(priv->rst); } - phy_reset = of_get_named_gpio(dev->device_node, "phy-reset-gpios", 0); + phy_reset = of_get_named_gpio(dev->of_node, "phy-reset-gpios", 0); if (gpio_is_valid(phy_reset)) { ret = gpio_request(phy_reset, "phy-reset"); if (ret) @@ -284,12 +283,12 @@ static const struct eqos_ops tegra186_ops = { .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_DCB, }; -static int eqos_probe_tegra186(struct device_d *dev) +static int eqos_probe_tegra186(struct device *dev) { return eqos_probe(dev, &tegra186_ops, xzalloc(sizeof(struct eqos_tegra186))); } -static void eqos_remove_tegra186(struct device_d *dev) +static void eqos_remove_tegra186(struct device *dev) { struct eqos_tegra186 *priv = to_tegra186(dev->priv); @@ -309,8 +308,9 @@ static const struct of_device_id eqos_tegra186_ids[] = { { .compatible = "nvidia,tegra186-eqos" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, eqos_tegra186_ids); -static struct driver_d eqos_tegra186_driver = { +static struct driver eqos_tegra186_driver = { .name = "eqos-tegra186", .probe = eqos_probe_tegra186, .remove = eqos_remove_tegra186, diff --git a/drivers/net/dm9k.c b/drivers/net/dm9k.c index d183ab55c9..29defece4e 100644 --- a/drivers/net/dm9k.c +++ b/drivers/net/dm9k.c @@ -349,7 +349,7 @@ static int dm9k_phy_read(struct mii_bus *bus, int addr, int reg) { unsigned val; struct dm9k *priv = bus->priv; - struct device_d *dev = &bus->dev; + struct device *dev = &bus->dev; /* only internal phy supported by now, so show only one phy on miibus */ if (addr != 0) { @@ -373,7 +373,7 @@ static int dm9k_phy_read(struct mii_bus *bus, int addr, int reg) static int dm9k_phy_write(struct mii_bus *bus, int addr, int reg, u16 val) { struct dm9k *priv = bus->priv; - struct device_d *dev = &bus->dev; + struct device *dev = &bus->dev; /* only internal phy supported by now, so show only one phy on miibus */ if (addr != 0) { @@ -397,7 +397,7 @@ static int dm9k_phy_write(struct mii_bus *bus, int addr, int reg, u16 val) static int dm9k_check_id(struct dm9k *priv) { - struct device_d *dev = priv->miibus.parent; + struct device *dev = priv->miibus.parent; u32 id; char c; @@ -461,7 +461,7 @@ static void dm9k_enable(struct dm9k *priv) static void dm9k_reset(struct dm9k *priv) { - struct device_d *dev = priv->miibus.parent; + struct device *dev = priv->miibus.parent; dev_dbg(dev, "%s\n", __func__); @@ -504,7 +504,7 @@ static void dm9k_write_length(struct dm9k *priv, unsigned length) static int dm9k_wait_for_trans_end(struct dm9k *priv) { - struct device_d *dev = priv->miibus.parent; + struct device *dev = priv->miibus.parent; static const uint64_t toffs = 1 * SECOND; uint8_t status; uint64_t start = get_time_ns(); @@ -530,7 +530,7 @@ static int dm9k_wait_for_trans_end(struct dm9k *priv) static int dm9k_eth_send(struct eth_device *edev, void *packet, int length) { struct dm9k *priv = (struct dm9k *)edev->priv; - struct device_d *dev = priv->miibus.parent; + struct device *dev = priv->miibus.parent; dev_dbg(dev, "%s: %d bytes\n", __func__, length); @@ -556,7 +556,7 @@ static int dm9k_eth_send(struct eth_device *edev, void *packet, int length) static int dm9k_check_for_rx_packet(struct dm9k *priv) { uint8_t status; - struct device_d *dev = priv->miibus.parent; + struct device *dev = priv->miibus.parent; status = dm9k_ior(priv, DM9K_ISR); if (!(status & ISR_PR)) @@ -568,7 +568,7 @@ static int dm9k_check_for_rx_packet(struct dm9k *priv) static int dm9k_validate_entry(struct dm9k *priv) { - struct device_d *dev = priv->miibus.parent; + struct device *dev = priv->miibus.parent; uint8_t p_stat; /* @@ -600,7 +600,7 @@ static int dm9k_validate_entry(struct dm9k *priv) static int dm9k_eth_rx(struct eth_device *edev) { struct dm9k *priv = (struct dm9k *)edev->priv; - struct device_d *dev = edev->parent; + struct device *dev = edev->parent; unsigned rx_stat = 0, rx_len = 0; bool p_valid; @@ -720,9 +720,9 @@ static int dm9k_init_dev(struct eth_device *edev) return 0; } -static int dm9000_parse_dt(struct device_d *dev, struct dm9k *priv) +static int dm9000_parse_dt(struct device *dev, struct dm9k *priv) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; uint32_t prop; if (!IS_ENABLED(CONFIG_OFDEVICE) || !np) @@ -757,7 +757,7 @@ static int dm9000_parse_dt(struct device_d *dev, struct dm9k *priv) return 0; } -static int dm9000_parse_pdata(struct device_d *dev, struct dm9k *priv) +static int dm9000_parse_pdata(struct device *dev, struct dm9k *priv) { struct dm9000_platform_data *pdata = dev->platform_data; @@ -768,7 +768,7 @@ static int dm9000_parse_pdata(struct device_d *dev, struct dm9k *priv) return 0; } -static int dm9k_probe(struct device_d *dev) +static int dm9k_probe(struct device *dev) { struct resource *iores; unsigned io_mode; @@ -870,8 +870,9 @@ static struct of_device_id dm9000_of_matches[] = { { .compatible = "davicom,dm9000", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, dm9000_of_matches); -static struct driver_d dm9k_driver = { +static struct driver dm9k_driver = { .name = "dm9000", .probe = dm9k_probe, .of_compatible = DRV_OF_COMPAT(dm9000_of_matches), diff --git a/drivers/net/dsa.c b/drivers/net/dsa.c new file mode 100644 index 0000000000..ccd7d87550 --- /dev/null +++ b/drivers/net/dsa.c @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <common.h> +#include <dma.h> +#include <dsa.h> +#include <of_net.h> + +u32 dsa_user_ports(struct dsa_switch *ds) +{ + u32 mask = 0; + int i; + + for (i = 0; i < ds->num_ports; i++) { + if (ds->dp[i]) + mask |= BIT(ds->dp[i]->index); + } + + return mask; +} + +static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg) +{ + struct dsa_switch *ds = bus->priv; + + if (ds->phys_mii_mask & BIT(addr)) + return ds->ops->phy_read(ds, addr, reg); + + return 0xffff; +} + +static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val) +{ + struct dsa_switch *ds = bus->priv; + + if (ds->phys_mii_mask & BIT(addr)) + return ds->ops->phy_write(ds, addr, reg, val); + + return 0; +} + +static int dsa_slave_mii_bus_init(struct dsa_switch *ds) +{ + ds->slave_mii_bus = xzalloc(sizeof(*ds->slave_mii_bus)); + ds->slave_mii_bus->priv = (void *)ds; + ds->slave_mii_bus->read = dsa_slave_phy_read; + ds->slave_mii_bus->write = dsa_slave_phy_write; + ds->slave_mii_bus->parent = ds->dev; + ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask; + + return mdiobus_register(ds->slave_mii_bus); +} + +static int dsa_port_probe(struct eth_device *edev) +{ + struct dsa_port *dp = edev->priv; + struct dsa_switch *ds = dp->ds; + const struct dsa_switch_ops *ops = ds->ops; + phy_interface_t interface; + int ret; + + if (ops->port_probe) { + interface = of_get_phy_mode(dp->dev->of_node); + ret = ops->port_probe(dp, dp->index, interface); + if (ret) + return ret; + } + + return 0; +} + +static void dsa_port_set_ethaddr(struct eth_device *edev) +{ + struct dsa_port *dp = edev->priv; + struct dsa_switch *ds = dp->ds; + + if (is_valid_ether_addr(edev->ethaddr)) + return; + + if (!is_valid_ether_addr(ds->edev_master->ethaddr)) + return; + + eth_set_ethaddr(edev, ds->edev_master->ethaddr); +} + +static int dsa_port_start(struct eth_device *edev) +{ + struct dsa_port *dp = edev->priv; + struct dsa_switch *ds = dp->ds; + const struct dsa_switch_ops *ops = ds->ops; + phy_interface_t interface; + int ret; + + if (dp->enabled) + return -EBUSY; + + interface = of_get_phy_mode(dp->dev->of_node); + + if (ops->port_pre_enable) { + /* In case of RMII interface we need to enable RMII clock + * before talking to the PHY. + */ + ret = ops->port_pre_enable(dp, dp->index, interface); + if (ret) + return ret; + } + + ret = phy_device_connect(edev, ds->slave_mii_bus, dp->index, + ops->adjust_link, 0, interface); + if (ret) + return ret; + + dsa_port_set_ethaddr(edev); + + if (ops->port_enable) { + ret = ops->port_enable(dp, dp->index, dp->edev.phydev); + if (ret) + return ret; + } + + dp->enabled = true; + + if (!ds->cpu_port_users) { + struct dsa_port *dpc = ds->dp[ds->cpu_port]; + + if (ops->port_pre_enable) { + /* In case of RMII interface we need to enable RMII clock + * before talking to the PHY. + */ + ret = ops->port_pre_enable(dpc, ds->cpu_port, + ds->cpu_port_fixed_phy->interface); + if (ret) + return ret; + } + + if (ops->port_enable) { + ret = ops->port_enable(dpc, ds->cpu_port, + ds->cpu_port_fixed_phy); + if (ret) + return ret; + } + + ret = eth_set_promisc(ds->edev_master, true); + if (ret) + dev_warn(ds->dev, "Failed to set promisc mode. Using different eth addresses may not work. %pe\n", + ERR_PTR(ret)); + + eth_open(ds->edev_master); + } + + ds->cpu_port_users++; + + return 0; +} + +/* Stop the desired port, the CPU port and the master Eth interface */ +static void dsa_port_stop(struct eth_device *edev) +{ + struct dsa_port *dp = edev->priv; + struct dsa_switch *ds = dp->ds; + const struct dsa_switch_ops *ops = ds->ops; + + if (!dp->enabled) + return; + + if (ops->port_disable) + ops->port_disable(dp, dp->index, dp->edev.phydev); + + dp->enabled = false; + ds->cpu_port_users--; + + if (!ds->cpu_port_users) { + struct dsa_port *dpc = ds->dp[ds->cpu_port]; + + if (ops->port_disable) + ops->port_disable(dpc, ds->cpu_port, + ds->cpu_port_fixed_phy); + + eth_set_promisc(ds->edev_master, false); + eth_close(ds->edev_master); + } +} + +static int dsa_port_send(struct eth_device *edev, void *packet, int length) +{ + struct dsa_port *dp = edev->priv; + struct dsa_switch *ds = dp->ds; + const struct dsa_switch_ops *ops = ds->ops; + void *tx_buf = ds->tx_buf; + size_t full_length, stuff = 0; + int ret; + + if (length < 64) + stuff = 64 - length; + + full_length = length + ds->needed_headroom + ds->needed_tx_tailroom + + stuff; + + if (full_length > DSA_PKTSIZE) + return -ENOMEM; + + memset(tx_buf + full_length - stuff, 0, stuff); + memcpy(tx_buf + ds->needed_headroom, packet, length); + ret = ops->xmit(dp, dp->index, tx_buf, full_length); + if (ret) + return ret; + + return eth_send_raw(ds->edev_master, tx_buf, full_length); +} + +static int dsa_port_recv(struct eth_device *edev) +{ + struct dsa_port *dp = edev->priv; + int length; + + if (!dp->rx_buf_length) + return 0; + + net_receive(edev, dp->rx_buf, dp->rx_buf_length); + length = dp->rx_buf_length; + dp->rx_buf_length = 0; + + return length; +} + +static int dsa_ether_set_ethaddr(struct eth_device *edev, + const unsigned char *adr) +{ + struct dsa_port *dp = edev->priv; + struct dsa_switch *ds = dp->ds; + struct eth_device *edev_master; + + edev_master = ds->edev_master; + + return edev_master->set_ethaddr(edev_master, adr); +} + +static int dsa_ether_get_ethaddr(struct eth_device *edev, unsigned char *adr) +{ + struct dsa_port *dp = edev->priv; + struct dsa_switch *ds = dp->ds; + struct eth_device *edev_master; + + edev_master = ds->edev_master; + + return edev_master->get_ethaddr(edev_master, adr); +} + +static struct dsa_port *dsa_port_alloc(struct dsa_switch *ds, + struct device_node *dn, int port) +{ + struct device *dev; + struct dsa_port *dp; + + ds->dp[port] = xzalloc(sizeof(*dp)); + dp = ds->dp[port]; + + dev = of_platform_device_create(dn, ds->dev); + of_platform_device_dummy_drv(dev); + dp->dev = dev; + dp->ds = ds; + dp->index = port; + + return dp; +} + +static int dsa_switch_register_edev(struct dsa_switch *ds, + struct device_node *dn, int port) +{ + struct eth_device *edev; + struct dsa_port *dp; + + dp = dsa_port_alloc(ds, dn, port); + dp->rx_buf = xmalloc(DSA_PKTSIZE); + + edev = &dp->edev; + edev->priv = dp; + edev->parent = dp->dev; + edev->init = dsa_port_probe; + edev->open = dsa_port_start; + edev->send = dsa_port_send; + edev->recv = dsa_port_recv; + edev->halt = dsa_port_stop; + edev->get_ethaddr = dsa_ether_get_ethaddr; + edev->set_ethaddr = dsa_ether_set_ethaddr; + + return eth_register(edev); +} + +static int dsa_rx_preprocessor(struct eth_device *edev, unsigned char **packet, + int *length) +{ + struct dsa_switch *ds = edev->rx_preprocessor_priv; + const struct dsa_switch_ops *ops = ds->ops; + struct dsa_port *dp; + int ret, port; + + ret = ops->rcv(ds, &port, *packet, *length); + if (ret) + return ret; + + *length -= ds->needed_headroom; + *packet += ds->needed_headroom; + *length -= ds->needed_rx_tailroom; + + if (port > DSA_MAX_PORTS) + return -ERANGE; + + dp = ds->dp[port]; + if (!dp) + return 0; + + if (*length > DSA_PKTSIZE) + return -ENOMEM; + + if (dp->rx_buf_length) + return -EIO; + + memcpy(dp->rx_buf, *packet, *length); + dp->rx_buf_length = *length; + + return -ENOMSG; +} + +static int dsa_switch_register_master(struct dsa_switch *ds, + struct device_node *np, + struct device_node *master, int port) +{ + struct device_node *phy_node; + struct phy_device *phydev; + int ret; + + if (ds->edev_master) { + dev_err(ds->dev, "master was already registered!\n"); + return -EINVAL; + } + + ds->edev_master = of_find_eth_device_by_node(master); + if (!ds->edev_master) { + dev_err(ds->dev, "can't find ethernet master device\n"); + return -ENODEV; + } + + ds->edev_master->rx_preprocessor = dsa_rx_preprocessor; + ds->edev_master->rx_preprocessor_priv = ds; + + ret = dev_set_param(&ds->edev_master->dev, "mode", "disabled"); + if (ret) + dev_warn(ds->dev, "Can't set disable master Ethernet device\n"); + + phy_node = of_get_child_by_name(np, "fixed-link"); + if (!phy_node) + return -ENODEV; + + phydev = of_phy_register_fixed_link(phy_node, ds->edev_master); + if (!phydev) + return -ENODEV; + + phydev->interface = of_get_phy_mode(np); + + dsa_port_alloc(ds, np, port); + + ds->cpu_port = port; + ds->cpu_port_fixed_phy = phydev; + + return 0; +} + +static int dsa_switch_parse_ports_of(struct dsa_switch *ds, + struct device_node *dn) +{ + struct device_node *ports, *port; + int ret = 0; + u32 reg; + + ports = of_get_child_by_name(dn, "ports"); + if (!ports) { + /* The second possibility is "ethernet-ports" */ + ports = of_get_child_by_name(dn, "ethernet-ports"); + if (!ports) { + dev_err(ds->dev, "no ports child node found\n"); + return -EINVAL; + } + } + + /* At first step, find and register master/CPU interface */ + for_each_available_child_of_node(ports, port) { + struct device_node *master; + + ret = of_property_read_u32(port, "reg", ®); + if (ret) { + dev_err(ds->dev, "No or too many ports are configured\n"); + goto out_put_node; + } + + if (reg >= ds->num_ports) { + dev_err(ds->dev, "port %pOF index %u exceeds num_ports (%zu)\n", + port, reg, ds->num_ports); + ret = -EINVAL; + goto out_put_node; + } + + master = of_parse_phandle(port, "ethernet", 0); + if (master) { + ret = dsa_switch_register_master(ds, port, master, reg); + if (ret) + return ret; + } + } + + /* Now we can register regular switch ports */ + for_each_available_child_of_node(ports, port) { + of_property_read_u32(port, "reg", ®); + + if (of_parse_phandle(port, "ethernet", 0)) + continue; + + ret = dsa_switch_register_edev(ds, port, reg); + if (ret) { + dev_err(ds->dev, "Can't create edev for port %i\n", + reg); + return ret; + } + } + +out_put_node: + return ret; +} + +int dsa_register_switch(struct dsa_switch *ds) +{ + int ret; + + if (!ds->dev) { + pr_err("No dev is set\n"); + return -ENODEV; + } + + if (!ds->dev->of_node) + return -ENODEV; + + if (!ds->num_ports || ds->num_ports > DSA_MAX_PORTS) { + dev_err(ds->dev, "No or too many ports are configured\n"); + return -EINVAL; + } + + ret = dsa_switch_parse_ports_of(ds, ds->dev->of_node); + if (ret) + return ret; + + if (!ds->slave_mii_bus && ds->ops->phy_read) { + ret = dsa_slave_mii_bus_init(ds); + if (ret) + return ret; + } + + ds->tx_buf = dma_alloc(DSA_PKTSIZE); + + return 0; +} +EXPORT_SYMBOL_GPL(dsa_register_switch); + diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile new file mode 100644 index 0000000000..4d10d84332 --- /dev/null +++ b/drivers/net/e1000/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += regio.o main.o eeprom.o +obj-$(CONFIG_MTD) += mtd.o diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index 52ad3d4cdb..d440d7540a 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -1388,6 +1388,9 @@ struct e1000_eeprom_info { #define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */ #define PHY_EXT_STATUS 0x0F /* Extended Status Reg */ +#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ +#define MAX_PHY_MULTI_PAGE_REG 0xF /* Registers equal on all pages */ + /* M88E1000 Specific Registers */ #define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ #define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */ @@ -2147,7 +2150,7 @@ struct e1000_hw { struct eth_device edev; struct pci_dev *pdev; - struct device_d *dev; + struct device *dev; void __iomem *hw_addr; @@ -2160,7 +2163,7 @@ struct e1000_hw { struct { struct cdev cdev; - struct device_d dev; + struct device dev; int line; } invm; @@ -2180,7 +2183,9 @@ struct e1000_hw { struct mii_bus miibus; struct e1000_tx_desc *tx_base; + dma_addr_t tx_base_phys; struct e1000_rx_desc *rx_base; + dma_addr_t rx_base_phys; unsigned char *packet; dma_addr_t packet_dma; diff --git a/drivers/net/e1000/eeprom.c b/drivers/net/e1000/eeprom.c index 5b34e9b8d1..effe0c6cff 100644 --- a/drivers/net/e1000/eeprom.c +++ b/drivers/net/e1000/eeprom.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <malloc.h> @@ -409,8 +410,7 @@ static void e1000_eeprom_uses_microwire(struct e1000_eeprom_info *eeprom, static size_t e1000_igb_get_flash_size(struct e1000_hw *hw) { - struct device_node *node = - hw->pdev->dev.device_node; + struct device_node *node = hw->pdev->dev.of_node; u32 flash_size; uint32_t fla; int ret = 0; @@ -494,6 +494,7 @@ int32_t e1000_init_eeprom_params(struct e1000_hw *hw) case e1000_82571: case e1000_82572: e1000_eeprom_uses_spi(eeprom, eecd); + eeprom->read = e1000_read_eeprom_eerd; break; case e1000_82573: @@ -501,7 +502,6 @@ int32_t e1000_init_eeprom_params(struct e1000_hw *hw) if (e1000_is_onboard_nvm_eeprom(hw)) { e1000_eeprom_uses_spi(eeprom, eecd); } else { - eeprom->read = e1000_read_eeprom_eerd; eeprom->type = e1000_eeprom_flash; eeprom->word_size = 2048; @@ -512,6 +512,7 @@ int32_t e1000_init_eeprom_params(struct e1000_hw *hw) eecd &= ~E1000_EECD_AUPDEN; e1000_write_reg(hw, E1000_EECD, eecd); } + eeprom->read = e1000_read_eeprom_eerd; break; case e1000_80003es2lan: @@ -731,250 +732,6 @@ static int32_t e1000_spi_eeprom_ready(struct e1000_hw *hw) return E1000_SUCCESS; } -static int e1000_flash_mode_wait_for_idle(struct e1000_hw *hw) -{ - const int ret = e1000_poll_reg(hw, E1000_FLSWCTL, E1000_FLSWCTL_DONE, - E1000_FLSWCTL_DONE, SECOND); - if (ret < 0) - dev_err(hw->dev, - "Timeout waiting for FLSWCTL.DONE to be set (wait)\n"); - return ret; -} - -static int e1000_flash_mode_check_command_valid(struct e1000_hw *hw) -{ - const uint32_t flswctl = e1000_read_reg(hw, E1000_FLSWCTL); - if (!(flswctl & E1000_FLSWCTL_CMDV)) { - dev_err(hw->dev, "FLSWCTL.CMDV was cleared\n"); - return -EIO; - } - - return E1000_SUCCESS; -} - -static void e1000_flash_cmd(struct e1000_hw *hw, - uint32_t cmd, uint32_t offset) -{ - uint32_t flswctl = e1000_read_reg(hw, E1000_FLSWCTL); - flswctl &= ~E1000_FLSWCTL_CMD_ADDR_MASK; - flswctl |= E1000_FLSWCTL_CMD(cmd) | E1000_FLSWCTL_ADDR(offset); - e1000_write_reg(hw, E1000_FLSWCTL, flswctl); -} - -static int e1000_flash_mode_read_chunk(struct e1000_hw *hw, loff_t offset, - size_t size, void *data) -{ - int ret; - size_t chunk, residue = size; - uint32_t flswdata; - - DEBUGFUNC(); - - if (size > SZ_4K || - E1000_FLSWCTL_ADDR(offset) != offset) - return -EINVAL; - - ret = e1000_flash_mode_wait_for_idle(hw); - if (ret < 0) - return ret; - - e1000_write_reg(hw, E1000_FLSWCNT, size); - e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_READ, offset); - - do { - ret = e1000_flash_mode_check_command_valid(hw); - if (ret < 0) - return -EIO; - - chunk = min(sizeof(flswdata), residue); - - ret = e1000_poll_reg(hw, E1000_FLSWCTL, - E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE, - SECOND); - if (ret < 0) { - dev_err(hw->dev, - "Timeout waiting for FLSWCTL.DONE to be set (read)\n"); - return ret; - } - - flswdata = e1000_read_reg(hw, E1000_FLSWDATA); - /* - * Readl does le32_to_cpu, so we need to undo that - */ - flswdata = cpu_to_le32(flswdata); - memcpy(data, &flswdata, chunk); - - data += chunk; - residue -= chunk; - } while (residue); - - return E1000_SUCCESS; -} - -static int e1000_flash_mode_write_chunk(struct e1000_hw *hw, loff_t offset, - size_t size, const void *data) -{ - int ret; - size_t chunk, residue = size; - uint32_t flswdata; - - if (size > 256 || - E1000_FLSWCTL_ADDR(offset) != offset) - return -EINVAL; - - ret = e1000_flash_mode_wait_for_idle(hw); - if (ret < 0) - return ret; - - - e1000_write_reg(hw, E1000_FLSWCNT, size); - e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_WRITE, offset); - - do { - chunk = min(sizeof(flswdata), residue); - memcpy(&flswdata, data, chunk); - /* - * writel does cpu_to_le32, so we do the inverse in - * order to account for that - */ - flswdata = le32_to_cpu(flswdata); - e1000_write_reg(hw, E1000_FLSWDATA, flswdata); - - ret = e1000_flash_mode_check_command_valid(hw); - if (ret < 0) - return -EIO; - - ret = e1000_poll_reg(hw, E1000_FLSWCTL, - E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE, - SECOND); - if (ret < 0) { - dev_err(hw->dev, - "Timeout waiting for FLSWCTL.DONE to be set (write)\n"); - return ret; - } - - data += chunk; - residue -= chunk; - - } while (residue); - - return E1000_SUCCESS; -} - - -static int e1000_flash_mode_erase_chunk(struct e1000_hw *hw, loff_t offset, - size_t size) -{ - int ret; - - ret = e1000_flash_mode_wait_for_idle(hw); - if (ret < 0) - return ret; - - if (!size && !offset) - e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_ERASE_DEVICE, 0); - else - e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_ERASE_SECTOR, offset); - - ret = e1000_flash_mode_check_command_valid(hw); - if (ret < 0) - return -EIO; - - ret = e1000_poll_reg(hw, E1000_FLSWCTL, - E1000_FLSWCTL_DONE | E1000_FLSWCTL_FLBUSY, - E1000_FLSWCTL_DONE, - 40 * SECOND); - if (ret < 0) { - dev_err(hw->dev, - "Timeout waiting for FLSWCTL.DONE to be set (erase)\n"); - return ret; - } - - return E1000_SUCCESS; -} - -enum { - E1000_FLASH_MODE_OP_READ = 0, - E1000_FLASH_MODE_OP_WRITE = 1, - E1000_FLASH_MODE_OP_ERASE = 2, -}; - - -static int e1000_flash_mode_io(struct e1000_hw *hw, int op, size_t granularity, - loff_t offset, size_t size, void *data) -{ - int ret; - size_t residue = size; - - do { - const size_t chunk = min(granularity, residue); - - switch (op) { - case E1000_FLASH_MODE_OP_READ: - ret = e1000_flash_mode_read_chunk(hw, offset, - chunk, data); - break; - case E1000_FLASH_MODE_OP_WRITE: - ret = e1000_flash_mode_write_chunk(hw, offset, - chunk, data); - break; - case E1000_FLASH_MODE_OP_ERASE: - ret = e1000_flash_mode_erase_chunk(hw, offset, - chunk); - break; - default: - return -ENOTSUPP; - } - - if (ret < 0) - return ret; - - offset += chunk; - residue -= chunk; - data += chunk; - } while (residue); - - return E1000_SUCCESS; -} - - -static int e1000_flash_mode_read(struct e1000_hw *hw, loff_t offset, - size_t size, void *data) -{ - return e1000_flash_mode_io(hw, - E1000_FLASH_MODE_OP_READ, SZ_4K, - offset, size, data); -} - -static int e1000_flash_mode_write(struct e1000_hw *hw, loff_t offset, - size_t size, const void *data) -{ - int ret; - - ret = e1000_flash_mode_io(hw, - E1000_FLASH_MODE_OP_WRITE, 256, - offset, size, (void *)data); - if (ret < 0) - return ret; - - ret = e1000_poll_reg(hw, E1000_FLSWCTL, - E1000_FLSWCTL_FLBUSY, - 0, SECOND); - if (ret < 0) - dev_err(hw->dev, "Timout while waiting for FLSWCTL.FLBUSY\n"); - - return ret; -} - -static int e1000_flash_mode_erase(struct e1000_hw *hw, loff_t offset, - size_t size) -{ - return e1000_flash_mode_io(hw, - E1000_FLASH_MODE_OP_ERASE, SZ_4K, - offset, size, NULL); -} - - /****************************************************************************** * Reads a 16 bit word from the EEPROM. * @@ -1043,7 +800,7 @@ int e1000_validate_eeprom_checksum(struct e1000_hw *hw) /* Read the EEPROM */ if (e1000_read_eeprom(hw, 0, EEPROM_CHECKSUM_REG + 1, buf) < 0) { - dev_err(&hw->edev.dev, "Unable to read EEPROM!\n"); + dev_err(hw->dev, "Unable to read EEPROM!\n"); return -E1000_ERR_EEPROM; } @@ -1059,495 +816,13 @@ int e1000_validate_eeprom_checksum(struct e1000_hw *hw) return 0; /* Hrm, verification failed, print an error */ - dev_err(&hw->edev.dev, "EEPROM checksum is incorrect!\n"); - dev_err(&hw->edev.dev, " ...register was 0x%04hx, calculated 0x%04hx\n", + dev_err(hw->dev, "EEPROM checksum is incorrect!\n"); + dev_err(hw->dev, " ...register was 0x%04hx, calculated 0x%04hx\n", checksum_reg, checksum); return -E1000_ERR_EEPROM; } -static ssize_t e1000_invm_cdev_read(struct cdev *cdev, void *buf, - size_t count, loff_t offset, unsigned long flags) -{ - uint8_t n, bnr; - uint32_t line; - size_t chunk, residue = count; - struct e1000_hw *hw = container_of(cdev, struct e1000_hw, invm.cdev); - - n = offset / sizeof(line); - if (n > E1000_INVM_DATA_MAX_N) - return -EINVAL; - - bnr = offset % sizeof(line); - if (bnr) { - /* - * if bnr in not zero it means we have a non 4-byte - * aligned start and need to do a partial read - */ - const uint8_t *bptr; - - bptr = (uint8_t *)&line + bnr; - chunk = min(bnr - sizeof(line), count); - line = e1000_read_reg(hw, E1000_INVM_DATA(n)); - line = cpu_to_le32(line); /* to account for readl */ - memcpy(buf, bptr, chunk); - - goto start_adjusted; - } - - do { - if (n > E1000_INVM_DATA_MAX_N) - return -EINVAL; - - chunk = min(sizeof(line), residue); - line = e1000_read_reg(hw, E1000_INVM_DATA(n)); - line = cpu_to_le32(line); /* to account for readl */ - - /* - * by using memcpy in conjunction with min should get - * dangling tail reads as well as aligned reads - */ - memcpy(buf, &line, chunk); - - start_adjusted: - residue -= chunk; - buf += chunk; - n++; - } while (residue); - - return count; -} - -static int e1000_invm_program(struct e1000_hw *hw, u32 offset, u32 value, - unsigned int delay) -{ - int retries = 400; - do { - if ((e1000_read_reg(hw, offset) & value) == value) - return E1000_SUCCESS; - - e1000_write_reg(hw, offset, value); - - if (delay) { - udelay(delay); - } else { - int ret; - - if (e1000_read_reg(hw, E1000_INVM_PROTECT) & - E1000_INVM_PROTECT_WRITE_ERROR) { - dev_err(hw->dev, "Error while writing to %x\n", offset); - return -EIO; - } - - ret = e1000_poll_reg(hw, E1000_INVM_PROTECT, - E1000_INVM_PROTECT_BUSY, - 0, SECOND); - if (ret < 0) { - dev_err(hw->dev, - "Timeout while waiting for INVM_PROTECT.BUSY\n"); - return ret; - } - } - } while (retries--); - - return -ETIMEDOUT; -} - -static int e1000_invm_set_lock(struct param_d *param, void *priv) -{ - struct e1000_hw *hw = priv; - - if (hw->invm.line > 31) - return -EINVAL; - - return e1000_invm_program(hw, - E1000_INVM_LOCK(hw->invm.line), - E1000_INVM_LOCK_BIT, - 10); -} - -static int e1000_invm_unlock(struct e1000_hw *hw) -{ - e1000_write_reg(hw, E1000_INVM_PROTECT, E1000_INVM_PROTECT_CODE); - /* - * If we were successful at unlocking iNVM for programming we - * should see ALLOW_WRITE bit toggle to 1 - */ - if (!(e1000_read_reg(hw, E1000_INVM_PROTECT) & - E1000_INVM_PROTECT_ALLOW_WRITE)) - return -EIO; - else - return E1000_SUCCESS; -} - -static void e1000_invm_lock(struct e1000_hw *hw) -{ - e1000_write_reg(hw, E1000_INVM_PROTECT, 0); -} - -static int e1000_invm_write_prepare(struct e1000_hw *hw) -{ - int ret; - /* - * This needs to be done accorging to the datasheet p. 541 and - * p. 79 - */ - e1000_write_reg(hw, E1000_PCIEMISC, - E1000_PCIEMISC_RESERVED_PATTERN1 | - E1000_PCIEMISC_DMA_IDLE | - E1000_PCIEMISC_RESERVED_PATTERN2); - - /* - * Needed for programming iNVM on devices with Flash with valid - * contents attached - */ - ret = e1000_poll_reg(hw, E1000_EEMNGCTL, - E1000_EEMNGCTL_CFG_DONE, - E1000_EEMNGCTL_CFG_DONE, SECOND); - if (ret < 0) { - dev_err(hw->dev, - "Timeout while waiting for EEMNGCTL.CFG_DONE\n"); - return ret; - } - - udelay(15); - - return E1000_SUCCESS; -} - -static ssize_t e1000_invm_cdev_write(struct cdev *cdev, const void *buf, - size_t count, loff_t offset, unsigned long flags) -{ - int ret; - uint8_t n, bnr; - uint32_t line; - size_t chunk, residue = count; - struct e1000_hw *hw = container_of(cdev, struct e1000_hw, invm.cdev); - - ret = e1000_invm_write_prepare(hw); - if (ret < 0) - return ret; - - ret = e1000_invm_unlock(hw); - if (ret < 0) - goto exit; - - n = offset / sizeof(line); - if (n > E1000_INVM_DATA_MAX_N) { - ret = -EINVAL; - goto exit; - } - - bnr = offset % sizeof(line); - if (bnr) { - uint8_t *bptr; - /* - * if bnr in not zero it means we have a non 4-byte - * aligned start and need to do a read-modify-write - * sequence - */ - - /* Read */ - line = e1000_read_reg(hw, E1000_INVM_DATA(n)); - - /* Modify */ - /* - * We need to ensure that line is LE32 in order for - * memcpy to copy byte from least significant to most - * significant, since that's how i210 will write the - * 32-bit word out to OTP - */ - line = cpu_to_le32(line); - bptr = (uint8_t *)&line + bnr; - chunk = min(sizeof(line) - bnr, count); - memcpy(bptr, buf, chunk); - line = le32_to_cpu(line); - - /* Jumping inside of the loop to take care of the - * Write */ - goto start_adjusted; - } - - do { - if (n > E1000_INVM_DATA_MAX_N) { - ret = -EINVAL; - goto exit; - } - - chunk = min(sizeof(line), residue); - if (chunk != sizeof(line)) { - /* - * If chunk is smaller that sizeof(line), which - * should be 4 bytes, we have a "dangling" - * chunk and we should read the unchanged - * portion of the 4-byte word from iNVM and do - * a read-modify-write sequence - */ - line = e1000_read_reg(hw, E1000_INVM_DATA(n)); - } - - line = cpu_to_le32(line); - memcpy(&line, buf, chunk); - line = le32_to_cpu(line); - - start_adjusted: - /* - * iNVM is organized in 32 64-bit lines and each of - * those lines can be locked to prevent any further - * modification, so for every i-th 32-bit word we need - * to check INVM_LINE[i/2] register to see if that word - * can be modified - */ - if (e1000_read_reg(hw, E1000_INVM_LOCK(n / 2)) & - E1000_INVM_LOCK_BIT) { - dev_err(hw->dev, "line %d is locked\n", n / 2); - ret = -EIO; - goto exit; - } - - ret = e1000_invm_program(hw, - E1000_INVM_DATA(n), - line, - 0); - if (ret < 0) - goto exit; - - residue -= chunk; - buf += chunk; - n++; - } while (residue); - - ret = E1000_SUCCESS; -exit: - e1000_invm_lock(hw); - return ret; -} - -static struct cdev_operations e1000_invm_ops = { - .read = e1000_invm_cdev_read, - .write = e1000_invm_cdev_write, -}; - -static ssize_t e1000_eeprom_cdev_read(struct cdev *cdev, void *buf, - size_t count, loff_t offset, unsigned long flags) -{ - struct e1000_hw *hw = container_of(cdev, struct e1000_hw, eepromcdev); - int32_t ret; - - /* - * The eeprom interface works on 16 bit words which gives a nice excuse - * for being lazy and not implementing unaligned reads. - */ - if (offset & 1 || count == 1) - return -EIO; - - ret = e1000_read_eeprom(hw, offset / 2, count / 2, buf); - if (ret) - return -EIO; - else - return (count / 2) * 2; -}; - -static struct cdev_operations e1000_eeprom_ops = { - .read = e1000_eeprom_cdev_read, -}; - -static int e1000_mtd_read_or_write(bool read, - struct mtd_info *mtd, loff_t off, size_t len, - size_t *retlen, u_char *buf) -{ - int ret; - struct e1000_hw *hw = container_of(mtd, struct e1000_hw, mtd); - - DEBUGFUNC(); - - if (e1000_acquire_eeprom(hw) == E1000_SUCCESS) { - if (read) - ret = e1000_flash_mode_read(hw, off, - len, buf); - else - ret = e1000_flash_mode_write(hw, off, - len, buf); - if (ret == E1000_SUCCESS) - *retlen = len; - - e1000_release_eeprom(hw); - } else { - ret = -E1000_ERR_EEPROM; - } - - return ret; - -} - -static int e1000_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - return e1000_mtd_read_or_write(true, - mtd, from, len, retlen, buf); -} - -static int e1000_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - return e1000_mtd_read_or_write(false, - mtd, to, len, retlen, (u_char *)buf); -} - -static int e1000_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - uint32_t rem; - struct e1000_hw *hw = container_of(mtd, struct e1000_hw, mtd); - int ret; - - div_u64_rem(instr->len, mtd->erasesize, &rem); - if (rem) - return -EINVAL; - - ret = e1000_acquire_eeprom(hw); - if (ret != E1000_SUCCESS) - goto fail; - - /* - * If mtd->size is 4096 it means we are dealing with - * unprogrammed flash and we don't really know its size to - * make an informed decision wheither to erase the whole chip or - * just a number of its sectors - */ - if (mtd->size > SZ_4K && - instr->len == mtd->size) - ret = e1000_flash_mode_erase(hw, 0, 0); - else - ret = e1000_flash_mode_erase(hw, - instr->addr, instr->len); - - e1000_release_eeprom(hw); - - if (ret < 0) - goto fail; - - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - - return 0; - -fail: - instr->state = MTD_ERASE_FAILED; - return ret; -} - -static int e1000_mtd_sr_rmw(struct mtd_info *mtd, u8 mask, u8 val) -{ - struct e1000_hw *hw = container_of(mtd, struct e1000_hw, mtd); - uint32_t flswdata; - int ret; - - ret = e1000_flash_mode_wait_for_idle(hw); - if (ret < 0) - return ret; - - e1000_write_reg(hw, E1000_FLSWCNT, 1); - e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_RDSR, 0); - - ret = e1000_flash_mode_check_command_valid(hw); - if (ret < 0) - return -EIO; - - ret = e1000_poll_reg(hw, E1000_FLSWCTL, - E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE, - SECOND); - if (ret < 0) { - dev_err(hw->dev, - "Timeout waiting for FLSWCTL.DONE to be set (RDSR)\n"); - return ret; - } - - flswdata = e1000_read_reg(hw, E1000_FLSWDATA); - - flswdata = (flswdata & ~mask) | val; - - e1000_write_reg(hw, E1000_FLSWCNT, 1); - e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_WRSR, 0); - - ret = e1000_flash_mode_check_command_valid(hw); - if (ret < 0) - return -EIO; - - e1000_write_reg(hw, E1000_FLSWDATA, flswdata); - - ret = e1000_poll_reg(hw, E1000_FLSWCTL, - E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE, - SECOND); - if (ret < 0) { - dev_err(hw->dev, - "Timeout waiting for FLSWCTL.DONE to be set (WRSR)\n"); - } - - return ret; -} - -/* - * The available spi nor devices are very different in how the block protection - * bits affect which sectors to be protected. So take the simple approach and - * only use BP[012] = b000 (unprotected) and BP[012] = b111 (protected). - */ -#define SR_BPALL (SR_BP0 | SR_BP1 | SR_BP2) - -static int e1000_mtd_lock(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - return e1000_mtd_sr_rmw(mtd, SR_BPALL, SR_BPALL); -} - -static int e1000_mtd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - return e1000_mtd_sr_rmw(mtd, SR_BPALL, 0x0); -} - -static int e1000_register_invm(struct e1000_hw *hw) -{ - int ret; - u16 word; - struct param_d *p; - - if (e1000_eeprom_valid(hw)) { - ret = e1000_read_eeprom(hw, 0x0a, 1, &word); - if (ret < 0) - return ret; - - if (word & (1 << 15)) - dev_warn(hw->dev, "iNVM lockout mechanism is active\n"); - } - - hw->invm.cdev.dev = hw->dev; - hw->invm.cdev.ops = &e1000_invm_ops; - hw->invm.cdev.priv = hw; - hw->invm.cdev.name = xasprintf("e1000-invm%d", hw->dev->id); - hw->invm.cdev.size = 4 * (E1000_INVM_DATA_MAX_N + 1); - - ret = devfs_create(&hw->invm.cdev); - if (ret < 0) - return ret; - - dev_set_name(&hw->invm.dev, "invm"); - hw->invm.dev.id = hw->dev->id; - hw->invm.dev.parent = hw->dev; - ret = register_device(&hw->invm.dev); - if (ret < 0) { - devfs_remove(&hw->invm.cdev); - return ret; - } - - p = dev_add_param_int(&hw->invm.dev, "lock", e1000_invm_set_lock, - NULL, &hw->invm.line, "%u", hw); - if (IS_ERR(p)) { - unregister_device(&hw->invm.dev); - devfs_remove(&hw->invm.cdev); - ret = PTR_ERR(p); - } - - return ret; -} - int e1000_eeprom_valid(struct e1000_hw *hw) { uint32_t valid_mask = E1000_EECD_FLASH_IN_USE | @@ -1566,95 +841,3 @@ int e1000_eeprom_valid(struct e1000_hw *hw) return 1; } - -/* - * This function has a wrong name for historic reasons, it doesn't add an - * eeprom, but the flash (if available) that is used to simulate the eeprom. - * Also a device that represents the invm is registered here (if available). - */ -int e1000_register_eeprom(struct e1000_hw *hw) -{ - struct e1000_eeprom_info *eeprom = &hw->eeprom; - uint32_t eecd; - int ret; - - if (hw->mac_type != e1000_igb) - return E1000_SUCCESS; - - eecd = e1000_read_reg(hw, E1000_EECD); - - if (eecd & E1000_EECD_AUTO_RD) { - if (eecd & E1000_EECD_EE_PRES) { - if (eecd & E1000_EECD_FLASH_IN_USE) { - uint32_t fla = e1000_read_reg(hw, E1000_FLA); - dev_info(hw->dev, - "Hardware programmed from flash (%ssecure)\n", - fla & E1000_FLA_LOCKED ? "" : "un"); - } else { - dev_info(hw->dev, "Hardware programmed from iNVM\n"); - } - } else { - dev_warn(hw->dev, "Shadow RAM invalid\n"); - } - } else { - /* - * I never saw this case in practise and I'm unsure how - * to handle that. Maybe just wait until the hardware is - * up enough that this bit is set? - */ - dev_err(hw->dev, "Flash Auto-Read not done\n"); - } - - if (e1000_eeprom_valid(hw)) { - hw->eepromcdev.dev = hw->dev; - hw->eepromcdev.ops = &e1000_eeprom_ops; - hw->eepromcdev.name = xasprintf("e1000-eeprom%d", - hw->dev->id); - hw->eepromcdev.size = 0x1000; - - ret = devfs_create(&hw->eepromcdev); - if (ret < 0) - return ret; - } - - if (eecd & E1000_EECD_I210_FLASH_DETECTED) { - hw->mtd.parent = hw->dev; - hw->mtd.read = e1000_mtd_read; - hw->mtd.write = e1000_mtd_write; - hw->mtd.erase = e1000_mtd_erase; - hw->mtd.lock = e1000_mtd_lock; - hw->mtd.unlock = e1000_mtd_unlock; - hw->mtd.size = eeprom->word_size * 2; - hw->mtd.writesize = 1; - hw->mtd.subpage_sft = 0; - - hw->mtd.eraseregions = xzalloc(sizeof(struct mtd_erase_region_info)); - hw->mtd.erasesize = SZ_4K; - hw->mtd.eraseregions[0].erasesize = SZ_4K; - hw->mtd.eraseregions[0].numblocks = hw->mtd.size / SZ_4K; - hw->mtd.numeraseregions = 1; - - hw->mtd.flags = MTD_CAP_NORFLASH; - hw->mtd.type = MTD_NORFLASH; - - ret = add_mtd_device(&hw->mtd, "e1000-nor", - DEVICE_ID_DYNAMIC); - if (ret) - goto out_eeprom; - } - - ret = e1000_register_invm(hw); - if (ret < 0) - goto out_mtd; - - return E1000_SUCCESS; - -out_mtd: - if (eecd & E1000_EECD_I210_FLASH_DETECTED) - del_mtd_device(&hw->mtd); -out_eeprom: - if (e1000_eeprom_valid(hw)) - devfs_remove(&hw->eepromcdev); - - return ret; -} diff --git a/drivers/net/e1000/main.c b/drivers/net/e1000/main.c index f67c5d867b..76acea563e 100644 --- a/drivers/net/e1000/main.c +++ b/drivers/net/e1000/main.c @@ -54,13 +54,17 @@ static int e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t *speed, uint16_t *duplex); static int e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy_data); +static int e1000_phy_write(struct mii_bus *bus, int phy_addr, int reg_addr, + u16 phy_data); static int e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t phy_data); static int32_t e1000_phy_hw_reset(struct e1000_hw *hw); static int e1000_phy_reset(struct e1000_hw *hw); static int e1000_detect_gig_phy(struct e1000_hw *hw); static void e1000_set_media_type(struct e1000_hw *hw); - +static void e1000_configure_tx(struct e1000_hw *hw); +static void e1000_configure_rx(struct e1000_hw *hw); +static void e1000_setup_rctl(struct e1000_hw *hw); static int32_t e1000_check_phy_reset_block(struct e1000_hw *hw); @@ -243,6 +247,10 @@ int32_t e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask) int32_t timeout = 200; DEBUGFUNC(); + + if (hw->mac_type <= e1000_82547_rev_2) + return E1000_SUCCESS; + while (timeout) { if (e1000_get_hw_eeprom_semaphore(hw)) return -E1000_ERR_SWFW_SYNC; @@ -274,6 +282,9 @@ int32_t e1000_swfw_sync_release(struct e1000_hw *hw, uint16_t mask) { uint32_t swfw_sync; + if (hw->mac_type <= e1000_82547_rev_2) + return E1000_SUCCESS; + if (e1000_get_hw_eeprom_semaphore(hw)) return -E1000_ERR_SWFW_SYNC; @@ -802,6 +813,10 @@ static int e1000_open(struct eth_device *edev) e1000_write_reg(hw, E1000_CTRL_EXT, ctrl_ext); } + e1000_configure_tx(hw); + e1000_configure_rx(hw); + e1000_setup_rctl(hw); + return 0; } @@ -2627,6 +2642,15 @@ static int e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, { int ret; + if ((hw->phy_type == e1000_phy_igp) && (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { + ret = e1000_phy_write(&hw->miibus, 1, IGP01E1000_PHY_PAGE_SELECT, + (u16)reg_addr); + if (ret) + return ret; + + reg_addr &= MAX_PHY_REG_ADDRESS; + } + ret = e1000_phy_read(&hw->miibus, 1, reg_addr); if (ret < 0) return ret; @@ -2702,6 +2726,17 @@ static int e1000_phy_write(struct mii_bus *bus, int phy_addr, ******************************************************************************/ static int e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t phy_data) { + int ret; + + if ((hw->phy_type == e1000_phy_igp) && (reg_addr > MAX_PHY_MULTI_PAGE_REG)) { + ret = e1000_phy_write(&hw->miibus, 1, IGP01E1000_PHY_PAGE_SELECT, + (u16)reg_addr); + if (ret) + return ret; + + reg_addr &= MAX_PHY_REG_ADDRESS; + } + return e1000_phy_write(&hw->miibus, 1, reg_addr, phy_data); } @@ -3191,7 +3226,7 @@ static int e1000_sw_init(struct eth_device *edev) /* identify the MAC */ result = e1000_set_mac_type(hw); if (result) { - dev_err(&hw->edev.dev, "Unknown MAC Type\n"); + dev_err(hw->dev, "Unknown MAC Type\n"); return result; } @@ -3232,7 +3267,7 @@ static void e1000_configure_tx(struct e1000_hw *hw) unsigned long tctl; unsigned long tipg, tarc; uint32_t ipgr1, ipgr2; - const unsigned long tx_base = (unsigned long)hw->tx_base; + const unsigned long tx_base = (unsigned long)hw->tx_base_phys; e1000_write_reg(hw, E1000_TDBAL, lower_32_bits(tx_base)); e1000_write_reg(hw, E1000_TDBAH, upper_32_bits(tx_base)); @@ -3351,7 +3386,7 @@ static void e1000_setup_rctl(struct e1000_hw *hw) static void e1000_configure_rx(struct e1000_hw *hw) { unsigned long rctl, ctrl_ext; - const unsigned long rx_base = (unsigned long)hw->rx_base; + const unsigned long rx_base = (unsigned long)hw->rx_base_phys; hw->rx_tail = 0; /* make sure receives are disabled while setting up the descriptors */ @@ -3403,12 +3438,12 @@ static int e1000_poll(struct eth_device *edev) if (readb(&rd->status) & E1000_RXD_STAT_DD) { const uint16_t len = readw(&rd->length); - dma_sync_single_for_cpu(hw->packet_dma, len, + dma_sync_single_for_cpu(hw->dev, hw->packet_dma, len, DMA_FROM_DEVICE); net_receive(edev, hw->packet, len); - dma_sync_single_for_device(hw->packet_dma, len, + dma_sync_single_for_device(hw->dev, hw->packet_dma, len, DMA_FROM_DEVICE); e1000_fill_rx(hw); return 1; @@ -3441,7 +3476,7 @@ static int e1000_transmit(struct eth_device *edev, void *txpacket, int length) ret = readl_poll_timeout(&txp->upper.data, stat, stat & E1000_TXD_STAT_DD, - MSECOND / USECOND); + USEC_PER_MSEC); if (ret) dev_dbg(hw->dev, "e1000: tx timeout\n"); @@ -3546,10 +3581,6 @@ static int e1000_init(struct eth_device *edev) if (hw->mac_type == e1000_igb) mdelay(15); - e1000_configure_tx(hw); - e1000_configure_rx(hw); - e1000_setup_rctl(hw); - return 0; } @@ -3564,8 +3595,8 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *id) hw = xzalloc(sizeof(*hw)); - hw->tx_base = dma_alloc_coherent(16 * sizeof(*hw->tx_base), DMA_ADDRESS_BROKEN); - hw->rx_base = dma_alloc_coherent(16 * sizeof(*hw->rx_base), DMA_ADDRESS_BROKEN); + hw->tx_base = dma_alloc_coherent(16 * sizeof(*hw->tx_base), &hw->tx_base_phys); + hw->rx_base = dma_alloc_coherent(16 * sizeof(*hw->rx_base), &hw->rx_base_phys); edev = &hw->edev; @@ -3575,9 +3606,6 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *id) edev->priv = hw; hw->packet = dma_alloc(PAGE_SIZE); - if (!hw->packet) - return -ENOMEM; - hw->packet_dma = dma_map_single(hw->dev, hw->packet, PAGE_SIZE, DMA_FROM_DEVICE); if (dma_mapping_error(hw->dev, hw->packet_dma)) diff --git a/drivers/net/e1000/mtd.c b/drivers/net/e1000/mtd.c new file mode 100644 index 0000000000..d472bd10a9 --- /dev/null +++ b/drivers/net/e1000/mtd.c @@ -0,0 +1,836 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <common.h> +#include <init.h> +#include <malloc.h> +#include <linux/math64.h> +#include <linux/sizes.h> +#include <of_device.h> +#include <linux/pci.h> +#include <linux/mtd/spi-nor.h> + +#include "e1000.h" + +static int32_t e1000_acquire_eeprom(struct e1000_hw *hw) +{ + if (hw->eeprom.acquire) + return hw->eeprom.acquire(hw); + else + return E1000_SUCCESS; +} + +static void e1000_release_eeprom(struct e1000_hw *hw) +{ + if (hw->eeprom.release) + hw->eeprom.release(hw); +} + +static int e1000_flash_mode_wait_for_idle(struct e1000_hw *hw) +{ + const int ret = e1000_poll_reg(hw, E1000_FLSWCTL, E1000_FLSWCTL_DONE, + E1000_FLSWCTL_DONE, SECOND); + if (ret < 0) + dev_err(hw->dev, + "Timeout waiting for FLSWCTL.DONE to be set (wait)\n"); + return ret; +} + +static int e1000_flash_mode_check_command_valid(struct e1000_hw *hw) +{ + const uint32_t flswctl = e1000_read_reg(hw, E1000_FLSWCTL); + if (!(flswctl & E1000_FLSWCTL_CMDV)) { + dev_err(hw->dev, "FLSWCTL.CMDV was cleared\n"); + return -EIO; + } + + return E1000_SUCCESS; +} + +static void e1000_flash_cmd(struct e1000_hw *hw, + uint32_t cmd, uint32_t offset) +{ + uint32_t flswctl = e1000_read_reg(hw, E1000_FLSWCTL); + flswctl &= ~E1000_FLSWCTL_CMD_ADDR_MASK; + flswctl |= E1000_FLSWCTL_CMD(cmd) | E1000_FLSWCTL_ADDR(offset); + e1000_write_reg(hw, E1000_FLSWCTL, flswctl); +} + +static int e1000_flash_mode_read_chunk(struct e1000_hw *hw, loff_t offset, + size_t size, void *data) +{ + int ret; + size_t chunk, residue = size; + uint32_t flswdata; + + DEBUGFUNC(); + + if (size > SZ_4K || + E1000_FLSWCTL_ADDR(offset) != offset) + return -EINVAL; + + ret = e1000_flash_mode_wait_for_idle(hw); + if (ret < 0) + return ret; + + e1000_write_reg(hw, E1000_FLSWCNT, size); + e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_READ, offset); + + do { + ret = e1000_flash_mode_check_command_valid(hw); + if (ret < 0) + return -EIO; + + chunk = min(sizeof(flswdata), residue); + + ret = e1000_poll_reg(hw, E1000_FLSWCTL, + E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE, + SECOND); + if (ret < 0) { + dev_err(hw->dev, + "Timeout waiting for FLSWCTL.DONE to be set (read)\n"); + return ret; + } + + flswdata = e1000_read_reg(hw, E1000_FLSWDATA); + /* + * Readl does le32_to_cpu, so we need to undo that + */ + flswdata = cpu_to_le32(flswdata); + memcpy(data, &flswdata, chunk); + + data += chunk; + residue -= chunk; + } while (residue); + + return E1000_SUCCESS; +} + +static int e1000_flash_mode_write_chunk(struct e1000_hw *hw, loff_t offset, + size_t size, const void *data) +{ + int ret; + size_t chunk, residue = size; + uint32_t flswdata; + + if (size > 256 || + E1000_FLSWCTL_ADDR(offset) != offset) + return -EINVAL; + + ret = e1000_flash_mode_wait_for_idle(hw); + if (ret < 0) + return ret; + + + e1000_write_reg(hw, E1000_FLSWCNT, size); + e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_WRITE, offset); + + do { + chunk = min(sizeof(flswdata), residue); + memcpy(&flswdata, data, chunk); + /* + * writel does cpu_to_le32, so we do the inverse in + * order to account for that + */ + flswdata = le32_to_cpu(flswdata); + e1000_write_reg(hw, E1000_FLSWDATA, flswdata); + + ret = e1000_flash_mode_check_command_valid(hw); + if (ret < 0) + return -EIO; + + ret = e1000_poll_reg(hw, E1000_FLSWCTL, + E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE, + SECOND); + if (ret < 0) { + dev_err(hw->dev, + "Timeout waiting for FLSWCTL.DONE to be set (write)\n"); + return ret; + } + + data += chunk; + residue -= chunk; + + } while (residue); + + return E1000_SUCCESS; +} + +static int e1000_flash_mode_erase_chunk(struct e1000_hw *hw, loff_t offset, + size_t size) +{ + int ret; + + ret = e1000_flash_mode_wait_for_idle(hw); + if (ret < 0) + return ret; + + if (!size && !offset) + e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_ERASE_DEVICE, 0); + else + e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_ERASE_SECTOR, offset); + + ret = e1000_flash_mode_check_command_valid(hw); + if (ret < 0) + return -EIO; + + ret = e1000_poll_reg(hw, E1000_FLSWCTL, + E1000_FLSWCTL_DONE | E1000_FLSWCTL_FLBUSY, + E1000_FLSWCTL_DONE, + 40 * SECOND); + if (ret < 0) { + dev_err(hw->dev, + "Timeout waiting for FLSWCTL.DONE to be set (erase)\n"); + return ret; + } + + return E1000_SUCCESS; +} + +enum { + E1000_FLASH_MODE_OP_READ = 0, + E1000_FLASH_MODE_OP_WRITE = 1, + E1000_FLASH_MODE_OP_ERASE = 2, +}; + + +static int e1000_flash_mode_io(struct e1000_hw *hw, int op, size_t granularity, + loff_t offset, size_t size, void *data) +{ + int ret; + size_t residue = size; + + do { + const size_t chunk = min(granularity, residue); + + switch (op) { + case E1000_FLASH_MODE_OP_READ: + ret = e1000_flash_mode_read_chunk(hw, offset, + chunk, data); + break; + case E1000_FLASH_MODE_OP_WRITE: + ret = e1000_flash_mode_write_chunk(hw, offset, + chunk, data); + break; + case E1000_FLASH_MODE_OP_ERASE: + ret = e1000_flash_mode_erase_chunk(hw, offset, + chunk); + break; + default: + return -ENOTSUPP; + } + + if (ret < 0) + return ret; + + offset += chunk; + residue -= chunk; + data += chunk; + } while (residue); + + return E1000_SUCCESS; +} + +static int e1000_flash_mode_read(struct e1000_hw *hw, loff_t offset, + size_t size, void *data) +{ + return e1000_flash_mode_io(hw, + E1000_FLASH_MODE_OP_READ, SZ_4K, + offset, size, data); +} + +static int e1000_flash_mode_write(struct e1000_hw *hw, loff_t offset, + size_t size, const void *data) +{ + int ret; + + ret = e1000_flash_mode_io(hw, + E1000_FLASH_MODE_OP_WRITE, 256, + offset, size, (void *)data); + if (ret < 0) + return ret; + + ret = e1000_poll_reg(hw, E1000_FLSWCTL, + E1000_FLSWCTL_FLBUSY, + 0, SECOND); + if (ret < 0) + dev_err(hw->dev, "Timout while waiting for FLSWCTL.FLBUSY\n"); + + return ret; +} + +static int e1000_flash_mode_erase(struct e1000_hw *hw, loff_t offset, + size_t size) +{ + return e1000_flash_mode_io(hw, + E1000_FLASH_MODE_OP_ERASE, SZ_4K, + offset, size, NULL); +} + +static ssize_t e1000_invm_cdev_read(struct cdev *cdev, void *buf, + size_t count, loff_t offset, unsigned long flags) +{ + uint8_t n, bnr; + uint32_t line; + size_t chunk, residue = count; + struct e1000_hw *hw = container_of(cdev, struct e1000_hw, invm.cdev); + + n = offset / sizeof(line); + if (n > E1000_INVM_DATA_MAX_N) + return -EINVAL; + + bnr = offset % sizeof(line); + if (bnr) { + /* + * if bnr in not zero it means we have a non 4-byte + * aligned start and need to do a partial read + */ + const uint8_t *bptr; + + bptr = (uint8_t *)&line + bnr; + chunk = min(bnr - sizeof(line), count); + line = e1000_read_reg(hw, E1000_INVM_DATA(n)); + line = cpu_to_le32(line); /* to account for readl */ + memcpy(buf, bptr, chunk); + + goto start_adjusted; + } + + do { + if (n > E1000_INVM_DATA_MAX_N) + return -EINVAL; + + chunk = min(sizeof(line), residue); + line = e1000_read_reg(hw, E1000_INVM_DATA(n)); + line = cpu_to_le32(line); /* to account for readl */ + + /* + * by using memcpy in conjunction with min should get + * dangling tail reads as well as aligned reads + */ + memcpy(buf, &line, chunk); + + start_adjusted: + residue -= chunk; + buf += chunk; + n++; + } while (residue); + + return count; +} + +static int e1000_invm_program(struct e1000_hw *hw, u32 offset, u32 value, + unsigned int delay) +{ + int retries = 400; + do { + if ((e1000_read_reg(hw, offset) & value) == value) + return E1000_SUCCESS; + + e1000_write_reg(hw, offset, value); + + if (delay) { + udelay(delay); + } else { + int ret; + + if (e1000_read_reg(hw, E1000_INVM_PROTECT) & + E1000_INVM_PROTECT_WRITE_ERROR) { + dev_err(hw->dev, "Error while writing to %x\n", offset); + return -EIO; + } + + ret = e1000_poll_reg(hw, E1000_INVM_PROTECT, + E1000_INVM_PROTECT_BUSY, + 0, SECOND); + if (ret < 0) { + dev_err(hw->dev, + "Timeout while waiting for INVM_PROTECT.BUSY\n"); + return ret; + } + } + } while (retries--); + + return -ETIMEDOUT; +} + +static int e1000_invm_set_lock(struct param_d *param, void *priv) +{ + struct e1000_hw *hw = priv; + + if (hw->invm.line > 31) + return -EINVAL; + + return e1000_invm_program(hw, + E1000_INVM_LOCK(hw->invm.line), + E1000_INVM_LOCK_BIT, + 10); +} + +static int e1000_invm_unlock(struct e1000_hw *hw) +{ + e1000_write_reg(hw, E1000_INVM_PROTECT, E1000_INVM_PROTECT_CODE); + /* + * If we were successful at unlocking iNVM for programming we + * should see ALLOW_WRITE bit toggle to 1 + */ + if (!(e1000_read_reg(hw, E1000_INVM_PROTECT) & + E1000_INVM_PROTECT_ALLOW_WRITE)) + return -EIO; + else + return E1000_SUCCESS; +} + +static void e1000_invm_lock(struct e1000_hw *hw) +{ + e1000_write_reg(hw, E1000_INVM_PROTECT, 0); +} + +static int e1000_invm_write_prepare(struct e1000_hw *hw) +{ + int ret; + /* + * This needs to be done accorging to the datasheet p. 541 and + * p. 79 + */ + e1000_write_reg(hw, E1000_PCIEMISC, + E1000_PCIEMISC_RESERVED_PATTERN1 | + E1000_PCIEMISC_DMA_IDLE | + E1000_PCIEMISC_RESERVED_PATTERN2); + + /* + * Needed for programming iNVM on devices with Flash with valid + * contents attached + */ + ret = e1000_poll_reg(hw, E1000_EEMNGCTL, + E1000_EEMNGCTL_CFG_DONE, + E1000_EEMNGCTL_CFG_DONE, SECOND); + if (ret < 0) { + dev_err(hw->dev, + "Timeout while waiting for EEMNGCTL.CFG_DONE\n"); + return ret; + } + + udelay(15); + + return E1000_SUCCESS; +} + +static ssize_t e1000_invm_cdev_write(struct cdev *cdev, const void *buf, + size_t count, loff_t offset, unsigned long flags) +{ + int ret; + uint8_t n, bnr; + uint32_t line; + size_t chunk, residue = count; + struct e1000_hw *hw = container_of(cdev, struct e1000_hw, invm.cdev); + + ret = e1000_invm_write_prepare(hw); + if (ret < 0) + return ret; + + ret = e1000_invm_unlock(hw); + if (ret < 0) + goto exit; + + n = offset / sizeof(line); + if (n > E1000_INVM_DATA_MAX_N) { + ret = -EINVAL; + goto exit; + } + + bnr = offset % sizeof(line); + if (bnr) { + uint8_t *bptr; + /* + * if bnr in not zero it means we have a non 4-byte + * aligned start and need to do a read-modify-write + * sequence + */ + + /* Read */ + line = e1000_read_reg(hw, E1000_INVM_DATA(n)); + + /* Modify */ + /* + * We need to ensure that line is LE32 in order for + * memcpy to copy byte from least significant to most + * significant, since that's how i210 will write the + * 32-bit word out to OTP + */ + line = cpu_to_le32(line); + bptr = (uint8_t *)&line + bnr; + chunk = min(sizeof(line) - bnr, count); + memcpy(bptr, buf, chunk); + line = le32_to_cpu(line); + + /* Jumping inside of the loop to take care of the + * Write */ + goto start_adjusted; + } + + do { + if (n > E1000_INVM_DATA_MAX_N) { + ret = -EINVAL; + goto exit; + } + + chunk = min(sizeof(line), residue); + if (chunk != sizeof(line)) { + /* + * If chunk is smaller that sizeof(line), which + * should be 4 bytes, we have a "dangling" + * chunk and we should read the unchanged + * portion of the 4-byte word from iNVM and do + * a read-modify-write sequence + */ + line = e1000_read_reg(hw, E1000_INVM_DATA(n)); + } + + line = cpu_to_le32(line); + memcpy(&line, buf, chunk); + line = le32_to_cpu(line); + + start_adjusted: + /* + * iNVM is organized in 32 64-bit lines and each of + * those lines can be locked to prevent any further + * modification, so for every i-th 32-bit word we need + * to check INVM_LINE[i/2] register to see if that word + * can be modified + */ + if (e1000_read_reg(hw, E1000_INVM_LOCK(n / 2)) & + E1000_INVM_LOCK_BIT) { + dev_err(hw->dev, "line %d is locked\n", n / 2); + ret = -EIO; + goto exit; + } + + ret = e1000_invm_program(hw, + E1000_INVM_DATA(n), + line, + 0); + if (ret < 0) + goto exit; + + residue -= chunk; + buf += chunk; + n++; + } while (residue); + + ret = E1000_SUCCESS; +exit: + e1000_invm_lock(hw); + return ret; +} + +static struct cdev_operations e1000_invm_ops = { + .read = e1000_invm_cdev_read, + .write = e1000_invm_cdev_write, +}; + +static ssize_t e1000_eeprom_cdev_read(struct cdev *cdev, void *buf, + size_t count, loff_t offset, unsigned long flags) +{ + struct e1000_hw *hw = container_of(cdev, struct e1000_hw, eepromcdev); + int32_t ret; + + /* + * The eeprom interface works on 16 bit words which gives a nice excuse + * for being lazy and not implementing unaligned reads. + */ + if (offset & 1 || count == 1) + return -EIO; + + ret = e1000_read_eeprom(hw, offset / 2, count / 2, buf); + if (ret) + return -EIO; + else + return (count / 2) * 2; +}; + +static struct cdev_operations e1000_eeprom_ops = { + .read = e1000_eeprom_cdev_read, +}; + +static int e1000_mtd_read_or_write(bool read, + struct mtd_info *mtd, loff_t off, size_t len, + size_t *retlen, u_char *buf) +{ + int ret; + struct e1000_hw *hw = container_of(mtd, struct e1000_hw, mtd); + + DEBUGFUNC(); + + if (e1000_acquire_eeprom(hw) == E1000_SUCCESS) { + if (read) + ret = e1000_flash_mode_read(hw, off, + len, buf); + else + ret = e1000_flash_mode_write(hw, off, + len, buf); + if (ret == E1000_SUCCESS) + *retlen = len; + + e1000_release_eeprom(hw); + } else { + ret = -E1000_ERR_EEPROM; + } + + return ret; + +} + +static int e1000_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + return e1000_mtd_read_or_write(true, + mtd, from, len, retlen, buf); +} + +static int e1000_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + return e1000_mtd_read_or_write(false, + mtd, to, len, retlen, (u_char *)buf); +} + +static int e1000_mtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + uint32_t rem; + struct e1000_hw *hw = container_of(mtd, struct e1000_hw, mtd); + int ret; + + div_u64_rem(instr->len, mtd->erasesize, &rem); + if (rem) + return -EINVAL; + + ret = e1000_acquire_eeprom(hw); + if (ret != E1000_SUCCESS) + goto fail; + + /* + * If mtd->size is 4096 it means we are dealing with + * unprogrammed flash and we don't really know its size to + * make an informed decision wheither to erase the whole chip or + * just a number of its sectors + */ + if (mtd->size > SZ_4K && + instr->len == mtd->size) + ret = e1000_flash_mode_erase(hw, 0, 0); + else + ret = e1000_flash_mode_erase(hw, + instr->addr, instr->len); + + e1000_release_eeprom(hw); + + if (ret < 0) + goto fail; + + return 0; + +fail: + return ret; +} + +static int e1000_mtd_sr_rmw(struct mtd_info *mtd, u8 mask, u8 val) +{ + struct e1000_hw *hw = container_of(mtd, struct e1000_hw, mtd); + uint32_t flswdata; + int ret; + + ret = e1000_flash_mode_wait_for_idle(hw); + if (ret < 0) + return ret; + + e1000_write_reg(hw, E1000_FLSWCNT, 1); + e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_RDSR, 0); + + ret = e1000_flash_mode_check_command_valid(hw); + if (ret < 0) + return -EIO; + + ret = e1000_poll_reg(hw, E1000_FLSWCTL, + E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE, + SECOND); + if (ret < 0) { + dev_err(hw->dev, + "Timeout waiting for FLSWCTL.DONE to be set (RDSR)\n"); + return ret; + } + + flswdata = e1000_read_reg(hw, E1000_FLSWDATA); + + flswdata = (flswdata & ~mask) | val; + + e1000_write_reg(hw, E1000_FLSWCNT, 1); + e1000_flash_cmd(hw, E1000_FLSWCTL_CMD_WRSR, 0); + + ret = e1000_flash_mode_check_command_valid(hw); + if (ret < 0) + return -EIO; + + e1000_write_reg(hw, E1000_FLSWDATA, flswdata); + + ret = e1000_poll_reg(hw, E1000_FLSWCTL, + E1000_FLSWCTL_DONE, E1000_FLSWCTL_DONE, + SECOND); + if (ret < 0) { + dev_err(hw->dev, + "Timeout waiting for FLSWCTL.DONE to be set (WRSR)\n"); + } + + return ret; +} + +/* + * The available spi nor devices are very different in how the block protection + * bits affect which sectors to be protected. So take the simple approach and + * only use BP[012] = b000 (unprotected) and BP[012] = b111 (protected). + */ +#define SR_BPALL (SR_BP0 | SR_BP1 | SR_BP2) + +static int e1000_mtd_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + return e1000_mtd_sr_rmw(mtd, SR_BPALL, SR_BPALL); +} + +static int e1000_mtd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + return e1000_mtd_sr_rmw(mtd, SR_BPALL, 0x0); +} + +static int e1000_register_invm(struct e1000_hw *hw) +{ + int ret; + u16 word; + struct param_d *p; + + if (e1000_eeprom_valid(hw)) { + ret = e1000_read_eeprom(hw, 0x0a, 1, &word); + if (ret < 0) + return ret; + + if (word & (1 << 15)) + dev_warn(hw->dev, "iNVM lockout mechanism is active\n"); + } + + hw->invm.cdev.dev = hw->dev; + hw->invm.cdev.ops = &e1000_invm_ops; + hw->invm.cdev.priv = hw; + hw->invm.cdev.name = xasprintf("e1000-invm%d", hw->dev->id); + hw->invm.cdev.size = 4 * (E1000_INVM_DATA_MAX_N + 1); + + ret = devfs_create(&hw->invm.cdev); + if (ret < 0) + return ret; + + dev_set_name(&hw->invm.dev, "invm"); + hw->invm.dev.id = hw->dev->id; + hw->invm.dev.parent = hw->dev; + ret = register_device(&hw->invm.dev); + if (ret < 0) { + devfs_remove(&hw->invm.cdev); + return ret; + } + + p = dev_add_param_int(&hw->invm.dev, "lock", e1000_invm_set_lock, + NULL, &hw->invm.line, "%u", hw); + if (IS_ERR(p)) { + unregister_device(&hw->invm.dev); + devfs_remove(&hw->invm.cdev); + ret = PTR_ERR(p); + } + + return ret; +} + +/* + * This function has a wrong name for historic reasons, it doesn't add an + * eeprom, but the flash (if available) that is used to simulate the eeprom. + * Also a device that represents the invm is registered here (if available). + */ +int e1000_register_eeprom(struct e1000_hw *hw) +{ + struct e1000_eeprom_info *eeprom = &hw->eeprom; + uint32_t eecd; + int ret; + + if (hw->mac_type != e1000_igb) + return E1000_SUCCESS; + + eecd = e1000_read_reg(hw, E1000_EECD); + + if (eecd & E1000_EECD_AUTO_RD) { + if (eecd & E1000_EECD_EE_PRES) { + if (eecd & E1000_EECD_FLASH_IN_USE) { + uint32_t fla = e1000_read_reg(hw, E1000_FLA); + dev_info(hw->dev, + "Hardware programmed from flash (%ssecure)\n", + fla & E1000_FLA_LOCKED ? "" : "un"); + } else { + dev_info(hw->dev, "Hardware programmed from iNVM\n"); + } + } else { + dev_warn(hw->dev, "Shadow RAM invalid\n"); + } + } else { + /* + * I never saw this case in practise and I'm unsure how + * to handle that. Maybe just wait until the hardware is + * up enough that this bit is set? + */ + dev_err(hw->dev, "Flash Auto-Read not done\n"); + } + + if (e1000_eeprom_valid(hw)) { + hw->eepromcdev.dev = hw->dev; + hw->eepromcdev.ops = &e1000_eeprom_ops; + hw->eepromcdev.name = xasprintf("e1000-eeprom%d", + hw->dev->id); + hw->eepromcdev.size = 0x1000; + + ret = devfs_create(&hw->eepromcdev); + if (ret < 0) + return ret; + } + + if (eecd & E1000_EECD_I210_FLASH_DETECTED) { + hw->mtd.dev.parent = hw->dev; + hw->mtd._read = e1000_mtd_read; + hw->mtd._write = e1000_mtd_write; + hw->mtd._erase = e1000_mtd_erase; + hw->mtd._lock = e1000_mtd_lock; + hw->mtd._unlock = e1000_mtd_unlock; + hw->mtd.size = eeprom->word_size * 2; + hw->mtd.writesize = 1; + hw->mtd.subpage_sft = 0; + + hw->mtd.eraseregions = xzalloc(sizeof(struct mtd_erase_region_info)); + hw->mtd.erasesize = SZ_4K; + hw->mtd.eraseregions[0].erasesize = SZ_4K; + hw->mtd.eraseregions[0].numblocks = hw->mtd.size / SZ_4K; + hw->mtd.numeraseregions = 1; + + hw->mtd.flags = MTD_CAP_NORFLASH; + hw->mtd.type = MTD_NORFLASH; + + ret = add_mtd_device(&hw->mtd, "e1000-nor", + DEVICE_ID_DYNAMIC); + if (ret) + goto out_eeprom; + } + + ret = e1000_register_invm(hw); + if (ret < 0) + goto out_mtd; + + return E1000_SUCCESS; + +out_mtd: + if (eecd & E1000_EECD_I210_FLASH_DETECTED) + del_mtd_device(&hw->mtd); +out_eeprom: + if (e1000_eeprom_valid(hw)) + devfs_remove(&hw->eepromcdev); + + return ret; +} diff --git a/drivers/net/e1000/regio.c b/drivers/net/e1000/regio.c index 5b2740fbc2..734c9a392e 100644 --- a/drivers/net/e1000/regio.c +++ b/drivers/net/e1000/regio.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include "e1000.h" diff --git a/drivers/net/efi-snp.c b/drivers/net/efi-snp.c index def2714bee..476015f1c2 100644 --- a/drivers/net/efi-snp.c +++ b/drivers/net/efi-snp.c @@ -11,7 +11,7 @@ #include <net.h> #include <init.h> #include <efi.h> -#include <efi/efi.h> +#include <efi/efi-payload.h> #include <efi/efi-device.h> struct efi_network_statistics { @@ -69,10 +69,10 @@ struct efi_simple_network_mode { uint32_t ReceiveFilterSetting; uint32_t MaxMCastFilterCount; uint32_t MCastFilterCount; - efi_mac_address MCastFilter[MAX_MCAST_FILTER_CNT]; - efi_mac_address CurrentAddress; - efi_mac_address BroadcastAddress; - efi_mac_address PermanentAddress; + struct efi_mac_address MCastFilter[MAX_MCAST_FILTER_CNT]; + struct efi_mac_address CurrentAddress; + struct efi_mac_address BroadcastAddress; + struct efi_mac_address PermanentAddress; uint8_t IfType; bool MacAddressChangeable; bool MultipleTxSupported; @@ -92,14 +92,14 @@ struct efi_simple_network { efi_status_t (EFIAPI *shutdown) (struct efi_simple_network *This); efi_status_t (EFIAPI *receive_filters) (struct efi_simple_network *This, uint32_t Enable, uint32_t Disable, bool ResetMCastFilter, - unsigned long MCastFilterCnt, efi_mac_address *MCastFilter); + unsigned long MCastFilterCnt, struct efi_mac_address *MCastFilter); efi_status_t (EFIAPI *station_address) (struct efi_simple_network *This, - bool Reset, efi_mac_address *New); + bool Reset, struct efi_mac_address *New); efi_status_t (EFIAPI *statistics) (struct efi_simple_network *This, bool Reset, unsigned long *StatisticsSize, struct efi_network_statistics *StatisticsTable); efi_status_t (EFIAPI *mcast_ip_to_mac) (struct efi_simple_network *This, - bool IPv6, efi_ip_address *IP, efi_mac_address *MAC); + bool IPv6, union efi_ip_address *IP, struct efi_mac_address *MAC); efi_status_t (EFIAPI *nvdata) (struct efi_simple_network *This, bool ReadWrite, unsigned long Offset, unsigned long BufferSize, void *Buffer); @@ -107,19 +107,20 @@ struct efi_simple_network { uint32_t *InterruptStatus, void **TxBuf); efi_status_t (EFIAPI *transmit) (struct efi_simple_network *This, unsigned long HeaderSize, unsigned long BufferSize, void *Buffer, - efi_mac_address *SrcAddr, efi_mac_address *DestAddr, + struct efi_mac_address *SrcAddr, struct efi_mac_address *DestAddr, uint16_t *Protocol); efi_status_t (EFIAPI *receive) (struct efi_simple_network *This, unsigned long *HeaderSize, unsigned long *BufferSize, void *Buffer, - efi_mac_address *SrcAddr, efi_mac_address *DestAddr, uint16_t *Protocol); + struct efi_mac_address *SrcAddr, struct efi_mac_address *DestAddr, uint16_t *Protocol); void *WaitForPacket; struct efi_simple_network_mode *Mode; }; struct efi_snp_priv { - struct device_d *dev; + struct device *dev; struct eth_device edev; struct efi_simple_network *snp; + void *rx_buf; }; static inline struct efi_snp_priv *to_priv(struct eth_device *edev) @@ -134,6 +135,9 @@ static int efi_snp_eth_send(struct eth_device *edev, void *packet, int length) void *txbuf; uint64_t start; + if (!priv->snp->Mode->MediaPresent) + return -ENOMEDIUM; + efiret = priv->snp->transmit(priv->snp, 0, length, packet, NULL, NULL, NULL); if (EFI_ERROR(efiret)) { dev_err(priv->dev, "failed to send: %s\n", efi_strerror(efiret)); @@ -160,7 +164,7 @@ static int efi_snp_eth_rx(struct eth_device *edev) long bufsize = PKTSIZE; efi_status_t efiret; - efiret = priv->snp->receive(priv->snp, NULL, &bufsize, NetRxPackets[0], NULL, NULL, NULL); + efiret = priv->snp->receive(priv->snp, NULL, &bufsize, priv->rx_buf, NULL, NULL, NULL); if (efiret == EFI_NOT_READY) return 0; @@ -169,11 +173,37 @@ static int efi_snp_eth_rx(struct eth_device *edev) return -efi_errno(efiret); } - net_receive(edev, NetRxPackets[0], bufsize); + net_receive(edev, priv->rx_buf, bufsize); return 0; } +static efi_guid_t snp_guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; + +static int efi_snp_open_exclusive(struct efi_device *efidev) +{ + void *interface; + efi_status_t efiret; + + /* + * Try to re-open SNP exlusively to close any active MNP protocol instance + * that may compete for packet polling + */ + efiret = BS->open_protocol(efidev->handle, &snp_guid, + &interface, efi_parent_image, NULL, EFI_OPEN_PROTOCOL_EXCLUSIVE); + if (EFI_ERROR(efiret)) { + dev_err(&efidev->dev, "failed to open exclusively: %s\n", efi_strerror(efiret)); + return -efi_errno(efiret); + } + + return 0; +} + +static void efi_snp_close_exclusive(struct efi_device *efidev) +{ + BS->close_protocol(efidev->handle, &snp_guid, efi_parent_image, NULL); +} + static int efi_snp_eth_open(struct eth_device *edev) { struct efi_snp_priv *priv = to_priv(edev); @@ -190,7 +220,7 @@ static int efi_snp_eth_open(struct eth_device *edev) } efiret = priv->snp->station_address(priv->snp, false, - (efi_mac_address *)priv->snp->Mode->PermanentAddress.Addr ); + (struct efi_mac_address *)priv->snp->Mode->PermanentAddress.Addr ); if (EFI_ERROR(efiret)) { dev_err(priv->dev, "failed to set MAC address: %s\n", efi_strerror(efiret)); @@ -231,6 +261,20 @@ static int efi_snp_set_ethaddr(struct eth_device *edev, const unsigned char *adr return 0; } +static int efi_snp_pause(struct efi_device *efidev) +{ + efi_snp_close_exclusive(efidev); + + return 0; +} + +static int efi_snp_continue(struct efi_device *efidev) +{ + efi_snp_open_exclusive(efidev); + + return 0; +} + static int efi_snp_probe(struct efi_device *efidev) { struct eth_device *edev; @@ -242,6 +286,7 @@ static int efi_snp_probe(struct efi_device *efidev) priv = xzalloc(sizeof(struct efi_snp_priv)); priv->snp = efidev->protocol; priv->dev = &efidev->dev; + priv->rx_buf = xmalloc(PKTSIZE); dev_dbg(&efidev->dev, "perm: %02x:%02x:%02x:%02x:%02x:%02x\n", priv->snp->Mode->PermanentAddress.Addr[0], @@ -269,16 +314,28 @@ static int efi_snp_probe(struct efi_device *efidev) edev->get_ethaddr = efi_snp_get_ethaddr; edev->set_ethaddr = efi_snp_set_ethaddr; + ret = efi_snp_open_exclusive(efidev); + if (ret) + return ret; + ret = eth_register(edev); return ret; } +static void efi_snp_remove(struct efi_device *efidev) +{ + efi_snp_close_exclusive(efidev); +} + static struct efi_driver efi_snp_driver = { - .driver = { + .driver = { .name = "efi-snp", }, - .probe = efi_snp_probe, + .probe = efi_snp_probe, + .remove = efi_snp_remove, + .dev_pause = efi_snp_pause, + .dev_continue = efi_snp_continue, .guid = EFI_SIMPLE_NETWORK_PROTOCOL_GUID, }; device_efi_driver(efi_snp_driver); diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c index 3628a88f7d..9455c6f5ea 100644 --- a/drivers/net/enc28j60.c +++ b/drivers/net/enc28j60.c @@ -46,6 +46,7 @@ struct enc28j60_net { /* store MAC address here while hardware is in the reset state */ u8 hwaddr[ETH_ALEN]; struct mii_bus miibus; + void *rx_buffer; }; /* @@ -273,7 +274,7 @@ static void enc28j60_mem_read(struct enc28j60_net *priv, { enc28j60_regw_write(priv, ERDPTL, addr); - if (IS_ENABLED(CONFIG_ENC28J60_WRITEVERIFY)) { + if (IS_ENABLED(CONFIG_DRIVER_NET_ENC28J60_WRITEVERIFY)) { u16 reg; reg = enc28j60_regw_read(priv, ERDPTL); if (reg != addr) @@ -293,7 +294,7 @@ enc28j60_packet_write(struct enc28j60_net *priv, int len, const u8 *data) /* Set the write pointer to start of transmit buffer area */ enc28j60_regw_write(priv, EWRPTL, TXSTART_INIT); - if (IS_ENABLED(CONFIG_ENC28J60_WRITEVERIFY)) { + if (IS_ENABLED(CONFIG_DRIVER_NET_ENC28J60_WRITEVERIFY)) { u16 reg; reg = enc28j60_regw_read(priv, EWRPTL); if (reg != TXSTART_INIT) @@ -670,7 +671,7 @@ static void enc28j60_hw_disable(struct enc28j60_net *priv) static inline void enc28j60_dump_rsv(struct enc28j60_net *priv, const char *msg, u16 pk_ptr, int len, u16 sts) { - struct device_d *dev = &priv->edev.dev; + struct device *dev = &priv->edev.dev; dev_dbg(dev, "%s - NextPk: 0x%04x - RSV:\n", msg, pk_ptr); @@ -710,7 +711,7 @@ static int enc28j60_eth_send(struct eth_device *edev, void *packet, enc28j60_packet_write(priv, packet_length, packet); /* readback and verify written data */ - if (IS_ENABLED(CONFIG_ENC28J60_WRITEVERIFY)) { + if (IS_ENABLED(CONFIG_DRIVER_NET_ENC28J60_WRITEVERIFY)) { int test_len, k; u8 test_buf[64]; /* limit the test to the first 64 bytes */ int okflag; @@ -793,9 +794,9 @@ static void enc28j60_hw_rx(struct eth_device *edev) /* copy the packet from the receive buffer */ enc28j60_mem_read(priv, rx_packet_start(priv->next_pk_ptr), - len, NetRxPackets[0]); + len, priv->rx_buffer); - net_receive(edev, NetRxPackets[0], len); + net_receive(edev, priv->rx_buffer, len); } /* @@ -808,7 +809,7 @@ static void enc28j60_hw_rx(struct eth_device *edev) enc28j60_regw_write(priv, ERXRDPTL, erxrdpt); - if (IS_ENABLED(CONFIG_ENC28J60_WRITEVERIFY)) { + if (IS_ENABLED(CONFIG_DRIVER_NET_ENC28J60_WRITEVERIFY)) { u16 reg; reg = enc28j60_regw_read(priv, ERXRDPTL); if (reg != erxrdpt) @@ -922,7 +923,7 @@ static void enc28j60_eth_halt(struct eth_device *edev) enc28j60_lowpower(priv, true); } -static int enc28j60_probe(struct device_d *dev) +static int enc28j60_probe(struct device *dev) { struct eth_device *edev; struct enc28j60_net *priv; @@ -931,6 +932,7 @@ static int enc28j60_probe(struct device_d *dev) priv = xzalloc(sizeof(*priv)); priv->spi = (struct spi_device *)dev->type_data; + priv->rx_buffer = net_alloc_packet(); edev = &priv->edev; edev->priv = priv; @@ -998,8 +1000,9 @@ static __maybe_unused struct of_device_id enc28j60_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, enc28j60_dt_ids); -static struct driver_d enc28j60_driver = { +static struct driver enc28j60_driver = { .name = DRV_NAME, .probe = enc28j60_probe, .of_compatible = DRV_OF_COMPAT(enc28j60_dt_ids), diff --git a/drivers/net/enc28j60_hw.h b/drivers/net/enc28j60_hw.h index 4c023c8aaa..1cacecb3be 100644 --- a/drivers/net/enc28j60_hw.h +++ b/drivers/net/enc28j60_hw.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * enc28j60_hw.h: EDTP FrameThrower style enc28j60 registers */ diff --git a/drivers/net/ep93xx.c b/drivers/net/ep93xx.c index f8b281093d..bd954e7a17 100644 --- a/drivers/net/ep93xx.c +++ b/drivers/net/ep93xx.c @@ -21,7 +21,7 @@ #include <malloc.h> #include <io.h> #include <linux/types.h> -#include <mach/ep93xx-regs.h> +#include <mach/ep93xx/ep93xx-regs.h> #include <linux/phy.h> #include <platform_data/eth-ep93xx.h> #include "ep93xx.h" @@ -62,7 +62,7 @@ static void dump_dev(struct eth_device *edev) printf(" rx_sq.end %p\n", priv->rx_sq.end); for (i = 0; i < NUMRXDESC; i++) - printf(" rx_buffer[%2.d] %p\n", i, NetRxPackets[i]); + printf(" rx_buffer[%2.d] %p\n", i, priv->rx_buffer[i]); printf(" tx_dq.base %p\n", priv->tx_dq.base); printf(" tx_dq.current %p\n", priv->tx_dq.current); @@ -258,7 +258,7 @@ static int ep93xx_eth_open(struct eth_device *edev) */ for (i = 0; i < NUMRXDESC; i++) { /* set buffer address */ - (priv->rx_dq.base + i)->word1 = (uint32_t)NetRxPackets[i]; + (priv->rx_dq.base + i)->word1 = (uint32_t)priv->rx_buffer[i]; /* set buffer length, clear buffer index and NSOF */ (priv->rx_dq.base + i)->word2 = EP93XX_MAX_PKT_SIZE; @@ -324,7 +324,7 @@ static int ep93xx_eth_rcv_packet(struct eth_device *edev) /* * We have a good frame. Extract the frame's length * from the current rx_status_queue entry, and copy - * the frame's data into NetRxPackets[] of the + * the frame's data into priv->rx_buffer of the * protocol stack. We track the total number of * bytes in the frame (nbytes_frame) which will be * used when we pass the data off to the protocol @@ -466,7 +466,7 @@ static int ep93xx_eth_set_ethaddr(struct eth_device *edev, return 0; } -static int ep93xx_eth_probe(struct device_d *dev) +static int ep93xx_eth_probe(struct device *dev) { struct ep93xx_eth_platform_data *pdata = (struct ep93xx_eth_platform_data *)dev->platform_data; struct eth_device *edev; @@ -532,6 +532,12 @@ static int ep93xx_eth_probe(struct device_d *dev) goto eth_probe_failed_3; } + ret = net_alloc_packets(priv->rx_buffer, NUMRXDESC); + if (ret) { + pr_err("net_alloc_packet() failed: rx_buffer"); + goto eth_probe_failed_4; + } + mdiobus_register(&priv->miibus); eth_register(edev); @@ -539,6 +545,10 @@ static int ep93xx_eth_probe(struct device_d *dev) goto eth_probe_done; +eth_probe_failed_4: + free(priv->rx_sq.base); + /* Fall through */ + eth_probe_failed_3: free(priv->rx_dq.base); /* Fall through */ @@ -650,7 +660,7 @@ static int ep93xx_phy_write(struct mii_bus *bus, int phy_addr, return 0; } -static struct driver_d ep93xx_eth_driver = { +static struct driver ep93xx_eth_driver = { .name = "ep93xx_eth", .probe = ep93xx_eth_probe, }; diff --git a/drivers/net/ethoc.c b/drivers/net/ethoc.c index f24120ce72..a31d3bb521 100644 --- a/drivers/net/ethoc.c +++ b/drivers/net/ethoc.c @@ -178,6 +178,8 @@ struct ethoc { u32 cur_rx; struct mii_bus miibus; + + void *rx_buffer[PKTBUFSRX]; }; /** @@ -266,7 +268,7 @@ static int ethoc_init_ring(struct ethoc *dev) if (i == dev->num_rx - 1) bd.stat |= RX_BD_WRAP; - bd.addr = (u32)NetRxPackets[i]; + bd.addr = (u32)dev->rx_buffer[i]; ethoc_write_bd(dev, dev->num_tx + i, &bd); flush_dcache_range(bd.addr, bd.addr + PKTSIZE); @@ -529,17 +531,23 @@ static int ethoc_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val) return 0; } -static int ethoc_probe(struct device_d *dev) +static int ethoc_probe(struct device *dev) { struct resource *iores; struct eth_device *edev; struct ethoc *priv; + int ret; edev = xzalloc(sizeof(struct eth_device) + sizeof(struct ethoc)); edev->priv = (struct ethoc *)(edev + 1); priv = edev->priv; + + ret = net_alloc_packets(priv->rx_buffer, ARRAY_SIZE(priv->rx_buffer)); + if (ret) + return ret; + iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); @@ -571,8 +579,9 @@ static struct of_device_id ethoc_dt_ids[] = { { .compatible = "opencores,ethoc", }, { } }; +MODULE_DEVICE_TABLE(of, ethoc_dt_ids); -static struct driver_d ethoc_driver = { +static struct driver ethoc_driver = { .name = "ethoc", .probe = ethoc_probe, .of_compatible = DRV_OF_COMPAT(ethoc_dt_ids), diff --git a/drivers/net/fec_imx.c b/drivers/net/fec_imx.c index 5ef1d4359e..75a6596282 100644 --- a/drivers/net/fec_imx.c +++ b/drivers/net/fec_imx.c @@ -25,6 +25,22 @@ #include "fec_imx.h" +static int fec_set_promisc(struct eth_device *edev, bool enable) +{ + struct fec_priv *fec = (struct fec_priv *)edev->priv; + u32 rcntl; + + rcntl = readl(fec->regs + FEC_R_CNTRL); + + if (enable) + rcntl |= FEC_R_CNTRL_PROMISC; + else + rcntl &= ~FEC_R_CNTRL_PROMISC; + + writel(rcntl, fec->regs + FEC_R_CNTRL); + + return 0; +} /* * MII-interface related functions @@ -257,10 +273,15 @@ static int fec_init(struct eth_device *dev) */ writel(0x00000000, fec->regs + FEC_IMASK); + rcntl = readl(fec->regs + FEC_R_CNTRL); + + /* Keep promisc setting */ + rcntl &= FEC_R_CNTRL_PROMISC; + /* * Set FEC-Lite receive control register(R_CNTRL): */ - rcntl = FEC_R_CNTRL_MAX_FL(1518); + rcntl |= FEC_R_CNTRL_MAX_FL(1518); rcntl |= FEC_R_CNTRL_MII_MODE; /* @@ -320,6 +341,10 @@ static int fec_init(struct eth_device *dev) /* size of each buffer */ writel(FEC_MAX_PKT_SIZE, fec->regs + FEC_EMRBR); + /* set rx and tx buffer descriptor base address */ + writel(virt_to_phys(fec->tbd_base), fec->regs + FEC_ETDSR); + writel(virt_to_phys(fec->rbd_base), fec->regs + FEC_ERDSR); + return 0; } @@ -359,6 +384,8 @@ static int fec_open(struct eth_device *edev) if (fec->phy_init) fec->phy_init(edev->phydev); + fec_init(edev); + /* * Initialize RxBD/TxBD rings */ @@ -514,6 +541,7 @@ static int fec_recv(struct eth_device *dev) * Check if any critical events have happened */ ievent = readl(fec->regs + FEC_IEVENT); + ievent &= ~FEC_IEVENT_MII; writel(ievent, fec->regs + FEC_IEVENT); if (ievent & FEC_IEVENT_BABT) { @@ -561,7 +589,7 @@ static int fec_recv(struct eth_device *dev) * fixup and net_receive below would get * proper data */ - dma_sync_single_for_cpu((unsigned long)frame, + dma_sync_single_for_cpu(fec->dev, (unsigned long)frame, data_length, DMA_FROM_DEVICE); if (fec_is_imx28(fec)) @@ -573,7 +601,7 @@ static int fec_recv(struct eth_device *dev) */ len = data_length - 4; net_receive(dev, frame, len); - dma_sync_single_for_device((unsigned long)frame, + dma_sync_single_for_device(fec->dev, (unsigned long)frame, data_length, DMA_FROM_DEVICE); } @@ -624,25 +652,25 @@ static void fec_free_receive_packets(struct fec_priv *fec, int count, int size) } #ifdef CONFIG_OFDEVICE -static int fec_probe_dt(struct device_d *dev, struct fec_priv *fec) +static int fec_probe_dt(struct device *dev, struct fec_priv *fec) { struct device_node *mdiobus; int ret; - ret = of_get_phy_mode(dev->device_node); + ret = of_get_phy_mode(dev->of_node); if (ret < 0) fec->interface = PHY_INTERFACE_MODE_MII; else fec->interface = ret; - mdiobus = of_get_child_by_name(dev->device_node, "mdio"); + mdiobus = of_get_child_by_name(dev->of_node, "mdio"); if (mdiobus) - fec->miibus.dev.device_node = mdiobus; + fec->miibus.dev.of_node = mdiobus; return 0; } #else -static int fec_probe_dt(struct device_d *dev, struct fec_priv *fec) +static int fec_probe_dt(struct device *dev, struct fec_priv *fec) { return -ENODEV; } @@ -704,10 +732,10 @@ static int fec_clk_get(struct fec_priv *fec) { int i, err = 0; static const char *clk_names[ARRAY_SIZE(fec->clk)] = { - "ipg", "ahb", "ptp" + "ipg", "ahb", }; static const char *opt_clk_names[ARRAY_SIZE(fec->opt_clk)] = { - "enet_clk_ref", "enet_out", + "enet_clk_ref", "enet_out", "ptp" }; for (i = 0; i < ARRAY_SIZE(fec->clk); i++) { @@ -715,7 +743,7 @@ static int fec_clk_get(struct fec_priv *fec) if (IS_ERR(fec->clk[i])) { err = PTR_ERR(fec->clk[i]); fec_clk_put(fec); - break; + return err; } } @@ -729,7 +757,7 @@ static int fec_clk_get(struct fec_priv *fec) return err; } -static int fec_probe(struct device_d *dev) +static int fec_probe(struct device *dev) { struct resource *iores; struct fec_platform_data *pdata = (struct fec_platform_data *)dev->platform_data; @@ -738,14 +766,17 @@ static int fec_probe(struct device_d *dev) void *base; int ret; enum fec_type type; + void const *type_v; int phy_reset; u32 msec = 1, phy_post_delay = 0; u32 reg; - ret = dev_get_drvdata(dev, (const void **)&type); + ret = dev_get_drvdata(dev, &type_v); if (ret) return ret; + type = (uintptr_t)(type_v); + fec = xzalloc(sizeof(*fec)); fec->type = type; fec->dev = dev; @@ -758,6 +789,7 @@ static int fec_probe(struct device_d *dev) edev->halt = fec_halt; edev->get_ethaddr = fec_get_hwaddr; edev->set_ethaddr = fec_set_hwaddr; + edev->set_promisc = fec_set_promisc; edev->parent = dev; dma_set_mask(dev, DMA_BIT_MASK(32)); @@ -793,10 +825,11 @@ static int fec_probe(struct device_d *dev) goto release_res; } - phy_reset = of_get_named_gpio(dev->device_node, "phy-reset-gpios", 0); + phy_reset = of_get_named_gpio(dev->of_node, "phy-reset-gpios", 0); if (gpio_is_valid(phy_reset)) { - of_property_read_u32(dev->device_node, "phy-reset-duration", &msec); - of_property_read_u32(dev->device_node, "phy-reset-post-delay", + of_property_read_u32(dev->of_node, "phy-reset-duration", + &msec); + of_property_read_u32(dev->of_node, "phy-reset-post-delay", &phy_post_delay); /* valid reset duration should be less than 1s */ if (phy_post_delay > 1000) @@ -824,6 +857,8 @@ static int fec_probe(struct device_d *dev) if (ret) goto free_gpio; + fec_set_promisc(edev, false); + /* * reserve memory for both buffer descriptor chains at once * Datasheet forces the startaddress of each chain is 16 byte aligned @@ -835,14 +870,11 @@ static int fec_probe(struct device_d *dev) base += FEC_RBD_NUM * sizeof(struct buffer_descriptor); fec->tbd_base = base; - writel(virt_to_phys(fec->tbd_base), fec->regs + FEC_ETDSR); - writel(virt_to_phys(fec->rbd_base), fec->regs + FEC_ERDSR); - ret = fec_alloc_receive_packets(fec, FEC_RBD_NUM, FEC_MAX_PKT_SIZE); if (ret < 0) goto free_xbd; - if (dev->device_node) { + if (dev->of_node) { ret = fec_probe_dt(dev, fec); fec->phy_addr = -1; } else if (pdata) { @@ -857,26 +889,24 @@ static int fec_probe(struct device_d *dev) if (ret) goto free_receive_packets; - fec_init(edev); - fec->miibus.read = fec_miibus_read; fec->miibus.write = fec_miibus_write; fec->miibus.priv = fec; fec->miibus.parent = dev; - ret = mdiobus_register(&fec->miibus); + ret = eth_register(edev); if (ret) goto free_receive_packets; - ret = eth_register(edev); + ret = mdiobus_register(&fec->miibus); if (ret) - goto unregister_mdio; + goto unregister_eth; return 0; -unregister_mdio: - mdiobus_unregister(&fec->miibus); +unregister_eth: + eth_unregister(edev); free_receive_packets: fec_free_receive_packets(fec, FEC_RBD_NUM, FEC_MAX_PKT_SIZE); free_xbd: @@ -898,7 +928,7 @@ err_free: return ret; } -static void fec_remove(struct device_d *dev) +static void fec_remove(struct device *dev) { struct fec_priv *fec = dev->priv; @@ -922,12 +952,16 @@ static __maybe_unused struct of_device_id imx_fec_dt_ids[] = { .compatible = "fsl,imx6sx-fec", .data = (void *)FEC_TYPE_IMX6, }, { + .compatible = "fsl,imx8mp-fec", + .data = (void *)FEC_TYPE_IMX6, + }, { .compatible = "fsl,mvf600-fec", .data = (void *)FEC_TYPE_IMX6, }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx_fec_dt_ids); static struct platform_device_id imx_fec_ids[] = { { @@ -947,7 +981,7 @@ static struct platform_device_id imx_fec_ids[] = { /** * Driver description for registering */ -static struct driver_d fec_driver = { +static struct driver fec_driver = { .name = "fec_imx", .probe = fec_probe, .remove = fec_remove, diff --git a/drivers/net/fec_imx.h b/drivers/net/fec_imx.h index d1ac92f0e3..1aaff87fdd 100644 --- a/drivers/net/fec_imx.h +++ b/drivers/net/fec_imx.h @@ -58,6 +58,7 @@ #define FEC_R_CNTRL_RMII_10T (1 << 9) /* i.MX28 specific */ #define FEC_R_CNTRL_RMII_MODE (1 << 8) /* i.MX28 specific */ #define FEC_R_CNTRL_FCE (1 << 5) +#define FEC_R_CNTRL_PROMISC (1 << 3) #define FEC_R_CNTRL_MII_MODE (1 << 2) #define FEC_IEVENT_HBERR 0x80000000 /* Note: Not on i.MX28 */ @@ -121,7 +122,6 @@ enum fec_type { enum fec_clock { FEC_CLK_IPG, FEC_CLK_AHB, - FEC_CLK_PTP, FEC_CLK_NUM }; @@ -129,6 +129,7 @@ enum fec_clock { enum fec_opt_clock { FEC_OPT_CLK_REF, FEC_OPT_CLK_OUT, + FEC_OPT_CLK_PTP, FEC_OPT_CLK_NUM }; @@ -138,7 +139,7 @@ enum fec_opt_clock { */ struct fec_priv { struct eth_device edev; - struct device_d *dev; + struct device *dev; void __iomem *regs; struct buffer_descriptor __iomem *rbd_base; /* RBD ring */ int rbd_index; /* next receive BD to read */ diff --git a/drivers/net/fec_mpc5200.c b/drivers/net/fec_mpc5200.c index b47813aaed..9c9b795f14 100644 --- a/drivers/net/fec_mpc5200.c +++ b/drivers/net/fec_mpc5200.c @@ -420,7 +420,8 @@ static void mpc5xxx_fec_halt(struct eth_device *dev) /* * wait for graceful stop to register */ - while ((counter--) && (!(fec->eth->ievent & FEC_IEVENT_GRA))) ; + while ((counter--) && (!(fec->eth->ievent & FEC_IEVENT_GRA))) + ; /* * Disable SmartDMA tasks @@ -637,7 +638,7 @@ static int mpc5xxx_fec_recv(struct eth_device *dev) return len; } -int mpc5xxx_fec_probe(struct device_d *dev) +static int mpc5xxx_fec_probe(struct device *dev) { struct resource *iores; struct fec_platform_data *pdata = dev->platform_data; @@ -680,14 +681,14 @@ int mpc5xxx_fec_probe(struct device_d *dev) return 0; } -static void mpc5xxx_fec_remove(struct device_d *dev) +static void mpc5xxx_fec_remove(struct device *dev) { struct eth_device *edev = dev->priv; mpc5xxx_fec_halt(edev); } -static struct driver_d mpc5xxx_driver = { +static struct driver mpc5xxx_driver = { .name = "fec_mpc5xxx", .probe = mpc5xxx_fec_probe, .remove = mpc5xxx_fec_remove, diff --git a/drivers/net/fec_mpc5200.h b/drivers/net/fec_mpc5200.h index f07ae0c1a1..cbc2fec9c6 100644 --- a/drivers/net/fec_mpc5200.h +++ b/drivers/net/fec_mpc5200.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * (C) Copyright 2003 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. diff --git a/drivers/net/fsl-fman.c b/drivers/net/fsl-fman.c index 6d54fcc7c9..5262928480 100644 --- a/drivers/net/fsl-fman.c +++ b/drivers/net/fsl-fman.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0+ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright 2009-2012 Freescale Semiconductor, Inc. * Dave Liu <daveliu@freescale.com> @@ -101,7 +101,7 @@ struct fm_eth { struct fm_bmi_rx_port *rx_port; phy_interface_t enet_if; struct eth_device edev; - struct device_d *dev; + struct device *dev; struct fm_port_global_pram *rx_pram; /* Rx parameter table */ struct fm_port_global_pram *tx_pram; /* Tx parameter table */ struct fm_port_bd *rx_bd_ring; /* Rx BD ring base */ @@ -207,7 +207,7 @@ static int fm_upload_ucode(struct fm_imem *imem, return 0; } -static int fman_upload_firmware(struct device_d *dev, struct fm_imem *fm_imem) +static int fman_upload_firmware(struct device *dev, struct fm_imem *fm_imem) { int i, size, ret; const struct qe_firmware *firmware; @@ -398,7 +398,7 @@ static void fm_init_qmi(struct fm_qmi_common *qmi) out_be32(&qmi->fmqm_ie, FMQM_IE_CLEAR_ALL); } -static int fm_init_common(struct device_d *dev, struct ccsr_fman *reg) +static int fm_init_common(struct device *dev, struct ccsr_fman *reg) { int ret; @@ -583,7 +583,6 @@ static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth) void *rx_bd_ring_base; void *rx_buf_pool; u32 bd_ring_base_lo, bd_ring_base_hi; - u32 buf_lo, buf_hi; struct fm_port_bd *rxbd; struct fm_port_qd *rxqd; struct fm_bmi_rx_port *bmi_rx_port = fm_eth->rx_port; @@ -620,9 +619,7 @@ static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth) * RX_BD_RING_SIZE); /* alloc Rx buffer from main memory */ - rx_buf_pool = malloc(MAX_RXBUF_LEN * RX_BD_RING_SIZE); - if (!rx_buf_pool) - return -ENOMEM; + rx_buf_pool = dma_alloc(MAX_RXBUF_LEN * RX_BD_RING_SIZE); memset(rx_buf_pool, 0, MAX_RXBUF_LEN * RX_BD_RING_SIZE); @@ -633,18 +630,21 @@ static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth) /* init Rx BDs ring */ for (i = 0; i < RX_BD_RING_SIZE; i++) { + dma_addr_t dma; + rxbd = &fm_eth->rx_bd_ring[i]; muram_writew(&rxbd->status, RxBD_EMPTY); muram_writew(&rxbd->len, 0); - buf_hi = upper_32_bits(virt_to_phys(rx_buf_pool + - i * MAX_RXBUF_LEN)); - buf_lo = lower_32_bits(virt_to_phys(rx_buf_pool + - i * MAX_RXBUF_LEN)); - dma_sync_single_for_device((unsigned long)rx_buf_pool + i * MAX_RXBUF_LEN, - MAX_RXBUF_LEN, DMA_FROM_DEVICE); - muram_writew(&rxbd->buf_ptr_hi, (u16)buf_hi); - out_be32(&rxbd->buf_ptr_lo, buf_lo); + + dma = dma_map_single(fm_eth->dev, + rx_buf_pool + i * MAX_RXBUF_LEN, + MAX_RXBUF_LEN, DMA_FROM_DEVICE); + if (dma_mapping_error(fm_eth->dev, dma)) + return -EFAULT; + + muram_writew(&rxbd->buf_ptr_hi, (u16)upper_32_bits(dma)); + out_be32(&rxbd->buf_ptr_lo, lower_32_bits(dma)); } /* set the Rx queue descriptor */ @@ -911,13 +911,13 @@ static int fm_eth_recv(struct eth_device *edev) data = (u8 *)((unsigned long)(buf_hi << 16) << 16 | buf_lo); len = muram_readw(&rxbd->len); - dma_sync_single_for_cpu((unsigned long)data, + dma_sync_single_for_cpu(fm_eth->dev, (unsigned long)data, len, DMA_FROM_DEVICE); net_receive(edev, data, len); - dma_sync_single_for_device((unsigned long)data, + dma_sync_single_for_device(fm_eth->dev, (unsigned long)data, len, DMA_FROM_DEVICE); } else { @@ -1058,7 +1058,7 @@ static int fm_eth_startup(struct fm_eth *fm_eth) return 0; } -static int fsl_fman_mdio_probe(struct device_d *dev) +static int fsl_fman_mdio_probe(struct device *dev) { struct resource *iores; int ret; @@ -1087,7 +1087,7 @@ static int fsl_fman_mdio_probe(struct device_d *dev) return 0; } -static int fsl_fman_port_probe(struct device_d *dev) +static int fsl_fman_port_probe(struct device *dev) { struct resource *iores; int ret; @@ -1121,15 +1121,15 @@ static int fsl_fman_port_probe(struct device_d *dev) static int fsl_fman_memac_port_bind(struct fm_eth *fm_eth, enum fman_port_type type) { - struct device_node *macnp = fm_eth->dev->device_node; + struct device_node *macnp = fm_eth->dev->of_node; struct device_node *portnp; - struct device_d *portdev; + struct device *portdev; struct fsl_fman_port *port; portnp = of_parse_phandle(macnp, "fsl,fman-ports", type); if (!portnp) { - dev_err(fm_eth->dev, "of_parse_phandle(%s, fsl,fman-ports) failed\n", - macnp->full_name); + dev_err(fm_eth->dev, "of_parse_phandle(%pOF, fsl,fman-ports) failed\n", + macnp); return -EINVAL; } @@ -1149,7 +1149,7 @@ static int fsl_fman_memac_port_bind(struct fm_eth *fm_eth, enum fman_port_type t return 0; } -static int fsl_fman_memac_probe(struct device_d *dev) +static int fsl_fman_memac_probe(struct device *dev) { struct resource *iores; struct fm_eth *fm_eth; @@ -1178,7 +1178,7 @@ static int fsl_fman_memac_probe(struct device_d *dev) if (ret) return ret; - phy_mode = of_get_phy_mode(dev->device_node); + phy_mode = of_get_phy_mode(dev->of_node); if (phy_mode < 0) return phy_mode; @@ -1209,14 +1209,14 @@ static int fsl_fman_memac_probe(struct device_d *dev) return 0; } -static void fsl_fman_memac_remove(struct device_d *dev) +static void fsl_fman_memac_remove(struct device *dev) { struct fm_eth *fm_eth = dev->priv; fm_eth_halt(&fm_eth->edev); } -static int fsl_fman_muram_probe(struct device_d *dev) +static int fsl_fman_muram_probe(struct device *dev) { struct resource *iores; @@ -1240,8 +1240,9 @@ static struct of_device_id fsl_fman_mdio_dt_ids[] = { }, { } }; +MODULE_DEVICE_TABLE(of, fsl_fman_mdio_dt_ids); -static struct driver_d fman_mdio_driver = { +static struct driver fman_mdio_driver = { .name = "fsl-fman-mdio", .probe = fsl_fman_mdio_probe, .of_compatible = DRV_OF_COMPAT(fsl_fman_mdio_dt_ids), @@ -1257,8 +1258,9 @@ static struct of_device_id fsl_fman_port_dt_ids[] = { }, { } }; +MODULE_DEVICE_TABLE(of, fsl_fman_port_dt_ids); -static struct driver_d fman_port_driver = { +static struct driver fman_port_driver = { .name = "fsl-fman-port", .probe = fsl_fman_port_probe, .of_compatible = DRV_OF_COMPAT(fsl_fman_port_dt_ids), @@ -1270,8 +1272,9 @@ static struct of_device_id fsl_fman_memac_dt_ids[] = { }, { } }; +MODULE_DEVICE_TABLE(of, fsl_fman_memac_dt_ids); -static struct driver_d fman_memac_driver = { +static struct driver fman_memac_driver = { .name = "fsl-fman-memac", .probe = fsl_fman_memac_probe, .remove = fsl_fman_memac_remove, @@ -1284,14 +1287,15 @@ static struct of_device_id fsl_fman_muram_dt_ids[] = { }, { } }; +MODULE_DEVICE_TABLE(of, fsl_fman_muram_dt_ids); -static struct driver_d fman_muram_driver = { +static struct driver fman_muram_driver = { .name = "fsl-fman-muram", .probe = fsl_fman_muram_probe, .of_compatible = DRV_OF_COMPAT(fsl_fman_muram_dt_ids), }; -static int fsl_fman_probe(struct device_d *dev) +static int fsl_fman_probe(struct device *dev) { struct resource *iores; struct ccsr_fman *reg; @@ -1306,7 +1310,7 @@ static int fsl_fman_probe(struct device_d *dev) reg = IOMEM(iores->start); dev->priv = reg; - ret = of_platform_populate(dev->device_node, NULL, dev); + ret = of_platform_populate(dev->of_node, NULL, dev); if (ret) return ret; @@ -1328,8 +1332,9 @@ static struct of_device_id fsl_fman_dt_ids[] = { }, { } }; +MODULE_DEVICE_TABLE(of, fsl_fman_dt_ids); -static struct driver_d fman_driver = { +static struct driver fman_driver = { .name = "fsl-fman", .probe = fsl_fman_probe, .of_compatible = DRV_OF_COMPAT(fsl_fman_dt_ids), @@ -1342,7 +1347,12 @@ static int fman_of_fixup(struct device_node *root, void *context) struct device_node *child, *child_bb; fman_bb = of_find_compatible_node(NULL, NULL, "fsl,fman"); + if (!fman_bb) + return 0; + fman = of_find_compatible_node(root, NULL, "fsl,fman"); + if (!fman) + return 0; /* * The dts files in the Linux tree have all network interfaces diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c new file mode 100644 index 0000000000..4812ed4363 --- /dev/null +++ b/drivers/net/fsl_enetc.c @@ -0,0 +1,598 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ENETC ethernet controller driver + * Copyright 2017-2021 NXP + */ +#include <common.h> +#include <dma.h> +#include <net.h> +#include <linux/phy.h> +#include <linux/pci.h> +#include <io.h> +#include <linux/mdio.h> +#include <asm/system.h> +#include <of_net.h> +#include <asm/unaligned.h> + +#include "fsl_enetc.h" + +/* MDIO wrappers, we're using these to drive internal MDIO to get to serdes */ +static __maybe_unused int enetc_mdio_read(struct enetc_priv *priv, int addr, int devad, int reg) +{ + struct enetc_mdio_priv mdio_priv; + + mdio_priv.regs_base = priv->port_regs + ENETC_PM_IMDIO_BASE; + + return enetc_mdio_read_priv(&mdio_priv, addr, devad, reg); +} + +static int enetc_mdio_write(struct enetc_priv *priv, int addr, int devad, int reg, + u16 val) +{ + struct enetc_mdio_priv mdio_priv; + int ret; + + mdio_priv.regs_base = priv->port_regs + ENETC_PM_IMDIO_BASE; + + ret = enetc_mdio_write_priv(&mdio_priv, addr, devad, reg, val); + + return ret; +} + +/* only interfaces that can pin out through serdes have internal MDIO */ +static bool enetc_has_imdio(struct eth_device *edev) +{ + struct enetc_priv *priv = edev->priv; + + return !!(enetc_read_port(priv, ENETC_PCAPR0) & ENETC_PCAPRO_MDIO); +} + +/* set up serdes for SGMII */ +static int enetc_init_sgmii(struct eth_device *edev) +{ + struct enetc_priv *priv = edev->priv; + bool is2500 = false; + u16 reg; + + if (!enetc_has_imdio(edev)) + return 0; + + if (priv->uclass_id == PHY_INTERFACE_MODE_2500BASEX) + is2500 = true; + + /* + * Set to SGMII mode, for 1Gbps enable AN, for 2.5Gbps set fixed speed. + * Although fixed speed is 1Gbps, we could be running at 2.5Gbps based + * on PLL configuration. Setting 1G for 2.5G here is counter intuitive + * but intentional. + */ + reg = ENETC_PCS_IF_MODE_SGMII; + reg |= is2500 ? ENETC_PCS_IF_MODE_SPEED_1G : ENETC_PCS_IF_MODE_SGMII_AN; + enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_IF_MODE, reg); + + /* Dev ability - SGMII */ + enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SGMII); + + /* Adjust link timer for SGMII */ + enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL); + enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL); + + reg = ENETC_PCS_CR_DEF_VAL; + reg |= is2500 ? ENETC_PCS_CR_RST : ENETC_PCS_CR_RESET_AN; + /* restart PCS AN */ + enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_CR, reg); + + return 0; +} + +/* set up MAC for RGMII */ +static void enetc_init_rgmii(struct eth_device *edev, struct phy_device *phydev) +{ + struct enetc_priv *priv = edev->priv; + u32 old_val, val; + + old_val = val = enetc_read_port(priv, ENETC_PM_IF_MODE); + + /* disable unreliable RGMII in-band signaling and force the MAC into + * the speed negotiated by the PHY. + */ + val &= ~ENETC_PM_IF_MODE_AN_ENA; + + if (phydev->speed == SPEED_1000) { + val &= ~ENETC_PM_IFM_SSP_MASK; + val |= ENETC_PM_IFM_SSP_1000; + } else if (phydev->speed == SPEED_100) { + val &= ~ENETC_PM_IFM_SSP_MASK; + val |= ENETC_PM_IFM_SSP_100; + } else if (phydev->speed == SPEED_10) { + val &= ~ENETC_PM_IFM_SSP_MASK; + val |= ENETC_PM_IFM_SSP_10; + } + + if (phydev->duplex == DUPLEX_FULL) + val |= ENETC_PM_IFM_FULL_DPX; + else + val &= ~ENETC_PM_IFM_FULL_DPX; + + if (val == old_val) + return; + + enetc_write_port(priv, ENETC_PM_IF_MODE, val); +} + +/* set up MAC configuration for the given interface type */ +static void enetc_setup_mac_iface(struct eth_device *edev, + struct phy_device *phydev) +{ + struct enetc_priv *priv = edev->priv; + u32 if_mode; + + switch (priv->uclass_id) { + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + enetc_init_rgmii(edev, phydev); + break; + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10GBASER: + /* set ifmode to (US)XGMII */ + if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE); + if_mode &= ~ENETC_PM_IF_IFMODE_MASK; + enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode); + break; + }; +} + +/* set up serdes for SXGMII */ +static int enetc_init_sxgmii(struct eth_device *edev) +{ + struct enetc_priv *priv = edev->priv; + + if (!enetc_has_imdio(edev)) + return 0; + + /* Dev ability - SXGMII */ + enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL, + ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SXGMII); + + /* Restart PCS AN */ + enetc_mdio_write(priv, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL, + ENETC_PCS_CR, + ENETC_PCS_CR_RST | ENETC_PCS_CR_RESET_AN); + + return 0; +} + +/* Apply protocol specific configuration to MAC, serdes as needed */ +static void enetc_start_pcs(struct eth_device *edev) +{ + struct enetc_priv *priv = edev->priv; + + priv->uclass_id = of_get_phy_mode(priv->dev->of_node); + if (priv->uclass_id == PHY_INTERFACE_MODE_NA) { + dev_dbg(&edev->dev, + "phy-mode property not found, defaulting to SGMII\n"); + priv->uclass_id = PHY_INTERFACE_MODE_SGMII; + } + + switch (priv->uclass_id) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_2500BASEX: + enetc_init_sgmii(edev); + break; + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10GBASER: + enetc_init_sxgmii(edev); + break; + }; +} + +/* + * LS1028A is the only part with IERB at this time and there are plans to + * change its structure, keep this LS1028A specific for now. + */ +#define LS1028A_IERB_BASE 0x1f0800000ULL +#define LS1028A_IERB_PSIPMAR0(pf, vf) (LS1028A_IERB_BASE + 0x8000 \ + + (pf) * 0x100 + (vf) * 8) +#define LS1028A_IERB_PSIPMAR1(pf, vf) (LS1028A_IERB_PSIPMAR0(pf, vf) + 4) + +static int enetc_get_hwaddr(struct eth_device *edev, unsigned char *mac) +{ + return -EOPNOTSUPP; +} + +static int enetc_ls1028a_write_hwaddr(struct eth_device *edev, const unsigned char *mac) +{ + struct enetc_priv *priv = edev->priv; + struct pci_dev *pdev = to_pci_dev(priv->dev); + const int devfn_to_pf[] = {0, 1, 2, -1, -1, -1, 3}; + int devfn = PCI_FUNC(pdev->devfn); + u32 lower, upper; + int pf; + + if (devfn >= ARRAY_SIZE(devfn_to_pf)) + return 0; + + pf = devfn_to_pf[devfn]; + if (pf < 0) + return 0; + + lower = get_unaligned_le16(mac + 4); + upper = get_unaligned_le32(mac); + + out_le32(LS1028A_IERB_PSIPMAR0(pf, 0), upper); + out_le32(LS1028A_IERB_PSIPMAR1(pf, 0), lower); + + return 0; +} + +static int enetc_write_hwaddr(struct eth_device *edev, const unsigned char *mac) +{ + struct enetc_priv *priv = edev->priv; + + u16 lower = get_unaligned_le16(mac + 4); + u32 upper = get_unaligned_le32(mac); + + enetc_write_port(priv, ENETC_PSIPMAR0, upper); + enetc_write_port(priv, ENETC_PSIPMAR1, lower); + + return 0; +} + +/* Configure port parameters (# of rings, frame size, enable port) */ +static void enetc_enable_si_port(struct enetc_priv *priv) +{ + u32 val; + + /* set Rx/Tx BDR count */ + val = ENETC_PSICFGR_SET_TXBDR(ENETC_TX_BDR_CNT); + val |= ENETC_PSICFGR_SET_RXBDR(ENETC_RX_BDR_CNT); + enetc_write_port(priv, ENETC_PSICFGR(0), val); + /* set Rx max frame size */ + enetc_write_port(priv, ENETC_PM_MAXFRM, ENETC_RX_MAXFRM_SIZE); + /* enable MAC port */ + enetc_write_port(priv, ENETC_PM_CC, ENETC_PM_CC_RX_TX_EN); + /* enable port */ + enetc_write_port(priv, ENETC_PMR, ENETC_PMR_SI0_EN); + /* set SI cache policy */ + enetc_write(priv, ENETC_SICAR0, + ENETC_SICAR_RD_CFG | ENETC_SICAR_WR_CFG); + /* enable SI */ + enetc_write(priv, ENETC_SIMR, ENETC_SIMR_EN); +} + +/* returns DMA address for a given buffer index */ +static inline dma_addr_t enetc_rxb_address(struct enetc_priv *priv, int i) +{ + return priv->rx_pkg_phys[i]; +} + +/* + * Setup a single Tx BD Ring (ID = 0): + * - set Tx buffer descriptor address + * - set the BD count + * - initialize the producer and consumer index + */ +static void enetc_setup_tx_bdr(struct eth_device *edev) +{ + struct enetc_priv *priv = edev->priv; + struct bd_ring *tx_bdr = &priv->tx_bdr; + u64 tx_bd_add = (u64)priv->enetc_txbd_phys; + + /* used later to advance to the next Tx BD */ + tx_bdr->bd_count = ENETC_BD_CNT; + tx_bdr->next_prod_idx = 0; + tx_bdr->next_cons_idx = 0; + tx_bdr->cons_idx = priv->regs_base + + ENETC_BDR(TX, ENETC_TX_BDR_ID, ENETC_TBCIR); + tx_bdr->prod_idx = priv->regs_base + + ENETC_BDR(TX, ENETC_TX_BDR_ID, ENETC_TBPIR); + + /* set Tx BD address */ + enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBBAR0, + lower_32_bits(tx_bd_add)); + enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBBAR1, + upper_32_bits(tx_bd_add)); + /* set Tx 8 BD count */ + enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBLENR, + tx_bdr->bd_count); + + /* reset both producer/consumer indexes */ + enetc_write_reg(tx_bdr->cons_idx, tx_bdr->next_cons_idx); + enetc_write_reg(tx_bdr->prod_idx, tx_bdr->next_prod_idx); + + /* enable TX ring */ + enetc_bdr_write(priv, TX, ENETC_TX_BDR_ID, ENETC_TBMR, ENETC_TBMR_EN); +} + +/* + * Setup a single Rx BD Ring (ID = 0): + * - set Rx buffer descriptors address (one descriptor per buffer) + * - set buffer size as max frame size + * - enable Rx ring + * - reset consumer and producer indexes + * - set buffer for each descriptor + */ +static void enetc_setup_rx_bdr(struct eth_device *edev) +{ + struct enetc_priv *priv = edev->priv; + struct bd_ring *rx_bdr = &priv->rx_bdr; + u64 rx_bd_add = (u64)priv->enetc_rxbd_phys; + int i; + + /* used later to advance to the next BD produced by ENETC HW */ + rx_bdr->bd_count = ENETC_BD_CNT; + rx_bdr->next_prod_idx = 0; + rx_bdr->next_cons_idx = 0; + rx_bdr->cons_idx = priv->regs_base + + ENETC_BDR(RX, ENETC_RX_BDR_ID, ENETC_RBCIR); + rx_bdr->prod_idx = priv->regs_base + + ENETC_BDR(RX, ENETC_RX_BDR_ID, ENETC_RBPIR); + + /* set Rx BD address */ + enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBBAR0, + lower_32_bits(rx_bd_add)); + enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBBAR1, + upper_32_bits(rx_bd_add)); + /* set Rx BD count (multiple of 8) */ + enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBLENR, + rx_bdr->bd_count); + /* set Rx buffer size */ + enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBBSR, PKTSIZE); + + /* fill Rx BD */ + memset_io(priv->enetc_rxbd, 0, + rx_bdr->bd_count * sizeof(union enetc_rx_bd)); + + for (i = 0; i < rx_bdr->bd_count; i++) { + priv->rx_pkg[i] = dma_alloc(PKTSIZE); + priv->rx_pkg_phys[i] = dma_map_single(priv->dev, priv->rx_pkg[i], + PKTSIZE, DMA_FROM_DEVICE); + priv->enetc_rxbd[i].w.addr = priv->rx_pkg_phys[i]; + } + + /* reset producer (ENETC owned) and consumer (SW owned) index */ + enetc_write_reg(rx_bdr->cons_idx, rx_bdr->next_cons_idx); + enetc_write_reg(rx_bdr->prod_idx, rx_bdr->next_prod_idx); + + /* enable Rx ring */ + enetc_bdr_write(priv, RX, ENETC_RX_BDR_ID, ENETC_RBMR, ENETC_RBMR_EN); +} + +/* + * Start ENETC interface: + * - perform FLR + * - enable access to port and SI registers + * - set mac address + * - setup TX/RX buffer descriptors + * - enable Tx/Rx rings + */ +static int enetc_start(struct eth_device *edev) +{ + struct enetc_priv *priv = edev->priv; + struct pci_dev *pdev = to_pci_dev(priv->dev); + u32 t; + int ret, interface; + + /* reset and enable the PCI device */ + pci_flr(pdev); + + pci_read_config_dword(pdev, PCI_COMMAND, &t); + pci_write_config_dword(pdev, PCI_COMMAND, t | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + + interface = of_get_phy_mode(priv->dev->of_node); + + ret = phy_device_connect(edev, NULL, 0, NULL, 0, interface); + if (ret) + return ret; + + enetc_enable_si_port(priv); + + /* setup Tx/Rx buffer descriptors */ + enetc_setup_tx_bdr(edev); + enetc_setup_rx_bdr(edev); + + enetc_setup_mac_iface(edev, priv->phy); + + return 0; +} + +/* + * Stop the network interface: + * - just quiesce it, we can wipe all configuration as _start starts from + * scratch each time + */ +static void enetc_stop(struct eth_device *edev) +{ + struct enetc_priv *priv = edev->priv; + struct pci_dev *pdev = to_pci_dev(priv->dev); + u32 t; + + /* FLR is sufficient to quiesce the device */ + pci_flr(pdev); + + /* leave the BARs accessible after we stop, this is needed to use + * internal MDIO in command line. + */ + pci_read_config_dword(pdev, PCI_COMMAND, &t); + pci_write_config_dword(pdev, PCI_COMMAND, t | PCI_COMMAND_MEMORY); +} + +/* + * ENETC transmit packet: + * - check if Tx BD ring is full + * - set buffer/packet address (dma address) + * - set final fragment flag + * - try while producer index equals consumer index or timeout + */ +static int enetc_send(struct eth_device *edev, void *packet, int length) +{ + struct enetc_priv *priv = edev->priv; + struct bd_ring *txr = &priv->tx_bdr; + int ret; + u32 pi, ci; + dma_addr_t dma; + u64 start; + + pi = txr->next_prod_idx; + ci = enetc_read_reg(txr->cons_idx) & ENETC_BDR_IDX_MASK; + /* Tx ring is full when */ + if (((pi + 1) % txr->bd_count) == ci) { + dev_err(&edev->dev, "Tx BDR full\n"); + return -ETIMEDOUT; + } + + dev_vdbg(&edev->dev, "TxBD[%d]send: pkt_len=%d, buff @0x%x%08x\n", pi, length, + upper_32_bits((u64)packet), lower_32_bits((u64)packet)); + + dma = dma_map_single(priv->dev, packet, length, DMA_TO_DEVICE); + + /* prepare Tx BD */ + writeq(dma, &priv->enetc_txbd[pi].addr); + writew(length, &priv->enetc_txbd[pi].buf_len); + writew(length, &priv->enetc_txbd[pi].frm_len); + writew(ENETC_TXBD_FLAGS_F, &priv->enetc_txbd[pi].flags); + + /* send frame: increment producer index */ + pi = (pi + 1) % txr->bd_count; + txr->next_prod_idx = pi; + enetc_write_reg(txr->prod_idx, pi); + + start = get_time_ns(); + + while (1) { + if (is_timeout(start, 100 * USECOND)) { + ret = -ETIMEDOUT; + break; + } + + if (pi == (enetc_read_reg(txr->cons_idx) & ENETC_BDR_IDX_MASK)) { + ret = 0; + break; + } + } + + dma_unmap_single(priv->dev, dma, length, DMA_TO_DEVICE); + + return ret; +} + +/* + * Receive frame: + * - wait for the next BD to get ready bit set + * - clean up the descriptor + * - move on and indicate to HW that the cleaned BD is available for Rx + */ +static int enetc_recv(struct eth_device *edev) +{ + struct enetc_priv *priv = edev->priv; + struct bd_ring *rxr = &priv->rx_bdr; + int pi = rxr->next_prod_idx; + int ci = rxr->next_cons_idx; + u32 status; + void *pkg; + int len; + + status = le32_to_cpu(priv->enetc_rxbd[pi].r.lstatus); + + /* check if current BD is ready to be consumed */ + if (!ENETC_RXBD_STATUS_R(status)) + return 0; + + len = readw(&priv->enetc_rxbd[pi].r.buf_len); + + dev_dbg(&edev->dev, "RxBD[%d]: len=%d err=%d pkt=0x%p\n", pi, len, + ENETC_RXBD_STATUS_ERRORS(status), pkg); + + dma_sync_single_for_cpu(priv->dev, priv->rx_pkg_phys[pi], PKTSIZE, DMA_FROM_DEVICE); + net_receive(edev, priv->rx_pkg[pi], len); + dma_sync_single_for_device(priv->dev, priv->rx_pkg_phys[pi], PKTSIZE, DMA_FROM_DEVICE); + + /* BD clean up and advance to next in ring */ + memset_io(&priv->enetc_rxbd[pi], 0, sizeof(union enetc_rx_bd)); + writeq(priv->rx_pkg_phys[pi], &priv->enetc_rxbd[pi].w.addr); + rxr->next_prod_idx = (pi + 1) % rxr->bd_count; + ci = (ci + 1) % rxr->bd_count; + rxr->next_cons_idx = ci; + dmb(); + /* free up the slot in the ring for HW */ + enetc_write_reg(rxr->cons_idx, ci); + + return 0; +} + +static int enetc_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct enetc_priv *priv; + struct eth_device *edev; + + pci_enable_device(pdev); + pci_set_master(pdev); + + priv = xzalloc(sizeof(*priv)); + priv->dev = dev; + + priv->enetc_txbd = dma_alloc_coherent(sizeof(struct enetc_tx_bd) * ENETC_BD_CNT, + &priv->enetc_txbd_phys); + priv->enetc_rxbd = dma_alloc_coherent(sizeof(union enetc_rx_bd) * ENETC_BD_CNT, + &priv->enetc_rxbd_phys); + + if (!priv->enetc_txbd || !priv->enetc_rxbd) + return -ENOMEM; + + /* initialize register */ + priv->regs_base = pci_iomap(pdev, 0); + if (!priv->regs_base) { + dev_err(dev, "failed to map BAR0\n"); + return -EINVAL; + } + + edev = &priv->edev; + dev->priv = priv; + edev->priv = priv; + edev->open = enetc_start; + edev->send = enetc_send; + edev->recv = enetc_recv; + edev->halt = enetc_stop; + edev->get_ethaddr = enetc_get_hwaddr; + + if (of_machine_is_compatible("fsl,ls1028a")) + edev->set_ethaddr = enetc_ls1028a_write_hwaddr; + else + edev->set_ethaddr = enetc_write_hwaddr; + + edev->parent = dev; + + priv->port_regs = priv->regs_base + ENETC_PORT_REGS_OFF; + + enetc_start_pcs(&priv->edev); + + return eth_register(edev); +} + +static void enetc_remove(struct pci_dev *pdev) +{ + struct enetc_priv *priv = pdev->dev.priv; + + enetc_stop(&priv->edev); +} + +static DEFINE_PCI_DEVICE_TABLE(enetc_pci_tbl) = { + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_ETH) }, + { }, +}; + +static struct pci_driver enetc_eth_driver = { + .name = "fsl_enetc", + .id_table = enetc_pci_tbl, + .probe = enetc_probe, + .remove = enetc_remove, +}; +device_pci_driver(enetc_eth_driver); diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h new file mode 100644 index 0000000000..dc59325ae7 --- /dev/null +++ b/drivers/net/fsl_enetc.h @@ -0,0 +1,252 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * ENETC ethernet controller driver + * Copyright 2017-2021 NXP + */ + +#ifndef _ENETC_H +#define _ENETC_H + +#include <linux/bitops.h> + +/* PCI function IDs */ +#define PCI_DEVICE_ID_ENETC_ETH 0xE100 +#define PCI_DEVICE_ID_ENETC_MDIO 0xEE01 + +/* ENETC Ethernet controller registers */ +/* Station interface register offsets */ +#define ENETC_SIMR 0x000 +#define ENETC_SIMR_EN BIT(31) +#define ENETC_SICAR0 0x040 +/* write cache cfg: snoop, no allocate, data & BD coherent */ +#define ENETC_SICAR_WR_CFG 0x6767 +/* read cache cfg: coherent copy, look up, don't alloc in cache */ +#define ENETC_SICAR_RD_CFG 0x27270000 +#define ENETC_SIROCT 0x300 +#define ENETC_SIRFRM 0x308 +#define ENETC_SITOCT 0x320 +#define ENETC_SITFRM 0x328 + +/* Rx/Tx Buffer Descriptor Ring registers */ +enum enetc_bdr_type {TX, RX}; +#define ENETC_BDR(type, n, off) (0x8000 + (type) * 0x100 + (n) * 0x200 + (off)) +#define ENETC_BDR_IDX_MASK 0xffff + +/* Rx BDR reg offsets */ +#define ENETC_RBMR 0x00 +#define ENETC_RBMR_EN BIT(31) +#define ENETC_RBBSR 0x08 +/* initial consumer index for Rx BDR */ +#define ENETC_RBCIR 0x0c +#define ENETC_RBBAR0 0x10 +#define ENETC_RBBAR1 0x14 +#define ENETC_RBPIR 0x18 +#define ENETC_RBLENR 0x20 + +/* Tx BDR reg offsets */ +#define ENETC_TBMR 0x00 +#define ENETC_TBMR_EN BIT(31) +#define ENETC_TBBAR0 0x10 +#define ENETC_TBBAR1 0x14 +#define ENETC_TBPIR 0x18 +#define ENETC_TBCIR 0x1c +#define ENETC_TBLENR 0x20 + +/* Port registers offset */ +#define ENETC_PORT_REGS_OFF 0x10000 + +/* Port registers */ +#define ENETC_PMR 0x0000 +#define ENETC_PMR_SI0_EN BIT(16) +#define ENETC_PSIPMMR 0x0018 +#define ENETC_PSIPMAR0 0x0100 +#define ENETC_PSIPMAR1 0x0104 +#define ENETC_PCAPR0 0x0900 +#define ENETC_PCAPRO_MDIO BIT(11) +#define ENETC_PSICFGR(n) (0x0940 + (n) * 0x10) +#define ENETC_PSICFGR_SET_TXBDR(val) ((val) & 0xff) +#define ENETC_PSICFGR_SET_RXBDR(val) (((val) & 0xff) << 16) +/* MAC configuration */ +#define ENETC_PM_CC 0x8008 +#define ENETC_PM_CC_DEFAULT 0x0810 +#define ENETC_PM_CC_RX_TX_EN 0x8813 +#define ENETC_PM_MAXFRM 0x8014 +#define ENETC_RX_MAXFRM_SIZE PKTSIZE +#define ENETC_PM_IMDIO_BASE 0x8030 +#define ENETC_PM_IF_MODE 0x8300 +#define ENETC_PM_IF_MODE_RG BIT(2) +#define ENETC_PM_IF_MODE_AN_ENA BIT(15) +#define ENETC_PM_IFM_SSP_MASK GENMASK(14, 13) +#define ENETC_PM_IFM_SSP_1000 (2 << 13) +#define ENETC_PM_IFM_SSP_100 (0 << 13) +#define ENETC_PM_IFM_SSP_10 (1 << 13) +#define ENETC_PM_IFM_FULL_DPX BIT(12) +#define ENETC_PM_IF_IFMODE_MASK GENMASK(1, 0) + +/* buffer descriptors count must be multiple of 8 and aligned to 128 bytes */ +#define ENETC_BD_CNT 128 +#define ENETC_BD_ALIGN 128 + +/* single pair of Rx/Tx rings */ +#define ENETC_RX_BDR_CNT 1 +#define ENETC_TX_BDR_CNT 1 +#define ENETC_RX_BDR_ID 0 +#define ENETC_TX_BDR_ID 0 + +/* Tx buffer descriptor */ +struct enetc_tx_bd { + __le64 addr; + __le16 buf_len; + __le16 frm_len; + __le16 err_csum; + __le16 flags; +} __packed; + +#define ENETC_TXBD_FLAGS_F BIT(15) +#define ENETC_POLL_TRIES 32000 + +/* Rx buffer descriptor */ +union enetc_rx_bd { + /* SW provided BD format */ + struct { + __le64 addr; + u8 reserved[8]; + } w; + + /* ENETC returned BD format */ + struct { + __le16 inet_csum; + __le16 parse_summary; + __le32 rss_hash; + __le16 buf_len; + __le16 vlan_opt; + union { + struct { + __le16 flags; + __le16 error; + } __packed; + __le32 lstatus; + }; + } r; +} __packed; + +#define ENETC_RXBD_STATUS_R(status) (((status) >> 30) & 0x1) +#define ENETC_RXBD_STATUS_F(status) (((status) >> 31) & 0x1) +#define ENETC_RXBD_STATUS_ERRORS(status) (((status) >> 16) & 0xff) +#define ENETC_RXBD_STATUS(flags) ((flags) << 16) + +/* Tx/Rx ring info */ +struct bd_ring { + void *cons_idx; + void *prod_idx; + /* next BD index to use */ + int next_prod_idx; + int next_cons_idx; + int bd_count; +}; + +/* ENETC private structure */ +struct enetc_priv { + struct eth_device edev; + struct device *dev; + + struct enetc_tx_bd *enetc_txbd; + union enetc_rx_bd *enetc_rxbd; + + dma_addr_t rx_pkg_phys[ENETC_BD_CNT]; + dma_addr_t enetc_txbd_phys; + dma_addr_t enetc_rxbd_phys; + + void *rx_pkg[ENETC_BD_CNT]; + + void __iomem *regs_base; /* base ENETC registers */ + void __iomem *port_regs; /* base ENETC port registers */ + + /* Rx/Tx buffer descriptor rings info */ + struct bd_ring tx_bdr; + struct bd_ring rx_bdr; + + int uclass_id; + struct phy_device *phy; +}; + +/* register accessors */ +#define enetc_read_reg(x) readl((x)) +#define enetc_write_reg(x, val) writel((val), (x)) +#define enetc_read(priv, off) enetc_read_reg((priv)->regs_base + (off)) +#define enetc_write(priv, off, v) \ + enetc_write_reg((priv)->regs_base + (off), v) + +/* port register accessors */ +#define enetc_port_regs(priv, off) ((priv)->port_regs + (off)) +#define enetc_read_port(priv, off) \ + enetc_read_reg(enetc_port_regs((priv), (off))) +#define enetc_write_port(priv, off, v) \ + enetc_write_reg(enetc_port_regs((priv), (off)), v) + +/* BDR register accessors, see ENETC_BDR() */ +#define enetc_bdr_read(priv, t, n, off) \ + enetc_read(priv, ENETC_BDR(t, n, off)) +#define enetc_bdr_write(priv, t, n, off, val) \ + enetc_write(priv, ENETC_BDR(t, n, off), val) + +/* PCS / internal SoC PHY ID, it defaults to 0 on all interfaces */ +#define ENETC_PCS_PHY_ADDR 0 + +/* PCS registers */ +#define ENETC_PCS_CR 0x00 +#define ENETC_PCS_CR_RESET_AN 0x1200 +#define ENETC_PCS_CR_DEF_VAL 0x0140 +#define ENETC_PCS_CR_RST BIT(15) +#define ENETC_PCS_DEV_ABILITY 0x04 +#define ENETC_PCS_DEV_ABILITY_SGMII 0x4001 +#define ENETC_PCS_DEV_ABILITY_SXGMII 0x5001 +#define ENETC_PCS_LINK_TIMER1 0x12 +#define ENETC_PCS_LINK_TIMER1_VAL 0x06a0 +#define ENETC_PCS_LINK_TIMER2 0x13 +#define ENETC_PCS_LINK_TIMER2_VAL 0x0003 +#define ENETC_PCS_IF_MODE 0x14 +#define ENETC_PCS_IF_MODE_SGMII BIT(0) +#define ENETC_PCS_IF_MODE_SGMII_AN BIT(1) +#define ENETC_PCS_IF_MODE_SPEED_1G BIT(3) + +/* PCS replicator block for USXGMII */ +#define ENETC_PCS_DEVAD_REPL 0x1f + +#define ENETC_PCS_REPL_LINK_TIMER_1 0x12 +#define ENETC_PCS_REPL_LINK_TIMER_1_DEF 0x0003 +#define ENETC_PCS_REPL_LINK_TIMER_2 0x13 +#define ENETC_PCS_REPL_LINK_TIMER_2_DEF 0x06a0 + +/* ENETC external MDIO registers */ +#define ENETC_MDIO_BASE 0x1c00 +#define ENETC_MDIO_CFG 0x00 +#define ENETC_EMDIO_CFG_C22 0x00809508 +#define ENETC_EMDIO_CFG_C45 0x00809548 +#define ENETC_EMDIO_CFG_RD_ER BIT(1) +#define ENETC_EMDIO_CFG_BSY BIT(0) +#define ENETC_MDIO_CTL 0x04 +#define ENETC_MDIO_CTL_READ BIT(15) +#define ENETC_MDIO_DATA 0x08 +#define ENETC_MDIO_STAT 0x0c + +#define ENETC_MDIO_READ_ERR 0xffff + +struct enetc_mdio_priv { + void __iomem *regs_base; + struct mii_bus bus; +}; + +/* + * these functions are implemented by ENETC_MDIO and are re-used by ENETC driver + * to drive serdes / internal SoC PHYs + */ +int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg); +int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg, u16 val); + +/* sets up primary MAC addresses in DT/IERB */ +void fdt_fixup_enetc_mac(void *blob); + +#endif /* _ENETC_H */ diff --git a/drivers/net/fsl_enetc_mdio.c b/drivers/net/fsl_enetc_mdio.c new file mode 100644 index 0000000000..773d4afd52 --- /dev/null +++ b/drivers/net/fsl_enetc_mdio.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * ENETC ethernet controller driver + * Copyright 2019 NXP + */ + +#include <common.h> +#include <net.h> +#include <linux/phy.h> +#include <linux/pci.h> +#include <io.h> +#include <linux/mdio.h> + +#include "fsl_enetc.h" + +static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv) +{ + int to = 10000; + + while ((enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_BSY) && + --to) + cpu_relax(); +} + +int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg) +{ + if (devad == MDIO_DEVAD_NONE) + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); + else + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); + enetc_mdio_wait_bsy(priv); + + if (devad == MDIO_DEVAD_NONE) { + enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ | + (addr << 5) | reg); + } else { + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad); + enetc_mdio_wait_bsy(priv); + + enetc_write(priv, ENETC_MDIO_STAT, reg); + enetc_mdio_wait_bsy(priv); + + enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ | + (addr << 5) | devad); + } + + enetc_mdio_wait_bsy(priv); + if (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_RD_ER) + return ENETC_MDIO_READ_ERR; + + return enetc_read(priv, ENETC_MDIO_DATA); +} + +int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg, u16 val) +{ + if (devad == MDIO_DEVAD_NONE) + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); + else + enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45); + enetc_mdio_wait_bsy(priv); + + if (devad != MDIO_DEVAD_NONE) { + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad); + enetc_write(priv, ENETC_MDIO_STAT, reg); + } else { + enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + reg); + } + enetc_mdio_wait_bsy(priv); + + enetc_write(priv, ENETC_MDIO_DATA, val); + enetc_mdio_wait_bsy(priv); + + return 0; +} + +static int enetc_mdio_read(struct mii_bus *bus, int addr, int reg) +{ + struct enetc_mdio_priv *priv = bus->priv; + + return enetc_mdio_read_priv(priv, addr, MDIO_DEVAD_NONE, reg); +} + +static int enetc_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val) +{ + struct enetc_mdio_priv *priv = bus->priv; + + return enetc_mdio_write_priv(priv, addr, MDIO_DEVAD_NONE, reg, val); +} + +static int enetc_mdio_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct enetc_mdio_priv *priv; + + pci_enable_device(pdev); + pci_set_master(pdev); + + priv = xzalloc(sizeof(*priv)); + + priv->regs_base = pci_iomap(pdev, 0); + if (!priv->regs_base) { + dev_err(&pdev->dev, "failed to map BAR0\n"); + return -EINVAL; + } + + priv->regs_base += ENETC_MDIO_BASE; + + priv->bus.read = enetc_mdio_read; + priv->bus.write = enetc_mdio_write; + priv->bus.parent = &pdev->dev; + priv->bus.priv = priv; + + return mdiobus_register(&priv->bus); +} + +static DEFINE_PCI_DEVICE_TABLE(enetc_mdio_pci_tbl) = { + { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) }, + { }, +}; + +static struct pci_driver enetc_mdio_driver = { + .name = "fsl_enetc_mdio", + .id_table = enetc_mdio_pci_tbl, + .probe = enetc_mdio_probe, +}; +device_pci_driver(enetc_mdio_driver); diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 8571d4f92c..21ffe822e1 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -21,11 +21,6 @@ #include <linux/err.h> #include "gianfar.h" -/* 2 seems to be the minimum number of TX descriptors to make it work. */ -#define TX_BUF_CNT 2 -#define RX_BUF_CNT PKTBUFSRX -#define BUF_ALIGN 8 - /* * Initialize required registers to appropriate values, zeroing * those we don't care about (unless zero is bad, in which case, @@ -199,7 +194,7 @@ static int gfar_open(struct eth_device *edev) for (ix = 0; ix < RX_BUF_CNT; ix++) { out_be16(&priv->rxbd[ix].status, RXBD_EMPTY); out_be16(&priv->rxbd[ix].length, 0); - out_be32(&priv->rxbd[ix].bufPtr, (uint) NetRxPackets[ix]); + out_be32(&priv->rxbd[ix].bufPtr, (uint) priv->rx_buffer[ix]); } out_be16(&priv->rxbd[RX_BUF_CNT - 1].status, RXBD_EMPTY | RXBD_WRAP); @@ -234,19 +229,13 @@ static int gfar_set_ethaddr(struct eth_device *edev, const unsigned char *mac) { struct gfar_private *priv = edev->priv; void __iomem *regs = priv->regs; - char tmpbuf[MAC_ADDR_LEN]; uint tempval; - int ix; - for (ix = 0; ix < MAC_ADDR_LEN; ix++) - tmpbuf[MAC_ADDR_LEN - 1 - ix] = mac[ix]; - - tempval = (tmpbuf[0] << 24) | (tmpbuf[1] << 16) | (tmpbuf[2] << 8) | - tmpbuf[3]; + tempval = (mac[5] << 24) | (mac[4] << 16) | (mac[3] << 8) | mac[2]; out_be32(regs + GFAR_MACSTRADDR1_OFFSET, tempval); - tempval = *((uint *)(tmpbuf + 4)); + tempval = (mac[1] << 24) | (mac[0] << 16); out_be32(regs + GFAR_MACSTRADDR2_OFFSET, tempval); @@ -361,7 +350,7 @@ static int gfar_send(struct eth_device *edev, void *packet, int length) { struct gfar_private *priv = edev->priv; void __iomem *regs = priv->regs; - struct device_d *dev = edev->parent; + struct device *dev = edev->parent; uint64_t start; uint tidx; uint16_t status; @@ -401,7 +390,7 @@ static int gfar_send(struct eth_device *edev, void *packet, int length) static int gfar_recv(struct eth_device *edev) { struct gfar_private *priv = edev->priv; - struct device_d *dev = edev->parent; + struct device *dev = edev->parent; void __iomem *regs = priv->regs; uint16_t status, length; @@ -413,7 +402,7 @@ static int gfar_recv(struct eth_device *edev) /* Send the packet up if there were no errors */ status = in_be16(&priv->rxbd[priv->rxidx].status); if (!(status & RXBD_STATS)) - net_receive(edev, NetRxPackets[priv->rxidx], length - 4); + net_receive(edev, priv->rx_buffer[priv->rxidx], length - 4); else dev_err(dev, "Got error %x\n", status & RXBD_STATS); @@ -468,18 +457,23 @@ static int gfar_miiphy_write(struct mii_bus *bus, int addr, int reg, * Initialize device structure. Returns success if * initialization succeeded. */ -static int gfar_probe(struct device_d *dev) +static int gfar_probe(struct device *dev) { struct gfar_info_struct *gfar_info = dev->platform_data; struct eth_device *edev; struct gfar_private *priv; - struct device_d *mdev; + struct device *mdev; size_t size; char devname[16]; char *p; + int ret; priv = xzalloc(sizeof(struct gfar_private)); + ret = net_alloc_packets(priv->rx_buffer, ARRAY_SIZE(priv->rx_buffer)); + if (ret) + return ret; + edev = &priv->edev; priv->mdiobus_tbi = gfar_info->mdiobus_tbi; @@ -536,13 +530,13 @@ static int gfar_probe(struct device_d *dev) return eth_register(edev); } -static struct driver_d gfar_eth_driver = { +static struct driver gfar_eth_driver = { .name = "gfar", .probe = gfar_probe, }; device_platform_driver(gfar_eth_driver); -static int gfar_phy_probe(struct device_d *dev) +static int gfar_phy_probe(struct device *dev) { struct gfar_phy *phy; int ret; @@ -567,13 +561,13 @@ static int gfar_phy_probe(struct device_d *dev) return 0; } -static struct driver_d gfar_phy_driver = { +static struct driver gfar_phy_driver = { .name = "gfar-mdio", .probe = gfar_phy_probe, }; register_driver_macro(coredevice, platform, gfar_phy_driver); -static int gfar_tbiphy_probe(struct device_d *dev) +static int gfar_tbiphy_probe(struct device *dev) { struct gfar_phy *phy; int ret; @@ -597,7 +591,7 @@ static int gfar_tbiphy_probe(struct device_d *dev) return 0; } -static struct driver_d gfar_tbiphy_driver = { +static struct driver gfar_tbiphy_driver = { .name = "gfar-tbiphy", .probe = gfar_tbiphy_probe, }; diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h index 081230189a..8a60c7f38e 100644 --- a/drivers/net/gianfar.h +++ b/drivers/net/gianfar.h @@ -261,10 +261,15 @@ struct rxbd8 { struct gfar_phy { void __iomem *regs; - struct device_d *dev; + struct device *dev; struct mii_bus miibus; }; +/* 2 seems to be the minimum number of TX descriptors to make it work. */ +#define TX_BUF_CNT 2 +#define RX_BUF_CNT PKTBUFSRX +#define BUF_ALIGN 8 + struct gfar_private { struct eth_device edev; void __iomem *regs; @@ -282,5 +287,6 @@ struct gfar_private { uint link; uint duplexity; uint speed; + void *rx_buffer[PKTBUFSRX]; }; #endif /* __GIANFAR_H */ diff --git a/drivers/net/ks8851_mll.c b/drivers/net/ks8851_mll.c index b037e19633..2120657bd9 100644 --- a/drivers/net/ks8851_mll.c +++ b/drivers/net/ks8851_mll.c @@ -353,7 +353,6 @@ * struct ks_net - KS8851 driver private data * @hw_addr : start address of data register. * @hw_addr_cmd : start address of command register. - * @pdev : Pointer to platform device. * @bus_width : i/o bus width. * @extra_byte : number of extra byte prepended rx pkt. * @@ -364,8 +363,8 @@ struct ks_net { struct mii_bus miibus; void __iomem *hw_addr; void __iomem *hw_addr_cmd; - struct platform_device *pdev; int bus_width; + void *rx_buf; }; #define BE3 0x8000 /* Byte Enable 3 */ @@ -592,7 +591,7 @@ static void ks_soft_reset(struct ks_net *ks, unsigned op) */ static int ks_read_selftest(struct ks_net *ks) { - struct device_d *dev = &ks->edev.dev; + struct device *dev = &ks->edev.dev; unsigned both_done = MBIR_TXMBF | MBIR_RXMBF; int ret = 0; unsigned rd; @@ -658,8 +657,7 @@ static void ks_setup(struct ks_net *ks) static int ks8851_rx_frame(struct ks_net *ks) { - struct device_d *dev = &ks->edev.dev; - u16 *rdptr = (u16 *) NetRxPackets[0]; + struct device *dev = &ks->edev.dev; u16 RxStatus, RxLen = 0; u16 tmp_rxqcr; @@ -679,14 +677,14 @@ static int ks8851_rx_frame(struct ks_net *ks) tmp_rxqcr = ks_rdreg16(ks, KS_RXQCR); ks_wrreg16(ks, KS_RXQCR, tmp_rxqcr | RXQCR_SDA); /* read 2 bytes for dummy, 2 for status, 2 for len*/ - ks_inblk(ks, rdptr, 2 + 2 + 2); - ks_inblk(ks, rdptr, ALIGN(RxLen, 4)); + ks_inblk(ks, ks->rx_buf, 2 + 2 + 2); + ks_inblk(ks, ks->rx_buf, ALIGN(RxLen, 4)); ks_wrreg16(ks, KS_RXQCR, tmp_rxqcr); if (RxStatus & RXFSHR_RXFV) { /* Pass to upper layer */ dev_dbg(dev, "passing packet to upper layer\n\n"); - net_receive(&ks->edev, NetRxPackets[0], RxLen); + net_receive(&ks->edev, ks->rx_buf, RxLen); return RxLen; } else if (RxStatus & RXFSHR_ERR) { dev_err(dev, "RxStatus error 0x%04x\n", RxStatus & RXFSHR_ERR); @@ -712,7 +710,7 @@ static int ks8851_rx_frame(struct ks_net *ks) static int ks8851_eth_rx(struct eth_device *edev) { struct ks_net *ks = (struct ks_net *)edev->priv; - struct device_d *dev = &edev->dev; + struct device *dev = &edev->dev; u16 frame_cnt; if (!(ks_rdreg16(ks, KS_ISR) & IRQ_RXI)) @@ -733,7 +731,7 @@ static int ks8851_eth_send(struct eth_device *edev, void *packet, int length) { struct ks_net *ks = (struct ks_net *)edev->priv; - struct device_d *dev = &edev->dev; + struct device *dev = &edev->dev; uint64_t tmo; u16 tmp_rxqcr; @@ -769,7 +767,7 @@ static int ks8851_eth_send(struct eth_device *edev, static int ks8851_eth_open(struct eth_device *edev) { struct ks_net *priv = (struct ks_net *)edev->priv; - struct device_d *dev = &edev->dev; + struct device *dev = &edev->dev; int ret; ks_enable_qmu(priv); @@ -792,14 +790,14 @@ static int ks8851_init_dev(struct eth_device *edev) static void ks8851_eth_halt(struct eth_device *edev) { struct ks_net *priv = (struct ks_net *)edev->priv; - struct device_d *dev = &edev->dev; + struct device *dev = &edev->dev; ks_disable_qmu(priv); dev_dbg(dev, "eth_halt\n"); } -static int ks8851_probe(struct device_d *dev) +static int ks8851_probe(struct device *dev) { struct resource *iores; struct eth_device *edev; @@ -827,6 +825,7 @@ static int ks8851_probe(struct device_d *dev) ks->hw_addr_cmd = IOMEM(iores->start); ks->bus_width = dev->resource[0].flags & IORESOURCE_MEM_TYPE_MASK; + ks->rx_buf = xmalloc(PKTSIZE); edev->init = ks8851_init_dev; edev->open = ks8851_eth_open; @@ -871,7 +870,7 @@ static int ks8851_probe(struct device_d *dev) return 0; } -static struct driver_d ks8851_driver = { +static struct driver ks8851_driver = { .name = "ks8851_mll", .probe = ks8851_probe, }; diff --git a/drivers/net/ksz8864rmn.c b/drivers/net/ksz8864rmn.c index 85063ff0d8..c4c30377af 100644 --- a/drivers/net/ksz8864rmn.c +++ b/drivers/net/ksz8864rmn.c @@ -31,48 +31,55 @@ #define CMD_WRITE 0x02 #define CMD_READ 0x03 +enum ksz_type { + unknown, + ksz87, + ksz88 +}; + struct micrel_switch_priv { struct cdev cdev; struct spi_device *spi; unsigned int p_enable; + unsigned int addr_width; + unsigned int pad; }; -static int micrel_switch_read_reg(struct spi_device *spi, uint8_t reg) +static int micrel_switch_read_reg(const struct micrel_switch_priv *priv, uint8_t reg) { uint8_t tx[2]; uint8_t rx[1]; int ret; - tx[0] = CMD_READ; - tx[1] = reg; + tx[0] = CMD_READ << (priv->addr_width + priv->pad - 8) | reg >> (8 - priv->pad); + tx[1] = reg << priv->pad; - ret = spi_write_then_read(spi, tx, 2, rx, 1); + ret = spi_write_then_read(priv->spi, tx, 2, rx, 1); if (ret < 0) return ret; return rx[0]; } -static void micrel_switch_write_reg(struct spi_device *spi, uint8_t reg, uint8_t val) +static void micrel_switch_write_reg(const struct micrel_switch_priv *priv, uint8_t reg, uint8_t val) { uint8_t tx[3]; - tx[0] = CMD_WRITE; - tx[1] = reg; + tx[0] = CMD_WRITE << (priv->addr_width + priv->pad - 8) | reg >> (8 - priv->pad); + tx[1] = reg << priv->pad; tx[2] = val; - spi_write_then_read(spi, tx, 3, NULL, 0); + spi_write_then_read(priv->spi, tx, 3, NULL, 0); } static int micrel_switch_enable_set(struct param_d *param, void *_priv) { struct micrel_switch_priv *priv = _priv; - struct spi_device *spi = priv->spi; if (priv->p_enable) - micrel_switch_write_reg(spi, REG_ID1, 1); + micrel_switch_write_reg(priv, REG_ID1, 1); else - micrel_switch_write_reg(spi, REG_ID1, 0); + micrel_switch_write_reg(priv, REG_ID1, 0); return 0; } @@ -84,7 +91,7 @@ static ssize_t micel_switch_read(struct cdev *cdev, void *_buf, size_t count, lo struct micrel_switch_priv *priv = cdev->priv; for (i = 0; i < count; i++) { - ret = micrel_switch_read_reg(priv->spi, offset); + ret = micrel_switch_read_reg(priv, offset); if (ret < 0) return ret; *buf = ret; @@ -102,7 +109,7 @@ static ssize_t micel_switch_write(struct cdev *cdev, const void *_buf, size_t co struct micrel_switch_priv *priv = cdev->priv; for (i = 0; i < count; i++) { - micrel_switch_write_reg(priv->spi, offset, *buf); + micrel_switch_write_reg(priv, offset, *buf); buf++; offset++; } @@ -115,10 +122,15 @@ static struct cdev_operations micrel_switch_ops = { .write = micel_switch_write, }; -static int micrel_switch_probe(struct device_d *dev) +static int micrel_switch_probe(struct device *dev) { struct micrel_switch_priv *priv; int ret = 0; + enum ksz_type kind = (enum ksz_type)device_get_match_data(dev); + uint8_t id; + + if (kind == unknown) + return -ENODEV; priv = xzalloc(sizeof(*priv)); @@ -128,12 +140,27 @@ static int micrel_switch_probe(struct device_d *dev) priv->spi->mode = SPI_MODE_0; priv->spi->bits_per_word = 8; - ret = micrel_switch_read_reg(priv->spi, REG_ID0); + switch (kind) { + case ksz87: + priv->addr_width = 12; + priv->pad = 1; + id = 0x87; + break; + case ksz88: + priv->addr_width = 8; + priv->pad = 0; + id = 0x95; + break; + default: + return -ENODEV; + }; + + ret = micrel_switch_read_reg(priv, REG_ID0); if (ret < 0) { dev_err(&priv->spi->dev, "failed to read device id\n"); return ret; } - if (ret != 0x95) { + if (ret != id) { dev_err(&priv->spi->dev, "unknown device id: %02x\n", ret); return -ENODEV; } @@ -149,13 +176,20 @@ static int micrel_switch_probe(struct device_d *dev) NULL, &priv->p_enable, priv); priv->p_enable = 1; - micrel_switch_write_reg(priv->spi, REG_ID1, 1); + micrel_switch_write_reg(priv, REG_ID1, 1); return 0; } -static struct driver_d micrel_switch_driver = { +static const struct platform_device_id ksz_ids[] = { + { .name = "ksz8864rmn", .driver_data = ksz88 }, + { .name = "ksz8795", .driver_data = ksz87 }, + { } +}; + +static struct driver micrel_switch_driver = { .name = "ksz8864rmn", .probe = micrel_switch_probe, + .id_table = ksz_ids, }; device_spi_driver(micrel_switch_driver); diff --git a/drivers/net/ksz8873.c b/drivers/net/ksz8873.c new file mode 100644 index 0000000000..03d3530754 --- /dev/null +++ b/drivers/net/ksz8873.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <common.h> +#include <complete.h> +#include <dsa.h> +#include <linux/gpio/consumer.h> +#include <linux/mii.h> +#include <linux/mdio.h> +#include <net.h> +#include <of_device.h> +#include <linux/regmap.h> + +#define KSZ8873_CHIP_ID0 0x00 +#define KSZ8873_CHIP_ID1 0x01 +#define KSZ88_CHIP_ID_M GENMASK(7, 4) +#define KSZ88_REV_ID_M GENMASK(3, 1) + +#define KSZ8873_GLOBAL_CTRL_1 0x03 +#define KSZ8873_PASS_ALL_FRAMES BIT(7) +#define KSZ8873_P3_TAIL_TAG_EN BIT(6) + +/* + * port specific registers. Should be used with ksz_pwrite/ksz_pread functions + */ +#define KSZ8873_PORTx_CTRL_1 0x01 +#define KSZ8873_PORTx_CTRL_12 0x0c + +#define PORT_AUTO_NEG_ENABLE BIT(7) +#define PORT_FORCE_100_MBIT BIT(6) +#define PORT_FORCE_FULL_DUPLEX BIT(5) +#define PORT_AUTO_NEG_100BTX_FD BIT(3) +#define PORT_AUTO_NEG_100BTX BIT(2) +#define PORT_AUTO_NEG_10BT_FD BIT(1) +#define PORT_AUTO_NEG_10BT BIT(0) + +#define KSZ8873_PORTx_CTRL_13 0x0d + +#define PORT_AUTO_NEG_RESTART BIT(5) +#define PORT_POWER_DOWN BIT(3) + +#define KSZ8873_PORTx_STATUS_0 0x0e + +#define PORT_AUTO_NEG_COMPLETE BIT(6) +#define PORT_STAT_LINK_GOOD BIT(5) +#define PORT_REMOTE_100BTX_FD BIT(3) +#define PORT_REMOTE_100BTX BIT(2) +#define PORT_REMOTE_10BT_FD BIT(1) +#define PORT_REMOTE_10BT BIT(0) + +#define KSZ8873_PORTx_STATUS_1 0x0f + +#define KSZ8795_ID_HI 0x0022 +#define KSZ8863_ID_LO 0x1430 + +#define PORT_CTRL_ADDR(port, addr) ((addr) + 0x10 + (port) * 0x10) + +struct ksz8873_dcfg { + unsigned int num_ports; + unsigned int phy_port_cnt; + u8 id0; + u8 id1; +}; + +struct ksz8873_switch { + struct phy_device *mdiodev; + struct dsa_switch ds; + struct device *dev; + const struct ksz8873_dcfg *dcfg; + struct regmap *regmap; +}; + +/* Serial Management Interface (SMI) uses the following frame format: + * + * preamble|start|Read/Write| PHY | REG |TA| Data bits | Idle + * |frame| OP code |address |address| | | + * read | 32x1´s | 01 | 00 | 1xRRR | RRRRR |Z0| 00000000DDDDDDDD | Z + * write| 32x1´s | 01 | 00 | 0xRRR | RRRRR |10| xxxxxxxxDDDDDDDD | Z + * + */ + +#define SMI_KSZ88XX_READ_PHY BIT(4) + +static int ksz8873_mdio_read(void *ctx, unsigned int reg, unsigned int *val) +{ + struct ksz8873_switch *priv = ctx; + struct phy_device *mdiodev = priv->mdiodev; + int ret; + + ret = mdiobus_read(mdiodev->bus, ((reg & 0xE0) >> 5) | + SMI_KSZ88XX_READ_PHY, reg); + if (ret < 0) + return ret; + + *val = ret; + + return 0; +} + +static int ksz8873_mdio_write(void *ctx, unsigned int reg, unsigned int val) +{ + struct ksz8873_switch *priv = ctx; + struct phy_device *mdiodev = priv->mdiodev; + + return mdiobus_write(mdiodev->bus, ((reg & 0xE0) >> 5), reg, val); +} + +static const struct regmap_bus ksz8873_regmap_smi = { + .reg_read = ksz8873_mdio_read, + .reg_write = ksz8873_mdio_write, +}; + +static const struct regmap_config ksz8873_regmap_config = { + .name = "#8", + .reg_bits = 8, + .pad_bits = 24, + .val_bits = 8, +}; + +static int ksz_read8(struct ksz8873_switch *priv, u32 reg, u8 *val) +{ + unsigned int value; + int ret = regmap_read(priv->regmap, reg, &value); + + *val = value & 0xff; + + return ret; +} + +static int ksz_write8(struct ksz8873_switch *priv, u32 reg, u8 value) +{ + return regmap_write(priv->regmap, reg, value); +} + +static int ksz_pread8(struct ksz8873_switch *priv, int port, int reg, u8 *val) +{ + return ksz_read8(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static int ksz_pwrite8(struct ksz8873_switch *priv, int port, int reg, u8 val) +{ + return ksz_write8(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static void ksz8_r_phy(struct ksz8873_switch *priv, u16 phy, u16 reg, u16 *val) +{ + u8 restart, ctrl, link; + int processed = true; + u16 data = 0; + u8 p = phy; + + switch (reg) { + case MII_BMCR: + ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_13, &restart); + ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_12, &ctrl); + if (ctrl & PORT_FORCE_100_MBIT) + data |= BMCR_SPEED100; + if ((ctrl & PORT_AUTO_NEG_ENABLE)) + data |= BMCR_ANENABLE; + if (restart & PORT_POWER_DOWN) + data |= BMCR_PDOWN; + if (restart & PORT_AUTO_NEG_RESTART) + data |= BMCR_ANRESTART; + if (ctrl & PORT_FORCE_FULL_DUPLEX) + data |= BMCR_FULLDPLX; + break; + case MII_BMSR: + ksz_pread8(priv, p, KSZ8873_PORTx_STATUS_0, &link); + data = BMSR_100FULL | + BMSR_100HALF | + BMSR_10FULL | + BMSR_10HALF | + BMSR_ANEGCAPABLE; + if (link & PORT_AUTO_NEG_COMPLETE) + data |= BMSR_ANEGCOMPLETE; + if (link & PORT_STAT_LINK_GOOD) + data |= BMSR_LSTATUS; + break; + case MII_PHYSID1: + data = KSZ8795_ID_HI; + break; + case MII_PHYSID2: + data = KSZ8863_ID_LO; + break; + case MII_ADVERTISE: + ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_12, &ctrl); + data = ADVERTISE_CSMA; + if (ctrl & PORT_AUTO_NEG_100BTX_FD) + data |= ADVERTISE_100FULL; + if (ctrl & PORT_AUTO_NEG_100BTX) + data |= ADVERTISE_100HALF; + if (ctrl & PORT_AUTO_NEG_10BT_FD) + data |= ADVERTISE_10FULL; + if (ctrl & PORT_AUTO_NEG_10BT) + data |= ADVERTISE_10HALF; + break; + case MII_LPA: + ksz_pread8(priv, p, KSZ8873_PORTx_STATUS_0, &link); + data = LPA_SLCT; + if (link & PORT_REMOTE_100BTX_FD) + data |= LPA_100FULL; + if (link & PORT_REMOTE_100BTX) + data |= LPA_100HALF; + if (link & PORT_REMOTE_10BT_FD) + data |= LPA_10FULL; + if (link & PORT_REMOTE_10BT) + data |= LPA_10HALF; + if (data & ~LPA_SLCT) + data |= LPA_LPACK; + break; + default: + processed = false; + break; + } + if (processed) + *val = data; +} + +static void ksz8_w_phy(struct ksz8873_switch *priv, u16 phy, u16 reg, u16 val) +{ + u8 restart, ctrl, data; + u8 p = phy; + + switch (reg) { + case MII_BMCR: + ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_12, &ctrl); + data = ctrl; + if ((val & BMCR_ANENABLE)) + data |= PORT_AUTO_NEG_ENABLE; + else + data &= ~PORT_AUTO_NEG_ENABLE; + + if (val & BMCR_SPEED100) + data |= PORT_FORCE_100_MBIT; + else + data &= ~PORT_FORCE_100_MBIT; + if (val & BMCR_FULLDPLX) + data |= PORT_FORCE_FULL_DUPLEX; + else + data &= ~PORT_FORCE_FULL_DUPLEX; + if (data != ctrl) + ksz_pwrite8(priv, p, KSZ8873_PORTx_CTRL_12, data); + ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_13, &restart); + data = restart; + if (val & BMCR_ANRESTART) + data |= PORT_AUTO_NEG_RESTART; + else + data &= ~(PORT_AUTO_NEG_RESTART); + if (val & BMCR_PDOWN) + data |= PORT_POWER_DOWN; + else + data &= ~PORT_POWER_DOWN; + if (data != restart) + ksz_pwrite8(priv, p, KSZ8873_PORTx_CTRL_13, data); + break; + case MII_ADVERTISE: + ksz_pread8(priv, p, KSZ8873_PORTx_CTRL_12, &ctrl); + data = ctrl; + data &= ~(PORT_AUTO_NEG_100BTX_FD | + PORT_AUTO_NEG_100BTX | + PORT_AUTO_NEG_10BT_FD | + PORT_AUTO_NEG_10BT); + if (val & ADVERTISE_100FULL) + data |= PORT_AUTO_NEG_100BTX_FD; + if (val & ADVERTISE_100HALF) + data |= PORT_AUTO_NEG_100BTX; + if (val & ADVERTISE_10FULL) + data |= PORT_AUTO_NEG_10BT_FD; + if (val & ADVERTISE_10HALF) + data |= PORT_AUTO_NEG_10BT; + if (data != ctrl) + ksz_pwrite8(priv, p, KSZ8873_PORTx_CTRL_12, data); + break; + default: + break; + } +} + +static int ksz8873_phy_read16(struct dsa_switch *ds, int addr, int reg) +{ + struct device *dev = ds->dev; + struct ksz8873_switch *priv = dev_get_priv(dev); + u16 val = 0xffff; + + if (addr >= priv->dcfg->phy_port_cnt) + return val; + + ksz8_r_phy(priv, addr, reg, &val); + + return val; +} + +static int ksz8873_phy_write16(struct dsa_switch *ds, int addr, int reg, + u16 val) +{ + struct device *dev = ds->dev; + struct ksz8873_switch *priv = dev_get_priv(dev); + + /* No real PHY after this. */ + if (addr >= priv->dcfg->phy_port_cnt) + return 0; + + ksz8_w_phy(priv, addr, reg, val); + + return 0; +} + +static void ksz8873_cfg_port_member(struct ksz8873_switch *priv, int port, + u8 member) +{ + ksz_pwrite8(priv, port, KSZ8873_PORTx_CTRL_1, member); +} + +static int ksz8873_port_enable(struct dsa_port *dp, int port, + struct phy_device *phy) +{ + return 0; +} + +static int ksz8873_xmit(struct dsa_port *dp, int port, void *packet, int length) +{ + u8 *tag = packet + length - dp->ds->needed_tx_tailroom; + + *tag = BIT(dp->index); + + return 0; +} + +static int ksz8873_recv(struct dsa_switch *ds, int *port, void *packet, + int length) +{ + u8 *tag = packet + length - ds->needed_rx_tailroom; + + *port = *tag & 7; + + return 0; +}; + +static const struct dsa_switch_ops ksz8873_dsa_ops = { + .port_enable = ksz8873_port_enable, + .xmit = ksz8873_xmit, + .rcv = ksz8873_recv, + .phy_read = ksz8873_phy_read16, + .phy_write = ksz8873_phy_write16, +}; + +static int ksz8873_default_setup(struct ksz8873_switch *priv) +{ + int i; + + ksz_write8(priv, KSZ8873_GLOBAL_CTRL_1, KSZ8873_PASS_ALL_FRAMES | + KSZ8873_P3_TAIL_TAG_EN); + + for (i = 0; i < priv->ds.num_ports; i++) { + u8 member; + /* isolate all ports by default */ + member = BIT(priv->ds.cpu_port); + ksz8873_cfg_port_member(priv, i, member); + + member = dsa_user_ports(&priv->ds); + ksz8873_cfg_port_member(priv, priv->ds.cpu_port, member); + } + + return 0; +} + +static int ksz8873_probe_mdio(struct phy_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + const struct ksz8873_dcfg *dcfg; + struct ksz8873_switch *priv; + struct dsa_switch *ds; + struct gpio_desc *gpio; + int ret; + u8 id0, id1; + + priv = xzalloc(sizeof(*priv)); + + dcfg = of_device_get_match_data(dev); + if (!dcfg) + return -EINVAL; + + dev->priv = priv; + priv->dev = dev; + priv->dcfg = dcfg; + priv->mdiodev = mdiodev; + + priv->regmap = regmap_init(dev, &ksz8873_regmap_smi, priv, + &ksz8873_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), + "Failed to initialize regmap.\n"); + + gpio = gpiod_get_optional(dev, "reset", GPIOF_OUT_INIT_ACTIVE); + if (IS_ERR(gpio)) { + dev_warn(dev, "Failed to get 'reset' GPIO (ignored)\n"); + } else if (gpio) { + mdelay(1); + gpiod_set_value(gpio, false); + } + + ret = ksz_read8(priv, KSZ8873_CHIP_ID0, &id0); + if (ret) + return ret; + + ret = ksz_read8(priv, KSZ8873_CHIP_ID1, &id1); + if (ret) + return ret; + + if (id0 != dcfg->id0 || + (id1 & (KSZ88_CHIP_ID_M | KSZ88_REV_ID_M)) != dcfg->id1) + return -ENODEV; + + ds = &priv->ds; + ds->dev = dev; + ds->num_ports = dcfg->num_ports; + ds->ops = &ksz8873_dsa_ops; + ds->needed_rx_tailroom = 1; + ds->needed_tx_tailroom = 1; + ds->phys_mii_mask = 0x3; + + ret = dsa_register_switch(ds); + if (ret) + return ret; + + ksz8873_default_setup(priv); + + return 0; +} + +static const struct ksz8873_dcfg ksz8873_dcfg = { + .num_ports = 3, + .phy_port_cnt = 2, + .id0 = 0x88, + .id1 = 0x30, +}; + +static const struct of_device_id ksz8873_dt_ids[] = { + { .compatible = "microchip,ksz8873", .data = &ksz8873_dcfg }, + { } +}; +MODULE_DEVICE_TABLE(of, ksz8873_dt_ids); + +static struct phy_driver ksz8873_driver_mdio = { + .drv = { + .name = "KSZ8873 MDIO", + .of_compatible = DRV_OF_COMPAT(ksz8873_dt_ids), + }, + .probe = ksz8873_probe_mdio, +}; +device_mdio_driver(ksz8873_driver_mdio); diff --git a/drivers/net/ksz9477.c b/drivers/net/ksz9477.c new file mode 100644 index 0000000000..1abea9d040 --- /dev/null +++ b/drivers/net/ksz9477.c @@ -0,0 +1,499 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <common.h> +#include <complete.h> +#include <dsa.h> +#include <linux/gpio/consumer.h> +#include <net.h> +#include <platform_data/ksz9477_reg.h> +#include <spi/spi.h> +#include <i2c/i2c.h> +#include "ksz_common.h" + +/* SPI frame opcodes */ + +#define SPI_ADDR_SHIFT 24 +#define SPI_ADDR_ALIGN 3 +#define SPI_TURNAROUND_SHIFT 5 + +#define GBIT_SUPPORT BIT(0) +#define NEW_XMII BIT(1) +#define IS_9893 BIT(2) +#define KSZ9477_PHY_ERRATA BIT(3) + +KSZ_REGMAP_TABLE(ksz9477_spi, 32, SPI_ADDR_SHIFT, + SPI_TURNAROUND_SHIFT, SPI_ADDR_ALIGN); +KSZ_REGMAP_TABLE(ksz9477_i2c, not_used, 16, 0, 0); + +static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg) +{ + struct device *dev = ds->dev; + struct ksz_switch *priv = dev_get_priv(dev); + u16 val = 0xffff; + + if (addr >= priv->phy_port_cnt) + return val; + + ksz_pread16(priv, addr, 0x100 + (reg << 1), &val); + + return val; +} + +static int ksz9477_phy_write16(struct dsa_switch *ds, int addr, int reg, + u16 val) +{ + struct device *dev = ds->dev; + struct ksz_switch *priv = dev_get_priv(dev); + + /* No real PHY after this. */ + if (addr >= priv->phy_port_cnt) + return 0; + + /* No gigabit support. Do not write to this register. */ + if (!(priv->features & GBIT_SUPPORT) && reg == MII_CTRL1000) + return 0; + ksz_pwrite16(priv, addr, 0x100 + (reg << 1), val); + + return 0; +} + +static int ksz9477_switch_detect(struct ksz_switch *priv) +{ + u8 id_hi, id_lo; + u8 data8; + u32 id32; + int ret; + + /* read chip id */ + ret = ksz_read32(priv, REG_CHIP_ID0__1, &id32); + if (ret) + return ret; + + ret = ksz_read8(priv, REG_GLOBAL_OPTIONS, &data8); + if (ret) + return ret; + + priv->chip_id = id32; + + priv->phy_port_cnt = 5; + priv->features = GBIT_SUPPORT | KSZ9477_PHY_ERRATA; + + id_hi = (u8)(id32 >> 16); + id_lo = (u8)(id32 >> 8); + if ((id_lo & 0xf) == 3) { + /* Chip is from KSZ9893 design. */ + dev_info(priv->dev, "Found KSZ9893 or compatible\n"); + priv->features |= IS_9893; + priv->features &= ~KSZ9477_PHY_ERRATA; + + /* Chip does not support gigabit. */ + if (data8 & SW_QW_ABLE) + priv->features &= ~GBIT_SUPPORT; + priv->phy_port_cnt = 2; + } else { + dev_info(priv->dev, "Found KSZ9477 or compatible\n"); + /* Chip uses new XMII register definitions. */ + priv->features |= NEW_XMII; + + /* Chip does not support gigabit. */ + if (!(data8 & SW_GIGABIT_ABLE)) + priv->features &= ~GBIT_SUPPORT; + } + + return 0; +} + +static int ksz_reset_switch(struct ksz_switch *priv) +{ + u8 data8; + u16 data16; + u32 data32; + + /* reset switch */ + ksz_cfg(priv, REG_SW_OPERATION, SW_RESET, true); + + /* turn off SPI DO Edge select */ + ksz_read8(priv, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8); + data8 &= ~SPI_AUTO_EDGE_DETECTION; + ksz_write8(priv, REG_SW_GLOBAL_SERIAL_CTRL_0, data8); + + /* default configuration */ + ksz_read8(priv, REG_SW_LUE_CTRL_1, &data8); + data8 = SW_AGING_ENABLE | SW_LINK_AUTO_AGING | + SW_SRC_ADDR_FILTER | SW_FLUSH_STP_TABLE | SW_FLUSH_MSTP_TABLE; + ksz_write8(priv, REG_SW_LUE_CTRL_1, data8); + + /* disable interrupts */ + ksz_write32(priv, REG_SW_INT_MASK__4, SWITCH_INT_MASK); + ksz_write32(priv, REG_SW_PORT_INT_MASK__4, 0x7F); + ksz_read32(priv, REG_SW_PORT_INT_STATUS__4, &data32); + + /* set broadcast storm protection 10% rate */ + ksz_read16(priv, REG_SW_MAC_CTRL_2, &data16); + data16 &= ~BROADCAST_STORM_RATE; + data16 |= (BROADCAST_STORM_VALUE * BROADCAST_STORM_PROT_RATE) / 100; + ksz_write16(priv, REG_SW_MAC_CTRL_2, data16); + + return 0; +} + +static void ksz9477_cfg_port_member(struct ksz_switch *priv, int port, + u8 member) +{ + ksz_pwrite32(priv, port, REG_PORT_VLAN_MEMBERSHIP__4, member); +} + +static void ksz9477_port_mmd_write(struct ksz_switch *priv, int port, + u8 dev_addr, u16 reg_addr, u16 val) +{ + ksz_pwrite16(priv, port, REG_PORT_PHY_MMD_SETUP, + MMD_SETUP(PORT_MMD_OP_INDEX, dev_addr)); + ksz_pwrite16(priv, port, REG_PORT_PHY_MMD_INDEX_DATA, reg_addr); + ksz_pwrite16(priv, port, REG_PORT_PHY_MMD_SETUP, + MMD_SETUP(PORT_MMD_OP_DATA_NO_INCR, dev_addr)); + ksz_pwrite16(priv, port, REG_PORT_PHY_MMD_INDEX_DATA, val); +} + +static void ksz9477_phy_errata_setup(struct ksz_switch *priv, int port) +{ + /* Apply PHY settings to address errata listed in + * KSZ9477, KSZ9897, KSZ9896, KSZ9567, KSZ8565 + * Silicon Errata and Data Sheet Clarification documents: + * + * Register settings are needed to improve PHY receive performance + */ + ksz9477_port_mmd_write(priv, port, 0x01, 0x6f, 0xdd0b); + ksz9477_port_mmd_write(priv, port, 0x01, 0x8f, 0x6032); + ksz9477_port_mmd_write(priv, port, 0x01, 0x9d, 0x248c); + ksz9477_port_mmd_write(priv, port, 0x01, 0x75, 0x0060); + ksz9477_port_mmd_write(priv, port, 0x01, 0xd3, 0x7777); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x06, 0x3008); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x08, 0x2001); + + /* Transmit waveform amplitude can be improved + * (1000BASE-T, 100BASE-TX, 10BASE-Te) + */ + ksz9477_port_mmd_write(priv, port, 0x1c, 0x04, 0x00d0); + + /* Register settings are required to meet data sheet + * supply current specifications + */ + ksz9477_port_mmd_write(priv, port, 0x1c, 0x13, 0x6eff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x14, 0xe6ff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x15, 0x6eff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x16, 0xe6ff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x17, 0x00ff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x18, 0x43ff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x19, 0xc3ff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x1a, 0x6fff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x1b, 0x07ff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x1c, 0x0fff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x1d, 0xe7ff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x1e, 0xefff); + ksz9477_port_mmd_write(priv, port, 0x1c, 0x20, 0xeeee); +} + +static void ksz9477_set_xmii(struct ksz_switch *priv, int mode, u8 *data) +{ + u8 xmii; + + if (priv->features & NEW_XMII) { + switch (mode) { + case 0: + xmii = PORT_MII_SEL; + break; + case 1: + xmii = PORT_RMII_SEL; + break; + case 2: + xmii = PORT_GMII_SEL; + break; + default: + xmii = PORT_RGMII_SEL; + break; + } + } else { + switch (mode) { + case 0: + xmii = PORT_MII_SEL_S1; + break; + case 1: + xmii = PORT_RMII_SEL_S1; + break; + case 2: + xmii = PORT_GMII_SEL_S1; + break; + default: + xmii = PORT_RGMII_SEL_S1; + break; + } + } + *data &= ~PORT_MII_SEL_M; + *data |= xmii; +} + +static void ksz9477_set_gbit(struct ksz_switch *priv, bool gbit, u8 *data) +{ + if (priv->features & NEW_XMII) { + if (gbit) + *data &= ~PORT_MII_NOT_1GBIT; + else + *data |= PORT_MII_NOT_1GBIT; + } else { + if (gbit) + *data |= PORT_MII_1000MBIT_S1; + else + *data &= ~PORT_MII_1000MBIT_S1; + } +} + +static int ksz_port_setup(struct ksz_switch *priv, int port, + phy_interface_t interface) +{ + u8 data8, member; + + if (port != priv->ds.cpu_port) { + ksz_pwrite8(priv, port, REG_PORT_CTRL_0, 0); + member = BIT(priv->ds.cpu_port); + ksz9477_cfg_port_member(priv, port, member); + + member = dsa_user_ports(&priv->ds); + ksz9477_cfg_port_member(priv, priv->ds.cpu_port, member); + + if (priv->features & KSZ9477_PHY_ERRATA) + ksz9477_phy_errata_setup(priv, port); + + ksz9477_port_mmd_write(priv, port, 0x07, 0x3c, 0x0000); + + ksz_pwrite16(priv, port, 0x100 + (MII_BMCR << 1), + BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET); + } else { + ksz_pwrite8(priv, port, REG_PORT_CTRL_0, PORT_TAIL_TAG_ENABLE); + /* cpu port: configure MAC interface mode */ + ksz_pread8(priv, port, REG_PORT_XMII_CTRL_1, &data8); + switch (interface) { + case PHY_INTERFACE_MODE_MII: + ksz9477_set_xmii(priv, 0, &data8); + ksz9477_set_gbit(priv, false, &data8); + break; + case PHY_INTERFACE_MODE_RMII: + ksz9477_set_xmii(priv, 1, &data8); + ksz9477_set_gbit(priv, false, &data8); + break; + case PHY_INTERFACE_MODE_GMII: + ksz9477_set_xmii(priv, 2, &data8); + ksz9477_set_gbit(priv, true, &data8); + break; + default: + ksz9477_set_xmii(priv, 3, &data8); + ksz9477_set_gbit(priv, true, &data8); + data8 &= ~PORT_RGMII_ID_IG_ENABLE; + data8 &= ~PORT_RGMII_ID_EG_ENABLE; + if (interface == PHY_INTERFACE_MODE_RGMII_ID || + interface == PHY_INTERFACE_MODE_RGMII_RXID) + data8 |= PORT_RGMII_ID_IG_ENABLE; + if (interface == PHY_INTERFACE_MODE_RGMII_ID || + interface == PHY_INTERFACE_MODE_RGMII_TXID) + data8 |= PORT_RGMII_ID_EG_ENABLE; + /* On KSZ9893, disable RGMII in-band status support */ + if (priv->features & IS_9893) + data8 &= ~PORT_MII_MAC_MODE; + break; + } + ksz_pwrite8(priv, port, REG_PORT_XMII_CTRL_1, data8); + } + + return 0; +} + +static int ksz_port_enable(struct dsa_port *dp, int port, + struct phy_device *phy) +{ + struct device *dev = dp->ds->dev; + struct ksz_switch *priv = dev_get_priv(dev); + u8 data8; + int ret; + + /* setup this port */ + ret = ksz_port_setup(priv, port, phy->interface); + if (ret) { + dev_err(dev, "port setup failed: %d\n", ret); + return ret; + } + + /* enable port forwarding for this port */ + ksz_pread8(priv, port, REG_PORT_LUE_MSTP_STATE, &data8); + data8 &= ~PORT_LEARN_DISABLE; + data8 |= (PORT_TX_ENABLE | PORT_RX_ENABLE); + ksz_pwrite8(priv, port, REG_PORT_LUE_MSTP_STATE, data8); + + /* if cpu master we are done */ + if (port == dp->ds->cpu_port) + return 0; + + /* start switch */ + ksz_read8(priv, REG_SW_OPERATION, &data8); + data8 |= SW_START; + ksz_write8(priv, REG_SW_OPERATION, data8); + + return 0; +} + +static int ksz_xmit(struct dsa_port *dp, int port, void *packet, int length) +{ + u16 *tag = packet + length - dp->ds->needed_tx_tailroom; + + *tag = cpu_to_be16(BIT(dp->index)); + + return 0; +} + +static int ksz_recv(struct dsa_switch *ds, int *port, void *packet, int length) +{ + u8 *tag = packet + length - ds->needed_rx_tailroom; + + *port = *tag & 7; + + return 0; +}; + +static const struct dsa_switch_ops ksz_dsa_ops = { + .port_enable = ksz_port_enable, + .xmit = ksz_xmit, + .rcv = ksz_recv, + .phy_read = ksz9477_phy_read16, + .phy_write = ksz9477_phy_write16, +}; + +static int ksz_default_setup(struct ksz_switch *priv) +{ + int i; + + for (i = 0; i < priv->ds.num_ports; i++) { + /* isolate all ports by default */ + ksz9477_cfg_port_member(priv, i, 0); + /* and suspend ports with integrated PHYs */ + if (i < priv->phy_port_cnt) + ksz_pwrite16(priv, i, 0x100 + (MII_BMCR << 1), + BMCR_PDOWN); + } + + return 0; +} + +static int microchip_switch_regmap_init(struct ksz_switch *priv) +{ + const struct regmap_config *cfg; + int i; + + cfg = priv->spi ? ksz9477_spi_regmap_config : ksz9477_i2c_regmap_config; + + for (i = 0; i < KSZ_REGMAP_ENTRY_COUNT; i++) { + if (priv->spi) + priv->regmap[i] = regmap_init_spi(priv->spi, &cfg[i]); + else + priv->regmap[i] = regmap_init_i2c(priv->i2c, &cfg[i]); + if (IS_ERR(priv->regmap[i])) + return dev_err_probe(priv->dev, PTR_ERR(priv->regmap[i]), + "Failed to initialize regmap%i\n", + cfg[i].val_bits); + } + + return 0; +} + +static int microchip_switch_probe(struct device *dev) +{ + struct device *hw_dev; + struct ksz_switch *priv; + struct gpio_desc *gpio; + int ret = 0; + struct dsa_switch *ds; + + priv = xzalloc(sizeof(*priv)); + + dev->priv = priv; + priv->dev = dev; + + if (dev_bus_is_spi(dev)) { + priv->spi = (struct spi_device *)dev->type_data; + priv->spi->mode = SPI_MODE_0; + priv->spi->bits_per_word = 8; + hw_dev = &priv->spi->dev; + } else if (dev_bus_is_i2c(dev)) { + priv->i2c = dev->type_data; + hw_dev = &priv->i2c->dev; + } + + ret = microchip_switch_regmap_init(priv); + if (ret) + return ret; + + gpio = gpiod_get_optional(dev, "reset", GPIOF_OUT_INIT_ACTIVE); + if (IS_ERR(gpio)) { + dev_warn(dev, "Failed to get 'reset' GPIO (ignored)\n"); + } else if (gpio) { + mdelay(1); + gpiod_set_value(gpio, false); + } + + ksz_reset_switch(dev->priv); + + ret = ksz9477_switch_detect(dev->priv); + if (ret) { + dev_err(hw_dev, "error detecting KSZ9477: %pe\n", ERR_PTR(ret)); + return -ENODEV; + } + + dev_info(dev, "chip id: 0x%08x\n", priv->chip_id); + + ds = &priv->ds; + ds->dev = dev; + ds->num_ports = 7; + ds->ops = &ksz_dsa_ops; + ds->needed_rx_tailroom = 1; + ds->needed_tx_tailroom = 2; + if (priv->phy_port_cnt == 5) + ds->phys_mii_mask = 0x1f; + else + ds->phys_mii_mask = 0x03; + + ksz_default_setup(priv); + + ret = dsa_register_switch(ds); + if (ret) + return ret; + + if (priv->i2c) + slice_depends_on(mdiobus_slice(ds->slave_mii_bus), + i2c_client_slice(priv->i2c)); + else + slice_depends_on(mdiobus_slice(ds->slave_mii_bus), + spi_device_slice(priv->spi)); + + return regmap_multi_register_cdev(priv->regmap[0], priv->regmap[1], + priv->regmap[2], NULL); +} + +static const struct of_device_id microchip_switch_dt_ids[] = { + { .compatible = "microchip,ksz8563" }, + { .compatible = "microchip,ksz9477" }, + { .compatible = "microchip,ksz9563" }, + { .compatible = "microchip,ksz9893" }, + { } +}; +MODULE_DEVICE_TABLE(of, microchip_switch_dt_ids); + +static struct driver microchip_switch_spi_driver = { + .name = "ksz9477-spi", + .probe = microchip_switch_probe, + .of_compatible = DRV_OF_COMPAT(microchip_switch_dt_ids), +}; +device_spi_driver(microchip_switch_spi_driver); + +static struct driver microchip_switch_i2c_driver = { + .name = "ksz9477-i2c", + .probe = microchip_switch_probe, + .of_compatible = DRV_OF_COMPAT(microchip_switch_dt_ids), +}; +device_i2c_driver(microchip_switch_i2c_driver); diff --git a/drivers/net/ksz_common.h b/drivers/net/ksz_common.h new file mode 100644 index 0000000000..291488fe34 --- /dev/null +++ b/drivers/net/ksz_common.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef NET_KSZ_COMMON_H_ +#define NET_KSZ_COMMON_H_ + +#include <linux/swab.h> +#include <linux/regmap.h> +#include <linux/bitops.h> +#include <platform_data/ksz9477_reg.h> + +struct ksz_switch { + struct spi_device *spi; + struct i2c_client *i2c; + struct dsa_switch ds; + struct device *dev; + int phy_port_cnt; + u32 chip_id; + u8 features; + struct regmap *regmap[3]; +}; + +static inline int ksz_read8(struct ksz_switch *priv, u32 reg, u8 *val) +{ + unsigned int value; + int ret = regmap_read(priv->regmap[0], reg, &value); + + *val = value; + return ret; +} + +static inline int ksz_read16(struct ksz_switch *priv, u32 reg, u16 *val) +{ + unsigned int value; + int ret = regmap_read(priv->regmap[1], reg, &value); + + *val = value; + return ret; +} + +static inline int ksz_read32(struct ksz_switch *priv, u32 reg, u32 *val) +{ + unsigned int value; + int ret = regmap_read(priv->regmap[2], reg, &value); + + *val = value; + return ret; +} + +static inline int ksz_read64(struct ksz_switch *priv, u32 reg, u64 *val) +{ + u32 value[2]; + int ret; + + ret = regmap_bulk_read(priv->regmap[2], reg, value, 2); + if (!ret) + *val = (u64)value[0] << 32 | value[1]; + + return ret; +} + +static inline int ksz_write8(struct ksz_switch *priv, u32 reg, u8 value) +{ + return regmap_write(priv->regmap[0], reg, value); +} + +static inline int ksz_write16(struct ksz_switch *priv, u32 reg, u16 value) +{ + return regmap_write(priv->regmap[1], reg, value); +} + +static inline int ksz_write32(struct ksz_switch *priv, u32 reg, u32 value) +{ + return regmap_write(priv->regmap[2], reg, value); +} + +static inline int ksz_write64(struct ksz_switch *priv, u32 reg, u64 value) +{ + u32 val[2]; + + /* Ick! ToDo: Add 64bit R/W to regmap on 32bit systems */ + value = swab64(value); + val[0] = swab32(value & 0xffffffffULL); + val[1] = swab32(value >> 32ULL); + + return regmap_bulk_write(priv->regmap[2], reg, val, 2); +} + +static inline int ksz_pread8(struct ksz_switch *priv, int port, int reg, u8 *val) +{ + return ksz_read8(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_pwrite8(struct ksz_switch *priv, int port, int reg, u8 val) +{ + return ksz_write8(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_pread16(struct ksz_switch *priv, int port, int reg, u16 *val) +{ + return ksz_read16(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_pwrite16(struct ksz_switch *priv, int port, int reg, u16 val) +{ + return ksz_write16(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static inline int ksz_pwrite32(struct ksz_switch *priv, int port, int reg, u32 val) +{ + return ksz_write32(priv, PORT_CTRL_ADDR(port, reg), val); +} + +static void ksz_cfg(struct ksz_switch *priv, u32 addr, u8 bits, bool set) +{ + regmap_update_bits(priv->regmap[0], addr, bits, set ? bits : 0); +} + +/* Regmap tables generation */ +#define KSZ_SPI_OP_RD 3 +#define KSZ_SPI_OP_WR 2 + +#define swabnot_used(x) 0 + +#define KSZ_SPI_OP_FLAG_MASK(opcode, swp, regbits, regpad) \ + swab##swp((opcode) << ((regbits) + (regpad))) + +#define KSZ_REGMAP_ENTRY_COUNT 3 + +#define KSZ_REGMAP_ENTRY(width, swp, regbits, regpad, regalign) \ + { \ + .name = #width, \ + .val_bits = (width), \ + .reg_stride = 1, \ + .reg_bits = (regbits) + (regalign), \ + .pad_bits = (regpad), \ + .max_register = BIT(regbits) - 1, \ + .read_flag_mask = \ + KSZ_SPI_OP_FLAG_MASK(KSZ_SPI_OP_RD, swp, \ + regbits, regpad), \ + .write_flag_mask = \ + KSZ_SPI_OP_FLAG_MASK(KSZ_SPI_OP_WR, swp, \ + regbits, regpad), \ + .reg_format_endian = REGMAP_ENDIAN_BIG, \ + .val_format_endian = REGMAP_ENDIAN_BIG \ + } + +#define KSZ_REGMAP_TABLE(ksz, swp, regbits, regpad, regalign) \ + static const struct regmap_config ksz##_regmap_config[KSZ_REGMAP_ENTRY_COUNT] = { \ + KSZ_REGMAP_ENTRY(8, swp, (regbits), (regpad), (regalign)), \ + KSZ_REGMAP_ENTRY(16, swp, (regbits), (regpad), (regalign)), \ + KSZ_REGMAP_ENTRY(32, swp, (regbits), (regpad), (regalign)), \ + } + + +#endif diff --git a/drivers/net/liteeth.c b/drivers/net/liteeth.c new file mode 100644 index 0000000000..1781e26348 --- /dev/null +++ b/drivers/net/liteeth.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * LiteX Liteeth Ethernet + * + * Copyright 2017 Joel Stanley <joel@jms.id.au> + * + * Ported to barebox from linux kernel + * Copyright (C) 2019-2021 Antony Pavlov <antonynpavlov@gmail.com> + * Copyright (C) 2021 Marek Czerski <m.czerski@ap-tech.pl> + * + */ + +#include <common.h> +#include <io.h> +#include <linux/iopoll.h> +#include <malloc.h> +#include <net.h> +#include <init.h> +#include <of_net.h> +#include <linux/phy.h> +#include <linux/mdio-bitbang.h> + +#define DRV_NAME "liteeth" + +#define LITEETH_WRITER_SLOT 0x00 +#define LITEETH_WRITER_LENGTH 0x04 +#define LITEETH_WRITER_ERRORS 0x08 +#define LITEETH_WRITER_EV_STATUS 0x0c +#define LITEETH_WRITER_EV_PENDING 0x10 +#define LITEETH_WRITER_EV_ENABLE 0x14 +#define LITEETH_READER_START 0x18 +#define LITEETH_READER_READY 0x1c +#define LITEETH_READER_LEVEL 0x20 +#define LITEETH_READER_SLOT 0x24 +#define LITEETH_READER_LENGTH 0x28 +#define LITEETH_READER_EV_STATUS 0x2c +#define LITEETH_READER_EV_PENDING 0x30 +#define LITEETH_READER_EV_ENABLE 0x34 +#define LITEETH_PREAMBLE_CRC 0x38 +#define LITEETH_PREAMBLE_ERRORS 0x3c +#define LITEETH_CRC_ERRORS 0x40 + +#define LITEETH_PHY_CRG_RESET 0x00 +#define LITEETH_MDIO_W 0x04 +#define MDIO_W_CLK BIT(0) +#define MDIO_W_OE BIT(1) +#define MDIO_W_DO BIT(2) + +#define LITEETH_MDIO_R 0x08 +#define MDIO_R_DI BIT(0) + +#define LITEETH_BUFFER_SIZE 0x800 +#define MAX_PKT_SIZE LITEETH_BUFFER_SIZE + +struct liteeth { + struct device *dev; + struct eth_device edev; + void __iomem *base; + void __iomem *mdio_base; + struct mii_bus *mii_bus; + struct mdiobb_ctrl mdiobb; + + /* Link management */ + int cur_duplex; + int cur_speed; + + /* Tx */ + int tx_slot; + int num_tx_slots; + void __iomem *tx_base; + + /* Rx */ + int rx_slot; + int num_rx_slots; + void __iomem *rx_base; + + void *rx_buf; +}; + +static inline void litex_write8(void __iomem *addr, u8 val) +{ + writeb(val, addr); +} + +static inline void litex_write16(void __iomem *addr, u16 val) +{ + writew(val, addr); +} + +static inline u8 litex_read8(void __iomem *addr) +{ + return readb(addr); +} + +static inline u32 litex_read32(void __iomem *addr) +{ + return readl(addr); +} + +static void liteeth_mdio_w_modify(struct liteeth *priv, u8 clear, u8 set) +{ + void __iomem *mdio_w = priv->mdio_base + LITEETH_MDIO_W; + + litex_write8(mdio_w, (litex_read8(mdio_w) & ~clear) | set); +} + +static void liteeth_mdio_ctrl(struct mdiobb_ctrl *ctrl, u8 mask, int set) +{ + struct liteeth *priv = container_of(ctrl, struct liteeth, mdiobb); + + liteeth_mdio_w_modify(priv, mask, set ? mask : 0); +} + +/* MDC pin control */ +static void liteeth_set_mdc(struct mdiobb_ctrl *ctrl, int level) +{ + liteeth_mdio_ctrl(ctrl, MDIO_W_CLK, level); +} + +/* Data I/O pin control */ +static void liteeth_set_mdio_dir(struct mdiobb_ctrl *ctrl, int output) +{ + liteeth_mdio_ctrl(ctrl, MDIO_W_OE, output); +} + +/* Set data bit */ +static void liteeth_set_mdio_data(struct mdiobb_ctrl *ctrl, int value) +{ + liteeth_mdio_ctrl(ctrl, MDIO_W_DO, value); +} + +/* Get data bit */ +static int liteeth_get_mdio_data(struct mdiobb_ctrl *ctrl) +{ + struct liteeth *priv = container_of(ctrl, struct liteeth, mdiobb); + + return (litex_read8(priv->mdio_base + LITEETH_MDIO_R) & MDIO_R_DI) != 0; +} + +/* MDIO bus control struct */ +static struct mdiobb_ops bb_ops = { + .set_mdc = liteeth_set_mdc, + .set_mdio_dir = liteeth_set_mdio_dir, + .set_mdio_data = liteeth_set_mdio_data, + .get_mdio_data = liteeth_get_mdio_data, +}; + +static int liteeth_init_dev(struct eth_device *edev) +{ + return 0; +} + +static int liteeth_eth_open(struct eth_device *edev) +{ + struct liteeth *priv = edev->priv; + int ret; + + /* Disable events */ + litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 0); + litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 0); + + /* Clear pending events? */ + litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, 1); + litex_write8(priv->base + LITEETH_READER_EV_PENDING, 1); + + ret = phy_device_connect(edev, priv->mii_bus, -1, NULL, 0, -1); + if (ret) + return ret; + + return 0; +} + +static int liteeth_eth_send(struct eth_device *edev, void *packet, + int packet_length) +{ + struct liteeth *priv = edev->priv; + void *txbuffer; + int ret; + u8 val; + u8 reg; + + reg = litex_read8(priv->base + LITEETH_READER_EV_PENDING); + if (reg) { + litex_write8(priv->base + LITEETH_READER_EV_PENDING, reg); + } + + /* Reject oversize packets */ + if (unlikely(packet_length > MAX_PKT_SIZE)) { + dev_err(priv->dev, "tx packet too big\n"); + goto drop; + } + + txbuffer = priv->tx_base + priv->tx_slot * LITEETH_BUFFER_SIZE; + memcpy(txbuffer, packet, packet_length); + litex_write8(priv->base + LITEETH_READER_SLOT, priv->tx_slot); + litex_write16(priv->base + LITEETH_READER_LENGTH, packet_length); + + ret = readb_poll_timeout(priv->base + LITEETH_READER_READY, + val, val, 1000); + if (ret == -ETIMEDOUT) { + dev_err(priv->dev, "LITEETH_READER_READY timed out\n"); + goto drop; + } + + litex_write8(priv->base + LITEETH_READER_START, 1); + + priv->tx_slot = (priv->tx_slot + 1) % priv->num_tx_slots; + +drop: + return 0; +} + +static int liteeth_eth_rx(struct eth_device *edev) +{ + struct liteeth *priv = edev->priv; + u8 rx_slot; + int len = 0; + u8 reg; + + reg = litex_read8(priv->base + LITEETH_WRITER_EV_PENDING); + if (!reg) { + goto done; + } + + len = litex_read32(priv->base + LITEETH_WRITER_LENGTH); + if (len == 0 || len > 2048) { + len = 0; + dev_err(priv->dev, "%s: invalid len %d\n", __func__, len); + litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, reg); + goto done; + } + + rx_slot = litex_read8(priv->base + LITEETH_WRITER_SLOT); + + memcpy(priv->rx_buf, priv->rx_base + rx_slot * LITEETH_BUFFER_SIZE, len); + + net_receive(edev, priv->rx_buf, len); + + litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, reg); + +done: + return len; +} + +static void liteeth_eth_halt(struct eth_device *edev) +{ + struct liteeth *priv = edev->priv; + + litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 0); + litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 0); +} + +static void liteeth_reset_hw(struct liteeth *priv) +{ + /* Reset, twice */ + litex_write8(priv->base + LITEETH_PHY_CRG_RESET, 0); + udelay(10); + litex_write8(priv->base + LITEETH_PHY_CRG_RESET, 1); + udelay(10); + litex_write8(priv->base + LITEETH_PHY_CRG_RESET, 0); + udelay(10); +} + +static int liteeth_get_ethaddr(struct eth_device *edev, unsigned char *m) +{ + return 0; +} + +static int liteeth_set_ethaddr(struct eth_device *edev, + const unsigned char *mac_addr) +{ + return 0; +} + +static int liteeth_probe(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct eth_device *edev; + void __iomem *buf_base; + struct liteeth *priv; + int err; + + priv = xzalloc(sizeof(struct liteeth)); + edev = &priv->edev; + edev->priv = priv; + priv->dev = dev; + + priv->base = dev_request_mem_region(dev, 0); + if (IS_ERR(priv->base)) { + err = PTR_ERR(priv->base); + goto err; + } + + priv->mdio_base = dev_request_mem_region(dev, 1); + if (IS_ERR(priv->mdio_base)) { + err = PTR_ERR(priv->mdio_base); + goto err; + } + + buf_base = dev_request_mem_region(dev, 2); + if (IS_ERR(buf_base)) { + err = PTR_ERR(buf_base); + goto err; + } + + err = of_property_read_u32(np, "rx-fifo-depth", + &priv->num_rx_slots); + if (err) { + dev_err(dev, "unable to get rx-fifo-depth\n"); + goto err; + } + + err = of_property_read_u32(np, "tx-fifo-depth", + &priv->num_tx_slots); + if (err) { + dev_err(dev, "unable to get tx-fifo-depth\n"); + goto err; + } + + /* Rx slots */ + priv->rx_base = buf_base; + priv->rx_slot = 0; + + /* Tx slots come after Rx slots */ + priv->tx_base = buf_base + priv->num_rx_slots * LITEETH_BUFFER_SIZE; + priv->tx_slot = 0; + + priv->rx_buf = xmalloc(PKTSIZE); + + edev->init = liteeth_init_dev; + edev->open = liteeth_eth_open; + edev->send = liteeth_eth_send; + edev->recv = liteeth_eth_rx; + edev->get_ethaddr = liteeth_get_ethaddr; + edev->set_ethaddr = liteeth_set_ethaddr; + edev->halt = liteeth_eth_halt; + edev->parent = dev; + + priv->mdiobb.ops = &bb_ops; + + priv->mii_bus = alloc_mdio_bitbang(&priv->mdiobb); + priv->mii_bus->parent = dev; + + liteeth_reset_hw(priv); + + err = eth_register(edev); + if (err) { + dev_err(dev, "failed to register edev\n"); + goto err; + } + + err = mdiobus_register(priv->mii_bus); + if (err) { + dev_err(dev, "failed to register mii_bus\n"); + goto err; + } + + dev_info(dev, DRV_NAME " driver registered\n"); + + return 0; + +err: + return err; +} + +static const struct of_device_id liteeth_dt_ids[] = { + { + .compatible = "litex,liteeth" + }, { + } +}; +MODULE_DEVICE_TABLE(of, liteeth_dt_ids); + +static struct driver liteeth_driver = { + .name = DRV_NAME, + .probe = liteeth_probe, + .of_compatible = DRV_OF_COMPAT(liteeth_dt_ids), +}; +device_platform_driver(liteeth_driver); + +MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 4850e60c49..f5b2fa74dc 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -52,6 +52,10 @@ #define TX_RING_BYTES (sizeof(struct macb_dma_desc) * TX_RING_SIZE) #define GEM_Q1_DESC_BYTES (sizeof(struct macb_dma_desc) * GEM_Q1_DESCS) +struct macb_config { + int (*txclk_init)(struct device *dev, struct clk **tx_clk); +}; + struct macb_device { void __iomem *regs; @@ -59,9 +63,13 @@ struct macb_device { unsigned int tx_head; void *rx_buffer; + dma_addr_t rx_buffer_phys; void *tx_buffer; + void *rx_packet_buf; struct macb_dma_desc *rx_ring; + dma_addr_t rx_ring_phys; struct macb_dma_desc *tx_ring; + dma_addr_t tx_ring_phys; struct macb_dma_desc *gem_q1_descs; int rx_buffer_size; @@ -70,7 +78,7 @@ struct macb_device { int phy_addr; struct clk *pclk, *hclk, *txclk, *rxclk; - const struct device_d *dev; + struct device *dev; struct eth_device netdev; phy_interface_t interface; @@ -100,6 +108,7 @@ static int macb_send(struct eth_device *edev, void *packet, int ret = 0; uint64_t start; unsigned int tx_head = macb->tx_head; + dma_addr_t packet_dma; ctrl = MACB_BF(TX_FRMLEN, length); ctrl |= MACB_BIT(TX_LAST); @@ -111,23 +120,25 @@ static int macb_send(struct eth_device *edev, void *packet, macb->tx_head++; } - macb->tx_ring[tx_head].ctrl = ctrl; - macb->tx_ring[tx_head].addr = (ulong)packet; - barrier(); - dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE); + packet_dma = dma_map_single(macb->dev, packet, length, DMA_TO_DEVICE); + if (dma_mapping_error(macb->dev, packet_dma)) + return -EFAULT; + + writel(ctrl, &macb->tx_ring[tx_head].ctrl); + writel(packet_dma, &macb->tx_ring[tx_head].addr); macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE) | MACB_BIT(TSTART)); start = get_time_ns(); ret = -ETIMEDOUT; do { - barrier(); - ctrl = macb->tx_ring[0].ctrl; + ctrl = readl(&macb->tx_ring[0].ctrl); if (ctrl & MACB_BIT(TX_USED)) { ret = 0; break; } } while (!is_timeout(start, 100 * MSECOND)); - dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE); + + dma_unmap_single(macb->dev, packet_dma, length, DMA_TO_DEVICE); if (ctrl & MACB_BIT(TX_UNDERRUN)) dev_err(macb->dev, "TX underrun\n"); @@ -144,50 +155,42 @@ static void reclaim_rx_buffers(struct macb_device *macb, { unsigned int i; - dev_dbg(macb->dev, "%s\n", __func__); - i = macb->rx_tail; while (i > new_tail) { - macb->rx_ring[i].addr &= ~MACB_BIT(RX_USED); + clrbits_le32(&macb->rx_ring[i].addr, MACB_BIT(RX_USED)); i++; if (i > macb->rx_ring_size) i = 0; } while (i < new_tail) { - macb->rx_ring[i].addr &= ~MACB_BIT(RX_USED); + clrbits_le32(&macb->rx_ring[i].addr, MACB_BIT(RX_USED)); i++; } - barrier(); macb->rx_tail = new_tail; } static int gem_recv(struct eth_device *edev) { struct macb_device *macb = edev->priv; - void *buffer; + dma_addr_t buffer; int length; u32 status; - dev_dbg(macb->dev, "%s\n", __func__); - for (;;) { - barrier(); - if (!(macb->rx_ring[macb->rx_tail].addr & MACB_BIT(RX_USED))) + if (!(readl(&macb->rx_ring[macb->rx_tail].addr) & MACB_BIT(RX_USED))) return -1; - barrier(); - status = macb->rx_ring[macb->rx_tail].ctrl; + status = readl(&macb->rx_ring[macb->rx_tail].ctrl); length = MACB_BFEXT(RX_FRMLEN, status); - buffer = macb->rx_buffer + macb->rx_buffer_size * macb->rx_tail; - dma_sync_single_for_cpu((unsigned long)buffer, length, - DMA_FROM_DEVICE); - net_receive(edev, buffer, length); - dma_sync_single_for_device((unsigned long)buffer, length, - DMA_FROM_DEVICE); - macb->rx_ring[macb->rx_tail].addr &= ~MACB_BIT(RX_USED); - barrier(); + buffer = macb->rx_buffer_phys + macb->rx_buffer_size * macb->rx_tail; + dma_sync_single_for_cpu(macb->dev, buffer, length, DMA_FROM_DEVICE); + net_receive(edev, + macb->rx_buffer + macb->rx_buffer_size * macb->rx_tail, + length); + dma_sync_single_for_device(macb->dev, buffer, length, DMA_FROM_DEVICE); + clrbits_le32(&macb->rx_ring[macb->rx_tail].addr, MACB_BIT(RX_USED)); macb->rx_tail++; if (macb->rx_tail >= macb->rx_ring_size) @@ -201,20 +204,16 @@ static int macb_recv(struct eth_device *edev) { struct macb_device *macb = edev->priv; unsigned int rx_tail = macb->rx_tail; - void *buffer; + dma_addr_t buffer; int length; int wrapped = 0; u32 status; - dev_dbg(macb->dev, "%s\n", __func__); - for (;;) { - barrier(); - if (!(macb->rx_ring[rx_tail].addr & MACB_BIT(RX_USED))) + if (!(readl(&macb->rx_ring[rx_tail].addr) & MACB_BIT(RX_USED))) return -1; - barrier(); - status = macb->rx_ring[rx_tail].ctrl; + status = readl(&macb->rx_ring[rx_tail].ctrl); if (status & MACB_BIT(RX_SOF)) { if (rx_tail != macb->rx_tail) reclaim_rx_buffers(macb, rx_tail); @@ -222,7 +221,7 @@ static int macb_recv(struct eth_device *edev) } if (status & MACB_BIT(RX_EOF)) { - buffer = macb->rx_buffer + macb->rx_buffer_size * macb->rx_tail; + buffer = macb->rx_buffer_phys + macb->rx_buffer_size * macb->rx_tail; length = MACB_BFEXT(RX_FRMLEN, status); if (wrapped) { unsigned int headlen, taillen; @@ -230,26 +229,24 @@ static int macb_recv(struct eth_device *edev) headlen = macb->rx_buffer_size * (macb->rx_ring_size - macb->rx_tail); taillen = length - headlen; - dma_sync_single_for_cpu((unsigned long)buffer, - headlen, DMA_FROM_DEVICE); - memcpy((void *)NetRxPackets[0], buffer, headlen); - dma_sync_single_for_cpu((unsigned long)macb->rx_buffer, + dma_sync_single_for_cpu(macb->dev, buffer, headlen, DMA_FROM_DEVICE); + memcpy(macb->rx_packet_buf, + macb->rx_buffer + macb->rx_buffer_size * macb->rx_tail, + headlen); + dma_sync_single_for_cpu(macb->dev, macb->rx_buffer_phys, taillen, DMA_FROM_DEVICE); - memcpy((void *)NetRxPackets[0] + headlen, - macb->rx_buffer, taillen); - dma_sync_single_for_device((unsigned long)buffer, - headlen, DMA_FROM_DEVICE); - dma_sync_single_for_device((unsigned long)macb->rx_buffer, + memcpy(macb->rx_packet_buf + headlen, macb->rx_buffer, taillen); + dma_sync_single_for_device(macb->dev, buffer, headlen, DMA_FROM_DEVICE); + dma_sync_single_for_device(macb->dev, macb->rx_buffer_phys, taillen, DMA_FROM_DEVICE); - net_receive(edev, NetRxPackets[0], length); + net_receive(edev, macb->rx_packet_buf, length); } else { - dma_sync_single_for_cpu((unsigned long)buffer, length, - DMA_FROM_DEVICE); - net_receive(edev, buffer, length); - dma_sync_single_for_device((unsigned long)buffer, length, - DMA_FROM_DEVICE); + dma_sync_single_for_cpu(macb->dev, buffer, length, DMA_FROM_DEVICE); + net_receive(edev, + macb->rx_buffer + macb->rx_buffer_size * macb->rx_tail, + length); + dma_sync_single_for_device(macb->dev, buffer, length, DMA_FROM_DEVICE); } - barrier(); if (++rx_tail >= macb->rx_ring_size) rx_tail = 0; reclaim_rx_buffers(macb, rx_tail); @@ -264,9 +261,41 @@ static int macb_recv(struct eth_device *edev) return 0; } +static int macb_set_tx_clk(struct macb_device *macb, int speed) +{ + int rate; + int rate_rounded; + + if (!macb->txclk) { + dev_dbg(macb->dev, "txclk not available\n"); + return 0; + } + + switch (speed) { + case SPEED_10: + rate = 2500000; + break; + case SPEED_100: + rate = 25000000; + break; + case SPEED_1000: + rate = 125000000; + break; + default: + return -EINVAL; + } + + rate_rounded = clk_round_rate(macb->txclk, rate); + if (rate_rounded <= 0) + return -EINVAL; + + return clk_set_rate(macb->txclk, rate_rounded); +} + static void macb_adjust_link(struct eth_device *edev) { struct macb_device *macb = edev->priv; + int err; u32 reg; reg = macb_readl(macb, NCFGR); @@ -282,14 +311,16 @@ static void macb_adjust_link(struct eth_device *edev) reg |= GEM_BIT(GBE); macb_or_gem_writel(macb, NCFGR, reg); + + err = macb_set_tx_clk(macb, edev->phydev->speed); + if (err) + dev_warn(macb->dev, "cannot set txclk\n"); } static int macb_open(struct eth_device *edev) { struct macb_device *macb = edev->priv; - dev_dbg(macb->dev, "%s\n", __func__); - /* Enable TX and RX */ macb_writel(macb, NCR, MACB_BIT(TE) | MACB_BIT(RE)); @@ -335,42 +366,45 @@ static int gmac_init_dummy_tx_queues(struct macb_device *macb) if (queue_mask & (1 << i)) num_queues++; - macb->gem_q1_descs[0].addr = 0; - macb->gem_q1_descs[0].ctrl = MACB_BIT(TX_WRAP) | - MACB_BIT(TX_LAST) | MACB_BIT(TX_USED); + writel(0, &macb->gem_q1_descs[0].addr); + writel(MACB_BIT(TX_WRAP) | MACB_BIT(TX_LAST) | MACB_BIT(TX_USED), + &macb->gem_q1_descs[0].ctrl); for (i = 1; i < num_queues; i++) - gem_writel_queue_TBQP(macb, &macb->gem_q1_descs[0], i - 1); + gem_writel_queue_TBQP(macb, (ulong)macb->gem_q1_descs, i - 1); return 0; } -static void macb_init(struct macb_device *macb) +static int macb_init(struct macb_device *macb) { unsigned long paddr, val = 0; int i; - dev_dbg(macb->dev, "%s\n", __func__); - /* * macb_halt should have been called at some point before now, * so we'll assume the controller is idle. */ + macb->rx_buffer_phys = dma_map_single(macb->dev, macb->rx_buffer, + macb->rx_buffer_size * macb->rx_ring_size, + DMA_FROM_DEVICE); + if (dma_mapping_error(macb->dev, macb->rx_buffer_phys)) + return -EFAULT; /* initialize DMA descriptors */ paddr = (ulong)macb->rx_buffer; for (i = 0; i < macb->rx_ring_size; i++) { - macb->rx_ring[i].addr = paddr; - macb->rx_ring[i].ctrl = 0; + writel(paddr, &macb->rx_ring[i].addr); + writel(0, &macb->rx_ring[i].ctrl); paddr += macb->rx_buffer_size; } - macb->rx_ring[macb->rx_ring_size - 1].addr |= MACB_BIT(RX_WRAP); + setbits_le32(&macb->rx_ring[macb->rx_ring_size - 1].addr, MACB_BIT(RX_WRAP)); for (i = 0; i < TX_RING_SIZE; i++) { - macb->tx_ring[i].addr = 0; - macb->tx_ring[i].ctrl = MACB_BIT(TX_USED); + writel(0, &macb->tx_ring[i].addr); + writel(MACB_BIT(TX_USED), &macb->tx_ring[i].ctrl); } - macb->tx_ring[TX_RING_SIZE - 1].addr |= MACB_BIT(TX_WRAP); + setbits_le32(&macb->tx_ring[TX_RING_SIZE - 1].addr, MACB_BIT(TX_WRAP)); macb->rx_tail = macb->tx_head = 0; @@ -383,9 +417,8 @@ static void macb_init(struct macb_device *macb) gmac_init_dummy_tx_queues(macb); /* Disable the second priority rx queue */ - macb->gem_q1_descs[1].addr = MACB_BIT(RX_USED) | - MACB_BIT(RX_WRAP); - macb->gem_q1_descs[1].ctrl = 0; + writel(MACB_BIT(RX_USED) | MACB_BIT(RX_WRAP), &macb->gem_q1_descs[1].addr); + writel(0, &macb->gem_q1_descs[1].ctrl); gem_writel(macb, RQ1, (ulong)&macb->gem_q1_descs[1]); } @@ -412,6 +445,7 @@ static void macb_init(struct macb_device *macb) macb_or_gem_writel(macb, USRIO, val); + return 0; } static void macb_halt(struct eth_device *edev) @@ -430,6 +464,13 @@ static void macb_halt(struct eth_device *edev) /* Disable TX and RX, and clear statistics */ macb_writel(macb, NCR, MACB_BIT(CLRSTAT)); + + dma_unmap_single(macb->dev, macb->rx_buffer_phys, + macb->rx_buffer_size * macb->rx_ring_size, + DMA_FROM_DEVICE); + free(macb->rx_buffer); + dma_free_coherent((void *)macb->rx_ring, macb->rx_ring_phys, RX_RING_BYTES(macb)); + dma_free_coherent((void *)macb->tx_ring, macb->tx_ring_phys, TX_RING_BYTES); } static int macb_phy_read(struct mii_bus *bus, int addr, int reg) @@ -441,8 +482,6 @@ static int macb_phy_read(struct mii_bus *bus, int addr, int reg) int value; uint64_t start; - dev_dbg(macb->dev, "%s\n", __func__); - netctl = macb_readl(macb, NCR); netctl |= MACB_BIT(MPE); macb_writel(macb, NCR, netctl); @@ -478,8 +517,6 @@ static int macb_phy_write(struct mii_bus *bus, int addr, int reg, u16 value) unsigned long netctl; unsigned long frame; - dev_dbg(macb->dev, "%s\n", __func__); - netctl = macb_readl(macb, NCR); netctl |= MACB_BIT(MPE); macb_writel(macb, NCR, netctl); @@ -510,8 +547,6 @@ static int macb_get_ethaddr(struct eth_device *edev, unsigned char *adr) u8 addr[6]; int i; - dev_dbg(macb->dev, "%s\n", __func__); - /* Check all 4 address register for vaild address */ for (i = 0; i < 4; i++) { bottom = macb_or_gem_readl(macb, SA1B + i * 8); @@ -537,8 +572,6 @@ static int macb_set_ethaddr(struct eth_device *edev, const unsigned char *adr) { struct macb_device *macb = edev->priv; - dev_dbg(macb->dev, "%s\n", __func__); - /* set hardware address */ macb_or_gem_writel(macb, SA1B, adr[0] | adr[1] << 8 | adr[2] << 16 | adr[3] << 24); macb_or_gem_writel(macb, SA1T, adr[4] | adr[5] << 8); @@ -648,13 +681,117 @@ static void macb_init_rx_buffer_size(struct macb_device *bp, size_t size) size, bp->rx_buffer_size); } -static int macb_probe(struct device_d *dev) +#ifdef CONFIG_COMMON_CLK +/* This structure is only used for MACB on SiFive FU540 devices */ +struct sifive_fu540_macb_mgmt { + void __iomem *reg; + unsigned long rate; + struct clk clk; +}; + +static struct sifive_fu540_macb_mgmt *mgmt; + +static unsigned long fu540_macb_tx_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return mgmt->rate; +} + +static long fu540_macb_tx_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + if (WARN_ON(rate < 2500000)) + return 2500000; + else if (rate == 2500000) + return 2500000; + else if (WARN_ON(rate < 13750000)) + return 2500000; + else if (WARN_ON(rate < 25000000)) + return 25000000; + else if (rate == 25000000) + return 25000000; + else if (WARN_ON(rate < 75000000)) + return 25000000; + else if (WARN_ON(rate < 125000000)) + return 125000000; + else if (rate == 125000000) + return 125000000; + + WARN_ON(rate > 125000000); + + return 125000000; +} + +static int fu540_macb_tx_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + rate = fu540_macb_tx_round_rate(hw, rate, &parent_rate); + if (rate != 125000000) + iowrite32(1, mgmt->reg); + else + iowrite32(0, mgmt->reg); + mgmt->rate = rate; + + return 0; +} + +static const struct clk_ops fu540_c000_ops = { + .recalc_rate = fu540_macb_tx_recalc_rate, + .round_rate = fu540_macb_tx_round_rate, + .set_rate = fu540_macb_tx_set_rate, +}; + +static int fu540_c000_txclk_init(struct device *dev, struct clk **tx_clk) +{ + struct clk *clk; + struct resource *res; + int err = 0; + + mgmt = xzalloc(sizeof(*mgmt)); + + res = dev_request_mem_resource(dev, 1); + if (IS_ERR(res)) + return PTR_ERR(res); + + mgmt->reg = IOMEM(res->start); + + clk = &mgmt->clk; + + clk->name = "sifive-gemgxl-mgmt"; + clk->ops = &fu540_c000_ops; + + err = bclk_register(&mgmt->clk); + if (err) + return err; + + *tx_clk = &mgmt->clk; + + err = clk_enable(*tx_clk); + if (err) { + dev_err(dev, "failed to enable tx_clk (%u)\n", err); + *tx_clk = NULL; + return err; + } + + dev_info(dev, "Registered clk switch '%s'\n", clk->name); + return 0; +} +#else +static int fu540_c000_txclk_init(struct device *dev, struct clk **tx_clk) +{ + return -ENOSYS; +} +#endif + +static int macb_probe(struct device *dev) { struct resource *iores; struct eth_device *edev; struct macb_device *macb; const char *pclk_name, *hclk_name; + const struct macb_config *config = NULL; u32 ncfgr; + int ret; macb = xzalloc(sizeof(*macb)); edev = &macb->netdev; @@ -690,23 +827,25 @@ static int macb_probe(struct device_d *dev) macb->phy_flags = pdata->phy_flags; pclk_name = "macb_clk"; hclk_name = NULL; - } else if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) { + } else if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node) { int ret; struct device_node *mdiobus; - ret = of_get_phy_mode(dev->device_node); + ret = of_get_phy_mode(dev->of_node); if (ret < 0) macb->interface = PHY_INTERFACE_MODE_MII; else macb->interface = ret; - mdiobus = of_get_child_by_name(dev->device_node, "mdio"); + mdiobus = of_get_child_by_name(dev->of_node, "mdio"); if (mdiobus) - macb->miibus.dev.device_node = mdiobus; + macb->miibus.dev.of_node = mdiobus; macb->phy_addr = -1; pclk_name = "pclk"; hclk_name = "hclk"; + + config = device_get_match_data(dev); } else { dev_err(dev, "macb: no platform_data\n"); return -ENODEV; @@ -731,7 +870,7 @@ static int macb_probe(struct device_d *dev) if (hclk_name) { macb->hclk = clk_get(dev, pclk_name); - if (IS_ERR(macb->pclk)) { + if (IS_ERR(macb->hclk)) { dev_err(dev, "no hclk\n"); return PTR_ERR(macb->hclk); } @@ -742,11 +881,19 @@ static int macb_probe(struct device_d *dev) macb->txclk = clk_get(dev, "tx_clk"); if (!IS_ERR(macb->txclk)) clk_enable(macb->txclk); + else + macb->txclk = NULL; macb->rxclk = clk_get(dev, "rx_clk"); if (!IS_ERR(macb->rxclk)) clk_enable(macb->rxclk); + if (config) { + ret = config->txclk_init(dev, &macb->txclk); + if (ret) + return ret; + } + macb->is_gem = read_is_gem(macb); if (macb_is_gem(macb)) @@ -756,13 +903,15 @@ static int macb_probe(struct device_d *dev) macb_init_rx_buffer_size(macb, PKTSIZE); macb->rx_buffer = dma_alloc(macb->rx_buffer_size * macb->rx_ring_size); - macb->rx_ring = dma_alloc_coherent(RX_RING_BYTES(macb), DMA_ADDRESS_BROKEN); - macb->tx_ring = dma_alloc_coherent(TX_RING_BYTES, DMA_ADDRESS_BROKEN); + macb->rx_ring = dma_alloc_coherent(RX_RING_BYTES(macb), &macb->rx_ring_phys); + macb->tx_ring = dma_alloc_coherent(TX_RING_BYTES, &macb->tx_ring_phys); if (macb->is_gem) macb->gem_q1_descs = dma_alloc_coherent(GEM_Q1_DESC_BYTES, DMA_ADDRESS_BROKEN); + macb->rx_packet_buf = xmalloc(PKTSIZE); + macb_reset_hw(macb); ncfgr = macb_mdc_clk_div(macb); ncfgr |= MACB_BIT(PAE); /* PAuse Enable */ @@ -770,7 +919,9 @@ static int macb_probe(struct device_d *dev) ncfgr |= macb_dbw(macb); macb_writel(macb, NCFGR, ncfgr); - macb_init(macb); + ret = macb_init(macb); + if (ret) + return ret; mdiobus_register(&macb->miibus); eth_register(edev); @@ -781,23 +932,32 @@ static int macb_probe(struct device_d *dev) return 0; } -static void macb_remove(struct device_d *dev) +static void macb_remove(struct device *dev) { struct macb_device *macb = dev->priv; macb_halt(&macb->netdev); + + free(macb->rx_packet_buf); } +static const struct macb_config fu540_c000_config = { + .txclk_init = fu540_c000_txclk_init, +}; + static const struct of_device_id macb_dt_ids[] = { { .compatible = "cdns,at91sam9260-macb",}, { .compatible = "atmel,sama5d2-gem",}, { .compatible = "atmel,sama5d3-gem",}, - { .compatible = "cdns,zynq-gem",}, - { .compatible = "cdns,zynqmp-gem",}, + { .compatible = "atmel,sama5d4-gem",}, + { .compatible = "xlnx,zynq-gem",}, + { .compatible = "xlnx,zynqmp-gem",}, + { .compatible = "sifive,fu540-c000-gem", .data = &fu540_c000_config }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, macb_dt_ids); -static struct driver_d macb_driver = { +static struct driver macb_driver = { .name = "macb", .probe = macb_probe, .remove = macb_remove, diff --git a/drivers/net/mvneta.c b/drivers/net/mvneta.c index 359c70c927..1e176dbdc2 100644 --- a/drivers/net/mvneta.c +++ b/drivers/net/mvneta.c @@ -166,7 +166,7 @@ #define MVNETA_MH_SIZE 2 #define TXQ_NUM 8 -#define RX_RING_SIZE 4 +#define RX_RING_SIZE 128 #define TRANSFER_TIMEOUT (10 * MSECOND) struct rxdesc { @@ -198,7 +198,7 @@ struct txdesc { struct mvneta_port { void __iomem *reg; - struct device_d dev; + struct device dev; struct eth_device edev; struct clk *clk; @@ -383,7 +383,7 @@ static int mvneta_send(struct eth_device *edev, void *data, int len) int ret, error, last_desc; /* Flush transmit data */ - dma_sync_single_for_device((unsigned long)data, len, DMA_TO_DEVICE); + dma_sync_single_for_device(&priv->dev, (unsigned long)data, len, DMA_TO_DEVICE); memset(txdesc, 0, sizeof(*txdesc)); /* Fill the Tx descriptor */ @@ -400,7 +400,7 @@ static int mvneta_send(struct eth_device *edev, void *data, int len) * the Tx port status register (PTXS). */ ret = wait_on_timeout(TRANSFER_TIMEOUT, !mvneta_pending_tx(priv)); - dma_sync_single_for_cpu((unsigned long)data, len, DMA_TO_DEVICE); + dma_sync_single_for_cpu(&priv->dev, (unsigned long)data, len, DMA_TO_DEVICE); if (ret) { dev_err(&edev->dev, "transmit timeout\n"); return ret; @@ -451,7 +451,7 @@ static int mvneta_recv(struct eth_device *edev) } /* invalidate current receive buffer */ - dma_sync_single_for_cpu((unsigned long)rxdesc->buf_phys_addr, + dma_sync_single_for_cpu(&priv->dev, (unsigned long)rxdesc->buf_phys_addr, ALIGN(PKTSIZE, 8), DMA_FROM_DEVICE); /* received packet is padded with two null bytes (Marvell header) */ @@ -459,7 +459,7 @@ static int mvneta_recv(struct eth_device *edev) rxdesc->data_size - MVNETA_MH_SIZE); ret = 0; - dma_sync_single_for_device((unsigned long)rxdesc->buf_phys_addr, + dma_sync_single_for_device(&priv->dev, (unsigned long)rxdesc->buf_phys_addr, ALIGN(PKTSIZE, 8), DMA_FROM_DEVICE); recv_err: @@ -706,7 +706,7 @@ static int mvneta_port_config(struct mvneta_port *priv) return 0; } -static int mvneta_probe(struct device_d *dev) +static int mvneta_probe(struct device *dev) { struct mvneta_port *priv; int ret; @@ -720,7 +720,7 @@ static int mvneta_probe(struct device_d *dev) return PTR_ERR(priv->clk); clk_enable(priv->clk); - ret = of_get_phy_mode(dev->device_node); + ret = of_get_phy_mode(dev->of_node); if (ret < 0) return ret; priv->intf = ret; @@ -755,8 +755,9 @@ static struct of_device_id mvneta_dt_ids[] = { { .compatible = "marvell,armada-xp-neta" }, { } }; +MODULE_DEVICE_TABLE(of, mvneta_dt_ids); -static struct driver_d mvneta_driver = { +static struct driver mvneta_driver = { .name = "mvneta", .probe = mvneta_probe, .of_compatible = DRV_OF_COMPAT(mvneta_dt_ids), diff --git a/drivers/net/orion-gbe.c b/drivers/net/orion-gbe.c index 0ae94e6840..e1b763893d 100644 --- a/drivers/net/orion-gbe.c +++ b/drivers/net/orion-gbe.c @@ -42,7 +42,7 @@ struct txdesc { }; struct port_priv { - struct device_d dev; + struct device dev; struct eth_device edev; void __iomem *regs; struct device_node *np; @@ -228,7 +228,7 @@ static int port_send(struct eth_device *edev, void *data, int len) int ret; /* flush transmit data */ - dma_sync_single_for_device((unsigned long)data, len, DMA_TO_DEVICE); + dma_sync_single_for_device(&port->dev, (unsigned long)data, len, DMA_TO_DEVICE); txdesc->cmd_sts = TXDESC_OWNED_BY_DMA; txdesc->cmd_sts |= TXDESC_FIRST | TXDESC_LAST; @@ -243,7 +243,7 @@ static int port_send(struct eth_device *edev, void *data, int len) /* wait for packet transmit completion */ ret = wait_on_timeout(TRANSFER_TIMEOUT, (readl(&txdesc->cmd_sts) & TXDESC_OWNED_BY_DMA) == 0); - dma_sync_single_for_cpu((unsigned long)data, len, DMA_TO_DEVICE); + dma_sync_single_for_cpu(&port->dev, (unsigned long)data, len, DMA_TO_DEVICE); if (ret) { dev_err(&edev->dev, "transmit timeout\n"); return ret; @@ -287,13 +287,13 @@ static int port_recv(struct eth_device *edev) } /* invalidate current receive buffer */ - dma_sync_single_for_cpu((unsigned long)rxdesc->buf_ptr, + dma_sync_single_for_cpu(&port->dev, (unsigned long)rxdesc->buf_ptr, ALIGN(PKTSIZE, 8), DMA_FROM_DEVICE); /* received packet is padded with two null bytes */ net_receive(edev, rxdesc->buf_ptr + 0x2, rxdesc->byte_cnt - 0x2); - dma_sync_single_for_device((unsigned long)rxdesc->buf_ptr, + dma_sync_single_for_device(&port->dev, (unsigned long)rxdesc->buf_ptr, ALIGN(PKTSIZE, 8), DMA_FROM_DEVICE); ret = 0; @@ -387,10 +387,10 @@ static int port_open(struct eth_device *edev) return 0; } -static int port_probe(struct device_d *parent, struct port_priv *port) +static int port_probe(struct device *parent, struct port_priv *port) { struct orion_gbe *gbe = parent->priv; - struct device_d *dev = &port->dev; + struct device *dev = &port->dev; u32 reg; int ret; @@ -451,7 +451,7 @@ static int port_probe(struct device_d *parent, struct port_priv *port) dev_set_name(dev, "%08x.ethernet-port", (u32)gbe->regs); dev->id = port->portno; dev->parent = parent; - dev->device_node = port->np; + dev->of_node = port->np; ret = register_device(dev); if (ret) return ret; @@ -473,7 +473,7 @@ static int port_probe(struct device_d *parent, struct port_priv *port) return 0; } -static int orion_gbe_probe(struct device_d *dev) +static int orion_gbe_probe(struct device *dev) { struct orion_gbe *gbe; struct port_priv *ppriv; @@ -499,13 +499,13 @@ static int orion_gbe_probe(struct device_d *dev) * Although untested, the driver should also be able to * deal with multi-port controllers. */ - for_each_child_of_node(dev->device_node, pnp) + for_each_child_of_node(dev->of_node, pnp) gbe->num_ports++; gbe->ports = xzalloc(gbe->num_ports * sizeof(*gbe->ports)); ppriv = gbe->ports; - for_each_child_of_node(dev->device_node, pnp) { + for_each_child_of_node(dev->of_node, pnp) { ppriv->np = pnp; ret = port_probe(dev, ppriv); @@ -518,7 +518,7 @@ static int orion_gbe_probe(struct device_d *dev) return 0; } -static void orion_gbe_remove(struct device_d *dev) +static void orion_gbe_remove(struct device *dev) { struct orion_gbe *gbe = dev->priv; int n; @@ -538,8 +538,9 @@ static struct of_device_id orion_gbe_dt_ids[] = { { .compatible = "marvell,kirkwood-eth", }, { } }; +MODULE_DEVICE_TABLE(of, orion_gbe_dt_ids); -static struct driver_d orion_gbe_driver = { +static struct driver orion_gbe_driver = { .name = "orion-gbe", .probe = orion_gbe_probe, .remove = orion_gbe_remove, diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 2806af376f..8e12671801 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # PHY Layer Configuration # @@ -28,6 +29,18 @@ config DP83867_PHY help Currently supports the DP83867 PHY. +config DP83TD510_PHY + tristate "Texas Instruments DP83TD510 Ethernet 10Base-T1L PHY" + help + Support for the DP83TD510 Ethernet 10Base-T1L PHY. This PHY supports + a 10M single pair Ethernet connection for up to 1000 meter cable. + +config DP83TG720_PHY + tristate "Texas Instruments DP83TG720 Ethernet 1000Base-T1 PHY" + help + Support for the DP83TG720 Ethernet 10000Base-T1 PHY. This PHY supports + a 1000M single pair Ethernet. + config LXT_PHY bool "Driver for the Intel LXT PHYs" help @@ -43,6 +56,11 @@ config MICREL_PHY help Supports the KSZ9021, VSC8201, KS8001 PHYs. +config MOTORCOMM_PHY + bool "Driver for Motorcomm PHYs" + help + Currently supports the YT8511 PHY. + config NATIONAL_PHY bool "Driver for National Semiconductor PHYs" help @@ -68,7 +86,7 @@ comment "MII bus device drivers" config MDIO_MVEBU bool "Driver for MVEBU SoC MDIO bus" - depends on ARCH_MVEBU + depends on ARCH_MVEBU || COMPILE_TEST help Driver for the MDIO bus found on Marvell EBU SoCs. diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 7053b5762c..ce15e1bab7 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-y += phy.o mdio_bus.o phy-core.o obj-$(CONFIG_AR8327N_PHY) += ar8327.o obj-$(CONFIG_AT803X_PHY) += at803x.o @@ -5,6 +6,7 @@ obj-$(CONFIG_DAVICOM_PHY) += davicom.o obj-$(CONFIG_LXT_PHY) += lxt.o obj-$(CONFIG_MARVELL_PHY) += marvell.o obj-$(CONFIG_MICREL_PHY) += micrel.o +obj-$(CONFIG_MOTORCOMM_PHY) += motorcomm.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_REALTEK_PHY) += realtek.o obj-$(CONFIG_SMSC_PHY) += smsc.o @@ -16,4 +18,6 @@ obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o obj-$(CONFIG_DP83867_PHY) += dp83867.o +obj-$(CONFIG_DP83TD510_PHY) += dp83td510.o +obj-$(CONFIG_DP83TG720_PHY) += dp83tg720.o diff --git a/drivers/net/phy/ar8327.c b/drivers/net/phy/ar8327.c index 5f3a2e2cf2..7717861c74 100644 --- a/drivers/net/phy/ar8327.c +++ b/drivers/net/phy/ar8327.c @@ -132,7 +132,7 @@ static int ar8327n_phy_is_link_alive(struct phy_device *phydev, int phy_addr) static int ar8327n_phy_setup(struct phy_device *phydev) { - struct device_d *dev = &phydev->dev; + struct device *dev = &phydev->dev; int phy_addr; /* start auto negotiation on each phy */ @@ -194,7 +194,7 @@ static int ar8327n_get_link(struct phy_device *phydev) static int ar8327n_config_init(struct phy_device *phydev) { - struct device_d *dev = &phydev->dev; + struct device *dev = &phydev->dev; int phy_addr = 0; if (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID) @@ -268,9 +268,4 @@ static struct phy_driver ar8327n_driver[] = { .aneg_done = &ar8327n_aneg_done, }}; -static int atheros_phy_init(void) -{ - return phy_drivers_register(ar8327n_driver, - ARRAY_SIZE(ar8327n_driver)); -} -fs_initcall(atheros_phy_init); +device_phy_drivers(ar8327n_driver); diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index b43cb0d23e..8d6b879a27 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -11,6 +11,9 @@ #include <init.h> #include <linux/phy.h> #include <linux/string.h> +#include <linux/bitfield.h> +#include <linux/mdio.h> +#include <dt-bindings/net/qca-ar803x.h> #define AT803X_INTR_ENABLE 0x12 #define AT803X_INTR_STATUS 0x13 @@ -24,64 +27,322 @@ #define AT803X_FUNC_DATA 0x4003 #define AT803X_DEBUG_ADDR 0x1D #define AT803X_DEBUG_DATA 0x1E -#define AT803X_DEBUG_SYSTEM_MODE_CTRL 0x05 -#define AT803X_DEBUG_RGMII_TX_CLK_DLY (1 << 8) +#define AT803X_DEBUG_REG_0 0x00 +#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15) +#define AT803X_DEBUG_REG_5 0x05 +#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) -static int at803x_config_init(struct phy_device *phydev) +#define AT803X_DEBUG_REG_HIB_CTRL 0x0b +#define AT803X_DEBUG_HIB_CTRL_PS_HIB_EN BIT(15) + +/* AT803x supports either the XTAL input pad, an internal PLL or the + * DSP as clock reference for the clock output pad. The XTAL reference + * is only used for 25 MHz output, all other frequencies need the PLL. + * The DSP as a clock reference is used in synchronous ethernet + * applications. + * + * By default the PLL is only enabled if there is a link. Otherwise + * the PHY will go into low power state and disabled the PLL. You can + * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always + * enabled. + */ +#define AT803X_MMD7_CLK25M 0x8016 +#define AT803X_CLK_OUT_MASK GENMASK(4, 2) +#define AT803X_CLK_OUT_25MHZ_XTAL 0 +#define AT803X_CLK_OUT_25MHZ_DSP 1 +#define AT803X_CLK_OUT_50MHZ_PLL 2 +#define AT803X_CLK_OUT_50MHZ_DSP 3 +#define AT803X_CLK_OUT_62_5MHZ_PLL 4 +#define AT803X_CLK_OUT_62_5MHZ_DSP 5 +#define AT803X_CLK_OUT_125MHZ_PLL 6 +#define AT803X_CLK_OUT_125MHZ_DSP 7 + +/* The AR8035 has another mask which is compatible with the AR8031/AR8033 mask + * but doesn't support choosing between XTAL/PLL and DSP. + */ +#define AT8035_CLK_OUT_MASK GENMASK(4, 3) + +#define AT803X_MMD3_SMARTEEE_CTL3 0x805d +#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8) + +#define AT803X_CLK_OUT_STRENGTH_MASK GENMASK(8, 7) +#define AT803X_CLK_OUT_STRENGTH_FULL 0 +#define AT803X_CLK_OUT_STRENGTH_HALF 1 +#define AT803X_CLK_OUT_STRENGTH_QUARTER 2 + +#define ATH9331_PHY_ID 0x004dd041 +#define ATH8030_PHY_ID 0x004dd076 +#define ATH8031_PHY_ID 0x004dd074 +#define ATH8032_PHY_ID 0x004dd023 +#define ATH8035_PHY_ID 0x004dd072 +#define AT8030_PHY_ID_MASK 0xffffffef + +struct at803x_priv { + u16 clk_25m_reg; + u16 clk_25m_mask; +}; + +static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg) { int ret; - ret = genphy_config_init(phydev); + ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg); if (ret < 0) return ret; - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { - ret = phy_write(phydev, AT803X_DEBUG_ADDR, - AT803X_DEBUG_SYSTEM_MODE_CTRL); - if (ret) - return ret; - ret = phy_write(phydev, AT803X_DEBUG_DATA, - AT803X_DEBUG_RGMII_TX_CLK_DLY); - if (ret) - return ret; + return phy_read(phydev, AT803X_DEBUG_DATA); +} + +static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, + u16 clear, u16 set) +{ + u16 val; + int ret; + + ret = at803x_debug_reg_read(phydev, reg); + if (ret < 0) + return ret; + + val = ret & 0xffff; + val &= ~clear; + val |= set; + + return phy_write(phydev, AT803X_DEBUG_DATA, val); +} + +static int at803x_enable_rx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0, + AT803X_DEBUG_RX_CLK_DLY_EN); +} + +static int at803x_enable_tx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0, + AT803X_DEBUG_TX_CLK_DLY_EN); +} + +static int at803x_disable_rx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, + AT803X_DEBUG_RX_CLK_DLY_EN, 0); +} + +static int at803x_disable_tx_delay(struct phy_device *phydev) +{ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, + AT803X_DEBUG_TX_CLK_DLY_EN, 0); +} + +static int at803x_hibernation_mode_config(struct phy_device *phydev) +{ + /* The default after hardware reset is hibernation mode enabled. After + * software reset, the value is retained. + */ + return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL, + AT803X_DEBUG_HIB_CTRL_PS_HIB_EN, 0); +} + +static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id) +{ + struct phy_driver *drv = to_phy_driver(phydev->dev.driver); + + return (phydev->phy_id & drv->phy_id_mask) + == (phy_id & drv->phy_id_mask); +} + +static int at803x_parse_dt(struct phy_device *phydev) +{ + const struct device *dev = &phydev->dev; + const struct device_node *node = dev->of_node; + struct at803x_priv *priv = phydev->priv; + unsigned int sel; + u32 freq, strength; + int ret; + + ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq); + if (!ret) { + switch (freq) { + case 25000000: + sel = AT803X_CLK_OUT_25MHZ_XTAL; + break; + case 50000000: + sel = AT803X_CLK_OUT_50MHZ_PLL; + break; + case 62500000: + sel = AT803X_CLK_OUT_62_5MHZ_PLL; + break; + case 125000000: + sel = AT803X_CLK_OUT_125MHZ_PLL; + break; + default: + dev_err(dev, "invalid qca,clk-out-frequency\n"); + return -EINVAL; + } + + priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel); + priv->clk_25m_mask |= AT803X_CLK_OUT_MASK; + + /* Fixup for the AR8030/AR8035. This chip has another mask and + * doesn't support the DSP reference. Eg. the lowest bit of the + * mask. The upper two bits select the same frequencies. Mask + * the lowest bit here. + * + * Warning: + * There was no datasheet for the AR8030 available so this is + * just a guess. But the AR8035 is listed as pin compatible + * to the AR8030 so there might be a good chance it works on + * the AR8030 too. + */ + if (at803x_match_phy_id(phydev, ATH8030_PHY_ID) || + at803x_match_phy_id(phydev, ATH8035_PHY_ID)) { + priv->clk_25m_reg &= AT8035_CLK_OUT_MASK; + priv->clk_25m_mask &= AT8035_CLK_OUT_MASK; + } } + ret = of_property_read_u32(node, "qca,clk-out-strength", &strength); + if (!ret) { + priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK; + switch (strength) { + case AR803X_STRENGTH_FULL: + priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL; + break; + case AR803X_STRENGTH_HALF: + priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF; + break; + case AR803X_STRENGTH_QUARTER: + priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER; + break; + default: + dev_err(dev, "invalid qca,clk-out-strength\n"); + return -EINVAL; + } + } + + return 0; +} + +static int at803x_probe(struct phy_device *phydev) +{ + struct at803x_priv *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + + return at803x_parse_dt(phydev); +} + +static int at803x_smarteee_config(struct phy_device *phydev) +{ + return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3, + AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0); +} + +static int at803x_clk_out_config(struct phy_device *phydev) +{ + struct at803x_priv *priv = phydev->priv; + int val; + + if (!priv->clk_25m_mask) + return 0; + + val = phy_read_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M); + if (val < 0) + return val; + + val &= ~priv->clk_25m_mask; + val |= priv->clk_25m_reg; + + phy_write_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, val); + return 0; } +static int at803x_config_init(struct phy_device *phydev) +{ + int ret; + + ret = genphy_config_init(phydev); + if (ret < 0) + return ret; + + /* The RX and TX delay default is: + * after HW reset: RX delay enabled and TX delay disabled + * after SW reset: RX delay enabled, while TX delay retains the + * value before reset. + */ + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + ret = at803x_enable_rx_delay(phydev); + else + ret = at803x_disable_rx_delay(phydev); + if (ret < 0) + return ret; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + ret = at803x_enable_tx_delay(phydev); + else + ret = at803x_disable_tx_delay(phydev); + if (ret < 0) + return ret; + + ret = at803x_smarteee_config(phydev); + if (ret < 0) + return ret; + + ret = at803x_clk_out_config(phydev); + if (ret < 0) + return ret; + + ret = at803x_hibernation_mode_config(phydev); + if (ret < 0) + return ret; + + /* Ar803x extended next page bit is enabled by default. Cisco + * multigig switches read this bit and attempt to negotiate 10Gbps + * rates even if the next page bit is disabled. This is incorrect + * behaviour but we still need to accommodate it. XNP is only needed + * for 10Gbps support, so disable XNP. + */ + return phy_modify(phydev, MII_ADVERTISE, MDIO_AN_CTRL1_XNP, 0); +} + static struct phy_driver at803x_driver[] = { { /* ATHEROS 8035 */ - .phy_id = 0x004dd072, - .phy_id_mask = 0xffffffef, + .phy_id = ATH8035_PHY_ID, + .phy_id_mask = AT8030_PHY_ID_MASK, .drv.name = "Atheros 8035 ethernet", + .probe = at803x_probe, .config_init = at803x_config_init, .features = PHY_GBIT_FEATURES, .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, }, { /* ATHEROS 8030 */ - .phy_id = 0x004dd076, - .phy_id_mask = 0xffffffef, + .phy_id = ATH8030_PHY_ID, + .phy_id_mask = AT8030_PHY_ID_MASK, .drv.name = "Atheros 8030 ethernet", .config_init = at803x_config_init, + .probe = at803x_probe, .features = PHY_GBIT_FEATURES, .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, }, { /* ATHEROS 8031 */ - .phy_id = 0x004dd074, - .phy_id_mask = 0xffffffef, + .phy_id = ATH8031_PHY_ID, + .phy_id_mask = AT8030_PHY_ID_MASK, .drv.name = "Atheros 8031 ethernet", + .probe = at803x_probe, .config_init = at803x_config_init, .features = PHY_GBIT_FEATURES, .config_aneg = &genphy_config_aneg, .read_status = &genphy_read_status, } }; -static int atheros_phy_init(void) -{ - return phy_drivers_register(at803x_driver, - ARRAY_SIZE(at803x_driver)); -} -fs_initcall(atheros_phy_init); +device_phy_drivers(at803x_driver); diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c index febaffa52c..794e5f2c96 100644 --- a/drivers/net/phy/davicom.c +++ b/drivers/net/phy/davicom.c @@ -127,9 +127,4 @@ static struct phy_driver dm91xx_driver[] = { .features = PHY_BASIC_FEATURES, } }; -static int dm9161_init(void) -{ - return phy_drivers_register(dm91xx_driver, - ARRAY_SIZE(dm91xx_driver)); -} -fs_initcall(dm9161_init); +device_phy_drivers(dm91xx_driver); diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index b3328b7e44..5dc5bac125 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -1,16 +1,7 @@ -/* - * Driver for the Texas Instruments DP83867 PHY +// SPDX-License-Identifier: GPL-2.0 +/* Driver for the Texas Instruments DP83867 PHY * * Copyright (C) 2015 Texas Instruments Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <common.h> @@ -18,24 +9,46 @@ #include <linux/phy.h> #include <dt-bindings/net/ti-dp83867.h> +#include <linux/mdio.h> #define DP83867_PHY_ID 0x2000a231 #define DP83867_DEVADDR 0x1f #define MII_DP83867_PHYCTRL 0x10 +#define MII_DP83867_PHYSTS 0x11 #define MII_DP83867_MICR 0x12 #define MII_DP83867_ISR 0x13 -#define MII_DP83867_CFG2 0x14 -#define MII_DP83867_BISCR 0x16 -#define DP83867_CTRL 0x1f +#define DP83867_CFG2 0x14 +#define DP83867_LEDCR1 0x18 +#define DP83867_LEDCR2 0x19 #define DP83867_CFG3 0x1e +#define DP83867_CTRL 0x1f /* Extended Registers */ +#define DP83867_FLD_THR_CFG 0x002e #define DP83867_CFG4 0x0031 +#define DP83867_CFG4_SGMII_ANEG_MASK (BIT(5) | BIT(6)) +#define DP83867_CFG4_SGMII_ANEG_TIMER_11MS (3 << 5) +#define DP83867_CFG4_SGMII_ANEG_TIMER_800US (2 << 5) +#define DP83867_CFG4_SGMII_ANEG_TIMER_2US (1 << 5) +#define DP83867_CFG4_SGMII_ANEG_TIMER_16MS (0 << 5) + #define DP83867_RGMIICTL 0x0032 #define DP83867_STRAP_STS1 0x006E +#define DP83867_STRAP_STS2 0x006f #define DP83867_RGMIIDCTL 0x0086 +#define DP83867_DSP_FFE_CFG 0x012c +#define DP83867_RXFCFG 0x0134 +#define DP83867_RXFPMD1 0x0136 +#define DP83867_RXFPMD2 0x0137 +#define DP83867_RXFPMD3 0x0138 +#define DP83867_RXFSOP1 0x0139 +#define DP83867_RXFSOP2 0x013A +#define DP83867_RXFSOP3 0x013B #define DP83867_IO_MUX_CFG 0x0170 +#define DP83867_SGMIICTL 0x00D3 +#define DP83867_10M_SGMII_CFG 0x016F +#define DP83867_10M_SGMII_RATE_ADAPT_MASK BIT(7) #define DP83867_SW_RESET BIT(15) #define DP83867_SW_RESTART BIT(14) @@ -58,48 +71,86 @@ #define DP83867_RGMII_TX_CLK_DELAY_EN BIT(1) #define DP83867_RGMII_RX_CLK_DELAY_EN BIT(0) +/* SGMIICTL bits */ +#define DP83867_SGMII_TYPE BIT(14) + +/* RXFCFG bits*/ +#define DP83867_WOL_MAGIC_EN BIT(0) +#define DP83867_WOL_BCAST_EN BIT(2) +#define DP83867_WOL_UCAST_EN BIT(4) +#define DP83867_WOL_SEC_EN BIT(5) +#define DP83867_WOL_ENH_MAC BIT(7) + /* STRAP_STS1 bits */ #define DP83867_STRAP_STS1_RESERVED BIT(11) +/* STRAP_STS2 bits */ +#define DP83867_STRAP_STS2_CLK_SKEW_TX_MASK GENMASK(6, 4) +#define DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT 4 +#define DP83867_STRAP_STS2_CLK_SKEW_RX_MASK GENMASK(2, 0) +#define DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT 0 +#define DP83867_STRAP_STS2_CLK_SKEW_NONE BIT(2) +#define DP83867_STRAP_STS2_STRAP_FLD BIT(10) + /* PHY CTRL bits */ -#define DP83867_PHYCR_FIFO_DEPTH_SHIFT 14 -#define DP83867_PHYCR_FIFO_DEPTH_MASK (3 << 14) -#define DP83867_MDI_CROSSOVER 5 -#define DP83867_MDI_CROSSOVER_AUTO 0b10 -#define DP83867_MDI_CROSSOVER_MDIX 0b01 -#define DP83867_PHYCTRL_SGMIIEN 0x0800 -#define DP83867_PHYCTRL_RXFIFO_SHIFT 12 -#define DP83867_PHYCTRL_TXFIFO_SHIFT 14 +#define DP83867_PHYCR_TX_FIFO_DEPTH_SHIFT 14 +#define DP83867_PHYCR_RX_FIFO_DEPTH_SHIFT 12 +#define DP83867_PHYCR_FIFO_DEPTH_MAX 0x03 +#define DP83867_PHYCR_TX_FIFO_DEPTH_MASK GENMASK(15, 14) +#define DP83867_PHYCR_RX_FIFO_DEPTH_MASK GENMASK(13, 12) #define DP83867_PHYCR_RESERVED_MASK BIT(11) +#define DP83867_PHYCR_FORCE_LINK_GOOD BIT(10) /* RGMIIDCTL bits */ +#define DP83867_RGMII_TX_CLK_DELAY_MAX 0xf #define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4 +#define DP83867_RGMII_TX_CLK_DELAY_INV (DP83867_RGMII_TX_CLK_DELAY_MAX + 1) +#define DP83867_RGMII_RX_CLK_DELAY_MAX 0xf +#define DP83867_RGMII_RX_CLK_DELAY_SHIFT 0 +#define DP83867_RGMII_RX_CLK_DELAY_INV (DP83867_RGMII_RX_CLK_DELAY_MAX + 1) + +/* IO_MUX_CFG bits */ +#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK 0x1f +#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0 +#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f +#define DP83867_IO_MUX_CFG_CLK_O_DISABLE BIT(6) +#define DP83867_IO_MUX_CFG_CLK_O_SEL_MASK (0x1f << 8) +#define DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT 8 + +/* PHY STS bits */ +#define DP83867_PHYSTS_1000 BIT(15) +#define DP83867_PHYSTS_100 BIT(14) +#define DP83867_PHYSTS_DUPLEX BIT(13) +#define DP83867_PHYSTS_LINK BIT(10) /* CFG2 bits */ -#define MII_DP83867_CFG2_SPEEDOPT_10EN 0x0040 -#define MII_DP83867_CFG2_SGMII_AUTONEGEN 0x0080 -#define MII_DP83867_CFG2_SPEEDOPT_ENH 0x0100 -#define MII_DP83867_CFG2_SPEEDOPT_CNT 0x0800 -#define MII_DP83867_CFG2_SPEEDOPT_INTLOW 0x2000 -#define MII_DP83867_CFG2_MASK 0x003F +#define DP83867_DOWNSHIFT_EN (BIT(8) | BIT(9)) +#define DP83867_DOWNSHIFT_ATTEMPT_MASK (BIT(10) | BIT(11)) +#define DP83867_DOWNSHIFT_1_COUNT_VAL 0 +#define DP83867_DOWNSHIFT_2_COUNT_VAL 1 +#define DP83867_DOWNSHIFT_4_COUNT_VAL 2 +#define DP83867_DOWNSHIFT_8_COUNT_VAL 3 +#define DP83867_DOWNSHIFT_1_COUNT 1 +#define DP83867_DOWNSHIFT_2_COUNT 2 +#define DP83867_DOWNSHIFT_4_COUNT 4 +#define DP83867_DOWNSHIFT_8_COUNT 8 +#define DP83867_SGMII_AUTONEG_EN BIT(7) + +/* CFG3 bits */ +#define DP83867_CFG3_INT_OE BIT(7) +#define DP83867_CFG3_ROBUST_AUTO_MDIX BIT(9) /* CFG4 bits */ -#define DP83867_CFG4_SGMII_AUTONEG_TIMER_MASK 0x60 -#define DP83867_CFG4_SGMII_AUTONEG_TIMER_16MS 0x00 -#define DP83867_CFG4_SGMII_AUTONEG_TIMER_2US 0x20 -#define DP83867_CFG4_SGMII_AUTONEG_TIMER_800US 0x40 -#define DP83867_CFG4_SGMII_AUTONEG_TIMER_11MS 0x60 -#define DP83867_CFG4_RESVDBIT7 BIT(7) -#define DP83867_CFG4_RESVDBIT8 BIT(8) +#define DP83867_CFG4_PORT_MIRROR_EN BIT(0) -/* IO_MUX_CFG bits */ -#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL 0x1f +/* FLD_THR_CFG */ +#define DP83867_FLD_THR_CFG_ENERGY_LOST_THR_MASK 0x7 -#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX 0x0 -#define DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN 0x1f +#define DP83867_LED_COUNT 4 -/* CFG4 bits */ -#define DP83867_CFG4_PORT_MIRROR_EN BIT(0) +/* LED_DRV bits */ +#define DP83867_LED_DRV_EN(x) BIT((x) * 4) +#define DP83867_LED_DRV_VAL(x) BIT((x) * 4 + 1) enum { DP83867_PORT_MIRROING_KEEP, @@ -108,27 +159,112 @@ enum { }; struct dp83867_private { - int rx_id_delay; - int tx_id_delay; - int fifo_depth; + u32 rx_id_delay; + u32 tx_id_delay; + u32 tx_fifo_depth; + u32 rx_fifo_depth; int io_impedance; int port_mirroring; bool rxctrl_strap_quirk; + bool set_clk_output; + u32 clk_output_sel; + bool sgmii_ref_clk_en; }; -static int dp83867_config_port_mirroring(struct phy_device *phydev) +static int dp83867_read_status(struct phy_device *phydev) { - struct dp83867_private *dp83867 = (struct dp83867_private *)phydev->priv; - u16 val; + int status = phy_read(phydev, MII_DP83867_PHYSTS); + int ret; - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR); + ret = genphy_read_status(phydev); + if (ret) + return ret; + + if (status < 0) + return status; + + if (status & DP83867_PHYSTS_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + if (status & DP83867_PHYSTS_1000) + phydev->speed = SPEED_1000; + else if (status & DP83867_PHYSTS_100) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + + return 0; +} + +static int dp83867_config_port_mirroring(struct phy_device *phydev) +{ + struct dp83867_private *dp83867 = phydev->priv; if (dp83867->port_mirroring == DP83867_PORT_MIRROING_EN) - val |= DP83867_CFG4_PORT_MIRROR_EN; + phy_set_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, + DP83867_CFG4_PORT_MIRROR_EN); else - val &= ~DP83867_CFG4_PORT_MIRROR_EN; + phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, + DP83867_CFG4_PORT_MIRROR_EN); + return 0; +} - phy_write_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR, val); +static int dp83867_verify_rgmii_cfg(struct phy_device *phydev) +{ + struct dp83867_private *dp83867 = phydev->priv; + + /* Existing behavior was to use default pin strapping delay in rgmii + * mode, but rgmii should have meant no delay. Warn existing users. + */ + if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { + const u16 val = phy_read_mmd(phydev, DP83867_DEVADDR, + DP83867_STRAP_STS2); + const u16 txskew = (val & DP83867_STRAP_STS2_CLK_SKEW_TX_MASK) >> + DP83867_STRAP_STS2_CLK_SKEW_TX_SHIFT; + const u16 rxskew = (val & DP83867_STRAP_STS2_CLK_SKEW_RX_MASK) >> + DP83867_STRAP_STS2_CLK_SKEW_RX_SHIFT; + + if (txskew != DP83867_STRAP_STS2_CLK_SKEW_NONE || + rxskew != DP83867_STRAP_STS2_CLK_SKEW_NONE) + phydev_warn(phydev, + "PHY has delays via pin strapping, but phy-mode = 'rgmii'\n" + "Should be 'rgmii-id' to use internal delays txskew:%x rxskew:%x\n", + txskew, rxskew); + } + + /* RX delay *must* be specified if internal delay of RX is used. */ + if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) && + dp83867->rx_id_delay == DP83867_RGMII_RX_CLK_DELAY_INV) { + phydev_err(phydev, "ti,rx-internal-delay must be specified\n"); + return -EINVAL; + } + + /* TX delay *must* be specified if internal delay of TX is used. */ + if ((phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || + phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) && + dp83867->tx_id_delay == DP83867_RGMII_TX_CLK_DELAY_INV) { + phydev_err(phydev, "ti,tx-internal-delay must be specified\n"); + return -EINVAL; + } + + return 0; +} + +static int dp83867_of_init_io_impedance(struct phy_device *phydev) +{ + struct dp83867_private *dp83867 = phydev->priv; + struct device *dev = &phydev->dev; + struct device_node *of_node = dev->of_node; + + if (of_property_read_bool(of_node, "ti,max-output-impedance")) + dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX; + else if (of_property_read_bool(of_node, "ti,min-output-impedance")) + dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN; + else + dp83867->io_impedance = -1; /* leave at default */ return 0; } @@ -136,36 +272,59 @@ static int dp83867_config_port_mirroring(struct phy_device *phydev) static int dp83867_of_init(struct phy_device *phydev) { struct dp83867_private *dp83867 = phydev->priv; - struct device_d *dev = &phydev->dev; - struct device_node *of_node = dev->device_node; + struct device *dev = &phydev->dev; + struct device_node *of_node = dev->of_node; int ret; if (!of_node) return -ENODEV; - dp83867->io_impedance = -EINVAL; - /* Optional configuration */ - if (of_property_read_bool(of_node, "ti,max-output-impedance")) - dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MAX; - else if (of_property_read_bool(of_node, "ti,min-output-impedance")) - dp83867->io_impedance = DP83867_IO_MUX_CFG_IO_IMPEDANCE_MIN; + ret = of_property_read_u32(of_node, "ti,clk-output-sel", + &dp83867->clk_output_sel); + /* If not set, keep default */ + if (!ret) { + dp83867->set_clk_output = true; + /* Valid values are 0 to DP83867_CLK_O_SEL_REF_CLK or + * DP83867_CLK_O_SEL_OFF. + */ + if (dp83867->clk_output_sel > DP83867_CLK_O_SEL_REF_CLK && + dp83867->clk_output_sel != DP83867_CLK_O_SEL_OFF) { + phydev_err(phydev, "ti,clk-output-sel value %u out of range\n", + dp83867->clk_output_sel); + return -EINVAL; + } + } + + ret = dp83867_of_init_io_impedance(phydev); + if (ret) + return ret; - dp83867->rxctrl_strap_quirk = - of_property_read_bool(of_node, - "ti,dp83867-rxctrl-strap-quirk"); + dp83867->rxctrl_strap_quirk = of_property_read_bool(of_node, + "ti,dp83867-rxctrl-strap-quirk"); + dp83867->sgmii_ref_clk_en = of_property_read_bool(of_node, + "ti,sgmii-ref-clock-output-enable"); + + dp83867->rx_id_delay = DP83867_RGMII_RX_CLK_DELAY_INV; ret = of_property_read_u32(of_node, "ti,rx-internal-delay", - &dp83867->rx_id_delay); - if (ret && (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)) - return ret; + &dp83867->rx_id_delay); + if (!ret && dp83867->rx_id_delay > DP83867_RGMII_RX_CLK_DELAY_MAX) { + phydev_err(phydev, + "ti,rx-internal-delay value of %u out of range\n", + dp83867->rx_id_delay); + return -EINVAL; + } + dp83867->tx_id_delay = DP83867_RGMII_TX_CLK_DELAY_INV; ret = of_property_read_u32(of_node, "ti,tx-internal-delay", - &dp83867->tx_id_delay); - if (ret && (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) - return ret; + &dp83867->tx_id_delay); + if (!ret && dp83867->tx_id_delay > DP83867_RGMII_TX_CLK_DELAY_MAX) { + phydev_err(phydev, + "ti,tx-internal-delay value of %u out of range\n", + dp83867->tx_id_delay); + return -EINVAL; + } if (of_property_read_bool(of_node, "enet-phy-lane-swap")) dp83867->port_mirroring = DP83867_PORT_MIRROING_EN; @@ -173,151 +332,252 @@ static int dp83867_of_init(struct phy_device *phydev) if (of_property_read_bool(of_node, "enet-phy-lane-no-swap")) dp83867->port_mirroring = DP83867_PORT_MIRROING_DIS; - return of_property_read_u32(of_node, "ti,fifo-depth", - &dp83867->fifo_depth); -} + ret = of_property_read_u32(of_node, "ti,fifo-depth", + &dp83867->tx_fifo_depth); + if (ret) { + ret = of_property_read_u32(of_node, "tx-fifo-depth", + &dp83867->tx_fifo_depth); + if (ret) + dp83867->tx_fifo_depth = + DP83867_PHYCR_FIFO_DEPTH_4_B_NIB; + } -static inline bool phy_interface_is_rgmii(struct phy_device *phydev) -{ - return phydev->interface >= PHY_INTERFACE_MODE_RGMII && - phydev->interface <= PHY_INTERFACE_MODE_RGMII_TXID; + if (dp83867->tx_fifo_depth > DP83867_PHYCR_FIFO_DEPTH_MAX) { + phydev_err(phydev, "tx-fifo-depth value %u out of range\n", + dp83867->tx_fifo_depth); + return -EINVAL; + } + + ret = of_property_read_u32(of_node, "rx-fifo-depth", + &dp83867->rx_fifo_depth); + if (ret) + dp83867->rx_fifo_depth = DP83867_PHYCR_FIFO_DEPTH_4_B_NIB; + + if (dp83867->rx_fifo_depth > DP83867_PHYCR_FIFO_DEPTH_MAX) { + phydev_err(phydev, "rx-fifo-depth value %u out of range\n", + dp83867->rx_fifo_depth); + return -EINVAL; + } + + return 0; } -static inline bool phy_interface_is_sgmii(struct phy_device *phydev) +static int dp83867_probe(struct phy_device *phydev) { - return phydev->interface == PHY_INTERFACE_MODE_SGMII || - phydev->interface == PHY_INTERFACE_MODE_QSGMII; + struct dp83867_private *dp83867; + + dp83867 = xzalloc(sizeof(*dp83867)); + + phydev->priv = dp83867; + + return dp83867_of_init(phydev); } static int dp83867_config_init(struct phy_device *phydev) { - struct dp83867_private *dp83867; - int ret; - u16 val, delay, cfg2; + struct dp83867_private *dp83867 = phydev->priv; + int ret, val, bs; + u16 delay; - if (!phydev->priv) { - dp83867 = kzalloc(sizeof(*dp83867), GFP_KERNEL); - if (!dp83867) - return -ENOMEM; + /* Force speed optimization for the PHY even if it strapped */ + ret = phy_modify(phydev, DP83867_CFG2, DP83867_DOWNSHIFT_EN, + DP83867_DOWNSHIFT_EN); + if (ret) + return ret; + + ret = dp83867_verify_rgmii_cfg(phydev); + if (ret) + return ret; - phydev->priv = dp83867; - ret = dp83867_of_init(phydev); + /* RX_DV/RX_CTRL strapped in mode 1 or mode 2 workaround */ + if (dp83867->rxctrl_strap_quirk) + phy_clear_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, + BIT(7)); + + bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS2); + if (bs & DP83867_STRAP_STS2_STRAP_FLD) { + /* When using strap to enable FLD, the ENERGY_LOST_FLD_THR will + * be set to 0x2. This may causes the PHY link to be unstable - + * the default value 0x1 need to be restored. + */ + ret = phy_modify_mmd(phydev, DP83867_DEVADDR, + DP83867_FLD_THR_CFG, + DP83867_FLD_THR_CFG_ENERGY_LOST_THR_MASK, + 0x1); if (ret) return ret; - } else { - dp83867 = (struct dp83867_private *)phydev->priv; } - /* Restart the PHY. */ - val = phy_read(phydev, DP83867_CTRL); - phy_write(phydev, DP83867_CTRL, val | DP83867_SW_RESTART); + if (phy_interface_is_rgmii(phydev) || + phydev->interface == PHY_INTERFACE_MODE_SGMII) { + val = phy_read(phydev, MII_DP83867_PHYCTRL); + if (val < 0) + return val; - if (dp83867->rxctrl_strap_quirk) { - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, - DP83867_DEVADDR); - val &= ~BIT(7); - phy_write_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR, - val); - } + val &= ~DP83867_PHYCR_TX_FIFO_DEPTH_MASK; + val |= (dp83867->tx_fifo_depth << + DP83867_PHYCR_TX_FIFO_DEPTH_SHIFT); + + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + val &= ~DP83867_PHYCR_RX_FIFO_DEPTH_MASK; + val |= (dp83867->rx_fifo_depth << + DP83867_PHYCR_RX_FIFO_DEPTH_SHIFT); + } - if (phy_interface_is_rgmii(phydev)) { - val = DP83867_MDI_CROSSOVER_AUTO << DP83867_MDI_CROSSOVER | - dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT; ret = phy_write(phydev, MII_DP83867_PHYCTRL, val); if (ret) return ret; - } else if (phy_interface_is_sgmii(phydev)) { - phy_write(phydev, MII_BMCR, BMCR_ANENABLE | - BMCR_FULLDPLX | - BMCR_SPEED1000); - - cfg2 = phy_read(phydev, MII_DP83867_CFG2); - cfg2 &= MII_DP83867_CFG2_MASK; - cfg2 |= MII_DP83867_CFG2_SPEEDOPT_10EN | - MII_DP83867_CFG2_SGMII_AUTONEGEN | - MII_DP83867_CFG2_SPEEDOPT_ENH | - MII_DP83867_CFG2_SPEEDOPT_CNT | - MII_DP83867_CFG2_SPEEDOPT_INTLOW; - - phy_write(phydev, MII_DP83867_CFG2, cfg2); - - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, 0x0); - - val = DP83867_PHYCTRL_SGMIIEN | - DP83867_MDI_CROSSOVER_MDIX << DP83867_MDI_CROSSOVER | - dp83867->fifo_depth << DP83867_PHYCTRL_RXFIFO_SHIFT | - dp83867->fifo_depth << DP83867_PHYCTRL_TXFIFO_SHIFT; - - phy_write(phydev, MII_DP83867_PHYCTRL, val); - phy_write(phydev, MII_DP83867_BISCR, 0x0); } if (phy_interface_is_rgmii(phydev)) { - val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR); - - switch (phydev->interface) { - case PHY_INTERFACE_MODE_RGMII_ID: - val |= (DP83867_RGMII_TX_CLK_DELAY_EN - | DP83867_RGMII_RX_CLK_DELAY_EN); - break; - case PHY_INTERFACE_MODE_RGMII_TXID: + val = phy_read(phydev, MII_DP83867_PHYCTRL); + if (val < 0) + return val; + + /* The code below checks if "port mirroring" N/A MODE4 has been + * enabled during power on bootstrap. + * + * Such N/A mode enabled by mistake can put PHY IC in some + * internal testing mode and disable RGMII transmission. + * + * In this particular case one needs to check STRAP_STS1 + * register's bit 11 (marked as RESERVED). + */ + + bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS1); + if (bs & DP83867_STRAP_STS1_RESERVED) + val &= ~DP83867_PHYCR_RESERVED_MASK; + + ret = phy_write(phydev, MII_DP83867_PHYCTRL, val); + if (ret) + return ret; + + /* If rgmii mode with no internal delay is selected, we do NOT use + * aligned mode as one might expect. Instead we use the PHY's default + * based on pin strapping. And the "mode 0" default is to *use* + * internal delay with a value of 7 (2.00 ns). + * + * Set up RGMII delays + */ + val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL); + + val &= ~(DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN); + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) val |= DP83867_RGMII_TX_CLK_DELAY_EN; - break; - case PHY_INTERFACE_MODE_RGMII_RXID: + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) val |= DP83867_RGMII_RX_CLK_DELAY_EN; - break; - default: - break; - } - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, val); - delay = (dp83867->rx_id_delay | - (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT)); + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val); - phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL, - DP83867_DEVADDR, delay); + delay = 0; + if (dp83867->rx_id_delay != DP83867_RGMII_RX_CLK_DELAY_INV) + delay |= dp83867->rx_id_delay; + if (dp83867->tx_id_delay != DP83867_RGMII_TX_CLK_DELAY_INV) + delay |= dp83867->tx_id_delay << + DP83867_RGMII_TX_CLK_DELAY_SHIFT; - if (dp83867->io_impedance >= 0) { - val = phy_read_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR); - val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; - val |= dp83867->io_impedance - & DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL, + delay); + } - phy_write_mmd_indirect(phydev, DP83867_IO_MUX_CFG, - DP83867_DEVADDR, val); - } + /* If specified, set io impedance */ + if (dp83867->io_impedance >= 0) + phy_modify_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG, + DP83867_IO_MUX_CFG_IO_IMPEDANCE_MASK, + dp83867->io_impedance); + + if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { + /* For support SPEED_10 in SGMII mode + * DP83867_10M_SGMII_RATE_ADAPT bit + * has to be cleared by software. That + * does not affect SPEED_100 and + * SPEED_1000. + */ + ret = phy_modify_mmd(phydev, DP83867_DEVADDR, + DP83867_10M_SGMII_CFG, + DP83867_10M_SGMII_RATE_ADAPT_MASK, + 0); + if (ret) + return ret; + + /* After reset SGMII Autoneg timer is set to 2us (bits 6 and 5 + * are 01). That is not enough to finalize autoneg on some + * devices. Increase this timer duration to maximum 16ms. + */ + ret = phy_modify_mmd(phydev, DP83867_DEVADDR, + DP83867_CFG4, + DP83867_CFG4_SGMII_ANEG_MASK, + DP83867_CFG4_SGMII_ANEG_TIMER_16MS); + + if (ret) + return ret; + + val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIICTL); + /* SGMII type is set to 4-wire mode by default. + * If we place appropriate property in dts (see above) + * switch on 6-wire mode. + */ + if (dp83867->sgmii_ref_clk_en) + val |= DP83867_SGMII_TYPE; + else + val &= ~DP83867_SGMII_TYPE; + phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIICTL, val); + + /* This is a SW workaround for link instability if RX_CTRL is + * not strapped to mode 3 or 4 in HW. This is required for SGMII + * in addition to clearing bit 7, handled above. + */ + if (dp83867->rxctrl_strap_quirk) + phy_set_bits_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, + BIT(8)); } - genphy_config_aneg(phydev); + val = phy_read(phydev, DP83867_CFG3); + + val |= DP83867_CFG3_ROBUST_AUTO_MDIX; + phy_write(phydev, DP83867_CFG3, val); if (dp83867->port_mirroring != DP83867_PORT_MIRROING_KEEP) dp83867_config_port_mirroring(phydev); - dev_info(&phydev->dev, "DP83867\n"); + /* Clock output selection if muxing property is set */ + if (dp83867->set_clk_output) { + u16 mask = DP83867_IO_MUX_CFG_CLK_O_DISABLE; + + if (dp83867->clk_output_sel == DP83867_CLK_O_SEL_OFF) { + val = DP83867_IO_MUX_CFG_CLK_O_DISABLE; + } else { + mask |= DP83867_IO_MUX_CFG_CLK_O_SEL_MASK; + val = dp83867->clk_output_sel << + DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT; + } + + phy_modify_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG, + mask, val); + } return 0; } static struct phy_driver dp83867_driver[] = { - { - .phy_id = DP83867_PHY_ID, - .phy_id_mask = 0xfffffff0, - .drv.name = "TI DP83867", - .features = PHY_GBIT_FEATURES, + { + .phy_id = DP83867_PHY_ID, + .phy_id_mask = 0xfffffff0, + .drv.name = "TI DP83867", + .features = PHY_GBIT_FEATURES, - .config_init = dp83867_config_init, + .probe = dp83867_probe, + .config_init = dp83867_config_init, - .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, - }, + .read_status = dp83867_read_status, + }, }; +device_phy_drivers(dp83867_driver); -static int dp83867_phy_init(void) -{ - return phy_drivers_register(dp83867_driver, ARRAY_SIZE(dp83867_driver)); -} -fs_initcall(dp83867_phy_init); +MODULE_DESCRIPTION("Texas Instruments DP83867 PHY driver"); +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/phy/dp83td510.c b/drivers/net/phy/dp83td510.c new file mode 100644 index 0000000000..44c551e795 --- /dev/null +++ b/drivers/net/phy/dp83td510.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <common.h> +#include <linux/phy.h> + +#define DP83TD510E_PHY_ID 0x20000181 + +#define DP83TD510E_PHY_STS 0x10 +#define DP83TD510E_LINK_STATUS BIT(0) + +static int dp83td510_read_status(struct phy_device *phydev) +{ + u16 phy_sts; + + phy_sts = phy_read(phydev, DP83TD510E_PHY_STS); + + phydev->link = !!(phy_sts & DP83TD510E_LINK_STATUS); + if (phydev->link) { + phydev->duplex = DUPLEX_FULL; + phydev->speed = SPEED_10; + } else { + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + } + + return 0; +} + +static int dp83td510_config_init(struct phy_device *phydev) +{ + phydev->supported = SUPPORTED_10baseT_Full | SUPPORTED_Autoneg; + phydev->advertising = SUPPORTED_10baseT_Full | SUPPORTED_Autoneg; + + return 0; +} + +static struct phy_driver dp83td510_driver[] = { + { + PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID), + .drv.name = "TI DP83TD510E", + .read_status = dp83td510_read_status, + .config_init = dp83td510_config_init, + } +}; +device_phy_drivers(dp83td510_driver); diff --git a/drivers/net/phy/dp83tg720.c b/drivers/net/phy/dp83tg720.c new file mode 100644 index 0000000000..0571f4cb52 --- /dev/null +++ b/drivers/net/phy/dp83tg720.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Driver for the Texas Instruments DP83TG720 PHY + * Copyright (c) 2023 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> + */ +#include <common.h> +#include <linux/mdio.h> +#include <linux/phy.h> + +#define DP83TG720S_PHY_ID 0x2000a284 + +/* MDIO_MMD_VEND2 registers */ +#define DP83TG720S_MII_REG_10 0x10 +#define DP83TG720S_LINK_STATUS BIT(0) + +#define DP83TG720S_RGMII_DELAY_CTRL 0x602 +/* In RGMII mode, Enable or disable the internal delay for RXD */ +#define DP83TG720S_RGMII_RX_CLK_SEL BIT(1) +/* In RGMII mode, Enable or disable the internal delay for TXD */ +#define DP83TG720S_RGMII_TX_CLK_SEL BIT(0) + +#define DP83TG720S_PHY_RESET 0x1f +#define DP83TG720S_HW_RESET BIT(15) + +static int dp83tg720_config_rgmii_delay(struct phy_device *phydev) +{ + u16 rgmii_delay_mask; + u16 rgmii_delay = 0; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + rgmii_delay = 0; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + rgmii_delay = DP83TG720S_RGMII_RX_CLK_SEL | + DP83TG720S_RGMII_TX_CLK_SEL; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + rgmii_delay = DP83TG720S_RGMII_RX_CLK_SEL; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + rgmii_delay = DP83TG720S_RGMII_TX_CLK_SEL; + break; + default: + return 0; + } + + rgmii_delay_mask = DP83TG720S_RGMII_RX_CLK_SEL | + DP83TG720S_RGMII_TX_CLK_SEL; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, + DP83TG720S_RGMII_DELAY_CTRL, rgmii_delay_mask, + rgmii_delay); +} + +static int dp83tg720_phy_init(struct phy_device *phydev) +{ + /* HW reset is needed to recover link if previous link was lost. SW + * reset is not enough. + */ + phy_write(phydev, DP83TG720S_PHY_RESET, DP83TG720S_HW_RESET); + + phydev->supported = SUPPORTED_1000baseT_Full; + phydev->advertising = SUPPORTED_1000baseT_Full; + + if (phy_interface_is_rgmii(phydev)) + return dp83tg720_config_rgmii_delay(phydev); + + return 0; +} + +static int dp83tg720_read_status(struct phy_device *phydev) +{ + u16 phy_sts; + + phy_sts = phy_read(phydev, DP83TG720S_MII_REG_10); + phydev->link = !!(phy_sts & DP83TG720S_LINK_STATUS); + if (!phydev->link) { + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; + + /* According to the "DP83TC81x, DP83TG72x Software + * Implementation Guide", the PHY needs to be reset after a + * link loss or if no link is created after at least 100ms. + */ + dp83tg720_phy_init(phydev); + return 0; + } + + phydev->duplex = DUPLEX_FULL; + phydev->speed = SPEED_1000; + + return 0; +} + +static struct phy_driver dp83tg720_driver[] = { + { + PHY_ID_MATCH_MODEL(DP83TG720S_PHY_ID), + .drv.name = "TI DP83TG720S", + .read_status = dp83tg720_read_status, + .config_init = dp83tg720_phy_init, + } +}; +device_phy_drivers(dp83tg720_driver); diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c index b661ae7316..9b023c8c40 100644 --- a/drivers/net/phy/lxt.c +++ b/drivers/net/phy/lxt.c @@ -19,9 +19,4 @@ static struct phy_driver lxt97x_driver[] = { .features = PHY_BASIC_FEATURES, } }; -static int lxt97x_phy_init(void) -{ - return phy_drivers_register(lxt97x_driver, - ARRAY_SIZE(lxt97x_driver)); -} -fs_initcall(lxt97x_phy_init); +device_phy_drivers(lxt97x_driver); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 73d6453b36..c0b819b109 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/net/phy/marvell.c * @@ -168,12 +169,6 @@ static int marvell_read_status(struct phy_device *phydev) #define MII_88E1510_GEN_CTRL_REG_1 0x14 -static inline bool phy_interface_is_rgmii(struct phy_device *phydev) -{ - return phydev->interface >= PHY_INTERFACE_MODE_RGMII && - phydev->interface <= PHY_INTERFACE_MODE_RGMII_TXID; -}; - /* * Set and/or override some configuration registers based on the * marvell,reg-init property stored in the of_node for the phydev. @@ -193,10 +188,10 @@ static int marvell_of_reg_init(struct phy_device *phydev) const __be32 *paddr; int len, i, saved_page, current_page, page_changed, ret; - if (!phydev->dev.device_node) + if (!phydev->dev.of_node) return 0; - paddr = of_get_property(phydev->dev.device_node, + paddr = of_get_property(phydev->dev.of_node, "marvell,reg-init", &len); if (!paddr || len < (4 * sizeof(*paddr))) return 0; @@ -739,9 +734,4 @@ static struct phy_driver marvell_drivers[] = { }, }; -static int __init marvell_phy_init(void) -{ - return phy_drivers_register(marvell_drivers, - ARRAY_SIZE(marvell_drivers)); -} -fs_initcall(marvell_phy_init); +device_phy_drivers(marvell_drivers); diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c index 839a7d1eb8..656557589d 100644 --- a/drivers/net/phy/mdio-bitbang.c +++ b/drivers/net/phy/mdio-bitbang.c @@ -158,7 +158,7 @@ static int mdiobb_read(struct mii_bus *bus, int phy, int reg) reg = mdiobb_cmd_addr(ctrl, phy, reg); mdiobb_cmd(ctrl, MDIO_C45_READ, phy, reg); } else - mdiobb_cmd(ctrl, MDIO_READ, phy, reg); + mdiobb_cmd(ctrl, ctrl->op_c22_read, phy, reg); ctrl->ops->set_mdio_dir(ctrl, 0); @@ -188,7 +188,7 @@ static int mdiobb_write(struct mii_bus *bus, int phy, int reg, u16 val) reg = mdiobb_cmd_addr(ctrl, phy, reg); mdiobb_cmd(ctrl, MDIO_C45_WRITE, phy, reg); } else - mdiobb_cmd(ctrl, MDIO_WRITE, phy, reg); + mdiobb_cmd(ctrl, ctrl->op_c22_write, phy, reg); /* send the turnaround (10) */ mdiobb_send_bit(ctrl, 1); @@ -219,6 +219,10 @@ struct mii_bus *alloc_mdio_bitbang(struct mdiobb_ctrl *ctrl) bus->write = mdiobb_write; bus->reset = mdiobb_reset; bus->priv = ctrl; + if (!ctrl->override_op_c22) { + ctrl->op_c22_read = MDIO_READ; + ctrl->op_c22_write = MDIO_WRITE; + } return bus; } diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index affa31ae2c..a28fb961e4 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -42,7 +42,7 @@ struct mdio_gpio_info { int mdc_active_low, mdio_active_low, mdo_active_low; }; -static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev) +static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device *dev) { int ret; enum of_gpio_flags flags; @@ -50,25 +50,25 @@ static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev) info = xzalloc(sizeof(*info)); - ret = of_get_gpio_flags(dev->device_node, 0, &flags); + ret = of_get_gpio_flags(dev->of_node, 0, &flags); if (ret < 0) { - dev_dbg(dev, "failed to get MDC inforamtion from DT\n"); + dev_dbg(dev, "failed to get MDC information from DT\n"); goto free_info; } info->mdc = ret; info->mdc_active_low = flags & OF_GPIO_ACTIVE_LOW; - ret = of_get_gpio_flags(dev->device_node, 1, &flags); + ret = of_get_gpio_flags(dev->of_node, 1, &flags); if (ret < 0) { - dev_dbg(dev, "failed to get MDIO inforamtion from DT\n"); + dev_dbg(dev, "failed to get MDIO information from DT\n"); goto free_info; } info->mdio = ret; info->mdio_active_low = flags & OF_GPIO_ACTIVE_LOW; - ret = of_get_gpio_flags(dev->device_node, 2, &flags); + ret = of_get_gpio_flags(dev->of_node, 2, &flags); if (ret > 0) { dev_dbg(dev, "found MDO information in DT\n"); info->mdo = ret; @@ -142,10 +142,10 @@ static struct mdiobb_ops mdio_gpio_ops = { .get_mdio_data = mdio_get, }; -static int mdio_gpio_probe(struct device_d *dev) +static int mdio_gpio_probe(struct device *dev) { int ret; - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct mdio_gpio_info *info; struct mii_bus *bus; @@ -193,9 +193,16 @@ static int mdio_gpio_probe(struct device_d *dev) goto free_mdo; } + if (np && + of_device_is_compatible(np, "microchip,mdio-smi0")) { + info->ctrl.op_c22_read = 0; + info->ctrl.op_c22_write = 0; + info->ctrl.override_op_c22 = 1; + } + bus = alloc_mdio_bitbang(&info->ctrl); bus->parent = dev; - bus->dev.device_node = np; + bus->dev.of_node = np; dev->priv = bus; @@ -217,10 +224,12 @@ free_info: static const struct of_device_id gpio_mdio_dt_ids[] = { { .compatible = "virtual,mdio-gpio", }, + { .compatible = "microchip,mdio-smi0" }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, gpio_mdio_dt_ids); -static struct driver_d mdio_gpio_driver = { +static struct driver mdio_gpio_driver = { .name = "mdio-gpio", .probe = mdio_gpio_probe, .of_compatible = DRV_OF_COMPAT(gpio_mdio_dt_ids), diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c index a36782c0b6..3dd04830a5 100644 --- a/drivers/net/phy/mdio-mux-gpio.c +++ b/drivers/net/phy/mdio-mux-gpio.c @@ -67,14 +67,14 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child, return 0; } -static int mdio_mux_gpio_probe(struct device_d *dev) +static int mdio_mux_gpio_probe(struct device *dev) { struct mdio_mux_gpio_state *s; int i, r; s = xzalloc(sizeof(*s)); - s->gpios_num = of_gpio_count(dev->device_node); + s->gpios_num = of_gpio_count(dev->of_node); if (s->gpios_num <= 0) { dev_err(dev, "No GPIOs specified\n"); r = -EINVAL; @@ -86,7 +86,7 @@ static int mdio_mux_gpio_probe(struct device_d *dev) for (i = 0; i < s->gpios_num; i++) { enum of_gpio_flags flags; - r = of_get_gpio_flags(dev->device_node, i, &flags); + r = of_get_gpio_flags(dev->of_node, i, &flags); if (!gpio_is_valid(r)) { r = (r < 0) ? r : -EINVAL; goto free_mem; @@ -105,7 +105,7 @@ static int mdio_mux_gpio_probe(struct device_d *dev) goto free_gpios; - r = mdio_mux_init(dev, dev->device_node, + r = mdio_mux_init(dev, dev->of_node, mdio_mux_gpio_switch_fn, s, NULL); if (r < 0) goto free_gpios; @@ -126,8 +126,9 @@ static const struct of_device_id mdio_mux_gpio_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, mdio_mux_gpio_match); -static struct driver_d mdio_mux_gpio_driver = { +static struct driver mdio_mux_gpio_driver = { .name = "mdio-mux-gpio", .probe = mdio_mux_gpio_probe, .of_compatible = mdio_mux_gpio_match, diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index aa63cbde97..c4088c16ca 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -37,11 +37,10 @@ static int mdio_mux_read_or_write(struct mii_bus *bus, int phy_id, if (!r) { pb->current_child = cb->bus_number; if (val) - r = pb->mii_bus->write(pb->mii_bus, phy_id, + r = mdiobus_write (pb->mii_bus, phy_id, regnum, *val); else - r = pb->mii_bus->read(pb->mii_bus, phy_id, - regnum); + r = mdiobus_read(pb->mii_bus, phy_id, regnum); } return r; } @@ -57,7 +56,7 @@ static int mdio_mux_write(struct mii_bus *bus, int phy_id, return mdio_mux_read_or_write(bus, phy_id, regnum, &val); } -int mdio_mux_init(struct device_d *dev, +int mdio_mux_init(struct device *dev, struct device_node *mux_node, int (*switch_fn)(int cur, int desired, void *data), void *data, @@ -118,7 +117,7 @@ int mdio_mux_init(struct device_d *dev, cb->mii_bus.parent = dev; cb->mii_bus.read = mdio_mux_read; cb->mii_bus.write = mdio_mux_write; - cb->mii_bus.dev.device_node = child_bus_node; + cb->mii_bus.dev.of_node = child_bus_node; r = mdiobus_register(&cb->mii_bus); if (r) { diff --git a/drivers/net/phy/mdio-mvebu.c b/drivers/net/phy/mdio-mvebu.c index 289ff4b05d..cd90ddd221 100644 --- a/drivers/net/phy/mdio-mvebu.c +++ b/drivers/net/phy/mdio-mvebu.c @@ -103,7 +103,7 @@ static int mvebu_mdio_write(struct mii_bus *bus, int addr, int reg, u16 data) return 0; } -static int mvebu_mdio_probe(struct device_d *dev) +static int mvebu_mdio_probe(struct device *dev) { struct mdio_priv *priv; @@ -119,7 +119,7 @@ static int mvebu_mdio_probe(struct device_d *dev) return PTR_ERR(priv->clk); clk_enable(priv->clk); - priv->miibus.dev.device_node = dev->device_node; + priv->miibus.dev.of_node = dev->of_node; priv->miibus.priv = priv; priv->miibus.parent = dev; priv->miibus.read = mvebu_mdio_read; @@ -128,7 +128,7 @@ static int mvebu_mdio_probe(struct device_d *dev) return mdiobus_register(&priv->miibus); } -static void mvebu_mdio_remove(struct device_d *dev) +static void mvebu_mdio_remove(struct device *dev) { struct mdio_priv *priv = dev->priv; @@ -141,8 +141,9 @@ static struct of_device_id mvebu_mdio_dt_ids[] = { { .compatible = "marvell,orion-mdio" }, { } }; +MODULE_DEVICE_TABLE(of, mvebu_mdio_dt_ids); -static struct driver_d mvebu_mdio_driver = { +static struct driver mvebu_mdio_driver = { .name = "mvebu-mdio", .probe = mvebu_mdio_probe, .remove = mvebu_mdio_remove, diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 3480e2ffb4..eed7c779e7 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -19,16 +19,68 @@ #include <clock.h> #include <net.h> #include <errno.h> +#include <linux/mdio.h> #include <linux/phy.h> #include <linux/err.h> #include <of_device.h> +#include <pinctrl.h> #define DEFAULT_GPIO_RESET_ASSERT 1000 /* us */ #define DEFAULT_GPIO_RESET_DEASSERT 1000 /* us */ LIST_HEAD(mii_bus_list); -int mdiobus_detect(struct device_d *dev) +static struct phy_device *mdio_device_create(struct mii_bus *bus, int addr) +{ + struct phy_device *phydev; + + phydev = xzalloc(sizeof(*phydev)); + + phydev->addr = addr; + phydev->bus = bus; + phydev->dev.bus = &mdio_bus_type; + + dev_set_name(&phydev->dev, "mdio%d-dev%02x", phydev->bus->dev.id, + phydev->addr); + phydev->dev.id = DEVICE_ID_SINGLE; + + return phydev; +} + +static int mdio_register_device(struct phy_device *phydev) +{ + int ret; + + if (phydev->registered) + return -EBUSY; + + if (!phydev->dev.parent) + phydev->dev.parent = &phydev->bus->dev; + + ret = register_device(&phydev->dev); + if (ret) + return ret; + + if (phydev->bus) + phydev->bus->phy_map[phydev->addr] = phydev; + + phydev->registered = 1; + + if (phydev->dev.driver) + return 0; + + return ret; +} + +int mdio_driver_register(struct phy_driver *phydrv) +{ + phydrv->drv.bus = &mdio_bus_type; + phydrv->is_phy = false; + + return register_driver(&phydrv->drv); +} + +int mdiobus_detect(struct device *dev) { struct mii_bus *mii = to_mii_bus(dev); int i, ret; @@ -67,7 +119,8 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi * Associate the OF node with the device structure so it * can be looked up later */ - phy->dev.device_node = child; + child->dev = &phy->dev; + phy->dev.of_node = child; /* * All data is now stored in the phy struct; @@ -83,6 +136,29 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi return 0; } +static int of_mdiobus_register_device(struct mii_bus *mdio, + struct device_node *child, u32 addr) +{ + struct phy_device *mdiodev; + int ret; + + mdiodev = mdio_device_create(mdio, addr); + if (IS_ERR(mdiodev)) + return PTR_ERR(mdiodev); + + child->dev = &mdiodev->dev; + mdiodev->dev.of_node = child; + + ret = mdio_register_device(mdiodev); + if (ret) + return ret; + + dev_dbg(&mdio->dev, "registered mdio device %s at address %i\n", + child->name, addr); + + return 0; +} + /* * Node is considered a PHY node if: * o Compatible string of "ethernet-phy-idX.X" @@ -175,35 +251,27 @@ static int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) /* Loop over the child nodes and register a phy_device for each one */ for_each_available_child_of_node(np, child) { - if (!of_mdiobus_child_is_phy(child)) { - if (of_get_property(child, "compatible", NULL)) { - if (!of_platform_device_create(child, - &mdio->dev)) { - dev_err(&mdio->dev, - "Failed to create device " - "for %s\n", - child->full_name); - } - } - - continue; - } - ret = of_property_read_u32(child, "reg", &addr); if (ret) { - dev_dbg(&mdio->dev, "%s has invalid PHY address\n", - child->full_name); + dev_dbg(&mdio->dev, "%pOF has invalid PHY address\n", + child); continue; } if (addr >= PHY_MAX_ADDR) { - dev_err(&mdio->dev, "%s PHY address %i is too large\n", - child->full_name, addr); + dev_err(&mdio->dev, "%pOF PHY address %i is too large\n", + child, addr); continue; } - of_mdiobus_reset_phy(mdio, child); - of_mdiobus_register_phy(mdio, child, addr); + of_pinctrl_select_state_default(child); + + if (of_mdiobus_child_is_phy(child)) { + of_mdiobus_reset_phy(mdio, child); + of_mdiobus_register_phy(mdio, child, addr); + } else { + of_mdiobus_register_device(mdio, child, addr); + } } return 0; @@ -239,6 +307,8 @@ int mdiobus_register(struct mii_bus *bus) return -EINVAL; } + slice_init(&bus->slice, dev_name(&bus->dev)); + if (bus->reset) bus->reset(bus); @@ -246,16 +316,14 @@ int mdiobus_register(struct mii_bus *bus) pr_info("%s: probed\n", dev_name(&bus->dev)); - if (bus->dev.device_node) { + if (!bus->dev.of_node) + bus->dev.of_node = bus->parent->of_node; + + if (bus->dev.of_node) { + bus->dev.of_node->dev = &bus->dev; + /* Register PHY's as child node to mdio node */ - of_mdiobus_register(bus, bus->dev.device_node); - } - else if (bus->parent->device_node) { - /* - * Register PHY's as child node to the ethernet node, - * if there was no mdio node - */ - of_mdiobus_register(bus, bus->parent->device_node); + of_mdiobus_register(bus, bus->dev.of_node); } return 0; @@ -272,6 +340,8 @@ void mdiobus_unregister(struct mii_bus *bus) bus->phy_map[i] = NULL; } + slice_exit(&bus->slice); + list_del(&bus->list); } EXPORT_SYMBOL(mdiobus_unregister); @@ -329,7 +399,7 @@ struct mii_bus *of_mdio_find_bus(struct device_node *mdio_bus_np) return NULL; for_each_mii_bus(mii) - if (mii->dev.device_node == mdio_bus_np) + if (mii->dev.of_node == mdio_bus_np) return mii; return NULL; @@ -345,18 +415,61 @@ EXPORT_SYMBOL(of_mdio_find_bus); * Description: Given a PHY device, and a PHY driver, return 0 if * the driver supports the device. Otherwise, return 1. */ -static int mdio_bus_match(struct device_d *dev, struct driver_d *drv) +static int mdio_bus_match(struct device *dev, struct driver *drv) { struct phy_device *phydev = to_phy_device(dev); struct phy_driver *phydrv = to_phy_driver(drv); - if ((phydrv->phy_id & phydrv->phy_id_mask) == - (phydev->phy_id & phydrv->phy_id_mask)) + if (phydrv->is_phy) { + if ((phydrv->phy_id & phydrv->phy_id_mask) == + (phydev->phy_id & phydrv->phy_id_mask)) return 0; + } else { + return device_match(dev, drv); + } return 1; } +/** + * mdiobus_read - Convenience function for reading a given MII mgmt register + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to read + */ +int mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) +{ + int ret; + + slice_acquire(&bus->slice); + + ret = bus->read(bus, addr, regnum); + + slice_release(&bus->slice); + + return ret; +} + +/** + * mdiobus_write - Convenience function for writing a given MII mgmt register + * @bus: the mii_bus struct + * @addr: the phy address + * @regnum: register number to write + * @val: value to write to @regnum + */ +int mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) +{ + int ret; + + slice_acquire(&bus->slice); + + ret = bus->write(bus, addr, regnum, val); + + slice_release(&bus->slice); + + return ret; +} + static ssize_t phydev_read(struct cdev *cdev, void *_buf, size_t count, loff_t offset, ulong flags) { int i = count; @@ -396,7 +509,7 @@ static struct cdev_operations phydev_ops = { static void of_set_phy_supported(struct phy_device *phydev) { - struct device_node *node = phydev->dev.device_node; + struct device_node *node = phydev->dev.of_node; u32 max_speed; if (!IS_ENABLED(CONFIG_OFDEVICE)) @@ -427,7 +540,7 @@ static void of_set_phy_supported(struct phy_device *phydev) } } -static int mdio_bus_probe(struct device_d *_dev) +static int mdio_bus_probe(struct device *_dev) { struct phy_device *dev = to_phy_device(_dev); struct phy_driver *drv = to_phy_driver(_dev->driver); @@ -481,7 +594,7 @@ err: return ret; } -static void mdio_bus_remove(struct device_d *_dev) +static void mdio_bus_remove(struct device *_dev) { struct phy_device *dev = to_phy_device(_dev); struct phy_driver *drv = to_phy_driver(_dev->driver); diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 8f0b81d8fa..a203669353 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -14,24 +14,31 @@ #include <common.h> #include <init.h> +#include <linux/clk.h> #include <linux/mii.h> #include <linux/ethtool.h> #include <linux/phy.h> +#include <linux/mdio.h> #include <linux/micrel_phy.h> +#include <linux/bitfield.h> /* Operation Mode Strap Override */ #define MII_KSZPHY_OMSO 0x16 #define KSZPHY_OMSO_B_CAST_OFF BIT(9) +#define KSZPHY_OMSO_NAND_TREE_ON BIT(5) #define KSZPHY_OMSO_RMII_OVERRIDE BIT(1) #define KSZPHY_OMSO_MII_OVERRIDE BIT(0) /* general PHY control reg in vendor specific block. */ -#define MII_KSZPHY_CTRL 0x1F +#define MII_KSZPHY_CTRL 0x1f /* bitmap of PHY register to set interrupt mode */ #define KSZPHY_CTRL_INT_ACTIVE_HIGH BIT(9) #define KSZ9021_CTRL_INT_ACTIVE_HIGH BIT(14) #define KS8737_CTRL_INT_ACTIVE_HIGH BIT(14) -#define KSZ8051_RMII_50MHZ_CLK BIT(7) +#define KSZPHY_RMII_REF_CLK_SEL BIT(7) + +/* PHY Control 1 */ +#define MII_KSZPHY_CTRL_1 0x1e /* Write/read to/from extended registers */ #define MII_KSZPHY_EXTREG 0x0b @@ -47,6 +54,47 @@ #define PS_TO_REG 200 +struct kszphy_type { + u32 led_mode_reg; + bool has_broadcast_disable; + bool has_nand_tree_disable; + bool has_rmii_ref_clk_sel; +}; + +struct kszphy_priv { + const struct kszphy_type *type; + int led_mode; + bool rmii_ref_clk_sel; + bool rmii_ref_clk_sel_val; +}; + +static const struct kszphy_type ksz8001_type = { + .led_mode_reg = MII_KSZPHY_CTRL_1, +}; + +static const struct kszphy_type ksz8021_type = { + .led_mode_reg = MII_KSZPHY_CTRL, + .has_broadcast_disable = true, + .has_nand_tree_disable = true, + .has_rmii_ref_clk_sel = true, +}; + +static const struct kszphy_type ksz8041_type = { + .led_mode_reg = MII_KSZPHY_CTRL_1, +}; + +static const struct kszphy_type ksz8051_type = { + .led_mode_reg = MII_KSZPHY_CTRL, + .has_nand_tree_disable = true, +}; + +static const struct kszphy_type ksz8081_type = { + .led_mode_reg = MII_KSZPHY_CTRL, + .has_broadcast_disable = true, + .has_nand_tree_disable = true, + .has_rmii_ref_clk_sel = true, +}; + static int kszphy_extended_write(struct phy_device *phydev, u32 regnum, u16 val) { @@ -61,35 +109,152 @@ static int kszphy_extended_read(struct phy_device *phydev, return phy_read(phydev, MII_KSZPHY_EXTREG_READ); } -static int kszphy_config_init(struct phy_device *phydev) +static int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val) { + int ctrl; + + ctrl = phy_read(phydev, MII_KSZPHY_CTRL); + if (ctrl < 0) + return ctrl; + + if (val) + ctrl |= KSZPHY_RMII_REF_CLK_SEL; + else + ctrl &= ~KSZPHY_RMII_REF_CLK_SEL; + + return phy_write(phydev, MII_KSZPHY_CTRL, ctrl); +} + +/* Handle LED mode, shift = position of first led mode bit, usually 4 or 14 */ +static int kszphy_led_mode(struct phy_device *phydev, int reg, int shift) +{ + const struct device *dev = &phydev->dev; + const struct device_node *of_node = dev->of_node ? : dev->parent->of_node; + u32 val; + + if (!of_property_read_u32(of_node, "micrel,led-mode", &val)) { + if (val > 0x03) { + dev_err(dev, "led-mode 0x%02x out of range\n", val); + return -1; + } + return phy_modify(phydev, reg, 0x03 << shift, val << shift); + } return 0; } -static int ksz8021_config_init(struct phy_device *phydev) +static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val) { - u16 val; + const struct device *dev = &phydev->dev; + int rc, temp, shift; + + switch (reg) { + case MII_KSZPHY_CTRL_1: + shift = 14; + break; + case MII_KSZPHY_CTRL: + shift = 4; + break; + default: + return -EINVAL; + } + + temp = phy_read(phydev, reg); + if (temp < 0) { + rc = temp; + goto out; + } - val = phy_read(phydev, MII_KSZPHY_OMSO); - val |= KSZPHY_OMSO_B_CAST_OFF; - phy_write(phydev, MII_KSZPHY_OMSO, val); + temp &= ~(3 << shift); + temp |= val << shift; + rc = phy_write(phydev, reg, temp); +out: + if (rc < 0) + dev_err(dev, "failed to set led mode\n"); - return 0; + return rc; } -static int ks8051_config_init(struct phy_device *phydev) +/* Disable PHY address 0 as the broadcast address, so that it can be used as a + * unique (non-broadcast) address on a shared bus. + */ +static int kszphy_broadcast_disable(struct phy_device *phydev) { - int regval; + const struct device *dev = &phydev->dev; + int ret; + + ret = phy_read(phydev, MII_KSZPHY_OMSO); + if (ret < 0) + goto out; - if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) { - regval = phy_read(phydev, MII_KSZPHY_CTRL); - regval |= KSZ8051_RMII_50MHZ_CLK; - phy_write(phydev, MII_KSZPHY_CTRL, regval); + ret = phy_write(phydev, MII_KSZPHY_OMSO, ret | KSZPHY_OMSO_B_CAST_OFF); +out: + if (ret) + dev_err(dev, "failed to disable broadcast address\n"); + + return ret; +} + +static int kszphy_nand_tree_disable(struct phy_device *phydev) +{ + const struct device *dev = &phydev->dev; + int ret; + + ret = phy_read(phydev, MII_KSZPHY_OMSO); + if (ret < 0) + goto out; + + if (!(ret & KSZPHY_OMSO_NAND_TREE_ON)) + return 0; + + ret = phy_write(phydev, MII_KSZPHY_OMSO, + ret & ~KSZPHY_OMSO_NAND_TREE_ON); +out: + if (ret) + dev_err(dev, "failed to disable NAND tree mode\n"); + + return ret; +} + +/* Some config bits need to be set again on resume, handle them here. */ +static int kszphy_config_reset(struct phy_device *phydev) +{ + struct kszphy_priv *priv = phydev->priv; + int ret; + + if (priv->rmii_ref_clk_sel) { + ret = kszphy_rmii_clk_sel(phydev, priv->rmii_ref_clk_sel_val); + if (ret) { + dev_err(&phydev->dev, + "failed to set rmii reference clock\n"); + return ret; + } } + if (priv->led_mode >= 0) + kszphy_setup_led(phydev, priv->type->led_mode_reg, priv->led_mode); + return 0; } +static int kszphy_config_init(struct phy_device *phydev) +{ + struct kszphy_priv *priv = phydev->priv; + const struct kszphy_type *type; + + if (!priv) + return 0; + + type = priv->type; + + if (type->has_broadcast_disable) + kszphy_broadcast_disable(phydev); + + if (type->has_nand_tree_disable) + kszphy_nand_tree_disable(phydev); + + return kszphy_config_reset(phydev); +} + static int ksz9021_load_values_from_of(struct phy_device *phydev, const struct device_node *of_node, u16 reg, const char *field[]) @@ -113,8 +278,8 @@ static int ksz9021_load_values_from_of(struct phy_device *phydev, static int ksz9021_config_init(struct phy_device *phydev) { - const struct device_d *dev = &phydev->dev; - const struct device_node *of_node = dev->device_node; + const struct device *dev = &phydev->dev; + const struct device_node *of_node = dev->of_node; const char *clk_pad_skew_names[] = { "txen-skew-ps", "txc-skew-ps", "rxdv-skew-ps", "rxc-skew-ps" @@ -128,8 +293,8 @@ static int ksz9021_config_init(struct phy_device *phydev) "txd2-skew-ps", "txd3-skew-ps" }; - if (!of_node && dev->parent->device_node) - of_node = dev->parent->device_node; + if (!of_node && dev->parent->of_node) + of_node = dev->parent->of_node; if (of_node) { ksz9021_load_values_from_of(phydev, of_node, @@ -141,6 +306,8 @@ static int ksz9021_config_init(struct phy_device *phydev) ksz9021_load_values_from_of(phydev, of_node, MII_KSZPHY_TX_DATA_PAD_SKEW, tx_pad_skew_names); + + kszphy_led_mode(phydev, 0x11, 6); } return 0; @@ -155,9 +322,50 @@ static int ksz9021_config_init(struct phy_device *phydev) /* MMD Address 0x2 */ #define MII_KSZ9031RN_CONTROL_PAD_SKEW 4 +#define MII_KSZ9031RN_RX_CTL_M GENMASK(7, 4) +#define MII_KSZ9031RN_TX_CTL_M GENMASK(3, 0) + #define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5 +#define MII_KSZ9031RN_RXD3 GENMASK(15, 12) +#define MII_KSZ9031RN_RXD2 GENMASK(11, 8) +#define MII_KSZ9031RN_RXD1 GENMASK(7, 4) +#define MII_KSZ9031RN_RXD0 GENMASK(3, 0) + #define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6 +#define MII_KSZ9031RN_TXD3 GENMASK(15, 12) +#define MII_KSZ9031RN_TXD2 GENMASK(11, 8) +#define MII_KSZ9031RN_TXD1 GENMASK(7, 4) +#define MII_KSZ9031RN_TXD0 GENMASK(3, 0) + #define MII_KSZ9031RN_CLK_PAD_SKEW 8 +#define MII_KSZ9031RN_GTX_CLK GENMASK(9, 5) +#define MII_KSZ9031RN_RX_CLK GENMASK(4, 0) + +/* KSZ9031 has internal RGMII_IDRX = 1.2ns and RGMII_IDTX = 0ns. To + * provide different RGMII options we need to configure delay offset + * for each pad relative to build in delay. + */ +/* keep rx as "No delay adjustment" and set rx_clk to +0.60ns to get delays of + * 1.80ns + */ +#define RX_ID 0x7 +#define RX_CLK_ID 0x19 + +/* set rx to +0.30ns and rx_clk to -0.90ns to compensate the + * internal 1.2ns delay. + */ +#define RX_ND 0xc +#define RX_CLK_ND 0x0 + +/* set tx to -0.42ns and tx_clk to +0.96ns to get 1.38ns delay */ +#define TX_ID 0x0 +#define TX_CLK_ID 0x1f + +/* set tx and tx_clk to "No delay adjustment" to keep 0ns + * dealy + */ +#define TX_ND 0x7 +#define TX_CLK_ND 0xf static int ksz9031_of_load_skew_values(struct phy_device *phydev, const struct device_node *of_node, @@ -179,7 +387,7 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev, return 0; if (matches < numfields) - newval = phy_read_mmd_indirect(phydev, reg, 2); + newval = phy_read_mmd(phydev, MDIO_MMD_WIS, reg); else newval = 0; @@ -193,23 +401,78 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev, << (field_sz * i)); } - phy_write_mmd_indirect(phydev, reg, 2, newval); + phy_write_mmd(phydev, MDIO_MMD_WIS, reg, newval); return 0; } static int ksz9031_center_flp_timing(struct phy_device *phydev) { /* Center KSZ9031RNX FLP timing at 16ms. */ - phy_write_mmd_indirect(phydev, MII_KSZ9031RN_FLP_BURST_TX_HI, 0, 0x0006); - phy_write_mmd_indirect(phydev, MII_KSZ9031RN_FLP_BURST_TX_LO, 0, 0x1a80); + phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_HI, 0x0006); + phy_write_mmd(phydev, 0, MII_KSZ9031RN_FLP_BURST_TX_LO, 0x1a80); return genphy_restart_aneg(phydev); } +static int ksz9031_config_rgmii_delay(struct phy_device *phydev) +{ + u16 rx, tx, rx_clk, tx_clk; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + tx = TX_ND; + tx_clk = TX_CLK_ND; + rx = RX_ND; + rx_clk = RX_CLK_ND; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + tx = TX_ID; + tx_clk = TX_CLK_ID; + rx = RX_ID; + rx_clk = RX_CLK_ID; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + tx = TX_ND; + tx_clk = TX_CLK_ND; + rx = RX_ID; + rx_clk = RX_CLK_ID; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + tx = TX_ID; + tx_clk = TX_CLK_ID; + rx = RX_ND; + rx_clk = RX_CLK_ND; + break; + default: + return 0; + } + + phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_CONTROL_PAD_SKEW, + FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) | + FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx)); + + phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_RX_DATA_PAD_SKEW, + FIELD_PREP(MII_KSZ9031RN_RXD3, rx) | + FIELD_PREP(MII_KSZ9031RN_RXD2, rx) | + FIELD_PREP(MII_KSZ9031RN_RXD1, rx) | + FIELD_PREP(MII_KSZ9031RN_RXD0, rx)); + + phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_TX_DATA_PAD_SKEW, + FIELD_PREP(MII_KSZ9031RN_TXD3, tx) | + FIELD_PREP(MII_KSZ9031RN_TXD2, tx) | + FIELD_PREP(MII_KSZ9031RN_TXD1, tx) | + FIELD_PREP(MII_KSZ9031RN_TXD0, tx)); + + phy_write_mmd(phydev, MDIO_MMD_WIS, MII_KSZ9031RN_CLK_PAD_SKEW, + FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) | + FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk)); + return 0; +} + static int ksz9031_config_init(struct phy_device *phydev) { - const struct device_d *dev = &phydev->dev; - const struct device_node *of_node = dev->device_node; + const struct device *dev = &phydev->dev; + const struct device_node *of_node = dev->of_node; static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"}; static const char *rx_data_skews[4] = { "rxd0-skew-ps", "rxd1-skew-ps", @@ -222,10 +485,16 @@ static int ksz9031_config_init(struct phy_device *phydev) static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"}; int ret; - if (!of_node && dev->parent->device_node) - of_node = dev->parent->device_node; + if (!of_node && dev->parent->of_node) + of_node = dev->parent->of_node; if (of_node) { + if (phy_interface_is_rgmii(phydev)) { + ret = ksz9031_config_rgmii_delay(phydev); + if (ret < 0) + return ret; + } + ksz9031_of_load_skew_values(phydev, of_node, MII_KSZ9031RN_CLK_PAD_SKEW, 5, clk_skews, 2); @@ -277,6 +546,186 @@ err_force_master: return ret; } +#define KSZ9131_SKEW_5BIT_MAX 2400 +#define KSZ9131_SKEW_4BIT_MAX 800 +#define KSZ9131_OFFSET 700 +#define KSZ9131_STEP 100 + +static int ksz9131_of_load_skew_values(struct phy_device *phydev, + const struct device_node *of_node, + u16 reg, size_t field_sz, + const char *field[], u8 numfields) +{ + int val[4] = {-(1 + KSZ9131_OFFSET), -(2 + KSZ9131_OFFSET), + -(3 + KSZ9131_OFFSET), -(4 + KSZ9131_OFFSET)}; + int skewval, skewmax = 0; + int matches = 0; + u16 maxval; + u16 newval; + u16 mask; + int i; + + /* psec properties in dts should mean x pico seconds */ + if (field_sz == 5) + skewmax = KSZ9131_SKEW_5BIT_MAX; + else + skewmax = KSZ9131_SKEW_4BIT_MAX; + + for (i = 0; i < numfields; i++) + if (!of_property_read_s32(of_node, field[i], &skewval)) { + if (skewval < -KSZ9131_OFFSET) + skewval = -KSZ9131_OFFSET; + else if (skewval > skewmax) + skewval = skewmax; + + val[i] = skewval + KSZ9131_OFFSET; + matches++; + } + + if (!matches) + return 0; + + if (matches < numfields) + newval = phy_read_mmd(phydev, 2, reg); + else + newval = 0; + + maxval = (field_sz == 4) ? 0xf : 0x1f; + for (i = 0; i < numfields; i++) + if (val[i] != -(i + 1 + KSZ9131_OFFSET)) { + mask = 0xffff; + mask ^= maxval << (field_sz * i); + newval = (newval & mask) | + (((val[i] / KSZ9131_STEP) & maxval) + << (field_sz * i)); + } + + return phy_write_mmd(phydev, 2, reg, newval); +} + +#define KSZ9131RN_MMD_COMMON_CTRL_REG 2 +#define KSZ9131RN_RXC_DLL_CTRL 76 +#define KSZ9131RN_TXC_DLL_CTRL 77 +#define KSZ9131RN_DLL_CTRL_BYPASS BIT_MASK(12) +#define KSZ9131RN_DLL_ENABLE_DELAY 0 +#define KSZ9131RN_DLL_DISABLE_DELAY BIT(12) + +static int ksz9131_config_rgmii_delay(struct phy_device *phydev) +{ + u16 rxcdll_val, txcdll_val; + int ret; + + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + rxcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + rxcdll_val = KSZ9131RN_DLL_DISABLE_DELAY; + txcdll_val = KSZ9131RN_DLL_ENABLE_DELAY; + break; + default: + return 0; + } + + ret = phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_RXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, + rxcdll_val); + if (ret < 0) + return ret; + + return phy_modify_mmd(phydev, KSZ9131RN_MMD_COMMON_CTRL_REG, + KSZ9131RN_TXC_DLL_CTRL, KSZ9131RN_DLL_CTRL_BYPASS, + txcdll_val); +} + +/* Silicon Errata DS80000693B + * + * When LEDs are configured in Individual Mode, LED1 is ON in a no-link + * condition. Workaround is to set register 0x1e, bit 9, this way LED1 behaves + * according to the datasheet (off if there is no link). + */ +static int ksz9131_led_errata(struct phy_device *phydev) +{ + int reg; + + reg = phy_read_mmd(phydev, 2, 0); + if (reg < 0) + return reg; + + if (!(reg & BIT(4))) + return 0; + + return phy_set_bits(phydev, 0x1e, BIT(9)); +} + +static int ksz9131_config_init(struct phy_device *phydev) +{ + const struct device *dev = &phydev->dev; + const struct device_node *of_node = dev->of_node; + static const char *clk_skews[2] = {"rxc-skew-psec", "txc-skew-psec"}; + static const char *rx_data_skews[4] = { + "rxd0-skew-psec", "rxd1-skew-psec", + "rxd2-skew-psec", "rxd3-skew-psec" + }; + static const char *tx_data_skews[4] = { + "txd0-skew-psec", "txd1-skew-psec", + "txd2-skew-psec", "txd3-skew-psec" + }; + static const char *control_skews[2] = {"txen-skew-psec", "rxdv-skew-psec"}; + int ret; + + if (!of_node && dev->parent->of_node) + of_node = dev->parent->of_node; + + if (!of_node) + return 0; + + if (phy_interface_is_rgmii(phydev)) { + ret = ksz9131_config_rgmii_delay(phydev); + if (ret < 0) + return ret; + } + + ret = ksz9131_of_load_skew_values(phydev, of_node, + MII_KSZ9031RN_CLK_PAD_SKEW, 5, + clk_skews, 2); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, of_node, + MII_KSZ9031RN_CONTROL_PAD_SKEW, 4, + control_skews, 2); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, of_node, + MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4, + rx_data_skews, 4); + if (ret < 0) + return ret; + + ret = ksz9131_of_load_skew_values(phydev, of_node, + MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4, + tx_data_skews, 4); + if (ret < 0) + return ret; + + ret = ksz9131_led_errata(phydev); + if (ret < 0) + return ret; + + return 0; +} + #define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX BIT(6) #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4) @@ -285,7 +734,7 @@ static int ksz8873mll_read_status(struct phy_device *phydev) int regval; /* dummy read */ - regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); + (void)phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4); @@ -340,13 +789,66 @@ static int ksz8873mll_config_init(struct phy_device *phydev) return 0; } +static int kszphy_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->dev; + struct device_node *np = dev->of_node; + struct phy_driver *drv = to_phy_driver(dev->driver); + const struct kszphy_type *type = drv->driver_data; + struct kszphy_priv *priv; + struct clk *clk; + int ret; + + priv = xzalloc(sizeof(*priv)); + + phydev->priv = priv; + + priv->type = type; + + if (type->led_mode_reg) { + ret = of_property_read_u32(np, "micrel,led-mode", + &priv->led_mode); + if (ret) + priv->led_mode = -1; + + if (priv->led_mode > 3) { + dev_err(dev, "invalid led mode: 0x%02x\n", + priv->led_mode); + priv->led_mode = -1; + } + } else { + priv->led_mode = -1; + } + + clk = clk_get(dev, "rmii-ref"); + /* NOTE: clk may be NULL if building without CONFIG_HAVE_CLK */ + if (!IS_ERR_OR_NULL(clk)) { + unsigned long rate = clk_get_rate(clk); + bool rmii_ref_clk_sel_25_mhz; + + priv->rmii_ref_clk_sel = type->has_rmii_ref_clk_sel; + rmii_ref_clk_sel_25_mhz = of_property_read_bool(np, + "micrel,rmii-reference-clock-select-25-mhz"); + + if (rate > 24500000 && rate < 25500000) { + priv->rmii_ref_clk_sel_val = rmii_ref_clk_sel_25_mhz; + } else if (rate > 49500000 && rate < 50500000) { + priv->rmii_ref_clk_sel_val = !rmii_ref_clk_sel_25_mhz; + } else { + dev_err(dev, "Clock rate out of range: %ld\n", rate); + return -EINVAL; + } + } + + return 0; +} + static struct phy_driver ksphy_driver[] = { { .phy_id = PHY_ID_KS8737, .phy_id_mask = 0x00fffff0, .drv.name = "Micrel KS8737", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), - .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, { @@ -355,7 +857,9 @@ static struct phy_driver ksphy_driver[] = { .drv.name = "Micrel KSZ8021", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause), - .config_init = ksz8021_config_init, + .driver_data = &ksz8021_type, + .probe = kszphy_probe, + .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, { @@ -364,7 +868,9 @@ static struct phy_driver ksphy_driver[] = { .drv.name = "Micrel KSZ8031", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause), - .config_init = ksz8021_config_init, + .driver_data = &ksz8021_type, + .probe = kszphy_probe, + .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, { @@ -373,6 +879,8 @@ static struct phy_driver ksphy_driver[] = { .drv.name = "Micrel KSZ8041", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause), + .driver_data = &ksz8041_type, + .probe = kszphy_probe, .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, @@ -382,22 +890,28 @@ static struct phy_driver ksphy_driver[] = { .drv.name = "Micrel KSZ8051", .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause | SUPPORTED_Asym_Pause), - .config_init = ks8051_config_init, + .driver_data = &ksz8051_type, + .probe = kszphy_probe, + .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, { .phy_id = PHY_ID_KSZ8081, .phy_id_mask = MICREL_PHY_ID_MASK, .drv.name = "Micrel KSZ8081/91", + .driver_data = &ksz8081_type, .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), - .config_init = ksz8021_config_init, + .probe = kszphy_probe, + .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, }, { .phy_id = PHY_ID_KSZ8001, - .drv.name = "Micrel KSZ8001 or KS8721", .phy_id_mask = 0x00ffffff, + .drv.name = "Micrel KSZ8001 or KS8721", + .driver_data = &ksz8001_type, .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause), + .probe = kszphy_probe, .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, @@ -424,6 +938,14 @@ static struct phy_driver ksphy_driver[] = { .config_aneg = genphy_config_aneg, .read_status = ksz9031_read_status, }, { + .phy_id = PHY_ID_KSZ9131, + .phy_id_mask = 0x00fffff0, + .drv.name = "Microchip KSZ9131 Gigabit PHY", + .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause), + .config_init = ksz9131_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, +}, { .phy_id = PHY_ID_KSZ8873MLL, .phy_id_mask = 0x00fffff0, .drv.name = "Micrel KSZ8873MLL Switch", @@ -433,9 +955,4 @@ static struct phy_driver ksphy_driver[] = { .read_status = ksz8873mll_read_status, } }; -static int ksphy_init(void) -{ - return phy_drivers_register(ksphy_driver, - ARRAY_SIZE(ksphy_driver)); -} -fs_initcall(ksphy_init); +device_phy_drivers(ksphy_driver); diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c new file mode 100644 index 0000000000..d4cd05a1f6 --- /dev/null +++ b/drivers/net/phy/motorcomm.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * drivers/net/phy/motorcomm.c + * + * Driver for Motorcomm PHYs + * + * Author: Peter Geis <pgwipeout@gmail.com> + */ + +#include <common.h> +#include <init.h> +#include <linux/phy.h> +#include <linux/mdio.h> + +#define PHY_ID_YT8511 0x0000010a + +#define YT8511_PAGE_SELECT 0x1e +#define YT8511_PAGE 0x1f +#define YT8511_EXT_CLK_GATE 0x0c +#define YT8511_EXT_DELAY_DRIVE 0x0d +#define YT8511_EXT_SLEEP_CTRL 0x27 + +/* 2b00 25m from pll + * 2b01 25m from xtl *default* + * 2b10 62.m from pll + * 2b11 125m from pll + */ +#define YT8511_CLK_125M (BIT(2) | BIT(1)) +#define YT8511_PLLON_SLP BIT(14) + +/* RX Delay enabled = 1.8ns 1000T, 8ns 10/100T */ +#define YT8511_DELAY_RX BIT(0) + +/* TX Gig-E Delay is bits 7:4, default 0x5 + * TX Fast-E Delay is bits 15:12, default 0xf + * Delay = 150ps * N - 250ps + * On = 2000ps, off = 50ps + */ +#define YT8511_DELAY_GE_TX_EN (0xf << 4) +#define YT8511_DELAY_GE_TX_DIS (0x2 << 4) +#define YT8511_DELAY_FE_TX_EN (0xf << 12) +#define YT8511_DELAY_FE_TX_DIS (0x2 << 12) + +static int yt8511_read_page(struct phy_device *phydev) +{ + return phy_read(phydev, YT8511_PAGE_SELECT); +}; + +static int yt8511_write_page(struct phy_device *phydev, int page) +{ + return phy_write(phydev, YT8511_PAGE_SELECT, page); +}; + +static int yt8511_config_init(struct phy_device *phydev) +{ + int oldpage, ret = 0; + unsigned int ge, fe; + + oldpage = phy_select_page(phydev, YT8511_EXT_CLK_GATE); + if (oldpage < 0) + goto err_restore_page; + + /* set rgmii delay mode */ + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + ge = YT8511_DELAY_GE_TX_DIS; + fe = YT8511_DELAY_FE_TX_DIS; + break; + case PHY_INTERFACE_MODE_RGMII_RXID: + ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_DIS; + fe = YT8511_DELAY_FE_TX_DIS; + break; + case PHY_INTERFACE_MODE_RGMII_TXID: + ge = YT8511_DELAY_GE_TX_EN; + fe = YT8511_DELAY_FE_TX_EN; + break; + case PHY_INTERFACE_MODE_RGMII_ID: + ge = YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN; + fe = YT8511_DELAY_FE_TX_EN; + break; + default: /* do not support other modes */ + ret = -EOPNOTSUPP; + goto err_restore_page; + } + + ret = phy_modify(phydev, YT8511_PAGE, (YT8511_DELAY_RX | YT8511_DELAY_GE_TX_EN), ge); + if (ret < 0) + goto err_restore_page; + + /* set clock mode to 125mhz */ + ret = phy_modify(phydev, YT8511_PAGE, 0, YT8511_CLK_125M); + if (ret < 0) + goto err_restore_page; + + /* fast ethernet delay is in a separate page */ + ret = phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_DELAY_DRIVE); + if (ret < 0) + goto err_restore_page; + + ret = phy_modify(phydev, YT8511_PAGE, YT8511_DELAY_FE_TX_EN, fe); + if (ret < 0) + goto err_restore_page; + + /* leave pll enabled in sleep */ + ret = phy_write(phydev, YT8511_PAGE_SELECT, YT8511_EXT_SLEEP_CTRL); + if (ret < 0) + goto err_restore_page; + + ret = phy_modify(phydev, YT8511_PAGE, 0, YT8511_PLLON_SLP); + if (ret < 0) + goto err_restore_page; + +err_restore_page: + return phy_restore_page(phydev, oldpage, ret); +} + +static struct phy_driver motorcomm_phy_drvs[] = { + { + .phy_id = PHY_ID_YT8511, + .phy_id_mask = 0xffffffff, + .drv.name = "YT8511 Gigabit Ethernet", + .config_init = yt8511_config_init, + .features = PHY_GBIT_FEATURES, + .read_page = yt8511_read_page, + .write_page = yt8511_write_page, + }, +}; + +device_phy_drivers(motorcomm_phy_drvs); diff --git a/drivers/net/phy/mv88e6xxx/Makefile b/drivers/net/phy/mv88e6xxx/Makefile index e1d4b1b9d7..4f569509e5 100644 --- a/drivers/net/phy/mv88e6xxx/Makefile +++ b/drivers/net/phy/mv88e6xxx/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-y += mv88e6xxx.o mv88e6xxx-objs := chip.o diff --git a/drivers/net/phy/mv88e6xxx/chip.c b/drivers/net/phy/mv88e6xxx/chip.c index b1bffe5cbc..b9b02c52f2 100644 --- a/drivers/net/phy/mv88e6xxx/chip.c +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <linux/mii.h> @@ -35,6 +36,7 @@ enum mv88e6xxx_model { MV88E6190X, MV88E6191, MV88E6240, + MV88E6250, MV88E6290, MV88E6320, MV88E6321, @@ -223,6 +225,18 @@ static const struct mv88e6xxx_ops mv88e6240_ops = { .port_link_state = mv88e6352_port_link_state, }; +static const struct mv88e6xxx_ops mv88e6250_ops = { + /* MV88E6XXX_FAMILY_6250 */ + .get_eeprom = mv88e6xxx_g2_get_eeprom16, + .set_eeprom = mv88e6xxx_g2_set_eeprom16, + .phy_read = mv88e6xxx_g2_smi_phy_read, + .phy_write = mv88e6xxx_g2_smi_phy_write, + .port_set_link = mv88e6xxx_port_set_link, + .port_set_duplex = mv88e6xxx_port_set_duplex, + .port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay, + .port_set_speed = mv88e6250_port_set_speed, +}; + static const struct mv88e6xxx_ops mv88e6290_ops = { /* MV88E6XXX_FAMILY_6390 */ .get_eeprom = mv88e6xxx_g2_get_eeprom8, @@ -524,6 +538,17 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { .ops = &mv88e6240_ops, }, + [MV88E6250] = { + .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6250, + .family = MV88E6XXX_FAMILY_6250, + .name = "Marvell 88E6250", + .num_ports = 7, + .port_base_addr = 0x08, + .global1_addr = 0xf, + .global2_addr = 0x7, + .ops = &mv88e6250_ops, + }, + [MV88E6290] = { .prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6290, .family = MV88E6XXX_FAMILY_6390, @@ -779,10 +804,9 @@ static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip) return 0; } -static int mv88e6xxx_eeprom_read(struct device_d *dev, const int offset, - void *val, int bytes) +static int mv88e6xxx_eeprom_read(void *ctx, unsigned offset, void *val, size_t bytes) { - struct mv88e6xxx_chip *chip = dev->parent->priv; + struct mv88e6xxx_chip *chip = ctx; struct ethtool_eeprom eeprom = { .offset = offset, .len = bytes, @@ -794,10 +818,9 @@ static int mv88e6xxx_eeprom_read(struct device_d *dev, const int offset, return chip->info->ops->get_eeprom(chip, &eeprom, val); } -static int mv88e6xxx_eeprom_write(struct device_d *dev, const int offset, - const void *val, int bytes) +static int mv88e6xxx_eeprom_write(void *ctx, unsigned offset, const void *val, size_t bytes) { - struct mv88e6xxx_chip *chip = dev->parent->priv; + struct mv88e6xxx_chip *chip = ctx; struct ethtool_eeprom eeprom = { .offset = offset, .len = bytes, @@ -809,14 +832,9 @@ static int mv88e6xxx_eeprom_write(struct device_d *dev, const int offset, return chip->info->ops->set_eeprom(chip, &eeprom, (void *)val); } -static const struct nvmem_bus mv88e6xxx_eeprom_nvmem_bus = { - .write = mv88e6xxx_eeprom_write, - .read = mv88e6xxx_eeprom_read, -}; - -static int mv88e6xxx_probe(struct device_d *dev) +static int mv88e6xxx_probe(struct device *dev) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct device_node *mdio_node; struct mv88e6xxx_chip *chip; enum of_gpio_flags of_flags; @@ -883,11 +901,13 @@ static int mv88e6xxx_probe(struct device_d *dev) struct nvmem_config config = { .name = basprintf("%s-eeprom", dev_name(dev)), .dev = dev, + .priv = chip, .word_size = 1, .stride = 1, .size = eeprom_len, .read_only = false, - .bus = &mv88e6xxx_eeprom_nvmem_bus, + .reg_write = mv88e6xxx_eeprom_write, + .reg_read = mv88e6xxx_eeprom_read, }; if (IS_ERR(nvmem_register(&config))) @@ -917,7 +937,7 @@ static int mv88e6xxx_probe(struct device_d *dev) mdio_node = of_get_child_by_name(np, "mdio"); if (mdio_node) - chip->miibus.dev.device_node = mdio_node; + chip->miibus.dev.of_node = mdio_node; err = mv88e6xxx_port_probe(chip); if (err) @@ -932,13 +952,18 @@ static const struct of_device_id mv88e6xxx_of_match[] = { .data = &mv88e6xxx_table[MV88E6085], }, { + .compatible = "marvell,mv88e6250", + .data = &mv88e6xxx_table[MV88E6250], + }, + { .compatible = "marvell,mv88e6190", .data = &mv88e6xxx_table[MV88E6190], }, {}, }; +MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match); -static struct driver_d mv88e6xxx_driver = { +static struct driver mv88e6xxx_driver = { .name = "mv88e6085", .probe = mv88e6xxx_probe, .of_compatible = mv88e6xxx_of_match, diff --git a/drivers/net/phy/mv88e6xxx/chip.h b/drivers/net/phy/mv88e6xxx/chip.h index 57f74a39a0..aec6c2891f 100644 --- a/drivers/net/phy/mv88e6xxx/chip.h +++ b/drivers/net/phy/mv88e6xxx/chip.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef _MV88E6XXX_CHIP_H #define _MV88E6XXX_CHIP_H @@ -18,6 +19,7 @@ enum mv88e6xxx_family { MV88E6XXX_FAMILY_6165, /* 6123 6161 6165 */ MV88E6XXX_FAMILY_6185, /* 6108 6121 6122 6131 6152 6155 6182 6185 */ MV88E6XXX_FAMILY_6320, /* 6320 6321 */ + MV88E6XXX_FAMILY_6250, /* 6250 */ MV88E6XXX_FAMILY_6341, /* 6141 6341 */ MV88E6XXX_FAMILY_6351, /* 6171 6175 6350 6351 */ MV88E6XXX_FAMILY_6352, /* 6172 6176 6240 6352 */ @@ -48,7 +50,7 @@ struct mv88e6xxx_chip { const struct mv88e6xxx_info *info; struct mii_bus *parent_miibus; struct mii_bus miibus; - struct device_d *dev; + struct device *dev; int reset; /* Array of port structures. */ diff --git a/drivers/net/phy/mv88e6xxx/global2.c b/drivers/net/phy/mv88e6xxx/global2.c index 970a7291e7..2728a66eea 100644 --- a/drivers/net/phy/mv88e6xxx/global2.c +++ b/drivers/net/phy/mv88e6xxx/global2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <linux/ethtool.h> #include <linux/bitfield.h> diff --git a/drivers/net/phy/mv88e6xxx/global2.h b/drivers/net/phy/mv88e6xxx/global2.h index 4e23b04232..0f2dc53c29 100644 --- a/drivers/net/phy/mv88e6xxx/global2.h +++ b/drivers/net/phy/mv88e6xxx/global2.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef _MV88E6XXX_GLOBAL2_H #define _MV88E6XXX_GLOBAL2_H diff --git a/drivers/net/phy/mv88e6xxx/port.c b/drivers/net/phy/mv88e6xxx/port.c index 52f95d622c..29ea4ec882 100644 --- a/drivers/net/phy/mv88e6xxx/port.c +++ b/drivers/net/phy/mv88e6xxx/port.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> @@ -276,6 +277,18 @@ int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) return mv88e6xxx_port_set_speed(chip, port, speed, false, false); } +/* Support 10, 100 (e.g. 88E6250 family) */ +int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) +{ + if (speed == SPEED_MAX) + speed = 100; + + if (speed > 100) + return -EOPNOTSUPP; + + return mv88e6xxx_port_set_speed(chip, port, speed, false, false); +} + /* Support 10, 100, 200, 1000 Mbps (e.g. 88E6352 family) */ int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed) { @@ -547,21 +560,17 @@ static struct phy_driver mv88e6xxx_port_driver = { .read_status = mv88e6xxx_port_read_status, }; -static int __init mv88e6xxx_port_driver_register(void) -{ - return phy_driver_register(&mv88e6xxx_port_driver); -} -fs_initcall(mv88e6xxx_port_driver_register); +device_phy_driver(mv88e6xxx_port_driver); int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip) { - struct device_d *dev = chip->dev; - struct device_node *np = dev->device_node; + struct device *dev = chip->dev; + struct device_node *np = dev->of_node; struct device_node *port_node, *switch_node; struct device_node *port_nodes[DSA_MAX_PORTS] = { NULL }; int err, i; - switch_node = of_find_node_by_name(np, "ports"); + switch_node = of_find_node_by_name_address(np, "ports"); if (!switch_node) return -EINVAL; @@ -571,8 +580,8 @@ int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip) err = of_property_read_u32(port_node, "reg", &nr); if (err) { dev_err(dev, - "Error: Failed to find reg for child %s\n", - port_node->full_name); + "Error: Failed to find reg for child %pOF\n", + port_node); continue; } @@ -650,7 +659,7 @@ int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip) phydev = phy_device_create(chip->parent_miibus, chip->info->port_base_addr + i, MV88E6XXX_SWITCH_PORT_PHY_ID); - phydev->dev.device_node = port_nodes[i]; + phydev->dev.of_node = port_nodes[i]; phydev->dev.priv = chip; phydev->duplex = DUPLEX_UNFORCED; @@ -660,4 +669,4 @@ int mv88e6xxx_port_probe(struct mv88e6xxx_chip *chip) } return 0; -}
\ No newline at end of file +} diff --git a/drivers/net/phy/mv88e6xxx/port.h b/drivers/net/phy/mv88e6xxx/port.h index 07d937ecbd..4bc5072948 100644 --- a/drivers/net/phy/mv88e6xxx/port.h +++ b/drivers/net/phy/mv88e6xxx/port.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef _MV88E6XXX_PORT_H #define _MV88E6XXX_PORT_H @@ -89,6 +90,7 @@ #define MV88E6XXX_PORT_SWITCH_ID_PROD_6191 0x1910 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6185 0x1a70 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6240 0x2400 +#define MV88E6XXX_PORT_SWITCH_ID_PROD_6250 0x2500 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6290 0x2900 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6321 0x3100 #define MV88E6XXX_PORT_SWITCH_ID_PROD_6141 0x3400 @@ -120,6 +122,7 @@ int mv88e6xxx_port_set_link(struct mv88e6xxx_chip *chip, int port, int link); int mv88e6xxx_port_set_duplex(struct mv88e6xxx_chip *chip, int port, int dup); int mv88e6065_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6185_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); +int mv88e6250_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6352_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6390_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); int mv88e6390x_port_set_speed(struct mv88e6xxx_chip *chip, int port, int speed); diff --git a/drivers/net/phy/national.c b/drivers/net/phy/national.c index 83390b99ab..d74cd81933 100644 --- a/drivers/net/phy/national.c +++ b/drivers/net/phy/national.c @@ -84,8 +84,4 @@ static struct phy_driver dp83865_driver = { .config_init = ns_config_init, }; -static int ns_phy_init(void) -{ - return phy_driver_register(&dp83865_driver); -} -fs_initcall(ns_phy_init); +device_phy_driver(dp83865_driver); diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index 21daaa9a2b..ab52de35b5 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <linux/phy.h> diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ccdc9f3716..abd78b2c80 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -18,11 +18,8 @@ #include <net.h> #include <malloc.h> #include <linux/phy.h> -#include <linux/phy.h> #include <linux/err.h> -#define PHY_AN_TIMEOUT 10 - static struct phy_driver genphy_driver; /** @@ -46,6 +43,7 @@ int phy_update_status(struct phy_device *phydev) struct eth_device *edev = phydev->attached_dev; int ret; int oldspeed = phydev->speed, oldduplex = phydev->duplex; + int oldlink = phydev->link; if (drv) { ret = drv->read_status(phydev); @@ -53,7 +51,15 @@ int phy_update_status(struct phy_device *phydev) return ret; } - if (phydev->speed == oldspeed && phydev->duplex == oldduplex) + /* + * If the phy is a fixed-link, set it to active state to trigger + * MAC configuration + */ + if (!phydev->bus && !phydev->link) + phydev->link = 1; + + if (phydev->speed == oldspeed && phydev->duplex == oldduplex && + phydev->link == oldlink) return 0; if (phydev->adjust_link) @@ -62,6 +68,8 @@ int phy_update_status(struct phy_device *phydev) if (phydev->link) dev_info(&edev->dev, "%dMbps %s duplex link detected\n", phydev->speed, phydev->duplex ? "full" : "half"); + else if (oldlink) + dev_info(&edev->dev, "link down\n"); return 0; } @@ -161,7 +169,6 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id) phydev->speed = 0; phydev->duplex = -1; phydev->pause = phydev->asym_pause = 0; - phydev->link = 1; phydev->autoneg = AUTONEG_ENABLE; phydev->addr = addr; @@ -299,8 +306,8 @@ void phy_unregister_device(struct phy_device *phydev) phydev->registered = 0; } -static struct phy_device *of_phy_register_fixed_link(struct device_node *np, - struct eth_device *edev) +struct phy_device *of_phy_register_fixed_link(struct device_node *np, + struct eth_device *edev) { struct phy_device *phydev; @@ -308,7 +315,7 @@ static struct phy_device *of_phy_register_fixed_link(struct device_node *np, phydev->dev.parent = &edev->dev; phydev->registered = 1; - phydev->link = 1; + phydev->link = 0; if (of_property_read_u32(np, "speed", &phydev->speed)) return NULL; @@ -321,7 +328,7 @@ static struct phy_device *of_phy_register_fixed_link(struct device_node *np, static struct phy_device *of_mdio_find_phy(struct eth_device *edev) { - struct device_d *dev; + struct device *dev; struct device_node *phy_node; struct mii_bus *bus; int addr; @@ -329,16 +336,18 @@ static struct phy_device *of_mdio_find_phy(struct eth_device *edev) if (!IS_ENABLED(CONFIG_OFDEVICE)) return NULL; - if (!edev->parent || !edev->parent->device_node) + if (!edev->parent || !edev->parent->of_node) return NULL; - phy_node = of_parse_phandle(edev->parent->device_node, "phy-handle", 0); + phy_node = of_parse_phandle(edev->parent->of_node, "phy-handle", 0); if (!phy_node) - phy_node = of_parse_phandle(edev->parent->device_node, "phy", 0); + phy_node = of_parse_phandle(edev->parent->of_node, "phy", 0); if (!phy_node) - phy_node = of_parse_phandle(edev->parent->device_node, "phy-device", 0); + phy_node = of_parse_phandle(edev->parent->of_node, + "phy-device", 0); if (!phy_node) { - phy_node = of_get_child_by_name(edev->parent->device_node, "fixed-link"); + phy_node = of_get_child_by_name(edev->parent->of_node, + "fixed-link"); if (phy_node) return of_phy_register_fixed_link(phy_node, edev); } @@ -347,8 +356,9 @@ static struct phy_device *of_mdio_find_phy(struct eth_device *edev) return NULL; if (!of_property_read_u32(phy_node, "reg", &addr)) { + of_device_ensure_probed(phy_node->parent); for_each_mii_bus(bus) { - if (bus->parent->device_node == phy_node->parent) { + if (bus->dev.of_node == phy_node->parent) { struct phy_device *phy = mdiobus_scan(bus, addr); if (!IS_ERR(phy)) return phy; @@ -357,7 +367,7 @@ static struct phy_device *of_mdio_find_phy(struct eth_device *edev) } bus_for_each_device(&mdio_bus_type, dev) { - if (dev->device_node == phy_node) + if (dev->of_node == phy_node) return container_of(dev, struct phy_device, dev); } @@ -397,10 +407,6 @@ static int phy_device_attach(struct phy_device *phy, struct eth_device *edev, phy->adjust_link = adjust_link; - /* If the phy is a fixed-link, then call adjust_link directly */ - if (!phy->bus && adjust_link) - adjust_link(edev); - return 0; } @@ -456,7 +462,7 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, ret = -ENODEV; out: if (ret) - puts("Unable to find a PHY (unknown ID?)\n"); + dev_err(&edev->dev, "Unable to find a PHY (unknown ID?)\n"); return ret; } @@ -568,7 +574,7 @@ int phy_wait_aneg_done(struct phy_device *phydev) } do { - genphy_update_link(phydev); + phy_update_status(phydev); if (phydev->link == 1) return 0; } while (!is_timeout(start, PHY_AN_TIMEOUT * SECOND)); @@ -669,7 +675,7 @@ int genphy_aneg_done(struct phy_device *phydev) /* Restart auto-negotiation if remote fault */ if (bmsr & BMSR_RFAULT) { - puts("PHY remote fault detected\n" + dev_info(&phydev->dev, "PHY remote fault detected\n" "PHY restarting auto-negotiation\n"); phy_write(phydev, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART); @@ -813,14 +819,14 @@ int genphy_read_status(struct phy_device *phydev) return 0; } -static inline void mmd_phy_indirect(struct phy_device *phydev, int prtad, - int devad) +static inline void mmd_phy_indirect(struct phy_device *phydev, int devad, + u16 regnum) { /* Write the desired MMD Devad */ phy_write(phydev, MII_MMD_CTRL, devad); /* Write the desired MMD register address */ - phy_write(phydev, MII_MMD_DATA, prtad); + phy_write(phydev, MII_MMD_DATA, regnum); /* Select the Function : DATA with no post increment */ phy_write(phydev, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR)); @@ -844,7 +850,10 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) { u32 ret; - mmd_phy_indirect(phydev, prtad, devad); + phydev_warn(phydev, "%s is deprectated use phy_read_mmd instead\n", + __func__); + + mmd_phy_indirect(phydev, devad, prtad); /* Read the content of the MMD's selected register */ ret = phy_read(phydev, MII_MMD_DATA); @@ -870,12 +879,150 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad) void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, int devad, u16 data) { - mmd_phy_indirect(phydev, prtad, devad); + phydev_warn(phydev, "%s is deprectated use phy_write_mmd instead\n", + __func__); + + mmd_phy_indirect(phydev, devad, prtad); /* Write the data into MMD's selected register */ phy_write(phydev, MII_MMD_DATA, data); } +/** + * phy_modify_mmd_indirect - Convenience function for modifying a MMD register + * @phydev: phy device + * @prtad: MMD Address + * @devad: MMD DEVAD + * @mask: bit mask of bits to clear + * @set: new value of bits set in @mask + * + */ +int phy_modify_mmd_indirect(struct phy_device *phydev, int prtad, int devad, + u16 mask, u16 set) +{ + int ret; + + phydev_warn(phydev, "%s is deprectated use phy_modify_mmd instead\n", + __func__); + + ret = phy_read_mmd_indirect(phydev, prtad, devad); + if (ret < 0) + return ret; + + phy_write_mmd_indirect(phydev, prtad, devad, (ret & ~mask) | set); + + return 0; +} + +/** + * phy_read_mmd - Convenience function for reading a register + * from an MMD on a given PHY. + * @phydev: The phy_device struct + * @devad: The MMD to read from + * @regnum: The register on the MMD to read + * + * Same rules as for phy_read(); + */ +int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) +{ + struct mii_bus *bus = phydev->bus; + int phy_addr = phydev->addr; + + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + + if (phydev->is_c45) { + phydev_warn(phydev, "Clause45 is not supported yet\n"); + return -EOPNOTSUPP; + } + + mmd_phy_indirect(phydev, devad, regnum); + + /* Read the content of the MMD's selected register */ + return mdiobus_read(bus, phy_addr, MII_MMD_DATA); +} +EXPORT_SYMBOL(phy_read_mmd); + +/** + * phy_write_mmd - Convenience function for writing a register + * on an MMD on a given PHY. + * @phydev: The phy_device struct + * @devad: The MMD to read from + * @regnum: The register on the MMD to read + * @val: value to write to @regnum + * + * Same rules as for phy_write(); + */ +int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) +{ + struct mii_bus *bus = phydev->bus; + int phy_addr = phydev->addr; + + if (regnum > (u16)~0 || devad > 32) + return -EINVAL; + + if (phydev->is_c45) { + phydev_warn(phydev, "Clause45 is not supported yet\n"); + return -EOPNOTSUPP; + } + + mmd_phy_indirect(phydev, devad, regnum); + + /* Write the data into MMD's selected register */ + mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); + + return 0; +} +EXPORT_SYMBOL(phy_write_mmd); + +/** + * phy_modify_mmd_changed - Function for modifying a register on MMD + * @phydev: the phy_device struct + * @devad: the MMD containing register to modify + * @regnum: register number to modify + * @mask: bit mask of bits to clear + * @set: new value of bits set in mask to write to @regnum + * + * Returns negative errno, 0 if there was no change, and 1 in case of change + */ +int phy_modify_mmd_changed(struct phy_device *phydev, int devad, u32 regnum, + u16 mask, u16 set) +{ + int new, ret; + + ret = phy_read_mmd(phydev, devad, regnum); + if (ret < 0) + return ret; + + new = (ret & ~mask) | set; + if (new == ret) + return 0; + + ret = phy_write_mmd(phydev, devad, regnum, new); + + return ret < 0 ? ret : 1; +} +EXPORT_SYMBOL_GPL(phy_modify_mmd_changed); + +/** + * phy_modify_mmd - Convenience function for modifying a register on MMD + * @phydev: the phy_device struct + * @devad: the MMD containing register to modify + * @regnum: register number to modify + * @mask: bit mask of bits to clear + * @set: new value of bits set in mask to write to @regnum + */ +int phy_modify_mmd(struct phy_device *phydev, int devad, u32 regnum, + u16 mask, u16 set) +{ + int ret; + + ret = phy_modify_mmd_changed(phydev, devad, regnum, mask, set); + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(phy_modify_mmd); + int genphy_config_init(struct phy_device *phydev) { int val; @@ -927,6 +1074,8 @@ int phy_driver_register(struct phy_driver *phydrv) { phydrv->drv.bus = &mdio_bus_type; + phydrv->is_phy = true; + if (!phydrv->config_init) phydrv->config_init = genphy_config_init; @@ -978,8 +1127,4 @@ static struct phy_driver genphy_driver = { SUPPORTED_BNC, }; -static int generic_phy_register(void) -{ - return phy_driver_register(&genphy_driver); -} -device_initcall(generic_phy_register); +device_phy_driver(genphy_driver); diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 4ae050128c..c23947b7cb 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * drivers/net/phy/realtek.c * @@ -6,12 +7,6 @@ * Author: Johnson Leung <r58129@freescale.com> * * Copyright (c) 2004 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * */ #include <common.h> #include <init.h> @@ -34,6 +29,7 @@ #define RTL8211F_INSR 0x1d #define RTL8211F_TX_DELAY BIT(8) +#define RTL8211F_RX_DELAY BIT(3) #define RTL8201F_ISR 0x1e #define RTL8201F_IER 0x13 @@ -84,19 +80,50 @@ static int rtl8211c_config_init(struct phy_device *phydev) static int rtl8211f_config_init(struct phy_device *phydev) { + struct device *dev = &phydev->dev; + u16 val_txdly, val_rxdly; int ret; - u16 val = 0; - ret = genphy_config_init(phydev); - if (ret < 0) + switch (phydev->interface) { + case PHY_INTERFACE_MODE_RGMII: + val_txdly = 0; + val_rxdly = 0; + break; + + case PHY_INTERFACE_MODE_RGMII_RXID: + val_txdly = 0; + val_rxdly = RTL8211F_RX_DELAY; + break; + + case PHY_INTERFACE_MODE_RGMII_TXID: + val_txdly = RTL8211F_TX_DELAY; + val_rxdly = 0; + break; + + case PHY_INTERFACE_MODE_RGMII_ID: + val_txdly = RTL8211F_TX_DELAY; + val_rxdly = RTL8211F_RX_DELAY; + break; + + default: /* the rest of the modes imply leaving delay as is. */ + return 0; + } + + ret = phy_modify_paged(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, + val_txdly); + if (ret < 0) { + dev_err(dev, "Failed to update the TX delay register\n"); return ret; + } - /* enable TX-delay for rgmii-id and rgmii-txid, otherwise disable it */ - if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || - phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) - val = RTL8211F_TX_DELAY; + ret = phy_modify_paged(phydev, 0xd08, 0x15, RTL8211F_RX_DELAY, + val_rxdly); + if (ret < 0) { + dev_err(dev, "Failed to update the RX delay register\n"); + return ret; + } - return phy_modify_paged(phydev, 0xd08, 0x11, RTL8211F_TX_DELAY, val); + return 0; } static int rtl8366rb_config_init(struct phy_device *phydev) @@ -128,6 +155,12 @@ static struct phy_driver realtek_drvs[] = { .read_page = rtl821x_read_page, .write_page = rtl821x_write_page, }, { + PHY_ID_MATCH_EXACT(0x001cc840), + .drv.name = "RTL8226B_RTL8221B 2.5Gbps PHY", + .features = PHY_GBIT_FEATURES, + .read_page = rtl821x_read_page, + .write_page = rtl821x_write_page, + }, { PHY_ID_MATCH_EXACT(0x001cc910), .drv.name = "RTL8211 Gigabit Ethernet", .features = PHY_GBIT_FEATURES, @@ -164,9 +197,4 @@ static struct phy_driver realtek_drvs[] = { }, }; -static int __init realtek_phy_init(void) -{ - return phy_drivers_register(realtek_drvs, - ARRAY_SIZE(realtek_drvs)); -} -fs_initcall(realtek_phy_init); +device_phy_drivers(realtek_drvs); diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index d6705e4fe2..1e1f3d5274 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -135,8 +135,4 @@ static struct phy_driver smsc_phy_driver[] = { .config_init = lan87xx_config_init, } }; -static int __init smsc_init(void) -{ - return phy_drivers_register(smsc_phy_driver, ARRAY_SIZE(smsc_phy_driver)); -} -fs_initcall(smsc_init); +device_phy_drivers(smsc_phy_driver); diff --git a/drivers/net/r8169.h b/drivers/net/r8169.h new file mode 100644 index 0000000000..55ef8251fe --- /dev/null +++ b/drivers/net/r8169.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* r8169.h: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + */ + +#include <linux/types.h> +#include <linux/phy.h> + +enum mac_version { + /* support for ancient RTL_GIGA_MAC_VER_01 has been removed */ + RTL_GIGA_MAC_VER_02, + RTL_GIGA_MAC_VER_03, + RTL_GIGA_MAC_VER_04, + RTL_GIGA_MAC_VER_05, + RTL_GIGA_MAC_VER_06, + RTL_GIGA_MAC_VER_07, + RTL_GIGA_MAC_VER_08, + RTL_GIGA_MAC_VER_09, + RTL_GIGA_MAC_VER_10, + RTL_GIGA_MAC_VER_11, + /* RTL_GIGA_MAC_VER_12 was handled the same as VER_17 */ + /* RTL_GIGA_MAC_VER_13 was merged with VER_10 */ + RTL_GIGA_MAC_VER_14, + /* RTL_GIGA_MAC_VER_16 was merged with VER_10 */ + RTL_GIGA_MAC_VER_17, + RTL_GIGA_MAC_VER_18, + RTL_GIGA_MAC_VER_19, + RTL_GIGA_MAC_VER_20, + RTL_GIGA_MAC_VER_21, + RTL_GIGA_MAC_VER_22, + RTL_GIGA_MAC_VER_23, + RTL_GIGA_MAC_VER_24, + RTL_GIGA_MAC_VER_25, + RTL_GIGA_MAC_VER_26, + /* support for RTL_GIGA_MAC_VER_27 has been removed */ + RTL_GIGA_MAC_VER_28, + RTL_GIGA_MAC_VER_29, + RTL_GIGA_MAC_VER_30, + RTL_GIGA_MAC_VER_31, + RTL_GIGA_MAC_VER_32, + RTL_GIGA_MAC_VER_33, + RTL_GIGA_MAC_VER_34, + RTL_GIGA_MAC_VER_35, + RTL_GIGA_MAC_VER_36, + RTL_GIGA_MAC_VER_37, + RTL_GIGA_MAC_VER_38, + RTL_GIGA_MAC_VER_39, + RTL_GIGA_MAC_VER_40, + /* support for RTL_GIGA_MAC_VER_41 has been removed */ + RTL_GIGA_MAC_VER_42, + RTL_GIGA_MAC_VER_43, + RTL_GIGA_MAC_VER_44, + /* support for RTL_GIGA_MAC_VER_45 has been removed */ + RTL_GIGA_MAC_VER_46, + /* support for RTL_GIGA_MAC_VER_47 has been removed */ + RTL_GIGA_MAC_VER_48, + /* support for RTL_GIGA_MAC_VER_49 has been removed */ + /* support for RTL_GIGA_MAC_VER_50 has been removed */ + RTL_GIGA_MAC_VER_51, + RTL_GIGA_MAC_VER_52, + RTL_GIGA_MAC_VER_53, + /* support for RTL_GIGA_MAC_VER_60 has been removed */ + RTL_GIGA_MAC_VER_61, + RTL_GIGA_MAC_VER_63, + RTL_GIGA_MAC_NONE +}; + +struct rtl8169_private; + +void r8169_apply_firmware(struct rtl8169_private *tp); +u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp); +u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr); +void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, + enum mac_version ver); diff --git a/drivers/net/r8169_firmware.c b/drivers/net/r8169_firmware.c new file mode 100644 index 0000000000..29c6be50a7 --- /dev/null +++ b/drivers/net/r8169_firmware.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* r8169_firmware.c: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + */ + +#include <common.h> +#include <firmware.h> + +#include "r8169_firmware.h" + +enum rtl_fw_opcode { + PHY_READ = 0x0, + PHY_DATA_OR = 0x1, + PHY_DATA_AND = 0x2, + PHY_BJMPN = 0x3, + PHY_MDIO_CHG = 0x4, + PHY_CLEAR_READCOUNT = 0x7, + PHY_WRITE = 0x8, + PHY_READCOUNT_EQ_SKIP = 0x9, + PHY_COMP_EQ_SKIPN = 0xa, + PHY_COMP_NEQ_SKIPN = 0xb, + PHY_WRITE_PREVIOUS = 0xc, + PHY_SKIPN = 0xd, + PHY_DELAY_MS = 0xe, +}; + +struct fw_info { + u32 magic; + char version[RTL_VER_SIZE]; + __le32 fw_start; + __le32 fw_len; + u8 chksum; +} __packed; + +#define FW_OPCODE_SIZE sizeof_field(struct rtl_fw_phy_action, code[0]) + +static bool rtl_fw_format_ok(struct rtl_fw *rtl_fw) +{ + const struct firmware *fw = rtl_fw->fw; + struct fw_info *fw_info = (struct fw_info *)fw->data; + struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; + + if (fw->size < FW_OPCODE_SIZE) + return false; + + if (!fw_info->magic) { + size_t i, size, start; + u8 checksum = 0; + + if (fw->size < sizeof(*fw_info)) + return false; + + for (i = 0; i < fw->size; i++) + checksum += fw->data[i]; + if (checksum != 0) + return false; + + start = le32_to_cpu(fw_info->fw_start); + if (start > fw->size) + return false; + + size = le32_to_cpu(fw_info->fw_len); + if (size > (fw->size - start) / FW_OPCODE_SIZE) + return false; + + strlcpy(rtl_fw->version, fw_info->version, RTL_VER_SIZE); + + pa->code = (__le32 *)(fw->data + start); + pa->size = size; + } else { + if (fw->size % FW_OPCODE_SIZE) + return false; + + strlcpy(rtl_fw->version, rtl_fw->fw_name, RTL_VER_SIZE); + + pa->code = (__le32 *)fw->data; + pa->size = fw->size / FW_OPCODE_SIZE; + } + + return true; +} + +static bool rtl_fw_data_ok(struct rtl_fw *rtl_fw) +{ + struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; + size_t index; + + for (index = 0; index < pa->size; index++) { + u32 action = le32_to_cpu(pa->code[index]); + u32 val = action & 0x0000ffff; + u32 regno = (action & 0x0fff0000) >> 16; + + switch (action >> 28) { + case PHY_READ: + case PHY_DATA_OR: + case PHY_DATA_AND: + case PHY_CLEAR_READCOUNT: + case PHY_WRITE: + case PHY_WRITE_PREVIOUS: + case PHY_DELAY_MS: + break; + + case PHY_MDIO_CHG: + if (val > 1) + goto out; + break; + + case PHY_BJMPN: + if (regno > index) + goto out; + break; + case PHY_READCOUNT_EQ_SKIP: + if (index + 2 >= pa->size) + goto out; + break; + case PHY_COMP_EQ_SKIPN: + case PHY_COMP_NEQ_SKIPN: + case PHY_SKIPN: + if (index + 1 + regno >= pa->size) + goto out; + break; + + default: + dev_err(rtl_fw->dev, "Invalid action 0x%08x\n", action); + return false; + } + } + + return true; +out: + dev_err(rtl_fw->dev, "Out of range of firmware\n"); + return false; +} + +void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw) +{ + struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; + rtl_fw_write_t fw_write = rtl_fw->phy_write; + rtl_fw_read_t fw_read = rtl_fw->phy_read; + int predata = 0, count = 0; + size_t index; + + for (index = 0; index < pa->size; index++) { + u32 action = le32_to_cpu(pa->code[index]); + u32 data = action & 0x0000ffff; + u32 regno = (action & 0x0fff0000) >> 16; + enum rtl_fw_opcode opcode = action >> 28; + + if (!action) + break; + + switch (opcode) { + case PHY_READ: + predata = fw_read(tp, regno); + count++; + break; + case PHY_DATA_OR: + predata |= data; + break; + case PHY_DATA_AND: + predata &= data; + break; + case PHY_BJMPN: + index -= (regno + 1); + break; + case PHY_MDIO_CHG: + if (data) { + fw_write = rtl_fw->mac_mcu_write; + fw_read = rtl_fw->mac_mcu_read; + } else { + fw_write = rtl_fw->phy_write; + fw_read = rtl_fw->phy_read; + } + + break; + case PHY_CLEAR_READCOUNT: + count = 0; + break; + case PHY_WRITE: + fw_write(tp, regno, data); + break; + case PHY_READCOUNT_EQ_SKIP: + if (count == data) + index++; + break; + case PHY_COMP_EQ_SKIPN: + if (predata == data) + index += regno; + break; + case PHY_COMP_NEQ_SKIPN: + if (predata != data) + index += regno; + break; + case PHY_WRITE_PREVIOUS: + fw_write(tp, regno, predata); + break; + case PHY_SKIPN: + index += regno; + break; + case PHY_DELAY_MS: + mdelay(data); + break; + } + } +} + +void rtl_fw_release_firmware(struct rtl_fw *rtl_fw) +{ + release_firmware(rtl_fw->fw); +} + +int rtl_fw_request_firmware(struct rtl_fw *rtl_fw) +{ + int rc; + + rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev); + if (rc < 0) + goto out; + + if (!rtl_fw_format_ok(rtl_fw) || !rtl_fw_data_ok(rtl_fw)) { + release_firmware(rtl_fw->fw); + rc = -EINVAL; + goto out; + } + + return 0; +out: + /* At least some NiCs work without firmware, even if there is one */ + dev_dbg(rtl_fw->dev, "Unable to load firmware %s (%d)\n", + rtl_fw->fw_name, rc); + return rc; +} diff --git a/drivers/net/r8169_firmware.h b/drivers/net/r8169_firmware.h new file mode 100644 index 0000000000..8d3b037225 --- /dev/null +++ b/drivers/net/r8169_firmware.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* r8169_firmware.h: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + */ + +struct rtl8169_private; +typedef void (*rtl_fw_write_t)(struct rtl8169_private *tp, int reg, int val); +typedef int (*rtl_fw_read_t)(struct rtl8169_private *tp, int reg); + +#define RTL_VER_SIZE 32 + +struct rtl_fw { + rtl_fw_write_t phy_write; + rtl_fw_read_t phy_read; + rtl_fw_write_t mac_mcu_write; + rtl_fw_read_t mac_mcu_read; + const struct firmware *fw; + const char *fw_name; + struct device *dev; + + char version[RTL_VER_SIZE]; + + struct rtl_fw_phy_action { + __le32 *code; + size_t size; + } phy_action; +}; + +int rtl_fw_request_firmware(struct rtl_fw *rtl_fw); +void rtl_fw_release_firmware(struct rtl_fw *rtl_fw); +void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw); diff --git a/drivers/net/r8169_main.c b/drivers/net/r8169_main.c new file mode 100644 index 0000000000..fd53ec1bc3 --- /dev/null +++ b/drivers/net/r8169_main.c @@ -0,0 +1,3215 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * r8169.c: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + */ + +#include <common.h> +#include <dma.h> +#include <init.h> +#include <net.h> +#include <malloc.h> +#include <linux/pci.h> +#include <linux/sizes.h> +#include <asm/unaligned.h> + +#include "r8169.h" +#include "r8169_firmware.h" + +#define FIRMWARE_8168D_1 "rtl_nic/rtl8168d-1.fw" +#define FIRMWARE_8168D_2 "rtl_nic/rtl8168d-2.fw" +#define FIRMWARE_8168E_1 "rtl_nic/rtl8168e-1.fw" +#define FIRMWARE_8168E_2 "rtl_nic/rtl8168e-2.fw" +#define FIRMWARE_8168E_3 "rtl_nic/rtl8168e-3.fw" +#define FIRMWARE_8168F_1 "rtl_nic/rtl8168f-1.fw" +#define FIRMWARE_8168F_2 "rtl_nic/rtl8168f-2.fw" +#define FIRMWARE_8105E_1 "rtl_nic/rtl8105e-1.fw" +#define FIRMWARE_8402_1 "rtl_nic/rtl8402-1.fw" +#define FIRMWARE_8411_1 "rtl_nic/rtl8411-1.fw" +#define FIRMWARE_8411_2 "rtl_nic/rtl8411-2.fw" +#define FIRMWARE_8106E_1 "rtl_nic/rtl8106e-1.fw" +#define FIRMWARE_8106E_2 "rtl_nic/rtl8106e-2.fw" +#define FIRMWARE_8168G_2 "rtl_nic/rtl8168g-2.fw" +#define FIRMWARE_8168G_3 "rtl_nic/rtl8168g-3.fw" +#define FIRMWARE_8168H_2 "rtl_nic/rtl8168h-2.fw" +#define FIRMWARE_8168FP_3 "rtl_nic/rtl8168fp-3.fw" +#define FIRMWARE_8107E_2 "rtl_nic/rtl8107e-2.fw" +#define FIRMWARE_8125A_3 "rtl_nic/rtl8125a-3.fw" +#define FIRMWARE_8125B_2 "rtl_nic/rtl8125b-2.fw" + +/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). + The RTL chips use a 64 element hash table based on the Ethernet CRC. */ +#define MC_FILTER_LIMIT 32 + +#define TX_DMA_BURST 7 /* Maximum PCI burst, '7' is unlimited */ +#define InterFrameGap 0x03 /* 3 means InterFrameGap = the shortest one */ + +#define R8169_REGS_SIZE 256 +#define R8169_RX_BUF_SIZE (SZ_16K - 1) +#define NUM_TX_DESC 256 /* Number of Tx descriptor registers */ +#define NUM_RX_DESC 256 /* Number of Rx descriptor registers */ +#define R8169_TX_RING_BYTES (NUM_TX_DESC * sizeof(struct TxDesc)) +#define R8169_RX_RING_BYTES (NUM_RX_DESC * sizeof(struct RxDesc)) + +#define OCP_STD_PHY_BASE 0xa400 + +#define RTL_CFG_NO_GBIT 1 + +/* write/read MMIO register */ +#define RTL_W8(tp, reg, val8) writeb((val8), tp->mmio_addr + (reg)) +#define RTL_W16(tp, reg, val16) writew((val16), tp->mmio_addr + (reg)) +#define RTL_W32(tp, reg, val32) writel((val32), tp->mmio_addr + (reg)) +#define RTL_R8(tp, reg) readb(tp->mmio_addr + (reg)) +#define RTL_R16(tp, reg) readw(tp->mmio_addr + (reg)) +#define RTL_R32(tp, reg) readl(tp->mmio_addr + (reg)) + +#define JUMBO_4K (4 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) +#define JUMBO_6K (6 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) +#define JUMBO_7K (7 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) +#define JUMBO_9K (9 * SZ_1K - VLAN_ETH_HLEN - ETH_FCS_LEN) + +static const struct { + const char *name; + const char *fw_name; +} rtl_chip_infos[] = { + /* PCI devices. */ + [RTL_GIGA_MAC_VER_02] = {"RTL8169s" }, + [RTL_GIGA_MAC_VER_03] = {"RTL8110s" }, + [RTL_GIGA_MAC_VER_04] = {"RTL8169sb/8110sb" }, + [RTL_GIGA_MAC_VER_05] = {"RTL8169sc/8110sc" }, + [RTL_GIGA_MAC_VER_06] = {"RTL8169sc/8110sc" }, + /* PCI-E devices. */ + [RTL_GIGA_MAC_VER_07] = {"RTL8102e" }, + [RTL_GIGA_MAC_VER_08] = {"RTL8102e" }, + [RTL_GIGA_MAC_VER_09] = {"RTL8102e/RTL8103e" }, + [RTL_GIGA_MAC_VER_10] = {"RTL8101e/RTL8100e" }, + [RTL_GIGA_MAC_VER_11] = {"RTL8168b/8111b" }, + [RTL_GIGA_MAC_VER_14] = {"RTL8401" }, + [RTL_GIGA_MAC_VER_17] = {"RTL8168b/8111b" }, + [RTL_GIGA_MAC_VER_18] = {"RTL8168cp/8111cp" }, + [RTL_GIGA_MAC_VER_19] = {"RTL8168c/8111c" }, + [RTL_GIGA_MAC_VER_20] = {"RTL8168c/8111c" }, + [RTL_GIGA_MAC_VER_21] = {"RTL8168c/8111c" }, + [RTL_GIGA_MAC_VER_22] = {"RTL8168c/8111c" }, + [RTL_GIGA_MAC_VER_23] = {"RTL8168cp/8111cp" }, + [RTL_GIGA_MAC_VER_24] = {"RTL8168cp/8111cp" }, + [RTL_GIGA_MAC_VER_25] = {"RTL8168d/8111d", FIRMWARE_8168D_1}, + [RTL_GIGA_MAC_VER_26] = {"RTL8168d/8111d", FIRMWARE_8168D_2}, + [RTL_GIGA_MAC_VER_28] = {"RTL8168dp/8111dp" }, + [RTL_GIGA_MAC_VER_29] = {"RTL8105e", FIRMWARE_8105E_1}, + [RTL_GIGA_MAC_VER_30] = {"RTL8105e", FIRMWARE_8105E_1}, + [RTL_GIGA_MAC_VER_31] = {"RTL8168dp/8111dp" }, + [RTL_GIGA_MAC_VER_32] = {"RTL8168e/8111e", FIRMWARE_8168E_1}, + [RTL_GIGA_MAC_VER_33] = {"RTL8168e/8111e", FIRMWARE_8168E_2}, + [RTL_GIGA_MAC_VER_34] = {"RTL8168evl/8111evl", FIRMWARE_8168E_3}, + [RTL_GIGA_MAC_VER_35] = {"RTL8168f/8111f", FIRMWARE_8168F_1}, + [RTL_GIGA_MAC_VER_36] = {"RTL8168f/8111f", FIRMWARE_8168F_2}, + [RTL_GIGA_MAC_VER_37] = {"RTL8402", FIRMWARE_8402_1 }, + [RTL_GIGA_MAC_VER_38] = {"RTL8411", FIRMWARE_8411_1 }, + [RTL_GIGA_MAC_VER_39] = {"RTL8106e", FIRMWARE_8106E_1}, + [RTL_GIGA_MAC_VER_40] = {"RTL8168g/8111g", FIRMWARE_8168G_2}, + [RTL_GIGA_MAC_VER_42] = {"RTL8168gu/8111gu", FIRMWARE_8168G_3}, + [RTL_GIGA_MAC_VER_43] = {"RTL8106eus", FIRMWARE_8106E_2}, + [RTL_GIGA_MAC_VER_44] = {"RTL8411b", FIRMWARE_8411_2 }, + [RTL_GIGA_MAC_VER_46] = {"RTL8168h/8111h", FIRMWARE_8168H_2}, + [RTL_GIGA_MAC_VER_48] = {"RTL8107e", FIRMWARE_8107E_2}, + [RTL_GIGA_MAC_VER_51] = {"RTL8168ep/8111ep" }, + [RTL_GIGA_MAC_VER_52] = {"RTL8168fp/RTL8117", FIRMWARE_8168FP_3}, + [RTL_GIGA_MAC_VER_53] = {"RTL8168fp/RTL8117", }, + [RTL_GIGA_MAC_VER_61] = {"RTL8125A", FIRMWARE_8125A_3}, + /* reserve 62 for CFG_METHOD_4 in the vendor driver */ + [RTL_GIGA_MAC_VER_63] = {"RTL8125B", FIRMWARE_8125B_2}, +}; + +static const struct pci_device_id rtl8169_pci_tbl[] = { + { PCI_VDEVICE(REALTEK, 0x2502) }, + { PCI_VDEVICE(REALTEK, 0x2600) }, + { PCI_VDEVICE(REALTEK, 0x8129) }, + { PCI_VDEVICE(REALTEK, 0x8136), RTL_CFG_NO_GBIT }, + { PCI_VDEVICE(REALTEK, 0x8161) }, + { PCI_VDEVICE(REALTEK, 0x8162) }, + { PCI_VDEVICE(REALTEK, 0x8167) }, + { PCI_VDEVICE(REALTEK, 0x8168) }, + { PCI_VDEVICE(NCUBE, 0x8168) }, + { PCI_VDEVICE(REALTEK, 0x8169) }, + { PCI_VENDOR_ID_DLINK, 0x4300, + PCI_VENDOR_ID_DLINK, 0x4b10, 0, 0 }, + { PCI_VDEVICE(DLINK, 0x4300) }, + { PCI_VDEVICE(DLINK, 0x4302) }, + { PCI_VDEVICE(AT, 0xc107) }, + { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0024 }, + { 0x0001, 0x8168, PCI_ANY_ID, 0x2410 }, + { PCI_VDEVICE(REALTEK, 0x8125) }, + { PCI_VDEVICE(REALTEK, 0x3000) }, + {} +}; + +MODULE_DEVICE_TABLE(pci, rtl8169_pci_tbl); + +enum rtl_registers { + MAC0 = 0, /* Ethernet hardware address. */ + MAC4 = 4, + MAR0 = 8, /* Multicast filter. */ + CounterAddrLow = 0x10, + CounterAddrHigh = 0x14, + TxDescStartAddrLow = 0x20, + TxDescStartAddrHigh = 0x24, + TxHDescStartAddrLow = 0x28, + TxHDescStartAddrHigh = 0x2c, + FLASH = 0x30, + ERSR = 0x36, + ChipCmd = 0x37, + TxPoll = 0x38, + IntrMask = 0x3c, + IntrStatus = 0x3e, + + TxConfig = 0x40, +#define TXCFG_AUTO_FIFO (1 << 7) /* 8111e-vl */ +#define TXCFG_EMPTY (1 << 11) /* 8111e-vl */ + + RxConfig = 0x44, +#define RX128_INT_EN (1 << 15) /* 8111c and later */ +#define RX_MULTI_EN (1 << 14) /* 8111c only */ +#define RXCFG_FIFO_SHIFT 13 + /* No threshold before first PCI xfer */ +#define RX_FIFO_THRESH (7 << RXCFG_FIFO_SHIFT) +#define RX_EARLY_OFF (1 << 11) +#define RXCFG_DMA_SHIFT 8 + /* Unlimited maximum PCI burst. */ +#define RX_DMA_BURST (7 << RXCFG_DMA_SHIFT) + RxMissed = 0x4c, + Cfg9346 = 0x50, + Config0 = 0x51, + Config1 = 0x52, + Config2 = 0x53, +#define PME_SIGNAL (1 << 5) /* 8168c and later */ + + Config3 = 0x54, + Config4 = 0x55, + Config5 = 0x56, + PHYAR = 0x60, + PHYstatus = 0x6c, + RxMaxSize = 0xda, + CPlusCmd = 0xe0, + IntrMitigate = 0xe2, + +#define RTL_COALESCE_TX_USECS GENMASK(15, 12) +#define RTL_COALESCE_TX_FRAMES GENMASK(11, 8) +#define RTL_COALESCE_RX_USECS GENMASK(7, 4) +#define RTL_COALESCE_RX_FRAMES GENMASK(3, 0) + +#define RTL_COALESCE_T_MAX 0x0fU +#define RTL_COALESCE_FRAME_MAX (RTL_COALESCE_T_MAX * 4) + + RxDescAddrLow = 0xe4, + RxDescAddrHigh = 0xe8, + EarlyTxThres = 0xec, /* 8169. Unit of 32 bytes. */ + +#define NoEarlyTx 0x3f /* Max value : no early transmit. */ + + MaxTxPacketSize = 0xec, /* 8101/8168. Unit of 128 bytes. */ + +#define TxPacketMax (8064 >> 7) +#define EarlySize 0x27 + + FuncEvent = 0xf0, + FuncEventMask = 0xf4, + FuncPresetState = 0xf8, + IBCR0 = 0xf8, + IBCR2 = 0xf9, + IBIMR0 = 0xfa, + IBISR0 = 0xfb, + FuncForceEvent = 0xfc, +}; + +enum rtl8168_8101_registers { + CSIDR = 0x64, + CSIAR = 0x68, +#define CSIAR_FLAG 0x80000000 +#define CSIAR_WRITE_CMD 0x80000000 +#define CSIAR_BYTE_ENABLE 0x0000f000 +#define CSIAR_ADDR_MASK 0x00000fff + PMCH = 0x6f, +#define D3COLD_NO_PLL_DOWN BIT(7) +#define D3HOT_NO_PLL_DOWN BIT(6) +#define D3_NO_PLL_DOWN (BIT(7) | BIT(6)) + EPHYAR = 0x80, +#define EPHYAR_FLAG 0x80000000 +#define EPHYAR_WRITE_CMD 0x80000000 +#define EPHYAR_REG_MASK 0x1f +#define EPHYAR_REG_SHIFT 16 +#define EPHYAR_DATA_MASK 0xffff + DLLPR = 0xd0, +#define PFM_EN (1 << 6) +#define TX_10M_PS_EN (1 << 7) + DBG_REG = 0xd1, +#define FIX_NAK_1 (1 << 4) +#define FIX_NAK_2 (1 << 3) + TWSI = 0xd2, + MCU = 0xd3, +#define NOW_IS_OOB (1 << 7) +#define TX_EMPTY (1 << 5) +#define RX_EMPTY (1 << 4) +#define RXTX_EMPTY (TX_EMPTY | RX_EMPTY) +#define EN_NDP (1 << 3) +#define EN_OOB_RESET (1 << 2) +#define LINK_LIST_RDY (1 << 1) + EFUSEAR = 0xdc, +#define EFUSEAR_FLAG 0x80000000 +#define EFUSEAR_WRITE_CMD 0x80000000 +#define EFUSEAR_READ_CMD 0x00000000 +#define EFUSEAR_REG_MASK 0x03ff +#define EFUSEAR_REG_SHIFT 8 +#define EFUSEAR_DATA_MASK 0xff + MISC_1 = 0xf2, +#define PFM_D3COLD_EN (1 << 6) +}; + +enum rtl8168_registers { + LED_FREQ = 0x1a, + EEE_LED = 0x1b, + ERIDR = 0x70, + ERIAR = 0x74, +#define ERIAR_FLAG 0x80000000 +#define ERIAR_WRITE_CMD 0x80000000 +#define ERIAR_READ_CMD 0x00000000 +#define ERIAR_ADDR_BYTE_ALIGN 4 +#define ERIAR_TYPE_SHIFT 16 +#define ERIAR_EXGMAC (0x00 << ERIAR_TYPE_SHIFT) +#define ERIAR_MSIX (0x01 << ERIAR_TYPE_SHIFT) +#define ERIAR_ASF (0x02 << ERIAR_TYPE_SHIFT) +#define ERIAR_OOB (0x02 << ERIAR_TYPE_SHIFT) +#define ERIAR_MASK_SHIFT 12 +#define ERIAR_MASK_0001 (0x1 << ERIAR_MASK_SHIFT) +#define ERIAR_MASK_0011 (0x3 << ERIAR_MASK_SHIFT) +#define ERIAR_MASK_0100 (0x4 << ERIAR_MASK_SHIFT) +#define ERIAR_MASK_0101 (0x5 << ERIAR_MASK_SHIFT) +#define ERIAR_MASK_1111 (0xf << ERIAR_MASK_SHIFT) + EPHY_RXER_NUM = 0x7c, + OCPDR = 0xb0, /* OCP GPHY access */ +#define OCPDR_WRITE_CMD 0x80000000 +#define OCPDR_READ_CMD 0x00000000 +#define OCPDR_REG_MASK 0x7f +#define OCPDR_GPHY_REG_SHIFT 16 +#define OCPDR_DATA_MASK 0xffff + OCPAR = 0xb4, +#define OCPAR_FLAG 0x80000000 +#define OCPAR_GPHY_WRITE_CMD 0x8000f060 +#define OCPAR_GPHY_READ_CMD 0x0000f060 + GPHY_OCP = 0xb8, + RDSAR1 = 0xd0, /* 8168c only. Undocumented on 8168dp */ + MISC = 0xf0, /* 8168e only. */ +#define TXPLA_RST (1 << 29) +#define DISABLE_LAN_EN (1 << 23) /* Enable GPIO pin */ +#define PWM_EN (1 << 22) +#define RXDV_GATED_EN (1 << 19) +#define EARLY_TALLY_EN (1 << 16) +}; + +enum rtl8125_registers { + IntrMask_8125 = 0x38, + IntrStatus_8125 = 0x3c, + TxPoll_8125 = 0x90, + MAC0_BKP = 0x19e0, + EEE_TXIDLE_TIMER_8125 = 0x6048, +}; + +#define RX_VLAN_INNER_8125 BIT(22) +#define RX_VLAN_OUTER_8125 BIT(23) +#define RX_VLAN_8125 (RX_VLAN_INNER_8125 | RX_VLAN_OUTER_8125) + +#define RX_FETCH_DFLT_8125 (8 << 27) + +enum rtl_register_content { + /* InterruptStatusBits */ + SYSErr = 0x8000, + PCSTimeout = 0x4000, + SWInt = 0x0100, + TxDescUnavail = 0x0080, + RxFIFOOver = 0x0040, + LinkChg = 0x0020, + RxOverflow = 0x0010, + TxErr = 0x0008, + TxOK = 0x0004, + RxErr = 0x0002, + RxOK = 0x0001, + + /* RxStatusDesc */ + RxRWT = (1 << 22), + RxRES = (1 << 21), + RxRUNT = (1 << 20), + RxCRC = (1 << 19), + + /* ChipCmdBits */ + StopReq = 0x80, + CmdReset = 0x10, + CmdRxEnb = 0x08, + CmdTxEnb = 0x04, + RxBufEmpty = 0x01, + + /* TXPoll register p.5 */ + HPQ = 0x80, /* Poll cmd on the high prio queue */ + NPQ = 0x40, /* Poll cmd on the low prio queue */ + FSWInt = 0x01, /* Forced software interrupt */ + + /* Cfg9346Bits */ + Cfg9346_Lock = 0x00, + Cfg9346_Unlock = 0xc0, + + /* rx_mode_bits */ + AcceptErr = 0x20, + AcceptRunt = 0x10, +#define RX_CONFIG_ACCEPT_ERR_MASK 0x30 + AcceptBroadcast = 0x08, + AcceptMulticast = 0x04, + AcceptMyPhys = 0x02, + AcceptAllPhys = 0x01, +#define RX_CONFIG_ACCEPT_OK_MASK 0x0f +#define RX_CONFIG_ACCEPT_MASK 0x3f + + /* TxConfigBits */ + TxInterFrameGapShift = 24, + TxDMAShift = 8, /* DMA burst value (0-7) is shift this many bits */ + + /* Config1 register p.24 */ + LEDS1 = (1 << 7), + LEDS0 = (1 << 6), + Speed_down = (1 << 4), + MEMMAP = (1 << 3), + IOMAP = (1 << 2), + VPD = (1 << 1), + PMEnable = (1 << 0), /* Power Management Enable */ + + /* Config2 register p. 25 */ + ClkReqEn = (1 << 7), /* Clock Request Enable */ + MSIEnable = (1 << 5), /* 8169 only. Reserved in the 8168. */ + PCI_Clock_66MHz = 0x01, + PCI_Clock_33MHz = 0x00, + + /* Config3 register p.25 */ + MagicPacket = (1 << 5), /* Wake up when receives a Magic Packet */ + LinkUp = (1 << 4), /* Wake up when the cable connection is re-established */ + Jumbo_En0 = (1 << 2), /* 8168 only. Reserved in the 8168b */ + Rdy_to_L23 = (1 << 1), /* L23 Enable */ + Beacon_en = (1 << 0), /* 8168 only. Reserved in the 8168b */ + + /* Config4 register */ + Jumbo_En1 = (1 << 1), /* 8168 only. Reserved in the 8168b */ + + /* Config5 register p.27 */ + BWF = (1 << 6), /* Accept Broadcast wakeup frame */ + MWF = (1 << 5), /* Accept Multicast wakeup frame */ + UWF = (1 << 4), /* Accept Unicast wakeup frame */ + Spi_en = (1 << 3), + LanWake = (1 << 1), /* LanWake enable/disable */ + PMEStatus = (1 << 0), /* PME status can be reset by PCI RST# */ + ASPM_en = (1 << 0), /* ASPM enable */ + + /* CPlusCmd p.31 */ + EnableBist = (1 << 15), // 8168 8101 + Mac_dbgo_oe = (1 << 14), // 8168 8101 + EnAnaPLL = (1 << 14), // 8169 + Normal_mode = (1 << 13), // unused + Force_half_dup = (1 << 12), // 8168 8101 + Force_rxflow_en = (1 << 11), // 8168 8101 + Force_txflow_en = (1 << 10), // 8168 8101 + Cxpl_dbg_sel = (1 << 9), // 8168 8101 + ASF = (1 << 8), // 8168 8101 + PktCntrDisable = (1 << 7), // 8168 8101 + Mac_dbgo_sel = 0x001c, // 8168 + RxVlan = (1 << 6), + RxChkSum = (1 << 5), + PCIDAC = (1 << 4), + PCIMulRW = (1 << 3), +#define INTT_MASK GENMASK(1, 0) +#define CPCMD_MASK (Normal_mode | RxVlan | RxChkSum | INTT_MASK) + + /* rtl8169_PHYstatus */ + TBI_Enable = 0x80, + TxFlowCtrl = 0x40, + RxFlowCtrl = 0x20, + _1000bpsF = 0x10, + _100bps = 0x08, + _10bps = 0x04, + LinkStatus = 0x02, + FullDup = 0x01, + + /* ResetCounterCommand */ + CounterReset = 0x1, + + /* DumpCounterCommand */ + CounterDump = 0x8, + + /* magic enable v2 */ + MagicPacket_v2 = (1 << 16), /* Wake up when receives a Magic Packet */ +}; + +enum rtl_desc_bit { + /* First doubleword. */ + DescOwn = (1 << 31), /* Descriptor is owned by NIC */ + RingEnd = (1 << 30), /* End of descriptor ring */ + FirstFrag = (1 << 29), /* First segment of a packet */ + LastFrag = (1 << 28), /* Final segment of a packet */ +}; + +/* Generic case. */ +enum rtl_tx_desc_bit { + /* First doubleword. */ + TD_LSO = (1 << 27), /* Large Send Offload */ +#define TD_MSS_MAX 0x07ffu /* MSS value */ + + /* Second doubleword. */ + TxVlanTag = (1 << 17), /* Add VLAN tag */ +}; + +/* 8169, 8168b and 810x except 8102e. */ +enum rtl_tx_desc_bit_0 { + /* First doubleword. */ +#define TD0_MSS_SHIFT 16 /* MSS position (11 bits) */ + TD0_TCP_CS = (1 << 16), /* Calculate TCP/IP checksum */ + TD0_UDP_CS = (1 << 17), /* Calculate UDP/IP checksum */ + TD0_IP_CS = (1 << 18), /* Calculate IP checksum */ +}; + +/* 8102e, 8168c and beyond. */ +enum rtl_tx_desc_bit_1 { + /* First doubleword. */ + TD1_GTSENV4 = (1 << 26), /* Giant Send for IPv4 */ + TD1_GTSENV6 = (1 << 25), /* Giant Send for IPv6 */ +#define GTTCPHO_SHIFT 18 +#define GTTCPHO_MAX 0x7f + + /* Second doubleword. */ +#define TCPHO_SHIFT 18 +#define TCPHO_MAX 0x3ff +#define TD1_MSS_SHIFT 18 /* MSS position (11 bits) */ + TD1_IPv6_CS = (1 << 28), /* Calculate IPv6 checksum */ + TD1_IPv4_CS = (1 << 29), /* Calculate IPv4 checksum */ + TD1_TCP_CS = (1 << 30), /* Calculate TCP/IP checksum */ + TD1_UDP_CS = (1 << 31), /* Calculate UDP/IP checksum */ +}; + +enum rtl_rx_desc_bit { + /* Rx private */ + PID1 = (1 << 18), /* Protocol ID bit 1/2 */ + PID0 = (1 << 17), /* Protocol ID bit 0/2 */ + +#define RxProtoUDP (PID1) +#define RxProtoTCP (PID0) +#define RxProtoIP (PID1 | PID0) +#define RxProtoMask RxProtoIP + + IPFail = (1 << 16), /* IP checksum failed */ + UDPFail = (1 << 15), /* UDP/IP checksum failed */ + TCPFail = (1 << 14), /* TCP/IP checksum failed */ + +#define RxCSFailMask (IPFail | UDPFail | TCPFail) + + RxVlanTag = (1 << 16), /* VLAN tag available */ +}; + +#define RTL_GSO_MAX_SIZE_V1 32000 +#define RTL_GSO_MAX_SEGS_V1 24 +#define RTL_GSO_MAX_SIZE_V2 64000 +#define RTL_GSO_MAX_SEGS_V2 64 + +struct TxDesc { + __le32 opts1; + __le32 opts2; + __le64 addr; +}; + +struct RxDesc { + __le32 opts1; + __le32 opts2; + __le64 addr; +}; + +struct ring_info { + struct sk_buff *skb; + u32 len; +}; + +struct rtl8169_counters { + __le64 tx_packets; + __le64 rx_packets; + __le64 tx_errors; + __le32 rx_errors; + __le16 rx_missed; + __le16 align_errors; + __le32 tx_one_collision; + __le32 tx_multi_collision; + __le64 rx_unicast; + __le64 rx_broadcast; + __le32 rx_multicast; + __le16 tx_aborted; + __le16 tx_underun; +}; + +struct rtl8169_tc_offsets { + bool inited; + __le64 tx_errors; + __le32 tx_multi_collision; + __le16 tx_aborted; + __le16 rx_missed; +}; + +enum rtl_flag { + RTL_FLAG_TASK_ENABLED = 0, + RTL_FLAG_TASK_RESET_PENDING, + RTL_FLAG_TASK_TX_TIMEOUT, + RTL_FLAG_MAX +}; + +enum rtl_dash_type { + RTL_DASH_NONE, + RTL_DASH_DP, + RTL_DASH_EP, +}; + +struct rtl8169_private { + void __iomem *mmio_addr; /* memory map physical address */ + struct pci_dev *pci_dev; + struct device *dev; + struct eth_device edev; + struct phy_device *phydev; + enum mac_version mac_version; + enum rtl_dash_type dash_type; + u32 cur_rx; /* Index into the Rx descriptor buffer of next Rx pkt. */ + u32 cur_tx; /* Index into the Tx descriptor buffer of next Rx pkt. */ + u32 dirty_tx; + struct TxDesc *TxDescArray; /* 256-aligned Tx descriptor ring */ + struct RxDesc *RxDescArray; /* 256-aligned Rx descriptor ring */ + dma_addr_t TxPhyAddr; + dma_addr_t RxPhyAddr; + u16 cp_cmd; + void *tx_buf; + dma_addr_t tx_buf_phys; + void *rx_buf; + dma_addr_t rx_buf_phys; + + unsigned supports_gmii:1; + unsigned aspm_manageable:1; + + const char *fw_name; + struct rtl_fw *rtl_fw; + + u32 ocp_base; + + struct mii_bus miibus; +}; + +typedef void (*rtl_generic_fct)(struct rtl8169_private *tp); + +static inline struct device *tp_to_dev(struct rtl8169_private *tp) +{ + return &tp->pci_dev->dev; +} + +static void rtl_lock_config_regs(struct rtl8169_private *tp) +{ + RTL_W8(tp, Cfg9346, Cfg9346_Lock); +} + +static void rtl_unlock_config_regs(struct rtl8169_private *tp) +{ + RTL_W8(tp, Cfg9346, Cfg9346_Unlock); +} + +static void rtl_pci_commit(struct rtl8169_private *tp) +{ + /* Read an arbitrary register to commit a preceding PCI write */ + RTL_R8(tp, ChipCmd); +} + +static bool rtl_is_8125(struct rtl8169_private *tp) +{ + return tp->mac_version >= RTL_GIGA_MAC_VER_61; +} + +static bool rtl_is_8168evl_up(struct rtl8169_private *tp) +{ + return tp->mac_version >= RTL_GIGA_MAC_VER_34 && + tp->mac_version != RTL_GIGA_MAC_VER_39 && + tp->mac_version <= RTL_GIGA_MAC_VER_53; +} + +static void rtl_read_mac_from_reg(struct rtl8169_private *tp, u8 *mac, int reg) +{ + int i; + + for (i = 0; i < ETH_ALEN; i++) + mac[i] = RTL_R8(tp, reg + i); +} + +struct rtl_cond { + bool (*check)(struct rtl8169_private *); + const char *msg; +}; + +static bool rtl_loop_wait(struct rtl8169_private *tp, const struct rtl_cond *c, + unsigned long usecs, int n, bool high) +{ + int i; + + for (i = 0; i < n; i++) { + if (c->check(tp) == high) + return true; + udelay(usecs); + } + + dev_err(tp->dev, "%s == %d (loop: %d, delay: %lu).\n", + c->msg, !high, n, usecs); + return false; +} + +static bool rtl_loop_wait_high(struct rtl8169_private *tp, + const struct rtl_cond *c, + unsigned long d, int n) +{ + return rtl_loop_wait(tp, c, d, n, true); +} + +static bool rtl_loop_wait_low(struct rtl8169_private *tp, + const struct rtl_cond *c, + unsigned long d, int n) +{ + return rtl_loop_wait(tp, c, d, n, false); +} + +#define DECLARE_RTL_COND(name) \ +static bool name ## _check(struct rtl8169_private *); \ + \ +static const struct rtl_cond name = { \ + .check = name ## _check, \ + .msg = #name \ +}; \ + \ +static bool name ## _check(struct rtl8169_private *tp) + +static void r8168fp_adjust_ocp_cmd(struct rtl8169_private *tp, u32 *cmd, int type) +{ + /* based on RTL8168FP_OOBMAC_BASE in vendor driver */ + if (type == ERIAR_OOB && + (tp->mac_version == RTL_GIGA_MAC_VER_52 || + tp->mac_version == RTL_GIGA_MAC_VER_53)) + *cmd |= 0xf70 << 18; +} + +DECLARE_RTL_COND(rtl_eriar_cond) +{ + return RTL_R32(tp, ERIAR) & ERIAR_FLAG; +} + +static void _rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask, + u32 val, int type) +{ + u32 cmd = ERIAR_WRITE_CMD | type | mask | addr; + + if (WARN(addr & 3 || !mask, "addr: 0x%x, mask: 0x%08x\n", addr, mask)) + return; + + RTL_W32(tp, ERIDR, val); + r8168fp_adjust_ocp_cmd(tp, &cmd, type); + RTL_W32(tp, ERIAR, cmd); + + rtl_loop_wait_low(tp, &rtl_eriar_cond, 100, 100); +} + +static void rtl_eri_write(struct rtl8169_private *tp, int addr, u32 mask, + u32 val) +{ + _rtl_eri_write(tp, addr, mask, val, ERIAR_EXGMAC); +} + +static u32 _rtl_eri_read(struct rtl8169_private *tp, int addr, int type) +{ + u32 cmd = ERIAR_READ_CMD | type | ERIAR_MASK_1111 | addr; + + r8168fp_adjust_ocp_cmd(tp, &cmd, type); + RTL_W32(tp, ERIAR, cmd); + + return rtl_loop_wait_high(tp, &rtl_eriar_cond, 100, 100) ? + RTL_R32(tp, ERIDR) : ~0; +} + +static u32 rtl_eri_read(struct rtl8169_private *tp, int addr) +{ + return _rtl_eri_read(tp, addr, ERIAR_EXGMAC); +} + +static void rtl_w0w1_eri(struct rtl8169_private *tp, int addr, u32 p, u32 m) +{ + u32 val = rtl_eri_read(tp, addr); + + rtl_eri_write(tp, addr, ERIAR_MASK_1111, (val & ~m) | p); +} + +static void rtl_eri_set_bits(struct rtl8169_private *tp, int addr, u32 p) +{ + rtl_w0w1_eri(tp, addr, p, 0); +} + +static void rtl_eri_clear_bits(struct rtl8169_private *tp, int addr, u32 m) +{ + rtl_w0w1_eri(tp, addr, 0, m); +} + +static bool rtl_ocp_reg_failure(u32 reg) +{ + return WARN_ONCE(reg & 0xffff0001, "Invalid ocp reg %x!\n", reg); +} + +DECLARE_RTL_COND(rtl_ocp_gphy_cond) +{ + return RTL_R32(tp, GPHY_OCP) & OCPAR_FLAG; +} + +static void r8168_phy_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) +{ + if (rtl_ocp_reg_failure(reg)) + return; + + RTL_W32(tp, GPHY_OCP, OCPAR_FLAG | (reg << 15) | data); + + rtl_loop_wait_low(tp, &rtl_ocp_gphy_cond, 25, 10); +} + +static int r8168_phy_ocp_read(struct rtl8169_private *tp, u32 reg) +{ + if (rtl_ocp_reg_failure(reg)) + return 0; + + RTL_W32(tp, GPHY_OCP, reg << 15); + + return rtl_loop_wait_high(tp, &rtl_ocp_gphy_cond, 25, 10) ? + (RTL_R32(tp, GPHY_OCP) & 0xffff) : -ETIMEDOUT; +} + +static void r8168_mac_ocp_write(struct rtl8169_private *tp, u32 reg, u32 data) +{ + if (rtl_ocp_reg_failure(reg)) + return; + + RTL_W32(tp, OCPDR, OCPAR_FLAG | (reg << 15) | data); +} + +static u16 r8168_mac_ocp_read(struct rtl8169_private *tp, u32 reg) +{ + if (rtl_ocp_reg_failure(reg)) + return 0; + + RTL_W32(tp, OCPDR, reg << 15); + + return RTL_R32(tp, OCPDR); +} + +static void r8168_mac_ocp_modify(struct rtl8169_private *tp, u32 reg, u16 mask, + u16 set) +{ + u16 data = r8168_mac_ocp_read(tp, reg); + + r8168_mac_ocp_write(tp, reg, (data & ~mask) | set); +} + +/* Work around a hw issue with RTL8168g PHY, the quirk disables + * PHY MCU interrupts before PHY power-down. + */ +static void rtl8168g_phy_suspend_quirk(struct rtl8169_private *tp, int value) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_40: + if (value & BMCR_RESET || !(value & BMCR_PDOWN)) + rtl_eri_set_bits(tp, 0x1a8, 0xfc000000); + else + rtl_eri_clear_bits(tp, 0x1a8, 0xfc000000); + break; + default: + break; + } +}; + +static void r8168g_mdio_write(struct rtl8169_private *tp, int reg, int value) +{ + if (reg == 0x1f) { + tp->ocp_base = value ? value << 4 : OCP_STD_PHY_BASE; + return; + } + + if (tp->ocp_base != OCP_STD_PHY_BASE) + reg -= 0x10; + + if (tp->ocp_base == OCP_STD_PHY_BASE && reg == MII_BMCR) + rtl8168g_phy_suspend_quirk(tp, value); + + r8168_phy_ocp_write(tp, tp->ocp_base + reg * 2, value); +} + +static int r8168g_mdio_read(struct rtl8169_private *tp, int reg) +{ + if (reg == 0x1f) + return tp->ocp_base == OCP_STD_PHY_BASE ? 0 : tp->ocp_base >> 4; + + if (tp->ocp_base != OCP_STD_PHY_BASE) + reg -= 0x10; + + return r8168_phy_ocp_read(tp, tp->ocp_base + reg * 2); +} + +static void mac_mcu_write(struct rtl8169_private *tp, int reg, int value) +{ + if (reg == 0x1f) { + tp->ocp_base = value << 4; + return; + } + + r8168_mac_ocp_write(tp, tp->ocp_base + reg, value); +} + +static int mac_mcu_read(struct rtl8169_private *tp, int reg) +{ + return r8168_mac_ocp_read(tp, tp->ocp_base + reg); +} + +DECLARE_RTL_COND(rtl_phyar_cond) +{ + return RTL_R32(tp, PHYAR) & 0x80000000; +} + +static void r8169_mdio_write(struct rtl8169_private *tp, int reg, int value) +{ + RTL_W32(tp, PHYAR, 0x80000000 | (reg & 0x1f) << 16 | (value & 0xffff)); + + rtl_loop_wait_low(tp, &rtl_phyar_cond, 25, 20); + /* + * According to hardware specs a 20us delay is required after write + * complete indication, but before sending next command. + */ + udelay(20); +} + +static int r8169_mdio_read(struct rtl8169_private *tp, int reg) +{ + int value; + + RTL_W32(tp, PHYAR, 0x0 | (reg & 0x1f) << 16); + + value = rtl_loop_wait_high(tp, &rtl_phyar_cond, 25, 20) ? + RTL_R32(tp, PHYAR) & 0xffff : -ETIMEDOUT; + + /* + * According to hardware specs a 20us delay is required after read + * complete indication, but before sending next command. + */ + udelay(20); + + return value; +} + +DECLARE_RTL_COND(rtl_ocpar_cond) +{ + return RTL_R32(tp, OCPAR) & OCPAR_FLAG; +} + +#define R8168DP_1_MDIO_ACCESS_BIT 0x00020000 + +static void r8168dp_2_mdio_start(struct rtl8169_private *tp) +{ + RTL_W32(tp, 0xd0, RTL_R32(tp, 0xd0) & ~R8168DP_1_MDIO_ACCESS_BIT); +} + +static void r8168dp_2_mdio_stop(struct rtl8169_private *tp) +{ + RTL_W32(tp, 0xd0, RTL_R32(tp, 0xd0) | R8168DP_1_MDIO_ACCESS_BIT); +} + +static void r8168dp_2_mdio_write(struct rtl8169_private *tp, int reg, int value) +{ + r8168dp_2_mdio_start(tp); + + r8169_mdio_write(tp, reg, value); + + r8168dp_2_mdio_stop(tp); +} + +static int r8168dp_2_mdio_read(struct rtl8169_private *tp, int reg) +{ + int value; + + /* Work around issue with chip reporting wrong PHY ID */ + if (reg == MII_PHYSID2) + return 0xc912; + + r8168dp_2_mdio_start(tp); + + value = r8169_mdio_read(tp, reg); + + r8168dp_2_mdio_stop(tp); + + return value; +} + +static void rtl_writephy(struct rtl8169_private *tp, int location, int val) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + r8168dp_2_mdio_write(tp, location, val); + break; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: + r8168g_mdio_write(tp, location, val); + break; + default: + r8169_mdio_write(tp, location, val); + break; + } +} + +static int rtl_readphy(struct rtl8169_private *tp, int location) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + return r8168dp_2_mdio_read(tp, location); + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: + return r8168g_mdio_read(tp, location); + default: + return r8169_mdio_read(tp, location); + } +} + +DECLARE_RTL_COND(rtl_ephyar_cond) +{ + return RTL_R32(tp, EPHYAR) & EPHYAR_FLAG; +} + +static void rtl_ephy_write(struct rtl8169_private *tp, int reg_addr, int value) +{ + RTL_W32(tp, EPHYAR, EPHYAR_WRITE_CMD | (value & EPHYAR_DATA_MASK) | + (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); + + rtl_loop_wait_low(tp, &rtl_ephyar_cond, 10, 100); + + udelay(10); +} + +static u16 rtl_ephy_read(struct rtl8169_private *tp, int reg_addr) +{ + RTL_W32(tp, EPHYAR, (reg_addr & EPHYAR_REG_MASK) << EPHYAR_REG_SHIFT); + + return rtl_loop_wait_high(tp, &rtl_ephyar_cond, 10, 100) ? + RTL_R32(tp, EPHYAR) & EPHYAR_DATA_MASK : ~0; +} + +static u32 r8168dp_ocp_read(struct rtl8169_private *tp, u16 reg) +{ + RTL_W32(tp, OCPAR, 0x0fu << 12 | (reg & 0x0fff)); + return rtl_loop_wait_high(tp, &rtl_ocpar_cond, 100, 20) ? + RTL_R32(tp, OCPDR) : ~0; +} + +static u32 r8168ep_ocp_read(struct rtl8169_private *tp, u16 reg) +{ + return _rtl_eri_read(tp, reg, ERIAR_OOB); +} + +static void r8168dp_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg, + u32 data) +{ + RTL_W32(tp, OCPDR, data); + RTL_W32(tp, OCPAR, OCPAR_FLAG | ((u32)mask & 0x0f) << 12 | (reg & 0x0fff)); + rtl_loop_wait_low(tp, &rtl_ocpar_cond, 100, 20); +} + +static void r8168ep_ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg, + u32 data) +{ + _rtl_eri_write(tp, reg, ((u32)mask & 0x0f) << ERIAR_MASK_SHIFT, + data, ERIAR_OOB); +} + +static void r8168dp_oob_notify(struct rtl8169_private *tp, u8 cmd) +{ + rtl_eri_write(tp, 0xe8, ERIAR_MASK_0001, cmd); + + r8168dp_ocp_write(tp, 0x1, 0x30, 0x00000001); +} + +#define OOB_CMD_RESET 0x00 +#define OOB_CMD_DRIVER_START 0x05 +#define OOB_CMD_DRIVER_STOP 0x06 + +static u16 rtl8168_get_ocp_reg(struct rtl8169_private *tp) +{ + return (tp->mac_version == RTL_GIGA_MAC_VER_31) ? 0xb8 : 0x10; +} + +DECLARE_RTL_COND(rtl_dp_ocp_read_cond) +{ + u16 reg; + + reg = rtl8168_get_ocp_reg(tp); + + return r8168dp_ocp_read(tp, reg) & 0x00000800; +} + +DECLARE_RTL_COND(rtl_ep_ocp_read_cond) +{ + return r8168ep_ocp_read(tp, 0x124) & 0x00000001; +} + +DECLARE_RTL_COND(rtl_ocp_tx_cond) +{ + return RTL_R8(tp, IBISR0) & 0x20; +} + +static void rtl8168ep_stop_cmac(struct rtl8169_private *tp) +{ + RTL_W8(tp, IBCR2, RTL_R8(tp, IBCR2) & ~0x01); + rtl_loop_wait_high(tp, &rtl_ocp_tx_cond, 50000, 2000); + RTL_W8(tp, IBISR0, RTL_R8(tp, IBISR0) | 0x20); + RTL_W8(tp, IBCR0, RTL_R8(tp, IBCR0) & ~0x01); +} + +static void rtl8168dp_driver_start(struct rtl8169_private *tp) +{ + r8168dp_oob_notify(tp, OOB_CMD_DRIVER_START); + rtl_loop_wait_high(tp, &rtl_dp_ocp_read_cond, 10000, 10); +} + +static void rtl8168ep_driver_start(struct rtl8169_private *tp) +{ + r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_START); + r8168ep_ocp_write(tp, 0x01, 0x30, r8168ep_ocp_read(tp, 0x30) | 0x01); + rtl_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 10); +} + +static void rtl8168_driver_start(struct rtl8169_private *tp) +{ + if (tp->dash_type == RTL_DASH_DP) + rtl8168dp_driver_start(tp); + else + rtl8168ep_driver_start(tp); +} + +static bool r8168dp_check_dash(struct rtl8169_private *tp) +{ + u16 reg = rtl8168_get_ocp_reg(tp); + + return r8168dp_ocp_read(tp, reg) & BIT(15); +} + +static bool r8168ep_check_dash(struct rtl8169_private *tp) +{ + return r8168ep_ocp_read(tp, 0x128) & BIT(0); +} + +static enum rtl_dash_type rtl_check_dash(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + return r8168dp_check_dash(tp) ? RTL_DASH_DP : RTL_DASH_NONE; + case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_53: + return r8168ep_check_dash(tp) ? RTL_DASH_EP : RTL_DASH_NONE; + default: + return RTL_DASH_NONE; + } +} + +static void rtl_set_d3_pll_down(struct rtl8169_private *tp, bool enable) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_25 ... RTL_GIGA_MAC_VER_26: + case RTL_GIGA_MAC_VER_29 ... RTL_GIGA_MAC_VER_30: + case RTL_GIGA_MAC_VER_32 ... RTL_GIGA_MAC_VER_37: + case RTL_GIGA_MAC_VER_39 ... RTL_GIGA_MAC_VER_63: + if (enable) + RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) & ~D3_NO_PLL_DOWN); + else + RTL_W8(tp, PMCH, RTL_R8(tp, PMCH) | D3_NO_PLL_DOWN); + break; + default: + break; + } +} + +static void rtl_reset_packet_filter(struct rtl8169_private *tp) +{ + rtl_eri_clear_bits(tp, 0xdc, BIT(0)); + rtl_eri_set_bits(tp, 0xdc, BIT(0)); +} + +DECLARE_RTL_COND(rtl_efusear_cond) +{ + return RTL_R32(tp, EFUSEAR) & EFUSEAR_FLAG; +} + +u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr) +{ + RTL_W32(tp, EFUSEAR, (reg_addr & EFUSEAR_REG_MASK) << EFUSEAR_REG_SHIFT); + + return rtl_loop_wait_high(tp, &rtl_efusear_cond, 100, 300) ? + RTL_R32(tp, EFUSEAR) & EFUSEAR_DATA_MASK : ~0; +} + +static void rtl_link_chg_patch(struct rtl8169_private *tp) +{ + struct phy_device *phydev = tp->phydev; + + if (tp->mac_version == RTL_GIGA_MAC_VER_34 || + tp->mac_version == RTL_GIGA_MAC_VER_38) { + if (phydev->speed == SPEED_1000) { + rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005); + } else if (phydev->speed == SPEED_100) { + rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005); + } else { + rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x0000003f); + } + rtl_reset_packet_filter(tp); + } else if (tp->mac_version == RTL_GIGA_MAC_VER_35 || + tp->mac_version == RTL_GIGA_MAC_VER_36) { + if (phydev->speed == SPEED_1000) { + rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x00000011); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x00000005); + } else { + rtl_eri_write(tp, 0x1bc, ERIAR_MASK_1111, 0x0000001f); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_1111, 0x0000003f); + } + } else if (tp->mac_version == RTL_GIGA_MAC_VER_37) { + if (phydev->speed == SPEED_10) { + rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x4d02); + rtl_eri_write(tp, 0x1dc, ERIAR_MASK_0011, 0x0060a); + } else { + rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x0000); + } + } +} + +static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii) +{ + /* + * The driver currently handles the 8168Bf and the 8168Be identically + * but they can be identified more specifically through the test below + * if needed: + * + * (RTL_R32(tp, TxConfig) & 0x700000) == 0x500000 ? 8168Bf : 8168Be + * + * Same thing for the 8101Eb and the 8101Ec: + * + * (RTL_R32(tp, TxConfig) & 0x700000) == 0x200000 ? 8101Eb : 8101Ec + */ + static const struct rtl_mac_info { + u16 mask; + u16 val; + enum mac_version ver; + } mac_info[] = { + /* 8125B family. */ + { 0x7cf, 0x641, RTL_GIGA_MAC_VER_63 }, + + /* 8125A family. */ + { 0x7cf, 0x609, RTL_GIGA_MAC_VER_61 }, + /* It seems only XID 609 made it to the mass market. + * { 0x7cf, 0x608, RTL_GIGA_MAC_VER_60 }, + * { 0x7c8, 0x608, RTL_GIGA_MAC_VER_61 }, + */ + + /* RTL8117 */ + { 0x7cf, 0x54b, RTL_GIGA_MAC_VER_53 }, + { 0x7cf, 0x54a, RTL_GIGA_MAC_VER_52 }, + + /* 8168EP family. */ + { 0x7cf, 0x502, RTL_GIGA_MAC_VER_51 }, + /* It seems this chip version never made it to + * the wild. Let's disable detection. + * { 0x7cf, 0x501, RTL_GIGA_MAC_VER_50 }, + * { 0x7cf, 0x500, RTL_GIGA_MAC_VER_49 }, + */ + + /* 8168H family. */ + { 0x7cf, 0x541, RTL_GIGA_MAC_VER_46 }, + /* It seems this chip version never made it to + * the wild. Let's disable detection. + * { 0x7cf, 0x540, RTL_GIGA_MAC_VER_45 }, + */ + + /* 8168G family. */ + { 0x7cf, 0x5c8, RTL_GIGA_MAC_VER_44 }, + { 0x7cf, 0x509, RTL_GIGA_MAC_VER_42 }, + /* It seems this chip version never made it to + * the wild. Let's disable detection. + * { 0x7cf, 0x4c1, RTL_GIGA_MAC_VER_41 }, + */ + { 0x7cf, 0x4c0, RTL_GIGA_MAC_VER_40 }, + + /* 8168F family. */ + { 0x7c8, 0x488, RTL_GIGA_MAC_VER_38 }, + { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36 }, + { 0x7cf, 0x480, RTL_GIGA_MAC_VER_35 }, + + /* 8168E family. */ + { 0x7c8, 0x2c8, RTL_GIGA_MAC_VER_34 }, + { 0x7cf, 0x2c1, RTL_GIGA_MAC_VER_32 }, + { 0x7c8, 0x2c0, RTL_GIGA_MAC_VER_33 }, + + /* 8168D family. */ + { 0x7cf, 0x281, RTL_GIGA_MAC_VER_25 }, + { 0x7c8, 0x280, RTL_GIGA_MAC_VER_26 }, + + /* 8168DP family. */ + /* It seems this early RTL8168dp version never made it to + * the wild. Support has been removed. + * { 0x7cf, 0x288, RTL_GIGA_MAC_VER_27 }, + */ + { 0x7cf, 0x28a, RTL_GIGA_MAC_VER_28 }, + { 0x7cf, 0x28b, RTL_GIGA_MAC_VER_31 }, + + /* 8168C family. */ + { 0x7cf, 0x3c9, RTL_GIGA_MAC_VER_23 }, + { 0x7cf, 0x3c8, RTL_GIGA_MAC_VER_18 }, + { 0x7c8, 0x3c8, RTL_GIGA_MAC_VER_24 }, + { 0x7cf, 0x3c0, RTL_GIGA_MAC_VER_19 }, + { 0x7cf, 0x3c2, RTL_GIGA_MAC_VER_20 }, + { 0x7cf, 0x3c3, RTL_GIGA_MAC_VER_21 }, + { 0x7c8, 0x3c0, RTL_GIGA_MAC_VER_22 }, + + /* 8168B family. */ + { 0x7c8, 0x380, RTL_GIGA_MAC_VER_17 }, + { 0x7c8, 0x300, RTL_GIGA_MAC_VER_11 }, + + /* 8101 family. */ + { 0x7c8, 0x448, RTL_GIGA_MAC_VER_39 }, + { 0x7c8, 0x440, RTL_GIGA_MAC_VER_37 }, + { 0x7cf, 0x409, RTL_GIGA_MAC_VER_29 }, + { 0x7c8, 0x408, RTL_GIGA_MAC_VER_30 }, + { 0x7cf, 0x349, RTL_GIGA_MAC_VER_08 }, + { 0x7cf, 0x249, RTL_GIGA_MAC_VER_08 }, + { 0x7cf, 0x348, RTL_GIGA_MAC_VER_07 }, + { 0x7cf, 0x248, RTL_GIGA_MAC_VER_07 }, + { 0x7cf, 0x240, RTL_GIGA_MAC_VER_14 }, + { 0x7c8, 0x348, RTL_GIGA_MAC_VER_09 }, + { 0x7c8, 0x248, RTL_GIGA_MAC_VER_09 }, + { 0x7c8, 0x340, RTL_GIGA_MAC_VER_10 }, + + /* 8110 family. */ + { 0xfc8, 0x980, RTL_GIGA_MAC_VER_06 }, + { 0xfc8, 0x180, RTL_GIGA_MAC_VER_05 }, + { 0xfc8, 0x100, RTL_GIGA_MAC_VER_04 }, + { 0xfc8, 0x040, RTL_GIGA_MAC_VER_03 }, + { 0xfc8, 0x008, RTL_GIGA_MAC_VER_02 }, + + /* Catch-all */ + { 0x000, 0x000, RTL_GIGA_MAC_NONE } + }; + const struct rtl_mac_info *p = mac_info; + enum mac_version ver; + + while ((xid & p->mask) != p->val) + p++; + ver = p->ver; + + if (ver != RTL_GIGA_MAC_NONE && !gmii) { + if (ver == RTL_GIGA_MAC_VER_42) + ver = RTL_GIGA_MAC_VER_43; + else if (ver == RTL_GIGA_MAC_VER_46) + ver = RTL_GIGA_MAC_VER_48; + } + + return ver; +} + +void r8169_apply_firmware(struct rtl8169_private *tp) +{ + int val; + u64 start; + + /* TODO: release firmware if rtl_fw_write_firmware signals failure. */ + if (tp->rtl_fw) { + rtl_fw_write_firmware(tp, tp->rtl_fw); + /* At least one firmware doesn't reset tp->ocp_base. */ + tp->ocp_base = OCP_STD_PHY_BASE; + + /* PHY soft reset may still be in progress */ + start = get_time_ns(); + while (is_timeout(start, SECOND)) { + val = phy_read(tp->phydev, MII_BMCR); + if (!(val & BMCR_RESET)) + return; + } + } +} + +static void rtl_rar_exgmac_set(struct rtl8169_private *tp, const u8 *addr) +{ + rtl_eri_write(tp, 0xe0, ERIAR_MASK_1111, get_unaligned_le32(addr)); + rtl_eri_write(tp, 0xe4, ERIAR_MASK_1111, get_unaligned_le16(addr + 4)); + rtl_eri_write(tp, 0xf0, ERIAR_MASK_1111, get_unaligned_le16(addr) << 16); + rtl_eri_write(tp, 0xf4, ERIAR_MASK_1111, get_unaligned_le32(addr + 2)); +} + +u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp) +{ + u16 data1, data2, ioffset; + + r8168_mac_ocp_write(tp, 0xdd02, 0x807d); + data1 = r8168_mac_ocp_read(tp, 0xdd02); + data2 = r8168_mac_ocp_read(tp, 0xdd00); + + ioffset = (data2 >> 1) & 0x7ff8; + ioffset |= data2 & 0x0007; + if (data1 & BIT(7)) + ioffset |= BIT(15); + + return ioffset; +} + +static void rtl8169_init_phy(struct rtl8169_private *tp) +{ + r8169_hw_phy_config(tp, tp->phydev, tp->mac_version); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) { + pci_write_config_byte(tp->pci_dev, PCI_LATENCY_TIMER, 0x40); + pci_write_config_byte(tp->pci_dev, PCI_CACHE_LINE_SIZE, 0x08); + /* set undocumented MAC Reg C+CR Offset 0x82h */ + RTL_W8(tp, 0x82, 0x01); + } +} + +static void rtl_rar_set(struct rtl8169_private *tp, const u8 *addr) +{ + rtl_unlock_config_regs(tp); + + RTL_W32(tp, MAC4, get_unaligned_le16(addr + 4)); + rtl_pci_commit(tp); + + RTL_W32(tp, MAC0, get_unaligned_le32(addr)); + rtl_pci_commit(tp); + + if (tp->mac_version == RTL_GIGA_MAC_VER_34) + rtl_rar_exgmac_set(tp, addr); + + rtl_lock_config_regs(tp); +} + +static void rtl_init_rxcfg(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_02 ... RTL_GIGA_MAC_VER_06: + case RTL_GIGA_MAC_VER_10 ... RTL_GIGA_MAC_VER_17: + RTL_W32(tp, RxConfig, RX_FIFO_THRESH | RX_DMA_BURST); + break; + case RTL_GIGA_MAC_VER_18 ... RTL_GIGA_MAC_VER_24: + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_36: + case RTL_GIGA_MAC_VER_38: + RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST); + break; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_53: + RTL_W32(tp, RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST | RX_EARLY_OFF); + break; + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: + RTL_W32(tp, RxConfig, RX_FETCH_DFLT_8125 | RX_DMA_BURST); + break; + default: + RTL_W32(tp, RxConfig, RX128_INT_EN | RX_DMA_BURST); + break; + } +} + +DECLARE_RTL_COND(rtl_chipcmd_cond) +{ + return RTL_R8(tp, ChipCmd) & CmdReset; +} + +static void rtl_hw_reset(struct rtl8169_private *tp) +{ + RTL_W8(tp, ChipCmd, CmdReset); + + rtl_loop_wait_low(tp, &rtl_chipcmd_cond, 100, 100); +} + +static void rtl_request_firmware(struct rtl8169_private *tp) +{ + struct rtl_fw *rtl_fw; + + /* firmware loaded already or no firmware available */ + if (tp->rtl_fw || !tp->fw_name) + return; + + rtl_fw = kzalloc(sizeof(*rtl_fw), GFP_KERNEL); + if (!rtl_fw) + return; + + rtl_fw->phy_write = rtl_writephy; + rtl_fw->phy_read = rtl_readphy; + rtl_fw->mac_mcu_write = mac_mcu_write; + rtl_fw->mac_mcu_read = mac_mcu_read; + rtl_fw->fw_name = tp->fw_name; + rtl_fw->dev = tp_to_dev(tp); + + if (rtl_fw_request_firmware(rtl_fw)) + kfree(rtl_fw); + else + tp->rtl_fw = rtl_fw; +} + +static void rtl_rx_close(struct rtl8169_private *tp) +{ + RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) & ~RX_CONFIG_ACCEPT_MASK); +} + +DECLARE_RTL_COND(rtl_npq_cond) +{ + return RTL_R8(tp, TxPoll) & NPQ; +} + +DECLARE_RTL_COND(rtl_txcfg_empty_cond) +{ + return RTL_R32(tp, TxConfig) & TXCFG_EMPTY; +} + +DECLARE_RTL_COND(rtl_rxtx_empty_cond) +{ + return (RTL_R8(tp, MCU) & RXTX_EMPTY) == RXTX_EMPTY; +} + +DECLARE_RTL_COND(rtl_rxtx_empty_cond_2) +{ + /* IntrMitigate has new functionality on RTL8125 */ + return (RTL_R16(tp, IntrMitigate) & 0x0103) == 0x0103; +} + +static void rtl_wait_txrx_fifo_empty(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_53: + rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 42); + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); + break; + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_61: + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); + break; + case RTL_GIGA_MAC_VER_63: + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond, 100, 42); + rtl_loop_wait_high(tp, &rtl_rxtx_empty_cond_2, 100, 42); + break; + default: + break; + } +} + +static void rtl_disable_rxdvgate(struct rtl8169_private *tp) +{ + RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~RXDV_GATED_EN); +} + +static void rtl_enable_rxdvgate(struct rtl8169_private *tp) +{ + RTL_W32(tp, MISC, RTL_R32(tp, MISC) | RXDV_GATED_EN); + udelay(2000); + rtl_wait_txrx_fifo_empty(tp); +} + +static void rtl_set_tx_config_registers(struct rtl8169_private *tp) +{ + u32 val = TX_DMA_BURST << TxDMAShift | + InterFrameGap << TxInterFrameGapShift; + + if (rtl_is_8168evl_up(tp)) + val |= TXCFG_AUTO_FIFO; + + RTL_W32(tp, TxConfig, val); +} + +static void rtl_set_rx_max_size(struct rtl8169_private *tp) +{ + /* Low hurts. Let's disable the filtering. */ + RTL_W16(tp, RxMaxSize, R8169_RX_BUF_SIZE + 1); +} + +static void rtl_set_rx_tx_desc_registers(struct rtl8169_private *tp) +{ + /* + * Magic spell: some iop3xx ARM board needs the TxDescAddrHigh + * register to be written before TxDescAddrLow to work. + * Switching from MMIO to I/O access fixes the issue as well. + */ + RTL_W32(tp, TxDescStartAddrHigh, ((u64) tp->TxPhyAddr) >> 32); + RTL_W32(tp, TxDescStartAddrLow, ((u64) tp->TxPhyAddr) & DMA_BIT_MASK(32)); + RTL_W32(tp, RxDescAddrHigh, ((u64) tp->RxPhyAddr) >> 32); + RTL_W32(tp, RxDescAddrLow, ((u64) tp->RxPhyAddr) & DMA_BIT_MASK(32)); +} + +static void rtl8169_set_magic_reg(struct rtl8169_private *tp) +{ + u32 val; + + if (tp->mac_version == RTL_GIGA_MAC_VER_05) + val = 0x000fff00; + else if (tp->mac_version == RTL_GIGA_MAC_VER_06) + val = 0x00ffff00; + else + return; + + if (RTL_R8(tp, Config2) & PCI_Clock_66MHz) + val |= 0xff; + + RTL_W32(tp, 0x7c, val); +} + +static void rtl_set_rx_mode(struct rtl8169_private *tp) +{ + u32 rx_mode = AcceptBroadcast | AcceptMyPhys | AcceptMulticast; + /* Multicast hash filter */ + u32 mc_filter[2] = { 0xffffffff, 0xffffffff }; + u32 tmp; + + RTL_W32(tp, MAR0 + 4, mc_filter[1]); + RTL_W32(tp, MAR0 + 0, mc_filter[0]); + + tmp = RTL_R32(tp, RxConfig); + RTL_W32(tp, RxConfig, (tmp & ~RX_CONFIG_ACCEPT_OK_MASK) | rx_mode); +} + +DECLARE_RTL_COND(rtl_csiar_cond) +{ + return RTL_R32(tp, CSIAR) & CSIAR_FLAG; +} + +static void rtl_csi_write(struct rtl8169_private *tp, int addr, int value) +{ + u32 func = PCI_FUNC(tp->pci_dev->devfn); + + RTL_W32(tp, CSIDR, value); + RTL_W32(tp, CSIAR, CSIAR_WRITE_CMD | (addr & CSIAR_ADDR_MASK) | + CSIAR_BYTE_ENABLE | func << 16); + + rtl_loop_wait_low(tp, &rtl_csiar_cond, 10, 100); +} + +static u32 rtl_csi_read(struct rtl8169_private *tp, int addr) +{ + u32 func = PCI_FUNC(tp->pci_dev->devfn); + + RTL_W32(tp, CSIAR, (addr & CSIAR_ADDR_MASK) | func << 16 | + CSIAR_BYTE_ENABLE); + + return rtl_loop_wait_high(tp, &rtl_csiar_cond, 10, 100) ? + RTL_R32(tp, CSIDR) : ~0; +} + +static void rtl_set_aspm_entry_latency(struct rtl8169_private *tp, u8 val) +{ + struct pci_dev *pdev = tp->pci_dev; + u32 csi; + + /* According to Realtek the value at config space address 0x070f + * controls the L0s/L1 entrance latency. We try standard ECAM access + * first and if it fails fall back to CSI. + * bit 0..2: L0: 0 = 1us, 1 = 2us .. 6 = 7us, 7 = 7us (no typo) + * bit 3..5: L1: 0 = 1us, 1 = 2us .. 6 = 64us, 7 = 64us + */ + if (pci_write_config_byte(pdev, 0x070f, val) == PCIBIOS_SUCCESSFUL) + return; + + dev_dbg(tp->dev, + "No native access to PCI extended config space, falling back to CSI\n"); + csi = rtl_csi_read(tp, 0x070c) & 0x00ffffff; + rtl_csi_write(tp, 0x070c, csi | val << 24); +} + +static void rtl_set_def_aspm_entry_latency(struct rtl8169_private *tp) +{ + /* L0 7us, L1 16us */ + rtl_set_aspm_entry_latency(tp, 0x27); +} + +struct ephy_info { + unsigned int offset; + u16 mask; + u16 bits; +}; + +static void __rtl_ephy_init(struct rtl8169_private *tp, + const struct ephy_info *e, int len) +{ + u16 w; + + while (len-- > 0) { + w = (rtl_ephy_read(tp, e->offset) & ~e->mask) | e->bits; + rtl_ephy_write(tp, e->offset, w); + e++; + } +} + +#define rtl_ephy_init(tp, a) __rtl_ephy_init(tp, a, ARRAY_SIZE(a)) + +static void rtl_disable_clock_request(struct rtl8169_private *tp) +{ + /* Not yet implemented */ +} + +static void rtl_enable_clock_request(struct rtl8169_private *tp) +{ + /* Not yet implemented */ +} + +static void rtl_pcie_state_l2l3_disable(struct rtl8169_private *tp) +{ + /* work around an issue when PCI reset occurs during L2/L3 state */ + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Rdy_to_L23); +} + +static void rtl_enable_exit_l1(struct rtl8169_private *tp) +{ + /* Bits control which events trigger ASPM L1 exit: + * Bit 12: rxdv + * Bit 11: ltr_msg + * Bit 10: txdma_poll + * Bit 9: xadm + * Bit 8: pktavi + * Bit 7: txpla + */ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_36: + rtl_eri_set_bits(tp, 0xd4, 0x1f00); + break; + case RTL_GIGA_MAC_VER_37 ... RTL_GIGA_MAC_VER_38: + rtl_eri_set_bits(tp, 0xd4, 0x0c00); + break; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: + r8168_mac_ocp_modify(tp, 0xc0ac, 0, 0x1f80); + break; + default: + break; + } +} + +static void rtl_disable_exit_l1(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38: + rtl_eri_clear_bits(tp, 0xd4, 0x1f00); + break; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: + r8168_mac_ocp_modify(tp, 0xc0ac, 0x1f80, 0); + break; + default: + break; + } +} + +static void rtl_hw_aspm_clkreq_enable(struct rtl8169_private *tp, bool enable) +{ + /* Don't enable ASPM in the chip if OS can't control ASPM */ + if (enable && tp->aspm_manageable) { + RTL_W8(tp, Config5, RTL_R8(tp, Config5) | ASPM_en); + RTL_W8(tp, Config2, RTL_R8(tp, Config2) | ClkReqEn); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: + /* reset ephy tx/rx disable timer */ + r8168_mac_ocp_modify(tp, 0xe094, 0xff00, 0); + /* chip can trigger L1.2 */ + r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, BIT(2)); + break; + default: + break; + } + } else { + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_46 ... RTL_GIGA_MAC_VER_48: + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: + r8168_mac_ocp_modify(tp, 0xe092, 0x00ff, 0); + break; + default: + break; + } + + RTL_W8(tp, Config2, RTL_R8(tp, Config2) & ~ClkReqEn); + RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~ASPM_en); + } + + udelay(10); +} + +static void rtl_set_fifo_size(struct rtl8169_private *tp, u16 rx_stat, + u16 tx_stat, u16 rx_dyn, u16 tx_dyn) +{ + /* Usage of dynamic vs. static FIFO is controlled by bit + * TXCFG_AUTO_FIFO. Exact meaning of FIFO values isn't known. + */ + rtl_eri_write(tp, 0xc8, ERIAR_MASK_1111, (rx_stat << 16) | rx_dyn); + rtl_eri_write(tp, 0xe8, ERIAR_MASK_1111, (tx_stat << 16) | tx_dyn); +} + +static void rtl8168g_set_pause_thresholds(struct rtl8169_private *tp, + u8 low, u8 high) +{ + /* FIFO thresholds for pause flow control */ + rtl_eri_write(tp, 0xcc, ERIAR_MASK_0001, low); + rtl_eri_write(tp, 0xd0, ERIAR_MASK_0001, high); +} + +static void rtl_hw_start_8168b(struct rtl8169_private *tp) +{ + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); +} + +static void __rtl_hw_start_8168cp(struct rtl8169_private *tp) +{ + RTL_W8(tp, Config1, RTL_R8(tp, Config1) | Speed_down); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + rtl_disable_clock_request(tp); +} + +static void rtl_hw_start_8168cp_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168cp[] = { + { 0x01, 0, 0x0001 }, + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0042 }, + { 0x06, 0x0080, 0x0000 }, + { 0x07, 0, 0x2000 } + }; + + rtl_set_def_aspm_entry_latency(tp); + + rtl_ephy_init(tp, e_info_8168cp); + + __rtl_hw_start_8168cp(tp); +} + +static void rtl_hw_start_8168cp_2(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); +} + +static void rtl_hw_start_8168cp_3(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + /* Magic. */ + RTL_W8(tp, DBG_REG, 0x20); +} + +static void rtl_hw_start_8168c_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168c_1[] = { + { 0x02, 0x0800, 0x1000 }, + { 0x03, 0, 0x0002 }, + { 0x06, 0x0080, 0x0000 } + }; + + rtl_set_def_aspm_entry_latency(tp); + + RTL_W8(tp, DBG_REG, 0x06 | FIX_NAK_1 | FIX_NAK_2); + + rtl_ephy_init(tp, e_info_8168c_1); + + __rtl_hw_start_8168cp(tp); +} + +static void rtl_hw_start_8168c_2(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168c_2[] = { + { 0x01, 0, 0x0001 }, + { 0x03, 0x0400, 0x0020 } + }; + + rtl_set_def_aspm_entry_latency(tp); + + rtl_ephy_init(tp, e_info_8168c_2); + + __rtl_hw_start_8168cp(tp); +} + +static void rtl_hw_start_8168c_4(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + __rtl_hw_start_8168cp(tp); +} + +static void rtl_hw_start_8168d(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + rtl_disable_clock_request(tp); +} + +static void rtl_hw_start_8168d_4(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168d_4[] = { + { 0x0b, 0x0000, 0x0048 }, + { 0x19, 0x0020, 0x0050 }, + { 0x0c, 0x0100, 0x0020 }, + { 0x10, 0x0004, 0x0000 }, + }; + + rtl_set_def_aspm_entry_latency(tp); + + rtl_ephy_init(tp, e_info_8168d_4); + + rtl_enable_clock_request(tp); +} + +static void rtl_hw_start_8168e_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168e_1[] = { + { 0x00, 0x0200, 0x0100 }, + { 0x00, 0x0000, 0x0004 }, + { 0x06, 0x0002, 0x0001 }, + { 0x06, 0x0000, 0x0030 }, + { 0x07, 0x0000, 0x2000 }, + { 0x00, 0x0000, 0x0020 }, + { 0x03, 0x5800, 0x2000 }, + { 0x03, 0x0000, 0x0001 }, + { 0x01, 0x0800, 0x1000 }, + { 0x07, 0x0000, 0x4000 }, + { 0x1e, 0x0000, 0x2000 }, + { 0x19, 0xffff, 0xfe6c }, + { 0x0a, 0x0000, 0x0040 } + }; + + rtl_set_def_aspm_entry_latency(tp); + + rtl_ephy_init(tp, e_info_8168e_1); + + rtl_disable_clock_request(tp); + + /* Reset tx FIFO pointer */ + RTL_W32(tp, MISC, RTL_R32(tp, MISC) | TXPLA_RST); + RTL_W32(tp, MISC, RTL_R32(tp, MISC) & ~TXPLA_RST); + + RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en); +} + +static void rtl_hw_start_8168e_2(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168e_2[] = { + { 0x09, 0x0000, 0x0080 }, + { 0x19, 0x0000, 0x0224 }, + { 0x00, 0x0000, 0x0004 }, + { 0x0c, 0x3df0, 0x0200 }, + }; + + rtl_set_def_aspm_entry_latency(tp); + + rtl_ephy_init(tp, e_info_8168e_2); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_1111, 0x0000); + rtl_set_fifo_size(tp, 0x10, 0x10, 0x02, 0x06); + rtl_eri_set_bits(tp, 0x1d0, BIT(1)); + rtl_reset_packet_filter(tp); + rtl_eri_set_bits(tp, 0x1b0, BIT(4)); + rtl_eri_write(tp, 0xcc, ERIAR_MASK_1111, 0x00000050); + rtl_eri_write(tp, 0xd0, ERIAR_MASK_1111, 0x07ff0060); + + rtl_disable_clock_request(tp); + + RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN); + RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN); + RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en); + + rtl_hw_aspm_clkreq_enable(tp, true); +} + +static void rtl_hw_start_8168f(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_1111, 0x0000); + rtl_set_fifo_size(tp, 0x10, 0x10, 0x02, 0x06); + rtl_reset_packet_filter(tp); + rtl_eri_set_bits(tp, 0x1b0, BIT(4)); + rtl_eri_set_bits(tp, 0x1d0, BIT(4) | BIT(1)); + rtl_eri_write(tp, 0xcc, ERIAR_MASK_1111, 0x00000050); + rtl_eri_write(tp, 0xd0, ERIAR_MASK_1111, 0x00000060); + + rtl_disable_clock_request(tp); + + RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN); + RTL_W32(tp, MISC, RTL_R32(tp, MISC) | PWM_EN); + RTL_W8(tp, Config5, RTL_R8(tp, Config5) & ~Spi_en); +} + +static void rtl_hw_start_8168f_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168f_1[] = { + { 0x06, 0x00c0, 0x0020 }, + { 0x08, 0x0001, 0x0002 }, + { 0x09, 0x0000, 0x0080 }, + { 0x19, 0x0000, 0x0224 }, + { 0x00, 0x0000, 0x0008 }, + { 0x0c, 0x3df0, 0x0200 }, + }; + + rtl_hw_start_8168f(tp); + + rtl_ephy_init(tp, e_info_8168f_1); +} + +static void rtl_hw_start_8411(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168f_1[] = { + { 0x06, 0x00c0, 0x0020 }, + { 0x0f, 0xffff, 0x5200 }, + { 0x19, 0x0000, 0x0224 }, + { 0x00, 0x0000, 0x0008 }, + { 0x0c, 0x3df0, 0x0200 }, + }; + + rtl_hw_start_8168f(tp); + rtl_pcie_state_l2l3_disable(tp); + + rtl_ephy_init(tp, e_info_8168f_1); +} + +static void rtl_hw_start_8168g(struct rtl8169_private *tp) +{ + rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06); + rtl8168g_set_pause_thresholds(tp, 0x38, 0x48); + + rtl_set_def_aspm_entry_latency(tp); + + rtl_reset_packet_filter(tp); + rtl_eri_write(tp, 0x2f8, ERIAR_MASK_0011, 0x1d8f); + + rtl_disable_rxdvgate(tp); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); + + rtl_w0w1_eri(tp, 0x2fc, 0x01, 0x06); + rtl_eri_clear_bits(tp, 0x1b0, BIT(12)); + + rtl_pcie_state_l2l3_disable(tp); +} + +static void rtl_hw_start_8168g_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168g_1[] = { + { 0x00, 0x0008, 0x0000 }, + { 0x0c, 0x3ff0, 0x0820 }, + { 0x1e, 0x0000, 0x0001 }, + { 0x19, 0x8000, 0x0000 } + }; + + rtl_hw_start_8168g(tp); + + /* disable aspm and clock request before access ephy */ + rtl_hw_aspm_clkreq_enable(tp, false); + rtl_ephy_init(tp, e_info_8168g_1); + rtl_hw_aspm_clkreq_enable(tp, true); +} + +static void rtl_hw_start_8168g_2(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168g_2[] = { + { 0x00, 0x0008, 0x0000 }, + { 0x0c, 0x3ff0, 0x0820 }, + { 0x19, 0xffff, 0x7c00 }, + { 0x1e, 0xffff, 0x20eb }, + { 0x0d, 0xffff, 0x1666 }, + { 0x00, 0xffff, 0x10a3 }, + { 0x06, 0xffff, 0xf050 }, + { 0x04, 0x0000, 0x0010 }, + { 0x1d, 0x4000, 0x0000 }, + }; + + rtl_hw_start_8168g(tp); + + /* disable aspm and clock request before access ephy */ + rtl_hw_aspm_clkreq_enable(tp, false); + rtl_ephy_init(tp, e_info_8168g_2); +} + +static void rtl_hw_start_8411_2(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8411_2[] = { + { 0x00, 0x0008, 0x0000 }, + { 0x0c, 0x37d0, 0x0820 }, + { 0x1e, 0x0000, 0x0001 }, + { 0x19, 0x8021, 0x0000 }, + { 0x1e, 0x0000, 0x2000 }, + { 0x0d, 0x0100, 0x0200 }, + { 0x00, 0x0000, 0x0080 }, + { 0x06, 0x0000, 0x0010 }, + { 0x04, 0x0000, 0x0010 }, + { 0x1d, 0x0000, 0x4000 }, + }; + + rtl_hw_start_8168g(tp); + + /* disable aspm and clock request before access ephy */ + rtl_hw_aspm_clkreq_enable(tp, false); + rtl_ephy_init(tp, e_info_8411_2); + + /* The following Realtek-provided magic fixes an issue with the RX unit + * getting confused after the PHY having been powered-down. + */ + r8168_mac_ocp_write(tp, 0xFC28, 0x0000); + r8168_mac_ocp_write(tp, 0xFC2A, 0x0000); + r8168_mac_ocp_write(tp, 0xFC2C, 0x0000); + r8168_mac_ocp_write(tp, 0xFC2E, 0x0000); + r8168_mac_ocp_write(tp, 0xFC30, 0x0000); + r8168_mac_ocp_write(tp, 0xFC32, 0x0000); + r8168_mac_ocp_write(tp, 0xFC34, 0x0000); + r8168_mac_ocp_write(tp, 0xFC36, 0x0000); + mdelay(3); + r8168_mac_ocp_write(tp, 0xFC26, 0x0000); + + r8168_mac_ocp_write(tp, 0xF800, 0xE008); + r8168_mac_ocp_write(tp, 0xF802, 0xE00A); + r8168_mac_ocp_write(tp, 0xF804, 0xE00C); + r8168_mac_ocp_write(tp, 0xF806, 0xE00E); + r8168_mac_ocp_write(tp, 0xF808, 0xE027); + r8168_mac_ocp_write(tp, 0xF80A, 0xE04F); + r8168_mac_ocp_write(tp, 0xF80C, 0xE05E); + r8168_mac_ocp_write(tp, 0xF80E, 0xE065); + r8168_mac_ocp_write(tp, 0xF810, 0xC602); + r8168_mac_ocp_write(tp, 0xF812, 0xBE00); + r8168_mac_ocp_write(tp, 0xF814, 0x0000); + r8168_mac_ocp_write(tp, 0xF816, 0xC502); + r8168_mac_ocp_write(tp, 0xF818, 0xBD00); + r8168_mac_ocp_write(tp, 0xF81A, 0x074C); + r8168_mac_ocp_write(tp, 0xF81C, 0xC302); + r8168_mac_ocp_write(tp, 0xF81E, 0xBB00); + r8168_mac_ocp_write(tp, 0xF820, 0x080A); + r8168_mac_ocp_write(tp, 0xF822, 0x6420); + r8168_mac_ocp_write(tp, 0xF824, 0x48C2); + r8168_mac_ocp_write(tp, 0xF826, 0x8C20); + r8168_mac_ocp_write(tp, 0xF828, 0xC516); + r8168_mac_ocp_write(tp, 0xF82A, 0x64A4); + r8168_mac_ocp_write(tp, 0xF82C, 0x49C0); + r8168_mac_ocp_write(tp, 0xF82E, 0xF009); + r8168_mac_ocp_write(tp, 0xF830, 0x74A2); + r8168_mac_ocp_write(tp, 0xF832, 0x8CA5); + r8168_mac_ocp_write(tp, 0xF834, 0x74A0); + r8168_mac_ocp_write(tp, 0xF836, 0xC50E); + r8168_mac_ocp_write(tp, 0xF838, 0x9CA2); + r8168_mac_ocp_write(tp, 0xF83A, 0x1C11); + r8168_mac_ocp_write(tp, 0xF83C, 0x9CA0); + r8168_mac_ocp_write(tp, 0xF83E, 0xE006); + r8168_mac_ocp_write(tp, 0xF840, 0x74F8); + r8168_mac_ocp_write(tp, 0xF842, 0x48C4); + r8168_mac_ocp_write(tp, 0xF844, 0x8CF8); + r8168_mac_ocp_write(tp, 0xF846, 0xC404); + r8168_mac_ocp_write(tp, 0xF848, 0xBC00); + r8168_mac_ocp_write(tp, 0xF84A, 0xC403); + r8168_mac_ocp_write(tp, 0xF84C, 0xBC00); + r8168_mac_ocp_write(tp, 0xF84E, 0x0BF2); + r8168_mac_ocp_write(tp, 0xF850, 0x0C0A); + r8168_mac_ocp_write(tp, 0xF852, 0xE434); + r8168_mac_ocp_write(tp, 0xF854, 0xD3C0); + r8168_mac_ocp_write(tp, 0xF856, 0x49D9); + r8168_mac_ocp_write(tp, 0xF858, 0xF01F); + r8168_mac_ocp_write(tp, 0xF85A, 0xC526); + r8168_mac_ocp_write(tp, 0xF85C, 0x64A5); + r8168_mac_ocp_write(tp, 0xF85E, 0x1400); + r8168_mac_ocp_write(tp, 0xF860, 0xF007); + r8168_mac_ocp_write(tp, 0xF862, 0x0C01); + r8168_mac_ocp_write(tp, 0xF864, 0x8CA5); + r8168_mac_ocp_write(tp, 0xF866, 0x1C15); + r8168_mac_ocp_write(tp, 0xF868, 0xC51B); + r8168_mac_ocp_write(tp, 0xF86A, 0x9CA0); + r8168_mac_ocp_write(tp, 0xF86C, 0xE013); + r8168_mac_ocp_write(tp, 0xF86E, 0xC519); + r8168_mac_ocp_write(tp, 0xF870, 0x74A0); + r8168_mac_ocp_write(tp, 0xF872, 0x48C4); + r8168_mac_ocp_write(tp, 0xF874, 0x8CA0); + r8168_mac_ocp_write(tp, 0xF876, 0xC516); + r8168_mac_ocp_write(tp, 0xF878, 0x74A4); + r8168_mac_ocp_write(tp, 0xF87A, 0x48C8); + r8168_mac_ocp_write(tp, 0xF87C, 0x48CA); + r8168_mac_ocp_write(tp, 0xF87E, 0x9CA4); + r8168_mac_ocp_write(tp, 0xF880, 0xC512); + r8168_mac_ocp_write(tp, 0xF882, 0x1B00); + r8168_mac_ocp_write(tp, 0xF884, 0x9BA0); + r8168_mac_ocp_write(tp, 0xF886, 0x1B1C); + r8168_mac_ocp_write(tp, 0xF888, 0x483F); + r8168_mac_ocp_write(tp, 0xF88A, 0x9BA2); + r8168_mac_ocp_write(tp, 0xF88C, 0x1B04); + r8168_mac_ocp_write(tp, 0xF88E, 0xC508); + r8168_mac_ocp_write(tp, 0xF890, 0x9BA0); + r8168_mac_ocp_write(tp, 0xF892, 0xC505); + r8168_mac_ocp_write(tp, 0xF894, 0xBD00); + r8168_mac_ocp_write(tp, 0xF896, 0xC502); + r8168_mac_ocp_write(tp, 0xF898, 0xBD00); + r8168_mac_ocp_write(tp, 0xF89A, 0x0300); + r8168_mac_ocp_write(tp, 0xF89C, 0x051E); + r8168_mac_ocp_write(tp, 0xF89E, 0xE434); + r8168_mac_ocp_write(tp, 0xF8A0, 0xE018); + r8168_mac_ocp_write(tp, 0xF8A2, 0xE092); + r8168_mac_ocp_write(tp, 0xF8A4, 0xDE20); + r8168_mac_ocp_write(tp, 0xF8A6, 0xD3C0); + r8168_mac_ocp_write(tp, 0xF8A8, 0xC50F); + r8168_mac_ocp_write(tp, 0xF8AA, 0x76A4); + r8168_mac_ocp_write(tp, 0xF8AC, 0x49E3); + r8168_mac_ocp_write(tp, 0xF8AE, 0xF007); + r8168_mac_ocp_write(tp, 0xF8B0, 0x49C0); + r8168_mac_ocp_write(tp, 0xF8B2, 0xF103); + r8168_mac_ocp_write(tp, 0xF8B4, 0xC607); + r8168_mac_ocp_write(tp, 0xF8B6, 0xBE00); + r8168_mac_ocp_write(tp, 0xF8B8, 0xC606); + r8168_mac_ocp_write(tp, 0xF8BA, 0xBE00); + r8168_mac_ocp_write(tp, 0xF8BC, 0xC602); + r8168_mac_ocp_write(tp, 0xF8BE, 0xBE00); + r8168_mac_ocp_write(tp, 0xF8C0, 0x0C4C); + r8168_mac_ocp_write(tp, 0xF8C2, 0x0C28); + r8168_mac_ocp_write(tp, 0xF8C4, 0x0C2C); + r8168_mac_ocp_write(tp, 0xF8C6, 0xDC00); + r8168_mac_ocp_write(tp, 0xF8C8, 0xC707); + r8168_mac_ocp_write(tp, 0xF8CA, 0x1D00); + r8168_mac_ocp_write(tp, 0xF8CC, 0x8DE2); + r8168_mac_ocp_write(tp, 0xF8CE, 0x48C1); + r8168_mac_ocp_write(tp, 0xF8D0, 0xC502); + r8168_mac_ocp_write(tp, 0xF8D2, 0xBD00); + r8168_mac_ocp_write(tp, 0xF8D4, 0x00AA); + r8168_mac_ocp_write(tp, 0xF8D6, 0xE0C0); + r8168_mac_ocp_write(tp, 0xF8D8, 0xC502); + r8168_mac_ocp_write(tp, 0xF8DA, 0xBD00); + r8168_mac_ocp_write(tp, 0xF8DC, 0x0132); + + r8168_mac_ocp_write(tp, 0xFC26, 0x8000); + + r8168_mac_ocp_write(tp, 0xFC2A, 0x0743); + r8168_mac_ocp_write(tp, 0xFC2C, 0x0801); + r8168_mac_ocp_write(tp, 0xFC2E, 0x0BE9); + r8168_mac_ocp_write(tp, 0xFC30, 0x02FD); + r8168_mac_ocp_write(tp, 0xFC32, 0x0C25); + r8168_mac_ocp_write(tp, 0xFC34, 0x00A9); + r8168_mac_ocp_write(tp, 0xFC36, 0x012D); + + rtl_hw_aspm_clkreq_enable(tp, true); +} + +static void rtl_hw_start_8168h_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168h_1[] = { + { 0x1e, 0x0800, 0x0001 }, + { 0x1d, 0x0000, 0x0800 }, + { 0x05, 0xffff, 0x2089 }, + { 0x06, 0xffff, 0x5881 }, + { 0x04, 0xffff, 0x854a }, + { 0x01, 0xffff, 0x068b } + }; + int rg_saw_cnt; + + /* disable aspm and clock request before access ephy */ + rtl_hw_aspm_clkreq_enable(tp, false); + rtl_ephy_init(tp, e_info_8168h_1); + + rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06); + rtl8168g_set_pause_thresholds(tp, 0x38, 0x48); + + rtl_set_def_aspm_entry_latency(tp); + + rtl_reset_packet_filter(tp); + + rtl_eri_set_bits(tp, 0xdc, 0x001c); + + rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87); + + rtl_disable_rxdvgate(tp); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN); + RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN); + + rtl_eri_clear_bits(tp, 0x1b0, BIT(12)); + + rtl_pcie_state_l2l3_disable(tp); + + rg_saw_cnt = phy_read_paged(tp->phydev, 0x0c42, 0x13) & 0x3fff; + if (rg_saw_cnt > 0) { + u16 sw_cnt_1ms_ini; + + sw_cnt_1ms_ini = 16000000/rg_saw_cnt; + sw_cnt_1ms_ini &= 0x0fff; + r8168_mac_ocp_modify(tp, 0xd412, 0x0fff, sw_cnt_1ms_ini); + } + + r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0070); + r8168_mac_ocp_modify(tp, 0xe052, 0x6000, 0x8008); + r8168_mac_ocp_modify(tp, 0xe0d6, 0x01ff, 0x017f); + r8168_mac_ocp_modify(tp, 0xd420, 0x0fff, 0x047f); + + r8168_mac_ocp_write(tp, 0xe63e, 0x0001); + r8168_mac_ocp_write(tp, 0xe63e, 0x0000); + r8168_mac_ocp_write(tp, 0xc094, 0x0000); + r8168_mac_ocp_write(tp, 0xc09e, 0x0000); + + rtl_hw_aspm_clkreq_enable(tp, true); +} + +static void rtl_hw_start_8168ep(struct rtl8169_private *tp) +{ + rtl8168ep_stop_cmac(tp); + + rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06); + rtl8168g_set_pause_thresholds(tp, 0x2f, 0x5f); + + rtl_set_def_aspm_entry_latency(tp); + + rtl_reset_packet_filter(tp); + + rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87); + + rtl_disable_rxdvgate(tp); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); + + rtl_w0w1_eri(tp, 0x2fc, 0x01, 0x06); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN); + + rtl_pcie_state_l2l3_disable(tp); +} + +static void rtl_hw_start_8168ep_3(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8168ep_3[] = { + { 0x00, 0x0000, 0x0080 }, + { 0x0d, 0x0100, 0x0200 }, + { 0x19, 0x8021, 0x0000 }, + { 0x1e, 0x0000, 0x2000 }, + }; + + /* disable aspm and clock request before access ephy */ + rtl_hw_aspm_clkreq_enable(tp, false); + rtl_ephy_init(tp, e_info_8168ep_3); + + rtl_hw_start_8168ep(tp); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN); + RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN); + + r8168_mac_ocp_modify(tp, 0xd3e2, 0x0fff, 0x0271); + r8168_mac_ocp_modify(tp, 0xd3e4, 0x00ff, 0x0000); + r8168_mac_ocp_modify(tp, 0xe860, 0x0000, 0x0080); + + rtl_hw_aspm_clkreq_enable(tp, true); +} + +static void rtl_hw_start_8117(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8117[] = { + { 0x19, 0x0040, 0x1100 }, + { 0x59, 0x0040, 0x1100 }, + }; + int rg_saw_cnt; + + rtl8168ep_stop_cmac(tp); + + /* disable aspm and clock request before access ephy */ + rtl_hw_aspm_clkreq_enable(tp, false); + rtl_ephy_init(tp, e_info_8117); + + rtl_set_fifo_size(tp, 0x08, 0x10, 0x02, 0x06); + rtl8168g_set_pause_thresholds(tp, 0x2f, 0x5f); + + rtl_set_def_aspm_entry_latency(tp); + + rtl_reset_packet_filter(tp); + + rtl_eri_set_bits(tp, 0xd4, 0x0010); + + rtl_eri_write(tp, 0x5f0, ERIAR_MASK_0011, 0x4f87); + + rtl_disable_rxdvgate(tp); + + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN); + RTL_W8(tp, MISC_1, RTL_R8(tp, MISC_1) & ~PFM_D3COLD_EN); + + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~TX_10M_PS_EN); + + rtl_eri_clear_bits(tp, 0x1b0, BIT(12)); + + rtl_pcie_state_l2l3_disable(tp); + + rg_saw_cnt = phy_read_paged(tp->phydev, 0x0c42, 0x13) & 0x3fff; + if (rg_saw_cnt > 0) { + u16 sw_cnt_1ms_ini; + + sw_cnt_1ms_ini = (16000000 / rg_saw_cnt) & 0x0fff; + r8168_mac_ocp_modify(tp, 0xd412, 0x0fff, sw_cnt_1ms_ini); + } + + r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0070); + r8168_mac_ocp_write(tp, 0xea80, 0x0003); + r8168_mac_ocp_modify(tp, 0xe052, 0x0000, 0x0009); + r8168_mac_ocp_modify(tp, 0xd420, 0x0fff, 0x047f); + + r8168_mac_ocp_write(tp, 0xe63e, 0x0001); + r8168_mac_ocp_write(tp, 0xe63e, 0x0000); + r8168_mac_ocp_write(tp, 0xc094, 0x0000); + r8168_mac_ocp_write(tp, 0xc09e, 0x0000); + + /* firmware is for MAC only */ + r8169_apply_firmware(tp); + + rtl_hw_aspm_clkreq_enable(tp, true); +} + +static void rtl_hw_start_8102e_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8102e_1[] = { + { 0x01, 0, 0x6e65 }, + { 0x02, 0, 0x091f }, + { 0x03, 0, 0xc2f9 }, + { 0x06, 0, 0xafb5 }, + { 0x07, 0, 0x0e00 }, + { 0x19, 0, 0xec80 }, + { 0x01, 0, 0x2e65 }, + { 0x01, 0, 0x6e65 } + }; + u8 cfg1; + + rtl_set_def_aspm_entry_latency(tp); + + RTL_W8(tp, DBG_REG, FIX_NAK_1); + + RTL_W8(tp, Config1, + LEDS1 | LEDS0 | Speed_down | MEMMAP | IOMAP | VPD | PMEnable); + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); + + cfg1 = RTL_R8(tp, Config1); + if ((cfg1 & LEDS0) && (cfg1 & LEDS1)) + RTL_W8(tp, Config1, cfg1 & ~LEDS0); + + rtl_ephy_init(tp, e_info_8102e_1); +} + +static void rtl_hw_start_8102e_2(struct rtl8169_private *tp) +{ + rtl_set_def_aspm_entry_latency(tp); + + RTL_W8(tp, Config1, MEMMAP | IOMAP | VPD | PMEnable); + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); +} + +static void rtl_hw_start_8102e_3(struct rtl8169_private *tp) +{ + rtl_hw_start_8102e_2(tp); + + rtl_ephy_write(tp, 0x03, 0xc2f9); +} + +static void rtl_hw_start_8401(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8401[] = { + { 0x01, 0xffff, 0x6fe5 }, + { 0x03, 0xffff, 0x0599 }, + { 0x06, 0xffff, 0xaf25 }, + { 0x07, 0xffff, 0x8e68 }, + }; + + rtl_ephy_init(tp, e_info_8401); + RTL_W8(tp, Config3, RTL_R8(tp, Config3) & ~Beacon_en); +} + +static void rtl_hw_start_8105e_1(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8105e_1[] = { + { 0x07, 0, 0x4000 }, + { 0x19, 0, 0x0200 }, + { 0x19, 0, 0x0020 }, + { 0x1e, 0, 0x2000 }, + { 0x03, 0, 0x0001 }, + { 0x19, 0, 0x0100 }, + { 0x19, 0, 0x0004 }, + { 0x0a, 0, 0x0020 } + }; + + /* Force LAN exit from ASPM if Rx/Tx are not idle */ + RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800); + + /* Disable Early Tally Counter */ + RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) & ~0x010000); + + RTL_W8(tp, MCU, RTL_R8(tp, MCU) | EN_NDP | EN_OOB_RESET); + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) | PFM_EN); + + rtl_ephy_init(tp, e_info_8105e_1); + + rtl_pcie_state_l2l3_disable(tp); +} + +static void rtl_hw_start_8105e_2(struct rtl8169_private *tp) +{ + rtl_hw_start_8105e_1(tp); + rtl_ephy_write(tp, 0x1e, rtl_ephy_read(tp, 0x1e) | 0x8000); +} + +static void rtl_hw_start_8402(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8402[] = { + { 0x19, 0xffff, 0xff64 }, + { 0x1e, 0, 0x4000 } + }; + + rtl_set_def_aspm_entry_latency(tp); + + /* Force LAN exit from ASPM if Rx/Tx are not idle */ + RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800); + + RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); + + rtl_ephy_init(tp, e_info_8402); + + rtl_set_fifo_size(tp, 0x00, 0x00, 0x02, 0x06); + rtl_reset_packet_filter(tp); + rtl_eri_write(tp, 0xc0, ERIAR_MASK_0011, 0x0000); + rtl_eri_write(tp, 0xb8, ERIAR_MASK_0011, 0x0000); + rtl_w0w1_eri(tp, 0x0d4, 0x0e00, 0xff00); + + /* disable EEE */ + rtl_eri_write(tp, 0x1b0, ERIAR_MASK_0011, 0x0000); + + rtl_pcie_state_l2l3_disable(tp); +} + +static void rtl_hw_start_8106(struct rtl8169_private *tp) +{ + rtl_hw_aspm_clkreq_enable(tp, false); + + /* Force LAN exit from ASPM if Rx/Tx are not idle */ + RTL_W32(tp, FuncEvent, RTL_R32(tp, FuncEvent) | 0x002800); + + RTL_W32(tp, MISC, (RTL_R32(tp, MISC) | DISABLE_LAN_EN) & ~EARLY_TALLY_EN); + RTL_W8(tp, MCU, RTL_R8(tp, MCU) | EN_NDP | EN_OOB_RESET); + RTL_W8(tp, DLLPR, RTL_R8(tp, DLLPR) & ~PFM_EN); + + /* L0 7us, L1 32us - needed to avoid issues with link-up detection */ + rtl_set_aspm_entry_latency(tp, 0x2f); + + rtl_eri_write(tp, 0x1d0, ERIAR_MASK_0011, 0x0000); + + /* disable EEE */ + rtl_eri_write(tp, 0x1b0, ERIAR_MASK_0011, 0x0000); + + rtl_pcie_state_l2l3_disable(tp); + rtl_hw_aspm_clkreq_enable(tp, true); +} + +DECLARE_RTL_COND(rtl_mac_ocp_e00e_cond) +{ + return r8168_mac_ocp_read(tp, 0xe00e) & BIT(13); +} + +static void rtl_hw_start_8125_common(struct rtl8169_private *tp) +{ + rtl_pcie_state_l2l3_disable(tp); + + RTL_W16(tp, 0x382, 0x221b); + RTL_W8(tp, 0x4500, 0); + RTL_W16(tp, 0x4800, 0); + + /* disable UPS */ + r8168_mac_ocp_modify(tp, 0xd40a, 0x0010, 0x0000); + + RTL_W8(tp, Config1, RTL_R8(tp, Config1) & ~0x10); + + r8168_mac_ocp_write(tp, 0xc140, 0xffff); + r8168_mac_ocp_write(tp, 0xc142, 0xffff); + + r8168_mac_ocp_modify(tp, 0xd3e2, 0x0fff, 0x03a9); + r8168_mac_ocp_modify(tp, 0xd3e4, 0x00ff, 0x0000); + r8168_mac_ocp_modify(tp, 0xe860, 0x0000, 0x0080); + + /* disable new tx descriptor format */ + r8168_mac_ocp_modify(tp, 0xeb58, 0x0001, 0x0000); + + if (tp->mac_version == RTL_GIGA_MAC_VER_63) + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0200); + else + r8168_mac_ocp_modify(tp, 0xe614, 0x0700, 0x0400); + + if (tp->mac_version == RTL_GIGA_MAC_VER_63) + r8168_mac_ocp_modify(tp, 0xe63e, 0x0c30, 0x0000); + else + r8168_mac_ocp_modify(tp, 0xe63e, 0x0c30, 0x0020); + + r8168_mac_ocp_modify(tp, 0xc0b4, 0x0000, 0x000c); + r8168_mac_ocp_modify(tp, 0xeb6a, 0x00ff, 0x0033); + r8168_mac_ocp_modify(tp, 0xeb50, 0x03e0, 0x0040); + r8168_mac_ocp_modify(tp, 0xe056, 0x00f0, 0x0030); + r8168_mac_ocp_modify(tp, 0xe040, 0x1000, 0x0000); + r8168_mac_ocp_modify(tp, 0xea1c, 0x0003, 0x0001); + r8168_mac_ocp_modify(tp, 0xe0c0, 0x4f0f, 0x4403); + r8168_mac_ocp_modify(tp, 0xe052, 0x0080, 0x0068); + r8168_mac_ocp_modify(tp, 0xd430, 0x0fff, 0x047f); + + r8168_mac_ocp_modify(tp, 0xea1c, 0x0004, 0x0000); + r8168_mac_ocp_modify(tp, 0xeb54, 0x0000, 0x0001); + udelay(1); + r8168_mac_ocp_modify(tp, 0xeb54, 0x0001, 0x0000); + RTL_W16(tp, 0x1880, RTL_R16(tp, 0x1880) & ~0x0030); + + r8168_mac_ocp_write(tp, 0xe098, 0xc302); + + rtl_loop_wait_low(tp, &rtl_mac_ocp_e00e_cond, 1000, 10); + + rtl_disable_rxdvgate(tp); +} + +static void rtl_hw_start_8125a_2(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8125a_2[] = { + { 0x04, 0xffff, 0xd000 }, + { 0x0a, 0xffff, 0x8653 }, + { 0x23, 0xffff, 0xab66 }, + { 0x20, 0xffff, 0x9455 }, + { 0x21, 0xffff, 0x99ff }, + { 0x29, 0xffff, 0xfe04 }, + + { 0x44, 0xffff, 0xd000 }, + { 0x4a, 0xffff, 0x8653 }, + { 0x63, 0xffff, 0xab66 }, + { 0x60, 0xffff, 0x9455 }, + { 0x61, 0xffff, 0x99ff }, + { 0x69, 0xffff, 0xfe04 }, + }; + + rtl_set_def_aspm_entry_latency(tp); + + /* disable aspm and clock request before access ephy */ + rtl_hw_aspm_clkreq_enable(tp, false); + rtl_ephy_init(tp, e_info_8125a_2); + + rtl_hw_start_8125_common(tp); + rtl_hw_aspm_clkreq_enable(tp, true); +} + +static void rtl_hw_start_8125b(struct rtl8169_private *tp) +{ + static const struct ephy_info e_info_8125b[] = { + { 0x0b, 0xffff, 0xa908 }, + { 0x1e, 0xffff, 0x20eb }, + { 0x4b, 0xffff, 0xa908 }, + { 0x5e, 0xffff, 0x20eb }, + { 0x22, 0x0030, 0x0020 }, + { 0x62, 0x0030, 0x0020 }, + }; + + rtl_set_def_aspm_entry_latency(tp); + rtl_hw_aspm_clkreq_enable(tp, false); + + rtl_ephy_init(tp, e_info_8125b); + rtl_hw_start_8125_common(tp); + + rtl_hw_aspm_clkreq_enable(tp, true); +} + +static void rtl_hw_config(struct rtl8169_private *tp) +{ + static const rtl_generic_fct hw_configs[] = { + [RTL_GIGA_MAC_VER_07] = rtl_hw_start_8102e_1, + [RTL_GIGA_MAC_VER_08] = rtl_hw_start_8102e_3, + [RTL_GIGA_MAC_VER_09] = rtl_hw_start_8102e_2, + [RTL_GIGA_MAC_VER_10] = NULL, + [RTL_GIGA_MAC_VER_11] = rtl_hw_start_8168b, + [RTL_GIGA_MAC_VER_14] = rtl_hw_start_8401, + [RTL_GIGA_MAC_VER_17] = rtl_hw_start_8168b, + [RTL_GIGA_MAC_VER_18] = rtl_hw_start_8168cp_1, + [RTL_GIGA_MAC_VER_19] = rtl_hw_start_8168c_1, + [RTL_GIGA_MAC_VER_20] = rtl_hw_start_8168c_2, + [RTL_GIGA_MAC_VER_21] = rtl_hw_start_8168c_2, + [RTL_GIGA_MAC_VER_22] = rtl_hw_start_8168c_4, + [RTL_GIGA_MAC_VER_23] = rtl_hw_start_8168cp_2, + [RTL_GIGA_MAC_VER_24] = rtl_hw_start_8168cp_3, + [RTL_GIGA_MAC_VER_25] = rtl_hw_start_8168d, + [RTL_GIGA_MAC_VER_26] = rtl_hw_start_8168d, + [RTL_GIGA_MAC_VER_28] = rtl_hw_start_8168d_4, + [RTL_GIGA_MAC_VER_29] = rtl_hw_start_8105e_1, + [RTL_GIGA_MAC_VER_30] = rtl_hw_start_8105e_2, + [RTL_GIGA_MAC_VER_31] = rtl_hw_start_8168d, + [RTL_GIGA_MAC_VER_32] = rtl_hw_start_8168e_1, + [RTL_GIGA_MAC_VER_33] = rtl_hw_start_8168e_1, + [RTL_GIGA_MAC_VER_34] = rtl_hw_start_8168e_2, + [RTL_GIGA_MAC_VER_35] = rtl_hw_start_8168f_1, + [RTL_GIGA_MAC_VER_36] = rtl_hw_start_8168f_1, + [RTL_GIGA_MAC_VER_37] = rtl_hw_start_8402, + [RTL_GIGA_MAC_VER_38] = rtl_hw_start_8411, + [RTL_GIGA_MAC_VER_39] = rtl_hw_start_8106, + [RTL_GIGA_MAC_VER_40] = rtl_hw_start_8168g_1, + [RTL_GIGA_MAC_VER_42] = rtl_hw_start_8168g_2, + [RTL_GIGA_MAC_VER_43] = rtl_hw_start_8168g_2, + [RTL_GIGA_MAC_VER_44] = rtl_hw_start_8411_2, + [RTL_GIGA_MAC_VER_46] = rtl_hw_start_8168h_1, + [RTL_GIGA_MAC_VER_48] = rtl_hw_start_8168h_1, + [RTL_GIGA_MAC_VER_51] = rtl_hw_start_8168ep_3, + [RTL_GIGA_MAC_VER_52] = rtl_hw_start_8117, + [RTL_GIGA_MAC_VER_53] = rtl_hw_start_8117, + [RTL_GIGA_MAC_VER_61] = rtl_hw_start_8125a_2, + [RTL_GIGA_MAC_VER_63] = rtl_hw_start_8125b, + }; + + if (hw_configs[tp->mac_version]) + hw_configs[tp->mac_version](tp); +} + +static void rtl_hw_start_8125(struct rtl8169_private *tp) +{ + int i; + + /* disable interrupt coalescing */ + for (i = 0xa00; i < 0xb00; i += 4) + RTL_W32(tp, i, 0); + + rtl_hw_config(tp); +} + +static void rtl_hw_start_8168(struct rtl8169_private *tp) +{ + if (rtl_is_8168evl_up(tp)) + RTL_W8(tp, MaxTxPacketSize, EarlySize); + else + RTL_W8(tp, MaxTxPacketSize, TxPacketMax); + + rtl_hw_config(tp); + + /* disable interrupt coalescing */ + RTL_W16(tp, IntrMitigate, 0x0000); +} + +static void rtl_hw_start_8169(struct rtl8169_private *tp) +{ + RTL_W8(tp, EarlyTxThres, NoEarlyTx); + + tp->cp_cmd |= PCIMulRW; + + if (tp->mac_version == RTL_GIGA_MAC_VER_02 || + tp->mac_version == RTL_GIGA_MAC_VER_03) + tp->cp_cmd |= EnAnaPLL; + + RTL_W16(tp, CPlusCmd, tp->cp_cmd); + + rtl8169_set_magic_reg(tp); + + /* disable interrupt coalescing */ + RTL_W16(tp, IntrMitigate, 0x0000); +} + +static void rtl_hw_start(struct rtl8169_private *tp) +{ + rtl_unlock_config_regs(tp); + + RTL_W16(tp, CPlusCmd, tp->cp_cmd); + + if (tp->mac_version <= RTL_GIGA_MAC_VER_06) + rtl_hw_start_8169(tp); + else if (rtl_is_8125(tp)) + rtl_hw_start_8125(tp); + else + rtl_hw_start_8168(tp); + + rtl_enable_exit_l1(tp); + rtl_set_rx_max_size(tp); + rtl_set_rx_tx_desc_registers(tp); + rtl_lock_config_regs(tp); + + /* Initially a 10 us delay. Turned it into a PCI commit. - FR */ + rtl_pci_commit(tp); + + RTL_W8(tp, ChipCmd, CmdTxEnb | CmdRxEnb); + rtl_init_rxcfg(tp); + rtl_set_tx_config_registers(tp); + rtl_set_rx_mode(tp); +} + +static void rtl8169_cleanup(struct rtl8169_private *tp) +{ + rtl_rx_close(tp); + + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_28: + case RTL_GIGA_MAC_VER_31: + rtl_loop_wait_low(tp, &rtl_npq_cond, 20, 2000); + break; + case RTL_GIGA_MAC_VER_34 ... RTL_GIGA_MAC_VER_38: + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); + rtl_loop_wait_high(tp, &rtl_txcfg_empty_cond, 100, 666); + break; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_63: + rtl_enable_rxdvgate(tp); + udelay(2000); + break; + default: + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) | StopReq); + udelay(100); + break; + } + + rtl_hw_reset(tp); +} + +static void rtl_read_mac_address(struct rtl8169_private *tp, + u8 mac_addr[ETH_ALEN]) +{ + /* Get MAC address */ + if (rtl_is_8168evl_up(tp) && tp->mac_version != RTL_GIGA_MAC_VER_34) { + u32 value; + + value = rtl_eri_read(tp, 0xe0); + put_unaligned_le32(value, mac_addr); + value = rtl_eri_read(tp, 0xe4); + put_unaligned_le16(value, mac_addr + 4); + } else if (rtl_is_8125(tp)) { + rtl_read_mac_from_reg(tp, mac_addr, MAC0_BKP); + } +} + +DECLARE_RTL_COND(rtl_link_list_ready_cond) +{ + return RTL_R8(tp, MCU) & LINK_LIST_RDY; +} + +static void r8168g_wait_ll_share_fifo_ready(struct rtl8169_private *tp) +{ + rtl_loop_wait_high(tp, &rtl_link_list_ready_cond, 100, 42); +} + +static void rtl_hw_init_8168g(struct rtl8169_private *tp) +{ + rtl_enable_rxdvgate(tp); + + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) & ~(CmdTxEnb | CmdRxEnb)); + mdelay(1); + RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); + + r8168_mac_ocp_modify(tp, 0xe8de, BIT(14), 0); + r8168g_wait_ll_share_fifo_ready(tp); + + r8168_mac_ocp_modify(tp, 0xe8de, 0, BIT(15)); + r8168g_wait_ll_share_fifo_ready(tp); +} + +static void rtl_hw_init_8125(struct rtl8169_private *tp) +{ + rtl_enable_rxdvgate(tp); + + RTL_W8(tp, ChipCmd, RTL_R8(tp, ChipCmd) & ~(CmdTxEnb | CmdRxEnb)); + mdelay(1); + RTL_W8(tp, MCU, RTL_R8(tp, MCU) & ~NOW_IS_OOB); + + r8168_mac_ocp_modify(tp, 0xe8de, BIT(14), 0); + r8168g_wait_ll_share_fifo_ready(tp); + + r8168_mac_ocp_write(tp, 0xc0aa, 0x07d0); + r8168_mac_ocp_write(tp, 0xc0a6, 0x0150); + r8168_mac_ocp_write(tp, 0xc01e, 0x5555); + r8168g_wait_ll_share_fifo_ready(tp); +} + +static void rtl_hw_initialize(struct rtl8169_private *tp) +{ + switch (tp->mac_version) { + case RTL_GIGA_MAC_VER_51 ... RTL_GIGA_MAC_VER_53: + rtl8168ep_stop_cmac(tp); + fallthrough; + case RTL_GIGA_MAC_VER_40 ... RTL_GIGA_MAC_VER_48: + rtl_hw_init_8168g(tp); + break; + case RTL_GIGA_MAC_VER_61 ... RTL_GIGA_MAC_VER_63: + rtl_hw_init_8125(tp); + break; + default: + break; + } +} + +static int rtl8169_init_dev(struct eth_device *edev) +{ + struct rtl8169_private *tp = edev->priv; + int ret; + + rtl_request_firmware(tp); + + pci_set_master(tp->pci_dev); + + tp->phydev = get_phy_device(&tp->miibus, 0); + if (IS_ERR(tp->phydev)) + return PTR_ERR(tp->phydev); + + ret = phy_register_device(tp->phydev); + if (ret) + return ret; + + rtl8169_init_phy(tp); + + return 0; +} + +#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ +#define PKT_BUF_SIZE 1536 + +static void rtl8169_init_ring(struct rtl8169_private *tp) +{ + struct eth_device *edev = &tp->edev; + int i; + + tp->cur_rx = tp->cur_tx = 0; + + tp->TxDescArray = dma_alloc_coherent(NUM_TX_DESC * sizeof(struct TxDesc), + &tp->TxPhyAddr); + tp->tx_buf = dma_alloc(NUM_TX_DESC * PKT_BUF_SIZE); + tp->tx_buf_phys = dma_map_single(edev->parent, tp->tx_buf, + NUM_TX_DESC * PKT_BUF_SIZE, DMA_TO_DEVICE); + + tp->RxDescArray = dma_alloc_coherent(NUM_RX_DESC * sizeof(struct RxDesc), + &tp->RxPhyAddr); + tp->rx_buf = dma_alloc(NUM_RX_DESC * PKT_BUF_SIZE); + tp->rx_buf_phys = dma_map_single(edev->parent, tp->rx_buf, + NUM_RX_DESC * PKT_BUF_SIZE, DMA_FROM_DEVICE); + + for (i = 0; i < NUM_RX_DESC; i++) { + if (i == (NUM_RX_DESC - 1)) + tp->RxDescArray[i].opts1 = + cpu_to_le32(DescOwn | RingEnd | PKT_BUF_SIZE); + else + tp->RxDescArray[i].opts1 = + cpu_to_le32(DescOwn | PKT_BUF_SIZE); + + tp->RxDescArray[i].addr = + cpu_to_le64(tp->rx_buf_phys + i * PKT_BUF_SIZE); + } +} + +static void r8169_phylink_handler(struct eth_device *edev) +{ + struct rtl8169_private *tp = edev->priv; + + rtl_link_chg_patch(tp); +} + +static int rtl8169_eth_open(struct eth_device *edev) +{ + struct rtl8169_private *tp = edev->priv; + int ret; + + pci_set_master(tp->pci_dev); + + rtl8169_init_ring(tp); + rtl_hw_start(tp); + + ret = phy_device_connect(edev, &tp->miibus, 0, r8169_phylink_handler, 0, + PHY_INTERFACE_MODE_NA); + + return ret; +} + +static int rtl8169_phy_write(struct mii_bus *bus, int phyaddr, int phyreg, u16 val) +{ + struct rtl8169_private *tp = bus->priv; + + if (phyaddr > 0) + return -ENODEV; + + rtl_writephy(tp, phyreg, val); + + return 0; +} + +static int rtl8169_phy_read(struct mii_bus *bus, int phyaddr, int phyreg) +{ + struct rtl8169_private *tp = bus->priv; + + if (phyaddr > 0) + return -ENODEV; + + return rtl_readphy(tp, phyreg); +} + +static void rtl8169_doorbell(struct rtl8169_private *tp) +{ + if (rtl_is_8125(tp)) + RTL_W16(tp, TxPoll_8125, BIT(0)); + else + RTL_W8(tp, TxPoll, NPQ); +} + +static int rtl8169_eth_send(struct eth_device *edev, void *packet, + int packet_length) +{ + struct rtl8169_private *tp = edev->priv; + struct device *dev = &tp->pci_dev->dev; + unsigned int entry; + u64 start; + int ret = 0; + + entry = tp->cur_tx % NUM_TX_DESC; + + if (packet_length < ETH_ZLEN) + memset(tp->tx_buf + entry * PKT_BUF_SIZE, 0, ETH_ZLEN); + memcpy(tp->tx_buf + entry * PKT_BUF_SIZE, packet, packet_length); + dma_sync_single_for_device(dev, tp->tx_buf_phys + entry * + PKT_BUF_SIZE, PKT_BUF_SIZE, DMA_TO_DEVICE); + + tp->TxDescArray[entry].addr = cpu_to_le64(tp->tx_buf_phys + entry * PKT_BUF_SIZE); + + if (entry != (NUM_TX_DESC - 1)) { + tp->TxDescArray[entry].opts1 = + cpu_to_le32(DescOwn | FirstFrag | LastFrag | + ((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN)); + } else { + tp->TxDescArray[entry].opts1 = + cpu_to_le32(DescOwn | RingEnd | FirstFrag | LastFrag | + ((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN)); + } + + rtl8169_doorbell(tp); + + start = get_time_ns(); + + while (le32_to_cpu(tp->TxDescArray[entry].opts1) & DescOwn) { + if (is_timeout(start, 100 * MSECOND)) { + ret = -ETIMEDOUT; + break; + } + } + + dma_sync_single_for_cpu(dev, tp->tx_buf_phys + entry * PKT_BUF_SIZE, + PKT_BUF_SIZE, DMA_TO_DEVICE); + + tp->cur_tx++; + + return ret; +} + +static int rtl8169_eth_rx(struct eth_device *edev) +{ + struct rtl8169_private *tp = edev->priv; + struct device *dev = &tp->pci_dev->dev; + unsigned int entry, pkt_size = 0; + u8 status; + + entry = tp->cur_rx % NUM_RX_DESC; + + if ((le32_to_cpu(tp->RxDescArray[entry].opts1) & DescOwn) == 0) { + if (!(le32_to_cpu(tp->RxDescArray[entry].opts1) & RxRES)) { + pkt_size = (le32_to_cpu(tp->RxDescArray[entry].opts1) & 0x1fff) - 4; + + dma_sync_single_for_cpu(dev, tp->rx_buf_phys + entry * PKT_BUF_SIZE, + pkt_size, DMA_FROM_DEVICE); + + net_receive(edev, tp->rx_buf + entry * PKT_BUF_SIZE, + pkt_size); + + dma_sync_single_for_device(dev, tp->rx_buf_phys + entry * PKT_BUF_SIZE, + pkt_size, DMA_FROM_DEVICE); + + if (entry == NUM_RX_DESC - 1) + tp->RxDescArray[entry].opts1 = cpu_to_le32(DescOwn | + RingEnd | PKT_BUF_SIZE); + else + tp->RxDescArray[entry].opts1 = + cpu_to_le32(DescOwn | PKT_BUF_SIZE); + tp->RxDescArray[entry].addr = cpu_to_le64(tp->rx_buf_phys + + entry * PKT_BUF_SIZE); + } else { + dev_err(&edev->dev, "rx error\n"); + } + + tp->cur_rx++; + + return pkt_size; + + } else { + status = RTL_R8(tp, IntrStatus); + RTL_W8(tp, IntrStatus, status & ~(TxErr | RxErr | SYSErr)); + udelay(100); /* wait */ + } + + return 0; +} + +static int rtl8169_get_ethaddr(struct eth_device *edev, unsigned char *mac_addr) +{ + struct rtl8169_private *tp = edev->priv; + + rtl_read_mac_address(tp, mac_addr); + if (is_valid_ether_addr(mac_addr)) + return 0; + + rtl_read_mac_address(tp, mac_addr); + if (is_valid_ether_addr(mac_addr)) + return 0; + + rtl_read_mac_from_reg(tp, mac_addr, MAC0); + if (is_valid_ether_addr(mac_addr)) + return 0; + + return 0; +} + +static int rtl8169_set_ethaddr(struct eth_device *edev, const unsigned char *mac_addr) +{ + struct rtl8169_private *tp = edev->priv; + + rtl_rar_set(tp, mac_addr); + + return 0; +} + +static void rtl8169_eth_halt(struct eth_device *edev) +{ + struct rtl8169_private *tp = edev->priv; + + /* Stop the chip's Tx and Rx DMA processes. */ + RTL_W8(tp, ChipCmd, 0x00); + + /* Disable interrupts by clearing the interrupt mask. */ + RTL_W16(tp, IntrMask, 0x0000); + RTL_W32(tp, RxMissed, 0); + + pci_clear_master(tp->pci_dev); + rtl_pci_commit(tp); + + rtl8169_cleanup(tp); + rtl_disable_exit_l1(tp); + + dma_unmap_single(edev->parent, tp->tx_buf_phys, NUM_TX_DESC * PKT_BUF_SIZE, + DMA_TO_DEVICE); + free(tp->tx_buf); + dma_free_coherent((void *)tp->TxDescArray, tp->TxPhyAddr, + NUM_TX_DESC * sizeof(struct TxDesc)); + + dma_unmap_single(edev->parent, tp->rx_buf_phys, NUM_RX_DESC * PKT_BUF_SIZE, + DMA_FROM_DEVICE); + free(tp->rx_buf); + dma_free_coherent((void *)tp->RxDescArray, tp->RxPhyAddr, + NUM_RX_DESC * sizeof(struct RxDesc)); +} + +static int rtl8169_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct device *dev = &pdev->dev; + struct eth_device *edev; + struct rtl8169_private *tp; + int region, ret; + u16 xid; + enum mac_version chipset; + + /* enable pci device */ + pci_enable_device(pdev); + + tp = xzalloc(sizeof(*tp)); + + edev = &tp->edev; + dev->type_data = edev; + edev->priv = tp; + + tp->pci_dev = pdev; + tp->dev = &pdev->dev; + + tp->miibus.read = rtl8169_phy_read; + tp->miibus.write = rtl8169_phy_write; + tp->miibus.priv = tp; + tp->miibus.parent = dev; + + /* use first MMIO region */ + region = ffs(pci_select_bars(pdev, IORESOURCE_MEM)) - 1; + if (region < 0) { + dev_err(&pdev->dev, "no MMIO resource found\n"); + return -ENODEV; + } + + tp->mmio_addr = pci_iomap(pdev, region); + + rtl_hw_reset(tp); + + xid = (RTL_R32(tp, TxConfig) >> 20) & 0xfcf; + + /* Identify chip attached to board */ + chipset = rtl8169_get_mac_version(xid, 1); + if (chipset == RTL_GIGA_MAC_NONE) { + dev_err(&pdev->dev, "unknown chip XID %03x\n", xid); + return -ENODEV; + } + + dev_info(dev, "found %s (base=0x%p)\n", + rtl_chip_infos[chipset].name, tp->mmio_addr); + + tp->mac_version = chipset; + + tp->dash_type = rtl_check_dash(tp); + + tp->cp_cmd = RTL_R16(tp, CPlusCmd) & CPCMD_MASK; + + rtl_init_rxcfg(tp); + + rtl_hw_initialize(tp); + + tp->fw_name = rtl_chip_infos[chipset].fw_name; + + if (tp->dash_type != RTL_DASH_NONE) + rtl8168_driver_start(tp); + + rtl_set_d3_pll_down(tp, tp->dash_type == RTL_DASH_NONE); + + edev->init = rtl8169_init_dev; + edev->open = rtl8169_eth_open; + edev->send = rtl8169_eth_send; + edev->recv = rtl8169_eth_rx; + edev->get_ethaddr = rtl8169_get_ethaddr; + edev->set_ethaddr = rtl8169_set_ethaddr; + edev->halt = rtl8169_eth_halt; + edev->parent = dev; + tp->ocp_base = OCP_STD_PHY_BASE; + + ret = mdiobus_register(&tp->miibus); + if (ret) + goto mdio_err; + + ret = eth_register(edev); + if (ret) + goto eth_err; + + return 0; + +mdio_err: + eth_unregister(edev); + +eth_err: + free(tp); + + return ret; +} + +static struct pci_driver rtl8169_eth_driver = { + .name = "rtl8169_eth", + .id_table = rtl8169_pci_tbl, + .probe = rtl8169_probe, +}; +device_pci_driver(rtl8169_eth_driver); diff --git a/drivers/net/r8169_phy_config.c b/drivers/net/r8169_phy_config.c new file mode 100644 index 0000000000..c57c221e13 --- /dev/null +++ b/drivers/net/r8169_phy_config.c @@ -0,0 +1,1156 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * r8169_phy_config.c: RealTek 8169/8168/8101 ethernet driver. + * + * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> + * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> + * Copyright (c) a lot of people too. Please respect their work. + * + * See MAINTAINERS file for support contact information. + */ + +#include <common.h> +#include <linux/phy.h> +#include <linux/mdio.h> + +#include "r8169.h" + +typedef void (*rtl_phy_cfg_fct)(struct rtl8169_private *tp, + struct phy_device *phydev); + +static void r8168d_modify_extpage(struct phy_device *phydev, int extpage, + int reg, u16 mask, u16 val) +{ + int oldpage = phy_select_page(phydev, 0x0007); + + phy_write(phydev, 0x1e, extpage); + phy_modify(phydev, reg, mask, val); + + phy_restore_page(phydev, oldpage, 0); +} + +static void r8168d_phy_param(struct phy_device *phydev, u16 parm, + u16 mask, u16 val) +{ + int oldpage = phy_select_page(phydev, 0x0005); + + phy_write(phydev, 0x05, parm); + phy_modify(phydev, 0x06, mask, val); + + phy_restore_page(phydev, oldpage, 0); +} + +static void r8168g_phy_param(struct phy_device *phydev, u16 parm, + u16 mask, u16 val) +{ + int oldpage = phy_select_page(phydev, 0x0a43); + + phy_write(phydev, 0x13, parm); + phy_modify(phydev, 0x14, mask, val); + + phy_restore_page(phydev, oldpage, 0); +} + +struct phy_reg { + u16 reg; + u16 val; +}; + +static void __rtl_writephy_batch(struct phy_device *phydev, + const struct phy_reg *regs, int len) +{ + while (len-- > 0) { + phy_write(phydev, regs->reg, regs->val); + regs++; + } +} + +#define rtl_writephy_batch(p, a) __rtl_writephy_batch(p, a, ARRAY_SIZE(a)) + +static void rtl8168f_config_eee_phy(struct phy_device *phydev) +{ + r8168d_modify_extpage(phydev, 0x0020, 0x15, 0, BIT(8)); + r8168d_phy_param(phydev, 0x8b85, 0, BIT(13)); +} + +static void rtl8168g_config_eee_phy(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0x0a43, 0x11, 0, BIT(4)); +} + +static void rtl8168h_config_eee_phy(struct phy_device *phydev) +{ + rtl8168g_config_eee_phy(phydev); + + phy_modify_paged(phydev, 0xa4a, 0x11, 0x0000, 0x0200); + phy_modify_paged(phydev, 0xa42, 0x14, 0x0000, 0x0080); +} + +static void rtl8125a_config_eee_phy(struct phy_device *phydev) +{ + rtl8168h_config_eee_phy(phydev); + + phy_modify_paged(phydev, 0xa6d, 0x12, 0x0001, 0x0000); + phy_modify_paged(phydev, 0xa6d, 0x14, 0x0010, 0x0000); +} + +static void rtl8125b_config_eee_phy(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0xa6d, 0x12, 0x0001, 0x0000); + phy_modify_paged(phydev, 0xa6d, 0x14, 0x0010, 0x0000); + phy_modify_paged(phydev, 0xa42, 0x14, 0x0080, 0x0000); + phy_modify_paged(phydev, 0xa4a, 0x11, 0x0200, 0x0000); +} + +static void rtl8169s_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x06, 0x006e }, + { 0x08, 0x0708 }, + { 0x15, 0x4000 }, + { 0x18, 0x65c7 }, + + { 0x1f, 0x0001 }, + { 0x03, 0x00a1 }, + { 0x02, 0x0008 }, + { 0x01, 0x0120 }, + { 0x00, 0x1000 }, + { 0x04, 0x0800 }, + { 0x04, 0x0000 }, + + { 0x03, 0xff41 }, + { 0x02, 0xdf60 }, + { 0x01, 0x0140 }, + { 0x00, 0x0077 }, + { 0x04, 0x7800 }, + { 0x04, 0x7000 }, + + { 0x03, 0x802f }, + { 0x02, 0x4f02 }, + { 0x01, 0x0409 }, + { 0x00, 0xf0f9 }, + { 0x04, 0x9800 }, + { 0x04, 0x9000 }, + + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0xff95 }, + { 0x00, 0xba00 }, + { 0x04, 0xa800 }, + { 0x04, 0xa000 }, + + { 0x03, 0xff41 }, + { 0x02, 0xdf20 }, + { 0x01, 0x0140 }, + { 0x00, 0x00bb }, + { 0x04, 0xb800 }, + { 0x04, 0xb000 }, + + { 0x03, 0xdf41 }, + { 0x02, 0xdc60 }, + { 0x01, 0x6340 }, + { 0x00, 0x007d }, + { 0x04, 0xd800 }, + { 0x04, 0xd000 }, + + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0x100a }, + { 0x00, 0xa0ff }, + { 0x04, 0xf800 }, + { 0x04, 0xf000 }, + + { 0x1f, 0x0000 }, + { 0x0b, 0x0000 }, + { 0x00, 0x9200 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); +} + +static void rtl8169sb_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_write_paged(phydev, 0x0002, 0x01, 0x90d0); +} + +static void rtl8169scd_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x04, 0x0000 }, + { 0x03, 0x00a1 }, + { 0x02, 0x0008 }, + { 0x01, 0x0120 }, + { 0x00, 0x1000 }, + { 0x04, 0x0800 }, + { 0x04, 0x9000 }, + { 0x03, 0x802f }, + { 0x02, 0x4f02 }, + { 0x01, 0x0409 }, + { 0x00, 0xf099 }, + { 0x04, 0x9800 }, + { 0x04, 0xa000 }, + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0xff95 }, + { 0x00, 0xba00 }, + { 0x04, 0xa800 }, + { 0x04, 0xf000 }, + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0x101a }, + { 0x00, 0xa0ff }, + { 0x04, 0xf800 }, + { 0x04, 0x0000 }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0001 }, + { 0x10, 0xf41b }, + { 0x14, 0xfb54 }, + { 0x18, 0xf5c7 }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0001 }, + { 0x17, 0x0cc0 }, + { 0x1f, 0x0000 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); +} + +static void rtl8169sce_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x04, 0x0000 }, + { 0x03, 0x00a1 }, + { 0x02, 0x0008 }, + { 0x01, 0x0120 }, + { 0x00, 0x1000 }, + { 0x04, 0x0800 }, + { 0x04, 0x9000 }, + { 0x03, 0x802f }, + { 0x02, 0x4f02 }, + { 0x01, 0x0409 }, + { 0x00, 0xf099 }, + { 0x04, 0x9800 }, + { 0x04, 0xa000 }, + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0xff95 }, + { 0x00, 0xba00 }, + { 0x04, 0xa800 }, + { 0x04, 0xf000 }, + { 0x03, 0xdf01 }, + { 0x02, 0xdf20 }, + { 0x01, 0x101a }, + { 0x00, 0xa0ff }, + { 0x04, 0xf800 }, + { 0x04, 0x0000 }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0001 }, + { 0x0b, 0x8480 }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0001 }, + { 0x18, 0x67c7 }, + { 0x04, 0x2000 }, + { 0x03, 0x002f }, + { 0x02, 0x4360 }, + { 0x01, 0x0109 }, + { 0x00, 0x3022 }, + { 0x04, 0x2800 }, + { 0x1f, 0x0000 }, + + { 0x1f, 0x0001 }, + { 0x17, 0x0cc0 }, + { 0x1f, 0x0000 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); +} + +static void rtl8168bb_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_write(phydev, 0x1f, 0x0001); + phy_set_bits(phydev, 0x16, BIT(0)); + phy_write(phydev, 0x10, 0xf41b); + phy_write(phydev, 0x1f, 0x0000); +} + +static void rtl8168bef_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_write_paged(phydev, 0x0001, 0x10, 0xf41b); +} + +static void rtl8168cp_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_write(phydev, 0x1d, 0x0f00); + phy_write_paged(phydev, 0x0002, 0x0c, 0x1ec8); +} + +static void rtl8168cp_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_set_bits(phydev, 0x14, BIT(5)); + phy_set_bits(phydev, 0x0d, BIT(5)); + phy_write_paged(phydev, 0x0001, 0x1d, 0x3d98); +} + +static void rtl8168c_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x1f, 0x0002 }, + { 0x00, 0x88d4 }, + { 0x01, 0x82b1 }, + { 0x03, 0x7002 }, + { 0x08, 0x9e30 }, + { 0x09, 0x01f0 }, + { 0x0a, 0x5500 }, + { 0x0c, 0x00c8 }, + { 0x1f, 0x0003 }, + { 0x12, 0xc096 }, + { 0x16, 0x000a }, + { 0x1f, 0x0000 }, + { 0x1f, 0x0000 }, + { 0x09, 0x2000 }, + { 0x09, 0x0000 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); + + phy_set_bits(phydev, 0x14, BIT(5)); + phy_set_bits(phydev, 0x0d, BIT(5)); +} + +static void rtl8168c_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x03, 0x802f }, + { 0x02, 0x4f02 }, + { 0x01, 0x0409 }, + { 0x00, 0xf099 }, + { 0x04, 0x9800 }, + { 0x04, 0x9000 }, + { 0x1d, 0x3d98 }, + { 0x1f, 0x0002 }, + { 0x0c, 0x7eb8 }, + { 0x06, 0x0761 }, + { 0x1f, 0x0003 }, + { 0x16, 0x0f0a }, + { 0x1f, 0x0000 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); + + phy_set_bits(phydev, 0x16, BIT(0)); + phy_set_bits(phydev, 0x14, BIT(5)); + phy_set_bits(phydev, 0x0d, BIT(5)); +} + +static void rtl8168c_3_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0001 }, + { 0x12, 0x2300 }, + { 0x1d, 0x3d98 }, + { 0x1f, 0x0002 }, + { 0x0c, 0x7eb8 }, + { 0x06, 0x5461 }, + { 0x1f, 0x0003 }, + { 0x16, 0x0f0a }, + { 0x1f, 0x0000 } + }; + + rtl_writephy_batch(phydev, phy_reg_init); + + phy_set_bits(phydev, 0x16, BIT(0)); + phy_set_bits(phydev, 0x14, BIT(5)); + phy_set_bits(phydev, 0x0d, BIT(5)); +} + +static const struct phy_reg rtl8168d_1_phy_reg_init_0[] = { + /* Channel Estimation */ + { 0x1f, 0x0001 }, + { 0x06, 0x4064 }, + { 0x07, 0x2863 }, + { 0x08, 0x059c }, + { 0x09, 0x26b4 }, + { 0x0a, 0x6a19 }, + { 0x0b, 0xdcc8 }, + { 0x10, 0xf06d }, + { 0x14, 0x7f68 }, + { 0x18, 0x7fd9 }, + { 0x1c, 0xf0ff }, + { 0x1d, 0x3d9c }, + { 0x1f, 0x0003 }, + { 0x12, 0xf49f }, + { 0x13, 0x070b }, + { 0x1a, 0x05ad }, + { 0x14, 0x94c0 }, + + /* + * Tx Error Issue + * Enhance line driver power + */ + { 0x1f, 0x0002 }, + { 0x06, 0x5561 }, + { 0x1f, 0x0005 }, + { 0x05, 0x8332 }, + { 0x06, 0x5561 }, + + /* + * Can not link to 1Gbps with bad cable + * Decrease SNR threshold form 21.07dB to 19.04dB + */ + { 0x1f, 0x0001 }, + { 0x17, 0x0cc0 }, + + { 0x1f, 0x0000 }, + { 0x0d, 0xf880 } +}; + +static void rtl8168d_apply_firmware_cond(struct rtl8169_private *tp, + struct phy_device *phydev, + u16 val) +{ + u16 reg_val; + + phy_write(phydev, 0x1f, 0x0005); + phy_write(phydev, 0x05, 0x001b); + reg_val = phy_read(phydev, 0x06); + phy_write(phydev, 0x1f, 0x0000); + + if (reg_val != val) + dev_warn(&phydev->dev, "chipset not ready for firmware\n"); + else + r8169_apply_firmware(tp); +} + +static void rtl8168d_1_common(struct phy_device *phydev) +{ + u16 val; + + phy_write_paged(phydev, 0x0002, 0x05, 0x669a); + r8168d_phy_param(phydev, 0x8330, 0xffff, 0x669a); + phy_write(phydev, 0x1f, 0x0002); + + val = phy_read(phydev, 0x0d); + + if ((val & 0x00ff) != 0x006c) { + static const u16 set[] = { + 0x0065, 0x0066, 0x0067, 0x0068, + 0x0069, 0x006a, 0x006b, 0x006c + }; + int i; + + val &= 0xff00; + for (i = 0; i < ARRAY_SIZE(set); i++) + phy_write(phydev, 0x0d, val | set[i]); + } +} + +static void rtl8168d_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + rtl_writephy_batch(phydev, rtl8168d_1_phy_reg_init_0); + + /* + * Rx Error Issue + * Fine Tune Switching regulator parameter + */ + phy_write(phydev, 0x1f, 0x0002); + phy_modify(phydev, 0x0b, 0x00ef, 0x0010); + phy_modify(phydev, 0x0c, 0x5d00, 0xa200); + + if (rtl8168d_efuse_read(tp, 0x01) == 0xb1) { + rtl8168d_1_common(phydev); + } else { + phy_write_paged(phydev, 0x0002, 0x05, 0x6662); + r8168d_phy_param(phydev, 0x8330, 0xffff, 0x6662); + } + + /* RSET couple improve */ + phy_write(phydev, 0x1f, 0x0002); + phy_set_bits(phydev, 0x0d, 0x0300); + phy_set_bits(phydev, 0x0f, 0x0010); + + /* Fine tune PLL performance */ + phy_write(phydev, 0x1f, 0x0002); + phy_modify(phydev, 0x02, 0x0600, 0x0100); + phy_clear_bits(phydev, 0x03, 0xe000); + phy_write(phydev, 0x1f, 0x0000); + + rtl8168d_apply_firmware_cond(tp, phydev, 0xbf00); +} + +static void rtl8168d_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + rtl_writephy_batch(phydev, rtl8168d_1_phy_reg_init_0); + + if (rtl8168d_efuse_read(tp, 0x01) == 0xb1) { + rtl8168d_1_common(phydev); + } else { + phy_write_paged(phydev, 0x0002, 0x05, 0x2642); + r8168d_phy_param(phydev, 0x8330, 0xffff, 0x2642); + } + + /* Fine tune PLL performance */ + phy_write(phydev, 0x1f, 0x0002); + phy_modify(phydev, 0x02, 0x0600, 0x0100); + phy_clear_bits(phydev, 0x03, 0xe000); + phy_write(phydev, 0x1f, 0x0000); + + /* Switching regulator Slew rate */ + phy_modify_paged(phydev, 0x0002, 0x0f, 0x0000, 0x0017); + + rtl8168d_apply_firmware_cond(tp, phydev, 0xb300); +} + +static void rtl8168d_4_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_write_paged(phydev, 0x0001, 0x17, 0x0cc0); + r8168d_modify_extpage(phydev, 0x002d, 0x18, 0xffff, 0x0040); + phy_set_bits(phydev, 0x0d, BIT(5)); +} + +static void rtl8168e_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + /* Channel estimation fine tune */ + { 0x1f, 0x0001 }, + { 0x0b, 0x6c20 }, + { 0x07, 0x2872 }, + { 0x1c, 0xefff }, + { 0x1f, 0x0003 }, + { 0x14, 0x6420 }, + { 0x1f, 0x0000 }, + }; + + r8169_apply_firmware(tp); + + /* Enable Delay cap */ + r8168d_phy_param(phydev, 0x8b80, 0xffff, 0xc896); + + rtl_writephy_batch(phydev, phy_reg_init); + + /* Update PFM & 10M TX idle timer */ + r8168d_modify_extpage(phydev, 0x002f, 0x15, 0xffff, 0x1919); + + r8168d_modify_extpage(phydev, 0x00ac, 0x18, 0xffff, 0x0006); + + /* DCO enable for 10M IDLE Power */ + r8168d_modify_extpage(phydev, 0x0023, 0x17, 0x0000, 0x0006); + + /* For impedance matching */ + phy_modify_paged(phydev, 0x0002, 0x08, 0x7f00, 0x8000); + + /* PHY auto speed down */ + r8168d_modify_extpage(phydev, 0x002d, 0x18, 0x0000, 0x0050); + phy_set_bits(phydev, 0x14, BIT(15)); + + r8168d_phy_param(phydev, 0x8b86, 0x0000, 0x0001); + r8168d_phy_param(phydev, 0x8b85, 0x2000, 0x0000); + + r8168d_modify_extpage(phydev, 0x0020, 0x15, 0x1100, 0x0000); + phy_write_paged(phydev, 0x0006, 0x00, 0x5a00); + + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0000); +} + +static void rtl8168e_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + + /* Enable Delay cap */ + r8168d_modify_extpage(phydev, 0x00ac, 0x18, 0xffff, 0x0006); + + /* Channel estimation fine tune */ + phy_write_paged(phydev, 0x0003, 0x09, 0xa20f); + + /* Green Setting */ + r8168d_phy_param(phydev, 0x8b5b, 0xffff, 0x9222); + r8168d_phy_param(phydev, 0x8b6d, 0xffff, 0x8000); + r8168d_phy_param(phydev, 0x8b76, 0xffff, 0x8000); + + /* For 4-corner performance improve */ + phy_write(phydev, 0x1f, 0x0005); + phy_write(phydev, 0x05, 0x8b80); + phy_set_bits(phydev, 0x17, 0x0006); + phy_write(phydev, 0x1f, 0x0000); + + /* PHY auto speed down */ + r8168d_modify_extpage(phydev, 0x002d, 0x18, 0x0000, 0x0010); + phy_set_bits(phydev, 0x14, BIT(15)); + + /* improve 10M EEE waveform */ + r8168d_phy_param(phydev, 0x8b86, 0x0000, 0x0001); + + /* Improve 2-pair detection performance */ + r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x4000); + + rtl8168f_config_eee_phy(phydev); + + /* Green feature */ + phy_write(phydev, 0x1f, 0x0003); + phy_set_bits(phydev, 0x19, BIT(0)); + phy_set_bits(phydev, 0x10, BIT(10)); + phy_write(phydev, 0x1f, 0x0000); + phy_modify_paged(phydev, 0x0005, 0x01, 0, BIT(8)); +} + +static void rtl8168f_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + /* For 4-corner performance improve */ + r8168d_phy_param(phydev, 0x8b80, 0x0000, 0x0006); + + /* PHY auto speed down */ + r8168d_modify_extpage(phydev, 0x002d, 0x18, 0x0000, 0x0010); + phy_set_bits(phydev, 0x14, BIT(15)); + + /* Improve 10M EEE waveform */ + r8168d_phy_param(phydev, 0x8b86, 0x0000, 0x0001); + + rtl8168f_config_eee_phy(phydev); +} + +static void rtl8168f_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + + /* Channel estimation fine tune */ + phy_write_paged(phydev, 0x0003, 0x09, 0xa20f); + + /* Modify green table for giga & fnet */ + r8168d_phy_param(phydev, 0x8b55, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b5e, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b67, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b70, 0xffff, 0x0000); + r8168d_modify_extpage(phydev, 0x0078, 0x17, 0xffff, 0x0000); + r8168d_modify_extpage(phydev, 0x0078, 0x19, 0xffff, 0x00fb); + + /* Modify green table for 10M */ + r8168d_phy_param(phydev, 0x8b79, 0xffff, 0xaa00); + + /* Disable hiimpedance detection (RTCT) */ + phy_write_paged(phydev, 0x0003, 0x01, 0x328a); + + rtl8168f_hw_phy_config(tp, phydev); + + /* Improve 2-pair detection performance */ + r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x4000); +} + +static void rtl8168f_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + + rtl8168f_hw_phy_config(tp, phydev); +} + +static void rtl8411_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + + rtl8168f_hw_phy_config(tp, phydev); + + /* Improve 2-pair detection performance */ + r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x4000); + + /* Channel estimation fine tune */ + phy_write_paged(phydev, 0x0003, 0x09, 0xa20f); + + /* Modify green table for giga & fnet */ + r8168d_phy_param(phydev, 0x8b55, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b5e, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b67, 0xffff, 0x0000); + r8168d_phy_param(phydev, 0x8b70, 0xffff, 0x0000); + r8168d_modify_extpage(phydev, 0x0078, 0x17, 0xffff, 0x0000); + r8168d_modify_extpage(phydev, 0x0078, 0x19, 0xffff, 0x00aa); + + /* Modify green table for 10M */ + r8168d_phy_param(phydev, 0x8b79, 0xffff, 0xaa00); + + /* Disable hiimpedance detection (RTCT) */ + phy_write_paged(phydev, 0x0003, 0x01, 0x328a); + + /* Modify green table for giga */ + r8168d_phy_param(phydev, 0x8b54, 0x0800, 0x0000); + r8168d_phy_param(phydev, 0x8b5d, 0x0800, 0x0000); + r8168d_phy_param(phydev, 0x8a7c, 0x0100, 0x0000); + r8168d_phy_param(phydev, 0x8a7f, 0x0000, 0x0100); + r8168d_phy_param(phydev, 0x8a82, 0x0100, 0x0000); + r8168d_phy_param(phydev, 0x8a85, 0x0100, 0x0000); + r8168d_phy_param(phydev, 0x8a88, 0x0100, 0x0000); + + /* uc same-seed solution */ + r8168d_phy_param(phydev, 0x8b85, 0x0000, 0x8000); + + /* Green feature */ + phy_write(phydev, 0x1f, 0x0003); + phy_clear_bits(phydev, 0x19, BIT(0)); + phy_clear_bits(phydev, 0x10, BIT(10)); + phy_write(phydev, 0x1f, 0x0000); +} + +static void rtl8168g_disable_aldps(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0x0a43, 0x10, BIT(2), 0); +} + +static void rtl8168g_enable_gphy_10m(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0x0a44, 0x11, 0, BIT(11)); +} + +static void rtl8168g_phy_adjust_10m_aldps(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0x0bcc, 0x14, BIT(8), 0); + phy_modify_paged(phydev, 0x0a44, 0x11, 0, BIT(7) | BIT(6)); + r8168g_phy_param(phydev, 0x8084, 0x6000, 0x0000); + phy_modify_paged(phydev, 0x0a43, 0x10, 0x0000, 0x1003); +} + +static void rtl8168g_1_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + int ret; + + r8169_apply_firmware(tp); + + ret = phy_read_paged(phydev, 0x0a46, 0x10); + if (ret & BIT(8)) + phy_modify_paged(phydev, 0x0bcc, 0x12, BIT(15), 0); + else + phy_modify_paged(phydev, 0x0bcc, 0x12, 0, BIT(15)); + + ret = phy_read_paged(phydev, 0x0a46, 0x13); + if (ret & BIT(8)) + phy_modify_paged(phydev, 0x0c41, 0x15, 0, BIT(1)); + else + phy_modify_paged(phydev, 0x0c41, 0x15, BIT(1), 0); + + /* Enable PHY auto speed down */ + phy_modify_paged(phydev, 0x0a44, 0x11, 0, BIT(3) | BIT(2)); + + rtl8168g_phy_adjust_10m_aldps(phydev); + + /* EEE auto-fallback function */ + phy_modify_paged(phydev, 0x0a4b, 0x11, 0, BIT(2)); + + /* Enable UC LPF tune function */ + r8168g_phy_param(phydev, 0x8012, 0x0000, 0x8000); + + phy_modify_paged(phydev, 0x0c42, 0x11, BIT(13), BIT(14)); + + /* Improve SWR Efficiency */ + phy_write(phydev, 0x1f, 0x0bcd); + phy_write(phydev, 0x14, 0x5065); + phy_write(phydev, 0x14, 0xd065); + phy_write(phydev, 0x1f, 0x0bc8); + phy_write(phydev, 0x11, 0x5655); + phy_write(phydev, 0x1f, 0x0bcd); + phy_write(phydev, 0x14, 0x1065); + phy_write(phydev, 0x14, 0x9065); + phy_write(phydev, 0x14, 0x1065); + phy_write(phydev, 0x1f, 0x0000); + + rtl8168g_disable_aldps(phydev); + rtl8168g_config_eee_phy(phydev); +} + +static void rtl8168g_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + rtl8168g_config_eee_phy(phydev); +} + +static void rtl8168h_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + u16 ioffset, rlen; + u32 data; + + r8169_apply_firmware(tp); + + /* CHIN EST parameter update */ + r8168g_phy_param(phydev, 0x808a, 0x003f, 0x000a); + + /* enable R-tune & PGA-retune function */ + r8168g_phy_param(phydev, 0x0811, 0x0000, 0x0800); + phy_modify_paged(phydev, 0x0a42, 0x16, 0x0000, 0x0002); + + rtl8168g_enable_gphy_10m(phydev); + + ioffset = rtl8168h_2_get_adc_bias_ioffset(tp); + if (ioffset != 0xffff) + phy_write_paged(phydev, 0x0bcf, 0x16, ioffset); + + /* Modify rlen (TX LPF corner frequency) level */ + data = phy_read_paged(phydev, 0x0bcd, 0x16); + data &= 0x000f; + rlen = 0; + if (data > 3) + rlen = data - 3; + data = rlen | (rlen << 4) | (rlen << 8) | (rlen << 12); + phy_write_paged(phydev, 0x0bcd, 0x17, data); + + /* disable phy pfm mode */ + phy_modify_paged(phydev, 0x0a44, 0x11, BIT(7), 0); + + /* disable 10m pll off */ + phy_modify_paged(phydev, 0x0a43, 0x10, BIT(0), 0); + + rtl8168g_disable_aldps(phydev); + rtl8168g_config_eee_phy(phydev); +} + +static void rtl8168ep_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + rtl8168g_phy_adjust_10m_aldps(phydev); + + /* Enable UC LPF tune function */ + r8168g_phy_param(phydev, 0x8012, 0x0000, 0x8000); + + /* Set rg_sel_sdm_rate */ + phy_modify_paged(phydev, 0x0c42, 0x11, BIT(13), BIT(14)); + + /* Channel estimation parameters */ + r8168g_phy_param(phydev, 0x80f3, 0xff00, 0x8b00); + r8168g_phy_param(phydev, 0x80f0, 0xff00, 0x3a00); + r8168g_phy_param(phydev, 0x80ef, 0xff00, 0x0500); + r8168g_phy_param(phydev, 0x80f6, 0xff00, 0x6e00); + r8168g_phy_param(phydev, 0x80ec, 0xff00, 0x6800); + r8168g_phy_param(phydev, 0x80ed, 0xff00, 0x7c00); + r8168g_phy_param(phydev, 0x80f2, 0xff00, 0xf400); + r8168g_phy_param(phydev, 0x80f4, 0xff00, 0x8500); + r8168g_phy_param(phydev, 0x8110, 0xff00, 0xa800); + r8168g_phy_param(phydev, 0x810f, 0xff00, 0x1d00); + r8168g_phy_param(phydev, 0x8111, 0xff00, 0xf500); + r8168g_phy_param(phydev, 0x8113, 0xff00, 0x6100); + r8168g_phy_param(phydev, 0x8115, 0xff00, 0x9200); + r8168g_phy_param(phydev, 0x810e, 0xff00, 0x0400); + r8168g_phy_param(phydev, 0x810c, 0xff00, 0x7c00); + r8168g_phy_param(phydev, 0x810b, 0xff00, 0x5a00); + r8168g_phy_param(phydev, 0x80d1, 0xff00, 0xff00); + r8168g_phy_param(phydev, 0x80cd, 0xff00, 0x9e00); + r8168g_phy_param(phydev, 0x80d3, 0xff00, 0x0e00); + r8168g_phy_param(phydev, 0x80d5, 0xff00, 0xca00); + r8168g_phy_param(phydev, 0x80d7, 0xff00, 0x8400); + + /* Force PWM-mode */ + phy_write(phydev, 0x1f, 0x0bcd); + phy_write(phydev, 0x14, 0x5065); + phy_write(phydev, 0x14, 0xd065); + phy_write(phydev, 0x1f, 0x0bc8); + phy_write(phydev, 0x12, 0x00ed); + phy_write(phydev, 0x1f, 0x0bcd); + phy_write(phydev, 0x14, 0x1065); + phy_write(phydev, 0x14, 0x9065); + phy_write(phydev, 0x14, 0x1065); + phy_write(phydev, 0x1f, 0x0000); + + rtl8168g_disable_aldps(phydev); + rtl8168g_config_eee_phy(phydev); +} + +static void rtl8117_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + /* CHN EST parameters adjust - fnet */ + r8168g_phy_param(phydev, 0x808e, 0xff00, 0x4800); + r8168g_phy_param(phydev, 0x8090, 0xff00, 0xcc00); + r8168g_phy_param(phydev, 0x8092, 0xff00, 0xb000); + + r8168g_phy_param(phydev, 0x8088, 0xff00, 0x6000); + r8168g_phy_param(phydev, 0x808b, 0x3f00, 0x0b00); + r8168g_phy_param(phydev, 0x808d, 0x1f00, 0x0600); + r8168g_phy_param(phydev, 0x808c, 0xff00, 0xb000); + r8168g_phy_param(phydev, 0x80a0, 0xff00, 0x2800); + r8168g_phy_param(phydev, 0x80a2, 0xff00, 0x5000); + r8168g_phy_param(phydev, 0x809b, 0xf800, 0xb000); + r8168g_phy_param(phydev, 0x809a, 0xff00, 0x4b00); + r8168g_phy_param(phydev, 0x809d, 0x3f00, 0x0800); + r8168g_phy_param(phydev, 0x80a1, 0xff00, 0x7000); + r8168g_phy_param(phydev, 0x809f, 0x1f00, 0x0300); + r8168g_phy_param(phydev, 0x809e, 0xff00, 0x8800); + r8168g_phy_param(phydev, 0x80b2, 0xff00, 0x2200); + r8168g_phy_param(phydev, 0x80ad, 0xf800, 0x9800); + r8168g_phy_param(phydev, 0x80af, 0x3f00, 0x0800); + r8168g_phy_param(phydev, 0x80b3, 0xff00, 0x6f00); + r8168g_phy_param(phydev, 0x80b1, 0x1f00, 0x0300); + r8168g_phy_param(phydev, 0x80b0, 0xff00, 0x9300); + + r8168g_phy_param(phydev, 0x8011, 0x0000, 0x0800); + + rtl8168g_enable_gphy_10m(phydev); + + r8168g_phy_param(phydev, 0x8016, 0x0000, 0x0400); + + rtl8168g_disable_aldps(phydev); + rtl8168h_config_eee_phy(phydev); +} + +static void rtl8102e_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0003 }, + { 0x08, 0x441d }, + { 0x01, 0x9100 }, + { 0x1f, 0x0000 } + }; + + phy_set_bits(phydev, 0x11, BIT(12)); + phy_set_bits(phydev, 0x19, BIT(13)); + phy_set_bits(phydev, 0x10, BIT(15)); + + rtl_writephy_batch(phydev, phy_reg_init); +} + +static void rtl8401_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + phy_set_bits(phydev, 0x11, BIT(12)); + phy_modify_paged(phydev, 0x0002, 0x0f, 0x0000, 0x0003); +} + +static void rtl8105e_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + /* Disable ALDPS before ram code */ + phy_write(phydev, 0x18, 0x0310); + mdelay(100); + + r8169_apply_firmware(tp); + + phy_write_paged(phydev, 0x0005, 0x1a, 0x0000); + phy_write_paged(phydev, 0x0004, 0x1c, 0x0000); + phy_write_paged(phydev, 0x0001, 0x15, 0x7701); +} + +static void rtl8402_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + /* Disable ALDPS before setting firmware */ + phy_write(phydev, 0x18, 0x0310); + mdelay(20); + + r8169_apply_firmware(tp); + + /* EEE setting */ + phy_write(phydev, 0x1f, 0x0004); + phy_write(phydev, 0x10, 0x401f); + phy_write(phydev, 0x19, 0x7030); + phy_write(phydev, 0x1f, 0x0000); +} + +static void rtl8106e_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + static const struct phy_reg phy_reg_init[] = { + { 0x1f, 0x0004 }, + { 0x10, 0xc07f }, + { 0x19, 0x7030 }, + { 0x1f, 0x0000 } + }; + + /* Disable ALDPS before ram code */ + phy_write(phydev, 0x18, 0x0310); + mdelay(100); + + r8169_apply_firmware(tp); + + rtl_writephy_batch(phydev, phy_reg_init); +} + +static void rtl8125_legacy_force_mode(struct phy_device *phydev) +{ + phy_modify_paged(phydev, 0xa5b, 0x12, BIT(15), 0); +} + +static void rtl8125a_2_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + int i; + + phy_modify_paged(phydev, 0xad4, 0x17, 0x0000, 0x0010); + phy_modify_paged(phydev, 0xad1, 0x13, 0x03ff, 0x03ff); + phy_modify_paged(phydev, 0xad3, 0x11, 0x003f, 0x0006); + phy_modify_paged(phydev, 0xac0, 0x14, 0x1100, 0x0000); + phy_modify_paged(phydev, 0xacc, 0x10, 0x0003, 0x0002); + phy_modify_paged(phydev, 0xad4, 0x10, 0x00e7, 0x0044); + phy_modify_paged(phydev, 0xac1, 0x12, 0x0080, 0x0000); + phy_modify_paged(phydev, 0xac8, 0x10, 0x0300, 0x0000); + phy_modify_paged(phydev, 0xac5, 0x17, 0x0007, 0x0002); + phy_write_paged(phydev, 0xad4, 0x16, 0x00a8); + phy_write_paged(phydev, 0xac5, 0x16, 0x01ff); + phy_modify_paged(phydev, 0xac8, 0x15, 0x00f0, 0x0030); + + phy_write(phydev, 0x1f, 0x0b87); + phy_write(phydev, 0x16, 0x80a2); + phy_write(phydev, 0x17, 0x0153); + phy_write(phydev, 0x16, 0x809c); + phy_write(phydev, 0x17, 0x0153); + phy_write(phydev, 0x1f, 0x0000); + + phy_write(phydev, 0x1f, 0x0a43); + phy_write(phydev, 0x13, 0x81B3); + phy_write(phydev, 0x14, 0x0043); + phy_write(phydev, 0x14, 0x00A7); + phy_write(phydev, 0x14, 0x00D6); + phy_write(phydev, 0x14, 0x00EC); + phy_write(phydev, 0x14, 0x00F6); + phy_write(phydev, 0x14, 0x00FB); + phy_write(phydev, 0x14, 0x00FD); + phy_write(phydev, 0x14, 0x00FF); + phy_write(phydev, 0x14, 0x00BB); + phy_write(phydev, 0x14, 0x0058); + phy_write(phydev, 0x14, 0x0029); + phy_write(phydev, 0x14, 0x0013); + phy_write(phydev, 0x14, 0x0009); + phy_write(phydev, 0x14, 0x0004); + phy_write(phydev, 0x14, 0x0002); + for (i = 0; i < 25; i++) + phy_write(phydev, 0x14, 0x0000); + phy_write(phydev, 0x1f, 0x0000); + + r8168g_phy_param(phydev, 0x8257, 0xffff, 0x020F); + r8168g_phy_param(phydev, 0x80ea, 0xffff, 0x7843); + + r8169_apply_firmware(tp); + + phy_modify_paged(phydev, 0xd06, 0x14, 0x0000, 0x2000); + + r8168g_phy_param(phydev, 0x81a2, 0x0000, 0x0100); + + phy_modify_paged(phydev, 0xb54, 0x16, 0xff00, 0xdb00); + phy_modify_paged(phydev, 0xa45, 0x12, 0x0001, 0x0000); + phy_modify_paged(phydev, 0xa5d, 0x12, 0x0000, 0x0020); + phy_modify_paged(phydev, 0xad4, 0x17, 0x0010, 0x0000); + phy_modify_paged(phydev, 0xa86, 0x15, 0x0001, 0x0000); + rtl8168g_enable_gphy_10m(phydev); + + rtl8125a_config_eee_phy(phydev); +} + +static void rtl8125b_hw_phy_config(struct rtl8169_private *tp, + struct phy_device *phydev) +{ + r8169_apply_firmware(tp); + + phy_modify_paged(phydev, 0xa44, 0x11, 0x0000, 0x0800); + phy_modify_paged(phydev, 0xac4, 0x13, 0x00f0, 0x0090); + phy_modify_paged(phydev, 0xad3, 0x10, 0x0003, 0x0001); + + phy_write(phydev, 0x1f, 0x0b87); + phy_write(phydev, 0x16, 0x80f5); + phy_write(phydev, 0x17, 0x760e); + phy_write(phydev, 0x16, 0x8107); + phy_write(phydev, 0x17, 0x360e); + phy_write(phydev, 0x16, 0x8551); + phy_modify(phydev, 0x17, 0xff00, 0x0800); + phy_write(phydev, 0x1f, 0x0000); + + phy_modify_paged(phydev, 0xbf0, 0x10, 0xe000, 0xa000); + phy_modify_paged(phydev, 0xbf4, 0x13, 0x0f00, 0x0300); + + r8168g_phy_param(phydev, 0x8044, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x804a, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x8050, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x8056, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x805c, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x8062, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x8068, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x806e, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x8074, 0xffff, 0x2417); + r8168g_phy_param(phydev, 0x807a, 0xffff, 0x2417); + + phy_modify_paged(phydev, 0xa4c, 0x15, 0x0000, 0x0040); + phy_modify_paged(phydev, 0xbf8, 0x12, 0xe000, 0xa000); + + rtl8125_legacy_force_mode(phydev); + rtl8125b_config_eee_phy(phydev); +} + +void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev, + enum mac_version ver) +{ + static const rtl_phy_cfg_fct phy_configs[] = { + /* PCI devices. */ + [RTL_GIGA_MAC_VER_02] = rtl8169s_hw_phy_config, + [RTL_GIGA_MAC_VER_03] = rtl8169s_hw_phy_config, + [RTL_GIGA_MAC_VER_04] = rtl8169sb_hw_phy_config, + [RTL_GIGA_MAC_VER_05] = rtl8169scd_hw_phy_config, + [RTL_GIGA_MAC_VER_06] = rtl8169sce_hw_phy_config, + /* PCI-E devices. */ + [RTL_GIGA_MAC_VER_07] = rtl8102e_hw_phy_config, + [RTL_GIGA_MAC_VER_08] = rtl8102e_hw_phy_config, + [RTL_GIGA_MAC_VER_09] = rtl8102e_hw_phy_config, + [RTL_GIGA_MAC_VER_10] = NULL, + [RTL_GIGA_MAC_VER_11] = rtl8168bb_hw_phy_config, + [RTL_GIGA_MAC_VER_14] = rtl8401_hw_phy_config, + [RTL_GIGA_MAC_VER_17] = rtl8168bef_hw_phy_config, + [RTL_GIGA_MAC_VER_18] = rtl8168cp_1_hw_phy_config, + [RTL_GIGA_MAC_VER_19] = rtl8168c_1_hw_phy_config, + [RTL_GIGA_MAC_VER_20] = rtl8168c_2_hw_phy_config, + [RTL_GIGA_MAC_VER_21] = rtl8168c_3_hw_phy_config, + [RTL_GIGA_MAC_VER_22] = rtl8168c_3_hw_phy_config, + [RTL_GIGA_MAC_VER_23] = rtl8168cp_2_hw_phy_config, + [RTL_GIGA_MAC_VER_24] = rtl8168cp_2_hw_phy_config, + [RTL_GIGA_MAC_VER_25] = rtl8168d_1_hw_phy_config, + [RTL_GIGA_MAC_VER_26] = rtl8168d_2_hw_phy_config, + [RTL_GIGA_MAC_VER_28] = rtl8168d_4_hw_phy_config, + [RTL_GIGA_MAC_VER_29] = rtl8105e_hw_phy_config, + [RTL_GIGA_MAC_VER_30] = rtl8105e_hw_phy_config, + [RTL_GIGA_MAC_VER_31] = NULL, + [RTL_GIGA_MAC_VER_32] = rtl8168e_1_hw_phy_config, + [RTL_GIGA_MAC_VER_33] = rtl8168e_1_hw_phy_config, + [RTL_GIGA_MAC_VER_34] = rtl8168e_2_hw_phy_config, + [RTL_GIGA_MAC_VER_35] = rtl8168f_1_hw_phy_config, + [RTL_GIGA_MAC_VER_36] = rtl8168f_2_hw_phy_config, + [RTL_GIGA_MAC_VER_37] = rtl8402_hw_phy_config, + [RTL_GIGA_MAC_VER_38] = rtl8411_hw_phy_config, + [RTL_GIGA_MAC_VER_39] = rtl8106e_hw_phy_config, + [RTL_GIGA_MAC_VER_40] = rtl8168g_1_hw_phy_config, + [RTL_GIGA_MAC_VER_42] = rtl8168g_2_hw_phy_config, + [RTL_GIGA_MAC_VER_43] = rtl8168g_2_hw_phy_config, + [RTL_GIGA_MAC_VER_44] = rtl8168g_2_hw_phy_config, + [RTL_GIGA_MAC_VER_46] = rtl8168h_2_hw_phy_config, + [RTL_GIGA_MAC_VER_48] = rtl8168h_2_hw_phy_config, + [RTL_GIGA_MAC_VER_51] = rtl8168ep_2_hw_phy_config, + [RTL_GIGA_MAC_VER_52] = rtl8117_hw_phy_config, + [RTL_GIGA_MAC_VER_53] = rtl8117_hw_phy_config, + [RTL_GIGA_MAC_VER_61] = rtl8125a_2_hw_phy_config, + [RTL_GIGA_MAC_VER_63] = rtl8125b_hw_phy_config, + }; + + if (phy_configs[ver]) + phy_configs[ver](tp, phydev); +} diff --git a/drivers/net/realtek-dsa/Kconfig b/drivers/net/realtek-dsa/Kconfig new file mode 100644 index 0000000000..f9404e0265 --- /dev/null +++ b/drivers/net/realtek-dsa/Kconfig @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig DRIVER_NET_DSA_REALTEK + tristate "Realtek Ethernet switch family support" + depends on DSA + select PHYLIB + select REALTEK_PHY + help + Select to enable support for Realtek Ethernet switch chips. + + Note that at least one interface driver must be enabled for the + subdrivers to be loaded. Moreover, an interface driver cannot achieve + anything without at least one subdriver enabled. + +config NET_DSA_TAG_RTL4_A + bool + help + Selected to enable support for tagging frames for the + Realtek switches with 4 byte protocol A tags, sich as found in + the Realtek RTL8366RB. + +config NET_DSA_TAG_RTL8_4 + bool + help + Selected to enable support for tagging frames for Realtek + switches with 8 byte protocol 4 tags, such as the Realtek RTL8365MB-VC. + +if DRIVER_NET_DSA_REALTEK + + +config NET_DSA_REALTEK_MDIO + tristate "Realtek MDIO interface driver" + depends on OFDEVICE + help + Select to enable support for registering switches configured + through MDIO. + +config NET_DSA_REALTEK_SMI + tristate "Realtek SMI interface driver" + depends on OFDEVICE + help + Select to enable support for registering switches connected + through SMI. + +config NET_DSA_REALTEK_RTL8365MB + tristate "Realtek RTL8365MB switch subdriver" + imply NET_DSA_REALTEK_SMI + imply NET_DSA_REALTEK_MDIO + select NET_DSA_TAG_RTL8_4 + help + Select to enable support for Realtek RTL8365MB-VC and RTL8367S. + +config NET_DSA_REALTEK_RTL8366RB + tristate "Realtek RTL8366RB switch subdriver" + imply NET_DSA_REALTEK_SMI + imply NET_DSA_REALTEK_MDIO + select NET_DSA_TAG_RTL4_A + help + Select to enable support for Realtek RTL8366RB. + +endif diff --git a/drivers/net/realtek-dsa/Makefile b/drivers/net/realtek-dsa/Makefile new file mode 100644 index 0000000000..3aafa0a8a7 --- /dev/null +++ b/drivers/net/realtek-dsa/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o tagger.o +obj-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o tagger.o +obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366rb.o +obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o +obj-$(CONFIG_NET_DSA_TAG_RTL4_A) += tag_rtl4_a.o +obj-$(CONFIG_NET_DSA_TAG_RTL8_4) += tag_rtl8_4.o diff --git a/drivers/net/realtek-dsa/dsa_priv.h b/drivers/net/realtek-dsa/dsa_priv.h new file mode 100644 index 0000000000..4cda518310 --- /dev/null +++ b/drivers/net/realtek-dsa/dsa_priv.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* SPDX-FileCopyrightText: (c) 2008-2009 Marvell Semiconductor */ + +#ifndef __DSA_PRIV_H +#define __DSA_PRIV_H + +#include <net.h> +#include <linux/string.h> + + +/* Helper for removing DSA header tags from packets in the RX path. + * Must not be called before skb_pull(len). + * + * Before: + * packet + * | + * v + * | | | | | | | | | | | | | | | | | | | + * +-----------------------+-----------------------+---------------+-------+ + * | Destination MAC | Source MAC | DSA header | EType | + * +-----------------------+-----------------------+---------------+-------+ + * | | + * <----- len -----> + * After: + * + * <----- len -----> + * | + * >>>>>>> v + * >>>>>>> | | | | | | | | | | | | | | | + * >>>>>>> +-----------------------+-----------------------+-------+ + * >>>>>>> | Destination MAC | Source MAC | EType | + * +-----------------------+-----------------------+-------+ + * + */ +static inline void dsa_strip_etype_header(void *packet, int len) +{ + memmove(packet + len, packet, 2 * ETH_ALEN); +} + +/* Helper for creating space for DSA header tags in TX path packets. + * + * Before: + * + * <<<<<<< | | | | | | | | | | | | | | | + * ^ <<<<<<< +-----------------------+-----------------------+-------+ + * | <<<<<<< | Destination MAC | Source MAC | EType | + * | +-----------------------+-----------------------+-------+ + * <----- len -----> + * | + * | + * packet + * + * After: + * + * | | | | | | | | | | | | | | | | | | | + * +-----------------------+-----------------------+---------------+-------+ + * | Destination MAC | Source MAC | DSA header | EType | + * +-----------------------+-----------------------+---------------+-------+ + * ^ | | + * | <----- len -----> + * packet + */ +static inline void dsa_alloc_etype_header(void *packet, int len) +{ + memmove(packet, packet + len, 2 * ETH_ALEN); +} + +/* On TX, skb->data points to skb_mac_header(skb), which means that EtherType + * header taggers start exactly where the EtherType is (the EtherType is + * treated as part of the DSA header). + */ +static inline void *dsa_etype_header_pos(void *packet) +{ + return packet + 2 * ETH_ALEN; +} + +#endif diff --git a/drivers/net/realtek-dsa/realtek-mdio.c b/drivers/net/realtek-dsa/realtek-mdio.c new file mode 100644 index 0000000000..4fc2295b1b --- /dev/null +++ b/drivers/net/realtek-dsa/realtek-mdio.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Realtek MDIO interface driver + * + * ASICs we intend to support with this driver: + * + * RTL8366 - The original version, apparently + * RTL8369 - Similar enough to have the same datsheet as RTL8366 + * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite + * different register layout from the other two + * RTL8366S - Is this "RTL8366 super"? + * RTL8367 - Has an OpenWRT driver as well + * RTL8368S - Seems to be an alternative name for RTL8366RB + * RTL8370 - Also uses SMI + * + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> + * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> + * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv> + * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com> + * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> + */ + +#include <of_device.h> +#include <linux/regmap.h> +#include <clock.h> +#include <linux/gpio/consumer.h> +#include <linux/printk.h> +#include <linux/mdio.h> + +#include "realtek.h" + +/* Read/write via mdiobus */ +#define REALTEK_MDIO_CTRL0_REG 31 +#define REALTEK_MDIO_START_REG 29 +#define REALTEK_MDIO_CTRL1_REG 21 +#define REALTEK_MDIO_ADDRESS_REG 23 +#define REALTEK_MDIO_DATA_WRITE_REG 24 +#define REALTEK_MDIO_DATA_READ_REG 25 + +#define REALTEK_MDIO_START_OP 0xFFFF +#define REALTEK_MDIO_ADDR_OP 0x000E +#define REALTEK_MDIO_READ_OP 0x0001 +#define REALTEK_MDIO_WRITE_OP 0x0003 + +static int realtek_mdio_write(void *ctx, u32 reg, u32 val) +{ + struct realtek_priv *priv = ctx; + struct mii_bus *bus = priv->bus; + int ret; + + ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, + REALTEK_MDIO_ADDR_OP); + if (ret) + return ret; + + ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, + reg); + if (ret) + return ret; + + ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_DATA_WRITE_REG, + val); + if (ret) + return ret; + + ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, + REALTEK_MDIO_WRITE_OP); + if (ret) + return ret; + + return 0; +} + +static int realtek_mdio_read(void *ctx, u32 reg, u32 *val) +{ + struct realtek_priv *priv = ctx; + struct mii_bus *bus = priv->bus; + int ret; + + ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL0_REG, + REALTEK_MDIO_ADDR_OP); + if (ret) + return ret; + + ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_ADDRESS_REG, + reg); + if (ret) + return ret; + + ret = mdiobus_write(bus, priv->mdio_addr, REALTEK_MDIO_CTRL1_REG, + REALTEK_MDIO_READ_OP); + if (ret) + return ret; + + ret = mdiobus_read(bus, priv->mdio_addr, REALTEK_MDIO_DATA_READ_REG); + if (ret < 0) + return ret; + + *val = ret; + + return 0; +} + +static int realtek_mdio_mdio_write(struct mii_bus *bus, int addr, int regnum, + u16 val) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_write(priv, addr, regnum, val); +} + +static int realtek_mdio_mdio_read(struct mii_bus *bus, int addr, int regnum) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_read(priv, addr, regnum); +} + +static const struct regmap_config realtek_mdio_regmap_config = { + .reg_bits = 10, /* A4..A0 R4..R0 */ + .val_bits = 16, + .reg_stride = 1, + /* PHY regs are at 0x8000 */ + .max_register = 0xffff, + .reg_format_endian = REGMAP_ENDIAN_BIG, +}; + +static const struct regmap_bus realtek_mdio_regmap_bus = { + .reg_write = realtek_mdio_write, + .reg_read = realtek_mdio_read, +}; + +static int realtek_mdio_setup_mdio(struct dsa_switch *ds) +{ + struct realtek_priv *priv = ds->priv; + struct device_node *np; + int ret; + + np = of_get_child_by_name(priv->dev->of_node, "mdio"); + if (!np) { + dev_err(priv->dev, "missing 'mdio' child node\n"); + return -ENODEV; + } + + priv->slave_mii_bus->priv = priv; + priv->slave_mii_bus->read = realtek_mdio_mdio_read; + priv->slave_mii_bus->write = realtek_mdio_mdio_write; + priv->slave_mii_bus->dev.of_node = np; + priv->slave_mii_bus->parent = priv->dev; + + ret = mdiobus_register(priv->slave_mii_bus); + if (ret) { + dev_err(priv->dev, "unable to register MDIO bus %pOF\n", np); + goto err_put_node; + } + + /* Avoid interleaved MDIO access during PHY status polling */ + slice_depends_on(mdiobus_slice(priv->slave_mii_bus), + mdiobus_slice(priv->bus)); + + return 0; + +err_put_node: + of_node_put(np); + + return ret; +} + +static int realtek_mdio_probe(struct phy_device *mdiodev) +{ + struct realtek_priv *priv; + struct device *dev = &mdiodev->dev; + const struct realtek_variant *var; + struct regmap_config rc; + struct device_node *np; + int ret; + + var = of_device_get_match_data(dev); + if (!var) + return -EINVAL; + + priv = kzalloc(sizeof(*priv) + var->chip_data_sz, GFP_KERNEL); + if (!priv) + return -ENOMEM; + + rc = realtek_mdio_regmap_config; + priv->map = regmap_init(dev, &realtek_mdio_regmap_bus, priv, &rc); + if (IS_ERR(priv->map)) { + ret = PTR_ERR(priv->map); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + priv->mdio_addr = mdiodev->addr; + priv->bus = mdiodev->bus; + priv->dev = &mdiodev->dev; + priv->chip_data = (void *)priv + sizeof(*priv); + + priv->clk_delay = var->clk_delay; + priv->cmd_read = var->cmd_read; + priv->cmd_write = var->cmd_write; + priv->ops = var->ops; + + priv->setup_interface = realtek_mdio_setup_mdio; + priv->write_reg_noack = realtek_mdio_write; + + np = dev->of_node; + + dev->priv = priv; + + /* TODO: if power is software controlled, set up any regulators here */ + priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); + + priv->reset = gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) + return dev_errp_probe(dev, priv->reset, "failed to get RESET GPIO\n"); + + if (priv->reset) { + gpiod_set_value(priv->reset, 1); + dev_dbg(dev, "asserted RESET\n"); + mdelay(REALTEK_HW_STOP_DELAY); + gpiod_set_value(priv->reset, 0); + mdelay(REALTEK_HW_START_DELAY); + dev_dbg(dev, "deasserted RESET\n"); + } + + ret = priv->ops->detect(priv); + if (ret) { + dev_err(dev, "unable to detect switch\n"); + return ret; + } + + priv->ds = kzalloc(sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; + + priv->ds->dev = dev; + priv->ds->num_ports = priv->num_ports; + priv->ds->priv = priv; + priv->ds->ops = var->ds_ops; + + ret = realtek_dsa_init_tagger(priv); + if (ret) + return ret; + + ret = dsa_register_switch(priv->ds); + if (ret) { + dev_err(priv->dev, "unable to register switch ret = %d\n", ret); + return ret; + } + + return priv->ops->setup ? priv->ops->setup(priv) : 0; +} + +static void realtek_mdio_remove(struct phy_device *mdiodev) +{ + struct realtek_priv *priv = mdiodev->dev.priv; + + /* leave the device reset asserted */ + gpiod_set_value(priv->reset, 1); +} + +static const struct of_device_id realtek_mdio_of_match[] = { +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) + { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, +#endif +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) + { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, +#endif + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, realtek_mdio_of_match); +MODULE_DEVICE_TABLE(of, realtek_mdio_of_match); + +static struct phy_driver realtek_mdio_driver = { + .drv = { + .name = "realtek-mdio", + .of_match_table = of_match_ptr(realtek_mdio_of_match), + }, + .probe = realtek_mdio_probe, + .remove = realtek_mdio_remove, +}; + +device_mdio_driver(realtek_mdio_driver); + +MODULE_AUTHOR("Luiz Angelo Daros de Luca <luizluca@gmail.com>"); +MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via MDIO interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/realtek-dsa/realtek-smi.c b/drivers/net/realtek-dsa/realtek-smi.c new file mode 100644 index 0000000000..da150dbc5d --- /dev/null +++ b/drivers/net/realtek-dsa/realtek-smi.c @@ -0,0 +1,504 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* Realtek Simple Management Interface (SMI) driver + * It can be discussed how "simple" this interface is. + * + * The SMI protocol piggy-backs the MDIO MDC and MDIO signals levels + * but the protocol is not MDIO at all. Instead it is a Realtek + * pecularity that need to bit-bang the lines in a special way to + * communicate with the switch. + * + * ASICs we intend to support with this driver: + * + * RTL8366 - The original version, apparently + * RTL8369 - Similar enough to have the same datsheet as RTL8366 + * RTL8366RB - Probably reads out "RTL8366 revision B", has a quite + * different register layout from the other two + * RTL8366S - Is this "RTL8366 super"? + * RTL8367 - Has an OpenWRT driver as well + * RTL8368S - Seems to be an alternative name for RTL8366RB + * RTL8370 - Also uses SMI + * + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> + * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> + * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv> + * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com> + * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> + */ + +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <driver.h> +#include <of.h> +#include <of_device.h> +#include <linux/mdio.h> +#include <linux/printk.h> +#include <clock.h> +#include <linux/gpio/consumer.h> +#include <driver.h> +#include <linux/regmap.h> +#include <linux/bitops.h> +#include <linux/if_bridge.h> + + +#include "realtek.h" + +#define REALTEK_SMI_ACK_RETRY_COUNT 5 + +static inline void realtek_smi_clk_delay(struct realtek_priv *priv) +{ + ndelay(priv->clk_delay); +} + +static void realtek_smi_start(struct realtek_priv *priv) +{ + /* Set GPIO pins to output mode, with initial state: + * SCK = 0, SDA = 1 + */ + gpiod_direction_output(priv->mdc, 0); + gpiod_direction_output(priv->mdio, 1); + realtek_smi_clk_delay(priv); + + /* CLK 1: 0 -> 1, 1 -> 0 */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + + /* CLK 2: */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 1); +} + +static void realtek_smi_stop(struct realtek_priv *priv) +{ + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 0); + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdio, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 1); + + /* Add a click */ + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 1); + + /* Set GPIO pins to input mode */ + gpiod_direction_input(priv->mdio); + gpiod_direction_input(priv->mdc); +} + +static void realtek_smi_write_bits(struct realtek_priv *priv, u32 data, u32 len) +{ + for (; len > 0; len--) { + realtek_smi_clk_delay(priv); + + /* Prepare data */ + gpiod_set_value(priv->mdio, !!(data & (1 << (len - 1)))); + realtek_smi_clk_delay(priv); + + /* Clocking */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + gpiod_set_value(priv->mdc, 0); + } +} + +static void realtek_smi_read_bits(struct realtek_priv *priv, u32 len, u32 *data) +{ + gpiod_direction_input(priv->mdio); + + for (*data = 0; len > 0; len--) { + u32 u; + + realtek_smi_clk_delay(priv); + + /* Clocking */ + gpiod_set_value(priv->mdc, 1); + realtek_smi_clk_delay(priv); + u = !!gpiod_get_value(priv->mdio); + gpiod_set_value(priv->mdc, 0); + + *data |= (u << (len - 1)); + } + + gpiod_direction_output(priv->mdio, 0); +} + +static int realtek_smi_wait_for_ack(struct realtek_priv *priv) +{ + int retry_cnt; + + retry_cnt = 0; + do { + u32 ack; + + realtek_smi_read_bits(priv, 1, &ack); + if (ack == 0) + break; + + if (++retry_cnt > REALTEK_SMI_ACK_RETRY_COUNT) { + dev_err(priv->dev, "ACK timeout\n"); + return -ETIMEDOUT; + } + } while (1); + + return 0; +} + +static int realtek_smi_write_byte(struct realtek_priv *priv, u8 data) +{ + realtek_smi_write_bits(priv, data, 8); + return realtek_smi_wait_for_ack(priv); +} + +static int realtek_smi_write_byte_noack(struct realtek_priv *priv, u8 data) +{ + realtek_smi_write_bits(priv, data, 8); + return 0; +} + +static int realtek_smi_read_byte0(struct realtek_priv *priv, u8 *data) +{ + u32 t; + + /* Read data */ + realtek_smi_read_bits(priv, 8, &t); + *data = (t & 0xff); + + /* Send an ACK */ + realtek_smi_write_bits(priv, 0x00, 1); + + return 0; +} + +static int realtek_smi_read_byte1(struct realtek_priv *priv, u8 *data) +{ + u32 t; + + /* Read data */ + realtek_smi_read_bits(priv, 8, &t); + *data = (t & 0xff); + + /* Send an ACK */ + realtek_smi_write_bits(priv, 0x01, 1); + + return 0; +} + +static int realtek_smi_read_reg(struct realtek_priv *priv, u32 addr, u32 *data) +{ + unsigned long flags; + u8 lo = 0; + u8 hi = 0; + int ret; + + spin_lock_irqsave(&priv->lock, flags); + + realtek_smi_start(priv); + + /* Send READ command */ + ret = realtek_smi_write_byte(priv, priv->cmd_read); + if (ret) + goto out; + + /* Set ADDR[7:0] */ + ret = realtek_smi_write_byte(priv, addr & 0xff); + if (ret) + goto out; + + /* Set ADDR[15:8] */ + ret = realtek_smi_write_byte(priv, addr >> 8); + if (ret) + goto out; + + /* Read DATA[7:0] */ + realtek_smi_read_byte0(priv, &lo); + /* Read DATA[15:8] */ + realtek_smi_read_byte1(priv, &hi); + + *data = ((u32)lo) | (((u32)hi) << 8); + + ret = 0; + + out: + realtek_smi_stop(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static int realtek_smi_write_reg(struct realtek_priv *priv, + u32 addr, u32 data, bool ack) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&priv->lock, flags); + + realtek_smi_start(priv); + + /* Send WRITE command */ + ret = realtek_smi_write_byte(priv, priv->cmd_write); + if (ret) + goto out; + + /* Set ADDR[7:0] */ + ret = realtek_smi_write_byte(priv, addr & 0xff); + if (ret) + goto out; + + /* Set ADDR[15:8] */ + ret = realtek_smi_write_byte(priv, addr >> 8); + if (ret) + goto out; + + /* Write DATA[7:0] */ + ret = realtek_smi_write_byte(priv, data & 0xff); + if (ret) + goto out; + + /* Write DATA[15:8] */ + if (ack) + ret = realtek_smi_write_byte(priv, data >> 8); + else + ret = realtek_smi_write_byte_noack(priv, data >> 8); + if (ret) + goto out; + + ret = 0; + + out: + realtek_smi_stop(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +/* There is one single case when we need to use this accessor and that + * is when issueing soft reset. Since the device reset as soon as we write + * that bit, no ACK will come back for natural reasons. + */ +static int realtek_smi_write_reg_noack(void *ctx, u32 reg, u32 val) +{ + return realtek_smi_write_reg(ctx, reg, val, false); +} + +/* Regmap accessors */ + +static int realtek_smi_write(void *ctx, u32 reg, u32 val) +{ + struct realtek_priv *priv = ctx; + + return realtek_smi_write_reg(priv, reg, val, true); +} + +static int realtek_smi_read(void *ctx, u32 reg, u32 *val) +{ + struct realtek_priv *priv = ctx; + + return realtek_smi_read_reg(priv, reg, val); +} + +static const struct regmap_config realtek_smi_regmap_config = { + .reg_bits = 10, /* A4..A0 R4..R0 */ + .val_bits = 16, + .reg_stride = 1, + /* PHY regs are at 0x8000 */ + .max_register = 0xffff, + .reg_format_endian = REGMAP_ENDIAN_BIG, +}; + +static const struct regmap_bus realtek_smi_regmap_bus = { + .reg_read = realtek_smi_read, + .reg_write = realtek_smi_write, +}; + +static int realtek_smi_mdio_read(struct mii_bus *bus, int addr, int regnum) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_read(priv, addr, regnum); +} + +static int realtek_smi_mdio_write(struct mii_bus *bus, int addr, int regnum, + u16 val) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_write(priv, addr, regnum, val); +} + +static int realtek_smi_setup_mdio(struct dsa_switch *ds) +{ + struct realtek_priv *priv = ds->priv; + struct device_node *mdio_np; + int ret; + + mdio_np = of_get_compatible_child(priv->dev->of_node, "realtek,smi-mdio"); + if (!mdio_np) { + dev_err(priv->dev, "no MDIO bus node\n"); + return -ENODEV; + } + + priv->slave_mii_bus->priv = priv; + priv->slave_mii_bus->read = realtek_smi_mdio_read; + priv->slave_mii_bus->write = realtek_smi_mdio_write; + priv->slave_mii_bus->dev.of_node = mdio_np; + priv->slave_mii_bus->parent = priv->dev; + ds->slave_mii_bus = priv->slave_mii_bus; + + ret = mdiobus_register(priv->slave_mii_bus); + if (ret) { + dev_err(priv->dev, "unable to register MDIO bus %pOF\n", + mdio_np); + goto err_put_node; + } + + return 0; + +err_put_node: + of_node_put(mdio_np); + + return ret; +} + +static int realtek_smi_probe(struct device *dev) +{ + const struct realtek_variant *var; + struct realtek_priv *priv; + struct regmap_config rc; + struct device_node *np; + int ret; + + var = of_device_get_match_data(dev); + np = dev->of_node; + + priv = kzalloc(sizeof(*priv) + var->chip_data_sz, GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->chip_data = (void *)priv + sizeof(*priv); + + rc = realtek_smi_regmap_config; + priv->map = regmap_init(dev, &realtek_smi_regmap_bus, priv, &rc); + if (IS_ERR(priv->map)) { + ret = PTR_ERR(priv->map); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + /* Link forward and backward */ + priv->dev = dev; + priv->clk_delay = var->clk_delay; + priv->cmd_read = var->cmd_read; + priv->cmd_write = var->cmd_write; + priv->ops = var->ops; + + priv->setup_interface = realtek_smi_setup_mdio; + priv->write_reg_noack = realtek_smi_write_reg_noack; + + dev->priv = priv; + spin_lock_init(&priv->lock); + + /* TODO: if power is software controlled, set up any regulators here */ + + priv->reset = gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(priv->reset)) + return dev_errp_probe(dev, priv->reset, "failed to get RESET GPIO\n"); + + if (priv->reset) { + gpiod_set_value(priv->reset, 1); + dev_dbg(dev, "asserted RESET\n"); + mdelay(REALTEK_HW_STOP_DELAY); + gpiod_set_value(priv->reset, 0); + mdelay(REALTEK_HW_START_DELAY); + dev_dbg(dev, "deasserted RESET\n"); + } + + /* Fetch MDIO pins */ + priv->mdc = gpiod_get(dev, "mdc", GPIOD_OUT_LOW); + if (IS_ERR(priv->mdc)) + return dev_errp_probe(dev, priv->mdc, "failed to get MDC GPIO\n"); + + priv->mdio = gpiod_get(dev, "mdio", GPIOD_OUT_LOW); + if (IS_ERR(priv->mdio)) + return dev_errp_probe(dev, priv->mdio, "failed to get MDIO GPIO\n"); + + priv->leds_disabled = of_property_read_bool(np, "realtek,disable-leds"); + + ret = priv->ops->detect(priv); + if (ret) { + dev_err(dev, "unable to detect switch\n"); + return ret; + } + + priv->ds = kzalloc(sizeof(*priv->ds), GFP_KERNEL); + if (!priv->ds) + return -ENOMEM; + + priv->ds->dev = dev; + priv->ds->num_ports = priv->num_ports; + priv->ds->priv = priv; + priv->ds->ops = var->ds_ops; + + ret = realtek_dsa_init_tagger(priv); + if (ret) + return ret; + + ret = dsa_register_switch(priv->ds); + if (ret) { + dev_err_probe(dev, ret, "unable to register switch\n"); + return ret; + } + + return priv->ops->setup ? priv->ops->setup(priv) : 0; +} + +static void realtek_smi_remove(struct device *dev) +{ + struct realtek_priv *priv = dev->priv; + + /* leave the device reset asserted */ + gpiod_set_value(priv->reset, 1); +} + +static const struct of_device_id realtek_smi_of_match[] = { +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8366RB) + { + .compatible = "realtek,rtl8366rb", + .data = &rtl8366rb_variant, + }, +#endif +#if IS_ENABLED(CONFIG_NET_DSA_REALTEK_RTL8365MB) + { + .compatible = "realtek,rtl8365mb", + .data = &rtl8365mb_variant, + }, +#endif + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, realtek_smi_of_match); +MODULE_DEVICE_TABLE(of, realtek_smi_of_match); + +static struct driver realtek_smi_driver = { + .name = "realtek-smi", + .of_match_table = of_match_ptr(realtek_smi_of_match), + .probe = realtek_smi_probe, + .remove = realtek_smi_remove, +}; +device_platform_driver(realtek_smi_driver); + +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); +MODULE_DESCRIPTION("Driver for Realtek ethernet switch connected via SMI interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/realtek-dsa/realtek.h b/drivers/net/realtek-dsa/realtek.h new file mode 100644 index 0000000000..dbca949462 --- /dev/null +++ b/drivers/net/realtek-dsa/realtek.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Realtek SMI interface driver defines + * + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> + * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> + */ + +#ifndef _REALTEK_H +#define _REALTEK_H + +#include <linux/spinlock.h> +#include <linux/phy.h> +#include <driver.h> +#include <gpio.h> +#include <dsa.h> + +#define REALTEK_HW_STOP_DELAY 25 /* msecs */ +#define REALTEK_HW_START_DELAY 100 /* msecs */ + +struct realtek_ops; + +struct realtek_priv { + struct device *dev; + struct gpio_desc *reset; + struct gpio_desc *mdc; + struct gpio_desc *mdio; + union { + struct regmap *map; + struct regmap *map_nolock; + }; + struct mii_bus slave_mii_bus[1]; + struct mii_bus *bus; + int mdio_addr; + + unsigned int clk_delay; + u8 cmd_read; + u8 cmd_write; + spinlock_t lock; /* Locks around command writes */ + struct dsa_switch *ds; + bool leds_disabled; + + unsigned int cpu_port; + unsigned int num_ports; + + const struct realtek_ops *ops; + int (*setup_interface)(struct dsa_switch *ds); + int (*write_reg_noack)(void *ctx, u32 addr, u32 data); + + char buf[4096]; + void *chip_data; /* Per-chip extra variant data */ +}; + +/* + * struct realtek_ops - vtable for the per-SMI-chiptype operations + * @detect: detects the chiptype + */ +struct realtek_ops { + int (*detect)(struct realtek_priv *priv); + int (*reset_chip)(struct realtek_priv *priv); + int (*setup)(struct realtek_priv *priv); + void (*cleanup)(struct realtek_priv *priv); + int (*enable_port)(struct realtek_priv *priv, int port, bool enable); + int (*phy_read)(struct realtek_priv *priv, int phy, int regnum); + int (*phy_write)(struct realtek_priv *priv, int phy, int regnum, + u16 val); + enum dsa_tag_protocol (*get_tag_protocol)(struct realtek_priv *priv); + int (*change_tag_protocol)(struct realtek_priv *priv, + enum dsa_tag_protocol proto); +}; + +struct realtek_variant { + const struct dsa_switch_ops *ds_ops; + const struct realtek_ops *ops; + unsigned int clk_delay; + u8 cmd_read; + u8 cmd_write; + size_t chip_data_sz; +}; + +enum dsa_tag_protocol { + DSA_TAG_PROTO_RTL4_A = 17, + DSA_TAG_PROTO_RTL8_4 = 24, + DSA_TAG_PROTO_RTL8_4T = 25, +}; + +struct dsa_device_ops { + int (*xmit)(struct dsa_port *dp, int port, void *packet, int length); + int (*rcv)(struct dsa_switch *ds, int *portp, void *packet, int length); + unsigned int needed_headroom; + unsigned int needed_tailroom; + const char *name; + enum dsa_tag_protocol proto; +}; + +extern const struct realtek_variant rtl8366rb_variant; +extern const struct realtek_variant rtl8365mb_variant; + +int realtek_dsa_init_tagger(struct realtek_priv *priv); + +extern const struct dsa_device_ops rtl4a_netdev_ops; +extern const struct dsa_device_ops rtl8_4_netdev_ops; +extern const struct dsa_device_ops rtl8_4t_netdev_ops; + +#endif /* _REALTEK_H */ diff --git a/drivers/net/realtek-dsa/rtl8365mb.c b/drivers/net/realtek-dsa/rtl8365mb.c new file mode 100644 index 0000000000..5889982358 --- /dev/null +++ b/drivers/net/realtek-dsa/rtl8365mb.c @@ -0,0 +1,1255 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Realtek SMI subdriver for the Realtek RTL8365MB-VC ethernet switch. + * + * Copyright (C) 2021 Alvin Šipraga <alsi@bang-olufsen.dk> + * Copyright (C) 2021 Michael Rasmussen <mir@bang-olufsen.dk> + * + * The RTL8365MB-VC is a 4+1 port 10/100/1000M switch controller. It includes 4 + * integrated PHYs for the user facing ports, and an extension interface which + * can be connected to the CPU - or another PHY - via either MII, RMII, or + * RGMII. The switch is configured via the Realtek Simple Management Interface + * (SMI), which uses the MDIO/MDC lines. + * + * Below is a simplified block diagram of the chip and its relevant interfaces. + * + * .-----------------------------------. + * | | + * UTP <---------------> Giga PHY <-> PCS <-> P0 GMAC | + * UTP <---------------> Giga PHY <-> PCS <-> P1 GMAC | + * UTP <---------------> Giga PHY <-> PCS <-> P2 GMAC | + * UTP <---------------> Giga PHY <-> PCS <-> P3 GMAC | + * | | + * CPU/PHY <-MII/RMII/RGMII---> Extension <---> Extension | + * | interface 1 GMAC 1 | + * | | + * SMI driver/ <-MDC/SCL---> Management ~~~~~~~~~~~~~~ | + * EEPROM <-MDIO/SDA--> interface ~REALTEK ~~~~~ | + * | ~RTL8365MB ~~~ | + * | ~GXXXC TAIWAN~ | + * GPIO <--------------> Reset ~~~~~~~~~~~~~~ | + * | | + * Interrupt <----------> Link UP/DOWN events | + * controller | | + * '-----------------------------------' + * + * The driver uses DSA to integrate the 4 user and 1 extension ports into the + * kernel. Netdevices are created for the user ports, as are PHY devices for + * their integrated PHYs. The device tree firmware should also specify the link + * partner of the extension port - either via a fixed-link or other phy-handle. + * See the device tree bindings for more detailed information. Note that the + * driver has only been tested with a fixed-link, but in principle it should not + * matter. + * + * NOTE: Currently, only the RGMII interface is implemented in this driver. + * + * The interrupt line is asserted on link UP/DOWN events. The driver creates a + * custom irqchip to handle this interrupt and demultiplex the events by reading + * the status registers via SMI. Interrupts are then propagated to the relevant + * PHY device. + * + * The EEPROM contains initial register values which the chip will read over I2C + * upon hardware reset. It is also possible to omit the EEPROM. In both cases, + * the driver will manually reprogram some registers using jam tables to reach + * an initial state defined by the vendor driver. + * + * This Linux driver is written based on an OS-agnostic vendor driver from + * Realtek. The reference GPL-licensed sources can be found in the OpenWrt + * source tree under the name rtl8367c. The vendor driver claims to support a + * number of similar switch controllers from Realtek, but the only hardware we + * have is the RTL8365MB-VC. Moreover, there does not seem to be any chip under + * the name RTL8367C. Although one wishes that the 'C' stood for some kind of + * common hardware revision, there exist examples of chips with the suffix -VC + * which are explicitly not supported by the rtl8367c driver and which instead + * require the rtl8367d vendor driver. With all this uncertainty, the driver has + * been modestly named rtl8365mb. Future implementors may wish to rename things + * accordingly. + * + * In the same family of chips, some carry up to 8 user ports and up to 2 + * extension ports. Where possible this driver tries to make things generic, but + * more work must be done to support these configurations. According to + * documentation from Realtek, the family should include the following chips: + * + * - RTL8363NB + * - RTL8363NB-VB + * - RTL8363SC + * - RTL8363SC-VB + * - RTL8364NB + * - RTL8364NB-VB + * - RTL8365MB-VC + * - RTL8366SC + * - RTL8367RB-VB + * - RTL8367SB + * - RTL8367S + * - RTL8370MB + * - RTL8310SR + * + * Some of the register logic for these additional chips has been skipped over + * while implementing this driver. It is therefore not possible to assume that + * things will work out-of-the-box for other chips, and a careful review of the + * vendor driver may be needed to expand support. The RTL8365MB-VC seems to be + * one of the simpler chips. + */ + +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <linux/printk.h> +#include <linux/export.h> +#include <linux/regmap.h> +#include <net.h> +#include <linux/if_bridge.h> + +#include "realtek.h" + +/* Family-specific data and limits */ +#define RTL8365MB_PHYADDRMAX 7 +#define RTL8365MB_NUM_PHYREGS 32 +#define RTL8365MB_PHYREGMAX (RTL8365MB_NUM_PHYREGS - 1) +#define RTL8365MB_MAX_NUM_PORTS 11 +#define RTL8365MB_MAX_NUM_EXTINTS 3 +#define RTL8365MB_LEARN_LIMIT_MAX 2112 + +/* Chip identification registers */ +#define RTL8365MB_CHIP_ID_REG 0x1300 + +#define RTL8365MB_CHIP_VER_REG 0x1301 + +#define RTL8365MB_MAGIC_REG 0x13C2 +#define RTL8365MB_MAGIC_VALUE 0x0249 + +/* Chip reset register */ +#define RTL8365MB_CHIP_RESET_REG 0x1322 +#define RTL8365MB_CHIP_RESET_SW_MASK 0x0002 +#define RTL8365MB_CHIP_RESET_HW_MASK 0x0001 + +/* Interrupt polarity register */ +#define RTL8365MB_INTR_POLARITY_REG 0x1100 +#define RTL8365MB_INTR_POLARITY_MASK 0x0001 +#define RTL8365MB_INTR_POLARITY_HIGH 0 +#define RTL8365MB_INTR_POLARITY_LOW 1 + +/* Interrupt control/status register - enable/check specific interrupt types */ +#define RTL8365MB_INTR_CTRL_REG 0x1101 +#define RTL8365MB_INTR_STATUS_REG 0x1102 +#define RTL8365MB_INTR_SLIENT_START_2_MASK 0x1000 +#define RTL8365MB_INTR_SLIENT_START_MASK 0x0800 +#define RTL8365MB_INTR_ACL_ACTION_MASK 0x0200 +#define RTL8365MB_INTR_CABLE_DIAG_FIN_MASK 0x0100 +#define RTL8365MB_INTR_INTERRUPT_8051_MASK 0x0080 +#define RTL8365MB_INTR_LOOP_DETECTION_MASK 0x0040 +#define RTL8365MB_INTR_GREEN_TIMER_MASK 0x0020 +#define RTL8365MB_INTR_SPECIAL_CONGEST_MASK 0x0010 +#define RTL8365MB_INTR_SPEED_CHANGE_MASK 0x0008 +#define RTL8365MB_INTR_LEARN_OVER_MASK 0x0004 +#define RTL8365MB_INTR_METER_EXCEEDED_MASK 0x0002 +#define RTL8365MB_INTR_LINK_CHANGE_MASK 0x0001 +#define RTL8365MB_INTR_ALL_MASK \ + (RTL8365MB_INTR_SLIENT_START_2_MASK | \ + RTL8365MB_INTR_SLIENT_START_MASK | \ + RTL8365MB_INTR_ACL_ACTION_MASK | \ + RTL8365MB_INTR_CABLE_DIAG_FIN_MASK | \ + RTL8365MB_INTR_INTERRUPT_8051_MASK | \ + RTL8365MB_INTR_LOOP_DETECTION_MASK | \ + RTL8365MB_INTR_GREEN_TIMER_MASK | \ + RTL8365MB_INTR_SPECIAL_CONGEST_MASK | \ + RTL8365MB_INTR_SPEED_CHANGE_MASK | \ + RTL8365MB_INTR_LEARN_OVER_MASK | \ + RTL8365MB_INTR_METER_EXCEEDED_MASK | \ + RTL8365MB_INTR_LINK_CHANGE_MASK) + +/* Per-port interrupt type status registers */ +#define RTL8365MB_PORT_LINKDOWN_IND_REG 0x1106 +#define RTL8365MB_PORT_LINKDOWN_IND_MASK 0x07FF + +#define RTL8365MB_PORT_LINKUP_IND_REG 0x1107 +#define RTL8365MB_PORT_LINKUP_IND_MASK 0x07FF + +/* PHY indirect access registers */ +#define RTL8365MB_INDIRECT_ACCESS_CTRL_REG 0x1F00 +#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK 0x0002 +#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ 0 +#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE 1 +#define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK 0x0001 +#define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE 1 +#define RTL8365MB_INDIRECT_ACCESS_STATUS_REG 0x1F01 +#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG 0x1F02 +#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK GENMASK(4, 0) +#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK GENMASK(7, 5) +#define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK GENMASK(11, 8) +#define RTL8365MB_PHY_BASE 0x2000 +#define RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG 0x1F03 +#define RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG 0x1F04 + +/* PHY OCP address prefix register */ +#define RTL8365MB_GPHY_OCP_MSB_0_REG 0x1D15 +#define RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK 0x0FC0 +#define RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK 0xFC00 + +/* The PHY OCP addresses of PHY registers 0~31 start here */ +#define RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE 0xA400 + +/* External interface port mode values - used in DIGITAL_INTERFACE_SELECT */ +#define RTL8365MB_EXT_PORT_MODE_DISABLE 0 +#define RTL8365MB_EXT_PORT_MODE_RGMII 1 +#define RTL8365MB_EXT_PORT_MODE_MII_MAC 2 +#define RTL8365MB_EXT_PORT_MODE_MII_PHY 3 +#define RTL8365MB_EXT_PORT_MODE_TMII_MAC 4 +#define RTL8365MB_EXT_PORT_MODE_TMII_PHY 5 +#define RTL8365MB_EXT_PORT_MODE_GMII 6 +#define RTL8365MB_EXT_PORT_MODE_RMII_MAC 7 +#define RTL8365MB_EXT_PORT_MODE_RMII_PHY 8 +#define RTL8365MB_EXT_PORT_MODE_SGMII 9 +#define RTL8365MB_EXT_PORT_MODE_HSGMII 10 +#define RTL8365MB_EXT_PORT_MODE_1000X_100FX 11 +#define RTL8365MB_EXT_PORT_MODE_1000X 12 +#define RTL8365MB_EXT_PORT_MODE_100FX 13 + +/* External interface mode configuration registers 0~1 */ +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 0x1305 /* EXT1 */ +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 0x13C3 /* EXT2 */ +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(_extint) \ + ((_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG0 : \ + (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_SELECT_REG1 : \ + 0x0) +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(_extint) \ + (0xF << (((_extint) % 2))) +#define RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET(_extint) \ + (((_extint) % 2) * 4) + +/* External interface RGMII TX/RX delay configuration registers 0~2 */ +#define RTL8365MB_EXT_RGMXF_REG0 0x1306 /* EXT0 */ +#define RTL8365MB_EXT_RGMXF_REG1 0x1307 /* EXT1 */ +#define RTL8365MB_EXT_RGMXF_REG2 0x13C5 /* EXT2 */ +#define RTL8365MB_EXT_RGMXF_REG(_extint) \ + ((_extint) == 0 ? RTL8365MB_EXT_RGMXF_REG0 : \ + (_extint) == 1 ? RTL8365MB_EXT_RGMXF_REG1 : \ + (_extint) == 2 ? RTL8365MB_EXT_RGMXF_REG2 : \ + 0x0) +#define RTL8365MB_EXT_RGMXF_RXDELAY_MASK 0x0007 +#define RTL8365MB_EXT_RGMXF_TXDELAY_MASK 0x0008 + +/* External interface port speed values - used in DIGITAL_INTERFACE_FORCE */ +#define RTL8365MB_PORT_SPEED_10M 0 +#define RTL8365MB_PORT_SPEED_100M 1 +#define RTL8365MB_PORT_SPEED_1000M 2 + +/* External interface force configuration registers 0~2 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 0x1310 /* EXT0 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 0x1311 /* EXT1 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 0x13C4 /* EXT2 */ +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(_extint) \ + ((_extint) == 0 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG0 : \ + (_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 : \ + (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 : \ + 0x0) +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK 0x1000 +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_NWAY_MASK 0x0080 +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK 0x0040 +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK 0x0020 +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK 0x0010 +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK 0x0004 +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK 0x0003 + +/* CPU port mask register - controls which ports are treated as CPU ports */ +#define RTL8365MB_CPU_PORT_MASK_REG 0x1219 +#define RTL8365MB_CPU_PORT_MASK_MASK 0x07FF + +/* CPU control register */ +#define RTL8365MB_CPU_CTRL_REG 0x121A +#define RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK 0x0400 +#define RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK 0x0200 +#define RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK 0x0080 +#define RTL8365MB_CPU_CTRL_TAG_POSITION_MASK 0x0040 +#define RTL8365MB_CPU_CTRL_TRAP_PORT_MASK 0x0038 +#define RTL8365MB_CPU_CTRL_INSERTMODE_MASK 0x0006 +#define RTL8365MB_CPU_CTRL_EN_MASK 0x0001 + +/* Maximum packet length register */ +#define RTL8365MB_CFG0_MAX_LEN_REG 0x088C +#define RTL8365MB_CFG0_MAX_LEN_MASK 0x3FFF + +/* Port learning limit registers */ +#define RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE 0x0A20 +#define RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(_physport) \ + (RTL8365MB_LUT_PORT_LEARN_LIMIT_BASE + (_physport)) + +/* Port isolation (forwarding mask) registers */ +#define RTL8365MB_PORT_ISOLATION_REG_BASE 0x08A2 +#define RTL8365MB_PORT_ISOLATION_REG(_physport) \ + (RTL8365MB_PORT_ISOLATION_REG_BASE + (_physport)) +#define RTL8365MB_PORT_ISOLATION_MASK 0x07FF + +/* MSTP port state registers - indexed by tree instance */ +#define RTL8365MB_MSTI_CTRL_BASE 0x0A00 +#define RTL8365MB_MSTI_CTRL_REG(_msti, _physport) \ + (RTL8365MB_MSTI_CTRL_BASE + ((_msti) << 1) + ((_physport) >> 3)) +#define RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(_physport) ((_physport) << 1) +#define RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(_physport) \ + (0x3 << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET((_physport))) + +struct rtl8365mb_jam_tbl_entry { + u16 reg; + u16 val; +}; + +/* Lifted from the vendor driver sources */ +static const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_8365mb_vc[] = { + { 0x13EB, 0x15BB }, { 0x1303, 0x06D6 }, { 0x1304, 0x0700 }, + { 0x13E2, 0x003F }, { 0x13F9, 0x0090 }, { 0x121E, 0x03CA }, + { 0x1233, 0x0352 }, { 0x1237, 0x00A0 }, { 0x123A, 0x0030 }, + { 0x1239, 0x0084 }, { 0x0301, 0x1000 }, { 0x1349, 0x001F }, + { 0x18E0, 0x4004 }, { 0x122B, 0x241C }, { 0x1305, 0xC000 }, + { 0x13F0, 0x0000 }, +}; + +static const struct rtl8365mb_jam_tbl_entry rtl8365mb_init_jam_common[] = { + { 0x1200, 0x7FCB }, { 0x0884, 0x0003 }, { 0x06EB, 0x0001 }, + { 0x03Fa, 0x0007 }, { 0x08C8, 0x00C0 }, { 0x0A30, 0x020E }, + { 0x0800, 0x0000 }, { 0x0802, 0x0000 }, { 0x09DA, 0x0013 }, + { 0x1D32, 0x0002 }, +}; + +enum rtl8365mb_phy_interface_mode { + RTL8365MB_PHY_INTERFACE_MODE_INVAL = 0, + RTL8365MB_PHY_INTERFACE_MODE_INTERNAL = BIT(0), + RTL8365MB_PHY_INTERFACE_MODE_MII = BIT(1), + RTL8365MB_PHY_INTERFACE_MODE_TMII = BIT(2), + RTL8365MB_PHY_INTERFACE_MODE_RMII = BIT(3), + RTL8365MB_PHY_INTERFACE_MODE_RGMII = BIT(4), + RTL8365MB_PHY_INTERFACE_MODE_SGMII = BIT(5), + RTL8365MB_PHY_INTERFACE_MODE_HSGMII = BIT(6), +}; + +/** + * struct rtl8365mb_extint - external interface info + * @port: the port with an external interface + * @id: the external interface ID, which is either 0, 1, or 2 + * @supported_interfaces: a bitmask of supported PHY interface modes + * + * Represents a mapping: port -> { id, supported_interfaces }. To be embedded + * in &struct rtl8365mb_chip_info for every port with an external interface. + */ +struct rtl8365mb_extint { + int port; + int id; + unsigned int supported_interfaces; +}; + +/** + * struct rtl8365mb_chip_info - static chip-specific info + * @name: human-readable chip name + * @chip_id: chip identifier + * @chip_ver: chip silicon revision + * @extints: available external interfaces + * @jam_table: chip-specific initialization jam table + * @jam_size: size of the chip's jam table + * + * These data are specific to a given chip in the family of switches supported + * by this driver. When adding support for another chip in the family, a new + * chip info should be added to the rtl8365mb_chip_infos array. + */ +struct rtl8365mb_chip_info { + const char *name; + u32 chip_id; + u32 chip_ver; + const struct rtl8365mb_extint extints[RTL8365MB_MAX_NUM_EXTINTS]; + const struct rtl8365mb_jam_tbl_entry *jam_table; + size_t jam_size; +}; + +/* Chip info for each supported switch in the family */ +#define PHY_INTF(_mode) (RTL8365MB_PHY_INTERFACE_MODE_ ## _mode) +static const struct rtl8365mb_chip_info rtl8365mb_chip_infos[] = { + { + .name = "RTL8365MB-VC", + .chip_id = 0x6367, + .chip_ver = 0x0040, + .extints = { + { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) | + PHY_INTF(RMII) | PHY_INTF(RGMII) }, + }, + .jam_table = rtl8365mb_init_jam_8365mb_vc, + .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), + }, + { + .name = "RTL8367S", + .chip_id = 0x6367, + .chip_ver = 0x00A0, + .extints = { + { 6, 1, PHY_INTF(SGMII) | PHY_INTF(HSGMII) }, + { 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) | + PHY_INTF(RMII) | PHY_INTF(RGMII) }, + }, + .jam_table = rtl8365mb_init_jam_8365mb_vc, + .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), + }, + { + .name = "RTL8367RB-VB", + .chip_id = 0x6367, + .chip_ver = 0x0020, + .extints = { + { 6, 1, PHY_INTF(MII) | PHY_INTF(TMII) | + PHY_INTF(RMII) | PHY_INTF(RGMII) }, + { 7, 2, PHY_INTF(MII) | PHY_INTF(TMII) | + PHY_INTF(RMII) | PHY_INTF(RGMII) }, + }, + .jam_table = rtl8365mb_init_jam_8365mb_vc, + .jam_size = ARRAY_SIZE(rtl8365mb_init_jam_8365mb_vc), + }, +}; + +enum rtl8365mb_stp_state { + RTL8365MB_STP_STATE_DISABLED = 0, + RTL8365MB_STP_STATE_BLOCKING = 1, + RTL8365MB_STP_STATE_LEARNING = 2, + RTL8365MB_STP_STATE_FORWARDING = 3, +}; + +enum rtl8365mb_cpu_insert { + RTL8365MB_CPU_INSERT_TO_ALL = 0, + RTL8365MB_CPU_INSERT_TO_TRAPPING = 1, + RTL8365MB_CPU_INSERT_TO_NONE = 2, +}; + +enum rtl8365mb_cpu_position { + RTL8365MB_CPU_POS_AFTER_SA = 0, + RTL8365MB_CPU_POS_BEFORE_CRC = 1, +}; + +enum rtl8365mb_cpu_format { + RTL8365MB_CPU_FORMAT_8BYTES = 0, + RTL8365MB_CPU_FORMAT_4BYTES = 1, +}; + +enum rtl8365mb_cpu_rxlen { + RTL8365MB_CPU_RXLEN_72BYTES = 0, + RTL8365MB_CPU_RXLEN_64BYTES = 1, +}; + +/** + * struct rtl8365mb_cpu - CPU port configuration + * @mask: port mask of ports that parse should parse CPU tags + * @trap_port: forward trapped frames to this port + * @insert: CPU tag insertion mode in switch->CPU frames + * @position: position of CPU tag in frame + * @rx_length: minimum CPU RX length + * @format: CPU tag format + * + * Represents the CPU tagging and CPU port configuration of the switch. These + * settings are configurable at runtime. + */ +struct rtl8365mb_cpu { + u32 mask; + u32 trap_port; + enum rtl8365mb_cpu_insert insert; + enum rtl8365mb_cpu_position position; + enum rtl8365mb_cpu_rxlen rx_length; + enum rtl8365mb_cpu_format format; +}; + +/** + * struct rtl8365mb_port - private per-port data + * @priv: pointer to parent realtek_priv data + * @index: DSA port index, same as dsa_port::index + */ +struct rtl8365mb_port { + struct realtek_priv *priv; + unsigned int index; +}; + +/** + * struct rtl8365mb - driver private data + * @priv: pointer to parent realtek_priv data + * @irq: registered IRQ or zero + * @chip_info: chip-specific info about the attached switch + * @cpu: CPU tagging and CPU port configuration for this chip + * @ports: per-port data + * + * Private data for this driver. + */ +struct rtl8365mb { + struct realtek_priv *priv; + const struct rtl8365mb_chip_info *chip_info; + struct rtl8365mb_cpu cpu; + struct rtl8365mb_port ports[RTL8365MB_MAX_NUM_PORTS]; +}; + +static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv) +{ + u32 val; + + return regmap_read_poll_timeout(priv->map_nolock, + RTL8365MB_INDIRECT_ACCESS_STATUS_REG, + val, !val, 100); +} + +static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, + u32 ocp_addr) +{ + u32 val; + int ret; + + /* Set OCP prefix */ + val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr); + ret = regmap_update_bits( + priv->map_nolock, RTL8365MB_GPHY_OCP_MSB_0_REG, + RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, + FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val)); + if (ret) + return ret; + + /* Set PHY register address */ + val = RTL8365MB_PHY_BASE; + val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK, phy); + val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK, + ocp_addr >> 1); + val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK, + ocp_addr >> 6); + ret = regmap_write(priv->map_nolock, + RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val); + if (ret) + return ret; + + return 0; +} + +static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy, + u32 ocp_addr, u16 *data) +{ + u32 val; + int ret; + + ret = rtl8365mb_phy_poll_busy(priv); + if (ret) + goto out; + + ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); + if (ret) + goto out; + + /* Execute read operation */ + val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, + RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | + FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, + RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ); + ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, + val); + if (ret) + goto out; + + ret = rtl8365mb_phy_poll_busy(priv); + if (ret) + goto out; + + /* Get PHY register data */ + ret = regmap_read(priv->map_nolock, + RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, &val); + if (ret) + goto out; + + *data = val & 0xFFFF; + +out: + + return ret; +} + +static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, + u32 ocp_addr, u16 data) +{ + u32 val; + int ret; + + ret = rtl8365mb_phy_poll_busy(priv); + if (ret) + goto out; + + ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); + if (ret) + goto out; + + /* Set PHY register data */ + ret = regmap_write(priv->map_nolock, + RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, data); + if (ret) + goto out; + + /* Execute write operation */ + val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, + RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | + FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, + RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE); + ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, + val); + if (ret) + goto out; + + ret = rtl8365mb_phy_poll_busy(priv); + if (ret) + goto out; + +out: + return 0; +} + +static int rtl8365mb_phy_read(struct realtek_priv *priv, int phy, int regnum) +{ + u32 ocp_addr; + u16 val; + int ret; + + if (phy > RTL8365MB_PHYADDRMAX) + return -EINVAL; + + if (regnum > RTL8365MB_PHYREGMAX) + return -EINVAL; + + ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; + + ret = rtl8365mb_phy_ocp_read(priv, phy, ocp_addr, &val); + if (ret) { + dev_err(priv->dev, + "failed to read PHY%d reg %02x @ %04x, ret %d\n", phy, + regnum, ocp_addr, ret); + return ret; + } + + dev_dbg(priv->dev, "read PHY%d register 0x%02x @ %04x, val <- %04x\n", + phy, regnum, ocp_addr, val); + + return val; +} + +static int rtl8365mb_phy_write(struct realtek_priv *priv, int phy, int regnum, + u16 val) +{ + u32 ocp_addr; + int ret; + + if (phy > RTL8365MB_PHYADDRMAX) + return -EINVAL; + + if (regnum > RTL8365MB_PHYREGMAX) + return -EINVAL; + + ocp_addr = RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE + regnum * 2; + + ret = rtl8365mb_phy_ocp_write(priv, phy, ocp_addr, val); + if (ret) { + dev_err(priv->dev, + "failed to write PHY%d reg %02x @ %04x, ret %d\n", phy, + regnum, ocp_addr, ret); + return ret; + } + + dev_dbg(priv->dev, "write PHY%d register 0x%02x @ %04x, val -> %04x\n", + phy, regnum, ocp_addr, val); + + return 0; +} + +static const struct rtl8365mb_extint * +rtl8365mb_get_port_extint(struct realtek_priv *priv, int port) +{ + struct rtl8365mb *mb = priv->chip_data; + int i; + + for (i = 0; i < RTL8365MB_MAX_NUM_EXTINTS; i++) { + const struct rtl8365mb_extint *extint = + &mb->chip_info->extints[i]; + + if (extint->port == port) + return extint; + } + + return NULL; +} + +static enum dsa_tag_protocol +rtl8365mb_get_tag_protocol(struct realtek_priv *priv) +{ + struct rtl8365mb_cpu *cpu; + struct rtl8365mb *mb; + + mb = priv->chip_data; + cpu = &mb->cpu; + + if (cpu->position == RTL8365MB_CPU_POS_BEFORE_CRC) + return DSA_TAG_PROTO_RTL8_4T; + + return DSA_TAG_PROTO_RTL8_4; +} + +static int rtl8365mb_ext_config_rgmii(struct realtek_priv *priv, int port, + phy_interface_t interface) +{ + const struct rtl8365mb_extint *extint = + rtl8365mb_get_port_extint(priv, port); + struct device_node *dn; + struct dsa_port *dp; + int tx_delay = 0; + int rx_delay = 0; + u32 val; + int ret; + + if (!extint) + return -ENODEV; + + dp = dsa_to_port(priv->ds, port); + dn = dp->dev->device_node; + + /* Set the RGMII TX/RX delay + * + * The Realtek vendor driver indicates the following possible + * configuration settings: + * + * TX delay: + * 0 = no delay, 1 = 2 ns delay + * RX delay: + * 0 = no delay, 7 = maximum delay + * Each step is approximately 0.3 ns, so the maximum delay is about + * 2.1 ns. + * + * The vendor driver also states that this must be configured *before* + * forcing the external interface into a particular mode, which is done + * in the rtl8365mb_phylink_mac_link_{up,down} functions. + * + * Only configure an RGMII TX (resp. RX) delay if the + * tx-internal-delay-ps (resp. rx-internal-delay-ps) OF property is + * specified. We ignore the detail of the RGMII interface mode + * (RGMII_{RXID, TXID, etc.}), as this is considered to be a PHY-only + * property. + */ + if (!of_property_read_u32(dn, "tx-internal-delay-ps", &val)) { + val = val / 1000; /* convert to ns */ + + if (val == 0 || val == 2) + tx_delay = val / 2; + else + dev_warn(priv->dev, + "RGMII TX delay must be 0 or 2 ns\n"); + } + + if (!of_property_read_u32(dn, "rx-internal-delay-ps", &val)) { + val = DIV_ROUND_CLOSEST(val, 300); /* convert to 0.3 ns step */ + + if (val <= 7) + rx_delay = val; + else + dev_warn(priv->dev, + "RGMII RX delay must be 0 to 2.1 ns\n"); + } + + ret = regmap_update_bits( + priv->map, RTL8365MB_EXT_RGMXF_REG(extint->id), + RTL8365MB_EXT_RGMXF_TXDELAY_MASK | + RTL8365MB_EXT_RGMXF_RXDELAY_MASK, + FIELD_PREP(RTL8365MB_EXT_RGMXF_TXDELAY_MASK, tx_delay) | + FIELD_PREP(RTL8365MB_EXT_RGMXF_RXDELAY_MASK, rx_delay)); + if (ret) + return ret; + + ret = regmap_update_bits( + priv->map, RTL8365MB_DIGITAL_INTERFACE_SELECT_REG(extint->id), + RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_MASK(extint->id), + RTL8365MB_EXT_PORT_MODE_RGMII + << RTL8365MB_DIGITAL_INTERFACE_SELECT_MODE_OFFSET( + extint->id)); + if (ret) + return ret; + + return 0; +} + +static int rtl8365mb_ext_config_forcemode(struct realtek_priv *priv, int port, + bool link, int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + const struct rtl8365mb_extint *extint = + rtl8365mb_get_port_extint(priv, port); + u32 r_tx_pause; + u32 r_rx_pause; + u32 r_duplex; + u32 r_speed; + u32 r_link; + int val; + int ret; + + if (!extint) + return -ENODEV; + + if (link) { + /* Force the link up with the desired configuration */ + r_link = 1; + r_rx_pause = rx_pause ? 1 : 0; + r_tx_pause = tx_pause ? 1 : 0; + + if (speed == SPEED_1000) { + r_speed = RTL8365MB_PORT_SPEED_1000M; + } else if (speed == SPEED_100) { + r_speed = RTL8365MB_PORT_SPEED_100M; + } else if (speed == SPEED_10) { + r_speed = RTL8365MB_PORT_SPEED_10M; + } else { + dev_err(priv->dev, "unsupported port speed %d\n", + speed); + dump_stack(); + return -EINVAL; + } + + if (duplex == DUPLEX_FULL) { + r_duplex = 1; + } else if (duplex == DUPLEX_HALF) { + r_duplex = 0; + } else { + dev_err(priv->dev, "unsupported duplex mode %d\n", + duplex); + return -EINVAL; + } + } else { + /* Force the link down and reset any programmed configuration */ + r_link = 0; + r_tx_pause = 0; + r_rx_pause = 0; + r_speed = 0; + r_duplex = 0; + } + + val = FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK, 1) | + FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK, + r_tx_pause) | + FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK, + r_rx_pause) | + FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK, r_link) | + FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK, + r_duplex) | + FIELD_PREP(RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK, r_speed); + ret = regmap_write(priv->map, + RTL8365MB_DIGITAL_INTERFACE_FORCE_REG(extint->id), + val); + if (ret) + return ret; + + return 0; +} + +static void rtl8365mb_port_stp_state_set(struct dsa_switch *ds, int port, + u8 state) +{ + struct realtek_priv *priv = ds->priv; + enum rtl8365mb_stp_state val; + int msti = 0; + + switch (state) { + case BR_STATE_DISABLED: + val = RTL8365MB_STP_STATE_DISABLED; + break; + case BR_STATE_FORWARDING: + val = RTL8365MB_STP_STATE_FORWARDING; + break; + default: + dev_err(priv->dev, "invalid STP state: %u\n", state); + return; + } + + regmap_update_bits(priv->map, RTL8365MB_MSTI_CTRL_REG(msti, port), + RTL8365MB_MSTI_CTRL_PORT_STATE_MASK(port), + val << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET(port)); +} + +static int rtl8365mb_phylink_mac_config(struct dsa_port *dp, int port, + phy_interface_t phy_mode) +{ + struct realtek_priv *priv = dp->ds->priv; + int ret = 0; + + if (phy_interface_mode_is_rgmii(phy_mode)) { + ret = rtl8365mb_ext_config_rgmii(priv, port, phy_mode); + if (ret) + dev_err(priv->dev, + "failed to configure RGMII mode on port %d: %d\n", + port, ret); + } + + return ret; +} + +static void rtl8365mb_phylink_mac_link_down(struct dsa_port *dp, int port, + struct phy_device *phy) +{ + struct realtek_priv *priv = dp->ds->priv; + int ret; + + rtl8365mb_port_stp_state_set(dp->ds, port, BR_STATE_DISABLED); + + if (phy_interface_mode_is_rgmii(phy->interface)) { + ret = rtl8365mb_ext_config_forcemode(priv, port, false, + 0, 0, 0, 0); + if (ret) + dev_err(priv->dev, + "failed to reset forced mode on port %d: %d\n", + port, ret); + } +} + +static int rtl8365mb_phylink_mac_link_up(struct dsa_port *dp, int port, + struct phy_device *phy) +{ + struct realtek_priv *priv = dp->ds->priv; + int ret = 0; + + if (phy_interface_mode_is_rgmii(phy->interface)) { + ret = rtl8365mb_ext_config_forcemode(priv, port, true, + phy->speed, phy->duplex, + phy->pause, phy->pause); + if (ret) + dev_err(priv->dev, + "failed to force mode on port %d: %d\n", port, + ret); + } + + rtl8365mb_port_stp_state_set(dp->ds, port, BR_STATE_FORWARDING); + + return ret; +} + +static int rtl8365mb_port_set_learning(struct realtek_priv *priv, int port, + bool enable) +{ + /* Enable/disable learning by limiting the number of L2 addresses the + * port can learn. Realtek documentation states that a limit of zero + * disables learning. When enabling learning, set it to the chip's + * maximum. + */ + return regmap_write(priv->map, RTL8365MB_LUT_PORT_LEARN_LIMIT_REG(port), + enable ? RTL8365MB_LEARN_LIMIT_MAX : 0); +} + +static int rtl8365mb_port_set_isolation(struct realtek_priv *priv, int port, + u32 mask) +{ + return regmap_write(priv->map, RTL8365MB_PORT_ISOLATION_REG(port), mask); +} + +static int rtl8365mb_set_irq_enable(struct realtek_priv *priv, bool enable) +{ + return regmap_update_bits(priv->map, RTL8365MB_INTR_CTRL_REG, + RTL8365MB_INTR_LINK_CHANGE_MASK, + FIELD_PREP(RTL8365MB_INTR_LINK_CHANGE_MASK, + enable ? 1 : 0)); +} + +static int rtl8365mb_irq_disable(struct realtek_priv *priv) +{ + return rtl8365mb_set_irq_enable(priv, false); +} + +static int rtl8365mb_irq_setup(struct realtek_priv *priv) +{ + int ret; + + /* Disable the interrupt in case the chip has it enabled on reset */ + ret = rtl8365mb_irq_disable(priv); + if (ret) + return ret; + + /* Clear the interrupt status register */ + return regmap_write(priv->map, RTL8365MB_INTR_STATUS_REG, + RTL8365MB_INTR_ALL_MASK); +} + +static int rtl8365mb_cpu_config(struct realtek_priv *priv) +{ + struct rtl8365mb *mb = priv->chip_data; + struct rtl8365mb_cpu *cpu = &mb->cpu; + u32 val; + int ret; + + ret = regmap_update_bits(priv->map, RTL8365MB_CPU_PORT_MASK_REG, + RTL8365MB_CPU_PORT_MASK_MASK, + FIELD_PREP(RTL8365MB_CPU_PORT_MASK_MASK, + cpu->mask)); + if (ret) + return ret; + + val = FIELD_PREP(RTL8365MB_CPU_CTRL_EN_MASK, 1) | + FIELD_PREP(RTL8365MB_CPU_CTRL_INSERTMODE_MASK, cpu->insert) | + FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_POSITION_MASK, cpu->position) | + FIELD_PREP(RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK, cpu->rx_length) | + FIELD_PREP(RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK, cpu->format) | + FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_MASK, cpu->trap_port & 0x7) | + FIELD_PREP(RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK, + cpu->trap_port >> 3 & 0x1); + ret = regmap_write(priv->map, RTL8365MB_CPU_CTRL_REG, val); + if (ret) + return ret; + + return 0; +} + +static int rtl8365mb_change_tag_protocol(struct realtek_priv *priv, + enum dsa_tag_protocol proto) +{ + struct rtl8365mb_cpu *cpu; + struct rtl8365mb *mb; + + mb = priv->chip_data; + cpu = &mb->cpu; + + switch (proto) { + case DSA_TAG_PROTO_RTL8_4: + cpu->format = RTL8365MB_CPU_FORMAT_8BYTES; + cpu->position = RTL8365MB_CPU_POS_AFTER_SA; + break; + case DSA_TAG_PROTO_RTL8_4T: + cpu->format = RTL8365MB_CPU_FORMAT_8BYTES; + cpu->position = RTL8365MB_CPU_POS_BEFORE_CRC; + break; + /* The switch also supports a 4-byte format, similar to rtl4a but with + * the same 0x04 8-bit version and probably 8-bit port source/dest. + * There is no public doc about it. Not supported yet and it will probably + * never be. + */ + default: + return -EPROTONOSUPPORT; + } + + return rtl8365mb_cpu_config(priv); +} + +static int rtl8365mb_switch_init(struct realtek_priv *priv) +{ + struct rtl8365mb *mb = priv->chip_data; + const struct rtl8365mb_chip_info *ci; + int ret; + int i; + + ci = mb->chip_info; + + /* Do any chip-specific init jam before getting to the common stuff */ + if (ci->jam_table) { + for (i = 0; i < ci->jam_size; i++) { + ret = regmap_write(priv->map, ci->jam_table[i].reg, + ci->jam_table[i].val); + if (ret) + return ret; + } + } + + /* Common init jam */ + for (i = 0; i < ARRAY_SIZE(rtl8365mb_init_jam_common); i++) { + ret = regmap_write(priv->map, rtl8365mb_init_jam_common[i].reg, + rtl8365mb_init_jam_common[i].val); + if (ret) + return ret; + } + + return 0; +} + +static int rtl8365mb_reset_chip(struct realtek_priv *priv) +{ + u32 val; + + priv->write_reg_noack(priv, RTL8365MB_CHIP_RESET_REG, + FIELD_PREP(RTL8365MB_CHIP_RESET_HW_MASK, 1)); + + /* Realtek documentation says the chip needs 1 second to reset. Sleep + * for 100 ms before accessing any registers to prevent ACK timeouts. + */ + mdelay(100); + return regmap_read_poll_timeout(priv->map, RTL8365MB_CHIP_RESET_REG, val, + !(val & RTL8365MB_CHIP_RESET_HW_MASK), + 1e6); +} + +static int rtl8365mb_setup(struct realtek_priv *priv) +{ + struct rtl8365mb_cpu *cpu; + struct dsa_port *cpu_dp; + struct rtl8365mb *mb; + int ret; + int i; + + mb = priv->chip_data; + cpu = &mb->cpu; + + ret = rtl8365mb_reset_chip(priv); + if (ret) { + dev_err(priv->dev, "failed to reset chip: %d\n", ret); + goto out_error; + } + + /* Configure switch to vendor-defined initial state */ + ret = rtl8365mb_switch_init(priv); + if (ret) { + dev_err(priv->dev, "failed to initialize switch: %d\n", ret); + goto out_error; + } + + rtl8365mb_irq_setup(priv); + + /* Configure CPU tagging */ + dsa_switch_for_each_cpu_port(cpu_dp, priv->ds) { + cpu->mask |= BIT(cpu_dp->index); + + if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS) + cpu->trap_port = cpu_dp->index; + } + + if (cpu->mask == 0) { + dev_err(priv->dev, "no CPU port found\n"); + goto out_teardown_irq; + } + + ret = rtl8365mb_cpu_config(priv); + if (ret) + goto out_teardown_irq; + + /* Configure ports */ + for (i = 0; i < priv->num_ports; i++) { + struct rtl8365mb_port *p = &mb->ports[i]; + + /* Forward only to the CPU */ + ret = rtl8365mb_port_set_isolation(priv, i, cpu->mask); + if (ret) + goto out_teardown_irq; + + /* Disable learning */ + ret = rtl8365mb_port_set_learning(priv, i, false); + if (ret) + goto out_teardown_irq; + + /* Set the initial STP state of all ports to DISABLED, otherwise + * ports will still forward frames to the CPU despite being + * administratively down by default. + */ + rtl8365mb_port_stp_state_set(priv->ds, i, BR_STATE_DISABLED); + + /* Set up per-port private data */ + p->priv = priv; + p->index = i; + } + + /* Set maximum packet length to 1536 bytes */ + ret = regmap_update_bits(priv->map, RTL8365MB_CFG0_MAX_LEN_REG, + RTL8365MB_CFG0_MAX_LEN_MASK, + FIELD_PREP(RTL8365MB_CFG0_MAX_LEN_MASK, 1536)); + if (ret) + goto out_teardown_irq; + + if (priv->setup_interface) { + ret = priv->setup_interface(priv->ds); + if (ret) { + dev_err(priv->dev, "could not set up MDIO bus\n"); + goto out_teardown_irq; + } + } + + return 0; + +out_teardown_irq: +out_error: + return ret; +} + +static int rtl8365mb_get_chip_id_and_ver(struct regmap *map, u32 *id, u32 *ver) +{ + int ret; + + /* For some reason we have to write a magic value to an arbitrary + * register whenever accessing the chip ID/version registers. + */ + ret = regmap_write(map, RTL8365MB_MAGIC_REG, RTL8365MB_MAGIC_VALUE); + if (ret) + return ret; + + ret = regmap_read(map, RTL8365MB_CHIP_ID_REG, id); + if (ret) + return ret; + + ret = regmap_read(map, RTL8365MB_CHIP_VER_REG, ver); + if (ret) + return ret; + + /* Reset magic register */ + ret = regmap_write(map, RTL8365MB_MAGIC_REG, 0); + if (ret) + return ret; + + return 0; +} + +static int rtl8365mb_detect(struct realtek_priv *priv) +{ + struct rtl8365mb *mb = priv->chip_data; + u32 chip_id; + u32 chip_ver; + int ret; + int i; + + ret = rtl8365mb_get_chip_id_and_ver(priv->map, &chip_id, &chip_ver); + if (ret) { + dev_err(priv->dev, "failed to read chip id and version: %d\n", + ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(rtl8365mb_chip_infos); i++) { + const struct rtl8365mb_chip_info *ci = &rtl8365mb_chip_infos[i]; + + if (ci->chip_id == chip_id && ci->chip_ver == chip_ver) { + mb->chip_info = ci; + break; + } + } + + if (!mb->chip_info) { + dev_err(priv->dev, + "unrecognized switch (id=0x%04x, ver=0x%04x)\n", chip_id, + chip_ver); + return -ENODEV; + } + + dev_info(priv->dev, "found an %s switch\n", mb->chip_info->name); + + priv->num_ports = RTL8365MB_MAX_NUM_PORTS; + mb->priv = priv; + mb->cpu.trap_port = RTL8365MB_MAX_NUM_PORTS; + mb->cpu.insert = RTL8365MB_CPU_INSERT_TO_ALL; + mb->cpu.position = RTL8365MB_CPU_POS_AFTER_SA; + mb->cpu.rx_length = RTL8365MB_CPU_RXLEN_64BYTES; + mb->cpu.format = RTL8365MB_CPU_FORMAT_8BYTES; + + return 0; +} + +static const struct dsa_switch_ops rtl8365mb_switch_ops = { + .port_pre_enable = rtl8365mb_phylink_mac_config, + .port_disable = rtl8365mb_phylink_mac_link_down, + .port_enable = rtl8365mb_phylink_mac_link_up, +}; + +static const struct realtek_ops rtl8365mb_ops = { + .detect = rtl8365mb_detect, + .phy_read = rtl8365mb_phy_read, + .phy_write = rtl8365mb_phy_write, + .setup = rtl8365mb_setup, + .get_tag_protocol = rtl8365mb_get_tag_protocol, + .change_tag_protocol = rtl8365mb_change_tag_protocol, +}; + +const struct realtek_variant rtl8365mb_variant = { + .ds_ops = &rtl8365mb_switch_ops, + .ops = &rtl8365mb_ops, + .clk_delay = 10, + .cmd_read = 0xb9, + .cmd_write = 0xb8, + .chip_data_sz = sizeof(struct rtl8365mb), +}; +EXPORT_SYMBOL_GPL(rtl8365mb_variant); + +MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>"); +MODULE_DESCRIPTION("Driver for RTL8365MB-VC ethernet switch"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/realtek-dsa/rtl8366rb.c b/drivers/net/realtek-dsa/rtl8366rb.c new file mode 100644 index 0000000000..35028d319e --- /dev/null +++ b/drivers/net/realtek-dsa/rtl8366rb.c @@ -0,0 +1,1106 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Realtek SMI subdriver for the Realtek RTL8366RB ethernet switch + * + * This is a sparsely documented chip, the only viable documentation seems + * to be a patched up code drop from the vendor that appear in various + * GPL source trees. + * + * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> + * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com> + * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv> + * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com> + */ + +#include <linux/bitops.h> +#include <net.h> +#include <linux/if_bridge.h> +#include <linux/printk.h> +#include <linux/export.h> +#include <linux/regmap.h> + +#include "realtek.h" + +#define RTL8366RB_PORT_NUM_CPU 5 +#define RTL8366RB_NUM_PORTS 6 +#define RTL8366RB_PHY_NO_MAX 4 +#define RTL8366RB_PHY_ADDR_MAX 31 + +/* Switch Global Configuration register */ +#define RTL8366RB_SGCR 0x0000 +#define RTL8366RB_SGCR_EN_BC_STORM_CTRL BIT(0) +#define RTL8366RB_SGCR_MAX_LENGTH(a) ((a) << 4) +#define RTL8366RB_SGCR_MAX_LENGTH_MASK RTL8366RB_SGCR_MAX_LENGTH(0x3) +#define RTL8366RB_SGCR_MAX_LENGTH_1522 RTL8366RB_SGCR_MAX_LENGTH(0x0) +#define RTL8366RB_SGCR_MAX_LENGTH_1536 RTL8366RB_SGCR_MAX_LENGTH(0x1) +#define RTL8366RB_SGCR_MAX_LENGTH_1552 RTL8366RB_SGCR_MAX_LENGTH(0x2) +#define RTL8366RB_SGCR_MAX_LENGTH_16000 RTL8366RB_SGCR_MAX_LENGTH(0x3) +#define RTL8366RB_SGCR_EN_VLAN BIT(13) +#define RTL8366RB_SGCR_EN_VLAN_4KTB BIT(14) + +/* Port Enable Control register */ +#define RTL8366RB_PECR 0x0001 + +/* Switch per-port learning disablement register */ +#define RTL8366RB_PORT_LEARNDIS_CTRL 0x0002 + +/* Security control, actually aging register */ +#define RTL8366RB_SECURITY_CTRL 0x0003 + +#define RTL8366RB_SSCR2 0x0004 +#define RTL8366RB_SSCR2_DROP_UNKNOWN_DA BIT(0) + +/* Port Mode Control registers */ +#define RTL8366RB_PMC0 0x0005 +#define RTL8366RB_PMC0_SPI BIT(0) +#define RTL8366RB_PMC0_EN_AUTOLOAD BIT(1) +#define RTL8366RB_PMC0_PROBE BIT(2) +#define RTL8366RB_PMC0_DIS_BISR BIT(3) +#define RTL8366RB_PMC0_ADCTEST BIT(4) +#define RTL8366RB_PMC0_SRAM_DIAG BIT(5) +#define RTL8366RB_PMC0_EN_SCAN BIT(6) +#define RTL8366RB_PMC0_P4_IOMODE_SHIFT 7 +#define RTL8366RB_PMC0_P4_IOMODE_MASK GENMASK(9, 7) +#define RTL8366RB_PMC0_P5_IOMODE_SHIFT 10 +#define RTL8366RB_PMC0_P5_IOMODE_MASK GENMASK(12, 10) +#define RTL8366RB_PMC0_SDSMODE_SHIFT 13 +#define RTL8366RB_PMC0_SDSMODE_MASK GENMASK(15, 13) +#define RTL8366RB_PMC1 0x0006 + +/* Port Mirror Control Register */ +#define RTL8366RB_PMCR 0x0007 +#define RTL8366RB_PMCR_SOURCE_PORT(a) (a) +#define RTL8366RB_PMCR_SOURCE_PORT_MASK 0x000f +#define RTL8366RB_PMCR_MONITOR_PORT(a) ((a) << 4) +#define RTL8366RB_PMCR_MONITOR_PORT_MASK 0x00f0 +#define RTL8366RB_PMCR_MIRROR_RX BIT(8) +#define RTL8366RB_PMCR_MIRROR_TX BIT(9) +#define RTL8366RB_PMCR_MIRROR_SPC BIT(10) +#define RTL8366RB_PMCR_MIRROR_ISO BIT(11) + +/* bits 0..7 = port 0, bits 8..15 = port 1 */ +#define RTL8366RB_PAACR0 0x0010 +/* bits 0..7 = port 2, bits 8..15 = port 3 */ +#define RTL8366RB_PAACR1 0x0011 +/* bits 0..7 = port 4, bits 8..15 = port 5 */ +#define RTL8366RB_PAACR2 0x0012 +#define RTL8366RB_PAACR_SPEED_10M 0 +#define RTL8366RB_PAACR_SPEED_100M 1 +#define RTL8366RB_PAACR_SPEED_1000M 2 +#define RTL8366RB_PAACR_FULL_DUPLEX BIT(2) +#define RTL8366RB_PAACR_LINK_UP BIT(4) +#define RTL8366RB_PAACR_TX_PAUSE BIT(5) +#define RTL8366RB_PAACR_RX_PAUSE BIT(6) +#define RTL8366RB_PAACR_AN BIT(7) + +#define RTL8366RB_PAACR_CPU_PORT (RTL8366RB_PAACR_SPEED_1000M | \ + RTL8366RB_PAACR_FULL_DUPLEX | \ + RTL8366RB_PAACR_LINK_UP | \ + RTL8366RB_PAACR_TX_PAUSE | \ + RTL8366RB_PAACR_RX_PAUSE) + +/* bits 0..7 = port 0, bits 8..15 = port 1 */ +#define RTL8366RB_PSTAT0 0x0014 +/* bits 0..7 = port 2, bits 8..15 = port 3 */ +#define RTL8366RB_PSTAT1 0x0015 +/* bits 0..7 = port 4, bits 8..15 = port 5 */ +#define RTL8366RB_PSTAT2 0x0016 + +#define RTL8366RB_POWER_SAVING_REG 0x0021 + +/* Spanning tree status (STP) control, two bits per port per FID */ +#define RTL8366RB_STP_STATE_BASE 0x0050 /* 0x0050..0x0057 */ +#define RTL8366RB_STP_STATE_DISABLED 0x0 +#define RTL8366RB_STP_STATE_BLOCKING 0x1 +#define RTL8366RB_STP_STATE_LEARNING 0x2 +#define RTL8366RB_STP_STATE_FORWARDING 0x3 +#define RTL8366RB_STP_MASK GENMASK(1, 0) +#define RTL8366RB_STP_STATE(port, state) \ + ((state) << ((port) * 2)) +#define RTL8366RB_STP_STATE_MASK(port) \ + RTL8366RB_STP_STATE((port), RTL8366RB_STP_MASK) + +/* CPU port control reg */ +#define RTL8368RB_CPU_CTRL_REG 0x0061 +#define RTL8368RB_CPU_PORTS_MSK 0x00FF +/* Disables inserting custom tag length/type 0x8899 */ +#define RTL8368RB_CPU_NO_TAG BIT(15) + +#define RTL8366RB_SMAR0 0x0070 /* bits 0..15 */ +#define RTL8366RB_SMAR1 0x0071 /* bits 16..31 */ +#define RTL8366RB_SMAR2 0x0072 /* bits 32..47 */ + +#define RTL8366RB_RESET_CTRL_REG 0x0100 +#define RTL8366RB_CHIP_CTRL_RESET_HW BIT(0) +#define RTL8366RB_CHIP_CTRL_RESET_SW BIT(1) + +#define RTL8366RB_CHIP_ID_REG 0x0509 +#define RTL8366RB_CHIP_ID_8366 0x5937 +#define RTL8366RB_CHIP_VERSION_CTRL_REG 0x050A +#define RTL8366RB_CHIP_VERSION_MASK 0xf + +/* PHY registers control */ +#define RTL8366RB_PHY_ACCESS_CTRL_REG 0x8000 +#define RTL8366RB_PHY_CTRL_READ BIT(0) +#define RTL8366RB_PHY_CTRL_WRITE 0 +#define RTL8366RB_PHY_ACCESS_BUSY_REG 0x8001 +#define RTL8366RB_PHY_INT_BUSY BIT(0) +#define RTL8366RB_PHY_EXT_BUSY BIT(4) +#define RTL8366RB_PHY_ACCESS_DATA_REG 0x8002 +#define RTL8366RB_PHY_EXT_CTRL_REG 0x8010 +#define RTL8366RB_PHY_EXT_WRDATA_REG 0x8011 +#define RTL8366RB_PHY_EXT_RDDATA_REG 0x8012 + +#define RTL8366RB_PHY_REG_MASK 0x1f +#define RTL8366RB_PHY_PAGE_OFFSET 5 +#define RTL8366RB_PHY_PAGE_MASK (0xf << 5) +#define RTL8366RB_PHY_NO_OFFSET 9 +#define RTL8366RB_PHY_NO_MASK (0x1f << 9) + +/* VLAN Ingress Control Register 1, one bit per port. + * bit 0 .. 5 will make the switch drop ingress frames without + * VID such as untagged or priority-tagged frames for respective + * port. + * bit 6 .. 11 will make the switch drop ingress frames carrying + * a C-tag with VID != 0 for respective port. + */ +#define RTL8366RB_VLAN_INGRESS_CTRL1_REG 0x037E +#define RTL8366RB_VLAN_INGRESS_CTRL1_DROP(port) (BIT((port)) | BIT((port) + 6)) + +/* VLAN Ingress Control Register 2, one bit per port. + * bit0 .. bit5 will make the switch drop all ingress frames with + * a VLAN classification that does not include the port is in its + * member set. + */ +#define RTL8366RB_VLAN_INGRESS_CTRL2_REG 0x037f + +/* LED control registers */ +#define RTL8366RB_LED_BLINKRATE_REG 0x0430 +#define RTL8366RB_LED_BLINKRATE_MASK 0x0007 +#define RTL8366RB_LED_BLINKRATE_28MS 0x0000 +#define RTL8366RB_LED_BLINKRATE_56MS 0x0001 +#define RTL8366RB_LED_BLINKRATE_84MS 0x0002 +#define RTL8366RB_LED_BLINKRATE_111MS 0x0003 +#define RTL8366RB_LED_BLINKRATE_222MS 0x0004 +#define RTL8366RB_LED_BLINKRATE_446MS 0x0005 + +#define RTL8366RB_LED_CTRL_REG 0x0431 +#define RTL8366RB_LED_OFF 0x0 +#define RTL8366RB_LED_DUP_COL 0x1 +#define RTL8366RB_LED_LINK_ACT 0x2 +#define RTL8366RB_LED_SPD1000 0x3 +#define RTL8366RB_LED_SPD100 0x4 +#define RTL8366RB_LED_SPD10 0x5 +#define RTL8366RB_LED_SPD1000_ACT 0x6 +#define RTL8366RB_LED_SPD100_ACT 0x7 +#define RTL8366RB_LED_SPD10_ACT 0x8 +#define RTL8366RB_LED_SPD100_10_ACT 0x9 +#define RTL8366RB_LED_FIBER 0xa +#define RTL8366RB_LED_AN_FAULT 0xb +#define RTL8366RB_LED_LINK_RX 0xc +#define RTL8366RB_LED_LINK_TX 0xd +#define RTL8366RB_LED_MASTER 0xe +#define RTL8366RB_LED_FORCE 0xf +#define RTL8366RB_LED_0_1_CTRL_REG 0x0432 +#define RTL8366RB_LED_1_OFFSET 6 +#define RTL8366RB_LED_2_3_CTRL_REG 0x0433 +#define RTL8366RB_LED_3_OFFSET 6 + +#define RTL8366RB_PORT_VLAN_CTRL_BASE 0x0063 +#define RTL8366RB_PORT_VLAN_CTRL_REG(_p) \ + (RTL8366RB_PORT_VLAN_CTRL_BASE + (_p) / 4) +#define RTL8366RB_PORT_VLAN_CTRL_MASK 0xf +#define RTL8366RB_PORT_VLAN_CTRL_SHIFT(_p) (4 * ((_p) % 4)) + +#define RTL8366RB_VLAN_TABLE_READ_BASE 0x018C +#define RTL8366RB_VLAN_TABLE_WRITE_BASE 0x0185 + +#define RTL8366RB_TABLE_ACCESS_CTRL_REG 0x0180 +#define RTL8366RB_TABLE_VLAN_READ_CTRL 0x0E01 +#define RTL8366RB_TABLE_VLAN_WRITE_CTRL 0x0F01 + +#define RTL8366RB_VLAN_MC_BASE(_x) (0x0020 + (_x) * 3) + +#define RTL8366RB_PORT_LINK_STATUS_BASE 0x0014 +#define RTL8366RB_PORT_STATUS_SPEED_MASK 0x0003 +#define RTL8366RB_PORT_STATUS_DUPLEX_MASK 0x0004 +#define RTL8366RB_PORT_STATUS_LINK_MASK 0x0010 +#define RTL8366RB_PORT_STATUS_TXPAUSE_MASK 0x0020 +#define RTL8366RB_PORT_STATUS_RXPAUSE_MASK 0x0040 +#define RTL8366RB_PORT_STATUS_AN_MASK 0x0080 + +#define RTL8366RB_NUM_VLANS 16 +#define RTL8366RB_NUM_LEDGROUPS 4 +#define RTL8366RB_NUM_VIDS 4096 +#define RTL8366RB_PRIORITYMAX 7 +#define RTL8366RB_NUM_FIDS 8 +#define RTL8366RB_FIDMAX 7 + +#define RTL8366RB_PORT_1 BIT(0) /* In userspace port 0 */ +#define RTL8366RB_PORT_2 BIT(1) /* In userspace port 1 */ +#define RTL8366RB_PORT_3 BIT(2) /* In userspace port 2 */ +#define RTL8366RB_PORT_4 BIT(3) /* In userspace port 3 */ +#define RTL8366RB_PORT_5 BIT(4) /* In userspace port 4 */ + +#define RTL8366RB_PORT_CPU BIT(5) /* CPU port */ + +#define RTL8366RB_PORT_ALL (RTL8366RB_PORT_1 | \ + RTL8366RB_PORT_2 | \ + RTL8366RB_PORT_3 | \ + RTL8366RB_PORT_4 | \ + RTL8366RB_PORT_5 | \ + RTL8366RB_PORT_CPU) + +#define RTL8366RB_PORT_ALL_BUT_CPU (RTL8366RB_PORT_1 | \ + RTL8366RB_PORT_2 | \ + RTL8366RB_PORT_3 | \ + RTL8366RB_PORT_4 | \ + RTL8366RB_PORT_5) + +#define RTL8366RB_PORT_ALL_EXTERNAL (RTL8366RB_PORT_1 | \ + RTL8366RB_PORT_2 | \ + RTL8366RB_PORT_3 | \ + RTL8366RB_PORT_4) + +#define RTL8366RB_PORT_ALL_INTERNAL RTL8366RB_PORT_CPU + +/* First configuration word per member config, VID and prio */ +#define RTL8366RB_VLAN_VID_MASK 0xfff +#define RTL8366RB_VLAN_PRIORITY_SHIFT 12 +#define RTL8366RB_VLAN_PRIORITY_MASK 0x7 +/* Second configuration word per member config, member and untagged */ +#define RTL8366RB_VLAN_UNTAG_SHIFT 8 +#define RTL8366RB_VLAN_UNTAG_MASK 0xff +#define RTL8366RB_VLAN_MEMBER_MASK 0xff +/* Third config word per member config, STAG currently unused */ +#define RTL8366RB_VLAN_STAG_MBR_MASK 0xff +#define RTL8366RB_VLAN_STAG_MBR_SHIFT 8 +#define RTL8366RB_VLAN_STAG_IDX_MASK 0x7 +#define RTL8366RB_VLAN_STAG_IDX_SHIFT 5 +#define RTL8366RB_VLAN_FID_MASK 0x7 + +/* Port ingress bandwidth control */ +#define RTL8366RB_IB_BASE 0x0200 +#define RTL8366RB_IB_REG(pnum) (RTL8366RB_IB_BASE + (pnum)) +#define RTL8366RB_IB_BDTH_MASK 0x3fff +#define RTL8366RB_IB_PREIFG BIT(14) + +/* Port egress bandwidth control */ +#define RTL8366RB_EB_BASE 0x02d1 +#define RTL8366RB_EB_REG(pnum) (RTL8366RB_EB_BASE + (pnum)) +#define RTL8366RB_EB_BDTH_MASK 0x3fff +#define RTL8366RB_EB_PREIFG_REG 0x02f8 +#define RTL8366RB_EB_PREIFG BIT(9) + +#define RTL8366RB_BDTH_SW_MAX 1048512 /* 1048576? */ +#define RTL8366RB_BDTH_UNIT 64 +#define RTL8366RB_BDTH_REG_DEFAULT 16383 + +/* QOS */ +#define RTL8366RB_QOS BIT(15) +/* Include/Exclude Preamble and IFG (20 bytes). 0:Exclude, 1:Include. */ +#define RTL8366RB_QOS_DEFAULT_PREIFG 1 + +/* Interrupt handling */ +#define RTL8366RB_INTERRUPT_CONTROL_REG 0x0440 +#define RTL8366RB_INTERRUPT_POLARITY BIT(0) +#define RTL8366RB_P4_RGMII_LED BIT(2) +#define RTL8366RB_INTERRUPT_MASK_REG 0x0441 +#define RTL8366RB_INTERRUPT_LINK_CHGALL GENMASK(11, 0) +#define RTL8366RB_INTERRUPT_ACLEXCEED BIT(8) +#define RTL8366RB_INTERRUPT_STORMEXCEED BIT(9) +#define RTL8366RB_INTERRUPT_P4_FIBER BIT(12) +#define RTL8366RB_INTERRUPT_P4_UTP BIT(13) +#define RTL8366RB_INTERRUPT_VALID (RTL8366RB_INTERRUPT_LINK_CHGALL | \ + RTL8366RB_INTERRUPT_ACLEXCEED | \ + RTL8366RB_INTERRUPT_STORMEXCEED | \ + RTL8366RB_INTERRUPT_P4_FIBER | \ + RTL8366RB_INTERRUPT_P4_UTP) +#define RTL8366RB_INTERRUPT_STATUS_REG 0x0442 +#define RTL8366RB_NUM_INTERRUPT 14 /* 0..13 */ + +/* Port isolation registers */ +#define RTL8366RB_PORT_ISO_BASE 0x0F08 +#define RTL8366RB_PORT_ISO(pnum) (RTL8366RB_PORT_ISO_BASE + (pnum)) +#define RTL8366RB_PORT_ISO_EN BIT(0) +#define RTL8366RB_PORT_ISO_PORTS_MASK GENMASK(7, 1) +#define RTL8366RB_PORT_ISO_PORTS(pmask) ((pmask) << 1) + +/* bits 0..5 enable force when cleared */ +#define RTL8366RB_MAC_FORCE_CTRL_REG 0x0F11 + +#define RTL8366RB_OAM_PARSER_REG 0x0F14 +#define RTL8366RB_OAM_MULTIPLEXER_REG 0x0F15 + +#define RTL8366RB_GREEN_FEATURE_REG 0x0F51 +#define RTL8366RB_GREEN_FEATURE_MSK 0x0007 +#define RTL8366RB_GREEN_FEATURE_TX BIT(0) +#define RTL8366RB_GREEN_FEATURE_RX BIT(2) + +static void rtl8366rb_mask_irqs(struct realtek_priv *priv) +{ + int ret; + + ret = regmap_write(priv->map, RTL8366RB_INTERRUPT_MASK_REG, 0); + if (ret) + dev_err(priv->dev, "could not mask IRQ\n"); +} + +static int rtl8366rb_irq_setup(struct realtek_priv *priv) +{ + int ret; + u32 val; + + rtl8366rb_mask_irqs(priv); + + /* This clears the IRQ status register */ + ret = regmap_read(priv->map, RTL8366RB_INTERRUPT_STATUS_REG, + &val); + if (ret) + dev_err(priv->dev, "can't read interrupt status\n"); + + return ret; +} + +static int rtl8366rb_set_addr(struct realtek_priv *priv) +{ + u8 addr[ETH_ALEN]; + u16 val; + int ret; + + random_ether_addr(addr); + + dev_info(priv->dev, "set MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + val = addr[0] << 8 | addr[1]; + ret = regmap_write(priv->map, RTL8366RB_SMAR0, val); + if (ret) + return ret; + val = addr[2] << 8 | addr[3]; + ret = regmap_write(priv->map, RTL8366RB_SMAR1, val); + if (ret) + return ret; + val = addr[4] << 8 | addr[5]; + ret = regmap_write(priv->map, RTL8366RB_SMAR2, val); + if (ret) + return ret; + + return 0; +} + +/* Found in a vendor driver */ + +/* Struct for handling the jam tables' entries */ +struct rtl8366rb_jam_tbl_entry { + u16 reg; + u16 val; +}; + +/* For the "version 0" early silicon, appear in most source releases */ +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_0[] = { + {0x000B, 0x0001}, {0x03A6, 0x0100}, {0x03A7, 0x0001}, {0x02D1, 0x3FFF}, + {0x02D2, 0x3FFF}, {0x02D3, 0x3FFF}, {0x02D4, 0x3FFF}, {0x02D5, 0x3FFF}, + {0x02D6, 0x3FFF}, {0x02D7, 0x3FFF}, {0x02D8, 0x3FFF}, {0x022B, 0x0688}, + {0x022C, 0x0FAC}, {0x03D0, 0x4688}, {0x03D1, 0x01F5}, {0x0000, 0x0830}, + {0x02F9, 0x0200}, {0x02F7, 0x7FFF}, {0x02F8, 0x03FF}, {0x0080, 0x03E8}, + {0x0081, 0x00CE}, {0x0082, 0x00DA}, {0x0083, 0x0230}, {0xBE0F, 0x2000}, + {0x0231, 0x422A}, {0x0232, 0x422A}, {0x0233, 0x422A}, {0x0234, 0x422A}, + {0x0235, 0x422A}, {0x0236, 0x422A}, {0x0237, 0x422A}, {0x0238, 0x422A}, + {0x0239, 0x422A}, {0x023A, 0x422A}, {0x023B, 0x422A}, {0x023C, 0x422A}, + {0x023D, 0x422A}, {0x023E, 0x422A}, {0x023F, 0x422A}, {0x0240, 0x422A}, + {0x0241, 0x422A}, {0x0242, 0x422A}, {0x0243, 0x422A}, {0x0244, 0x422A}, + {0x0245, 0x422A}, {0x0246, 0x422A}, {0x0247, 0x422A}, {0x0248, 0x422A}, + {0x0249, 0x0146}, {0x024A, 0x0146}, {0x024B, 0x0146}, {0xBE03, 0xC961}, + {0x024D, 0x0146}, {0x024E, 0x0146}, {0x024F, 0x0146}, {0x0250, 0x0146}, + {0xBE64, 0x0226}, {0x0252, 0x0146}, {0x0253, 0x0146}, {0x024C, 0x0146}, + {0x0251, 0x0146}, {0x0254, 0x0146}, {0xBE62, 0x3FD0}, {0x0084, 0x0320}, + {0x0255, 0x0146}, {0x0256, 0x0146}, {0x0257, 0x0146}, {0x0258, 0x0146}, + {0x0259, 0x0146}, {0x025A, 0x0146}, {0x025B, 0x0146}, {0x025C, 0x0146}, + {0x025D, 0x0146}, {0x025E, 0x0146}, {0x025F, 0x0146}, {0x0260, 0x0146}, + {0x0261, 0xA23F}, {0x0262, 0x0294}, {0x0263, 0xA23F}, {0x0264, 0x0294}, + {0x0265, 0xA23F}, {0x0266, 0x0294}, {0x0267, 0xA23F}, {0x0268, 0x0294}, + {0x0269, 0xA23F}, {0x026A, 0x0294}, {0x026B, 0xA23F}, {0x026C, 0x0294}, + {0x026D, 0xA23F}, {0x026E, 0x0294}, {0x026F, 0xA23F}, {0x0270, 0x0294}, + {0x02F5, 0x0048}, {0xBE09, 0x0E00}, {0xBE1E, 0x0FA0}, {0xBE14, 0x8448}, + {0xBE15, 0x1007}, {0xBE4A, 0xA284}, {0xC454, 0x3F0B}, {0xC474, 0x3F0B}, + {0xBE48, 0x3672}, {0xBE4B, 0x17A7}, {0xBE4C, 0x0B15}, {0xBE52, 0x0EDD}, + {0xBE49, 0x8C00}, {0xBE5B, 0x785C}, {0xBE5C, 0x785C}, {0xBE5D, 0x785C}, + {0xBE61, 0x368A}, {0xBE63, 0x9B84}, {0xC456, 0xCC13}, {0xC476, 0xCC13}, + {0xBE65, 0x307D}, {0xBE6D, 0x0005}, {0xBE6E, 0xE120}, {0xBE2E, 0x7BAF}, +}; + +/* This v1 init sequence is from Belkin F5D8235 U-Boot release */ +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_1[] = { + {0x0000, 0x0830}, {0x0001, 0x8000}, {0x0400, 0x8130}, {0xBE78, 0x3C3C}, + {0x0431, 0x5432}, {0xBE37, 0x0CE4}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0}, + {0xC44C, 0x1585}, {0xC44C, 0x1185}, {0xC44C, 0x1585}, {0xC46C, 0x1585}, + {0xC46C, 0x1185}, {0xC46C, 0x1585}, {0xC451, 0x2135}, {0xC471, 0x2135}, + {0xBE10, 0x8140}, {0xBE15, 0x0007}, {0xBE6E, 0xE120}, {0xBE69, 0xD20F}, + {0xBE6B, 0x0320}, {0xBE24, 0xB000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF20}, + {0xBE21, 0x0140}, {0xBE20, 0x00BB}, {0xBE24, 0xB800}, {0xBE24, 0x0000}, + {0xBE24, 0x7000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF60}, {0xBE21, 0x0140}, + {0xBE20, 0x0077}, {0xBE24, 0x7800}, {0xBE24, 0x0000}, {0xBE2E, 0x7B7A}, + {0xBE36, 0x0CE4}, {0x02F5, 0x0048}, {0xBE77, 0x2940}, {0x000A, 0x83E0}, + {0xBE79, 0x3C3C}, {0xBE00, 0x1340}, +}; + +/* This v2 init sequence is from Belkin F5D8235 U-Boot release */ +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_2[] = { + {0x0450, 0x0000}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0431, 0x5432}, + {0xC44F, 0x6250}, {0xC46F, 0x6250}, {0xC456, 0x0C14}, {0xC476, 0x0C14}, + {0xC44C, 0x1C85}, {0xC44C, 0x1885}, {0xC44C, 0x1C85}, {0xC46C, 0x1C85}, + {0xC46C, 0x1885}, {0xC46C, 0x1C85}, {0xC44C, 0x0885}, {0xC44C, 0x0881}, + {0xC44C, 0x0885}, {0xC46C, 0x0885}, {0xC46C, 0x0881}, {0xC46C, 0x0885}, + {0xBE2E, 0x7BA7}, {0xBE36, 0x1000}, {0xBE37, 0x1000}, {0x8000, 0x0001}, + {0xBE69, 0xD50F}, {0x8000, 0x0000}, {0xBE69, 0xD50F}, {0xBE6E, 0x0320}, + {0xBE77, 0x2940}, {0xBE78, 0x3C3C}, {0xBE79, 0x3C3C}, {0xBE6E, 0xE120}, + {0x8000, 0x0001}, {0xBE15, 0x1007}, {0x8000, 0x0000}, {0xBE15, 0x1007}, + {0xBE14, 0x0448}, {0xBE1E, 0x00A0}, {0xBE10, 0x8160}, {0xBE10, 0x8140}, + {0xBE00, 0x1340}, {0x0F51, 0x0010}, +}; + +/* Appears in a DDWRT code dump */ +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_ver_3[] = { + {0x0000, 0x0830}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0431, 0x5432}, + {0x0F51, 0x0017}, {0x02F5, 0x0048}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0}, + {0xC456, 0x0C14}, {0xC476, 0x0C14}, {0xC454, 0x3F8B}, {0xC474, 0x3F8B}, + {0xC450, 0x2071}, {0xC470, 0x2071}, {0xC451, 0x226B}, {0xC471, 0x226B}, + {0xC452, 0xA293}, {0xC472, 0xA293}, {0xC44C, 0x1585}, {0xC44C, 0x1185}, + {0xC44C, 0x1585}, {0xC46C, 0x1585}, {0xC46C, 0x1185}, {0xC46C, 0x1585}, + {0xC44C, 0x0185}, {0xC44C, 0x0181}, {0xC44C, 0x0185}, {0xC46C, 0x0185}, + {0xC46C, 0x0181}, {0xC46C, 0x0185}, {0xBE24, 0xB000}, {0xBE23, 0xFF51}, + {0xBE22, 0xDF20}, {0xBE21, 0x0140}, {0xBE20, 0x00BB}, {0xBE24, 0xB800}, + {0xBE24, 0x0000}, {0xBE24, 0x7000}, {0xBE23, 0xFF51}, {0xBE22, 0xDF60}, + {0xBE21, 0x0140}, {0xBE20, 0x0077}, {0xBE24, 0x7800}, {0xBE24, 0x0000}, + {0xBE2E, 0x7BA7}, {0xBE36, 0x1000}, {0xBE37, 0x1000}, {0x8000, 0x0001}, + {0xBE69, 0xD50F}, {0x8000, 0x0000}, {0xBE69, 0xD50F}, {0xBE6B, 0x0320}, + {0xBE77, 0x2800}, {0xBE78, 0x3C3C}, {0xBE79, 0x3C3C}, {0xBE6E, 0xE120}, + {0x8000, 0x0001}, {0xBE10, 0x8140}, {0x8000, 0x0000}, {0xBE10, 0x8140}, + {0xBE15, 0x1007}, {0xBE14, 0x0448}, {0xBE1E, 0x00A0}, {0xBE10, 0x8160}, + {0xBE10, 0x8140}, {0xBE00, 0x1340}, {0x0450, 0x0000}, {0x0401, 0x0000}, +}; + +/* Belkin F5D8235 v1, "belkin,f5d8235-v1" */ +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_f5d8235[] = { + {0x0242, 0x02BF}, {0x0245, 0x02BF}, {0x0248, 0x02BF}, {0x024B, 0x02BF}, + {0x024E, 0x02BF}, {0x0251, 0x02BF}, {0x0254, 0x0A3F}, {0x0256, 0x0A3F}, + {0x0258, 0x0A3F}, {0x025A, 0x0A3F}, {0x025C, 0x0A3F}, {0x025E, 0x0A3F}, + {0x0263, 0x007C}, {0x0100, 0x0004}, {0xBE5B, 0x3500}, {0x800E, 0x200F}, + {0xBE1D, 0x0F00}, {0x8001, 0x5011}, {0x800A, 0xA2F4}, {0x800B, 0x17A3}, + {0xBE4B, 0x17A3}, {0xBE41, 0x5011}, {0xBE17, 0x2100}, {0x8000, 0x8304}, + {0xBE40, 0x8304}, {0xBE4A, 0xA2F4}, {0x800C, 0xA8D5}, {0x8014, 0x5500}, + {0x8015, 0x0004}, {0xBE4C, 0xA8D5}, {0xBE59, 0x0008}, {0xBE09, 0x0E00}, + {0xBE36, 0x1036}, {0xBE37, 0x1036}, {0x800D, 0x00FF}, {0xBE4D, 0x00FF}, +}; + +/* DGN3500, "netgear,dgn3500", "netgear,dgn3500b" */ +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_init_jam_dgn3500[] = { + {0x0000, 0x0830}, {0x0400, 0x8130}, {0x000A, 0x83ED}, {0x0F51, 0x0017}, + {0x02F5, 0x0048}, {0x02FA, 0xFFDF}, {0x02FB, 0xFFE0}, {0x0450, 0x0000}, + {0x0401, 0x0000}, {0x0431, 0x0960}, +}; + +/* This jam table activates "green ethernet", which means low power mode + * and is claimed to detect the cable length and not use more power than + * necessary, and the ports should enter power saving mode 10 seconds after + * a cable is disconnected. Seems to always be the same. + */ +static const struct rtl8366rb_jam_tbl_entry rtl8366rb_green_jam[] = { + {0xBE78, 0x323C}, {0xBE77, 0x5000}, {0xBE2E, 0x7BA7}, + {0xBE59, 0x3459}, {0xBE5A, 0x745A}, {0xBE5B, 0x785C}, + {0xBE5C, 0x785C}, {0xBE6E, 0xE120}, {0xBE79, 0x323C}, +}; + +/* Function that jams the tables in the proper registers */ +static int rtl8366rb_jam_table(const struct rtl8366rb_jam_tbl_entry *jam_table, + int jam_size, struct realtek_priv *priv, + bool write_dbg) +{ + u32 val; + int ret; + int i; + + for (i = 0; i < jam_size; i++) { + if ((jam_table[i].reg & 0xBE00) == 0xBE00) { + ret = regmap_read(priv->map, + RTL8366RB_PHY_ACCESS_BUSY_REG, + &val); + if (ret) + return ret; + if (!(val & RTL8366RB_PHY_INT_BUSY)) { + ret = regmap_write(priv->map, + RTL8366RB_PHY_ACCESS_CTRL_REG, + RTL8366RB_PHY_CTRL_WRITE); + if (ret) + return ret; + } + } + if (write_dbg) + dev_dbg(priv->dev, "jam %04x into register %04x\n", + jam_table[i].val, + jam_table[i].reg); + ret = regmap_write(priv->map, + jam_table[i].reg, + jam_table[i].val); + if (ret) + return ret; + } + return 0; +} + +static int rtl8366rb_setup(struct realtek_priv *priv) +{ + const struct rtl8366rb_jam_tbl_entry *jam_table; + u32 chip_ver = 0; + u32 chip_id = 0; + int jam_size; + u32 val; + int ret; + int i; + + ret = regmap_read(priv->map, RTL8366RB_CHIP_ID_REG, &chip_id); + if (ret) { + dev_err(priv->dev, "unable to read chip id\n"); + return ret; + } + + switch (chip_id) { + case RTL8366RB_CHIP_ID_8366: + break; + default: + dev_err(priv->dev, "unknown chip id (%04x)\n", chip_id); + return -ENODEV; + } + + ret = regmap_read(priv->map, RTL8366RB_CHIP_VERSION_CTRL_REG, + &chip_ver); + if (ret) { + dev_err(priv->dev, "unable to read chip version\n"); + return ret; + } + + dev_info(priv->dev, "RTL%04x ver %u chip found\n", + chip_id, chip_ver & RTL8366RB_CHIP_VERSION_MASK); + + /* Do the init dance using the right jam table */ + switch (chip_ver) { + case 0: + jam_table = rtl8366rb_init_jam_ver_0; + jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_0); + break; + case 1: + jam_table = rtl8366rb_init_jam_ver_1; + jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_1); + break; + case 2: + jam_table = rtl8366rb_init_jam_ver_2; + jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_2); + break; + default: + jam_table = rtl8366rb_init_jam_ver_3; + jam_size = ARRAY_SIZE(rtl8366rb_init_jam_ver_3); + break; + } + + /* Special jam tables for special routers + * TODO: are these necessary? Maintainers, please test + * without them, using just the off-the-shelf tables. + */ + if (of_machine_is_compatible("belkin,f5d8235-v1")) { + jam_table = rtl8366rb_init_jam_f5d8235; + jam_size = ARRAY_SIZE(rtl8366rb_init_jam_f5d8235); + } + if (of_machine_is_compatible("netgear,dgn3500") || + of_machine_is_compatible("netgear,dgn3500b")) { + jam_table = rtl8366rb_init_jam_dgn3500; + jam_size = ARRAY_SIZE(rtl8366rb_init_jam_dgn3500); + } + + ret = rtl8366rb_jam_table(jam_table, jam_size, priv, true); + if (ret) + return ret; + + /* Isolate all user ports so they can only send packets to itself and the CPU port */ + for (i = 0; i < RTL8366RB_PORT_NUM_CPU; i++) { + ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(i), + RTL8366RB_PORT_ISO_PORTS(BIT(RTL8366RB_PORT_NUM_CPU)) | + RTL8366RB_PORT_ISO_EN); + if (ret) + return ret; + } + /* CPU port can send packets to all ports */ + ret = regmap_write(priv->map, RTL8366RB_PORT_ISO(RTL8366RB_PORT_NUM_CPU), + RTL8366RB_PORT_ISO_PORTS(dsa_user_ports(priv->ds)) | + RTL8366RB_PORT_ISO_EN); + if (ret) + return ret; + + /* Set up the "green ethernet" feature */ + ret = rtl8366rb_jam_table(rtl8366rb_green_jam, + ARRAY_SIZE(rtl8366rb_green_jam), priv, false); + if (ret) + return ret; + + ret = regmap_write(priv->map, + RTL8366RB_GREEN_FEATURE_REG, + (chip_ver == 1) ? 0x0007 : 0x0003); + if (ret) + return ret; + + /* Vendor driver sets 0x240 in registers 0xc and 0xd (undocumented) */ + ret = regmap_write(priv->map, 0x0c, 0x240); + if (ret) + return ret; + ret = regmap_write(priv->map, 0x0d, 0x240); + if (ret) + return ret; + + /* Set some random MAC address */ + ret = rtl8366rb_set_addr(priv); + if (ret) + return ret; + + /* Enable CPU port with custom DSA tag 8899. + * + * If you set RTL8368RB_CPU_NO_TAG (bit 15) in this registers + * the custom tag is turned off. + */ + ret = regmap_update_bits(priv->map, RTL8368RB_CPU_CTRL_REG, + 0xFFFF, + BIT(priv->cpu_port)); + if (ret) + return ret; + + /* Make sure we default-enable the fixed CPU port */ + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, + BIT(priv->cpu_port), + 0); + if (ret) + return ret; + + /* Set maximum packet length to 1536 bytes */ + ret = regmap_update_bits(priv->map, RTL8366RB_SGCR, + RTL8366RB_SGCR_MAX_LENGTH_MASK, + RTL8366RB_SGCR_MAX_LENGTH_1536); + if (ret) + return ret; + + /* Disable learning for all ports */ + ret = regmap_write(priv->map, RTL8366RB_PORT_LEARNDIS_CTRL, + RTL8366RB_PORT_ALL); + if (ret) + return ret; + + /* Enable auto ageing for all ports */ + ret = regmap_write(priv->map, RTL8366RB_SECURITY_CTRL, 0); + if (ret) + return ret; + + /* Port 4 setup: this enables Port 4, usually the WAN port, + * common PHY IO mode is apparently mode 0, and this is not what + * the port is initialized to. There is no explanation of the + * IO modes in the Realtek source code, if your WAN port is + * connected to something exotic such as fiber, then this might + * be worth experimenting with. + */ + ret = regmap_update_bits(priv->map, RTL8366RB_PMC0, + RTL8366RB_PMC0_P4_IOMODE_MASK, + 0 << RTL8366RB_PMC0_P4_IOMODE_SHIFT); + if (ret) + return ret; + + /* Accept all packets by default, we enable filtering on-demand */ + ret = regmap_write(priv->map, RTL8366RB_VLAN_INGRESS_CTRL1_REG, + 0); + if (ret) + return ret; + ret = regmap_write(priv->map, RTL8366RB_VLAN_INGRESS_CTRL2_REG, + 0); + if (ret) + return ret; + + /* Don't drop packets whose DA has not been learned */ + ret = regmap_update_bits(priv->map, RTL8366RB_SSCR2, + RTL8366RB_SSCR2_DROP_UNKNOWN_DA, 0); + if (ret) + return ret; + + /* Set blinking, TODO: make this configurable */ + ret = regmap_update_bits(priv->map, RTL8366RB_LED_BLINKRATE_REG, + RTL8366RB_LED_BLINKRATE_MASK, + RTL8366RB_LED_BLINKRATE_56MS); + if (ret) + return ret; + + /* Set up LED activity: + * Each port has 4 LEDs, we configure all ports to the same + * behaviour (no individual config) but we can set up each + * LED separately. + */ + if (priv->leds_disabled) { + /* Turn everything off */ + regmap_update_bits(priv->map, + RTL8366RB_LED_0_1_CTRL_REG, + 0x0FFF, 0); + regmap_update_bits(priv->map, + RTL8366RB_LED_2_3_CTRL_REG, + 0x0FFF, 0); + regmap_update_bits(priv->map, + RTL8366RB_INTERRUPT_CONTROL_REG, + RTL8366RB_P4_RGMII_LED, + 0); + val = RTL8366RB_LED_OFF; + } else { + /* TODO: make this configurable per LED */ + val = RTL8366RB_LED_FORCE; + } + for (i = 0; i < 4; i++) { + ret = regmap_update_bits(priv->map, + RTL8366RB_LED_CTRL_REG, + 0xf << (i * 4), + val << (i * 4)); + if (ret) + return ret; + } + + // TODO: Untested: We'll assume POR defaults to suffice for our usecase + // rtl8366_reset_vlan(priv); + + rtl8366rb_irq_setup(priv); + + if (priv->setup_interface) { + ret = priv->setup_interface(priv->ds); + if (ret) { + dev_err(priv->dev, "could not set up MDIO bus\n"); + return -ENODEV; + } + } + + return 0; +} + +static enum dsa_tag_protocol rtl8366_get_tag_protocol(struct realtek_priv *priv) +{ + /* This switch uses the 4 byte protocol A Realtek DSA tag */ + return DSA_TAG_PROTO_RTL4_A; +} + +static void +rtl8366rb_mac_link_up(struct dsa_switch *ds, int port) +{ + struct realtek_priv *priv = ds->priv; + int ret; + + if (port != priv->cpu_port) + return; + + dev_dbg(priv->dev, "MAC link up on CPU port (%d)\n", port); + + /* Force the fixed CPU port into 1Gbit mode, no autonegotiation */ + ret = regmap_update_bits(priv->map, RTL8366RB_MAC_FORCE_CTRL_REG, + BIT(port), BIT(port)); + if (ret) { + dev_err(priv->dev, "failed to force 1Gbit on CPU port\n"); + return; + } + + ret = regmap_update_bits(priv->map, RTL8366RB_PAACR2, + 0xFF00U, + RTL8366RB_PAACR_CPU_PORT << 8); + if (ret) { + dev_err(priv->dev, "failed to set PAACR on CPU port\n"); + return; + } + + /* Enable the CPU port */ + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), + 0); + if (ret) { + dev_err(priv->dev, "failed to enable the CPU port\n"); + return; + } +} + +static void +rtl8366rb_mac_link_down(struct dsa_switch *ds, int port) +{ + struct realtek_priv *priv = ds->priv; + int ret; + + if (port != priv->cpu_port) + return; + + dev_dbg(priv->dev, "MAC link down on CPU port (%d)\n", port); + + /* Disable the CPU port */ + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), + BIT(port)); + if (ret) { + dev_err(priv->dev, "failed to disable the CPU port\n"); + return; + } +} + +static void rb8366rb_set_port_led(struct realtek_priv *priv, + int port, bool enable) +{ + u16 val = enable ? 0x3f : 0; + int ret; + + if (priv->leds_disabled) + return; + + switch (port) { + case 0: + ret = regmap_update_bits(priv->map, + RTL8366RB_LED_0_1_CTRL_REG, + 0x3F, val); + break; + case 1: + ret = regmap_update_bits(priv->map, + RTL8366RB_LED_0_1_CTRL_REG, + 0x3F << RTL8366RB_LED_1_OFFSET, + val << RTL8366RB_LED_1_OFFSET); + break; + case 2: + ret = regmap_update_bits(priv->map, + RTL8366RB_LED_2_3_CTRL_REG, + 0x3F, val); + break; + case 3: + ret = regmap_update_bits(priv->map, + RTL8366RB_LED_2_3_CTRL_REG, + 0x3F << RTL8366RB_LED_3_OFFSET, + val << RTL8366RB_LED_3_OFFSET); + break; + case 4: + ret = regmap_update_bits(priv->map, + RTL8366RB_INTERRUPT_CONTROL_REG, + RTL8366RB_P4_RGMII_LED, + enable ? RTL8366RB_P4_RGMII_LED : 0); + break; + default: + dev_err(priv->dev, "no LED for port %d\n", port); + return; + } + if (ret) + dev_err(priv->dev, "error updating LED on port %d\n", port); +} + +static void +rtl8366rb_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +{ + struct realtek_priv *priv = ds->priv; + u32 val; + int i; + + switch (state) { + case BR_STATE_DISABLED: + val = RTL8366RB_STP_STATE_DISABLED; + break; + case BR_STATE_FORWARDING: + val = RTL8366RB_STP_STATE_FORWARDING; + break; + default: + dev_err(priv->dev, "unknown bridge state requested\n"); + return; + } + + /* Set the same status for the port on all the FIDs */ + for (i = 0; i < RTL8366RB_NUM_FIDS; i++) { + regmap_update_bits(priv->map, RTL8366RB_STP_STATE_BASE + i, + RTL8366RB_STP_STATE_MASK(port), + RTL8366RB_STP_STATE(port, val)); + } +} + +static int +rtl8366rb_port_enable(struct dsa_port *dp, int port, + struct phy_device *phy) +{ + struct realtek_priv *priv = dp->ds->priv; + int ret; + + rtl8366rb_mac_link_up(dp->ds, port); + + dev_dbg(priv->dev, "enable port %d\n", port); + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), + 0); + if (ret) + return ret; + + rb8366rb_set_port_led(priv, port, true); + + rtl8366rb_port_stp_state_set(dp->ds, port, BR_STATE_FORWARDING); + + return 0; +} + +static void +rtl8366rb_port_disable(struct dsa_port *dp, int port, + struct phy_device *phy) +{ + struct realtek_priv *priv = dp->ds->priv; + int ret; + + rtl8366rb_port_stp_state_set(dp->ds, port, BR_STATE_DISABLED); + + dev_dbg(priv->dev, "disable port %d\n", port); + ret = regmap_update_bits(priv->map, RTL8366RB_PECR, BIT(port), + BIT(port)); + if (ret) + return; + + rb8366rb_set_port_led(priv, port, false); + + rtl8366rb_mac_link_down(dp->ds, port); +} + +static int rtl8366rb_phy_read(struct realtek_priv *priv, int phy, int regnum) +{ + u32 val; + u32 reg; + int ret; + + if (phy > RTL8366RB_PHY_NO_MAX) + return -EINVAL; + + ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, + RTL8366RB_PHY_CTRL_READ); + if (ret) + goto out; + + reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum; + + ret = regmap_write(priv->map_nolock, reg, 0); + if (ret) { + dev_err(priv->dev, + "failed to write PHY%d reg %04x @ %04x, ret %d\n", + phy, regnum, reg, ret); + goto out; + } + + ret = regmap_read(priv->map_nolock, RTL8366RB_PHY_ACCESS_DATA_REG, + &val); + if (ret) + goto out; + + ret = val; + + dev_dbg(priv->dev, "read PHY%d register 0x%04x @ %08x, val <- %04x\n", + phy, regnum, reg, val); + +out: + return ret; +} + +static int rtl8366rb_phy_write(struct realtek_priv *priv, int phy, int regnum, + u16 val) +{ + u32 reg; + int ret; + + if (phy > RTL8366RB_PHY_NO_MAX) + return -EINVAL; + + ret = regmap_write(priv->map_nolock, RTL8366RB_PHY_ACCESS_CTRL_REG, + RTL8366RB_PHY_CTRL_WRITE); + if (ret) + goto out; + + reg = 0x8000 | (1 << (phy + RTL8366RB_PHY_NO_OFFSET)) | regnum; + + dev_dbg(priv->dev, "write PHY%d register 0x%04x @ %04x, val -> %04x\n", + phy, regnum, reg, val); + + ret = regmap_write(priv->map_nolock, reg, val); + if (ret) + goto out; + +out: + return ret; +} + +static int rtl8366rb_reset_chip(struct realtek_priv *priv) +{ + int timeout = 10; + u32 val; + int ret; + + priv->write_reg_noack(priv, RTL8366RB_RESET_CTRL_REG, + RTL8366RB_CHIP_CTRL_RESET_HW); + do { + udelay(20000); + ret = regmap_read(priv->map, RTL8366RB_RESET_CTRL_REG, &val); + if (ret) + return ret; + + if (!(val & RTL8366RB_CHIP_CTRL_RESET_HW)) + break; + } while (--timeout); + + if (!timeout) { + dev_err(priv->dev, "timeout waiting for the switch to reset\n"); + return -EIO; + } + + return 0; +} + +static int rtl8366rb_detect(struct realtek_priv *priv) +{ + struct device *dev = priv->dev; + int ret; + u32 val; + + /* Detect device */ + ret = regmap_read(priv->map, 0x5c, &val); + if (ret) { + dev_err(dev, "can't get chip ID (%d)\n", ret); + return ret; + } + + switch (val) { + case 0x6027: + dev_info(dev, "found an RTL8366S switch\n"); + dev_err(dev, "this switch is not yet supported, submit patches!\n"); + return -ENODEV; + case 0x5937: + dev_info(dev, "found an RTL8366RB switch\n"); + priv->cpu_port = RTL8366RB_PORT_NUM_CPU; + priv->num_ports = RTL8366RB_NUM_PORTS; + break; + default: + dev_info(dev, "found an Unknown Realtek switch (id=0x%04x)\n", + val); + break; + } + + return rtl8366rb_reset_chip(priv); +} + +static const struct dsa_switch_ops rtl8366rb_switch_ops = { + .port_enable = rtl8366rb_port_enable, + .port_disable = rtl8366rb_port_disable, +}; + +static const struct realtek_ops rtl8366rb_ops = { + .detect = rtl8366rb_detect, + .phy_read = rtl8366rb_phy_read, + .phy_write = rtl8366rb_phy_write, + .setup = rtl8366rb_setup, + .get_tag_protocol = rtl8366_get_tag_protocol, +}; + +const struct realtek_variant rtl8366rb_variant = { + .ds_ops = &rtl8366rb_switch_ops, + .ops = &rtl8366rb_ops, + .clk_delay = 10, + .cmd_read = 0xa9, + .cmd_write = 0xa8, +}; +EXPORT_SYMBOL_GPL(rtl8366rb_variant); + +MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); +MODULE_DESCRIPTION("Driver for RTL8366RB ethernet switch"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/realtek-dsa/tag_rtl4_a.c b/drivers/net/realtek-dsa/tag_rtl4_a.c new file mode 100644 index 0000000000..30c6a712d9 --- /dev/null +++ b/drivers/net/realtek-dsa/tag_rtl4_a.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Handler for Realtek 4 byte DSA switch tags + * Currently only supports protocol "A" found in RTL8366RB + * Copyright (c) 2020 Linus Walleij <linus.walleij@linaro.org> + * + * This "proprietary tag" header looks like so: + * + * ------------------------------------------------- + * | MAC DA | MAC SA | 0x8899 | 2 bytes tag | Type | + * ------------------------------------------------- + * + * The 2 bytes tag form a 16 bit big endian word. The exact + * meaning has been guessed from packet dumps from ingress + * frames. + */ + +#include <net.h> +#include <linux/printk.h> + +#include "realtek.h" +#include "dsa_priv.h" + +#define RTL4_A_HDR_LEN 4 +#define RTL4_A_ETHERTYPE 0x8899 +#define RTL4_A_PROTOCOL_SHIFT 12 +/* + * 0x1 = Realtek Remote Control protocol (RRCP) + * 0x2/0x3 seems to be used for loopback testing + * 0x9 = RTL8306 DSA protocol + * 0xa = RTL8366RB DSA protocol + */ +#define RTL4_A_PROTOCOL_RTL8366RB 0xa + +static int rtl4a_tag_xmit(struct dsa_port *dp, int port, void *packet, int length) +{ + struct device *dev = dp->ds->dev; + __be16 *p; + u8 *tag; + u16 out; + + /* DSA core already pads out to at least 60 bytes */ + + dev_dbg(dev, "add realtek tag to package to port %d\n", port); + + dsa_alloc_etype_header(packet, RTL4_A_HDR_LEN); + tag = dsa_etype_header_pos(packet); + + /* Set Ethertype */ + p = (__be16 *)tag; + *p = htons(RTL4_A_ETHERTYPE); + + out = (RTL4_A_PROTOCOL_RTL8366RB << RTL4_A_PROTOCOL_SHIFT); + /* The lower bits indicate the port number */ + out |= BIT(port); + + p = (__be16 *)(tag + 2); + *p = htons(out); + + return 0; +} + +static int rtl4a_tag_rcv(struct dsa_switch *ds, int *port, void *packet, int length) +{ + struct device *dev = ds->dev; + u16 protport; + __be16 *p; + u16 etype; + u8 *tag; + u8 prot; + + tag = packet + 2 * ETH_ALEN; + p = (__be16 *)tag; + etype = ntohs(*p); + if (etype != RTL4_A_ETHERTYPE) { + /* Not custom, just pass through */ + dev_dbg(dev, "non-realtek ethertype 0x%04x\n", etype); + return -EINVAL; + } + p = (__be16 *)(tag + 2); + protport = ntohs(*p); + /* The 4 upper bits are the protocol */ + prot = (protport >> RTL4_A_PROTOCOL_SHIFT) & 0x0f; + if (prot != RTL4_A_PROTOCOL_RTL8366RB) { + dev_err(dev, "unknown realtek protocol 0x%01x\n", prot); + return -EPROTO; + } + *port = protport & 0xff; + + dsa_strip_etype_header(packet, RTL4_A_HDR_LEN); + + return 0; +} + +const struct dsa_device_ops rtl4a_netdev_ops = { + .name = "rtl4a", + .proto = DSA_TAG_PROTO_RTL4_A, + .xmit = rtl4a_tag_xmit, + .rcv = rtl4a_tag_rcv, + .needed_headroom = RTL4_A_HDR_LEN, +}; + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL4_A); diff --git a/drivers/net/realtek-dsa/tag_rtl8_4.c b/drivers/net/realtek-dsa/tag_rtl8_4.c new file mode 100644 index 0000000000..80e977a65d --- /dev/null +++ b/drivers/net/realtek-dsa/tag_rtl8_4.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Handler for Realtek 8 byte switch tags + * + * Copyright (C) 2021 Alvin Šipraga <alsi@bang-olufsen.dk> + * + * NOTE: Currently only supports protocol "4" found in the RTL8365MB, hence + * named tag_rtl8_4. + * + * This tag has the following format: + * + * 0 7|8 15 + * |-----------------------------------+-----------------------------------|--- + * | (16-bit) | ^ + * | Realtek EtherType [0x8899] | | + * |-----------------------------------+-----------------------------------| 8 + * | (8-bit) | (8-bit) | + * | Protocol [0x04] | REASON | b + * |-----------------------------------+-----------------------------------| y + * | (1) | (1) | (2) | (1) | (3) | (1) | (1) | (1) | (5) | t + * | FID_EN | X | FID | PRI_EN | PRI | KEEP | X | LEARN_DIS | X | e + * |-----------------------------------+-----------------------------------| s + * | (1) | (15-bit) | | + * | ALLOW | TX/RX | v + * |-----------------------------------+-----------------------------------|--- + * + * With the following field descriptions: + * + * field | description + * ------------+------------- + * Realtek | 0x8899: indicates that this is a proprietary Realtek tag; + * EtherType | note that Realtek uses the same EtherType for + * | other incompatible tag formats (e.g. tag_rtl4_a.c) + * Protocol | 0x04: indicates that this tag conforms to this format + * X | reserved + * ------------+------------- + * REASON | reason for forwarding packet to CPU + * | 0: packet was forwarded or flooded to CPU + * | 80: packet was trapped to CPU + * FID_EN | 1: packet has an FID + * | 0: no FID + * FID | FID of packet (if FID_EN=1) + * PRI_EN | 1: force priority of packet + * | 0: don't force priority + * PRI | priority of packet (if PRI_EN=1) + * KEEP | preserve packet VLAN tag format + * LEARN_DIS | don't learn the source MAC address of the packet + * ALLOW | 1: treat TX/RX field as an allowance port mask, meaning the + * | packet may only be forwarded to ports specified in the + * | mask + * | 0: no allowance port mask, TX/RX field is the forwarding + * | port mask + * TX/RX | TX (switch->CPU): port number the packet was received on + * | RX (CPU->switch): forwarding port mask (if ALLOW=0) + * | allowance port mask (if ALLOW=1) + * + * The tag can be positioned before Ethertype, using tag "rtl8_4": + * + * +--------+--------+------------+------+----- + * | MAC DA | MAC SA | 8 byte tag | Type | ... + * +--------+--------+------------+------+----- + * + * The tag can also appear between the end of the payload and before the CRC, + * using tag "rtl8_4t": + * + * +--------+--------+------+-----+---------+------------+-----+ + * | MAC DA | MAC SA | TYPE | ... | payload | 8-byte tag | CRC | + * +--------+--------+------+-----+---------+------------+-----+ + * + * The added bytes after the payload will break most checksums, either in + * software or hardware. We don't care for checksums in barebox, so this + * is just ignored. + * + */ + +#include <linux/bitfield.h> +#include <linux/printk.h> +#include <net.h> + +#include "realtek.h" +#include "dsa_priv.h" + +/* Protocols supported: + * + * 0x04 = RTL8365MB DSA protocol + */ + +#define ETH_P_REALTEK 0x8899 + +#define RTL8_4_TAG_LEN 8 + +#define RTL8_4_PROTOCOL GENMASK(15, 8) +#define RTL8_4_PROTOCOL_RTL8365MB 0x04 +#define RTL8_4_REASON GENMASK(7, 0) +#define RTL8_4_REASON_FORWARD 0 +#define RTL8_4_REASON_TRAP 80 + +#define RTL8_4_LEARN_DIS BIT(5) + +#define RTL8_4_TX GENMASK(3, 0) +#define RTL8_4_RX GENMASK(10, 0) + +static void rtl8_4_write_tag(int port, void *tag) +{ + __be16 tag16[RTL8_4_TAG_LEN / 2]; + + /* Set Realtek EtherType */ + tag16[0] = htons(ETH_P_REALTEK); + + /* Set Protocol; zero REASON */ + tag16[1] = htons(FIELD_PREP(RTL8_4_PROTOCOL, RTL8_4_PROTOCOL_RTL8365MB)); + + /* Zero FID_EN, FID, PRI_EN, PRI, KEEP; set LEARN_DIS */ + tag16[2] = htons(FIELD_PREP(RTL8_4_LEARN_DIS, 1)); + + /* Zero ALLOW; set RX (CPU->switch) forwarding port mask */ + tag16[3] = htons(FIELD_PREP(RTL8_4_RX, BIT(port))); + + memcpy(tag, tag16, RTL8_4_TAG_LEN); +} + +static int rtl8_4_tag_xmit(struct dsa_port *dp, int port, void *packet, int length) +{ + dsa_alloc_etype_header(packet, RTL8_4_TAG_LEN); + + rtl8_4_write_tag(port, dsa_etype_header_pos(packet)); + + return 0; +} + +static int rtl8_4t_tag_xmit(struct dsa_port *dp, int port, void *packet, int length) +{ + rtl8_4_write_tag(port, packet + length - dp->ds->needed_tx_tailroom); + + return 0; +} + +static int rtl8_4_read_tag(int *port, struct device *dev, void *tag) +{ + __be16 tag16[RTL8_4_TAG_LEN / 2]; + u16 etype; + u8 proto; + + memcpy(tag16, tag, RTL8_4_TAG_LEN); + + /* Parse Realtek EtherType */ + etype = ntohs(tag16[0]); + if (unlikely(etype != ETH_P_REALTEK)) { + dev_warn(dev, "non-realtek ethertype 0x%04x\n", etype); + return -EPROTO; + } + + /* Parse Protocol */ + proto = FIELD_GET(RTL8_4_PROTOCOL, ntohs(tag16[1])); + if (unlikely(proto != RTL8_4_PROTOCOL_RTL8365MB)) { + dev_warn(dev, "unknown realtek protocol 0x%02x\n", proto); + return -EPROTO; + } + + /* Parse TX (switch->CPU) */ + *port = FIELD_GET(RTL8_4_TX, ntohs(tag16[3])); + + return 0; +} + +static int rtl8_4_tag_rcv(struct dsa_switch *ds, int *port, void *packet, int length) +{ + int ret; + + ret = rtl8_4_read_tag(port, ds->dev, dsa_etype_header_pos(packet)); + if (unlikely(ret)) + return ret; + + dsa_strip_etype_header(packet, RTL8_4_TAG_LEN); + + return 0; +} + +static int rtl8_4t_tag_rcv(struct dsa_switch *ds, int *port, void *packet, int length) +{ + return rtl8_4_read_tag(port, ds->dev, packet + length - ds->needed_rx_tailroom); +} + +/* Ethertype version */ +const struct dsa_device_ops rtl8_4_netdev_ops = { + .name = "rtl8_4", + .proto = DSA_TAG_PROTO_RTL8_4, + .xmit = rtl8_4_tag_xmit, + .rcv = rtl8_4_tag_rcv, + .needed_headroom = RTL8_4_TAG_LEN, +}; + +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4); + +/* Tail version */ +const struct dsa_device_ops rtl8_4t_netdev_ops = { + .name = "rtl8_4t", + .proto = DSA_TAG_PROTO_RTL8_4T, + .xmit = rtl8_4t_tag_xmit, + .rcv = rtl8_4t_tag_rcv, + .needed_tailroom = RTL8_4_TAG_LEN, +}; + +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_RTL8_4T); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/realtek-dsa/tagger.c b/drivers/net/realtek-dsa/tagger.c new file mode 100644 index 0000000000..3a41f3b3c1 --- /dev/null +++ b/drivers/net/realtek-dsa/tagger.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "realtek.h" + +int realtek_dsa_init_tagger(struct realtek_priv *priv) +{ + const struct dsa_device_ops *tagger_ops = NULL; + struct dsa_switch_ops *ops; + + /* TODO: Tagging can be configured per port in Linux. barebox DSA core + * will need some refactoring to do that. For now we just use the + * Linux default and leave ->change_tag_protocol unused and + * dsa-tag-protocol OF properties unheeded. + */ + switch (priv->ops->get_tag_protocol(priv)) { + case DSA_TAG_PROTO_RTL4_A: + if (IS_ENABLED(CONFIG_NET_DSA_TAG_RTL4_A)) + tagger_ops = &rtl4a_netdev_ops; + break; + case DSA_TAG_PROTO_RTL8_4: + if (IS_ENABLED(CONFIG_NET_DSA_TAG_RTL8_4)) + tagger_ops = &rtl8_4_netdev_ops; + break; + case DSA_TAG_PROTO_RTL8_4T: + if (IS_ENABLED(CONFIG_NET_DSA_TAG_RTL8_4)) + tagger_ops = &rtl8_4t_netdev_ops; + break; + default: + break; + } + + if (!tagger_ops) + return -EINVAL; + + ops = memdup(priv->ds->ops, sizeof(*priv->ds->ops)); + ops->xmit = tagger_ops->xmit; + ops->rcv = tagger_ops->rcv; + priv->ds->ops = ops; + priv->ds->needed_headroom = tagger_ops->needed_headroom; + priv->ds->needed_rx_tailroom = tagger_ops->needed_tailroom; + priv->ds->needed_tx_tailroom = tagger_ops->needed_tailroom; + + return 0; +} diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c index e1c57e6b7c..5c91c10fea 100644 --- a/drivers/net/rtl8139.c +++ b/drivers/net/rtl8139.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <dma.h> #include <net.h> @@ -9,8 +10,6 @@ #include <linux/phy.h> #include <linux/pci.h> -#include <asm/dma-mapping.h> - #define RTL8139_DEBUG #undef RTL8139_DEBUG @@ -374,7 +373,6 @@ static int rtl8139_init_dev(struct eth_device *edev) struct rtl8139_priv *priv = edev->priv; rtl8139_chip_reset(priv); - pci_set_master(priv->pci_dev); return 0; } @@ -391,6 +389,8 @@ static int rtl8139_eth_open(struct eth_device *edev) rtl8139_init_ring(priv); rtl8139_hw_start(priv); + pci_set_master(priv->pci_dev); + ret = phy_device_connect(edev, &priv->miibus, 0, NULL, 0, PHY_INTERFACE_MODE_NA); @@ -409,6 +409,11 @@ static void rtl8139_eth_halt(struct eth_device *edev) pci_clear_master(priv->pci_dev); + dma_free_coherent((void *)priv->tx_bufs, priv->tx_bufs_dma, + TX_BUF_TOT_LEN); + dma_free_coherent((void *)priv->rx_ring, priv->rx_ring_dma, + RX_BUF_TOT_LEN); + /* Green! Put the chip in low-power mode. */ RTL_W8(priv, Cfg9346, Cfg9346_Unlock); } @@ -530,7 +535,7 @@ static int rtl8139_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct eth_device *edev; struct rtl8139_priv *priv; int ret; - struct device_d *dev = &pdev->dev; + struct device *dev = &pdev->dev; /* enable pci device */ pci_enable_device(pdev); diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c deleted file mode 100644 index 80997dc89f..0000000000 --- a/drivers/net/rtl8169.c +++ /dev/null @@ -1,544 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de> - */ - -#include <common.h> -#include <dma.h> -#include <init.h> -#include <net.h> -#include <malloc.h> -#include <linux/pci.h> - -#define NUM_TX_DESC 1 -#define NUM_RX_DESC 4 -#define PKT_BUF_SIZE 1536 -#define ETH_ZLEN 60 - -struct rtl8169_chip_info { - const char *name; - u8 version; - u32 RxConfigMask; -}; - -#define BD_STAT_OWN 0x80000000 -#define BD_STAT_EOR 0x40000000 -#define BD_STAT_FS 0x20000000 -#define BD_STAT_LS 0x10000000 -#define BD_STAT_RX_RES 0x00200000 -struct bufdesc { - u32 status; - u32 vlan_tag; - u32 buf_addr; - u32 buf_Haddr; -}; - -struct rtl8169_priv { - struct eth_device edev; - void __iomem *base; - struct pci_dev *pci_dev; - int chipset; - - volatile struct bufdesc *tx_desc; - dma_addr_t tx_desc_phys; - void *tx_buf; - unsigned int cur_tx; - - volatile struct bufdesc *rx_desc; - dma_addr_t rx_desc_phys; - void *rx_buf; - unsigned int cur_rx; - - struct mii_bus miibus; -}; - -#define MAC0 0x00 -#define MAR0 0x08 -#define TxDescStartAddrLow 0x20 -#define TxDescStartAddrHigh 0x24 -#define TxHDescStartAddrLow 0x28 -#define TxHDescStartAddrHigh 0x2c -#define FLASH 0x30 -#define ERSR 0x36 -#define ChipCmd 0x37 -#define CmdReset 0x10 -#define CmdRxEnb 0x08 -#define CmdTxEnb 0x04 -#define RxBufEmpty 0x01 -#define TxPoll 0x38 -#define IntrMask 0x3c -#define IntrStatus 0x3e -#define SYSErr 0x8000 -#define PCSTimeout 0x4000 -#define SWInt 0x0100 -#define TxDescUnavail 0x80 -#define RxFIFOOver 0x40 -#define RxUnderrun 0x20 -#define RxOverflow 0x10 -#define TxErr 0x08 -#define TxOK 0x04 -#define RxErr 0x02 -#define RxOK 0x01 -#define TxConfig 0x40 -#define TxInterFrameGapShift 24 -#define TxDMAShift 8 -#define RxConfig 0x44 -#define AcceptErr 0x20 -#define AcceptRunt 0x10 -#define AcceptBroadcast 0x08 -#define AcceptMulticast 0x04 -#define AcceptMyPhys 0x02 -#define AcceptAllPhys 0x01 -#define RxCfgFIFOShift 13 -#define RxCfgDMAShift 8 -#define RxMissed 0x4c -#define Cfg9346 0x50 -#define Cfg9346_Lock 0x00 -#define Cfg9346_Unlock 0xc0 -#define Config0 0x51 -#define Config1 0x52 -#define Config2 0x53 -#define Config3 0x54 -#define Config4 0x55 -#define Config5 0x56 -#define MultiIntr 0x5c -#define PHYAR 0x60 -#define TBICSR 0x64 -#define TBI_ANAR 0x68 -#define TBI_LPAR 0x6a -#define PHYstatus 0x6c -#define RxMaxSize 0xda -#define CPlusCmd 0xe0 -#define RxDescStartAddrLow 0xe4 -#define RxDescStartAddrHigh 0xe8 -#define EarlyTxThres 0xec -#define FuncEvent 0xf0 -#define FuncEventMask 0xf4 -#define FuncPresetState 0xf8 -#define FuncForceEvent 0xfc - -/* write MMIO register */ -#define RTL_W8(priv, reg, val) writeb(val, ((char *)(priv->base) + reg)) -#define RTL_W16(priv, reg, val) writew(val, ((char *)(priv->base) + reg)) -#define RTL_W32(priv, reg, val) writel(val, ((char *)(priv->base) + reg)) - -/* read MMIO register */ -#define RTL_R8(priv, reg) readb(((char *)(priv->base) + reg)) -#define RTL_R16(priv, reg) readw(((char *)(priv->base) + reg)) -#define RTL_R32(priv, reg) readl(((char *)(priv->base) + reg)) - -static const u32 rtl8169_rx_config = - (7 << RxCfgFIFOShift) | (6 << RxCfgDMAShift); - -static void rtl8169_chip_reset(struct rtl8169_priv *priv) -{ - int i; - - /* Soft reset the chip. */ - RTL_W8(priv, ChipCmd, CmdReset); - - /* Check that the chip has finished the reset. */ - for (i = 1000; i > 0; i--) { - if ((RTL_R8(priv, ChipCmd) & CmdReset) == 0) - break; - udelay(10); - } -} - -static struct rtl8169_chip_info chip_info[] = { - {"RTL-8169", 0x00, 0xff7e1880}, - {"RTL-8169", 0x04, 0xff7e1880}, - {"RTL-8169", 0x00, 0xff7e1880}, - {"RTL-8169s/8110s", 0x02, 0xff7e1880}, - {"RTL-8169s/8110s", 0x04, 0xff7e1880}, - {"RTL-8169sb/8110sb", 0x10, 0xff7e1880}, - {"RTL-8169sc/8110sc", 0x18, 0xff7e1880}, - {"RTL-8168b/8111sb", 0x30, 0xff7e1880}, - {"RTL-8168b/8111sb", 0x38, 0xff7e1880}, - {"RTL-8168d/8111d", 0x28, 0xff7e1880}, - {"RTL-8168evl/8111evl", 0x2e, 0xff7e1880}, - {"RTL-8168/8111g", 0x4c, 0xff7e1880,}, - {"RTL-8101e", 0x34, 0xff7e1880}, - {"RTL-8100e", 0x32, 0xff7e1880}, -}; - -static void rtl8169_chip_identify(struct rtl8169_priv *priv) -{ - u32 val; - int i; - - val = RTL_R32(priv, TxConfig); - val = ((val & 0x7c000000) + ((val & 0x00800000) << 2)) >> 24; - - for (i = ARRAY_SIZE(chip_info) - 1; i >= 0; i--){ - if (val == chip_info[i].version) { - priv->chipset = i; - dev_dbg(&priv->pci_dev->dev, "found %s chipset\n", - chip_info[i].name); - return; - } - } - - dev_dbg(&priv->pci_dev->dev, - "no matching chip version found, assuming RTL-8169\n"); - priv->chipset = 0; -} - -static int rtl8169_init_dev(struct eth_device *edev) -{ - struct rtl8169_priv *priv = edev->priv; - - rtl8169_chip_reset(priv); - rtl8169_chip_identify(priv); - pci_set_master(priv->pci_dev); - - return 0; -} - -static void __set_rx_mode(struct rtl8169_priv *priv) -{ - u32 mc_filter[2], val; - - /* IFF_ALLMULTI */ - /* Too many to filter perfectly -- accept all multicasts. */ - mc_filter[1] = mc_filter[0] = 0xffffffff; - - val = AcceptBroadcast | AcceptMulticast | AcceptMyPhys | - rtl8169_rx_config | (RTL_R32(priv, RxConfig) & - chip_info[priv->chipset].RxConfigMask); - - RTL_W32(priv, RxConfig, val); - RTL_W32(priv, MAR0 + 0, mc_filter[0]); - RTL_W32(priv, MAR0 + 4, mc_filter[1]); -} - -static void rtl8169_init_ring(struct rtl8169_priv *priv) -{ - int i; - - priv->cur_rx = priv->cur_tx = 0; - - priv->tx_desc = dma_alloc_coherent(NUM_TX_DESC * - sizeof(struct bufdesc), &priv->tx_desc_phys); - priv->tx_buf = malloc(NUM_TX_DESC * PKT_BUF_SIZE); - priv->rx_desc = dma_alloc_coherent(NUM_RX_DESC * - sizeof(struct bufdesc), &priv->rx_desc_phys); - priv->rx_buf = malloc(NUM_RX_DESC * PKT_BUF_SIZE); - dma_sync_single_for_device((unsigned long)priv->rx_buf, - NUM_RX_DESC * PKT_BUF_SIZE, DMA_FROM_DEVICE); - - for (i = 0; i < NUM_RX_DESC; i++) { - if (i == (NUM_RX_DESC - 1)) - priv->rx_desc[i].status = - BD_STAT_OWN | BD_STAT_EOR | PKT_BUF_SIZE; - else - priv->rx_desc[i].status = - BD_STAT_OWN | PKT_BUF_SIZE; - - priv->rx_desc[i].buf_addr = - virt_to_phys(priv->rx_buf + i * PKT_BUF_SIZE); - } -} - -static void rtl8169_hw_start(struct rtl8169_priv *priv) -{ - u32 val; - - RTL_W8(priv, Cfg9346, Cfg9346_Unlock); - - /* RTL-8169sb/8110sb or previous version */ - if (priv->chipset <= 5) - RTL_W8(priv, ChipCmd, CmdTxEnb | CmdRxEnb); - - RTL_W8(priv, EarlyTxThres, 0x3f); - - /* For gigabit rtl8169 */ - RTL_W16(priv, RxMaxSize, 0x800); - - /* Set Rx Config register */ - val = rtl8169_rx_config | (RTL_R32(priv, RxConfig) & - chip_info[priv->chipset].RxConfigMask); - RTL_W32(priv, RxConfig, val); - - /* Set DMA burst size and Interframe Gap Time */ - RTL_W32(priv, TxConfig, (6 << TxDMAShift) | (3 << TxInterFrameGapShift)); - - RTL_W32(priv, TxDescStartAddrLow, priv->tx_desc_phys); - RTL_W32(priv, TxDescStartAddrHigh, 0); - RTL_W32(priv, RxDescStartAddrLow, priv->rx_desc_phys); - RTL_W32(priv, RxDescStartAddrHigh, 0); - - /* RTL-8169sc/8110sc or later version */ - if (priv->chipset > 5) - RTL_W8(priv, ChipCmd, CmdTxEnb | CmdRxEnb); - - RTL_W8(priv, Cfg9346, Cfg9346_Lock); - udelay(10); - - RTL_W32(priv, RxMissed, 0); - - __set_rx_mode(priv); - - /* no early-rx interrupts */ - RTL_W16(priv, MultiIntr, RTL_R16(priv, MultiIntr) & 0xf000); -} - -static int rtl8169_eth_open(struct eth_device *edev) -{ - struct rtl8169_priv *priv = edev->priv; - int ret; - - rtl8169_init_ring(priv); - rtl8169_hw_start(priv); - - ret = phy_device_connect(edev, &priv->miibus, 0, NULL, 0, - PHY_INTERFACE_MODE_NA); - - return ret; -} - -static int rtl8169_phy_write(struct mii_bus *bus, int phy_addr, - int reg, u16 val) -{ - struct rtl8169_priv *priv = bus->priv; - int i; - - if (phy_addr != 0) - return -1; - - RTL_W32(priv, PHYAR, 0x80000000 | (reg & 0xff) << 16 | val); - mdelay(1); - - for (i = 2000; i > 0; i--) { - if (!(RTL_R32(priv, PHYAR) & 0x80000000)) { - return 0; - } else { - udelay(100); - } - } - - return -1; -} - -static int rtl8169_phy_read(struct mii_bus *bus, int phy_addr, int reg) -{ - struct rtl8169_priv *priv = bus->priv; - int i, val = 0xffff; - - RTL_W32(priv, PHYAR, 0x0 | (reg & 0xff) << 16); - mdelay(10); - - if (phy_addr != 0) - return val; - - for (i = 2000; i > 0; i--) { - if (RTL_R32(priv, PHYAR) & 0x80000000) { - val = (int) (RTL_R32(priv, PHYAR) & 0xffff); - break; - } else { - udelay(100); - } - } - return val; -} - -static int rtl8169_eth_send(struct eth_device *edev, void *packet, - int packet_length) -{ - struct rtl8169_priv *priv = edev->priv; - unsigned int entry; - - entry = priv->cur_tx % NUM_TX_DESC; - - if (packet_length < ETH_ZLEN) - memset(priv->tx_buf + entry * PKT_BUF_SIZE, 0, ETH_ZLEN); - memcpy(priv->tx_buf + entry * PKT_BUF_SIZE, packet, packet_length); - dma_sync_single_for_device((unsigned long)priv->tx_buf + entry * - PKT_BUF_SIZE, PKT_BUF_SIZE, DMA_TO_DEVICE); - - priv->tx_desc[entry].buf_Haddr = 0; - priv->tx_desc[entry].buf_addr = - virt_to_phys(priv->tx_buf + entry * PKT_BUF_SIZE); - - if (entry != (NUM_TX_DESC - 1)) { - priv->tx_desc[entry].status = - BD_STAT_OWN | BD_STAT_FS | BD_STAT_LS | - ((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN); - } else { - priv->tx_desc[entry].status = - BD_STAT_OWN | BD_STAT_EOR | BD_STAT_FS | BD_STAT_LS | - ((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN); - } - - RTL_W8(priv, TxPoll, 0x40); - - while (priv->tx_desc[entry].status & BD_STAT_OWN) - ; - - dma_sync_single_for_cpu((unsigned long)priv->tx_buf + entry * - PKT_BUF_SIZE, PKT_BUF_SIZE, DMA_TO_DEVICE); - - priv->cur_tx++; - - return 0; -} - -static int rtl8169_eth_rx(struct eth_device *edev) -{ - struct rtl8169_priv *priv = edev->priv; - unsigned int entry, pkt_size = 0; - u8 status; - - entry = priv->cur_rx % NUM_RX_DESC; - - if ((priv->rx_desc[entry].status & BD_STAT_OWN) == 0) { - if (!(priv->rx_desc[entry].status & BD_STAT_RX_RES)) { - pkt_size = (priv->rx_desc[entry].status & 0x1fff) - 4; - - dma_sync_single_for_cpu((unsigned long)priv->rx_buf - + entry * PKT_BUF_SIZE, - pkt_size, DMA_FROM_DEVICE); - - net_receive(edev, priv->rx_buf + entry * PKT_BUF_SIZE, - pkt_size); - - dma_sync_single_for_device((unsigned long)priv->rx_buf - + entry * PKT_BUF_SIZE, - pkt_size, DMA_FROM_DEVICE); - - if (entry == NUM_RX_DESC - 1) - priv->rx_desc[entry].status = BD_STAT_OWN | - BD_STAT_EOR | PKT_BUF_SIZE; - else - priv->rx_desc[entry].status = - BD_STAT_OWN | PKT_BUF_SIZE; - priv->rx_desc[entry].buf_addr = - virt_to_phys(priv->rx_buf + - entry * PKT_BUF_SIZE); - } else { - dev_err(&edev->dev, "rx error\n"); - } - - priv->cur_rx++; - - return pkt_size; - - } else { - status = RTL_R8(priv, IntrStatus); - RTL_W8(priv, IntrStatus, status & ~(TxErr | RxErr | SYSErr)); - udelay(100); /* wait */ - } - - return 0; -} - -static int rtl8169_get_ethaddr(struct eth_device *edev, unsigned char *m) -{ - struct rtl8169_priv *priv = edev->priv; - int i; - - for (i = 0; i < 6; i++) { - m[i] = RTL_R8(priv, MAC0 + i); - } - - return 0; -} - -static int rtl8169_set_ethaddr(struct eth_device *edev, const unsigned char *mac_addr) -{ - struct rtl8169_priv *priv = edev->priv; - int i; - - RTL_W8(priv, Cfg9346, Cfg9346_Unlock); - - for (i = 0; i < 6; i++) { - RTL_W8(priv, (MAC0 + i), mac_addr[i]); - RTL_R8(priv, mac_addr[i]); - } - - RTL_W8(priv, Cfg9346, Cfg9346_Lock); - - return 0; -} - -static void rtl8169_eth_halt(struct eth_device *edev) -{ - struct rtl8169_priv *priv = edev->priv; - - /* Stop the chip's Tx and Rx DMA processes. */ - RTL_W8(priv, ChipCmd, 0x00); - - /* Disable interrupts by clearing the interrupt mask. */ - RTL_W16(priv, IntrMask, 0x0000); - RTL_W32(priv, RxMissed, 0); - - pci_clear_master(priv->pci_dev); -} - -static int rtl8169_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - struct device_d *dev = &pdev->dev; - struct eth_device *edev; - struct rtl8169_priv *priv; - int ret; - - /* enable pci device */ - pci_enable_device(pdev); - - priv = xzalloc(sizeof(struct rtl8169_priv)); - - edev = &priv->edev; - dev->type_data = edev; - edev->priv = priv; - - priv->pci_dev = pdev; - - priv->miibus.read = rtl8169_phy_read; - priv->miibus.write = rtl8169_phy_write; - priv->miibus.priv = priv; - priv->miibus.parent = &edev->dev; - - priv->base = pci_iomap(pdev, pdev->device == 0x8168 ? 2 : 1); - - dev_dbg(dev, "rtl%04x (rev %02x) (base=%p)\n", - pdev->device, pdev->revision, priv->base); - - edev->init = rtl8169_init_dev; - edev->open = rtl8169_eth_open; - edev->send = rtl8169_eth_send; - edev->recv = rtl8169_eth_rx; - edev->get_ethaddr = rtl8169_get_ethaddr; - edev->set_ethaddr = rtl8169_set_ethaddr; - edev->halt = rtl8169_eth_halt; - edev->parent = dev; - ret = eth_register(edev); - if (ret) - goto eth_err; - - ret = mdiobus_register(&priv->miibus); - if (ret) - goto mdio_err; - - return 0; - -mdio_err: - eth_unregister(edev); - -eth_err: - free(priv); - - return ret; -} -static DEFINE_PCI_DEVICE_TABLE(rtl8169_pci_tbl) = { - { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8167), }, - { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8168), }, - { PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8169), }, - { /* sentinel */ } -}; - -static struct pci_driver rtl8169_eth_driver = { - .name = "rtl8169_eth", - .id_table = rtl8169_pci_tbl, - .probe = rtl8169_probe, -}; -device_pci_driver(rtl8169_eth_driver); diff --git a/drivers/net/sja1105.c b/drivers/net/sja1105.c new file mode 100644 index 0000000000..d88a5e2fcf --- /dev/null +++ b/drivers/net/sja1105.c @@ -0,0 +1,2998 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright 2016-2018 NXP + * Copyright 2018, Sensor-Technik Wiedemann GmbH + * Copyright 2018-2019, Vladimir Oltean <olteanv@gmail.com> + * Copyright 2020-2021 NXP + * + * Ported from Linux (drivers/net/dsa/sja1105/). + * Ported from U-boot .... + */ + +#include <common.h> +#include <dsa.h> +#include <linux/gpio/consumer.h> +#include <linux/bitrev.h> +#include <linux/if_vlan.h> +#include <net.h> +#include <of_device.h> +#include <spi/spi.h> + +enum packing_op { + PACK, + UNPACK, +}; + +#define ETHER_CRC32_POLY 0x04C11DB7 +#define ETH_P_SJA1105 0xdadb +#define SJA1105_NUM_PORTS 5 +#define SJA1110_NUM_PORTS 11 +#define SJA1105_MAX_NUM_PORTS SJA1110_NUM_PORTS +#define SJA1105_NUM_TC 8 +#define SJA1105ET_FDB_BIN_SIZE 4 +#define SJA1105_SIZE_CGU_CMD 4 +#define SJA1105_SIZE_RESET_CMD 4 +#define SJA1105_SIZE_MDIO_CMD 4 +#define SJA1105_SIZE_SPI_MSG_HEADER 4 +#define SJA1105_SIZE_SPI_MSG_MAXLEN (64 * 4) +#define SJA1105_SIZE_DEVICE_ID 4 +#define SJA1105_SIZE_TABLE_HEADER 12 +#define SJA1105_SIZE_L2_POLICING_ENTRY 8 +#define SJA1105_SIZE_VLAN_LOOKUP_ENTRY 8 +#define SJA1110_SIZE_VLAN_LOOKUP_ENTRY 12 +#define SJA1105_SIZE_L2_FORWARDING_ENTRY 8 +#define SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY 12 +#define SJA1105_SIZE_XMII_PARAMS_ENTRY 4 +#define SJA1110_SIZE_XMII_PARAMS_ENTRY 8 +#define SJA1105ET_SIZE_MAC_CONFIG_ENTRY 28 +#define SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY 40 +#define SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY 32 +#define SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY 44 +#define SJA1110_SIZE_GENERAL_PARAMS_ENTRY 56 + +#define SJA1105_MAX_L2_LOOKUP_COUNT 1024 +#define SJA1105_MAX_L2_POLICING_COUNT 45 +#define SJA1110_MAX_L2_POLICING_COUNT 110 +#define SJA1105_MAX_VLAN_LOOKUP_COUNT 4096 +#define SJA1105_MAX_L2_FORWARDING_COUNT 13 +#define SJA1110_MAX_L2_FORWARDING_COUNT 19 +#define SJA1105_MAX_MAC_CONFIG_COUNT 5 +#define SJA1110_MAX_MAC_CONFIG_COUNT 11 +#define SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT 1 +#define SJA1105_MAX_GENERAL_PARAMS_COUNT 1 +#define SJA1105_MAX_XMII_PARAMS_COUNT 1 + +#define SJA1105_MAX_FRAME_MEMORY 929 + +#define SJA1105E_DEVICE_ID 0x9C00000Cull +#define SJA1105T_DEVICE_ID 0x9E00030Eull +#define SJA1105PR_DEVICE_ID 0xAF00030Eull +#define SJA1105QS_DEVICE_ID 0xAE00030Eull +#define SJA1110_DEVICE_ID 0xB700030Full + +#define SJA1105ET_PART_NO 0x9A83 +#define SJA1105P_PART_NO 0x9A84 +#define SJA1105Q_PART_NO 0x9A85 +#define SJA1105R_PART_NO 0x9A86 +#define SJA1105S_PART_NO 0x9A87 +#define SJA1110A_PART_NO 0x1110 +#define SJA1110B_PART_NO 0x1111 +#define SJA1110C_PART_NO 0x1112 +#define SJA1110D_PART_NO 0x1113 + +#define SJA1110_ACU 0x1c4400 +#define SJA1110_RGU 0x1c6000 +#define SJA1110_CGU 0x1c6400 + +#define SJA1110_SPI_ADDR(x) ((x) / 4) +#define SJA1110_ACU_ADDR(x) (SJA1110_ACU + SJA1110_SPI_ADDR(x)) +#define SJA1110_CGU_ADDR(x) (SJA1110_CGU + SJA1110_SPI_ADDR(x)) +#define SJA1110_RGU_ADDR(x) (SJA1110_RGU + SJA1110_SPI_ADDR(x)) + +#define SJA1105_RSV_ADDR 0xffffffffffffffffull + +#define SJA1110_PCS_BANK_REG SJA1110_SPI_ADDR(0x3fc) + +#define DSA_8021Q_DIR_TX BIT(11) +#define DSA_8021Q_PORT_SHIFT 0 +#define DSA_8021Q_PORT_MASK GENMASK(3, 0) +#define DSA_8021Q_PORT(x) (((x) << DSA_8021Q_PORT_SHIFT) & \ + DSA_8021Q_PORT_MASK) + +#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000) + +/* XPCS registers */ + +/* VR MII MMD registers offsets */ +#define DW_VR_MII_DIG_CTRL1 0x8000 +#define DW_VR_MII_AN_CTRL 0x8001 +#define DW_VR_MII_DIG_CTRL2 0x80e1 + +/* VR_MII_DIG_CTRL1 */ +#define DW_VR_MII_DIG_CTRL1_MAC_AUTO_SW BIT(9) + +/* VR_MII_DIG_CTRL2 */ +#define DW_VR_MII_DIG_CTRL2_TX_POL_INV BIT(4) + +/* VR_MII_AN_CTRL */ +#define DW_VR_MII_AN_CTRL_TX_CONFIG_SHIFT 3 +#define DW_VR_MII_TX_CONFIG_MASK BIT(3) +#define DW_VR_MII_TX_CONFIG_MAC_SIDE_SGMII 0x0 +#define DW_VR_MII_AN_CTRL_PCS_MODE_SHIFT 1 +#define DW_VR_MII_PCS_MODE_MASK GENMASK(2, 1) +#define DW_VR_MII_PCS_MODE_C37_SGMII 0x2 + +/* PMA registers */ + +/* LANE_DRIVER1_0 register */ +#define SJA1110_LANE_DRIVER1_0 0x8038 +#define SJA1110_TXDRV(x) (((x) << 12) & GENMASK(14, 12)) + +/* LANE_DRIVER2_0 register */ +#define SJA1110_LANE_DRIVER2_0 0x803a +#define SJA1110_TXDRVTRIM_LSB(x) ((x) & GENMASK_ULL(15, 0)) + +/* LANE_DRIVER2_1 register */ +#define SJA1110_LANE_DRIVER2_1 0x803b +#define SJA1110_LANE_DRIVER2_1_RSV BIT(9) +#define SJA1110_TXDRVTRIM_MSB(x) (((x) & GENMASK_ULL(23, 16)) >> 16) + +/* LANE_TRIM register */ +#define SJA1110_LANE_TRIM 0x8040 +#define SJA1110_TXTEN BIT(11) +#define SJA1110_TXRTRIM(x) (((x) << 8) & GENMASK(10, 8)) +#define SJA1110_TXPLL_BWSEL BIT(7) +#define SJA1110_RXTEN BIT(6) +#define SJA1110_RXRTRIM(x) (((x) << 3) & GENMASK(5, 3)) +#define SJA1110_CDR_GAIN BIT(2) +#define SJA1110_ACCOUPLE_RXVCM_EN BIT(0) + +/* LANE_DATAPATH_1 register */ +#define SJA1110_LANE_DATAPATH_1 0x8037 + +/* POWERDOWN_ENABLE register */ +#define SJA1110_POWERDOWN_ENABLE 0x8041 +#define SJA1110_TXPLL_PD BIT(12) +#define SJA1110_TXPD BIT(11) +#define SJA1110_RXPKDETEN BIT(10) +#define SJA1110_RXCH_PD BIT(9) +#define SJA1110_RXBIAS_PD BIT(8) +#define SJA1110_RESET_SER_EN BIT(7) +#define SJA1110_RESET_SER BIT(6) +#define SJA1110_RESET_DES BIT(5) +#define SJA1110_RCVEN BIT(4) + +/* RXPLL_CTRL0 register */ +#define SJA1110_RXPLL_CTRL0 0x8065 +#define SJA1110_RXPLL_FBDIV(x) (((x) << 2) & GENMASK(9, 2)) + +/* RXPLL_CTRL1 register */ +#define SJA1110_RXPLL_CTRL1 0x8066 +#define SJA1110_RXPLL_REFDIV(x) ((x) & GENMASK(4, 0)) + +/* TXPLL_CTRL0 register */ +#define SJA1110_TXPLL_CTRL0 0x806d +#define SJA1110_TXPLL_FBDIV(x) ((x) & GENMASK(11, 0)) + +/* TXPLL_CTRL1 register */ +#define SJA1110_TXPLL_CTRL1 0x806e +#define SJA1110_TXPLL_REFDIV(x) ((x) & GENMASK(5, 0)) + +/* RX_DATA_DETECT register */ +#define SJA1110_RX_DATA_DETECT 0x8045 + +/* RX_CDR_CTLE register */ +#define SJA1110_RX_CDR_CTLE 0x8042 + +#define ETH_FCS_LEN 4 + +/* UM10944.pdf Page 11, Table 2. Configuration Blocks */ +enum { + BLKID_L2_POLICING = 0x06, + BLKID_VLAN_LOOKUP = 0x07, + BLKID_L2_FORWARDING = 0x08, + BLKID_MAC_CONFIG = 0x09, + BLKID_L2_FORWARDING_PARAMS = 0x0E, + BLKID_GENERAL_PARAMS = 0x11, + BLKID_XMII_PARAMS = 0x4E, +}; + +enum sja1105_blk_idx { + BLK_IDX_L2_POLICING = 0, + BLK_IDX_VLAN_LOOKUP, + BLK_IDX_L2_FORWARDING, + BLK_IDX_MAC_CONFIG, + BLK_IDX_L2_FORWARDING_PARAMS, + BLK_IDX_GENERAL_PARAMS, + BLK_IDX_XMII_PARAMS, + BLK_IDX_MAX, +}; + +struct sja1105_general_params_entry { + u64 mac_fltres1; + u64 mac_fltres0; + u64 mac_flt1; + u64 mac_flt0; + u64 casc_port; + u64 host_port; + u64 mirr_port; + u64 tpid; + u64 tpid2; +}; + +struct sja1105_vlan_lookup_entry { + u64 vmemb_port; + u64 vlan_bc; + u64 tag_port; + u64 vlanid; + u64 type_entry; /* SJA1110 only */ +}; + +struct sja1105_l2_forwarding_entry { + u64 bc_domain; + u64 reach_port; + u64 fl_domain; +}; + +struct sja1105_l2_forwarding_params_entry { + u64 part_spc[SJA1105_NUM_TC]; +}; + +struct sja1105_l2_policing_entry { + u64 sharindx; + u64 smax; + u64 rate; + u64 maxlen; + u64 partition; +}; + +struct sja1105_mac_config_entry { + u64 top[SJA1105_NUM_TC]; + u64 base[SJA1105_NUM_TC]; + u64 enabled[SJA1105_NUM_TC]; + u64 speed; + u64 vlanid; + u64 egress; + u64 ingress; +}; + +struct sja1105_xmii_params_entry { + u64 phy_mac[SJA1105_MAX_NUM_PORTS]; + u64 xmii_mode[SJA1105_MAX_NUM_PORTS]; + u64 special[SJA1105_MAX_NUM_PORTS]; +}; + +struct sja1105_table_header { + u64 block_id; + u64 len; + u64 crc; +}; + +struct sja1105_table_ops { + size_t (*packing)(void *buf, void *entry_ptr, enum packing_op op); + size_t unpacked_entry_size; + size_t packed_entry_size; + size_t max_entry_count; +}; + +struct sja1105_table { + const struct sja1105_table_ops *ops; + size_t entry_count; + void *entries; +}; + +struct sja1105_static_config { + u64 device_id; + struct sja1105_table tables[BLK_IDX_MAX]; +}; + +struct sja1105_xpcs_cfg { + bool inband_an; + int speed; +}; + +struct sja1105_private { + struct sja1105_static_config static_config; + bool rgmii_rx_delay[SJA1105_MAX_NUM_PORTS]; + bool rgmii_tx_delay[SJA1105_MAX_NUM_PORTS]; + u16 pvid[SJA1105_MAX_NUM_PORTS]; + struct sja1105_xpcs_cfg xpcs_cfg[SJA1105_MAX_NUM_PORTS]; + const struct sja1105_dcfg *dcfg; + struct device *dev; + struct dsa_switch ds; + struct spi_device *spidev; + size_t max_xfer_len; +}; + +enum sja1105_spi_rw_mode { + SPI_READ = 0, + SPI_WRITE = 1, +}; + +enum sja1105_mii_role { + XMII_MAC = 0, + XMII_PHY = 1, +}; + +enum sja1105_phy_interface { + XMII_MODE_MII = 0, + XMII_MODE_RMII = 1, + XMII_MODE_RGMII = 2, + XMII_MODE_SGMII = 3, +}; + +enum { + SJA1105_SPEED_AUTO, + SJA1105_SPEED_10MBPS, + SJA1105_SPEED_100MBPS, + SJA1105_SPEED_1000MBPS, + SJA1105_SPEED_MAX, +}; + +enum sja1110_vlan_type { + SJA1110_VLAN_INVALID = 0, + SJA1110_VLAN_C_TAG = 1, /* Single inner VLAN tag */ + SJA1110_VLAN_S_TAG = 2, /* Single outer VLAN tag */ + SJA1110_VLAN_D_TAG = 3, /* Double tagged, use outer tag for lookup */ +}; + +/* Keeps the different addresses between E/T and P/Q/R/S */ +struct sja1105_regs { + u64 device_id; + u64 prod_id; + u64 status; + u64 port_control; + u64 rgu; + u64 config; + u64 rmii_pll1; + u64 pad_mii_tx[SJA1105_MAX_NUM_PORTS]; + u64 pad_mii_rx[SJA1105_MAX_NUM_PORTS]; + u64 pad_mii_id[SJA1105_MAX_NUM_PORTS]; + u64 cgu_idiv[SJA1105_MAX_NUM_PORTS]; + u64 mii_tx_clk[SJA1105_MAX_NUM_PORTS]; + u64 mii_rx_clk[SJA1105_MAX_NUM_PORTS]; + u64 mii_ext_tx_clk[SJA1105_MAX_NUM_PORTS]; + u64 mii_ext_rx_clk[SJA1105_MAX_NUM_PORTS]; + u64 rgmii_tx_clk[SJA1105_MAX_NUM_PORTS]; + u64 rmii_ref_clk[SJA1105_MAX_NUM_PORTS]; + u64 rmii_ext_tx_clk[SJA1105_MAX_NUM_PORTS]; + u64 pcs_base[SJA1105_MAX_NUM_PORTS]; +}; + +struct sja1105_dcfg { + u64 device_id; + u64 part_no; + const struct sja1105_table_ops *static_ops; + const struct sja1105_regs *regs; + int (*reset_cmd)(struct sja1105_private *priv); + int (*setup_rgmii_delay)(struct sja1105_private *priv, int port); + const char *name; + bool supports_mii[SJA1105_MAX_NUM_PORTS]; + bool supports_rmii[SJA1105_MAX_NUM_PORTS]; + bool supports_rgmii[SJA1105_MAX_NUM_PORTS]; + bool supports_sgmii[SJA1105_MAX_NUM_PORTS]; + const u64 port_speed[SJA1105_SPEED_MAX]; + unsigned int num_ports; +}; + +struct sja1105_chunk { + u8 *buf; + size_t len; + u64 reg_addr; +}; + +struct sja1105_spi_message { + u64 access; + u64 read_count; + u64 address; +}; + +/* Common structure for CFG_PAD_MIIx_RX and CFG_PAD_MIIx_TX */ +struct sja1105_cfg_pad_mii { + u64 d32_os; + u64 d32_ih; + u64 d32_ipud; + u64 d10_ih; + u64 d10_os; + u64 d10_ipud; + u64 ctrl_os; + u64 ctrl_ih; + u64 ctrl_ipud; + u64 clk_os; + u64 clk_ih; + u64 clk_ipud; +}; + +struct sja1105_cfg_pad_mii_id { + u64 rxc_stable_ovr; + u64 rxc_delay; + u64 rxc_bypass; + u64 rxc_pd; + u64 txc_stable_ovr; + u64 txc_delay; + u64 txc_bypass; + u64 txc_pd; +}; + +struct sja1105_cgu_idiv { + u64 clksrc; + u64 autoblock; + u64 idiv; + u64 pd; +}; + +struct sja1105_cgu_pll_ctrl { + u64 pllclksrc; + u64 msel; + u64 autoblock; + u64 psel; + u64 direct; + u64 fbsel; + u64 bypass; + u64 pd; +}; + +enum { + CLKSRC_MII0_TX_CLK = 0x00, + CLKSRC_MII0_RX_CLK = 0x01, + CLKSRC_MII1_TX_CLK = 0x02, + CLKSRC_MII1_RX_CLK = 0x03, + CLKSRC_MII2_TX_CLK = 0x04, + CLKSRC_MII2_RX_CLK = 0x05, + CLKSRC_MII3_TX_CLK = 0x06, + CLKSRC_MII3_RX_CLK = 0x07, + CLKSRC_MII4_TX_CLK = 0x08, + CLKSRC_MII4_RX_CLK = 0x09, + CLKSRC_PLL0 = 0x0B, + CLKSRC_PLL1 = 0x0E, + CLKSRC_IDIV0 = 0x11, + CLKSRC_IDIV1 = 0x12, + CLKSRC_IDIV2 = 0x13, + CLKSRC_IDIV3 = 0x14, + CLKSRC_IDIV4 = 0x15, +}; + +struct sja1105_cgu_mii_ctrl { + u64 clksrc; + u64 autoblock; + u64 pd; +}; + +static int get_reverse_lsw32_offset(int offset, size_t len) +{ + int closest_multiple_of_4; + int word_index; + + word_index = offset / 4; + closest_multiple_of_4 = word_index * 4; + offset -= closest_multiple_of_4; + word_index = (len / 4) - word_index - 1; + return word_index * 4 + offset; +} + +/* Simplified version of the "packing" function from Linux, adapted + * to support only sja1105's quirk: QUIRK_LSW32_IS_FIRST + */ +static void sja1105_packing(void *pbuf, u64 *uval, int startbit, int endbit, + size_t pbuflen, enum packing_op op) +{ + int plogical_first_u8, plogical_last_u8, box; + + if (op == UNPACK) + *uval = 0; + + plogical_first_u8 = startbit / 8; + plogical_last_u8 = endbit / 8; + + for (box = plogical_first_u8; box >= plogical_last_u8; box--) { + int box_start_bit, box_end_bit, box_addr; + int proj_start_bit, proj_end_bit; + u64 proj_mask; + u8 box_mask; + + if (box == plogical_first_u8) + box_start_bit = startbit % 8; + else + box_start_bit = 7; + if (box == plogical_last_u8) + box_end_bit = endbit % 8; + else + box_end_bit = 0; + + proj_start_bit = ((box * 8) + box_start_bit) - endbit; + proj_end_bit = ((box * 8) + box_end_bit) - endbit; + proj_mask = GENMASK_ULL(proj_start_bit, proj_end_bit); + box_mask = GENMASK_ULL(box_start_bit, box_end_bit); + + box_addr = pbuflen - box - 1; + box_addr = get_reverse_lsw32_offset(box_addr, pbuflen); + + if (op == UNPACK) { + u64 pval; + + /* Read from pbuf, write to uval */ + pval = ((u8 *)pbuf)[box_addr] & box_mask; + + pval >>= box_end_bit; + pval <<= proj_end_bit; + *uval &= ~proj_mask; + *uval |= pval; + } else { + u64 pval; + + /* Write to pbuf, read from uval */ + pval = (*uval) & proj_mask; + pval >>= proj_end_bit; + + pval <<= box_end_bit; + ((u8 *)pbuf)[box_addr] &= ~box_mask; + ((u8 *)pbuf)[box_addr] |= pval; + } + } +} + +static u32 crc32_add(u32 crc, u8 byte) +{ + u32 byte32 = bitrev32(byte); + int i; + + for (i = 0; i < 8; i++) { + if ((crc ^ byte32) & BIT(31)) { + crc <<= 1; + crc ^= ETHER_CRC32_POLY; + } else { + crc <<= 1; + } + byte32 <<= 1; + } + return crc; +} + +/* Little-endian Ethernet CRC32 of data packed as big-endian u32 words */ +static uint32_t sja1105_crc32(void *buf, size_t len) +{ + unsigned int i; + u64 chunk; + u32 crc; + + /* seed */ + crc = 0xFFFFFFFF; + for (i = 0; i < len; i += 4) { + sja1105_packing(buf + i, &chunk, 31, 0, 4, UNPACK); + crc = crc32_add(crc, chunk & 0xFF); + crc = crc32_add(crc, (chunk >> 8) & 0xFF); + crc = crc32_add(crc, (chunk >> 16) & 0xFF); + crc = crc32_add(crc, (chunk >> 24) & 0xFF); + } + return bitrev32(~crc); +} + +static void sja1105_spi_message_pack(void *buf, struct sja1105_spi_message *msg) +{ + const int size = SJA1105_SIZE_SPI_MSG_HEADER; + + memset(buf, 0, size); + + sja1105_packing(buf, &msg->access, 31, 31, size, PACK); + sja1105_packing(buf, &msg->read_count, 30, 25, size, PACK); + sja1105_packing(buf, &msg->address, 24, 4, size, PACK); +} + +static int sja1105_xfer_buf(const struct sja1105_private *priv, + enum sja1105_spi_rw_mode rw, u64 reg_addr, + u8 *buf, size_t len) +{ + u8 hdr_buf[SJA1105_SIZE_SPI_MSG_HEADER] = {0}; + struct spi_device *spi = priv->spidev; + struct spi_transfer xfers[2] = {0}; + struct spi_transfer *chunk_xfer; + struct spi_transfer *hdr_xfer; + struct sja1105_chunk chunk; + int num_chunks; + int rc, i = 0; + + num_chunks = DIV_ROUND_UP(len, priv->max_xfer_len); + + chunk.reg_addr = reg_addr; + chunk.buf = buf; + chunk.len = min_t(size_t, len, priv->max_xfer_len); + + hdr_xfer = &xfers[0]; + chunk_xfer = &xfers[1]; + + for (i = 0; i < num_chunks; i++) { + struct sja1105_spi_message msg; + + /* Populate the transfer's header buffer */ + msg.address = chunk.reg_addr; + msg.access = rw; + if (rw == SPI_READ) + msg.read_count = chunk.len / 4; + else + /* Ignored */ + msg.read_count = 0; + sja1105_spi_message_pack(hdr_buf, &msg); + hdr_xfer->tx_buf = hdr_buf; + hdr_xfer->len = SJA1105_SIZE_SPI_MSG_HEADER; + + /* Populate the transfer's data buffer */ + if (rw == SPI_READ) + chunk_xfer->rx_buf = chunk.buf; + else + chunk_xfer->tx_buf = chunk.buf; + chunk_xfer->len = chunk.len; + + /* Calculate next chunk */ + chunk.buf += chunk.len; + chunk.reg_addr += chunk.len / 4; + chunk.len = min_t(size_t, (ptrdiff_t)(buf + len - chunk.buf), + priv->max_xfer_len); + + rc = spi_sync_transfer(spi, xfers, 2); + if (rc < 0) { + dev_err(&spi->dev, "SPI transfer failed: %d\n", rc); + return rc; + } + } + + return 0; +} + +static int sja1105et_reset_cmd(struct sja1105_private *priv) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0}; + const int size = SJA1105_SIZE_RESET_CMD; + u64 cold_rst = 1; + + sja1105_packing(packed_buf, &cold_rst, 3, 3, size, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf, + SJA1105_SIZE_RESET_CMD); +} + +static int sja1105pqrs_reset_cmd(struct sja1105_private *priv) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0}; + const int size = SJA1105_SIZE_RESET_CMD; + u64 cold_rst = 1; + + sja1105_packing(packed_buf, &cold_rst, 2, 2, size, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf, + SJA1105_SIZE_RESET_CMD); +} + +static int sja1110_reset_cmd(struct sja1105_private *priv) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + u8 packed_buf[SJA1105_SIZE_RESET_CMD] = {0}; + const int size = SJA1105_SIZE_RESET_CMD; + u64 switch_rst = 1; + + /* Only reset the switch core. + * A full cold reset would re-enable the BASE_MCSS_CLOCK PLL which + * would turn on the microcontroller, potentially letting it execute + * code which could interfere with our configuration. + */ + sja1105_packing(packed_buf, &switch_rst, 20, 20, size, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgu, packed_buf, + SJA1105_SIZE_RESET_CMD); +} + +static size_t sja1105et_general_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY; + struct sja1105_general_params_entry *entry = entry_ptr; + + sja1105_packing(buf, &entry->mac_fltres1, 311, 264, size, op); + sja1105_packing(buf, &entry->mac_fltres0, 263, 216, size, op); + sja1105_packing(buf, &entry->mac_flt1, 215, 168, size, op); + sja1105_packing(buf, &entry->mac_flt0, 167, 120, size, op); + sja1105_packing(buf, &entry->casc_port, 115, 113, size, op); + sja1105_packing(buf, &entry->host_port, 112, 110, size, op); + sja1105_packing(buf, &entry->mirr_port, 109, 107, size, op); + sja1105_packing(buf, &entry->tpid, 42, 27, size, op); + sja1105_packing(buf, &entry->tpid2, 25, 10, size, op); + return size; +} + +static size_t sja1110_general_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_general_params_entry *entry = entry_ptr; + const size_t size = SJA1110_SIZE_GENERAL_PARAMS_ENTRY; + + sja1105_packing(buf, &entry->mac_fltres1, 438, 391, size, op); + sja1105_packing(buf, &entry->mac_fltres0, 390, 343, size, op); + sja1105_packing(buf, &entry->mac_flt1, 342, 295, size, op); + sja1105_packing(buf, &entry->mac_flt0, 294, 247, size, op); + sja1105_packing(buf, &entry->casc_port, 242, 232, size, op); + sja1105_packing(buf, &entry->host_port, 231, 228, size, op); + sja1105_packing(buf, &entry->mirr_port, 227, 224, size, op); + sja1105_packing(buf, &entry->tpid2, 159, 144, size, op); + sja1105_packing(buf, &entry->tpid, 142, 127, size, op); + return size; +} + +static size_t +sja1105pqrs_general_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY; + struct sja1105_general_params_entry *entry = entry_ptr; + + sja1105_packing(buf, &entry->mac_fltres1, 343, 296, size, op); + sja1105_packing(buf, &entry->mac_fltres0, 295, 248, size, op); + sja1105_packing(buf, &entry->mac_flt1, 247, 200, size, op); + sja1105_packing(buf, &entry->mac_flt0, 199, 152, size, op); + sja1105_packing(buf, &entry->casc_port, 147, 145, size, op); + sja1105_packing(buf, &entry->host_port, 144, 142, size, op); + sja1105_packing(buf, &entry->mirr_port, 141, 139, size, op); + sja1105_packing(buf, &entry->tpid, 74, 59, size, op); + sja1105_packing(buf, &entry->tpid2, 57, 42, size, op); + return size; +} + +static size_t +sja1105_l2_forwarding_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY; + struct sja1105_l2_forwarding_params_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 13; i < SJA1105_NUM_TC; i++, offset += 10) + sja1105_packing(buf, &entry->part_spc[i], + offset + 9, offset + 0, size, op); + return size; +} + +static size_t +sja1110_l2_forwarding_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_l2_forwarding_params_entry *entry = entry_ptr; + const size_t size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY; + int offset, i; + + for (i = 0, offset = 5; i < 8; i++, offset += 11) + sja1105_packing(buf, &entry->part_spc[i], + offset + 10, offset + 0, size, op); + return size; +} + +static size_t sja1105_l2_forwarding_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105_SIZE_L2_FORWARDING_ENTRY; + struct sja1105_l2_forwarding_entry *entry = entry_ptr; + + sja1105_packing(buf, &entry->bc_domain, 63, 59, size, op); + sja1105_packing(buf, &entry->reach_port, 58, 54, size, op); + sja1105_packing(buf, &entry->fl_domain, 53, 49, size, op); + return size; +} + +static size_t sja1110_l2_forwarding_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_l2_forwarding_entry *entry = entry_ptr; + const size_t size = SJA1105_SIZE_L2_FORWARDING_ENTRY; + + sja1105_packing(buf, &entry->bc_domain, 63, 53, size, op); + sja1105_packing(buf, &entry->reach_port, 52, 42, size, op); + sja1105_packing(buf, &entry->fl_domain, 41, 31, size, op); + return size; +} + +static size_t sja1105_l2_policing_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_l2_policing_entry *entry = entry_ptr; + const size_t size = SJA1105_SIZE_L2_POLICING_ENTRY; + + sja1105_packing(buf, &entry->sharindx, 63, 58, size, op); + sja1105_packing(buf, &entry->smax, 57, 42, size, op); + sja1105_packing(buf, &entry->rate, 41, 26, size, op); + sja1105_packing(buf, &entry->maxlen, 25, 15, size, op); + sja1105_packing(buf, &entry->partition, 14, 12, size, op); + return size; +} + +static size_t sja1110_l2_policing_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_l2_policing_entry *entry = entry_ptr; + const size_t size = SJA1105_SIZE_L2_POLICING_ENTRY; + + sja1105_packing(buf, &entry->sharindx, 63, 57, size, op); + sja1105_packing(buf, &entry->smax, 56, 39, size, op); + sja1105_packing(buf, &entry->rate, 38, 21, size, op); + sja1105_packing(buf, &entry->maxlen, 20, 10, size, op); + sja1105_packing(buf, &entry->partition, 9, 7, size, op); + return size; +} + +static size_t sja1105et_mac_config_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105ET_SIZE_MAC_CONFIG_ENTRY; + struct sja1105_mac_config_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 72; i < SJA1105_NUM_TC; i++, offset += 19) { + sja1105_packing(buf, &entry->enabled[i], + offset + 0, offset + 0, size, op); + sja1105_packing(buf, &entry->base[i], + offset + 9, offset + 1, size, op); + sja1105_packing(buf, &entry->top[i], + offset + 18, offset + 10, size, op); + } + sja1105_packing(buf, &entry->speed, 66, 65, size, op); + sja1105_packing(buf, &entry->vlanid, 21, 10, size, op); + sja1105_packing(buf, &entry->egress, 2, 2, size, op); + sja1105_packing(buf, &entry->ingress, 1, 1, size, op); + return size; +} + +static size_t sja1105pqrs_mac_config_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY; + struct sja1105_mac_config_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 104; i < SJA1105_NUM_TC; i++, offset += 19) { + sja1105_packing(buf, &entry->enabled[i], + offset + 0, offset + 0, size, op); + sja1105_packing(buf, &entry->base[i], + offset + 9, offset + 1, size, op); + sja1105_packing(buf, &entry->top[i], + offset + 18, offset + 10, size, op); + } + sja1105_packing(buf, &entry->speed, 98, 97, size, op); + sja1105_packing(buf, &entry->vlanid, 53, 42, size, op); + sja1105_packing(buf, &entry->egress, 32, 32, size, op); + sja1105_packing(buf, &entry->ingress, 31, 31, size, op); + return size; +} + +static size_t sja1110_mac_config_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY; + struct sja1105_mac_config_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 104; i < 8; i++, offset += 19) { + sja1105_packing(buf, &entry->enabled[i], + offset + 0, offset + 0, size, op); + sja1105_packing(buf, &entry->base[i], + offset + 9, offset + 1, size, op); + sja1105_packing(buf, &entry->top[i], + offset + 18, offset + 10, size, op); + } + sja1105_packing(buf, &entry->speed, 98, 96, size, op); + sja1105_packing(buf, &entry->vlanid, 52, 41, size, op); + sja1105_packing(buf, &entry->egress, 31, 31, size, op); + sja1105_packing(buf, &entry->ingress, 30, 30, size, op); + return size; +} + +static size_t sja1105_vlan_lookup_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY; + struct sja1105_vlan_lookup_entry *entry = entry_ptr; + + sja1105_packing(buf, &entry->vmemb_port, 53, 49, size, op); + sja1105_packing(buf, &entry->vlan_bc, 48, 44, size, op); + sja1105_packing(buf, &entry->tag_port, 43, 39, size, op); + sja1105_packing(buf, &entry->vlanid, 38, 27, size, op); + return size; +} + +static size_t sja1110_vlan_lookup_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + struct sja1105_vlan_lookup_entry *entry = entry_ptr; + const size_t size = SJA1110_SIZE_VLAN_LOOKUP_ENTRY; + + sja1105_packing(buf, &entry->vmemb_port, 73, 63, size, op); + sja1105_packing(buf, &entry->vlan_bc, 62, 52, size, op); + sja1105_packing(buf, &entry->tag_port, 51, 41, size, op); + sja1105_packing(buf, &entry->type_entry, 40, 39, size, op); + sja1105_packing(buf, &entry->vlanid, 38, 27, size, op); + return size; +} + +static size_t sja1105_xmii_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105_SIZE_XMII_PARAMS_ENTRY; + struct sja1105_xmii_params_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 17; i < SJA1105_NUM_PORTS; i++, offset += 3) { + sja1105_packing(buf, &entry->xmii_mode[i], + offset + 1, offset + 0, size, op); + sja1105_packing(buf, &entry->phy_mac[i], + offset + 2, offset + 2, size, op); + } + return size; +} + +static size_t sja1110_xmii_params_entry_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1110_SIZE_XMII_PARAMS_ENTRY; + struct sja1105_xmii_params_entry *entry = entry_ptr; + int offset, i; + + for (i = 0, offset = 20; i < SJA1110_NUM_PORTS; i++, offset += 4) { + sja1105_packing(buf, &entry->xmii_mode[i], + offset + 1, offset + 0, size, op); + sja1105_packing(buf, &entry->phy_mac[i], + offset + 2, offset + 2, size, op); + sja1105_packing(buf, &entry->special[i], + offset + 3, offset + 3, size, op); + } + return size; +} + +static size_t sja1105_table_header_packing(void *buf, void *entry_ptr, + enum packing_op op) +{ + const size_t size = SJA1105_SIZE_TABLE_HEADER; + struct sja1105_table_header *entry = entry_ptr; + + sja1105_packing(buf, &entry->block_id, 31, 24, size, op); + sja1105_packing(buf, &entry->len, 55, 32, size, op); + sja1105_packing(buf, &entry->crc, 95, 64, size, op); + return size; +} + +static void +sja1105_table_header_pack_with_crc(void *buf, struct sja1105_table_header *hdr) +{ + /* First pack the table as-is, then calculate the CRC, and + * finally put the proper CRC into the packed buffer + */ + memset(buf, 0, SJA1105_SIZE_TABLE_HEADER); + sja1105_table_header_packing(buf, hdr, PACK); + hdr->crc = sja1105_crc32(buf, SJA1105_SIZE_TABLE_HEADER - 4); + sja1105_packing(buf + SJA1105_SIZE_TABLE_HEADER - 4, &hdr->crc, + 31, 0, 4, PACK); +} + +static void sja1105_table_write_crc(u8 *table_start, u8 *crc_ptr) +{ + u64 computed_crc; + int len_bytes; + + len_bytes = (uintptr_t)(crc_ptr - table_start); + computed_crc = sja1105_crc32(table_start, len_bytes); + sja1105_packing(crc_ptr, &computed_crc, 31, 0, 4, PACK); +} + +/* The block IDs that the switches support are unfortunately sparse, so keep a + * mapping table to "block indices" and translate back and forth. + */ +static const u64 blk_id_map[BLK_IDX_MAX] = { + [BLK_IDX_L2_POLICING] = BLKID_L2_POLICING, + [BLK_IDX_VLAN_LOOKUP] = BLKID_VLAN_LOOKUP, + [BLK_IDX_L2_FORWARDING] = BLKID_L2_FORWARDING, + [BLK_IDX_MAC_CONFIG] = BLKID_MAC_CONFIG, + [BLK_IDX_L2_FORWARDING_PARAMS] = BLKID_L2_FORWARDING_PARAMS, + [BLK_IDX_GENERAL_PARAMS] = BLKID_GENERAL_PARAMS, + [BLK_IDX_XMII_PARAMS] = BLKID_XMII_PARAMS, +}; + +static void +sja1105_static_config_pack(void *buf, struct sja1105_static_config *config) +{ + struct sja1105_table_header header = {0}; + enum sja1105_blk_idx i; + u8 *p = buf; + int j; + + sja1105_packing(p, &config->device_id, 31, 0, 4, PACK); + p += SJA1105_SIZE_DEVICE_ID; + + for (i = 0; i < BLK_IDX_MAX; i++) { + const struct sja1105_table *table; + u8 *table_start; + + table = &config->tables[i]; + if (!table->entry_count) + continue; + + header.block_id = blk_id_map[i]; + header.len = table->entry_count * + table->ops->packed_entry_size / 4; + sja1105_table_header_pack_with_crc(p, &header); + p += SJA1105_SIZE_TABLE_HEADER; + table_start = p; + for (j = 0; j < table->entry_count; j++) { + u8 *entry_ptr = table->entries; + + entry_ptr += j * table->ops->unpacked_entry_size; + memset(p, 0, table->ops->packed_entry_size); + table->ops->packing(p, entry_ptr, PACK); + p += table->ops->packed_entry_size; + } + sja1105_table_write_crc(table_start, p); + p += 4; + } + /* Final header: + * Block ID does not matter + * Length of 0 marks that header is final + * CRC will be replaced on-the-fly + */ + header.block_id = 0; + header.len = 0; + header.crc = 0xDEADBEEF; + memset(p, 0, SJA1105_SIZE_TABLE_HEADER); + sja1105_table_header_packing(p, &header, PACK); +} + +static size_t +sja1105_static_config_get_length(const struct sja1105_static_config *config) +{ + unsigned int header_count; + enum sja1105_blk_idx i; + unsigned int sum; + + /* Ending header */ + header_count = 1; + sum = SJA1105_SIZE_DEVICE_ID; + + /* Tables (headers and entries) */ + for (i = 0; i < BLK_IDX_MAX; i++) { + const struct sja1105_table *table; + + table = &config->tables[i]; + if (table->entry_count) + header_count++; + + sum += table->ops->packed_entry_size * table->entry_count; + } + /* Headers have an additional CRC at the end */ + sum += header_count * (SJA1105_SIZE_TABLE_HEADER + 4); + /* Last header does not have an extra CRC because there is no data */ + sum -= 4; + + return sum; +} + +/* Compatibility matrices */ +static const struct sja1105_table_ops sja1105et_table_ops[BLK_IDX_MAX] = { + [BLK_IDX_L2_POLICING] = { + .packing = sja1105_l2_policing_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_policing_entry), + .packed_entry_size = SJA1105_SIZE_L2_POLICING_ENTRY, + .max_entry_count = SJA1105_MAX_L2_POLICING_COUNT, + }, + [BLK_IDX_VLAN_LOOKUP] = { + .packing = sja1105_vlan_lookup_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_vlan_lookup_entry), + .packed_entry_size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY, + .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT, + }, + [BLK_IDX_L2_FORWARDING] = { + .packing = sja1105_l2_forwarding_entry_packing, + .unpacked_entry_size = + sizeof(struct sja1105_l2_forwarding_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_ENTRY, + .max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT, + }, + [BLK_IDX_MAC_CONFIG] = { + .packing = sja1105et_mac_config_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_mac_config_entry), + .packed_entry_size = SJA1105ET_SIZE_MAC_CONFIG_ENTRY, + .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, + }, + [BLK_IDX_L2_FORWARDING_PARAMS] = { + .packing = sja1105_l2_forwarding_params_entry_packing, + .unpacked_entry_size = + sizeof(struct sja1105_l2_forwarding_params_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT, + }, + [BLK_IDX_GENERAL_PARAMS] = { + .packing = sja1105et_general_params_entry_packing, + .unpacked_entry_size = + sizeof(struct sja1105_general_params_entry), + .packed_entry_size = SJA1105ET_SIZE_GENERAL_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, + }, + [BLK_IDX_XMII_PARAMS] = { + .packing = sja1105_xmii_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry), + .packed_entry_size = SJA1105_SIZE_XMII_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT, + }, +}; + +static const struct sja1105_table_ops sja1105pqrs_table_ops[BLK_IDX_MAX] = { + [BLK_IDX_L2_POLICING] = { + .packing = sja1105_l2_policing_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_policing_entry), + .packed_entry_size = SJA1105_SIZE_L2_POLICING_ENTRY, + .max_entry_count = SJA1105_MAX_L2_POLICING_COUNT, + }, + [BLK_IDX_VLAN_LOOKUP] = { + .packing = sja1105_vlan_lookup_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_vlan_lookup_entry), + .packed_entry_size = SJA1105_SIZE_VLAN_LOOKUP_ENTRY, + .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT, + }, + [BLK_IDX_L2_FORWARDING] = { + .packing = sja1105_l2_forwarding_entry_packing, + .unpacked_entry_size = + sizeof(struct sja1105_l2_forwarding_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_ENTRY, + .max_entry_count = SJA1105_MAX_L2_FORWARDING_COUNT, + }, + [BLK_IDX_MAC_CONFIG] = { + .packing = sja1105pqrs_mac_config_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_mac_config_entry), + .packed_entry_size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY, + .max_entry_count = SJA1105_MAX_MAC_CONFIG_COUNT, + }, + [BLK_IDX_L2_FORWARDING_PARAMS] = { + .packing = sja1105_l2_forwarding_params_entry_packing, + .unpacked_entry_size = + sizeof(struct sja1105_l2_forwarding_params_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT, + }, + [BLK_IDX_GENERAL_PARAMS] = { + .packing = sja1105pqrs_general_params_entry_packing, + .unpacked_entry_size = + sizeof(struct sja1105_general_params_entry), + .packed_entry_size = SJA1105PQRS_SIZE_GENERAL_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, + }, + [BLK_IDX_XMII_PARAMS] = { + .packing = sja1105_xmii_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry), + .packed_entry_size = SJA1105_SIZE_XMII_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT, + }, +}; + +static const struct sja1105_table_ops sja1110_table_ops[BLK_IDX_MAX] = { + [BLK_IDX_L2_POLICING] = { + .packing = sja1110_l2_policing_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_l2_policing_entry), + .packed_entry_size = SJA1105_SIZE_L2_POLICING_ENTRY, + .max_entry_count = SJA1110_MAX_L2_POLICING_COUNT, + }, + [BLK_IDX_VLAN_LOOKUP] = { + .packing = sja1110_vlan_lookup_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_vlan_lookup_entry), + .packed_entry_size = SJA1110_SIZE_VLAN_LOOKUP_ENTRY, + .max_entry_count = SJA1105_MAX_VLAN_LOOKUP_COUNT, + }, + [BLK_IDX_L2_FORWARDING] = { + .packing = sja1110_l2_forwarding_entry_packing, + .unpacked_entry_size = + sizeof(struct sja1105_l2_forwarding_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_ENTRY, + .max_entry_count = SJA1110_MAX_L2_FORWARDING_COUNT, + }, + [BLK_IDX_MAC_CONFIG] = { + .packing = sja1110_mac_config_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_mac_config_entry), + .packed_entry_size = SJA1105PQRS_SIZE_MAC_CONFIG_ENTRY, + .max_entry_count = SJA1110_MAX_MAC_CONFIG_COUNT, + }, + [BLK_IDX_L2_FORWARDING_PARAMS] = { + .packing = sja1110_l2_forwarding_params_entry_packing, + .unpacked_entry_size = + sizeof(struct sja1105_l2_forwarding_params_entry), + .packed_entry_size = SJA1105_SIZE_L2_FORWARDING_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT, + }, + [BLK_IDX_GENERAL_PARAMS] = { + .packing = sja1110_general_params_entry_packing, + .unpacked_entry_size = + sizeof(struct sja1105_general_params_entry), + .packed_entry_size = SJA1110_SIZE_GENERAL_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT, + }, + [BLK_IDX_XMII_PARAMS] = { + .packing = sja1110_xmii_params_entry_packing, + .unpacked_entry_size = sizeof(struct sja1105_xmii_params_entry), + .packed_entry_size = SJA1110_SIZE_XMII_PARAMS_ENTRY, + .max_entry_count = SJA1105_MAX_XMII_PARAMS_COUNT, + }, +}; + +static int sja1105_init_mii_settings(struct sja1105_private *priv) +{ + struct sja1105_table *table; + + table = &priv->static_config.tables[BLK_IDX_XMII_PARAMS]; + + table->entries = calloc(SJA1105_MAX_XMII_PARAMS_COUNT, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + /* Table will be populated at runtime */ + table->entry_count = SJA1105_MAX_XMII_PARAMS_COUNT; + + return 0; +} + +static void sja1105_setup_tagging(struct sja1105_private *priv, int port) +{ + struct sja1105_vlan_lookup_entry *vlan; + struct dsa_switch *ds = &priv->ds; + int cpu = ds->cpu_port; + + /* The CPU port is implicitly configured by + * configuring the front-panel ports + */ + if (port == cpu) + return; + + vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries; + + priv->pvid[port] = DSA_8021Q_DIR_TX | DSA_8021Q_PORT(port); + + vlan[port].vmemb_port = BIT(port) | BIT(cpu); + vlan[port].vlan_bc = BIT(port) | BIT(cpu); + vlan[port].tag_port = BIT(cpu); + vlan[port].vlanid = priv->pvid[port]; + vlan[port].type_entry = SJA1110_VLAN_D_TAG; +} + +static int sja1105_init_vlan(struct sja1105_private *priv) +{ + struct dsa_switch *ds = &priv->ds; + struct sja1105_table *table; + int port; + + table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; + + table->entries = calloc(ds->num_ports, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = ds->num_ports; + + for (port = 0; port < ds->num_ports; port++) + sja1105_setup_tagging(priv, port); + + return 0; +} + +static void +sja1105_port_allow_traffic(struct sja1105_l2_forwarding_entry *l2_fwd, + int from, int to) +{ + l2_fwd[from].bc_domain |= BIT(to); + l2_fwd[from].reach_port |= BIT(to); + l2_fwd[from].fl_domain |= BIT(to); +} + +static int sja1105_init_l2_forwarding(struct sja1105_private *priv) +{ + struct sja1105_l2_forwarding_entry *l2fwd; + struct dsa_switch *ds = &priv->ds; + struct sja1105_table *table; + int cpu = ds->cpu_port; + int i; + + table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING]; + + table->entries = calloc(SJA1105_MAX_L2_FORWARDING_COUNT, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = SJA1105_MAX_L2_FORWARDING_COUNT; + + l2fwd = table->entries; + + /* First 5 entries define the forwarding rules */ + for (i = 0; i < ds->num_ports; i++) { + if (i == cpu) + continue; + + sja1105_port_allow_traffic(l2fwd, i, cpu); + sja1105_port_allow_traffic(l2fwd, cpu, i); + } + /* Next 8 entries define VLAN PCP mapping from ingress to egress. + * Leave them unpopulated (implicitly 0) but present. + */ + return 0; +} + +static int sja1105_init_l2_forwarding_params(struct sja1105_private *priv) +{ + struct sja1105_l2_forwarding_params_entry default_l2fwd_params = { + /* Use a single memory partition for all ingress queues */ + .part_spc = { SJA1105_MAX_FRAME_MEMORY, 0, 0, 0, 0, 0, 0, 0 }, + }; + struct sja1105_table *table; + + table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING_PARAMS]; + + table->entries = calloc(SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT; + + /* This table only has a single entry */ + ((struct sja1105_l2_forwarding_params_entry *)table->entries)[0] = + default_l2fwd_params; + + return 0; +} + +static int sja1105_init_general_params(struct sja1105_private *priv) +{ + struct dsa_switch *ds = &priv->ds; + struct sja1105_general_params_entry default_general_params = { + /* No frame trapping */ + .mac_fltres1 = 0x0, + .mac_flt1 = 0xffffffffffff, + .mac_fltres0 = 0x0, + .mac_flt0 = 0xffffffffffff, + .host_port = ds->num_ports, + /* No mirroring => specify an out-of-range port value */ + .mirr_port = ds->num_ports, + /* No link-local trapping => specify an out-of-range port value + */ + .casc_port = ds->num_ports, + /* Force the switch to see all traffic as untagged. */ + .tpid = ETH_P_SJA1105, + .tpid2 = ETH_P_SJA1105, + }; + struct sja1105_table *table; + + table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; + + table->entries = calloc(SJA1105_MAX_GENERAL_PARAMS_COUNT, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT; + + /* This table only has a single entry */ + ((struct sja1105_general_params_entry *)table->entries)[0] = + default_general_params; + + return 0; +} + +static void sja1105_setup_policer(struct sja1105_l2_policing_entry *policing, + int index, int mtu) +{ + policing[index].sharindx = index; + policing[index].smax = 65535; /* Burst size in bytes */ + policing[index].rate = SJA1105_RATE_MBPS(1000); + policing[index].maxlen = mtu; + policing[index].partition = 0; +} + +static int sja1105_init_l2_policing(struct sja1105_private *priv) +{ + struct sja1105_l2_policing_entry *policing; + struct dsa_switch *ds = &priv->ds; + struct sja1105_table *table; + int cpu = ds->cpu_port; + int i, j, k; + + table = &priv->static_config.tables[BLK_IDX_L2_POLICING]; + + table->entries = calloc(SJA1105_MAX_L2_POLICING_COUNT, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = SJA1105_MAX_L2_POLICING_COUNT; + + policing = table->entries; + + /* k sweeps through all unicast policers (0-39). + * bcast sweeps through policers 40-44. + */ + for (i = 0, k = 0; i < ds->num_ports; i++) { + int bcast = (ds->num_ports * SJA1105_NUM_TC) + i; + int mtu = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN; + + if (i == cpu) + mtu += VLAN_HLEN; + + for (j = 0; j < SJA1105_NUM_TC; j++, k++) + sja1105_setup_policer(policing, k, mtu); + + /* Set up this port's policer for broadcast traffic */ + sja1105_setup_policer(policing, bcast, mtu); + } + return 0; +} + +static int sja1105_init_mac_settings(struct sja1105_private *priv) +{ + struct sja1105_mac_config_entry default_mac = { + /* Enable 1 priority queue on egress. */ + .top = {0x1FF, 0, 0, 0, 0, 0, 0}, + .base = {0x0, 0, 0, 0, 0, 0, 0, 0}, + .enabled = {1, 0, 0, 0, 0, 0, 0, 0}, + /* Will be overridden in sja1105_adjust_link. */ + .speed = priv->dcfg->port_speed[SJA1105_SPEED_AUTO], + .egress = true, + .ingress = true, + }; + struct sja1105_mac_config_entry *mac; + struct dsa_switch *ds = &priv->ds; + struct sja1105_table *table; + int port; + + table = &priv->static_config.tables[BLK_IDX_MAC_CONFIG]; + + table->entries = calloc(ds->num_ports, + table->ops->unpacked_entry_size); + if (!table->entries) + return -ENOMEM; + + table->entry_count = ds->num_ports; + + mac = table->entries; + + for (port = 0; port < ds->num_ports; port++) { + mac[port] = default_mac; + /* Internal VLAN (pvid) to apply to untagged ingress */ + mac[port].vlanid = priv->pvid[port]; + } + + return 0; +} + +static int sja1105_static_config_init(struct sja1105_private *priv) +{ + struct sja1105_static_config *config = &priv->static_config; + const struct sja1105_table_ops *static_ops = priv->dcfg->static_ops; + u64 device_id = priv->dcfg->device_id; + enum sja1105_blk_idx i; + int rc; + + *config = (struct sja1105_static_config) {0}; + + /* Transfer static_ops array from priv into per-table ops + * for handier access + */ + for (i = 0; i < BLK_IDX_MAX; i++) + config->tables[i].ops = &static_ops[i]; + + config->device_id = device_id; + + /* Build initial static configuration, to be fixed up during runtime */ + rc = sja1105_init_vlan(priv); + if (rc < 0) + return rc; + rc = sja1105_init_mac_settings(priv); + if (rc < 0) + return rc; + rc = sja1105_init_mii_settings(priv); + if (rc < 0) + return rc; + rc = sja1105_init_l2_forwarding(priv); + if (rc < 0) + return rc; + rc = sja1105_init_l2_forwarding_params(priv); + if (rc < 0) + return rc; + rc = sja1105_init_l2_policing(priv); + if (rc < 0) + return rc; + rc = sja1105_init_general_params(priv); + if (rc < 0) + return rc; + + return 0; +} + +static void sja1105_cgu_idiv_packing(void *buf, struct sja1105_cgu_idiv *idiv, + enum packing_op op) +{ + const int size = 4; + + sja1105_packing(buf, &idiv->clksrc, 28, 24, size, op); + sja1105_packing(buf, &idiv->autoblock, 11, 11, size, op); + sja1105_packing(buf, &idiv->idiv, 5, 2, size, op); + sja1105_packing(buf, &idiv->pd, 0, 0, size, op); +} + +static int sja1105_cgu_idiv_config(struct sja1105_private *priv, int port, + bool enabled, int factor) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + struct sja1105_cgu_idiv idiv; + + if (regs->cgu_idiv[port] == SJA1105_RSV_ADDR) + return 0; + + if (enabled && factor != 1 && factor != 10) + return -ERANGE; + + /* Payload for packed_buf */ + idiv.clksrc = 0x0A; /* 25MHz */ + idiv.autoblock = 1; /* Block clk automatically */ + idiv.idiv = factor - 1; /* Divide by 1 or 10 */ + idiv.pd = enabled ? 0 : 1; /* Power down? */ + sja1105_cgu_idiv_packing(packed_buf, &idiv, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->cgu_idiv[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static void +sja1105_cgu_mii_control_packing(void *buf, struct sja1105_cgu_mii_ctrl *cmd, + enum packing_op op) +{ + const int size = 4; + + sja1105_packing(buf, &cmd->clksrc, 28, 24, size, op); + sja1105_packing(buf, &cmd->autoblock, 11, 11, size, op); + sja1105_packing(buf, &cmd->pd, 0, 0, size, op); +} + +static int sja1105_cgu_mii_tx_clk_config(struct sja1105_private *priv, + int port, enum sja1105_mii_role role) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + struct sja1105_cgu_mii_ctrl mii_tx_clk; + const int mac_clk_sources[] = { + CLKSRC_MII0_TX_CLK, + CLKSRC_MII1_TX_CLK, + CLKSRC_MII2_TX_CLK, + CLKSRC_MII3_TX_CLK, + CLKSRC_MII4_TX_CLK, + }; + const int phy_clk_sources[] = { + CLKSRC_IDIV0, + CLKSRC_IDIV1, + CLKSRC_IDIV2, + CLKSRC_IDIV3, + CLKSRC_IDIV4, + }; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + int clksrc; + + if (regs->mii_tx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + if (role == XMII_MAC) + clksrc = mac_clk_sources[port]; + else + clksrc = phy_clk_sources[port]; + + /* Payload for packed_buf */ + mii_tx_clk.clksrc = clksrc; + mii_tx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + mii_tx_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &mii_tx_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_tx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int +sja1105_cgu_mii_rx_clk_config(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + struct sja1105_cgu_mii_ctrl mii_rx_clk; + const int clk_sources[] = { + CLKSRC_MII0_RX_CLK, + CLKSRC_MII1_RX_CLK, + CLKSRC_MII2_RX_CLK, + CLKSRC_MII3_RX_CLK, + CLKSRC_MII4_RX_CLK, + }; + + if (regs->mii_rx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload for packed_buf */ + mii_rx_clk.clksrc = clk_sources[port]; + mii_rx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + mii_rx_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &mii_rx_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_rx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int +sja1105_cgu_mii_ext_tx_clk_config(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + struct sja1105_cgu_mii_ctrl mii_ext_tx_clk; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + const int clk_sources[] = { + CLKSRC_IDIV0, + CLKSRC_IDIV1, + CLKSRC_IDIV2, + CLKSRC_IDIV3, + CLKSRC_IDIV4, + }; + + if (regs->mii_ext_tx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload for packed_buf */ + mii_ext_tx_clk.clksrc = clk_sources[port]; + mii_ext_tx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + mii_ext_tx_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &mii_ext_tx_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_ext_tx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int +sja1105_cgu_mii_ext_rx_clk_config(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + struct sja1105_cgu_mii_ctrl mii_ext_rx_clk; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + const int clk_sources[] = { + CLKSRC_IDIV0, + CLKSRC_IDIV1, + CLKSRC_IDIV2, + CLKSRC_IDIV3, + CLKSRC_IDIV4, + }; + + if (regs->mii_ext_rx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload for packed_buf */ + mii_ext_rx_clk.clksrc = clk_sources[port]; + mii_ext_rx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + mii_ext_rx_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &mii_ext_rx_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->mii_ext_rx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int sja1105_mii_clocking_setup(struct sja1105_private *priv, int port, + enum sja1105_mii_role role) +{ + int rc; + + rc = sja1105_cgu_idiv_config(priv, port, (role == XMII_PHY), 1); + if (rc < 0) + return rc; + + rc = sja1105_cgu_mii_tx_clk_config(priv, port, role); + if (rc < 0) + return rc; + + rc = sja1105_cgu_mii_rx_clk_config(priv, port); + if (rc < 0) + return rc; + + if (role == XMII_PHY) { + rc = sja1105_cgu_mii_ext_tx_clk_config(priv, port); + if (rc < 0) + return rc; + + rc = sja1105_cgu_mii_ext_rx_clk_config(priv, port); + if (rc < 0) + return rc; + } + return 0; +} + +static void +sja1105_cgu_pll_control_packing(void *buf, struct sja1105_cgu_pll_ctrl *cmd, + enum packing_op op) +{ + const int size = 4; + + sja1105_packing(buf, &cmd->pllclksrc, 28, 24, size, op); + sja1105_packing(buf, &cmd->msel, 23, 16, size, op); + sja1105_packing(buf, &cmd->autoblock, 11, 11, size, op); + sja1105_packing(buf, &cmd->psel, 9, 8, size, op); + sja1105_packing(buf, &cmd->direct, 7, 7, size, op); + sja1105_packing(buf, &cmd->fbsel, 6, 6, size, op); + sja1105_packing(buf, &cmd->bypass, 1, 1, size, op); + sja1105_packing(buf, &cmd->pd, 0, 0, size, op); +} + +static int sja1105_cgu_rgmii_tx_clk_config(struct sja1105_private *priv, + int port, u64 speed) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + struct sja1105_cgu_mii_ctrl txc; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + int clksrc; + + if (regs->rgmii_tx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + if (speed == priv->dcfg->port_speed[SJA1105_SPEED_1000MBPS]) { + clksrc = CLKSRC_PLL0; + } else { + int clk_sources[] = {CLKSRC_IDIV0, CLKSRC_IDIV1, CLKSRC_IDIV2, + CLKSRC_IDIV3, CLKSRC_IDIV4}; + clksrc = clk_sources[port]; + } + + /* RGMII: 125MHz for 1000, 25MHz for 100, 2.5MHz for 10 */ + txc.clksrc = clksrc; + /* Autoblock clk while changing clksrc */ + txc.autoblock = 1; + /* Power Down off => enabled */ + txc.pd = 0; + sja1105_cgu_mii_control_packing(packed_buf, &txc, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rgmii_tx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +/* AGU */ +static void +sja1105_cfg_pad_mii_packing(void *buf, struct sja1105_cfg_pad_mii *cmd, + enum packing_op op) +{ + const int size = 4; + + sja1105_packing(buf, &cmd->d32_os, 28, 27, size, op); + sja1105_packing(buf, &cmd->d32_ih, 26, 26, size, op); + sja1105_packing(buf, &cmd->d32_ipud, 25, 24, size, op); + sja1105_packing(buf, &cmd->d10_os, 20, 19, size, op); + sja1105_packing(buf, &cmd->d10_ih, 18, 18, size, op); + sja1105_packing(buf, &cmd->d10_ipud, 17, 16, size, op); + sja1105_packing(buf, &cmd->ctrl_os, 12, 11, size, op); + sja1105_packing(buf, &cmd->ctrl_ih, 10, 10, size, op); + sja1105_packing(buf, &cmd->ctrl_ipud, 9, 8, size, op); + sja1105_packing(buf, &cmd->clk_os, 4, 3, size, op); + sja1105_packing(buf, &cmd->clk_ih, 2, 2, size, op); + sja1105_packing(buf, &cmd->clk_ipud, 1, 0, size, op); +} + +static void +sja1110_cfg_pad_mii_id_packing(void *buf, struct sja1105_cfg_pad_mii_id *cmd, + enum packing_op op) +{ + const int size = SJA1105_SIZE_CGU_CMD; + u64 range = 4; + + /* Fields RXC_RANGE and TXC_RANGE select the input frequency range: + * 0 = 2.5MHz + * 1 = 25MHz + * 2 = 50MHz + * 3 = 125MHz + * 4 = Automatically determined by port speed. + * There's no point in defining a structure different than the one for + * SJA1105, so just hardcode the frequency range to automatic, just as + * before. + */ + sja1105_packing(buf, &cmd->rxc_stable_ovr, 26, 26, size, op); + sja1105_packing(buf, &cmd->rxc_delay, 25, 21, size, op); + sja1105_packing(buf, &range, 20, 18, size, op); + sja1105_packing(buf, &cmd->rxc_bypass, 17, 17, size, op); + sja1105_packing(buf, &cmd->rxc_pd, 16, 16, size, op); + sja1105_packing(buf, &cmd->txc_stable_ovr, 10, 10, size, op); + sja1105_packing(buf, &cmd->txc_delay, 9, 5, size, op); + sja1105_packing(buf, &range, 4, 2, size, op); + sja1105_packing(buf, &cmd->txc_bypass, 1, 1, size, op); + sja1105_packing(buf, &cmd->txc_pd, 0, 0, size, op); +} + +static int sja1105_rgmii_cfg_pad_tx_config(struct sja1105_private *priv, + int port) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + struct sja1105_cfg_pad_mii pad_mii_tx = {0}; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + + if (regs->pad_mii_tx[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload */ + pad_mii_tx.d32_os = 3; /* TXD[3:2] output stage: */ + /* high noise/high speed */ + pad_mii_tx.d10_os = 3; /* TXD[1:0] output stage: */ + /* high noise/high speed */ + pad_mii_tx.d32_ipud = 2; /* TXD[3:2] input stage: */ + /* plain input (default) */ + pad_mii_tx.d10_ipud = 2; /* TXD[1:0] input stage: */ + /* plain input (default) */ + pad_mii_tx.ctrl_os = 3; /* TX_CTL / TX_ER output stage */ + pad_mii_tx.ctrl_ipud = 2; /* TX_CTL / TX_ER input stage (default) */ + pad_mii_tx.clk_os = 3; /* TX_CLK output stage */ + pad_mii_tx.clk_ih = 0; /* TX_CLK input hysteresis (default) */ + pad_mii_tx.clk_ipud = 2; /* TX_CLK input stage (default) */ + sja1105_cfg_pad_mii_packing(packed_buf, &pad_mii_tx, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_tx[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int sja1105_cfg_pad_rx_config(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + struct sja1105_cfg_pad_mii pad_mii_rx = {0}; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + + if (regs->pad_mii_rx[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload */ + pad_mii_rx.d32_ih = 0; /* RXD[3:2] input stage hysteresis: */ + /* non-Schmitt (default) */ + pad_mii_rx.d32_ipud = 2; /* RXD[3:2] input weak pull-up/down */ + /* plain input (default) */ + pad_mii_rx.d10_ih = 0; /* RXD[1:0] input stage hysteresis: */ + /* non-Schmitt (default) */ + pad_mii_rx.d10_ipud = 2; /* RXD[1:0] input weak pull-up/down */ + /* plain input (default) */ + pad_mii_rx.ctrl_ih = 0; /* RX_DV/CRS_DV/RX_CTL and RX_ER */ + /* input stage hysteresis: */ + /* non-Schmitt (default) */ + pad_mii_rx.ctrl_ipud = 3; /* RX_DV/CRS_DV/RX_CTL and RX_ER */ + /* input stage weak pull-up/down: */ + /* pull-down */ + pad_mii_rx.clk_os = 2; /* RX_CLK/RXC output stage: */ + /* medium noise/fast speed (default) */ + pad_mii_rx.clk_ih = 0; /* RX_CLK/RXC input hysteresis: */ + /* non-Schmitt (default) */ + pad_mii_rx.clk_ipud = 2; /* RX_CLK/RXC input pull-up/down: */ + /* plain input (default) */ + sja1105_cfg_pad_mii_packing(packed_buf, &pad_mii_rx, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_rx[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static void +sja1105_cfg_pad_mii_id_packing(void *buf, struct sja1105_cfg_pad_mii_id *cmd, + enum packing_op op) +{ + const int size = SJA1105_SIZE_CGU_CMD; + + sja1105_packing(buf, &cmd->rxc_stable_ovr, 15, 15, size, op); + sja1105_packing(buf, &cmd->rxc_delay, 14, 10, size, op); + sja1105_packing(buf, &cmd->rxc_bypass, 9, 9, size, op); + sja1105_packing(buf, &cmd->rxc_pd, 8, 8, size, op); + sja1105_packing(buf, &cmd->txc_stable_ovr, 7, 7, size, op); + sja1105_packing(buf, &cmd->txc_delay, 6, 2, size, op); + sja1105_packing(buf, &cmd->txc_bypass, 1, 1, size, op); + sja1105_packing(buf, &cmd->txc_pd, 0, 0, size, op); +} + +/* Valid range in degrees is an integer between 73.8 and 101.7 */ +static u64 sja1105_rgmii_delay(u64 phase) +{ + /* UM11040.pdf: The delay in degree phase is 73.8 + delay_tune * 0.9. + * To avoid floating point operations we'll multiply by 10 + * and get 1 decimal point precision. + */ + phase *= 10; + return (phase - 738) / 9; +} + +static int sja1105pqrs_setup_rgmii_delay(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + struct sja1105_cfg_pad_mii_id pad_mii_id = {0}; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + int rc; + + if (priv->rgmii_rx_delay[port]) + pad_mii_id.rxc_delay = sja1105_rgmii_delay(90); + if (priv->rgmii_tx_delay[port]) + pad_mii_id.txc_delay = sja1105_rgmii_delay(90); + + /* Stage 1: Turn the RGMII delay lines off. */ + pad_mii_id.rxc_bypass = 1; + pad_mii_id.rxc_pd = 1; + pad_mii_id.txc_bypass = 1; + pad_mii_id.txc_pd = 1; + sja1105_cfg_pad_mii_id_packing(packed_buf, &pad_mii_id, PACK); + + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_id[port], + packed_buf, SJA1105_SIZE_CGU_CMD); + if (rc < 0) + return rc; + + /* Stage 2: Turn the RGMII delay lines on. */ + if (priv->rgmii_rx_delay[port]) { + pad_mii_id.rxc_bypass = 0; + pad_mii_id.rxc_pd = 0; + } + if (priv->rgmii_tx_delay[port]) { + pad_mii_id.txc_bypass = 0; + pad_mii_id.txc_pd = 0; + } + sja1105_cfg_pad_mii_id_packing(packed_buf, &pad_mii_id, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_id[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int sja1110_setup_rgmii_delay(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + struct sja1105_cfg_pad_mii_id pad_mii_id = {0}; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + + pad_mii_id.rxc_pd = 1; + pad_mii_id.txc_pd = 1; + + if (priv->rgmii_rx_delay[port]) { + pad_mii_id.rxc_delay = sja1105_rgmii_delay(90); + /* The "BYPASS" bit in SJA1110 is actually a "don't bypass" */ + pad_mii_id.rxc_bypass = 1; + pad_mii_id.rxc_pd = 0; + } + + if (priv->rgmii_tx_delay[port]) { + pad_mii_id.txc_delay = sja1105_rgmii_delay(90); + pad_mii_id.txc_bypass = 1; + pad_mii_id.txc_pd = 0; + } + + sja1110_cfg_pad_mii_id_packing(packed_buf, &pad_mii_id, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->pad_mii_id[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int sja1105_rgmii_clocking_setup(struct sja1105_private *priv, int port, + enum sja1105_mii_role role) +{ + struct sja1105_mac_config_entry *mac; + struct device *dev = priv->dev; + u64 speed; + int rc = -EINVAL; + + mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; + speed = mac[port].speed; + + if (speed == priv->dcfg->port_speed[SJA1105_SPEED_1000MBPS]) { + /* 1000Mbps, IDIV disabled (125 MHz) */ + rc = sja1105_cgu_idiv_config(priv, port, false, 1); + } else if (speed == priv->dcfg->port_speed[SJA1105_SPEED_100MBPS]) { + /* 100Mbps, IDIV enabled, divide by 1 (25 MHz) */ + rc = sja1105_cgu_idiv_config(priv, port, true, 1); + } else if (speed == priv->dcfg->port_speed[SJA1105_SPEED_10MBPS]) { + /* 10Mbps, IDIV enabled, divide by 10 (2.5 MHz) */ + rc = sja1105_cgu_idiv_config(priv, port, true, 10); + } else if (speed == priv->dcfg->port_speed[SJA1105_SPEED_AUTO]) { + /* Skip CGU configuration if there is no speed available + * (e.g. link is not established yet) + */ + dev_dbg(dev, "Speed not available, skipping CGU config\n"); + + return 0; + } + + if (rc < 0) { + dev_err(dev, "Failed to configure idiv\n"); + return rc; + } + rc = sja1105_cgu_rgmii_tx_clk_config(priv, port, speed); + if (rc < 0) { + dev_err(dev, "Failed to configure RGMII Tx clock\n"); + return rc; + } + rc = sja1105_rgmii_cfg_pad_tx_config(priv, port); + if (rc < 0) { + dev_err(dev, "Failed to configure Tx pad registers\n"); + return rc; + } + + if (!priv->dcfg->setup_rgmii_delay) + return 0; + + return priv->dcfg->setup_rgmii_delay(priv, port); +} + +static int sja1105_cgu_rmii_ref_clk_config(struct sja1105_private *priv, + int port) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + struct sja1105_cgu_mii_ctrl ref_clk; + const int clk_sources[] = { + CLKSRC_MII0_TX_CLK, + CLKSRC_MII1_TX_CLK, + CLKSRC_MII2_TX_CLK, + CLKSRC_MII3_TX_CLK, + CLKSRC_MII4_TX_CLK, + }; + + if (regs->rmii_ref_clk[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload for packed_buf */ + ref_clk.clksrc = clk_sources[port]; + ref_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + ref_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &ref_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_ref_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int +sja1105_cgu_rmii_ext_tx_clk_config(struct sja1105_private *priv, int port) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + struct sja1105_cgu_mii_ctrl ext_tx_clk; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + + if (regs->rmii_ext_tx_clk[port] == SJA1105_RSV_ADDR) + return 0; + + /* Payload for packed_buf */ + ext_tx_clk.clksrc = CLKSRC_PLL1; + ext_tx_clk.autoblock = 1; /* Autoblock clk while changing clksrc */ + ext_tx_clk.pd = 0; /* Power Down off => enabled */ + sja1105_cgu_mii_control_packing(packed_buf, &ext_tx_clk, PACK); + + return sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_ext_tx_clk[port], + packed_buf, SJA1105_SIZE_CGU_CMD); +} + +static int sja1105_cgu_rmii_pll_config(struct sja1105_private *priv) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + u8 packed_buf[SJA1105_SIZE_CGU_CMD] = {0}; + struct sja1105_cgu_pll_ctrl pll = {0}; + int rc; + + if (regs->rmii_pll1 == SJA1105_RSV_ADDR) + return 0; + + /* Step 1: PLL1 setup for 50Mhz */ + pll.pllclksrc = 0xA; + pll.msel = 0x1; + pll.autoblock = 0x1; + pll.psel = 0x1; + pll.direct = 0x0; + pll.fbsel = 0x1; + pll.bypass = 0x0; + pll.pd = 0x1; + + sja1105_cgu_pll_control_packing(packed_buf, &pll, PACK); + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_pll1, packed_buf, + SJA1105_SIZE_CGU_CMD); + if (rc < 0) + return rc; + + /* Step 2: Enable PLL1 */ + pll.pd = 0x0; + + sja1105_cgu_pll_control_packing(packed_buf, &pll, PACK); + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->rmii_pll1, packed_buf, + SJA1105_SIZE_CGU_CMD); + return rc; +} + +static int sja1105_rmii_clocking_setup(struct sja1105_private *priv, int port, + enum sja1105_mii_role role) +{ + int rc; + + /* AH1601.pdf chapter 2.5.1. Sources */ + if (role == XMII_MAC) { + /* Configure and enable PLL1 for 50Mhz output */ + rc = sja1105_cgu_rmii_pll_config(priv); + if (rc < 0) + return rc; + } + /* Disable IDIV for this port */ + rc = sja1105_cgu_idiv_config(priv, port, false, 1); + if (rc < 0) + return rc; + /* Source to sink mappings */ + rc = sja1105_cgu_rmii_ref_clk_config(priv, port); + if (rc < 0) + return rc; + if (role == XMII_MAC) { + rc = sja1105_cgu_rmii_ext_tx_clk_config(priv, port); + if (rc < 0) + return rc; + } + return 0; +} + +static int sja1105_clocking_setup_port(struct sja1105_private *priv, int port) +{ + struct sja1105_xmii_params_entry *mii; + enum sja1105_phy_interface phy_mode; + enum sja1105_mii_role role; + int rc; + + mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; + + /* RGMII etc */ + phy_mode = mii->xmii_mode[port]; + /* MAC or PHY, for applicable types (not RGMII) */ + role = mii->phy_mac[port]; + + switch (phy_mode) { + case XMII_MODE_MII: + rc = sja1105_mii_clocking_setup(priv, port, role); + break; + case XMII_MODE_RMII: + rc = sja1105_rmii_clocking_setup(priv, port, role); + break; + case XMII_MODE_RGMII: + rc = sja1105_rgmii_clocking_setup(priv, port, role); + break; + default: + return -EINVAL; + } + if (rc) + return rc; + + /* Internally pull down the RX_DV/CRS_DV/RX_CTL and RX_ER inputs */ + return sja1105_cfg_pad_rx_config(priv, port); +} + +static int sja1105_clocking_setup(struct sja1105_private *priv) +{ + struct dsa_switch *ds = &priv->ds; + int port, rc; + + for (port = 0; port < ds->num_ports; port++) { + rc = sja1105_clocking_setup_port(priv, port); + if (rc < 0) + return rc; + } + return 0; +} + +static const struct sja1105_regs sja1105et_regs = { + .device_id = 0x0, + .prod_id = 0x100BC3, + .status = 0x1, + .port_control = 0x11, + .config = 0x020000, + .rgu = 0x100440, + /* UM10944.pdf, Table 86, ACU Register overview */ + .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808}, + .pad_mii_rx = {0x100801, 0x100803, 0x100805, 0x100807, 0x100809}, + .rmii_pll1 = 0x10000A, + .cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F}, + /* UM10944.pdf, Table 78, CGU Register overview */ + .mii_tx_clk = {0x100013, 0x10001A, 0x100021, 0x100028, 0x10002F}, + .mii_rx_clk = {0x100014, 0x10001B, 0x100022, 0x100029, 0x100030}, + .mii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034}, + .mii_ext_rx_clk = {0x100019, 0x100020, 0x100027, 0x10002E, 0x100035}, + .rgmii_tx_clk = {0x100016, 0x10001D, 0x100024, 0x10002B, 0x100032}, + .rmii_ref_clk = {0x100015, 0x10001C, 0x100023, 0x10002A, 0x100031}, + .rmii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034}, +}; + +static const struct sja1105_regs sja1105pqrs_regs = { + .device_id = 0x0, + .prod_id = 0x100BC3, + .status = 0x1, + .port_control = 0x12, + .config = 0x020000, + .rgu = 0x100440, + /* UM10944.pdf, Table 86, ACU Register overview */ + .pad_mii_tx = {0x100800, 0x100802, 0x100804, 0x100806, 0x100808}, + .pad_mii_rx = {0x100801, 0x100803, 0x100805, 0x100807, 0x100809}, + .pad_mii_id = {0x100810, 0x100811, 0x100812, 0x100813, 0x100814}, + .rmii_pll1 = 0x10000A, + .cgu_idiv = {0x10000B, 0x10000C, 0x10000D, 0x10000E, 0x10000F}, + /* UM11040.pdf, Table 114 */ + .mii_tx_clk = {0x100013, 0x100019, 0x10001F, 0x100025, 0x10002B}, + .mii_rx_clk = {0x100014, 0x10001A, 0x100020, 0x100026, 0x10002C}, + .mii_ext_tx_clk = {0x100017, 0x10001D, 0x100023, 0x100029, 0x10002F}, + .mii_ext_rx_clk = {0x100018, 0x10001E, 0x100024, 0x10002A, 0x100030}, + .rgmii_tx_clk = {0x100016, 0x10001C, 0x100022, 0x100028, 0x10002E}, + .rmii_ref_clk = {0x100015, 0x10001B, 0x100021, 0x100027, 0x10002D}, + .rmii_ext_tx_clk = {0x100017, 0x10001D, 0x100023, 0x100029, 0x10002F}, +}; + +static const struct sja1105_regs sja1110_regs = { + .device_id = SJA1110_SPI_ADDR(0x0), + .prod_id = SJA1110_ACU_ADDR(0xf00), + .status = SJA1110_SPI_ADDR(0x4), + .port_control = SJA1110_SPI_ADDR(0x50), /* actually INHIB_TX */ + .config = 0x020000, + .rgu = SJA1110_RGU_ADDR(0x100), /* Reset Control Register 0 */ + /* Ports 2 and 3 are capable of xMII, but there isn't anything to + * configure in the CGU/ACU for them. + */ + .pad_mii_tx = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR}, + .pad_mii_rx = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR}, + .pad_mii_id = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1110_ACU_ADDR(0x18), SJA1110_ACU_ADDR(0x28), + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR}, + .rmii_pll1 = SJA1105_RSV_ADDR, + .cgu_idiv = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .mii_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .mii_rx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .mii_ext_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .mii_ext_rx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .rgmii_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .rmii_ref_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, + .rmii_ext_tx_clk = {SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR}, + .pcs_base = {SJA1105_RSV_ADDR, 0x1c1400, 0x1c1800, 0x1c1c00, 0x1c2000, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, + SJA1105_RSV_ADDR, SJA1105_RSV_ADDR, SJA1105_RSV_ADDR}, +}; + +static const struct sja1105_dcfg sja1105e_dcfg = { + .device_id = SJA1105E_DEVICE_ID, + .part_no = SJA1105ET_PART_NO, + .static_ops = sja1105et_table_ops, + .reset_cmd = sja1105et_reset_cmd, + .regs = &sja1105et_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .name = "SJA1105E", + .num_ports = SJA1105_NUM_PORTS, +}; + +static const struct sja1105_dcfg sja1105t_dcfg = { + .device_id = SJA1105T_DEVICE_ID, + .part_no = SJA1105ET_PART_NO, + .static_ops = sja1105et_table_ops, + .reset_cmd = sja1105et_reset_cmd, + .regs = &sja1105et_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .name = "SJA1105T", + .num_ports = SJA1105_NUM_PORTS, +}; + +static const struct sja1105_dcfg sja1105p_dcfg = { + .device_id = SJA1105PR_DEVICE_ID, + .part_no = SJA1105P_PART_NO, + .static_ops = sja1105pqrs_table_ops, + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay, + .reset_cmd = sja1105pqrs_reset_cmd, + .regs = &sja1105pqrs_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .name = "SJA1105P", + .num_ports = SJA1105_NUM_PORTS, +}; + +static const struct sja1105_dcfg sja1105q_dcfg = { + .device_id = SJA1105QS_DEVICE_ID, + .part_no = SJA1105Q_PART_NO, + .static_ops = sja1105pqrs_table_ops, + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay, + .reset_cmd = sja1105pqrs_reset_cmd, + .regs = &sja1105pqrs_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .name = "SJA1105Q", + .num_ports = SJA1105_NUM_PORTS, +}; + +static const struct sja1105_dcfg sja1105r_dcfg = { + .device_id = SJA1105PR_DEVICE_ID, + .part_no = SJA1105R_PART_NO, + .static_ops = sja1105pqrs_table_ops, + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay, + .reset_cmd = sja1105pqrs_reset_cmd, + .regs = &sja1105pqrs_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .supports_sgmii = {false, false, false, false, true}, + .name = "SJA1105R", + .num_ports = SJA1105_NUM_PORTS, +}; + +static const struct sja1105_dcfg sja1105s_dcfg = { + .device_id = SJA1105QS_DEVICE_ID, + .part_no = SJA1105S_PART_NO, + .static_ops = sja1105pqrs_table_ops, + .setup_rgmii_delay = sja1105pqrs_setup_rgmii_delay, + .reset_cmd = sja1105pqrs_reset_cmd, + .regs = &sja1105pqrs_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 3, + [SJA1105_SPEED_100MBPS] = 2, + [SJA1105_SPEED_1000MBPS] = 1, + }, + .supports_mii = {true, true, true, true, true}, + .supports_rmii = {true, true, true, true, true}, + .supports_rgmii = {true, true, true, true, true}, + .supports_sgmii = {false, false, false, false, true}, + .name = "SJA1105S", + .num_ports = SJA1105_NUM_PORTS, +}; + +static const struct sja1105_dcfg sja1110a_dcfg = { + .device_id = SJA1110_DEVICE_ID, + .part_no = SJA1110A_PART_NO, + .static_ops = sja1110_table_ops, + .setup_rgmii_delay = sja1110_setup_rgmii_delay, + .reset_cmd = sja1110_reset_cmd, + .regs = &sja1110_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 4, + [SJA1105_SPEED_100MBPS] = 3, + [SJA1105_SPEED_1000MBPS] = 2, + }, + .supports_mii = {true, true, true, true, false, + true, true, true, true, true, true}, + .supports_rmii = {false, false, true, true, false, + false, false, false, false, false, + false}, + .supports_rgmii = {false, false, true, true, false, + false, false, false, false, false, + false}, + .supports_sgmii = {false, true, true, true, true, + false, false, false, false, false, + false}, + .name = "SJA1110A", + .num_ports = SJA1110_NUM_PORTS, +}; + +static const struct sja1105_dcfg sja1110b_dcfg = { + .device_id = SJA1110_DEVICE_ID, + .part_no = SJA1110B_PART_NO, + .static_ops = sja1110_table_ops, + .setup_rgmii_delay = sja1110_setup_rgmii_delay, + .reset_cmd = sja1110_reset_cmd, + .regs = &sja1110_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 4, + [SJA1105_SPEED_100MBPS] = 3, + [SJA1105_SPEED_1000MBPS] = 2, + }, + .supports_mii = {true, true, true, true, false, + true, true, true, true, true, false}, + .supports_rmii = {false, false, true, true, false, + false, false, false, false, false, + false}, + .supports_rgmii = {false, false, true, true, false, + false, false, false, false, false, + false}, + .supports_sgmii = {false, false, false, true, true, + false, false, false, false, false, + false}, + .name = "SJA1110B", + .num_ports = SJA1110_NUM_PORTS, +}; + +static const struct sja1105_dcfg sja1110c_dcfg = { + .device_id = SJA1110_DEVICE_ID, + .part_no = SJA1110C_PART_NO, + .static_ops = sja1110_table_ops, + .setup_rgmii_delay = sja1110_setup_rgmii_delay, + .reset_cmd = sja1110_reset_cmd, + .regs = &sja1110_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 4, + [SJA1105_SPEED_100MBPS] = 3, + [SJA1105_SPEED_1000MBPS] = 2, + }, + .supports_mii = {true, true, true, true, false, + true, true, true, false, false, + false}, + .supports_rmii = {false, false, true, true, false, + false, false, false, false, false, + false}, + .supports_rgmii = {false, false, true, true, false, + false, false, false, false, false, + false}, + .supports_sgmii = {false, false, false, false, true, + false, false, false, false, false, + false}, + .name = "SJA1110C", + .num_ports = SJA1110_NUM_PORTS, +}; + +static const struct sja1105_dcfg sja1110d_dcfg = { + .device_id = SJA1110_DEVICE_ID, + .part_no = SJA1110D_PART_NO, + .static_ops = sja1110_table_ops, + .setup_rgmii_delay = sja1110_setup_rgmii_delay, + .reset_cmd = sja1110_reset_cmd, + .regs = &sja1110_regs, + .port_speed = { + [SJA1105_SPEED_AUTO] = 0, + [SJA1105_SPEED_10MBPS] = 4, + [SJA1105_SPEED_100MBPS] = 3, + [SJA1105_SPEED_1000MBPS] = 2, + }, + .supports_mii = {true, false, true, false, false, + true, true, true, false, false, + false}, + .supports_rmii = {false, false, true, false, false, + false, false, false, false, false, + false}, + .supports_rgmii = {false, false, true, false, false, + false, false, false, false, false, + false}, + .supports_sgmii = {false, true, true, true, true, + false, false, false, false, false, + false}, + .name = "SJA1110D", + .num_ports = SJA1110_NUM_PORTS, +}; + +struct sja1105_status { + u64 configs; + u64 crcchkl; + u64 ids; + u64 crcchkg; +}; + +static void sja1105_status_unpack(void *buf, struct sja1105_status *status) +{ + sja1105_packing(buf, &status->configs, 31, 31, 4, UNPACK); + sja1105_packing(buf, &status->crcchkl, 30, 30, 4, UNPACK); + sja1105_packing(buf, &status->ids, 29, 29, 4, UNPACK); + sja1105_packing(buf, &status->crcchkg, 28, 28, 4, UNPACK); +} + +static int sja1105_status_get(struct sja1105_private *priv, + struct sja1105_status *status) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + u8 packed_buf[4]; + int rc; + + rc = sja1105_xfer_buf(priv, SPI_READ, regs->status, packed_buf, 4); + if (rc < 0) + return rc; + + sja1105_status_unpack(packed_buf, status); + + return 0; +} + +/* Not const because unpacking priv->static_config into buffers and preparing + * for upload requires the recalculation of table CRCs and updating the + * structures with these. + */ +static int +static_config_buf_prepare_for_upload(struct sja1105_private *priv, + void *config_buf, int buf_len) +{ + struct sja1105_static_config *config = &priv->static_config; + struct sja1105_table_header final_header; + char *final_header_ptr; + int crc_len; + + /* Write Device ID and config tables to config_buf */ + sja1105_static_config_pack(config_buf, config); + /* Recalculate CRC of the last header (right now 0xDEADBEEF). + * Don't include the CRC field itself. + */ + crc_len = buf_len - 4; + /* Read the whole table header */ + final_header_ptr = config_buf + buf_len - SJA1105_SIZE_TABLE_HEADER; + sja1105_table_header_packing(final_header_ptr, &final_header, UNPACK); + /* Modify */ + final_header.crc = sja1105_crc32(config_buf, crc_len); + /* Rewrite */ + sja1105_table_header_packing(final_header_ptr, &final_header, PACK); + + return 0; +} + +static int sja1105_static_config_upload(struct sja1105_private *priv) +{ + struct sja1105_static_config *config = &priv->static_config; + const struct sja1105_regs *regs = priv->dcfg->regs; + struct sja1105_status status; + u8 *config_buf; + int buf_len; + int rc; + + buf_len = sja1105_static_config_get_length(config); + config_buf = calloc(buf_len, sizeof(char)); + if (!config_buf) + return -ENOMEM; + + rc = static_config_buf_prepare_for_upload(priv, config_buf, buf_len); + if (rc < 0) { + dev_err(priv->dev, "Invalid config, cannot upload\n"); + rc = -EINVAL; + goto out; + } + /* Put the SJA1105 in programming mode */ + rc = priv->dcfg->reset_cmd(priv); + if (rc < 0) { + dev_err(priv->dev, "Failed to reset switch\n"); + goto out; + } + /* Wait for the switch to come out of reset */ + udelay(1000); + /* Upload the static config to the device */ + rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->config, + config_buf, buf_len); + if (rc < 0) { + dev_err(priv->dev, "Failed to upload config\n"); + goto out; + } + /* Check that SJA1105 responded well to the config upload */ + rc = sja1105_status_get(priv, &status); + if (rc < 0) + goto out; + + if (status.ids == 1) { + dev_err(priv->dev, "Mismatch between hardware and static config device id. Wrote 0x%llx, wants 0x%llx\n", + config->device_id, priv->dcfg->device_id); + rc = -EIO; + goto out; + } + if (status.crcchkl == 1 || status.crcchkg == 1) { + dev_err(priv->dev, "Switch reported invalid CRC on static config\n"); + rc = -EIO; + goto out; + } + if (status.configs == 0) { + dev_err(priv->dev, "Switch reported that config is invalid\n"); + rc = -EIO; + goto out; + } + +out: + free(config_buf); + return rc; +} + +static int sja1105_static_config_reload(struct sja1105_private *priv) +{ + int rc; + + rc = sja1105_static_config_upload(priv); + if (rc < 0) { + dev_err(priv->dev, "Failed to load static config: %d\n", rc); + return rc; + } + + /* Configure the CGU (PHY link modes and speeds) */ + rc = sja1105_clocking_setup(priv); + if (rc < 0) { + dev_err(priv->dev, "Failed to configure MII clocking: %d\n", + rc); + return rc; + } + + return 0; +} + +static int sja1105_port_set_mode(struct dsa_port *dp, int port, + phy_interface_t phy_mode) +{ + struct device *dev = dp->ds->dev; + struct sja1105_private *priv = dev_get_priv(dev); + struct sja1105_xmii_params_entry *mii; + + mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; + + mii->phy_mac[port] = XMII_MAC; + + switch (phy_mode) { + case PHY_INTERFACE_MODE_REVMII: + mii->phy_mac[port] = XMII_PHY; + fallthrough; + case PHY_INTERFACE_MODE_MII: + if (!priv->dcfg->supports_mii[port]) + goto unsupported; + + mii->xmii_mode[port] = XMII_MODE_MII; + break; + case PHY_INTERFACE_MODE_REVRMII: + mii->phy_mac[port] = XMII_PHY; + fallthrough; + case PHY_INTERFACE_MODE_RMII: + if (!priv->dcfg->supports_rmii[port]) + goto unsupported; + + mii->xmii_mode[port] = XMII_MODE_RMII; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + if (!priv->dcfg->supports_rgmii[port]) + goto unsupported; + + mii->xmii_mode[port] = XMII_MODE_RGMII; + break; + case PHY_INTERFACE_MODE_SGMII: + if (!priv->dcfg->supports_sgmii[port]) + goto unsupported; + + mii->xmii_mode[port] = XMII_MODE_SGMII; + mii->special[port] = true; + break; +unsupported: + default: + dev_err(dev, "Unsupported PHY mode %d on port %d!\n", + phy_mode, port); + return -EINVAL; + } + + return 0; +} + +static int sja1105_port_pre_enable(struct dsa_port *dp, int port, + phy_interface_t phy_mode) +{ + struct device *dev = dp->ds->dev; + struct sja1105_private *priv = dev_get_priv(dev); + int ret; + + ret = sja1105_port_set_mode(dp, port, phy_mode); + if (ret) + return ret; + + return sja1105_static_config_reload(priv); +} + +static void sja1105_adjust_link(struct eth_device *edev) +{ + struct dsa_port *dp = edev->priv; + struct device *dev = dp->ds->dev; + struct sja1105_private *priv = dev_get_priv(dev); + struct phy_device *phy = dp->edev.phydev; + phy_interface_t phy_mode = phy->interface; + struct sja1105_xmii_params_entry *mii; + struct sja1105_mac_config_entry *mac; + int port = dp->index; + int ret; + + mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; + mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; + + ret = sja1105_port_set_mode(dp, port, phy_mode); + if (ret) + goto error; + + /* Let the PHY handle the RGMII delays, if present. */ + if (phy->phy_id == 0) { + if (phy_mode == PHY_INTERFACE_MODE_RGMII_RXID || + phy_mode == PHY_INTERFACE_MODE_RGMII_ID) + priv->rgmii_rx_delay[port] = true; + + if (phy_mode == PHY_INTERFACE_MODE_RGMII_TXID || + phy_mode == PHY_INTERFACE_MODE_RGMII_ID) + priv->rgmii_tx_delay[port] = true; + + if ((priv->rgmii_rx_delay[port] || + priv->rgmii_tx_delay[port]) && + !priv->dcfg->setup_rgmii_delay) { + dev_err(priv->dev, "Chip does not support internal RGMII delays\n"); + return; + } + } + + if (mii->xmii_mode[port] == XMII_MODE_SGMII) { + mac[port].speed = + priv->dcfg->port_speed[SJA1105_SPEED_1000MBPS]; + priv->xpcs_cfg[port].speed = phy->speed; + } else if (phy->speed == SPEED_1000) { + mac[port].speed = + priv->dcfg->port_speed[SJA1105_SPEED_1000MBPS]; + } else if (phy->speed == SPEED_100) { + mac[port].speed = + priv->dcfg->port_speed[SJA1105_SPEED_100MBPS]; + } else if (phy->speed == SPEED_10) { + mac[port].speed = + priv->dcfg->port_speed[SJA1105_SPEED_10MBPS]; + } else { + mac[port].speed = priv->dcfg->port_speed[SJA1105_SPEED_AUTO]; + return; + } + + ret = sja1105_static_config_reload(priv); + if (ret) + goto error; + + return; + +error: + dev_err(priv->dev, "Failed to adjust link on port %d, error %pe\n", + port, ERR_PTR(ret)); +} + +static int sja1105_xmit(struct dsa_port *dp, int port, void *packet, int length) +{ + struct sja1105_private *priv = dev_get_priv(dp->ds->dev); + u8 *from = (u8 *)packet + VLAN_HLEN; + struct vlan_ethhdr *hdr = packet; + u8 *dest = (u8 *)packet; + + memmove(dest, from, 2 * ETH_ALEN); + hdr->h_vlan_proto = htons(ETH_P_SJA1105); + hdr->h_vlan_TCI = htons(priv->pvid[port]); + + return 0; +} + +static int sja1105_rcv(struct dsa_switch *ds, int *port, void *packet, + int length) +{ + struct vlan_ethhdr *hdr = packet; + u8 *dest = packet + VLAN_HLEN; + u8 *from = packet; + + if (ntohs(hdr->h_vlan_proto) != ETH_P_SJA1105) + return -EINVAL; + + *port = ntohs(hdr->h_vlan_TCI) & DSA_8021Q_PORT_MASK; + memmove(dest, from, 2 * ETH_ALEN); + + return 0; +} + +static const struct dsa_switch_ops sja1105_dsa_ops = { + .port_pre_enable = sja1105_port_pre_enable, + .adjust_link = sja1105_adjust_link, + .xmit = sja1105_xmit, + .rcv = sja1105_rcv, +}; + +static int sja1105_init(struct sja1105_private *priv) +{ + int rc; + + rc = sja1105_static_config_init(priv); + if (rc) { + dev_err(priv->dev, "Failed to initialize static config: %d\n", + rc); + return rc; + } + + return 0; +} + +static int sja1105_check_device_id(struct sja1105_private *priv) +{ + const struct sja1105_regs *regs = priv->dcfg->regs; + u8 packed_buf[SJA1105_SIZE_DEVICE_ID] = {0}; + u64 device_id; + u64 part_no; + int rc; + + rc = sja1105_xfer_buf(priv, SPI_READ, regs->device_id, packed_buf, + SJA1105_SIZE_DEVICE_ID); + if (rc < 0) + return rc; + + sja1105_packing(packed_buf, &device_id, 31, 0, SJA1105_SIZE_DEVICE_ID, + UNPACK); + + rc = sja1105_xfer_buf(priv, SPI_READ, regs->prod_id, packed_buf, + SJA1105_SIZE_DEVICE_ID); + if (rc < 0) + return rc; + + sja1105_packing(packed_buf, &part_no, 19, 4, SJA1105_SIZE_DEVICE_ID, + UNPACK); + + if (priv->dcfg->device_id != device_id || + priv->dcfg->part_no != part_no) { + dev_err(priv->dev, "Device tree specifies chip %llu/%llu but found %llu/%llu, please fix it!\n", + priv->dcfg->device_id, priv->dcfg->part_no, device_id, + part_no); + return -ENODEV; + } + + return 0; +} + +/* Configure the optional reset pin and bring up switch */ +static int sja1105_hw_reset(struct device *dev, unsigned int pulse_len, + unsigned int startup_delay) +{ + struct gpio_desc *gpio; + + gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio)) { + dev_warn(dev, "Failed to get 'reset' GPIO (ignored)\n"); + } else if (gpio) { + gpiod_set_value(gpio, 1); + /* Wait for minimum reset pulse length */ + mdelay(pulse_len); + gpiod_set_value(gpio, 0); + /* Wait until chip is ready after reset */ + mdelay(startup_delay); + } + + return 0; +} + +static int sja1105_probe(struct device *dev) +{ + struct spi_device *spi = dev->type_data; + const struct sja1105_dcfg *dcfg; + struct sja1105_private *priv; + size_t max_xfer, max_msg; + struct dsa_switch *ds; + int rc; + + dcfg = of_device_get_match_data(dev); + if (!dcfg) + return -EINVAL; + + rc = sja1105_hw_reset(dev, 1, 1); + if (rc) + return rc; + + priv = xzalloc(sizeof(*priv)); + + priv->dcfg = dcfg; + priv->dev = dev; + dev->priv = priv; + + /* spi init */ + priv->spidev = spi; + /* Configure the SPI bus */ + spi->bits_per_word = 8; + + /* In sja1105_xfer, we send spi_messages composed of two spi_transfers: + * a small one for the message header and another one for the current + * chunk of the packed buffer. + * Check that the restrictions imposed by the SPI controller are + * respected: the chunk buffer is smaller than the max transfer size, + * and the total length of the chunk plus its message header is smaller + * than the max message size. + * We do that during probe time since the maximum transfer size is a + * runtime invariant. + */ + max_xfer = spi_max_transfer_size(spi); + max_msg = spi_max_message_size(spi); + + /* We need to send at least one 64-bit word of SPI payload per message + * in order to be able to make useful progress. + */ + if (max_msg < SJA1105_SIZE_SPI_MSG_HEADER + 8) { + dev_err(dev, "SPI master cannot send large enough buffers, aborting\n"); + return -EINVAL; + } + + priv->max_xfer_len = SJA1105_SIZE_SPI_MSG_MAXLEN; + if (priv->max_xfer_len > max_xfer) + priv->max_xfer_len = max_xfer; + if (priv->max_xfer_len > max_msg - SJA1105_SIZE_SPI_MSG_HEADER) + priv->max_xfer_len = max_msg - SJA1105_SIZE_SPI_MSG_HEADER; + + rc = sja1105_check_device_id(priv); + if (rc < 0) { + dev_err(dev, "Device ID check failed: %d\n", rc); + return rc; + } + + ds = &priv->ds; + ds->dev = dev; + ds->num_ports = dcfg->num_ports; + ds->ops = &sja1105_dsa_ops; + ds->needed_headroom = VLAN_HLEN; + + rc = dsa_register_switch(ds); + if (rc) + return rc; + + return sja1105_init(priv); +} + +static const struct of_device_id sja1105_ids[] = { + { .compatible = "nxp,sja1105e", .data = &sja1105e_dcfg }, + { .compatible = "nxp,sja1105t", .data = &sja1105t_dcfg }, + { .compatible = "nxp,sja1105p", .data = &sja1105p_dcfg }, + { .compatible = "nxp,sja1105q", .data = &sja1105q_dcfg }, + { .compatible = "nxp,sja1105r", .data = &sja1105r_dcfg }, + { .compatible = "nxp,sja1105s", .data = &sja1105s_dcfg }, + { .compatible = "nxp,sja1110a", .data = &sja1110a_dcfg }, + { .compatible = "nxp,sja1110b", .data = &sja1110b_dcfg }, + { .compatible = "nxp,sja1110c", .data = &sja1110c_dcfg }, + { .compatible = "nxp,sja1110d", .data = &sja1110d_dcfg }, + { } +}; +MODULE_DEVICE_TABLE(of, sja1105_ids); + +static struct driver sja1105_driver = { + .name = "sja1105", + .probe = sja1105_probe, + .of_compatible = DRV_OF_COMPAT(sja1105_ids), +}; + +device_spi_driver(sja1105_driver); diff --git a/drivers/net/smc91111.c b/drivers/net/smc91111.c index d503535c25..4bbb2a3dee 100644 --- a/drivers/net/smc91111.c +++ b/drivers/net/smc91111.c @@ -440,12 +440,12 @@ struct smc91c111_priv { struct mii_bus miibus; struct accessors a; void __iomem *base; - int qemu_fixup; unsigned shift; int version; int revision; unsigned int control_setup; unsigned int config_setup; + void *rx_buf; }; #if (SMC_DEBUG > 2 ) @@ -1047,7 +1047,8 @@ static int smc91c111_eth_open(struct eth_device *edev) if (ret) return ret; - if (priv->qemu_fixup && edev->phydev->phy_id == 0x00000000) { + if (of_machine_is_compatible("arm,versatile-pb") || + of_machine_is_compatible("arm,versatile-ab")) { struct phy_device *dev = edev->phydev; dev->speed = SPEED_100; @@ -1302,14 +1303,14 @@ static int smc91c111_eth_rx(struct eth_device *edev) to send the DWORDs or the bytes first, or some mixture. A mixture might improve already slow PIO performance */ - SMC_insl(priv, SMC91111_DATA_REG , NetRxPackets[0], + SMC_insl(priv, SMC91111_DATA_REG , priv->rx_buf, packet_length >> 2); /* read the left over bytes */ if (packet_length & 3) { int i; unsigned char *tail = - (unsigned char *)(NetRxPackets[0] + + (unsigned char *)(priv->rx_buf + (packet_length & ~3)); unsigned long leftover = SMC_inl(priv, SMC91111_DATA_REG); @@ -1320,7 +1321,7 @@ static int smc91c111_eth_rx(struct eth_device *edev) #if SMC_DEBUG > 2 printf("Receiving Packet\n"); - print_packet( NetRxPackets[0], packet_length ); + print_packet(priv->rx_buf, packet_length ); #endif } else { /* error ... */ @@ -1343,7 +1344,7 @@ static int smc91c111_eth_rx(struct eth_device *edev) if (!is_error) { /* Pass the packet up to the protocol layers. */ - net_receive(edev, NetRxPackets[0], packet_length); + net_receive(edev, priv->rx_buf, packet_length); return 0; } @@ -1433,7 +1434,7 @@ static int smc91c111_init_dev(struct eth_device *edev) return 0; } -static int smc91c111_probe(struct device_d *dev) +static int smc91c111_probe(struct device *dev) { struct resource *iores; struct eth_device *edev; @@ -1445,11 +1446,11 @@ static int smc91c111_probe(struct device_d *dev) priv = edev->priv; priv->a = access_via_32bit; + priv->rx_buf = xmalloc(PKTSIZE); if (dev->platform_data) { struct smc91c111_pdata *pdata = dev->platform_data; - priv->qemu_fixup = pdata->qemu_fixup; priv->shift = pdata->addr_shift; if (pdata->bus_width == 16) priv->a = access_via_16bit; @@ -1487,8 +1488,18 @@ static int smc91c111_probe(struct device_d *dev) return 0; } -static struct driver_d smc91c111_driver = { - .name = "smc91c111", - .probe = smc91c111_probe, +static __maybe_unused struct of_device_id smc91c111_dt_ids[] = { + { + .compatible = "smsc,lan91c111", + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, smc91c111_dt_ids); +MODULE_DEVICE_TABLE(of, smc91c111_dt_ids); + +static struct driver smc91c111_driver = { + .of_compatible = DRV_OF_COMPAT(smc91c111_dt_ids), + .name = "smc91c111", + .probe = smc91c111_probe, }; device_platform_driver(smc91c111_driver); diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index ea7cea5f1b..767d51761b 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -32,6 +32,8 @@ struct smc911x_priv { unsigned int using_extphy; unsigned int phy_mask; + void *rx_buf; + u32 (*reg_read)(struct smc911x_priv *priv, u32 reg); void (*reg_write)(struct smc911x_priv *priv, u32 reg, u32 val); }; @@ -447,7 +449,7 @@ static void smc911x_eth_halt(struct eth_device *edev) static int smc911x_eth_rx(struct eth_device *edev) { struct smc911x_priv *priv = (struct smc911x_priv *)edev->priv; - u32 *data = (u32 *)NetRxPackets[0]; + u32 *data = priv->rx_buf; u32 pktlen, tmplen; u32 status; @@ -465,7 +467,7 @@ static int smc911x_eth_rx(struct eth_device *edev) dev_err(&edev->dev, "dropped bad packet. Status: 0x%08x\n", status); else - net_receive(edev, NetRxPackets[0], pktlen); + net_receive(edev, priv->rx_buf, pktlen); } return 0; @@ -479,7 +481,7 @@ static int smc911x_init_dev(struct eth_device *edev) return 0; } -static int smc911x_probe(struct device_d *dev) +static int smc911x_probe(struct device *dev) { struct resource *iores; struct eth_device *edev; @@ -503,18 +505,18 @@ static int smc911x_probe(struct device_d *dev) priv->shift = pdata->shift; priv->flags = pdata->flags; priv->phy_mask = pdata->phy_mask; - } else if (IS_ENABLED(CONFIG_OFDEVICE) && dev->device_node) { - ret = of_property_read_u32(dev->device_node, "reg-io-width", &val); + } else if (IS_ENABLED(CONFIG_OFDEVICE) && dev->of_node) { + ret = of_property_read_u32(dev->of_node, "reg-io-width", &val); if (ret) return ret; is_32bit = (val == 4); - of_property_read_u32(dev->device_node, "reg-shift", &priv->shift); + of_property_read_u32(dev->of_node, "reg-shift", &priv->shift); - if (of_property_read_bool(dev->device_node, "smsc,force-internal-phy")) + if (of_property_read_bool(dev->of_node, "smsc,force-internal-phy")) priv->flags |= SMC911X_FORCE_INTERNAL_PHY; - if (of_property_read_bool(dev->device_node, "smsc,force-external-phy")) + if (of_property_read_bool(dev->of_node, "smsc,force-external-phy")) priv->flags |= SMC911X_FORCE_EXTERNAL_PHY; } @@ -541,7 +543,7 @@ static int smc911x_probe(struct device_d *dev) * forbidden while this bit isn't set. Try for 100ms */ ret = wait_on_timeout(100 * MSECOND, smc911x_reg_read(priv, PMT_CTRL) & PMT_CTRL_READY); - if (!ret) { + if (ret) { dev_err(dev, "Device not READY in 100ms aborting\n"); return -ENODEV; } @@ -608,6 +610,8 @@ static int smc911x_probe(struct device_d *dev) dev_info(dev, "LAN911x identified, idrev: 0x%08X, generation: %d\n", val, priv->generation); + priv->rx_buf = xmalloc(PKTSIZE); + edev = &priv->edev; edev->priv = priv; @@ -638,8 +642,9 @@ static const struct of_device_id smsc911x_dt_ids[] = { { .compatible = "smsc,lan9115", }, { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, smsc911x_dt_ids); -static struct driver_d smc911x_driver = { +static struct driver smc911x_driver = { .name = "smc911x", .probe = smc911x_probe, .of_compatible = DRV_OF_COMPAT(smsc911x_dt_ids), diff --git a/drivers/net/tap.c b/drivers/net/tap.c index 1fbfa085b1..18f83c6a2c 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -15,6 +15,7 @@ struct tap_priv { int fd; char *name; + char *rx_buf; }; static int tap_eth_send(struct eth_device *edev, void *packet, int length) @@ -30,10 +31,10 @@ static int tap_eth_rx(struct eth_device *edev) struct tap_priv *priv = edev->priv; int length; - length = linux_read_nonblock(priv->fd, NetRxPackets[0], PKTSIZE); + length = linux_read_nonblock(priv->fd, priv->rx_buf, PKTSIZE); if (length > 0) - net_receive(edev, NetRxPackets[0], length); + net_receive(edev, priv->rx_buf, length); return 0; } @@ -58,7 +59,7 @@ static int tap_set_ethaddr(struct eth_device *edev, const unsigned char *adr) return 0; } -static int tap_probe(struct device_d *dev) +static int tap_probe(struct device *dev) { struct eth_device *edev; struct tap_priv *priv; @@ -73,6 +74,8 @@ static int tap_probe(struct device_d *dev) goto out; } + priv->rx_buf = xmalloc(PKTSIZE); + edev = xzalloc(sizeof(struct eth_device)); edev->priv = priv; edev->parent = dev; @@ -94,7 +97,7 @@ out: return ret; } -static struct driver_d tap_driver = { +static struct driver tap_driver = { .name = "tap", .probe = tap_probe, }; diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 5bad9546be..6dc6a24aee 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -1,15 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0-only menuconfig NET_USB depends on USB_HOST + select PHYLIB bool "USB network support" if NET_USB config NET_USB_ASIX - select PHYLIB bool "Asix compatible" +config USB_NET_AX88179_178A + tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet" + help + This option adds support for ASIX AX88179 based USB 3.0/2.0 + to Gigabit Ethernet adapters. + + This driver should work with at least the following devices: + * ASIX AX88179 + * ASIX AX88178A + * Sitcomm LN-032 + config NET_USB_SMSC95XX - select PHYLIB bool "SMSC95xx" +config NET_USB_RTL8152 + bool "Realtek RTL8152B/RTL8153 support" + help + Say Y here if you would like to support Realtek RTL8152B/RTL8153 base + USB Ethernet Devices. This driver also supports compatible devices + from Samsung, Lenovo, TP-LINK and Nvidia. + endif diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index 564e44de4e..d876438c3f 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -1,3 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_NET_USB) += usbnet.o obj-$(CONFIG_NET_USB_ASIX) += asix.o +obj-$(CONFIG_USB_NET_AX88179_178A) += ax88179_178a.o obj-$(CONFIG_NET_USB_SMSC95XX) += smsc95xx.o +obj-$(CONFIG_NET_USB_RTL8152) += r8152.o r8152_fw.o diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 3ca27ff027..9d34beab0d 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -1,9 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> #include <init.h> #include <net.h> #include <linux/phy.h> -#include <usb/usb.h> -#include <usb/usbnet.h> +#include <linux/usb/usb.h> +#include <linux/usb/usbnet.h> #include <errno.h> #include <malloc.h> #include <asm/byteorder.h> @@ -252,11 +253,19 @@ static int asix_mdio_read(struct mii_bus *bus, int phy_id, int loc) { struct usbnet *dev = bus->priv; __le16 res; + int ret; + + ret = asix_set_sw_mii(dev); + if (ret < 0) + return ret; - asix_set_sw_mii(dev); - asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, - (__u16)loc, 2, &res); - asix_set_hw_mii(dev); + ret = asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id, (__u16)loc, 2, &res); + if (ret < 0) + return ret; + + ret = asix_set_hw_mii(dev); + if (ret < 0) + return ret; dev_dbg(&dev->edev.dev, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n", phy_id, loc, le16_to_cpu(res)); @@ -268,13 +277,22 @@ static int asix_mdio_write(struct mii_bus *bus, int phy_id, int loc, u16 val) { struct usbnet *dev = bus->priv; __le16 res = cpu_to_le16(val); + int ret; dev_dbg(&dev->edev.dev, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n", phy_id, loc, val); - asix_set_sw_mii(dev); - asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res); - asix_set_hw_mii(dev); + ret = asix_set_sw_mii(dev); + if (ret < 0) + return ret; + + ret = asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res); + if (ret < 0) + return ret; + + ret = asix_set_hw_mii(dev); + if (ret < 0) + return ret; return 0; } @@ -409,6 +427,25 @@ static int asix_set_ethaddr(struct eth_device *edev, const unsigned char *adr) return 0; } +static int asix_set_promisc(struct eth_device *edev, bool enable) +{ + struct usbnet *dev = container_of(edev, struct usbnet, edev); + u16 rx_ctl; + int ret; + + rx_ctl = asix_read_rx_ctl(dev); + + if (enable) + rx_ctl |= AX_RX_CTL_PRO; + else + rx_ctl &= ~AX_RX_CTL_PRO; + + if ((ret = asix_write_rx_ctl(dev, rx_ctl)) < 0) + return ret; + + return 0; +} + static int ax88172_get_ethaddr(struct eth_device *edev, unsigned char *adr) { struct usbnet *udev = container_of(edev, struct usbnet, edev); @@ -646,6 +683,7 @@ static int ax88772_bind(struct usbnet *dev) dev->edev.get_ethaddr = asix_get_ethaddr; dev->edev.set_ethaddr = asix_set_ethaddr; + dev->edev.set_promisc = asix_set_promisc; asix_init_mii(dev); if ((ret = asix_sw_reset(dev, AX_SWRESET_PRL)) < 0) diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c new file mode 100644 index 0000000000..c6108c488b --- /dev/null +++ b/drivers/net/usb/ax88179_178a.c @@ -0,0 +1,753 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices + * + * Copyright (C) 2011-2013 ASIX + */ +#include <common.h> +#include <init.h> +#include <net.h> +#include <linux/phy.h> +#include <linux/usb/usb.h> +#include <linux/usb/usbnet.h> +#include <errno.h> +#include <malloc.h> +#include <poller.h> +#include <dma.h> +#include <asm/byteorder.h> +#include <asm/unaligned.h> + +#define AX88179_PHY_ID 0x03 +#define AX_EEPROM_LEN 0x100 +#define AX88179_EEPROM_MAGIC 0x17900b95 +#define AX_MCAST_FLTSIZE 8 +#define AX_MAX_MCAST 64 +#define AX_INT_PPLS_LINK ((u32)BIT(16)) +#define AX_RXHDR_L4_TYPE_MASK 0x1c +#define AX_RXHDR_L4_TYPE_UDP 4 +#define AX_RXHDR_L4_TYPE_TCP 16 +#define AX_RXHDR_L3CSUM_ERR 2 +#define AX_RXHDR_L4CSUM_ERR 1 +#define AX_RXHDR_CRC_ERR ((u32)BIT(29)) +#define AX_RXHDR_DROP_ERR ((u32)BIT(31)) +#define AX_ACCESS_MAC 0x01 +#define AX_ACCESS_PHY 0x02 +#define AX_ACCESS_EEPROM 0x04 +#define AX_ACCESS_EFUS 0x05 +#define AX_PAUSE_WATERLVL_HIGH 0x54 +#define AX_PAUSE_WATERLVL_LOW 0x55 + +#define PHYSICAL_LINK_STATUS 0x02 + #define AX_USB_SS 0x04 + #define AX_USB_HS 0x02 + +#define GENERAL_STATUS 0x03 +/* Check AX88179 version. UA1:Bit2 = 0, UA2:Bit2 = 1 */ + #define AX_SECLD 0x04 + +#define AX_SROM_ADDR 0x07 +#define AX_SROM_CMD 0x0a + #define EEP_RD 0x04 + #define EEP_BUSY 0x10 + +#define AX_SROM_DATA_LOW 0x08 +#define AX_SROM_DATA_HIGH 0x09 + +#define AX_RX_CTL 0x0b + #define AX_RX_CTL_DROPCRCERR 0x0100 + #define AX_RX_CTL_IPE 0x0200 + #define AX_RX_CTL_START 0x0080 + #define AX_RX_CTL_AP 0x0020 + #define AX_RX_CTL_AM 0x0010 + #define AX_RX_CTL_AB 0x0008 + #define AX_RX_CTL_AMALL 0x0002 + #define AX_RX_CTL_PRO 0x0001 + #define AX_RX_CTL_STOP 0x0000 + +#define AX_NODE_ID 0x10 +#define AX_MULFLTARY 0x16 + +#define AX_MEDIUM_STATUS_MODE 0x22 + #define AX_MEDIUM_GIGAMODE 0x01 + #define AX_MEDIUM_FULL_DUPLEX 0x02 + #define AX_MEDIUM_EN_125MHZ 0x08 + #define AX_MEDIUM_RXFLOW_CTRLEN 0x10 + #define AX_MEDIUM_TXFLOW_CTRLEN 0x20 + #define AX_MEDIUM_RECEIVE_EN 0x100 + #define AX_MEDIUM_PS 0x200 + #define AX_MEDIUM_JUMBO_EN 0x8040 + +#define AX_MONITOR_MOD 0x24 + #define AX_MONITOR_MODE_RWLC 0x02 + #define AX_MONITOR_MODE_RWMP 0x04 + #define AX_MONITOR_MODE_PMEPOL 0x20 + #define AX_MONITOR_MODE_PMETYPE 0x40 + +#define AX_GPIO_CTRL 0x25 + #define AX_GPIO_CTRL_GPIO3EN 0x80 + #define AX_GPIO_CTRL_GPIO2EN 0x40 + #define AX_GPIO_CTRL_GPIO1EN 0x20 + +#define AX_PHYPWR_RSTCTL 0x26 + #define AX_PHYPWR_RSTCTL_BZ 0x0010 + #define AX_PHYPWR_RSTCTL_IPRL 0x0020 + #define AX_PHYPWR_RSTCTL_AT 0x1000 + +#define AX_RX_BULKIN_QCTRL 0x2e +#define AX_CLK_SELECT 0x33 + #define AX_CLK_SELECT_BCS 0x01 + #define AX_CLK_SELECT_ACS 0x02 + #define AX_CLK_SELECT_ULR 0x08 + +#define AX_RXCOE_CTL 0x34 + #define AX_RXCOE_IP 0x01 + #define AX_RXCOE_TCP 0x02 + #define AX_RXCOE_UDP 0x04 + #define AX_RXCOE_TCPV6 0x20 + #define AX_RXCOE_UDPV6 0x40 + +#define AX_TXCOE_CTL 0x35 + #define AX_TXCOE_IP 0x01 + #define AX_TXCOE_TCP 0x02 + #define AX_TXCOE_UDP 0x04 + #define AX_TXCOE_TCPV6 0x20 + #define AX_TXCOE_UDPV6 0x40 + +#define AX_LEDCTRL 0x73 + +#define GMII_PHY_PHYSR 0x11 + #define GMII_PHY_PHYSR_SMASK 0xc000 + #define GMII_PHY_PHYSR_GIGA 0x8000 + #define GMII_PHY_PHYSR_100 0x4000 + #define GMII_PHY_PHYSR_FULL 0x2000 + #define GMII_PHY_PHYSR_LINK 0x400 + +#define GMII_LED_ACT 0x1a + #define GMII_LED_ACTIVE_MASK 0xff8f + #define GMII_LED0_ACTIVE BIT(4) + #define GMII_LED1_ACTIVE BIT(5) + #define GMII_LED2_ACTIVE BIT(6) + +#define GMII_LED_LINK 0x1c + #define GMII_LED_LINK_MASK 0xf888 + #define GMII_LED0_LINK_10 BIT(0) + #define GMII_LED0_LINK_100 BIT(1) + #define GMII_LED0_LINK_1000 BIT(2) + #define GMII_LED1_LINK_10 BIT(4) + #define GMII_LED1_LINK_100 BIT(5) + #define GMII_LED1_LINK_1000 BIT(6) + #define GMII_LED2_LINK_10 BIT(8) + #define GMII_LED2_LINK_100 BIT(9) + #define GMII_LED2_LINK_1000 BIT(10) + #define LED0_ACTIVE BIT(0) + #define LED0_LINK_10 BIT(1) + #define LED0_LINK_100 BIT(2) + #define LED0_LINK_1000 BIT(3) + #define LED0_FD BIT(4) + #define LED0_USB3_MASK 0x001f + #define LED1_ACTIVE BIT(5) + #define LED1_LINK_10 BIT(6) + #define LED1_LINK_100 BIT(7) + #define LED1_LINK_1000 BIT(8) + #define LED1_FD BIT(9) + #define LED1_USB3_MASK 0x03e0 + #define LED2_ACTIVE BIT(10) + #define LED2_LINK_1000 BIT(13) + #define LED2_LINK_100 BIT(12) + #define LED2_LINK_10 BIT(11) + #define LED2_FD BIT(14) + #define LED_VALID BIT(15) + #define LED2_USB3_MASK 0x7c00 + +#define GMII_PHYPAGE 0x1e +#define GMII_PHY_PAGE_SELECT 0x1f + #define GMII_PHY_PGSEL_EXT 0x0007 + #define GMII_PHY_PGSEL_PAGE0 0x0000 + #define GMII_PHY_PGSEL_PAGE3 0x0003 + #define GMII_PHY_PGSEL_PAGE5 0x0005 + +static const struct { + unsigned char ctrl, timer_l, timer_h, size, ifg; +} AX88179_BULKIN_SIZE[] = { + {7, 0x4f, 0, 2, 0xff}, + {7, 0x20, 3, 3, 0xff}, + {7, 0xae, 7, 4, 0xff}, + {7, 0xcc, 0x4c, 4, 8}, +}; + +static +int usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, + u16 value, u16 index, void *data, u16 size) +{ + void *buf = NULL; + int err = -ENOMEM; + + if (size) { + buf = dma_alloc(size); + if (!buf) + goto out; + } + + err = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + cmd, reqtype, value, index, buf, size, + USB_CTRL_GET_TIMEOUT); + if (err > 0 && err <= size) { + if (data) + memcpy(data, buf, err); + } + free(buf); +out: + return err; +} + +static +int usbnet_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype, + u16 value, u16 index, const void *data, u16 size) +{ + void *buf = NULL; + int err = -ENOMEM; + + if (data) { + buf = dma_alloc(size); + if (!buf) + goto out; + memcpy(buf, data, size); + } else { + if (size) { + err = -EINVAL; + goto out; + } + } + + err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + cmd, reqtype, value, index, buf, size, + USB_CTRL_SET_TIMEOUT); + free(buf); + +out: + return err; +} + +static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int ret; + + BUG_ON(!dev); + + ret = usbnet_read_cmd(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, data, size); + + if (ret < 0) + dev_warn(&dev->edev.dev, "Failed to read reg index 0x%04x: %d\n", + index, ret); + + return ret; +} + +static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, + u16 size, const void *data) +{ + int ret; + + BUG_ON(!dev); + + ret = usbnet_write_cmd(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, data, size); + + if (ret < 0) + dev_warn(&dev->edev.dev, "Failed to write reg index 0x%04x: %d\n", + index, ret); + + return ret; +} + +static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, + u16 size, void *data) +{ + int ret; + + if (size == 2) { + u16 buf; + ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf); + *((u16 *)data) = le16_to_cpu(buf); + } else if (size == 4) { + u32 buf; + ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf); + *((u32 *)data) = le32_to_cpu(buf); + } else { + ret = __ax88179_read_cmd(dev, cmd, value, index, size, data); + } + + return ret; +} + +static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, + u16 size, const void *data) +{ + int ret; + + if (size == 2) { + u16 buf = cpu_to_le16(*((u16 *)data)); + ret = __ax88179_write_cmd(dev, cmd, value, index, size, &buf); + } else { + ret = __ax88179_write_cmd(dev, cmd, value, index, size, data); + } + + return ret; +} + +static int ax88179_mdio_read(struct mii_bus *bus, int phy_id, int loc) +{ + struct usbnet *dev = bus->priv; + u16 res; + int ret; + u16 tmp16; + + tmp16 = AX_PHYPWR_RSTCTL_IPRL; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); + + ret = ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res); + if (ret < 0) + return ret; + + dev_dbg(&dev->udev->dev, "%s: phy: %d loc: %d ret: %d, res: 0x%04x\n", + __func__, phy_id, loc, ret, res); + + return res; +} + +static int ax88179_mdio_write(struct mii_bus *bus, int phy_id, int loc, u16 val) +{ + struct usbnet *dev = bus->priv; + u16 res = (u16) val; + int ret; + + ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res); + if (ret < 0) + return ret; + + return 0; +} + +static int ax88179_set_mac_addr(struct eth_device *edev, const unsigned char *adr) +{ + struct usbnet *udev = container_of(edev, struct usbnet, edev); + int ret; + + ret = ax88179_write_cmd(udev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, + ETH_ALEN, adr); + if (ret < 0) + return ret; + + return 0; +} + +static int ax88179_get_mac_addr(struct eth_device *edev, unsigned char *adr) +{ + struct usbnet *udev = container_of(edev, struct usbnet, edev); + + ax88179_read_cmd(udev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, + ETH_ALEN, adr); + return 0; +} + +struct ax88179_priv { + struct poller_struct poller; + uint64_t last; + struct usbnet *dev; +}; + +/* + * FIXME: What is happening here? We have to read from the mdio bus every few + * seconds. Otherwise the mdio registers all return zero and the link goes + * down. It seems the phy goes into some power saving mode, but I can't + * find any reason for this or any traces in the U-Boot or kernel driver + * what we could do different. + */ +static void ax88179_poller(struct poller_struct *poller) +{ + struct ax88179_priv *priv = container_of(poller, struct ax88179_priv, poller); + struct usbnet *dev = priv->dev; + + if (!is_timeout_non_interruptible(priv->last, 2 * SECOND)) + return; + + priv->last = get_time_ns(); + + ax88179_mdio_read(&dev->miibus, 3, 0); +} + +static int ax88179_bind(struct usbnet *dev) +{ + int ret; + struct ax88179_priv *priv; + + dev_dbg(&dev->udev->dev, "%s\n", __func__); + + usbnet_get_endpoints(dev); + + /* Initialize MII structure */ + dev->miibus.parent = &dev->udev->dev; + dev->miibus.read = ax88179_mdio_read; + dev->miibus.write = ax88179_mdio_write; + dev->miibus.priv = dev; + dev->phy_addr = AX88179_PHY_ID; + + dev->rx_urb_size = 1024 * (AX88179_BULKIN_SIZE[3].size + 2); + + ret = mdiobus_register(&dev->miibus); + if (ret) + return ret; + + dev->edev.get_ethaddr = ax88179_get_mac_addr; + dev->edev.set_ethaddr = ax88179_set_mac_addr; + + priv = xzalloc(sizeof(*priv)); + priv->dev = dev; + dev->driver_priv = priv; + + priv->last = get_time_ns(); + priv->poller.func = ax88179_poller; + poller_register(&priv->poller, dev_name(&dev->udev->dev)); + + return 0; +} + +static void ax88179_unbind(struct usbnet *dev) +{ + u16 tmp16; + + /* Configure RX control register => stop operation */ + tmp16 = AX_RX_CTL_STOP; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); + + tmp16 = 0; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16); + + /* Power down ethernet PHY */ + tmp16 = 0; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); +} + +static int ax88179_rx_fixup(struct usbnet *dev, void *buf, int len) +{ + int pkt_cnt, frame_pos; + u32 rx_hdr; + u16 hdr_off; + u32 *pkt_hdr; + + if (len == dev->rx_urb_size) { + dev_err(&dev->udev->dev, "broken package\n"); + return 0; + } + + rx_hdr = get_unaligned_le32(buf + len - 4); + + pkt_cnt = (u16)rx_hdr; + hdr_off = (u16)(rx_hdr >> 16); + pkt_hdr = (u32 *)(buf + hdr_off); + + frame_pos = 0; + + while (pkt_cnt--) { + u16 pkt_len; + u32 hdr = le32_to_cpup(pkt_hdr); + + pkt_len = (hdr >> 16) & 0x1fff; + + /* Check CRC or runt packet */ + if ((hdr & AX_RXHDR_CRC_ERR) || + (hdr & AX_RXHDR_DROP_ERR)) { + pkt_hdr++; + continue; + } + + frame_pos += 2; + + dev_dbg(&dev->udev->dev, "%s: loop: frame_pos: %d len: %d\n", + __func__, frame_pos, pkt_len); + + net_receive(&dev->edev, buf + frame_pos, pkt_len); + + pkt_hdr++; + frame_pos += ((pkt_len + 7) & 0xfff8) - 2; + } + + return 0; +} + +static int ax88179_tx_fixup(struct usbnet *dev, void *buf, int len, + void *nbuf, int *nlen) +{ + u32 tx_hdr1, tx_hdr2; + int frame_size = dev->maxpacket; + + tx_hdr1 = len; + tx_hdr2 = 0; + if (((len + 8) % frame_size) == 0) + tx_hdr2 |= 0x80008000; /* Enable padding */ + + put_unaligned_le32(tx_hdr1, nbuf); + put_unaligned_le32(tx_hdr2, nbuf + 4); + + memcpy(nbuf + 8, buf, len); + + *nlen = len + 8; + + return 0; +} + +static int ax88179_link_reset(struct usbnet *dev) +{ + u8 link_sts; + u16 mode, physr; + int idx; + + dev_dbg(&dev->udev->dev, "%s\n", __func__); + + mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | + AX_MEDIUM_RXFLOW_CTRLEN; + + ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, + 1, 1, &link_sts); + + ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, + GMII_PHY_PHYSR, 2, &physr); + + if (!(physr & GMII_PHY_PHYSR_LINK)) + return 0; + + dev_dbg(&dev->udev->dev, "%s: link_sts: 0x%08x GMII_PHY_PHYSR: 0x%08x\n", + __func__, link_sts, physr); + + if ((physr & GMII_PHY_PHYSR_SMASK) == GMII_PHY_PHYSR_GIGA) { + mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ; + + if (link_sts & AX_USB_SS) + idx = 0; + else if (link_sts & AX_USB_HS) + idx = 1; + else + idx = 3; + } else if ((physr & GMII_PHY_PHYSR_SMASK) == GMII_PHY_PHYSR_100) { + mode |= AX_MEDIUM_PS; + + if (link_sts & (AX_USB_SS | AX_USB_HS)) + idx = 2; + else + idx = 3; + } else { + idx = 3; + } + + /* RX bulk configuration */ + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, + &AX88179_BULKIN_SIZE[idx]); + + dev->rx_urb_size = 1024 * (AX88179_BULKIN_SIZE[idx].size + 2); + + if (physr & GMII_PHY_PHYSR_FULL) + mode |= AX_MEDIUM_FULL_DUPLEX; + + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + 2, 2, &mode); + + return 0; +} + +static int ax88179_reset(struct usbnet *dev) +{ + u16 tmp16; + u8 tmp; + + dev_dbg(&dev->udev->dev, "%s\n", __func__); + + /* Power up ethernet PHY */ + tmp16 = 0; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); + + tmp16 = AX_PHYPWR_RSTCTL_IPRL; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16); + mdelay(200); + + tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp); + mdelay(100); + + /* RX bulk configuration */ + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, + &AX88179_BULKIN_SIZE[0]); + + tmp = 0x34; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, &tmp); + + tmp = 0x52; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, + 1, 1, &tmp); + + /* Enable checksum offload */ + tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP | + AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp); + + tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP | + AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp); + + /* Configure RX control register => start operation */ + tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START | + AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16); + + tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | + AX_MONITOR_MODE_RWMP; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, &tmp); + + /* Configure default medium type => giga */ + tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN | + AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX | + AX_MEDIUM_GIGAMODE; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, + 2, 2, &tmp16); + + return 0; +} + +static const struct driver_info ax88179_info = { + .description = "ASIX AX88179 USB 3.0 Gigabit Ethernet", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info ax88178a_info = { + .description = "ASIX AX88178A USB 2.0 Gigabit Ethernet", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info cypress_GX3_info = { + .description = "Cypress GX3 SuperSpeed to Gigabit Ethernet Controller", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info dlink_dub1312_info = { + .description = "D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info sitecom_info = { + .description = "Sitecom USB 3.0 to Gigabit Adapter", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info samsung_info = { + .description = "Samsung USB Ethernet Adapter", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info lenovo_info = { + .description = "Lenovo OneLinkDock Gigabit LAN", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct driver_info belkin_info = { + .description = "Belkin USB Ethernet Adapter", + .bind = ax88179_bind, + .unbind = ax88179_unbind, + .link_reset = ax88179_link_reset, + .reset = ax88179_reset, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = ax88179_rx_fixup, + .tx_fixup = ax88179_tx_fixup, +}; + +static const struct usb_device_id products[] = { +{ + /* ASIX AX88179 10/100/1000 */ + USB_DEVICE(0x0b95, 0x1790), + .driver_info = &ax88179_info, +}, { + /* ASIX AX88178A 10/100/1000 */ + USB_DEVICE(0x0b95, 0x178a), + .driver_info = &ax88178a_info, +}, { + /* Cypress GX3 SuperSpeed to Gigabit Ethernet Bridge Controller */ + USB_DEVICE(0x04b4, 0x3610), + .driver_info = &cypress_GX3_info, +}, { + /* D-Link DUB-1312 USB 3.0 to Gigabit Ethernet Adapter */ + USB_DEVICE(0x2001, 0x4a00), + .driver_info = &dlink_dub1312_info, +}, { + /* Sitecom USB 3.0 to Gigabit Adapter */ + USB_DEVICE(0x0df6, 0x0072), + .driver_info = &sitecom_info, +}, { + /* Samsung USB Ethernet Adapter */ + USB_DEVICE(0x04e8, 0xa100), + .driver_info = &samsung_info, +}, { + /* Lenovo OneLinkDock Gigabit LAN */ + USB_DEVICE(0x17ef, 0x304b), + .driver_info = &lenovo_info, +}, { + /* Belkin B2B128 USB 3.0 Hub + Gigabit Ethernet Adapter */ + USB_DEVICE(0x050d, 0x0128), + .driver_info = &belkin_info, +}, + { }, +}; + +static struct usb_driver ax88179_178a_driver = { + .name = "ax88179_178a", + .id_table = products, + .probe = usbnet_probe, + .disconnect = usbnet_disconnect, +}; + +static int __init ax88179_178a_init(void) +{ + return usb_driver_register(&ax88179_178a_driver); +} +device_initcall(ax88179_178a_init); diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c new file mode 100644 index 0000000000..2511c524cd --- /dev/null +++ b/drivers/net/usb/r8152.c @@ -0,0 +1,1593 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015 Realtek Semiconductor Corp. All rights reserved. */ + +#include <common.h> +#include <dma.h> +#include <errno.h> +#include <init.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include <linux/phy.h> +#include <linux/usb/usb.h> +#include <linux/usb/usbnet.h> +#include "r8152.h" + +#define R8152_TX_BURST_SIZE 512 +#define R8152_RX_BURST_SIZE 64 + +struct r8152_version { + unsigned short tcr; + unsigned short version; + bool gmii; +}; + +static const struct r8152_version r8152_versions[] = { + { 0x4c00, RTL_VER_01, 0 }, + { 0x4c10, RTL_VER_02, 0 }, + { 0x5c00, RTL_VER_03, 1 }, + { 0x5c10, RTL_VER_04, 1 }, + { 0x5c20, RTL_VER_05, 1 }, + { 0x5c30, RTL_VER_06, 1 }, + { 0x4800, RTL_VER_07, 0 }, + { 0x6000, RTL_VER_08, 1 }, + { 0x6010, RTL_VER_09, 1 }, +}; + +static inline struct r8152 *r8152_get_priv(struct usbnet *dev) +{ + return (struct r8152 *)dev->driver_priv; +} + +static int r8152_get_registers(struct r8152 *tp, u16 value, u16 index, u16 size, + void *data) +{ + int ret; + + if (WARN_ON(size > R8152_RX_BURST_SIZE)) + return -EINVAL; + + ret = usb_control_msg(tp->udev, usb_rcvctrlpipe(tp->udev, 0), + RTL8152_REQ_GET_REGS, RTL8152_REQT_READ, + value, index, tp->rxbuf, size, 500); + memcpy(data, tp->rxbuf, size); + + return ret; +} + +static int r8152_set_registers(struct r8152 *tp, u16 value, u16 index, u16 size, + const void *data) +{ + int ret; + + if (WARN_ON(size > R8152_TX_BURST_SIZE)) + return -EINVAL; + + memcpy(tp->txbuf, data, size); + ret = usb_control_msg(tp->udev, usb_sndctrlpipe(tp->udev, 0), + RTL8152_REQ_SET_REGS, RTL8152_REQT_WRITE, + value, index, tp->txbuf, size, 500); + + return ret; +} + +static int r8152_generic_ocp_read(struct r8152 *tp, u16 index, u16 size, + void *data, u16 type) +{ + u16 burst_size = R8152_RX_BURST_SIZE; + int txsize; + int ret; + + /* both size and index must be 4 bytes align */ + if ((size & 3) || !size || (index & 3) || !data) + return -EINVAL; + + if (index + size > 0xffff) + return -EINVAL; + + while (size) { + txsize = min(size, burst_size); + ret = r8152_get_registers(tp, index, type, txsize, data); + if (ret < 0) + break; + + index += txsize; + data += txsize; + size -= txsize; + } + + return ret; +} + +int r8152_generic_ocp_write(struct r8152 *tp, u16 index, u16 byteen, + u16 size, const void *data, u16 type) +{ + u16 byteen_start, byteen_end, byte_en_to_hw; + u16 burst_size = R8152_TX_BURST_SIZE; + int txsize; + int ret; + + /* both size and index must be 4 bytes align */ + if ((size & 3) || !size || (index & 3) || !data) + return -EINVAL; + + if (index + size > 0xffff) + return -EINVAL; + + byteen_start = byteen & BYTE_EN_START_MASK; + byteen_end = byteen & BYTE_EN_END_MASK; + + byte_en_to_hw = byteen_start | (byteen_start << 4); + ret = r8152_set_registers(tp, index, type | byte_en_to_hw, 4, data); + if (ret < 0) + return ret; + + index += 4; + data += 4; + size -= 4; + + if (size) { + size -= 4; + + while (size) { + txsize = min(size, burst_size); + + ret = r8152_set_registers(tp, index, + type | BYTE_EN_DWORD, + txsize, data); + if (ret < 0) + return ret; + + index += txsize; + data += txsize; + size -= txsize; + } + + byte_en_to_hw = byteen_end | (byteen_end >> 4); + ret = r8152_set_registers(tp, index, type | byte_en_to_hw, 4, + data); + if (ret < 0) + return ret; + } + + return ret; +} + +static int r8152_pla_ocp_read(struct r8152 *tp, u16 index, u16 size, void *data) +{ + return r8152_generic_ocp_read(tp, index, size, data, MCU_TYPE_PLA); +} + +static int r8152_pla_ocp_write(struct r8152 *tp, u16 index, u16 byteen, + u16 size, const void *data) +{ + return r8152_generic_ocp_write(tp, index, byteen, size, data, + MCU_TYPE_PLA); +} + +static int r8152_usb_ocp_write(struct r8152 *tp, u16 index, u16 byteen, + u16 size, const void *data) +{ + return r8152_generic_ocp_write(tp, index, byteen, size, data, + MCU_TYPE_USB); +} + +static u32 r8152_ocp_read_dword(struct r8152 *tp, u16 type, u16 index) +{ + __le32 data; + + r8152_generic_ocp_read(tp, index, sizeof(data), &data, type); + + return __le32_to_cpu(data); +} + +static void r8152_ocp_write_dword(struct r8152 *tp, u16 type, u16 index, u32 data) +{ + __le32 tmp = __cpu_to_le32(data); + + r8152_generic_ocp_write(tp, index, BYTE_EN_DWORD, sizeof(tmp), &tmp, + type); +} + +u16 r8152_ocp_read_word(struct r8152 *tp, u16 type, u16 index) +{ + u32 data; + __le32 tmp; + u8 shift = index & 2; + + index &= ~3; + + r8152_generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); + + data = __le32_to_cpu(tmp); + data >>= (shift * 8); + data &= 0xffff; + + return data; +} + +void r8152_ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data) +{ + u32 mask = 0xffff; + __le32 tmp; + u16 byen = BYTE_EN_WORD; + u8 shift = index & 2; + + data &= mask; + + if (index & 2) { + byen <<= shift; + mask <<= (shift * 8); + data <<= (shift * 8); + index &= ~3; + } + + tmp = __cpu_to_le32(data); + + r8152_generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); +} + +u8 r8152_ocp_read_byte(struct r8152 *tp, u16 type, u16 index) +{ + u32 data; + __le32 tmp; + u8 shift = index & 3; + + index &= ~3; + + r8152_generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); + + data = __le32_to_cpu(tmp); + data >>= (shift * 8); + data &= 0xff; + + return data; +} + +void r8152_ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data) +{ + u32 mask = 0xff; + __le32 tmp; + u16 byen = BYTE_EN_BYTE; + u8 shift = index & 3; + + data &= mask; + + if (index & 3) { + byen <<= shift; + mask <<= (shift * 8); + data <<= (shift * 8); + index &= ~3; + } + + tmp = __cpu_to_le32(data); + + r8152_generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); +} + +u16 r8152_ocp_reg_read(struct r8152 *tp, u16 addr) +{ + u16 ocp_base, ocp_index; + + ocp_base = addr & 0xf000; + if (ocp_base != tp->ocp_base) { + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, + ocp_base); + tp->ocp_base = ocp_base; + } + + ocp_index = (addr & 0x0fff) | 0xb000; + return r8152_ocp_read_word(tp, MCU_TYPE_PLA, ocp_index); +} + +void r8152_ocp_reg_write(struct r8152 *tp, u16 addr, u16 data) +{ + u16 ocp_base, ocp_index; + + ocp_base = addr & 0xf000; + if (ocp_base != tp->ocp_base) { + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, + ocp_base); + tp->ocp_base = ocp_base; + } + + ocp_index = (addr & 0x0fff) | 0xb000; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, ocp_index, data); +} + +static void r8152_mdio_write(struct r8152 *tp, u32 reg_addr, u32 value) +{ + r8152_ocp_reg_write(tp, OCP_BASE_MII + reg_addr * 2, value); +} + +static int r8152_mdio_read(struct r8152 *tp, u32 reg_addr) +{ + return r8152_ocp_reg_read(tp, OCP_BASE_MII + reg_addr * 2); +} + +void r8152_sram_write(struct r8152 *tp, u16 addr, u16 data) +{ + r8152_ocp_reg_write(tp, OCP_SRAM_ADDR, addr); + r8152_ocp_reg_write(tp, OCP_SRAM_DATA, data); +} + +static u16 r8152_sram_read(struct r8152 *tp, u16 addr) +{ + r8152_ocp_reg_write(tp, OCP_SRAM_ADDR, addr); + return r8152_ocp_reg_read(tp, OCP_SRAM_DATA); +} + +static int r8152_wait_for_bit(struct r8152 *tp, bool ocp_reg, u16 type, + u16 index, const u32 mask, bool set, + unsigned int timeout) +{ + u32 val; + u64 start; + + start = get_time_ns(); + do { + if (ocp_reg) + val = r8152_ocp_reg_read(tp, index); + else + val = r8152_ocp_read_dword(tp, type, index); + + if (!set) + val = ~val; + + if ((val & mask) == mask) + return 0; + + mdelay(2); + } while (!is_timeout(start, timeout * MSECOND)); + + dev_dbg(&tp->dev->edev.dev, "%s: Timeout (index=%04x mask=%08x timeout=%d)\n", + __func__, index, mask, timeout); + + return -ETIMEDOUT; +} + +static void r8152b_reset_packet_filter(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_FMC); + ocp_data &= ~FMC_FCR_MCU_EN; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); + ocp_data |= FMC_FCR_MCU_EN; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_FMC, ocp_data); +} + +static void r8152_wait_fifo_empty(struct r8152 *tp) +{ + int ret; + + ret = r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_PHY_PWR, + PLA_PHY_PWR_TXEMP, 1, R8152_WAIT_TIMEOUT); + if (ret) + dev_dbg(&tp->dev->edev.dev, "Timeout waiting for FIFO empty\n"); + + ret = r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_TCR0, + TCR0_TX_EMPTY, 1, R8152_WAIT_TIMEOUT); + if (ret) + dev_dbg(&tp->dev->edev.dev, "Timeout waiting for TX empty\n"); +} + +static void r8152_nic_reset(struct r8152 *tp) +{ + int ret; + u32 ocp_data; + + ocp_data = r8152_ocp_read_dword(tp, MCU_TYPE_PLA, BIST_CTRL); + ocp_data |= BIST_CTRL_SW_RESET; + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, BIST_CTRL, ocp_data); + + ret = r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, BIST_CTRL, + BIST_CTRL_SW_RESET, 0, R8152_WAIT_TIMEOUT); + if (ret) + dev_dbg(&tp->dev->edev.dev, "Timeout waiting for NIC reset\n"); +} + +static u8 r8152_get_speed(struct r8152 *tp) +{ + return r8152_ocp_read_byte(tp, MCU_TYPE_PLA, PLA_PHYSTATUS); +} + +static void r8152_set_eee_plus(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR); + ocp_data &= ~EEEP_CR_EEEP_TX; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_EEEP_CR, ocp_data); +} + +static void rxdy_gated_en(struct r8152 *tp, bool enable) +{ + u32 ocp_data; + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_MISC_1); + if (enable) + ocp_data |= RXDY_GATED_EN; + else + ocp_data &= ~RXDY_GATED_EN; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_MISC_1, ocp_data); +} + +static void rtl8152_set_rx_mode(struct r8152 *tp) +{ + u32 ocp_data; + __le32 tmp[2]; + + tmp[0] = 0xffffffff; + tmp[1] = 0xffffffff; + + r8152_pla_ocp_write(tp, PLA_MAR, BYTE_EN_DWORD, sizeof(tmp), tmp); + + ocp_data = r8152_ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data |= RCR_APM | RCR_AM | RCR_AB; + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); +} + +static inline void r8153b_rx_agg_chg_indicate(struct r8152 *tp) +{ + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_UPT_RXDMA_OWN, + OWN_UPDATE | OWN_CLEAR); +} + +static int rtl_enable(struct r8152 *tp) +{ + u32 ocp_data; + + r8152b_reset_packet_filter(tp); + + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_PLA, PLA_CR); + ocp_data |= PLA_CR_RE | PLA_CR_TE; + r8152_ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, ocp_data); + + switch (tp->version) { + case RTL_VER_08: + case RTL_VER_09: + r8153b_rx_agg_chg_indicate(tp); + break; + default: + break; + } + + rxdy_gated_en(tp, false); + + rtl8152_set_rx_mode(tp); + + return 0; +} + +static int rtl8152_enable(struct r8152 *tp) +{ + r8152_set_eee_plus(tp); + + return rtl_enable(tp); +} + +static void r8153_set_rx_early_timeout(struct r8152 *tp) +{ + u32 ocp_data = tp->coalesce / 8; + + switch (tp->version) { + case RTL_VER_03: + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, + ocp_data); + break; + + case RTL_VER_08: + case RTL_VER_09: + /* The RTL8153B uses USB_RX_EXTRA_AGGR_TMR for rx timeout + * primarily. For USB_RX_EARLY_TIMEOUT, we fix it to 1264ns. + */ + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, + RX_AUXILIARY_TIMER / 8); + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EXTRA_AGGR_TMR, + ocp_data); + break; + + default: + dev_dbg(&tp->dev->edev.dev, "** %s Invalid Device\n", __func__); + break; + } +} + +static void r8153_set_rx_early_size(struct r8152 *tp) +{ + u32 ocp_data = (RTL8152_AGG_BUF_SZ - RTL8153_RMS - + sizeof(struct rx_desc)); + + switch (tp->version) { + case RTL_VER_03: + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, + ocp_data / 4); + break; + + case RTL_VER_08: + case RTL_VER_09: + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, + ocp_data / 8); + break; + + default: + dev_dbg(&tp->dev->edev.dev, "** %s Invalid Device\n", __func__); + break; + } +} + +static int rtl8153_enable(struct r8152 *tp) +{ + r8152_set_eee_plus(tp); + r8153_set_rx_early_timeout(tp); + r8153_set_rx_early_size(tp); + + return rtl_enable(tp); +} + +static void rtl_disable(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = r8152_ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data &= ~RCR_ACPT_ALL; + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); + + rxdy_gated_en(tp, true); + + r8152_wait_fifo_empty(tp); + r8152_nic_reset(tp); +} + +static void r8152_power_cut_en(struct r8152 *tp, bool enable) +{ + u32 ocp_data; + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CTRL); + if (enable) + ocp_data |= POWER_CUT; + else + ocp_data &= ~POWER_CUT; + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CTRL, ocp_data); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS); + ocp_data &= ~RESUME_INDICATE; + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_PM_CTRL_STATUS, ocp_data); +} + +static void rtl_rx_vlan_en(struct r8152 *tp, bool enable) +{ + u32 ocp_data; + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_CPCR); + if (enable) + ocp_data |= CPCR_RX_VLAN; + else + ocp_data &= ~CPCR_RX_VLAN; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_CPCR, ocp_data); +} + +static void r8153_u1u2en(struct r8152 *tp, bool enable) +{ + u8 u1u2[8]; + + if (enable) + memset(u1u2, 0xff, sizeof(u1u2)); + else + memset(u1u2, 0x00, sizeof(u1u2)); + + r8152_usb_ocp_write(tp, USB_TOLERANCE, BYTE_EN_SIX_BYTES, sizeof(u1u2), + u1u2); +} + +static void r8153b_u1u2en(struct r8152 *tp, bool enable) +{ + u16 ocp_data; + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_LPM_CONFIG); + if (enable) + ocp_data |= LPM_U1U2_EN; + else + ocp_data &= ~LPM_U1U2_EN; + + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_LPM_CONFIG, ocp_data); +} + +static void r8153_u2p3en(struct r8152 *tp, bool enable) +{ + u32 ocp_data; + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL); + if (enable && tp->version != RTL_VER_03 && tp->version != RTL_VER_04) + ocp_data |= U2P3_ENABLE; + else + ocp_data &= ~U2P3_ENABLE; + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL, ocp_data); +} + +static void r8153_power_cut_en(struct r8152 *tp, bool enable) +{ + u32 ocp_data; + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_POWER_CUT); + if (enable) + ocp_data |= PWR_EN | PHASE2_EN; + else + ocp_data &= ~(PWR_EN | PHASE2_EN); + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0); + ocp_data &= ~PCUT_STATUS; + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data); +} + +static void rtl_reset_bmu(struct r8152 *tp) +{ + u8 ocp_data; + + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_USB, USB_BMU_RESET); + ocp_data &= ~(BMU_RESET_EP_IN | BMU_RESET_EP_OUT); + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data); + ocp_data |= BMU_RESET_EP_IN | BMU_RESET_EP_OUT; + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_BMU_RESET, ocp_data); +} + +static int r8152_read_mac(struct r8152 *tp, unsigned char *macaddr) +{ + int ret; + unsigned char enetaddr[8] = {0}; + + ret = r8152_pla_ocp_read(tp, PLA_IDR, 8, enetaddr); + if (ret < 0) + return ret; + + memcpy(macaddr, enetaddr, ETH_ALEN); + return 0; +} + +static void r8152b_disable_aldps(struct r8152 *tp) +{ + r8152_ocp_reg_write(tp, OCP_ALDPS_CONFIG, + ENPDNPS | LINKENA | DIS_SDSAVE); + mdelay(20); +} + +static void r8152b_enable_aldps(struct r8152 *tp) +{ + r8152_ocp_reg_write(tp, OCP_ALDPS_CONFIG, ENPWRSAVE | ENPDNPS | + LINKENA | DIS_SDSAVE); +} + +static void rtl8152_disable(struct r8152 *tp) +{ + r8152b_disable_aldps(tp); + rtl_disable(tp); + r8152b_enable_aldps(tp); +} + +static void r8152b_hw_phy_cfg(struct r8152 *tp) +{ + u16 data; + + data = r8152_mdio_read(tp, MII_BMCR); + if (data & BMCR_PDOWN) { + data &= ~BMCR_PDOWN; + r8152_mdio_write(tp, MII_BMCR, data); + } + + r8152b_firmware(tp); +} + +static void rtl8152_reinit_ll(struct r8152 *tp) +{ + u32 ocp_data; + int ret; + + ret = r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_PHY_PWR, + PLA_PHY_PWR_LLR, 1, R8152_WAIT_TIMEOUT); + if (ret) + dev_dbg(&tp->dev->edev.dev, "Timeout waiting for link list ready\n"); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); + ocp_data |= RE_INIT_LL; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); + + ret = r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_PHY_PWR, + PLA_PHY_PWR_LLR, 1, R8152_WAIT_TIMEOUT); + if (ret) + dev_dbg(&tp->dev->edev.dev, "Timeout waiting for link list ready\n"); +} + +static void r8152b_exit_oob(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = r8152_ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data &= ~RCR_ACPT_ALL; + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); + + rxdy_gated_en(tp, true); + r8152b_hw_phy_cfg(tp); + + r8152_ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); + r8152_ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CR, 0x00); + + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); + ocp_data &= ~NOW_IS_OOB; + r8152_ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); + ocp_data &= ~MCU_BORW_EN; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); + + rtl8152_reinit_ll(tp); + r8152_nic_reset(tp); + + /* rx share fifo credit full threshold */ + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, + RXFIFO_THR1_NORMAL); + + if (tp->udev->speed == USB_SPEED_FULL || + tp->udev->speed == USB_SPEED_LOW) { + /* rx share fifo credit near full threshold */ + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, + RXFIFO_THR2_FULL); + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, + RXFIFO_THR3_FULL); + } else { + /* rx share fifo credit near full threshold */ + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, + RXFIFO_THR2_HIGH); + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, + RXFIFO_THR3_HIGH); + } + + /* TX share fifo free credit full threshold */ + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, + TXFIFO_THR_NORMAL); + + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_TX_AGG, TX_AGG_MAX_THRESHOLD); + r8152_ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_HIGH); + r8152_ocp_write_dword(tp, MCU_TYPE_USB, USB_TX_DMA, + TEST_MODE_DISABLE | TX_SIZE_ADJUST1); + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8152_RMS); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); + ocp_data |= TCR0_AUTO_FIFO; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); +} + +static void r8153_hw_phy_cfg(struct r8152 *tp) +{ + u32 ocp_data; + u16 data; + + if (tp->version == RTL_VER_03 || tp->version == RTL_VER_04 || + tp->version == RTL_VER_05) + r8152_ocp_reg_write(tp, OCP_ADC_CFG, + CKADSEL_L | ADC_EN | EN_EMI_L); + + data = r8152_mdio_read(tp, MII_BMCR); + if (data & BMCR_PDOWN) { + data &= ~BMCR_PDOWN; + r8152_mdio_write(tp, MII_BMCR, data); + } + + r8153_firmware(tp); + + if (tp->version == RTL_VER_03) { + data = r8152_ocp_reg_read(tp, OCP_EEE_CFG); + data &= ~CTAP_SHORT_EN; + r8152_ocp_reg_write(tp, OCP_EEE_CFG, data); + } + + data = r8152_ocp_reg_read(tp, OCP_POWER_CFG); + data |= EEE_CLKDIV_EN; + r8152_ocp_reg_write(tp, OCP_POWER_CFG, data); + + data = r8152_ocp_reg_read(tp, OCP_DOWN_SPEED); + data |= EN_10M_BGOFF; + r8152_ocp_reg_write(tp, OCP_DOWN_SPEED, data); + data = r8152_ocp_reg_read(tp, OCP_POWER_CFG); + data |= EN_10M_PLLOFF; + r8152_ocp_reg_write(tp, OCP_POWER_CFG, data); + r8152_sram_write(tp, SRAM_IMPEDANCE, 0x0b13); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); + ocp_data |= PFM_PWM_SWITCH; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); + + /* Enable LPF corner auto tune */ + r8152_sram_write(tp, SRAM_LPF_CFG, 0xf70f); + + /* Adjust 10M Amplitude */ + r8152_sram_write(tp, SRAM_10M_AMP1, 0x00af); + r8152_sram_write(tp, SRAM_10M_AMP2, 0x0208); +} + +static u32 r8152_efuse_read(struct r8152 *tp, u8 addr) +{ + u32 ocp_data; + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_EFUSE_CMD, + EFUSE_READ_CMD | addr); + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_EFUSE_CMD); + ocp_data = (ocp_data & EFUSE_DATA_BIT16) << 9; /* data of bit16 */ + ocp_data |= r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_EFUSE_DATA); + + return ocp_data; +} + +static void r8153b_hw_phy_cfg(struct r8152 *tp) +{ + u32 ocp_data; + u16 data; + + data = r8152_mdio_read(tp, MII_BMCR); + if (data & BMCR_PDOWN) { + data &= ~BMCR_PDOWN; + r8152_mdio_write(tp, MII_BMCR, data); + } + + /* U1/U2/L1 idle timer. 500 us */ + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500); + + r8153b_firmware(tp); + + data = r8152_sram_read(tp, SRAM_GREEN_CFG); + data |= R_TUNE_EN; + r8152_sram_write(tp, SRAM_GREEN_CFG, data); + data = r8152_ocp_reg_read(tp, OCP_NCTL_CFG); + data |= PGA_RETURN_EN; + r8152_ocp_reg_write(tp, OCP_NCTL_CFG, data); + + /* ADC Bias Calibration: + * read efuse offset 0x7d to get a 17-bit data. Remove the dummy/fake + * bit (bit3) to rebuild the real 16-bit data. Write the data to the + * ADC ioffset. + */ + ocp_data = r8152_efuse_read(tp, 0x7d); + ocp_data = ((ocp_data & 0x1fff0) >> 1) | (ocp_data & 0x7); + if (ocp_data != 0xffff) + r8152_ocp_reg_write(tp, OCP_ADC_IOFFSET, ocp_data); + + /* ups mode tx-link-pulse timing adjustment: + * rg_saw_cnt = OCP reg 0xC426 Bit[13:0] + * swr_cnt_1ms_ini = 16000000 / rg_saw_cnt + */ + ocp_data = r8152_ocp_reg_read(tp, 0xc426); + ocp_data &= 0x3fff; + if (ocp_data) { + u32 swr_cnt_1ms_ini; + + swr_cnt_1ms_ini = (16000000 / ocp_data) & SAW_CNT_1MS_MASK; + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CFG); + ocp_data = (ocp_data & ~SAW_CNT_1MS_MASK) | swr_cnt_1ms_ini; + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CFG, ocp_data); + } + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); + ocp_data |= PFM_PWM_SWITCH; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); +} + +static void r8153_first_init(struct r8152 *tp) +{ + u32 ocp_data; + + rxdy_gated_en(tp, true); + + ocp_data = r8152_ocp_read_dword(tp, MCU_TYPE_PLA, PLA_RCR); + ocp_data &= ~RCR_ACPT_ALL; + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data); + + r8153_hw_phy_cfg(tp); + + r8152_nic_reset(tp); + rtl_reset_bmu(tp); + + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL); + ocp_data &= ~NOW_IS_OOB; + r8152_ocp_write_byte(tp, MCU_TYPE_PLA, PLA_OOB_CTRL, ocp_data); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7); + ocp_data &= ~MCU_BORW_EN; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_SFF_STS_7, ocp_data); + + rtl8152_reinit_ll(tp); + + rtl_rx_vlan_en(tp, false); + + ocp_data = RTL8153_RMS; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, ocp_data); + r8152_ocp_write_byte(tp, MCU_TYPE_PLA, PLA_MTPS, MTPS_JUMBO); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR0); + ocp_data |= TCR0_AUTO_FIFO; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_TCR0, ocp_data); + + r8152_nic_reset(tp); + + /* rx share fifo credit full threshold */ + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL0, + RXFIFO_THR1_NORMAL); + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL1, + RXFIFO_THR2_NORMAL); + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_RXFIFO_CTRL2, + RXFIFO_THR3_NORMAL); + /* TX share fifo free credit full threshold */ + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_TXFIFO_CTRL, + TXFIFO_THR_NORMAL2); + + /* rx aggregation */ + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); + + ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); +} + +static void r8153_disable_aldps(struct r8152 *tp) +{ + u16 data; + + data = r8152_ocp_reg_read(tp, OCP_POWER_CFG); + data &= ~EN_ALDPS; + r8152_ocp_reg_write(tp, OCP_POWER_CFG, data); + mdelay(20); +} + +static void rtl8153_disable(struct r8152 *tp) +{ + r8153_disable_aldps(tp); + rtl_disable(tp); + rtl_reset_bmu(tp); +} + +static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex) +{ + u16 bmcr, anar, gbcr; + + anar = r8152_mdio_read(tp, MII_ADVERTISE); + anar &= ~(ADVERTISE_10HALF | ADVERTISE_10FULL | + ADVERTISE_100HALF | ADVERTISE_100FULL); + if (tp->supports_gmii) { + gbcr = r8152_mdio_read(tp, MII_CTRL1000); + gbcr &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + } else { + gbcr = 0; + } + + if (autoneg == AUTONEG_DISABLE) { + if (speed == SPEED_10) { + bmcr = 0; + anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; + } else if (speed == SPEED_100) { + bmcr = BMCR_SPEED100; + anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; + } else if (speed == SPEED_1000 && tp->supports_gmii) { + bmcr = BMCR_SPEED1000; + gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; + } else { + return -EINVAL; + } + + if (duplex == DUPLEX_FULL) + bmcr |= BMCR_FULLDPLX; + } else { + if (speed == SPEED_10) { + if (duplex == DUPLEX_FULL) + anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; + else + anar |= ADVERTISE_10HALF; + } else if (speed == SPEED_100) { + if (duplex == DUPLEX_FULL) { + anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; + anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; + } else { + anar |= ADVERTISE_10HALF; + anar |= ADVERTISE_100HALF; + } + } else if (speed == SPEED_1000 && tp->supports_gmii) { + if (duplex == DUPLEX_FULL) { + anar |= ADVERTISE_10HALF | ADVERTISE_10FULL; + anar |= ADVERTISE_100HALF | ADVERTISE_100FULL; + gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF; + } else { + anar |= ADVERTISE_10HALF; + anar |= ADVERTISE_100HALF; + gbcr |= ADVERTISE_1000HALF; + } + } else { + return -EINVAL; + } + + bmcr = BMCR_ANENABLE | BMCR_ANRESTART | BMCR_RESET; + } + + if (tp->supports_gmii) + r8152_mdio_write(tp, MII_CTRL1000, gbcr); + + r8152_mdio_write(tp, MII_ADVERTISE, anar); + r8152_mdio_write(tp, MII_BMCR, bmcr); + + return 0; +} + +static void rtl8152_up(struct r8152 *tp) +{ + r8152b_disable_aldps(tp); + r8152b_exit_oob(tp); + r8152b_enable_aldps(tp); +} + +static void rtl8153_up(struct r8152 *tp) +{ + r8153_u1u2en(tp, false); + r8153_disable_aldps(tp); + r8153_first_init(tp); + r8153_u2p3en(tp, false); +} + +static void rtl8153b_up(struct r8152 *tp) +{ + r8153_first_init(tp); +} + +static void r8152_get_version(struct r8152 *tp) +{ + u32 ocp_data; + u16 tcr; + int i; + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1); + tcr = (u16)(ocp_data & VERSION_MASK); + + for (i = 0; i < ARRAY_SIZE(r8152_versions); i++) { + if (tcr == r8152_versions[i].tcr) { + /* Found a supported version */ + tp->version = r8152_versions[i].version; + tp->supports_gmii = r8152_versions[i].gmii; + break; + } + } + + if (tp->version == RTL_VER_UNKNOWN) + dev_dbg(&tp->dev->edev.dev, + "r8152 Unknown tcr version 0x%04x\n", tcr); +} + +static void r8152b_enable_fc(struct r8152 *tp) +{ + u16 anar; + + anar = r8152_mdio_read(tp, MII_ADVERTISE); + anar |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM; + r8152_mdio_write(tp, MII_ADVERTISE, anar); +} + +static void rtl_tally_reset(struct r8152 *tp) +{ + u32 ocp_data; + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY); + ocp_data |= TALLY_RESET; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_RSTTALLY, ocp_data); +} + +static void rtl8152b_init(struct r8152 *tp) +{ + u32 ocp_data; + + r8152b_disable_aldps(tp); + + if (tp->version == RTL_VER_01) { + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, + PLA_LED_FEATURE); + ocp_data &= ~LED_MODE_MASK; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); + } + + r8152_power_cut_en(tp, false); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); + ocp_data |= TX_10M_IDLE_EN | PFM_PWM_SWITCH; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); + ocp_data = r8152_ocp_read_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL); + ocp_data &= ~MCU_CLK_RATIO_MASK; + ocp_data |= MCU_CLK_RATIO | D3_CLK_GATED_EN; + r8152_ocp_write_dword(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL, ocp_data); + ocp_data = GPHY_STS_MSK | SPEED_DOWN_MSK | + SPDWN_RXDV_MSK | SPDWN_LINKCHG_MSK; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_GPHY_INTR_IMR, ocp_data); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_USB_TIMER); + ocp_data |= BIT(15); + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_USB_TIMER, ocp_data); + r8152_ocp_write_word(tp, MCU_TYPE_USB, 0xcbfc, 0x03e8); + ocp_data &= ~BIT(15); + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_USB_TIMER, ocp_data); + + r8152b_enable_fc(tp); + rtl_tally_reset(tp); + + /* enable rx aggregation */ + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); + + ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); +} + +static void rtl8153_init(struct r8152 *tp) +{ + int i; + u32 ocp_data; + + r8153_disable_aldps(tp); + r8153_u1u2en(tp, false); + + r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_BOOT_CTRL, + AUTOLOAD_DONE, 1, R8152_WAIT_TIMEOUT); + + for (i = 0; i < R8152_WAIT_TIMEOUT; i++) { + ocp_data = r8152_ocp_reg_read(tp, OCP_PHY_STATUS) & + PHY_STAT_MASK; + if (ocp_data == PHY_STAT_LAN_ON || ocp_data == PHY_STAT_PWRDN) + break; + + mdelay(1); + } + + r8153_u2p3en(tp, false); + + if (tp->version == RTL_VER_04) { + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, + USB_SSPHYLINK2); + ocp_data &= ~pwd_dn_scale_mask; + ocp_data |= pwd_dn_scale(96); + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_SSPHYLINK2, + ocp_data); + + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); + ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); + } else if (tp->version == RTL_VER_05) { + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_PLA, PLA_DMY_REG0); + ocp_data &= ~ECM_ALDPS; + r8152_ocp_write_byte(tp, MCU_TYPE_PLA, PLA_DMY_REG0, ocp_data); + + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_USB, + USB_CSR_DUMMY1); + if (r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_BURST_SIZE) == 0) + ocp_data &= ~DYNAMIC_BURST; + else + ocp_data |= DYNAMIC_BURST; + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1, + ocp_data); + } else if (tp->version == RTL_VER_06) { + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_USB, + USB_CSR_DUMMY1); + if (r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_BURST_SIZE) == 0) + ocp_data &= ~DYNAMIC_BURST; + else + ocp_data |= DYNAMIC_BURST; + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY1, + ocp_data); + } + + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY2); + ocp_data |= EP4_FULL_FC; + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_CSR_DUMMY2, ocp_data); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL); + ocp_data &= ~TIMER11_EN; + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_WDT11_CTRL, ocp_data); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE); + ocp_data &= ~LED_MODE_MASK; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_LED_FEATURE, ocp_data); + + ocp_data = FIFO_EMPTY_1FB | ROK_EXIT_LPM; + if (tp->version == RTL_VER_04 && tp->udev->speed != USB_SPEED_SUPER) + ocp_data |= LPM_TIMER_500MS; + else + ocp_data |= LPM_TIMER_500US; + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_LPM_CTRL, ocp_data); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2); + ocp_data &= ~SEN_VAL_MASK; + ocp_data |= SEN_VAL_NORMAL | SEL_RXIDLE; + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2, ocp_data); + + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_CONNECT_TIMER, 0x0001); + + r8153_power_cut_en(tp, false); + + r8152b_enable_fc(tp); + rtl_tally_reset(tp); +} + +static void r8153b_init(struct r8152 *tp) +{ + u32 ocp_data; + int i; + + r8153_disable_aldps(tp); + r8153b_u1u2en(tp, false); + + r8152_wait_for_bit(tp, 0, MCU_TYPE_PLA, PLA_BOOT_CTRL, + AUTOLOAD_DONE, 1, R8152_WAIT_TIMEOUT); + + for (i = 0; i < R8152_WAIT_TIMEOUT; i++) { + ocp_data = r8152_ocp_reg_read(tp, OCP_PHY_STATUS) & PHY_STAT_MASK; + if (ocp_data == PHY_STAT_LAN_ON || ocp_data == PHY_STAT_PWRDN) + break; + + mdelay(1); + } + + r8153_u2p3en(tp, false); + + /* MSC timer = 0xfff * 8ms = 32760 ms */ + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff); + + r8153_power_cut_en(tp, false); + + /* MAC clock speed down */ + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2); + ocp_data |= MAC_CLK_SPDWN_EN; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3); + ocp_data &= ~PLA_MCU_SPDWN_EN; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL3, ocp_data); + + if (tp->version == RTL_VER_09) { + /* Disable Test IO for 32QFN */ + if (r8152_ocp_read_byte(tp, MCU_TYPE_PLA, 0xdc00) & BIT(5)) { + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_PLA, + PLA_PHY_PWR); + ocp_data |= TEST_IO_OFF; + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, + ocp_data); + } + } + + /* rx aggregation */ + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL); + ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN); + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data); + + rtl_tally_reset(tp); + r8153b_hw_phy_cfg(tp); + r8152b_enable_fc(tp); +} + +static int r8152_ops_init(struct r8152 *tp) +{ + struct rtl_ops *ops = &tp->rtl_ops; + int ret = 0; + + switch (tp->version) { + case RTL_VER_01: + case RTL_VER_02: + case RTL_VER_07: + ops->init = rtl8152b_init; + ops->enable = rtl8152_enable; + ops->disable = rtl8152_disable; + ops->up = rtl8152_up; + break; + + case RTL_VER_03: + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + ops->init = rtl8153_init; + ops->enable = rtl8153_enable; + ops->disable = rtl8153_disable; + ops->up = rtl8153_up; + break; + + case RTL_VER_08: + case RTL_VER_09: + ops->init = r8153b_init; + ops->enable = rtl8153_enable; + ops->disable = rtl8153_disable; + ops->up = rtl8153b_up; + break; + + default: + ret = -ENODEV; + dev_warn(&tp->dev->edev.dev, "r8152 Unknown Device\n"); + break; + } + + return ret; +} + +static int r8152_init_common(struct r8152 *tp) +{ + int link_detected; + u64 start; + u8 speed; + + dev_dbg(&tp->dev->edev.dev, "** %s()\n", __func__); + + dev_info(&tp->dev->edev.dev, "Waiting for Ethernet connection...\n"); + start = get_time_ns(); + while (1) { + speed = r8152_get_speed(tp); + + link_detected = speed & LINK_STATUS; + if (link_detected) { + tp->rtl_ops.enable(tp); + dev_info(&tp->dev->edev.dev, "done.\n"); + break; + } + + mdelay(TIMEOUT_RESOLUTION); + if (is_timeout(start, PHY_CONNECT_TIMEOUT * MSECOND)) { + dev_warn(&tp->dev->edev.dev, "unable to connect.\n"); + return -ETIMEDOUT; + } + }; + + return 0; +} + +static int r8152_tx_fixup(struct usbnet *dev, void *buf, int len, void *nbuf, + int *nlen) +{ + struct tx_desc *tx_desc = (struct tx_desc *)nbuf; + u32 opts1; + + dev_dbg(&dev->edev.dev, "** %s(), len %d\n", __func__, len); + + opts1 = len | TX_FS | TX_LS; + + tx_desc->opts1 = cpu_to_le32(opts1); + tx_desc->opts2 = 0; + + memcpy(nbuf + sizeof(struct tx_desc), buf, len); + + *nlen = len + sizeof(struct tx_desc); + + return 0; +} + +static int r8152_rx_fixup(struct usbnet *dev, void *buf, int len) +{ + struct rx_desc *rx_desc; + unsigned char *packet; + u16 packet_len; + + rx_desc = (struct rx_desc *)buf; + packet_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK; + packet_len -= CRC_SIZE; + + dev_dbg(&dev->edev.dev, "%s: buf len=%d, packet len=%d\n", __func__, + len, packet_len); + + if (packet_len > len - (sizeof(struct rx_desc) + CRC_SIZE)) { + dev_dbg(&dev->edev.dev, "Rx: too large packet: %d\n", + packet_len); + return -EIO; + } + + packet = buf + sizeof(struct rx_desc); + net_receive(&dev->edev, packet, len - sizeof(struct rx_desc)); + + return 0; +} + +static int r8152_eth_reset(struct usbnet *dev) +{ + struct r8152 *tp = r8152_get_priv(dev); + + dev_dbg(&tp->dev->edev.dev, "** %s (%d)\n", __func__, __LINE__); + + tp->rtl_ops.disable(tp); + return r8152_init_common(tp); +} + +static int r8152_common_mdio_read(struct mii_bus *bus, int phy_id, int idx) +{ + struct usbnet *dev = bus->priv; + struct r8152 *tp = r8152_get_priv(dev); + u32 val; + + /* No phy_id is supported, so fake support of address 0 */ + if (phy_id) + return 0xffff; + + val = r8152_mdio_read(tp, idx); + + return val & 0xffff; +} + +static int r8152_common_mdio_write(struct mii_bus *bus, int phy_id, int idx, + u16 regval) +{ + struct usbnet *dev = bus->priv; + struct r8152 *tp = r8152_get_priv(dev); + + /* No phy_id is supported, so fake support of address 0 */ + if (phy_id) + return -EIO; + + r8152_mdio_write(tp, idx, regval); + + return 0; +} + +static int r8152_init_mii(struct usbnet *dev) +{ + dev->miibus.read = r8152_common_mdio_read; + dev->miibus.write = r8152_common_mdio_write; + dev->phy_addr = 0; + dev->miibus.priv = dev; + dev->miibus.parent = &dev->udev->dev; + + return mdiobus_register(&dev->miibus); +} + +static int r8152_write_hwaddr(struct eth_device *edev, const unsigned char *adr) +{ + struct usbnet *dev = container_of(edev, struct usbnet, edev); + struct r8152 *tp = r8152_get_priv(dev); + + dev_dbg(&tp->dev->edev.dev, "** %s (%d)\n", __func__, __LINE__); + + r8152_ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_CONFIG); + r8152_pla_ocp_write(tp, PLA_IDR, BYTE_EN_SIX_BYTES, ETH_ALEN, adr); + r8152_ocp_write_byte(tp, MCU_TYPE_PLA, PLA_CRWECR, CRWECR_NORAML); + + dev_dbg(&tp->dev->edev.dev, "MAC %pM\n", adr); + return 0; +} + +static int r8152_read_rom_hwaddr(struct eth_device *edev, unsigned char *adr) +{ + struct usbnet *dev = container_of(edev, struct usbnet, edev); + struct r8152 *tp = r8152_get_priv(dev); + + dev_dbg(&tp->dev->edev.dev, "** %s (%d)\n", __func__, __LINE__); + return r8152_read_mac(tp, adr); +} + +static int r8152_eth_bind(struct usbnet *dev) +{ + struct r8152 *tp; + int ret; + + usbnet_get_endpoints(dev); + + tp = xzalloc(sizeof(*tp)); + if (!tp) + return -ENOMEM; + + tp->txbuf = dma_alloc(R8152_TX_BURST_SIZE); + if (!tp->txbuf) + return -ENOMEM; + + tp->rxbuf = dma_alloc(R8152_RX_BURST_SIZE); + if (!tp->rxbuf) + return -ENOMEM; + + dev->driver_priv = tp; + + dev->edev.set_ethaddr = r8152_write_hwaddr; + dev->edev.get_ethaddr = r8152_read_rom_hwaddr; + + r8152_init_mii(dev); + + tp->udev = dev->udev; + tp->dev = dev; + + r8152_get_version(tp); + + ret = r8152_ops_init(tp); + if (ret) + return ret; + + tp->rtl_ops.init(tp); + tp->rtl_ops.up(tp); + + dev->rx_urb_size = RTL8152_AGG_BUF_SZ; + return rtl8152_set_speed(tp, AUTONEG_ENABLE, + tp->supports_gmii ? SPEED_1000 : SPEED_100, + DUPLEX_FULL); +} + +static void r8152_unbind(struct usbnet *dev) +{ + struct r8152 *tp = r8152_get_priv(dev); + + tp->rtl_ops.disable(tp); + mdiobus_unregister(&dev->miibus); + free(tp->txbuf); + free(tp->rxbuf); + free(tp); +} + +static struct driver_info r8152_info = { + .description = R8152_BASE_NAME, + .bind = r8152_eth_bind, + .reset = r8152_eth_reset, + .unbind = r8152_unbind, + .flags = FLAG_ETHER | FLAG_FRAMING_AX, + .rx_fixup = r8152_rx_fixup, + .tx_fixup = r8152_tx_fixup, +}; + +static const struct usb_device_id products[] = { +{ + /* Realtek */ + USB_DEVICE(0x0bda, 0x8050), + .driver_info = &r8152_info, +}, { + USB_DEVICE(0x0bda, 0x8152), + .driver_info = &r8152_info, +}, { + USB_DEVICE(0x0bda, 0x8153), + .driver_info = &r8152_info, +}, { + /* Samsung */ + USB_DEVICE(0x04e8, 0xa101), + .driver_info = &r8152_info, +}, { + /* Lenovo */ + USB_DEVICE(0x17ef, 0x304f), + .driver_info = &r8152_info, +}, { + USB_DEVICE(0x17ef, 0x3052), + .driver_info = &r8152_info, +}, { + USB_DEVICE(0x17ef, 0x3054), + .driver_info = &r8152_info, +}, { + USB_DEVICE(0x17ef, 0x3057), + .driver_info = &r8152_info, +}, { + USB_DEVICE(0x17ef, 0x7205), + .driver_info = &r8152_info, +}, { + USB_DEVICE(0x17ef, 0x720a), + .driver_info = &r8152_info, +}, { + USB_DEVICE(0x17ef, 0x720b), + .driver_info = &r8152_info, +}, { + USB_DEVICE(0x17ef, 0x720c), + .driver_info = &r8152_info, +}, { + /* TP-LINK */ + USB_DEVICE(0x2357, 0x0601), + .driver_info = &r8152_info, +}, { + /* Nvidia */ + USB_DEVICE(0x0955, 0x09ff), + .driver_info = &r8152_info, +}, + + { } /* Terminating entry */ +}; + +static struct usb_driver r8152_driver = { + .name = R8152_BASE_NAME, + .id_table = products, + .probe = usbnet_probe, + .disconnect = usbnet_disconnect, +}; + +static int __init r8152_init(void) +{ + return usb_driver_register(&r8152_driver); +} +device_initcall(r8152_init); diff --git a/drivers/net/usb/r8152.h b/drivers/net/usb/r8152.h new file mode 100644 index 0000000000..618ae5d169 --- /dev/null +++ b/drivers/net/usb/r8152.h @@ -0,0 +1,619 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2015 Realtek Semiconductor Corp. All rights reserved. */ + +#ifndef _RTL8152_ETH_H +#define _RTL8152_ETH_H + +#define R8152_BASE_NAME "r8152" + +#define PLA_IDR 0xc000 +#define PLA_RCR 0xc010 +#define PLA_RMS 0xc016 +#define PLA_RXFIFO_CTRL0 0xc0a0 +#define PLA_RXFIFO_CTRL1 0xc0a4 +#define PLA_RXFIFO_CTRL2 0xc0a8 +#define PLA_DMY_REG0 0xc0b0 +#define PLA_FMC 0xc0b4 +#define PLA_CFG_WOL 0xc0b6 +#define PLA_TEREDO_CFG 0xc0bc +#define PLA_MAR 0xcd00 +#define PLA_BACKUP 0xd000 +#define PLA_BDC_CR 0xd1a0 +#define PLA_TEREDO_TIMER 0xd2cc +#define PLA_REALWOW_TIMER 0xd2e8 +#define PLA_EXTRA_STATUS 0xd398 +#define PLA_EFUSE_DATA 0xdd00 +#define PLA_EFUSE_CMD 0xdd02 +#define PLA_LEDSEL 0xdd90 +#define PLA_LED_FEATURE 0xdd92 +#define PLA_PHYAR 0xde00 +#define PLA_BOOT_CTRL 0xe004 +#define PLA_GPHY_INTR_IMR 0xe022 +#define PLA_EEE_CR 0xe040 +#define PLA_EEEP_CR 0xe080 +#define PLA_MAC_PWR_CTRL 0xe0c0 +#define PLA_MAC_PWR_CTRL2 0xe0ca +#define PLA_MAC_PWR_CTRL3 0xe0cc +#define PLA_MAC_PWR_CTRL4 0xe0ce +#define PLA_WDT6_CTRL 0xe428 +#define PLA_TCR0 0xe610 +#define PLA_TCR1 0xe612 +#define PLA_MTPS 0xe615 +#define PLA_TXFIFO_CTRL 0xe618 +#define PLA_RSTTALLY 0xe800 +#define BIST_CTRL 0xe810 +#define PLA_CR 0xe813 +#define PLA_CRWECR 0xe81c +#define PLA_CONFIG12 0xe81e /* CONFIG1, CONFIG2 */ +#define PLA_CONFIG34 0xe820 /* CONFIG3, CONFIG4 */ +#define PLA_CONFIG5 0xe822 +#define PLA_PHY_PWR 0xe84c +#define PLA_OOB_CTRL 0xe84f +#define PLA_CPCR 0xe854 +#define PLA_MISC_0 0xe858 +#define PLA_MISC_1 0xe85a +#define PLA_OCP_GPHY_BASE 0xe86c +#define PLA_TALLYCNT 0xe890 +#define PLA_SFF_STS_7 0xe8de +#define PLA_PHYSTATUS 0xe908 +#define PLA_BP_BA 0xfc26 +#define PLA_BP_0 0xfc28 +#define PLA_BP_1 0xfc2a +#define PLA_BP_2 0xfc2c +#define PLA_BP_3 0xfc2e +#define PLA_BP_4 0xfc30 +#define PLA_BP_5 0xfc32 +#define PLA_BP_6 0xfc34 +#define PLA_BP_7 0xfc36 +#define PLA_BP_EN 0xfc38 + +#define USB_USB2PHY 0xb41e +#define USB_SSPHYLINK2 0xb428 +#define USB_U2P3_CTRL 0xb460 +#define USB_CSR_DUMMY1 0xb464 +#define USB_CSR_DUMMY2 0xb466 +#define USB_DEV_STAT 0xb808 +#define USB_CONNECT_TIMER 0xcbf8 +#define USB_MSC_TIMER 0xcbfc +#define USB_BURST_SIZE 0xcfc0 +#define USB_FW_FIX_EN1 0xcfcc +#define USB_LPM_CONFIG 0xcfd8 +#define USB_USB_CTRL 0xd406 +#define USB_PHY_CTRL 0xd408 +#define USB_TX_AGG 0xd40a +#define USB_RX_BUF_TH 0xd40c +#define USB_USB_TIMER 0xd428 +#define USB_RX_EARLY_TIMEOUT 0xd42c +#define USB_RX_EARLY_SIZE 0xd42e +#define USB_PM_CTRL_STATUS 0xd432 /* RTL8153A */ +#define USB_RX_EXTRA_AGGR_TMR 0xd432 /* RTL8153B */ +#define USB_TX_DMA 0xd434 +#define USB_UPT_RXDMA_OWN 0xd437 +#define USB_TOLERANCE 0xd490 +#define USB_LPM_CTRL 0xd41a +#define USB_BMU_RESET 0xd4b0 +#define USB_U1U2_TIMER 0xd4da +#define USB_UPS_CTRL 0xd800 +#define USB_POWER_CUT 0xd80a +#define USB_MISC_0 0xd81a +#define USB_AFE_CTRL2 0xd824 +#define USB_UPS_CFG 0xd842 +#define USB_WDT11_CTRL 0xe43c +#define USB_BP_BA PLA_BP_BA +#define USB_BP(n) (0xfc28 + 2 * (n)) +#define USB_BP_EN PLA_BP_EN /* RTL8153A */ +#define USB_BP2_EN 0xfc48 + +/* OCP Registers */ +#define OCP_ALDPS_CONFIG 0x2010 +#define OCP_EEE_CONFIG1 0x2080 +#define OCP_EEE_CONFIG2 0x2092 +#define OCP_EEE_CONFIG3 0x2094 +#define OCP_BASE_MII 0xa400 +#define OCP_EEE_AR 0xa41a +#define OCP_EEE_DATA 0xa41c +#define OCP_PHY_STATUS 0xa420 +#define OCP_NCTL_CFG 0xa42c +#define OCP_POWER_CFG 0xa430 +#define OCP_EEE_CFG 0xa432 +#define OCP_SRAM_ADDR 0xa436 +#define OCP_SRAM_DATA 0xa438 +#define OCP_DOWN_SPEED 0xa442 +#define OCP_EEE_ABLE 0xa5c4 +#define OCP_EEE_ADV 0xa5d0 +#define OCP_EEE_LPABLE 0xa5d2 +#define OCP_PHY_STATE 0xa708 /* nway state for 8153 */ +#define OCP_ADC_IOFFSET 0xbcfc +#define OCP_ADC_CFG 0xbc06 + +/* SRAM Register */ +#define SRAM_GREEN_CFG 0x8011 +#define SRAM_LPF_CFG 0x8012 +#define SRAM_10M_AMP1 0x8080 +#define SRAM_10M_AMP2 0x8082 +#define SRAM_IMPEDANCE 0x8084 + +/* PLA_RCR */ +#define RCR_AAP 0x00000001 +#define RCR_APM 0x00000002 +#define RCR_AM 0x00000004 +#define RCR_AB 0x00000008 +#define RCR_ACPT_ALL (RCR_AAP | RCR_APM | RCR_AM | RCR_AB) + +/* PLA_RXFIFO_CTRL0 */ +#define RXFIFO_THR1_NORMAL 0x00080002 +#define RXFIFO_THR1_OOB 0x01800003 + +/* PLA_RXFIFO_CTRL1 */ +#define RXFIFO_THR2_FULL 0x00000060 +#define RXFIFO_THR2_HIGH 0x00000038 +#define RXFIFO_THR2_OOB 0x0000004a +#define RXFIFO_THR2_NORMAL 0x00a0 + +/* PLA_RXFIFO_CTRL2 */ +#define RXFIFO_THR3_FULL 0x00000078 +#define RXFIFO_THR3_HIGH 0x00000048 +#define RXFIFO_THR3_OOB 0x0000005a +#define RXFIFO_THR3_NORMAL 0x0110 + +/* PLA_TXFIFO_CTRL */ +#define TXFIFO_THR_NORMAL 0x00400008 +#define TXFIFO_THR_NORMAL2 0x01000008 + +/* PLA_DMY_REG0 */ +#define ECM_ALDPS 0x0002 + +/* PLA_FMC */ +#define FMC_FCR_MCU_EN 0x0001 + +/* PLA_EEEP_CR */ +#define EEEP_CR_EEEP_TX 0x0002 + +/* PLA_WDT6_CTRL */ +#define WDT6_SET_MODE 0x0010 + +/* PLA_TCR0 */ +#define TCR0_TX_EMPTY 0x0800 +#define TCR0_AUTO_FIFO 0x0080 + +/* PLA_TCR1 */ +#define VERSION_MASK 0x7cf0 + +/* PLA_MTPS */ +#define MTPS_JUMBO (12 * 1024 / 64) +#define MTPS_DEFAULT (6 * 1024 / 64) + +/* PLA_RSTTALLY */ +#define TALLY_RESET 0x0001 + +/* PLA_CR */ +#define PLA_CR_RST 0x10 +#define PLA_CR_RE 0x08 +#define PLA_CR_TE 0x04 + +/* PLA_BIST_CTRL */ +#define BIST_CTRL_SW_RESET (0x10 << 24) + +/* PLA_CRWECR */ +#define CRWECR_NORAML 0x00 +#define CRWECR_CONFIG 0xc0 + +/* PLA_OOB_CTRL */ +#define NOW_IS_OOB 0x80 +#define TXFIFO_EMPTY 0x20 +#define RXFIFO_EMPTY 0x10 +#define LINK_LIST_READY 0x02 +#define DIS_MCU_CLROOB 0x01 +#define FIFO_EMPTY (TXFIFO_EMPTY | RXFIFO_EMPTY) + +/* PLA_PHY_PWR */ +#define PLA_PHY_PWR_LLR (LINK_LIST_READY << 24) +#define PLA_PHY_PWR_TXEMP (TXFIFO_EMPTY << 24) +#define TEST_IO_OFF BIT(4) + +/* PLA_MISC_1 */ +#define RXDY_GATED_EN 0x0008 + +/* PLA_SFF_STS_7 */ +#define RE_INIT_LL 0x8000 +#define MCU_BORW_EN 0x4000 + +/* PLA_CPCR */ +#define CPCR_RX_VLAN 0x0040 + +/* PLA_CFG_WOL */ +#define MAGIC_EN 0x0001 + +/* PLA_TEREDO_CFG */ +#define TEREDO_SEL 0x8000 +#define TEREDO_WAKE_MASK 0x7f00 +#define TEREDO_RS_EVENT_MASK 0x00fe +#define OOB_TEREDO_EN 0x0001 + +/* PLA_BDC_CR */ +#define ALDPS_PROXY_MODE 0x0001 + +/* PLA_EFUSE_CMD */ +#define EFUSE_READ_CMD BIT(15) +#define EFUSE_DATA_BIT16 BIT(7) + +/* PLA_CONFIG34 */ +#define LINK_ON_WAKE_EN 0x0010 +#define LINK_OFF_WAKE_EN 0x0008 + +/* PLA_CONFIG5 */ +#define BWF_EN 0x0040 +#define MWF_EN 0x0020 +#define UWF_EN 0x0010 +#define LAN_WAKE_EN 0x0002 + +/* PLA_LED_FEATURE */ +#define LED_MODE_MASK 0x0700 + +/* PLA_PHY_PWR */ +#define TX_10M_IDLE_EN 0x0080 +#define PFM_PWM_SWITCH 0x0040 + +/* PLA_MAC_PWR_CTRL */ +#define D3_CLK_GATED_EN 0x00004000 +#define MCU_CLK_RATIO 0x07010f07 +#define MCU_CLK_RATIO_MASK 0x0f0f0f0f +#define ALDPS_SPDWN_RATIO 0x0f87 + +/* PLA_MAC_PWR_CTRL2 */ +#define EEE_SPDWN_RATIO 0x8007 +#define MAC_CLK_SPDWN_EN BIT(15) + +/* PLA_MAC_PWR_CTRL3 */ +#define PLA_MCU_SPDWN_EN BIT(14) +#define PKT_AVAIL_SPDWN_EN 0x0100 +#define SUSPEND_SPDWN_EN 0x0004 +#define U1U2_SPDWN_EN 0x0002 +#define L1_SPDWN_EN 0x0001 + +/* PLA_MAC_PWR_CTRL4 */ +#define PWRSAVE_SPDWN_EN 0x1000 +#define RXDV_SPDWN_EN 0x0800 +#define TX10MIDLE_EN 0x0100 +#define TP100_SPDWN_EN 0x0020 +#define TP500_SPDWN_EN 0x0010 +#define TP1000_SPDWN_EN 0x0008 +#define EEE_SPDWN_EN 0x0001 + +/* PLA_GPHY_INTR_IMR */ +#define GPHY_STS_MSK 0x0001 +#define SPEED_DOWN_MSK 0x0002 +#define SPDWN_RXDV_MSK 0x0004 +#define SPDWN_LINKCHG_MSK 0x0008 + +/* PLA_PHYAR */ +#define PHYAR_FLAG 0x80000000 + +/* PLA_EEE_CR */ +#define EEE_RX_EN 0x0001 +#define EEE_TX_EN 0x0002 + +/* PLA_BOOT_CTRL */ +#define AUTOLOAD_DONE 0x0002 + +/* PLA_EXTRA_STATUS */ +#define U3P3_CHECK_EN BIT(7) + +/* USB_USB2PHY */ +#define USB2PHY_SUSPEND 0x0001 +#define USB2PHY_L1 0x0002 + +/* USB_SSPHYLINK2 */ +#define pwd_dn_scale_mask 0x3ffe +#define pwd_dn_scale(x) ((x) << 1) + +/* USB_CSR_DUMMY1 */ +#define DYNAMIC_BURST 0x0001 + +/* USB_CSR_DUMMY2 */ +#define EP4_FULL_FC 0x0001 + +/* USB_DEV_STAT */ +#define STAT_SPEED_MASK 0x0006 +#define STAT_SPEED_HIGH 0x0000 +#define STAT_SPEED_FULL 0x0002 + +/* USB_FW_FIX_EN1 */ +#define FW_IP_RESET_EN BIT(9) + +/* USB_LPM_CONFIG */ +#define LPM_U1U2_EN BIT(0) + +/* USB_TX_AGG */ +#define TX_AGG_MAX_THRESHOLD 0x03 + +/* USB_RX_BUF_TH */ +#define RX_THR_SUPPER 0x0c350180 +#define RX_THR_HIGH 0x7a120180 +#define RX_THR_SLOW 0xffff0180 + +/* USB_RX_EARLY_TIMEOUT */ +#define RX_AUXILIARY_TIMER 1264 + +/* USB_TX_DMA */ +#define TEST_MODE_DISABLE 0x00000001 +#define TX_SIZE_ADJUST1 0x00000100 + +/* USB_BMU_RESET */ +#define BMU_RESET_EP_IN 0x01 +#define BMU_RESET_EP_OUT 0x02 + +/* USB_UPT_RXDMA_OWN */ +#define OWN_UPDATE BIT(0) +#define OWN_CLEAR BIT(1) + +/* USB_UPS_CTRL */ +#define POWER_CUT 0x0100 + +/* USB_PM_CTRL_STATUS */ +#define RESUME_INDICATE 0x0001 + +/* USB_USB_CTRL */ +#define RX_AGG_DISABLE 0x0010 +#define RX_ZERO_EN 0x0080 + +/* USB_U2P3_CTRL */ +#define U2P3_ENABLE 0x0001 + +/* USB_POWER_CUT */ +#define PWR_EN 0x0001 +#define PHASE2_EN 0x0008 + +/* USB_MISC_0 */ +#define PCUT_STATUS 0x0001 + +/* USB_RX_EARLY_TIMEOUT */ +#define COALESCE_SUPER 85000U +#define COALESCE_HIGH 250000U +#define COALESCE_SLOW 524280U + +/* USB_WDT11_CTRL */ +#define TIMER11_EN 0x0001 + +/* USB_LPM_CTRL */ +/* bit 4 ~ 5: fifo empty boundary */ +#define FIFO_EMPTY_1FB 0x30 /* 0x1fb * 64 = 32448 bytes */ +/* bit 2 ~ 3: LMP timer */ +#define LPM_TIMER_MASK 0x0c +#define LPM_TIMER_500MS 0x04 /* 500 ms */ +#define LPM_TIMER_500US 0x0c /* 500 us */ +#define ROK_EXIT_LPM 0x02 + +/* USB_AFE_CTRL2 */ +#define SEN_VAL_MASK 0xf800 +#define SEN_VAL_NORMAL 0xa000 +#define SEL_RXIDLE 0x0100 + +/* USB_UPS_CFG */ +#define SAW_CNT_1MS_MASK 0x0fff + +/* OCP_ALDPS_CONFIG */ +#define ENPWRSAVE 0x8000 +#define ENPDNPS 0x0200 +#define LINKENA 0x0100 +#define DIS_SDSAVE 0x0010 + +/* OCP_PHY_STATUS */ +#define PHY_STAT_MASK 0x0007 +#define PHY_STAT_LAN_ON 3 +#define PHY_STAT_PWRDN 5 + +/* OCP_NCTL_CFG */ +#define PGA_RETURN_EN BIT(1) + +/* OCP_POWER_CFG */ +#define EEE_CLKDIV_EN 0x8000 +#define EN_ALDPS 0x0004 +#define EN_10M_PLLOFF 0x0001 + +/* OCP_EEE_CONFIG1 */ +#define RG_TXLPI_MSK_HFDUP 0x8000 +#define RG_MATCLR_EN 0x4000 +#define EEE_10_CAP 0x2000 +#define EEE_NWAY_EN 0x1000 +#define TX_QUIET_EN 0x0200 +#define RX_QUIET_EN 0x0100 +#define sd_rise_time_mask 0x0070 +#define sd_rise_time(x) (min((x), 7) << 4) /* bit 4 ~ 6 */ +#define RG_RXLPI_MSK_HFDUP 0x0008 +#define SDFALLTIME 0x0007 /* bit 0 ~ 2 */ + +/* OCP_EEE_CONFIG2 */ +#define RG_LPIHYS_NUM 0x7000 /* bit 12 ~ 15 */ +#define RG_DACQUIET_EN 0x0400 +#define RG_LDVQUIET_EN 0x0200 +#define RG_CKRSEL 0x0020 +#define RG_EEEPRG_EN 0x0010 + +/* OCP_EEE_CONFIG3 */ +#define fast_snr_mask 0xff80 +#define fast_snr(x) (min((x), 0x1ff) << 7) /* bit 7 ~ 15 */ +#define RG_LFS_SEL 0x0060 /* bit 6 ~ 5 */ +#define MSK_PH 0x0006 /* bit 0 ~ 3 */ + +/* OCP_EEE_AR */ +/* bit[15:14] function */ +#define FUN_ADDR 0x0000 +#define FUN_DATA 0x4000 +/* bit[4:0] device addr */ + +/* OCP_EEE_CFG */ +#define CTAP_SHORT_EN 0x0040 +#define EEE10_EN 0x0010 + +/* OCP_DOWN_SPEED */ +#define EN_10M_BGOFF 0x0080 + +/* OCP_PHY_STATE */ +#define TXDIS_STATE 0x01 +#define ABD_STATE 0x02 + +/* OCP_ADC_CFG */ +#define CKADSEL_L 0x0100 +#define ADC_EN 0x0080 +#define EN_EMI_L 0x0040 + +/* SRAM_GREEN_CFG */ +#define GREEN_ETH_EN BIT(15) +#define R_TUNE_EN BIT(11) + +/* SRAM_LPF_CFG */ +#define LPF_AUTO_TUNE 0x8000 + +/* SRAM_10M_AMP1 */ +#define GDAC_IB_UPALL 0x0008 + +/* SRAM_10M_AMP2 */ +#define AMP_DN 0x0200 + +/* SRAM_IMPEDANCE */ +#define RX_DRIVING_MASK 0x6000 + +#define RTL8152_MAX_TX 4 +#define RTL8152_MAX_RX 10 +#define INTBUFSIZE 2 +#define CRC_SIZE 4 +#define TX_ALIGN 4 +#define RX_ALIGN 8 + +#define INTR_LINK 0x0004 + +#define RTL8152_REQT_READ 0xc0 +#define RTL8152_REQT_WRITE 0x40 +#define RTL8152_REQ_GET_REGS 0x05 +#define RTL8152_REQ_SET_REGS 0x05 + +#define BYTE_EN_DWORD 0xff +#define BYTE_EN_WORD 0x33 +#define BYTE_EN_BYTE 0x11 +#define BYTE_EN_SIX_BYTES 0x3f +#define BYTE_EN_START_MASK 0x0f +#define BYTE_EN_END_MASK 0xf0 + +#define RTL8152_ETH_FRAME_LEN 1514 +#define RTL8152_AGG_BUF_SZ 2048 + +#define RTL8152_RMS (RTL8152_ETH_FRAME_LEN + CRC_SIZE) +#define RTL8153_RMS (RTL8152_ETH_FRAME_LEN + CRC_SIZE) +#define RTL8152_TX_TIMEOUT (5 * HZ) + +#define MCU_TYPE_PLA 0x0100 +#define MCU_TYPE_USB 0x0000 + +#define TIMEOUT_RESOLUTION 50 +#define PHY_CONNECT_TIMEOUT 5000 +#define USB_BULK_SEND_TIMEOUT 5000 +#define USB_BULK_RECV_TIMEOUT 5000 +#define R8152_WAIT_TIMEOUT 2000 + +struct rx_desc { + __le32 opts1; +#define RD_CRC BIT(15) +#define RX_LEN_MASK 0x7fff + + __le32 opts2; +#define RD_UDP_CS BIT(23) +#define RD_TCP_CS BIT(22) +#define RD_IPV6_CS BIT(20) +#define RD_IPV4_CS BIT(19) + + __le32 opts3; +#define IPF BIT(23) /* IP checksum fail */ +#define UDPF BIT(22) /* UDP checksum fail */ +#define TCPF BIT(21) /* TCP checksum fail */ +#define RX_VLAN_TAG BIT(16) + + __le32 opts4; + __le32 opts5; + __le32 opts6; +}; + +struct tx_desc { + __le32 opts1; +#define TX_FS BIT(31) /* First segment of a packet */ +#define TX_LS BIT(30) /* Final segment of a packet */ +#define LGSEND BIT(29) +#define GTSENDV4 BIT(28) +#define GTSENDV6 BIT(27) +#define GTTCPHO_SHIFT 18 +#define GTTCPHO_MAX 0x7fU +#define TX_LEN_MAX 0x3ffffU + + __le32 opts2; +#define UDP_CS BIT(31) /* Calculate UDP/IP checksum */ +#define TCP_CS BIT(30) /* Calculate TCP/IP checksum */ +#define IPV4_CS BIT(29) /* Calculate IPv4 checksum */ +#define IPV6_CS BIT(28) /* Calculate IPv6 checksum */ +#define MSS_SHIFT 17 +#define MSS_MAX 0x7ffU +#define TCPHO_SHIFT 17 +#define TCPHO_MAX 0x7ffU +#define TX_VLAN_TAG BIT(16) +}; + +enum rtl_version { + RTL_VER_UNKNOWN = 0, + RTL_VER_01, + RTL_VER_02, + RTL_VER_03, + RTL_VER_04, + RTL_VER_05, + RTL_VER_06, + RTL_VER_07, + RTL_VER_08, + RTL_VER_09, + RTL_VER_MAX +}; + +enum rtl_register_content { + _1000bps = 0x10, + _100bps = 0x08, + _10bps = 0x04, + LINK_STATUS = 0x02, + FULL_DUP = 0x01, +}; + +struct r8152 { + struct usb_device *udev; + struct usbnet *dev; + struct usb_interface *intf; + bool supports_gmii; + void *txbuf; + void *rxbuf; + + struct rtl_ops { + void (*init)(struct r8152 *tp); + int (*enable)(struct r8152 *tp); + void (*disable)(struct r8152 *tp); + void (*up)(struct r8152 *tp); + } rtl_ops; + + u32 coalesce; + u16 ocp_base; + + u8 version; +}; + +int r8152_generic_ocp_write(struct r8152 *tp, u16 index, u16 byteen, + u16 size, const void *data, u16 type); + +u16 r8152_ocp_read_word(struct r8152 *tp, u16 type, u16 index); +void r8152_ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data); + +u8 r8152_ocp_read_byte(struct r8152 *tp, u16 type, u16 index); +void r8152_ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data); + +u16 r8152_ocp_reg_read(struct r8152 *tp, u16 addr); +void r8152_ocp_reg_write(struct r8152 *tp, u16 addr, u16 data); + +void r8152_sram_write(struct r8152 *tp, u16 addr, u16 data); + +void r8152b_firmware(struct r8152 *tp); +void r8153_firmware(struct r8152 *tp); +void r8153b_firmware(struct r8152 *tp); +#endif diff --git a/drivers/net/usb/r8152_fw.c b/drivers/net/usb/r8152_fw.c new file mode 100644 index 0000000000..a6d61ac0d6 --- /dev/null +++ b/drivers/net/usb/r8152_fw.c @@ -0,0 +1,1199 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015 Realtek Semiconductor Corp. All rights reserved. */ + +#include <common.h> +#include <linux/usb/usb.h> +#include <linux/usb/usbnet.h> +#include "r8152.h" + +static const u8 r8152b_pla_patch_a[] = { + 0x08, 0xe0, 0x40, 0xe0, 0x78, 0xe0, 0x85, 0xe0, + 0x5d, 0xe1, 0xa1, 0xe1, 0xa3, 0xe1, 0xab, 0xe1, + 0x31, 0xc3, 0x60, 0x72, 0xa0, 0x49, 0x10, 0xf0, + 0xa4, 0x49, 0x0e, 0xf0, 0x2c, 0xc3, 0x62, 0x72, + 0x26, 0x70, 0x80, 0x49, 0x05, 0xf0, 0x2f, 0x48, + 0x62, 0x9a, 0x24, 0x70, 0x60, 0x98, 0x24, 0xc3, + 0x60, 0x99, 0x23, 0xc3, 0x00, 0xbb, 0x2c, 0x75, + 0xdc, 0x21, 0xbc, 0x25, 0x04, 0x13, 0x0a, 0xf0, + 0x03, 0x13, 0x08, 0xf0, 0x02, 0x13, 0x06, 0xf0, + 0x01, 0x13, 0x04, 0xf0, 0x08, 0x13, 0x02, 0xf0, + 0x03, 0xe0, 0xd4, 0x49, 0x04, 0xf1, 0x14, 0xc2, + 0x12, 0xc3, 0x00, 0xbb, 0x12, 0xc3, 0x60, 0x75, + 0xd0, 0x49, 0x05, 0xf1, 0x50, 0x48, 0x60, 0x9d, + 0x09, 0xc6, 0x00, 0xbe, 0xd0, 0x48, 0x60, 0x9d, + 0xf3, 0xe7, 0xc2, 0xc0, 0x38, 0xd2, 0xc6, 0xd2, + 0x84, 0x17, 0xa2, 0x13, 0x0c, 0x17, 0xbc, 0xc0, + 0xa2, 0xd1, 0x33, 0xc5, 0xa0, 0x74, 0xc0, 0x49, + 0x1f, 0xf0, 0x30, 0xc5, 0xa0, 0x73, 0x00, 0x13, + 0x04, 0xf1, 0xa2, 0x73, 0x00, 0x13, 0x14, 0xf0, + 0x28, 0xc5, 0xa0, 0x74, 0xc8, 0x49, 0x1b, 0xf1, + 0x26, 0xc5, 0xa0, 0x76, 0xa2, 0x74, 0x01, 0x06, + 0x20, 0x37, 0xa0, 0x9e, 0xa2, 0x9c, 0x1e, 0xc5, + 0xa2, 0x73, 0x23, 0x40, 0x10, 0xf8, 0x04, 0xf3, + 0xa0, 0x73, 0x33, 0x40, 0x0c, 0xf8, 0x15, 0xc5, + 0xa0, 0x74, 0x41, 0x48, 0xa0, 0x9c, 0x14, 0xc5, + 0xa0, 0x76, 0x62, 0x48, 0xe0, 0x48, 0xa0, 0x9e, + 0x10, 0xc6, 0x00, 0xbe, 0x0a, 0xc5, 0xa0, 0x74, + 0x48, 0x48, 0xa0, 0x9c, 0x0b, 0xc5, 0x20, 0x1e, + 0xa0, 0x9e, 0xe5, 0x48, 0xa0, 0x9e, 0xf0, 0xe7, + 0xbc, 0xc0, 0xc8, 0xd2, 0xcc, 0xd2, 0x28, 0xe4, + 0x22, 0x02, 0xf0, 0xc0, 0x0b, 0xc0, 0x00, 0x71, + 0x0a, 0xc0, 0x00, 0x72, 0xa0, 0x49, 0x04, 0xf0, + 0xa4, 0x49, 0x02, 0xf0, 0x93, 0x48, 0x04, 0xc0, + 0x00, 0xb8, 0x00, 0xe4, 0xc2, 0xc0, 0x8c, 0x09, + 0x14, 0xc2, 0x40, 0x73, 0xba, 0x48, 0x40, 0x9b, + 0x11, 0xc2, 0x40, 0x73, 0xb0, 0x49, 0x17, 0xf0, + 0xbf, 0x49, 0x03, 0xf1, 0x09, 0xc5, 0x00, 0xbd, + 0xb1, 0x49, 0x11, 0xf0, 0xb1, 0x48, 0x40, 0x9b, + 0x02, 0xc2, 0x00, 0xba, 0x82, 0x18, 0x00, 0xa0, + 0x1e, 0xfc, 0xbc, 0xc0, 0xf0, 0xc0, 0xde, 0xe8, + 0x00, 0x80, 0x00, 0x60, 0x2c, 0x75, 0xd4, 0x49, + 0x12, 0xf1, 0x29, 0xe0, 0xf8, 0xc2, 0x46, 0x71, + 0xf7, 0xc2, 0x40, 0x73, 0xbe, 0x49, 0x03, 0xf1, + 0xf5, 0xc7, 0x02, 0xe0, 0xf2, 0xc7, 0x4f, 0x30, + 0x26, 0x62, 0xa1, 0x49, 0xf0, 0xf1, 0x22, 0x72, + 0xa0, 0x49, 0xed, 0xf1, 0x25, 0x25, 0x18, 0x1f, + 0x97, 0x30, 0x91, 0x30, 0x36, 0x9a, 0x2c, 0x75, + 0x32, 0xc3, 0x60, 0x73, 0xb1, 0x49, 0x0d, 0xf1, + 0xdc, 0x21, 0xbc, 0x25, 0x27, 0xc6, 0xc0, 0x77, + 0x04, 0x13, 0x18, 0xf0, 0x03, 0x13, 0x19, 0xf0, + 0x02, 0x13, 0x1a, 0xf0, 0x01, 0x13, 0x1b, 0xf0, + 0xd4, 0x49, 0x03, 0xf1, 0x1c, 0xc5, 0x00, 0xbd, + 0xcd, 0xc6, 0xc6, 0x67, 0x2e, 0x75, 0xd7, 0x22, + 0xdd, 0x26, 0x05, 0x15, 0x1a, 0xf0, 0x14, 0xc6, + 0x00, 0xbe, 0x13, 0xc5, 0x00, 0xbd, 0x12, 0xc5, + 0x00, 0xbd, 0xf1, 0x49, 0xfb, 0xf1, 0xef, 0xe7, + 0xf4, 0x49, 0xfa, 0xf1, 0xec, 0xe7, 0xf3, 0x49, + 0xf7, 0xf1, 0xe9, 0xe7, 0xf2, 0x49, 0xf4, 0xf1, + 0xe6, 0xe7, 0xb6, 0xc0, 0x6a, 0x14, 0xac, 0x13, + 0xd6, 0x13, 0xfa, 0x14, 0xa0, 0xd1, 0x00, 0x00, + 0xc0, 0x75, 0xd0, 0x49, 0x46, 0xf0, 0x26, 0x72, + 0xa7, 0x49, 0x43, 0xf0, 0x22, 0x72, 0x25, 0x25, + 0x20, 0x1f, 0x97, 0x30, 0x91, 0x30, 0x40, 0x73, + 0xf3, 0xc4, 0x1c, 0x40, 0x04, 0xf0, 0xd7, 0x49, + 0x05, 0xf1, 0x37, 0xe0, 0x53, 0x48, 0xc0, 0x9d, + 0x08, 0x02, 0x40, 0x66, 0x64, 0x27, 0x06, 0x16, + 0x30, 0xf1, 0x46, 0x63, 0x3b, 0x13, 0x2d, 0xf1, + 0x34, 0x9b, 0x18, 0x1b, 0x93, 0x30, 0x2b, 0xc3, + 0x10, 0x1c, 0x2b, 0xe8, 0x01, 0x14, 0x25, 0xf1, + 0x00, 0x1d, 0x26, 0x1a, 0x8a, 0x30, 0x22, 0x73, + 0xb5, 0x25, 0x0e, 0x0b, 0x00, 0x1c, 0x2c, 0xe8, + 0x1f, 0xc7, 0x27, 0x40, 0x1a, 0xf1, 0x38, 0xe8, + 0x32, 0x1f, 0x8f, 0x30, 0x08, 0x1b, 0x24, 0xe8, + 0x36, 0x72, 0x46, 0x77, 0x00, 0x17, 0x0d, 0xf0, + 0x13, 0xc3, 0x1f, 0x40, 0x03, 0xf1, 0x00, 0x1f, + 0x46, 0x9f, 0x44, 0x77, 0x9f, 0x44, 0x5f, 0x44, + 0x17, 0xe8, 0x0a, 0xc7, 0x27, 0x40, 0x05, 0xf1, + 0x02, 0xc3, 0x00, 0xbb, 0x50, 0x1a, 0x06, 0x1a, + 0xff, 0xc7, 0x00, 0xbf, 0xb8, 0xcd, 0xff, 0xff, + 0x02, 0x0c, 0x54, 0xa5, 0xdc, 0xa5, 0x2f, 0x40, + 0x05, 0xf1, 0x00, 0x14, 0xfa, 0xf1, 0x01, 0x1c, + 0x02, 0xe0, 0x00, 0x1c, 0x80, 0xff, 0xb0, 0x49, + 0x04, 0xf0, 0x01, 0x0b, 0xd3, 0xa1, 0x03, 0xe0, + 0x02, 0x0b, 0xd3, 0xa5, 0x27, 0x31, 0x20, 0x37, + 0x02, 0x0b, 0xd3, 0xa5, 0x27, 0x31, 0x20, 0x37, + 0x00, 0x13, 0xfb, 0xf1, 0x80, 0xff, 0x22, 0x73, + 0xb5, 0x25, 0x18, 0x1e, 0xde, 0x30, 0xd9, 0x30, + 0x64, 0x72, 0x11, 0x1e, 0x68, 0x23, 0x16, 0x31, + 0x80, 0xff, 0xd4, 0x49, 0x28, 0xf0, 0x02, 0xb4, + 0x2a, 0xc4, 0x00, 0x1d, 0x2e, 0xe8, 0xe0, 0x73, + 0xb9, 0x21, 0xbd, 0x25, 0x04, 0x13, 0x02, 0xf0, + 0x1a, 0xe0, 0x22, 0xc4, 0x23, 0xc3, 0x2f, 0xe8, + 0x23, 0xc3, 0x2d, 0xe8, 0x00, 0x1d, 0x21, 0xe8, + 0xe2, 0x73, 0xbb, 0x49, 0xfc, 0xf0, 0xe0, 0x73, + 0xb7, 0x48, 0x03, 0xb4, 0x81, 0x1d, 0x19, 0xe8, + 0x40, 0x1a, 0x84, 0x1d, 0x16, 0xe8, 0x12, 0xc3, + 0x1e, 0xe8, 0x03, 0xb0, 0x81, 0x1d, 0x11, 0xe8, + 0x0e, 0xc3, 0x19, 0xe8, 0x02, 0xb0, 0x06, 0xc7, + 0x04, 0x1e, 0xe0, 0x9e, 0x02, 0xc6, 0x00, 0xbe, + 0x22, 0x02, 0x20, 0xe4, 0x04, 0xb8, 0x34, 0xb0, + 0x00, 0x02, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x0c, + 0x09, 0xc7, 0xe0, 0x9b, 0xe2, 0x9a, 0xe4, 0x9c, + 0xe6, 0x8d, 0xe6, 0x76, 0xef, 0x49, 0xfe, 0xf1, + 0x80, 0xff, 0x08, 0xea, 0x82, 0x1d, 0xf5, 0xef, + 0x00, 0x1a, 0x88, 0x1d, 0xf2, 0xef, 0xed, 0xc2, + 0xf0, 0xef, 0x80, 0xff, 0x02, 0xc6, 0x00, 0xbe, + 0x46, 0x06, 0x08, 0xc2, 0x40, 0x73, 0x3a, 0x48, + 0x40, 0x9b, 0x06, 0xff, 0x02, 0xc6, 0x00, 0xbe, + 0x86, 0x17, 0x1e, 0xfc, 0x36, 0xf0, 0x08, 0x1c, + 0xea, 0x8c, 0xe3, 0x64, 0xc7, 0x49, 0x25, 0xf1, + 0xe0, 0x75, 0xff, 0x1b, 0xeb, 0x47, 0xff, 0x1b, + 0x6b, 0x47, 0xe0, 0x9d, 0x15, 0xc3, 0x60, 0x75, + 0xd8, 0x49, 0x04, 0xf0, 0x81, 0x1d, 0xe2, 0x8d, + 0x05, 0xe0, 0xe2, 0x63, 0x81, 0x1d, 0xdd, 0x47, + 0xe2, 0x8b, 0x0b, 0xc3, 0x00, 0x1d, 0x61, 0x8d, + 0x3c, 0x03, 0x60, 0x75, 0xd8, 0x49, 0x06, 0xf1, + 0xdf, 0x48, 0x61, 0x95, 0x16, 0xe0, 0x4e, 0xe8, + 0x12, 0xe8, 0x21, 0xc5, 0xa0, 0x73, 0xb0, 0x49, + 0x03, 0xf0, 0x31, 0x48, 0xa0, 0x9b, 0x0d, 0xe0, + 0xc0, 0x49, 0x0b, 0xf1, 0xe2, 0x63, 0x7e, 0x1d, + 0xdd, 0x46, 0xe2, 0x8b, 0xe0, 0x75, 0x83, 0x1b, + 0xeb, 0x46, 0xfe, 0x1b, 0x6b, 0x46, 0xe0, 0x9d, + 0xe4, 0x49, 0x11, 0xf0, 0x10, 0x1d, 0xea, 0x8d, + 0xe3, 0x64, 0xc6, 0x49, 0x09, 0xf1, 0x07, 0xc5, + 0xa0, 0x73, 0xb1, 0x48, 0xa0, 0x9b, 0x02, 0xc5, + 0x00, 0xbd, 0xe6, 0x04, 0xa0, 0xd1, 0x02, 0xc5, + 0x00, 0xbd, 0xfe, 0x04, 0x02, 0xc5, 0x00, 0xbd, + 0x30, 0x05, 0x00, 0x00 }; + +static const u16 r8152b_ram_code1[] = { + 0x9700, 0x7fe0, 0x4c00, 0x4007, 0x4400, 0x4800, 0x7c1f, 0x4c00, + 0x5310, 0x6000, 0x7c07, 0x6800, 0x673e, 0x0000, 0x0000, 0x571f, + 0x5ffb, 0xaa05, 0x5b58, 0x7d80, 0x6100, 0x3019, 0x5b64, 0x7d80, + 0x6080, 0xa6f8, 0xdcdb, 0x0015, 0xb915, 0xb511, 0xd16b, 0x000f, + 0xb40f, 0xd06b, 0x000d, 0xb206, 0x7c01, 0x5800, 0x7c04, 0x5c00, + 0x3011, 0x7c01, 0x5801, 0x7c04, 0x5c04, 0x3019, 0x30a5, 0x3127, + 0x31d5, 0x7fe0, 0x4c60, 0x7c07, 0x6803, 0x7d00, 0x6900, 0x65a0, + 0x0000, 0x0000, 0xaf03, 0x6015, 0x303e, 0x6017, 0x57e0, 0x580c, + 0x588c, 0x7fdd, 0x5fa2, 0x4827, 0x7c1f, 0x4c00, 0x7c1f, 0x4c10, + 0x8400, 0x7c30, 0x6020, 0x48bf, 0x7c1f, 0x4c00, 0x7c1f, 0x4c01, + 0x7c07, 0x6803, 0xb806, 0x7c08, 0x6800, 0x0000, 0x0000, 0x305c, + 0x7c08, 0x6808, 0x0000, 0x0000, 0xae06, 0x7c02, 0x5c02, 0x0000, + 0x0000, 0x3067, 0x8e05, 0x7c02, 0x5c00, 0x0000, 0x0000, 0xad06, + 0x7c20, 0x5c20, 0x0000, 0x0000, 0x3072, 0x8d05, 0x7c20, 0x5c00, + 0x0000, 0x0000, 0xa008, 0x7c07, 0x6800, 0xb8db, 0x7c07, 0x6803, + 0xd9b3, 0x00d7, 0x7fe0, 0x4c80, 0x7c08, 0x6800, 0x0000, 0x0000, + 0x7c23, 0x5c23, 0x481d, 0x7c1f, 0x4c00, 0x7c1f, 0x4c02, 0x5310, + 0x81ff, 0x30f5, 0x7fe0, 0x4d00, 0x4832, 0x7c1f, 0x4c00, 0x7c1f, + 0x4c10, 0x7c08, 0x6000, 0xa49e, 0x7c07, 0x6800, 0xb89b, 0x7c07, + 0x6803, 0xd9b3, 0x00f9, 0x7fe0, 0x4d20, 0x7e00, 0x6200, 0x3001, + 0x7fe0, 0x4dc0, 0xd09d, 0x0002, 0xb4fe, 0x7fe0, 0x4d80, 0x7c04, + 0x6004, 0x7c07, 0x6802, 0x6728, 0x0000, 0x0000, 0x7c08, 0x6000, + 0x486c, 0x7c1f, 0x4c00, 0x7c1f, 0x4c01, 0x9503, 0x7e00, 0x6200, + 0x571f, 0x5fbb, 0xaa05, 0x5b58, 0x7d80, 0x6100, 0x30c2, 0x5b64, + 0x7d80, 0x6080, 0xcdab, 0x0063, 0xcd8d, 0x0061, 0xd96b, 0x005f, + 0xd0a0, 0x00d7, 0xcba0, 0x0003, 0x80ec, 0x30cf, 0x30dc, 0x7fe0, + 0x4ce0, 0x4832, 0x7c1f, 0x4c00, 0x7c1f, 0x4c08, 0x7c08, 0x6008, + 0x8300, 0xb902, 0x30a5, 0x308a, 0x7fe0, 0x4da0, 0x65a8, 0x0000, + 0x0000, 0x56a0, 0x590c, 0x7ffd, 0x5fa2, 0xae06, 0x7c02, 0x5c02, + 0x0000, 0x0000, 0x30f0, 0x8e05, 0x7c02, 0x5c00, 0x0000, 0x0000, + 0xcba4, 0x0004, 0xcd8d, 0x0002, 0x80f1, 0x7fe0, 0x4ca0, 0x7c08, + 0x6408, 0x0000, 0x0000, 0x7d00, 0x6800, 0xb603, 0x7c10, 0x6010, + 0x7d1f, 0x551f, 0x5fb3, 0xaa07, 0x7c80, 0x5800, 0x5b58, 0x7d80, + 0x6100, 0x310f, 0x7c80, 0x5800, 0x5b64, 0x7d80, 0x6080, 0x4827, + 0x7c1f, 0x4c00, 0x7c1f, 0x4c10, 0x8400, 0x7c10, 0x6000, 0x7fe0, + 0x4cc0, 0x5fbb, 0x4824, 0x7c1f, 0x4c00, 0x7c1f, 0x4c04, 0x8200, + 0x7ce0, 0x5400, 0x6728, 0x0000, 0x0000, 0x30cf, 0x3001, 0x7fe0, + 0x4e00, 0x4007, 0x4400, 0x5310, 0x7c07, 0x6800, 0x673e, 0x0000, + 0x0000, 0x570f, 0x5fff, 0xaa05, 0x585b, 0x7d80, 0x6100, 0x313b, + 0x5867, 0x7d80, 0x6080, 0x9403, 0x7e00, 0x6200, 0xcda3, 0x00e7, + 0xcd85, 0x00e5, 0xd96b, 0x00e3, 0x96e3, 0x7c07, 0x6800, 0x673e, + 0x0000, 0x0000, 0x7fe0, 0x4e20, 0x96db, 0x8b04, 0x7c08, 0x5008, + 0xab03, 0x7c08, 0x5000, 0x7c07, 0x6801, 0x677e, 0x0000, 0x0000, + 0xdb7c, 0x00ec, 0x0000, 0x7fe1, 0x4f40, 0x4837, 0x4418, 0x41c7, + 0x7fe0, 0x4e40, 0x7c40, 0x5400, 0x7c1f, 0x4c01, 0x7c1f, 0x4c01, + 0x8fbf, 0xd2a0, 0x004b, 0x9204, 0xa042, 0x3168, 0x3127, 0x7fe1, + 0x4f60, 0x489c, 0x4628, 0x7fe0, 0x4e60, 0x7e28, 0x4628, 0x7c40, + 0x5400, 0x7c01, 0x5800, 0x7c04, 0x5c00, 0x41e8, 0x7c1f, 0x4c01, + 0x7c1f, 0x4c01, 0x8fa5, 0xb241, 0xa02a, 0x3182, 0x7fe0, 0x4ea0, + 0x7c02, 0x4402, 0x4448, 0x4894, 0x7c1f, 0x4c01, 0x7c1f, 0x4c03, + 0x4824, 0x7c1f, 0x4c07, 0x41ef, 0x41ff, 0x4891, 0x7c1f, 0x4c07, + 0x7c1f, 0x4c17, 0x8400, 0x8ef8, 0x41c7, 0x8f8a, 0x92d5, 0xa10f, + 0xd480, 0x0008, 0xd580, 0x00b8, 0xa202, 0x319d, 0x7c04, 0x4404, + 0x319d, 0xd484, 0x00f3, 0xd484, 0x00f1, 0x3127, 0x7fe0, 0x4ee0, + 0x7c40, 0x5400, 0x4488, 0x41cf, 0x3127, 0x7fe0, 0x4ec0, 0x48f3, + 0x7c1f, 0x4c01, 0x7c1f, 0x4c09, 0x4508, 0x41c7, 0x8fb0, 0xd218, + 0x00ae, 0xd2a4, 0x009e, 0x31be, 0x7fe0, 0x4e80, 0x4832, 0x7c1f, + 0x4c01, 0x7c1f, 0x4c11, 0x4428, 0x7c40, 0x5440, 0x7c01, 0x5801, + 0x7c04, 0x5c04, 0x41e8, 0xa4b3, 0x31d3, 0x7fe0, 0x4f20, 0x7c07, + 0x6800, 0x673e, 0x0000, 0x0000, 0x570f, 0x5fff, 0xaa04, 0x585b, + 0x6100, 0x31e4, 0x5867, 0x6080, 0xbcf1, 0x3001 }; + +static const u16 r8152b_pla_patch_a_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x170b, 0xfc2a, 0x01e1, 0xfc2c, 0x0989, + 0xfc2e, 0x1349, 0xfc30, 0x01b7, 0xfc32, 0x061d, 0xe422, 0x0020, + 0xe420, 0x0018, 0xfc34, 0x1785, 0xfc36, 0x047b }; + +static const u8 r8152b_pla_patch_a2[] = { + 0x08, 0xe0, 0x1a, 0xe0, 0xf2, 0xe0, 0xfa, 0xe0, + 0x32, 0xe1, 0x34, 0xe1, 0x36, 0xe1, 0x38, 0xe1, + 0x2c, 0x75, 0xdc, 0x21, 0xbc, 0x25, 0x04, 0x13, + 0x0b, 0xf0, 0x03, 0x13, 0x09, 0xf0, 0x02, 0x13, + 0x07, 0xf0, 0x01, 0x13, 0x05, 0xf0, 0x08, 0x13, + 0x03, 0xf0, 0x04, 0xc3, 0x00, 0xbb, 0x03, 0xc3, + 0x00, 0xbb, 0xd2, 0x17, 0xbc, 0x17, 0x14, 0xc2, + 0x40, 0x73, 0xba, 0x48, 0x40, 0x9b, 0x11, 0xc2, + 0x40, 0x73, 0xb0, 0x49, 0x17, 0xf0, 0xbf, 0x49, + 0x03, 0xf1, 0x09, 0xc5, 0x00, 0xbd, 0xb1, 0x49, + 0x11, 0xf0, 0xb1, 0x48, 0x40, 0x9b, 0x02, 0xc2, + 0x00, 0xba, 0x4e, 0x19, 0x00, 0xa0, 0x1e, 0xfc, + 0xbc, 0xc0, 0xf0, 0xc0, 0xde, 0xe8, 0x00, 0x80, + 0x00, 0x60, 0x2c, 0x75, 0xd4, 0x49, 0x12, 0xf1, + 0x29, 0xe0, 0xf8, 0xc2, 0x46, 0x71, 0xf7, 0xc2, + 0x40, 0x73, 0xbe, 0x49, 0x03, 0xf1, 0xf5, 0xc7, + 0x02, 0xe0, 0xf2, 0xc7, 0x4f, 0x30, 0x26, 0x62, + 0xa1, 0x49, 0xf0, 0xf1, 0x22, 0x72, 0xa0, 0x49, + 0xed, 0xf1, 0x25, 0x25, 0x18, 0x1f, 0x97, 0x30, + 0x91, 0x30, 0x36, 0x9a, 0x2c, 0x75, 0x32, 0xc3, + 0x60, 0x73, 0xb1, 0x49, 0x0d, 0xf1, 0xdc, 0x21, + 0xbc, 0x25, 0x27, 0xc6, 0xc0, 0x77, 0x04, 0x13, + 0x18, 0xf0, 0x03, 0x13, 0x19, 0xf0, 0x02, 0x13, + 0x1a, 0xf0, 0x01, 0x13, 0x1b, 0xf0, 0xd4, 0x49, + 0x03, 0xf1, 0x1c, 0xc5, 0x00, 0xbd, 0xcd, 0xc6, + 0xc6, 0x67, 0x2e, 0x75, 0xd7, 0x22, 0xdd, 0x26, + 0x05, 0x15, 0x1a, 0xf0, 0x14, 0xc6, 0x00, 0xbe, + 0x13, 0xc5, 0x00, 0xbd, 0x12, 0xc5, 0x00, 0xbd, + 0xf1, 0x49, 0xfb, 0xf1, 0xef, 0xe7, 0xf4, 0x49, + 0xfa, 0xf1, 0xec, 0xe7, 0xf3, 0x49, 0xf7, 0xf1, + 0xe9, 0xe7, 0xf2, 0x49, 0xf4, 0xf1, 0xe6, 0xe7, + 0xb6, 0xc0, 0xf6, 0x14, 0x36, 0x14, 0x62, 0x14, + 0x86, 0x15, 0xa0, 0xd1, 0x00, 0x00, 0xc0, 0x75, + 0xd0, 0x49, 0x46, 0xf0, 0x26, 0x72, 0xa7, 0x49, + 0x43, 0xf0, 0x22, 0x72, 0x25, 0x25, 0x20, 0x1f, + 0x97, 0x30, 0x91, 0x30, 0x40, 0x73, 0xf3, 0xc4, + 0x1c, 0x40, 0x04, 0xf0, 0xd7, 0x49, 0x05, 0xf1, + 0x37, 0xe0, 0x53, 0x48, 0xc0, 0x9d, 0x08, 0x02, + 0x40, 0x66, 0x64, 0x27, 0x06, 0x16, 0x30, 0xf1, + 0x46, 0x63, 0x3b, 0x13, 0x2d, 0xf1, 0x34, 0x9b, + 0x18, 0x1b, 0x93, 0x30, 0x2b, 0xc3, 0x10, 0x1c, + 0x2b, 0xe8, 0x01, 0x14, 0x25, 0xf1, 0x00, 0x1d, + 0x26, 0x1a, 0x8a, 0x30, 0x22, 0x73, 0xb5, 0x25, + 0x0e, 0x0b, 0x00, 0x1c, 0x2c, 0xe8, 0x1f, 0xc7, + 0x27, 0x40, 0x1a, 0xf1, 0x38, 0xe8, 0x32, 0x1f, + 0x8f, 0x30, 0x08, 0x1b, 0x24, 0xe8, 0x36, 0x72, + 0x46, 0x77, 0x00, 0x17, 0x0d, 0xf0, 0x13, 0xc3, + 0x1f, 0x40, 0x03, 0xf1, 0x00, 0x1f, 0x46, 0x9f, + 0x44, 0x77, 0x9f, 0x44, 0x5f, 0x44, 0x17, 0xe8, + 0x0a, 0xc7, 0x27, 0x40, 0x05, 0xf1, 0x02, 0xc3, + 0x00, 0xbb, 0x1c, 0x1b, 0xd2, 0x1a, 0xff, 0xc7, + 0x00, 0xbf, 0xb8, 0xcd, 0xff, 0xff, 0x02, 0x0c, + 0x54, 0xa5, 0xdc, 0xa5, 0x2f, 0x40, 0x05, 0xf1, + 0x00, 0x14, 0xfa, 0xf1, 0x01, 0x1c, 0x02, 0xe0, + 0x00, 0x1c, 0x80, 0xff, 0xb0, 0x49, 0x04, 0xf0, + 0x01, 0x0b, 0xd3, 0xa1, 0x03, 0xe0, 0x02, 0x0b, + 0xd3, 0xa5, 0x27, 0x31, 0x20, 0x37, 0x02, 0x0b, + 0xd3, 0xa5, 0x27, 0x31, 0x20, 0x37, 0x00, 0x13, + 0xfb, 0xf1, 0x80, 0xff, 0x22, 0x73, 0xb5, 0x25, + 0x18, 0x1e, 0xde, 0x30, 0xd9, 0x30, 0x64, 0x72, + 0x11, 0x1e, 0x68, 0x23, 0x16, 0x31, 0x80, 0xff, + 0x08, 0xc2, 0x40, 0x73, 0x3a, 0x48, 0x40, 0x9b, + 0x06, 0xff, 0x02, 0xc6, 0x00, 0xbe, 0x4e, 0x18, + 0x1e, 0xfc, 0x33, 0xc5, 0xa0, 0x74, 0xc0, 0x49, + 0x1f, 0xf0, 0x30, 0xc5, 0xa0, 0x73, 0x00, 0x13, + 0x04, 0xf1, 0xa2, 0x73, 0x00, 0x13, 0x14, 0xf0, + 0x28, 0xc5, 0xa0, 0x74, 0xc8, 0x49, 0x1b, 0xf1, + 0x26, 0xc5, 0xa0, 0x76, 0xa2, 0x74, 0x01, 0x06, + 0x20, 0x37, 0xa0, 0x9e, 0xa2, 0x9c, 0x1e, 0xc5, + 0xa2, 0x73, 0x23, 0x40, 0x10, 0xf8, 0x04, 0xf3, + 0xa0, 0x73, 0x33, 0x40, 0x0c, 0xf8, 0x15, 0xc5, + 0xa0, 0x74, 0x41, 0x48, 0xa0, 0x9c, 0x14, 0xc5, + 0xa0, 0x76, 0x62, 0x48, 0xe0, 0x48, 0xa0, 0x9e, + 0x10, 0xc6, 0x00, 0xbe, 0x0a, 0xc5, 0xa0, 0x74, + 0x48, 0x48, 0xa0, 0x9c, 0x0b, 0xc5, 0x20, 0x1e, + 0xa0, 0x9e, 0xe5, 0x48, 0xa0, 0x9e, 0xf0, 0xe7, + 0xbc, 0xc0, 0xc8, 0xd2, 0xcc, 0xd2, 0x28, 0xe4, + 0x22, 0x02, 0xf0, 0xc0, 0x02, 0xc6, 0x00, 0xbe, + 0x00, 0x00, 0x02, 0xc6, 0x00, 0xbe, 0x00, 0x00, + 0x02, 0xc6, 0x00, 0xbe, 0x00, 0x00, 0x02, 0xc6, + 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static const u16 r8152b_pla_patch_a2_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x17a5, 0xfc2a, 0x13ad, + 0xfc2c, 0x184d, 0xfc2e, 0x01e1 }; + +static const u16 r8153_ram_code_a[] = { + 0xE86C, 0xA000, 0xB436, 0xB820, 0xB438, 0x0290, 0xB436, 0xA012, + 0xB438, 0x0000, 0xB436, 0xA014, 0xB438, 0x2c04, 0xB438, 0x2c18, + 0xB438, 0x2c45, 0xB438, 0x2c45, 0xB438, 0xd502, 0xB438, 0x8301, + 0xB438, 0x8306, 0xB438, 0xd500, 0xB438, 0x8208, 0xB438, 0xd501, + 0xB438, 0xe018, 0xB438, 0x0308, 0xB438, 0x60f2, 0xB438, 0x8404, + 0xB438, 0x607d, 0xB438, 0xc117, 0xB438, 0x2c16, 0xB438, 0xc116, + 0xB438, 0x2c16, 0xB438, 0x607d, 0xB438, 0xc117, 0xB438, 0xa404, + 0xB438, 0xd500, 0xB438, 0x0800, 0xB438, 0xd501, 0xB438, 0x62d2, + 0xB438, 0x615d, 0xB438, 0xc115, 0xB438, 0xa404, 0xB438, 0xc307, + 0xB438, 0xd502, 0xB438, 0x8301, 0xB438, 0x8306, 0xB438, 0xd500, + 0xB438, 0x8208, 0xB438, 0x2c42, 0xB438, 0xc114, 0xB438, 0x8404, + 0xB438, 0xc317, 0xB438, 0xd701, 0xB438, 0x435d, 0xB438, 0xd500, + 0xB438, 0xa208, 0xB438, 0xd502, 0xB438, 0xa306, 0xB438, 0xa301, + 0xB438, 0x2c42, 0xB438, 0x8404, 0xB438, 0x613d, 0xB438, 0xc115, + 0xB438, 0xc307, 0xB438, 0xd502, 0xB438, 0x8301, 0xB438, 0x8306, + 0xB438, 0xd500, 0xB438, 0x8208, 0xB438, 0x2c42, 0xB438, 0xc114, + 0xB438, 0xc317, 0xB438, 0xd701, 0xB438, 0x40dd, 0xB438, 0xd500, + 0xB438, 0xa208, 0xB438, 0xd502, 0xB438, 0xa306, 0xB438, 0xa301, + 0xB438, 0xd500, 0xB438, 0xd702, 0xB438, 0x0800, 0xB436, 0xA01A, + 0xB438, 0x0000, 0xB436, 0xA006, 0xB438, 0x0fff, 0xB436, 0xA004, + 0xB438, 0x0fff, 0xB436, 0xA002, 0xB438, 0x05a3, 0xB436, 0xA000, + 0xB438, 0x3591, 0xB436, 0xB820, 0xB438, 0x0210 }; + +static const u8 r8153_usb_patch_c[] = { + 0x08, 0xe0, 0x0a, 0xe0, 0x14, 0xe0, 0x58, 0xe0, + 0x64, 0xe0, 0x79, 0xe0, 0xab, 0xe0, 0xb6, 0xe0, + 0x02, 0xc5, 0x00, 0xbd, 0x38, 0x3b, 0xdb, 0x49, + 0x04, 0xf1, 0x06, 0xc3, 0x00, 0xbb, 0x5a, 0x02, + 0x05, 0xc4, 0x03, 0xc3, 0x00, 0xbb, 0xa4, 0x04, + 0x7e, 0x02, 0x30, 0xd4, 0x65, 0xc6, 0x66, 0x61, + 0x92, 0x49, 0x12, 0xf1, 0x3e, 0xc0, 0x02, 0x61, + 0x97, 0x49, 0x05, 0xf0, 0x3c, 0xc0, 0x00, 0x61, + 0x90, 0x49, 0x0a, 0xf1, 0xca, 0x63, 0xb0, 0x49, + 0x09, 0xf1, 0xb1, 0x49, 0x05, 0xf0, 0x32, 0xc0, + 0x00, 0x71, 0x9e, 0x49, 0x03, 0xf1, 0xb0, 0x48, + 0x05, 0xe0, 0x30, 0x48, 0xda, 0x61, 0x10, 0x48, + 0xda, 0x89, 0x4a, 0xc6, 0xc0, 0x60, 0x85, 0x49, + 0x03, 0xf0, 0x31, 0x48, 0x04, 0xe0, 0xb1, 0x48, + 0xb2, 0x48, 0x0f, 0xe0, 0x30, 0x18, 0x1b, 0xc1, + 0x0f, 0xe8, 0x1a, 0xc6, 0xc7, 0x65, 0xd0, 0x49, + 0x05, 0xf0, 0x32, 0x48, 0x02, 0xc2, 0x00, 0xba, + 0x3e, 0x16, 0x02, 0xc2, 0x00, 0xba, 0x48, 0x16, + 0x02, 0xc2, 0x00, 0xba, 0x4a, 0x16, 0x02, 0xb4, + 0x09, 0xc2, 0x40, 0x99, 0x0e, 0x48, 0x42, 0x98, + 0x42, 0x70, 0x8e, 0x49, 0xfe, 0xf1, 0x02, 0xb0, + 0x80, 0xff, 0xc0, 0xd4, 0xe4, 0x40, 0x20, 0xd4, + 0xca, 0xcf, 0x00, 0xcf, 0x3c, 0xe4, 0x0c, 0xc0, + 0x00, 0x63, 0xb5, 0x49, 0x09, 0xc0, 0x30, 0x18, + 0x06, 0xc1, 0xea, 0xef, 0xf5, 0xc7, 0x02, 0xc0, + 0x00, 0xb8, 0xd0, 0x10, 0xe4, 0x4b, 0x00, 0xd8, + 0x14, 0xc3, 0x60, 0x61, 0x90, 0x49, 0x06, 0xf0, + 0x11, 0xc3, 0x70, 0x61, 0x12, 0x48, 0x70, 0x89, + 0x08, 0xe0, 0x0a, 0xc6, 0xd4, 0x61, 0x93, 0x48, + 0xd4, 0x89, 0x02, 0xc1, 0x00, 0xb9, 0x72, 0x17, + 0x02, 0xc1, 0x00, 0xb9, 0x9c, 0x15, 0x00, 0xd8, + 0xef, 0xcf, 0x20, 0xd4, 0x30, 0x18, 0xe7, 0xc1, + 0xcb, 0xef, 0x2b, 0xc5, 0xa0, 0x77, 0x00, 0x1c, + 0xa0, 0x9c, 0x28, 0xc5, 0xa0, 0x64, 0xc0, 0x48, + 0xc1, 0x48, 0xc2, 0x48, 0xa0, 0x8c, 0xb1, 0x64, + 0xc0, 0x48, 0xb1, 0x8c, 0x20, 0xc5, 0xa0, 0x64, + 0x40, 0x48, 0x41, 0x48, 0xc2, 0x48, 0xa0, 0x8c, + 0x19, 0xc5, 0xa4, 0x64, 0x44, 0x48, 0xa4, 0x8c, + 0xb1, 0x64, 0x40, 0x48, 0xb1, 0x8c, 0x14, 0xc4, + 0x80, 0x73, 0x13, 0xc4, 0x82, 0x9b, 0x11, 0x1b, + 0x80, 0x9b, 0x0c, 0xc5, 0xa0, 0x64, 0x40, 0x48, + 0x41, 0x48, 0x42, 0x48, 0xa0, 0x8c, 0x05, 0xc5, + 0xa0, 0x9f, 0x02, 0xc5, 0x00, 0xbd, 0x6c, 0x3a, + 0x1e, 0xfc, 0x10, 0xd8, 0x86, 0xd4, 0xf8, 0xcb, + 0x20, 0xe4, 0x0a, 0xc0, 0x16, 0x61, 0x91, 0x48, + 0x16, 0x89, 0x07, 0xc0, 0x11, 0x19, 0x0c, 0x89, + 0x02, 0xc1, 0x00, 0xb9, 0x02, 0x06, 0x00, 0xd4, + 0x40, 0xb4, 0xfe, 0xc0, 0x16, 0x61, 0x91, 0x48, + 0x16, 0x89, 0xfb, 0xc0, 0x11, 0x19, 0x0c, 0x89, + 0x02, 0xc1, 0x00, 0xb9, 0xd2, 0x05, 0x00, 0x00 }; + +static const u16 r8153_usb_patch_c_bp[] = { + 0xfc26, 0xa000, 0xfc28, 0x3b34, 0xfc2a, 0x027c, 0xfc2c, 0x15de, + 0xfc2e, 0x10ce, 0xfc30, 0x1adc, 0xfc32, 0x3a28, 0xfc34, 0x05f8, + 0xfc36, 0x05c8, 0xfc38, 0x00f3 }; + +static const u8 r8153_pla_patch_c[] = { + 0x5d, 0xe0, 0x07, 0xe0, 0x0f, 0xe0, 0x5a, 0xe0, + 0x59, 0xe0, 0x1f, 0xe0, 0x57, 0xe0, 0x3e, 0xe1, + 0x08, 0xc2, 0x40, 0x73, 0x3a, 0x48, 0x40, 0x9b, + 0x06, 0xff, 0x02, 0xc6, 0x00, 0xbe, 0xcc, 0x17, + 0x1e, 0xfc, 0x2c, 0x75, 0xdc, 0x21, 0xbc, 0x25, + 0x04, 0x13, 0x0b, 0xf0, 0x03, 0x13, 0x09, 0xf0, + 0x02, 0x13, 0x07, 0xf0, 0x01, 0x13, 0x05, 0xf0, + 0x08, 0x13, 0x03, 0xf0, 0x04, 0xc3, 0x00, 0xbb, + 0x03, 0xc3, 0x00, 0xbb, 0x50, 0x17, 0x3a, 0x17, + 0x33, 0xc5, 0xa0, 0x74, 0xc0, 0x49, 0x1f, 0xf0, + 0x30, 0xc5, 0xa0, 0x73, 0x00, 0x13, 0x04, 0xf1, + 0xa2, 0x73, 0x00, 0x13, 0x14, 0xf0, 0x28, 0xc5, + 0xa0, 0x74, 0xc8, 0x49, 0x1b, 0xf1, 0x26, 0xc5, + 0xa0, 0x76, 0xa2, 0x74, 0x01, 0x06, 0x20, 0x37, + 0xa0, 0x9e, 0xa2, 0x9c, 0x1e, 0xc5, 0xa2, 0x73, + 0x23, 0x40, 0x10, 0xf8, 0x04, 0xf3, 0xa0, 0x73, + 0x33, 0x40, 0x0c, 0xf8, 0x15, 0xc5, 0xa0, 0x74, + 0x41, 0x48, 0xa0, 0x9c, 0x14, 0xc5, 0xa0, 0x76, + 0x62, 0x48, 0xe0, 0x48, 0xa0, 0x9e, 0x10, 0xc6, + 0x00, 0xbe, 0x0a, 0xc5, 0xa0, 0x74, 0x48, 0x48, + 0xa0, 0x9c, 0x0b, 0xc5, 0x20, 0x1e, 0xa0, 0x9e, + 0xe5, 0x48, 0xa0, 0x9e, 0xf0, 0xe7, 0xbc, 0xc0, + 0xc8, 0xd2, 0xcc, 0xd2, 0x28, 0xe4, 0xfa, 0x01, + 0xf0, 0xc0, 0x18, 0x89, 0x74, 0xc0, 0xcd, 0xe8, + 0x80, 0x76, 0x00, 0x1d, 0x6e, 0xc3, 0x66, 0x62, + 0xa0, 0x49, 0x06, 0xf0, 0x64, 0xc0, 0x02, 0x71, + 0x60, 0x99, 0x62, 0xc1, 0x03, 0xe0, 0x5f, 0xc0, + 0x60, 0xc1, 0x02, 0x99, 0x00, 0x61, 0x0f, 0x1b, + 0x59, 0x41, 0x03, 0x13, 0x18, 0xf1, 0xe4, 0x49, + 0x20, 0xf1, 0xe5, 0x49, 0x1e, 0xf0, 0x59, 0xc6, + 0xd0, 0x73, 0xb7, 0x49, 0x08, 0xf0, 0x01, 0x0b, + 0x80, 0x13, 0x03, 0xf0, 0xd0, 0x8b, 0x03, 0xe0, + 0x3f, 0x48, 0xd0, 0x9b, 0x51, 0xc0, 0x10, 0x1a, + 0x84, 0x1b, 0xb1, 0xe8, 0x4b, 0xc2, 0x40, 0x63, + 0x30, 0x48, 0x0a, 0xe0, 0xe5, 0x49, 0x09, 0xf0, + 0x47, 0xc0, 0x00, 0x1a, 0x84, 0x1b, 0xa7, 0xe8, + 0x41, 0xc2, 0x40, 0x63, 0xb0, 0x48, 0x40, 0x8b, + 0x67, 0x11, 0x3f, 0xf1, 0x69, 0x33, 0x32, 0xc0, + 0x28, 0x40, 0xd2, 0xf1, 0x33, 0xc0, 0x00, 0x19, + 0x81, 0x1b, 0x99, 0xe8, 0x30, 0xc0, 0x04, 0x1a, + 0x84, 0x1b, 0x95, 0xe8, 0x8a, 0xe8, 0xa3, 0x49, + 0xfe, 0xf0, 0x2a, 0xc0, 0x86, 0xe8, 0xa1, 0x48, + 0x84, 0x1b, 0x8d, 0xe8, 0x00, 0x1d, 0x69, 0x33, + 0x00, 0x1e, 0x01, 0x06, 0xff, 0x18, 0x30, 0x40, + 0xfd, 0xf1, 0x1f, 0xc0, 0x00, 0x76, 0x2e, 0x40, + 0xf7, 0xf1, 0x21, 0x48, 0x19, 0xc0, 0x84, 0x1b, + 0x7e, 0xe8, 0x74, 0x08, 0x72, 0xe8, 0xa1, 0x49, + 0xfd, 0xf0, 0x11, 0xc0, 0x00, 0x1a, 0x84, 0x1b, + 0x76, 0xe8, 0x6b, 0xe8, 0xa5, 0x49, 0xfe, 0xf0, + 0x09, 0xc0, 0x01, 0x19, 0x81, 0x1b, 0x6f, 0xe8, + 0x5a, 0xe0, 0xb8, 0x0b, 0x50, 0xe8, 0x83, 0x00, + 0x82, 0x00, 0x20, 0xb4, 0x10, 0xd8, 0x84, 0xd4, + 0x88, 0xd3, 0x10, 0xe0, 0x00, 0xd8, 0x24, 0xd4, + 0xf9, 0xc0, 0x57, 0xe8, 0x48, 0x33, 0xf3, 0xc0, + 0x00, 0x61, 0x6a, 0xc0, 0x47, 0x11, 0x03, 0xf0, + 0x57, 0x11, 0x05, 0xf1, 0x00, 0x61, 0x17, 0x48, + 0x00, 0x89, 0x41, 0xe0, 0x9c, 0x20, 0x9c, 0x24, + 0xd0, 0x49, 0x09, 0xf0, 0x04, 0x11, 0x07, 0xf1, + 0x00, 0x61, 0x97, 0x49, 0x38, 0xf0, 0x97, 0x48, + 0x00, 0x89, 0x2b, 0xe0, 0x00, 0x11, 0x05, 0xf1, + 0x00, 0x61, 0x92, 0x48, 0x00, 0x89, 0x2f, 0xe0, + 0x06, 0x11, 0x05, 0xf1, 0x00, 0x61, 0x11, 0x48, + 0x00, 0x89, 0x29, 0xe0, 0x05, 0x11, 0x0f, 0xf1, + 0x00, 0x61, 0x93, 0x49, 0x1a, 0xf1, 0x91, 0x49, + 0x0a, 0xf0, 0x91, 0x48, 0x00, 0x89, 0x0f, 0xe0, + 0xc6, 0xc0, 0x00, 0x61, 0x98, 0x20, 0x98, 0x24, + 0x25, 0x11, 0x80, 0xff, 0xfa, 0xef, 0x17, 0xf1, + 0x38, 0xc0, 0x1f, 0xe8, 0x95, 0x49, 0x13, 0xf0, + 0xf4, 0xef, 0x11, 0xf1, 0x31, 0xc0, 0x00, 0x61, + 0x92, 0x49, 0x0d, 0xf1, 0x12, 0x48, 0x00, 0x89, + 0x29, 0xc0, 0x00, 0x19, 0x00, 0x89, 0x27, 0xc0, + 0x01, 0x89, 0x23, 0xc0, 0x0e, 0xe8, 0x12, 0x48, + 0x81, 0x1b, 0x15, 0xe8, 0xae, 0xc3, 0x66, 0x62, + 0xa0, 0x49, 0x04, 0xf0, 0x64, 0x71, 0xa3, 0xc0, + 0x02, 0x99, 0x02, 0xc0, 0x00, 0xb8, 0xd6, 0x07, + 0x13, 0xc4, 0x84, 0x98, 0x00, 0x1b, 0x86, 0x8b, + 0x86, 0x73, 0xbf, 0x49, 0xfe, 0xf1, 0x80, 0x71, + 0x82, 0x72, 0x80, 0xff, 0x09, 0xc4, 0x84, 0x98, + 0x80, 0x99, 0x82, 0x9a, 0x86, 0x8b, 0x86, 0x73, + 0xbf, 0x49, 0xfe, 0xf1, 0x80, 0xff, 0x08, 0xea, + 0x30, 0xd4, 0x10, 0xc0, 0x12, 0xe8, 0x8a, 0xd3, + 0x00, 0xd8, 0x02, 0xc6, 0x00, 0xbe, 0xe0, 0x08 }; + +static const u16 r8153_pla_patch_c_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x1306, 0xfc2a, 0x17ca, 0xfc2c, 0x171e, + 0xfc2e, 0x0000, 0xfc30, 0x0000, 0xfc32, 0x01b4, 0xfc34, 0x07d4, + 0xfc36, 0x0894, 0xfc38, 0x00e6 }; + +static const u16 r8153_ram_code_bc[] = { + 0xB436, 0xB820, 0xB438, 0x0290, 0xB436, 0xA012, 0xB438, 0x0000, + 0xB436, 0xA014, 0xB438, 0x2c04, 0xB438, 0x2c07, 0xB438, 0x2c0a, + 0xB438, 0x2c0d, 0xB438, 0xa240, 0xB438, 0xa104, 0xB438, 0x292d, + 0xB438, 0x8620, 0xB438, 0xa480, 0xB438, 0x2a2c, 0xB438, 0x8480, + 0xB438, 0xa101, 0xB438, 0x2a36, 0xB438, 0xd056, 0xB438, 0x2223, + 0xB436, 0xA01A, 0xB438, 0x0000, 0xB436, 0xA006, 0xB438, 0x0222, + 0xB436, 0xA004, 0xB438, 0x0a35, 0xB436, 0xA002, 0xB438, 0x0a2b, + 0xB436, 0xA000, 0xB438, 0xf92c, 0xB436, 0xB820, 0xB438, 0x0210 }; + +static const u8 r8153_usb_patch_b[] = { + 0x08, 0xe0, 0x0f, 0xe0, 0x18, 0xe0, 0x24, 0xe0, + 0x26, 0xe0, 0x3a, 0xe0, 0x84, 0xe0, 0x9c, 0xe0, + 0xc2, 0x49, 0x04, 0xf0, 0x02, 0xc0, 0x00, 0xb8, + 0x14, 0x18, 0x02, 0xc0, 0x00, 0xb8, 0x2e, 0x18, + 0x06, 0x89, 0x08, 0xc0, 0x0c, 0x61, 0x92, 0x48, + 0x93, 0x48, 0x0c, 0x89, 0x02, 0xc0, 0x00, 0xb8, + 0x08, 0x05, 0x40, 0xb4, 0x16, 0x89, 0x6d, 0xc0, + 0x00, 0x61, 0x95, 0x49, 0x06, 0xf0, 0xfa, 0xc0, + 0x0c, 0x61, 0x92, 0x48, 0x93, 0x48, 0x0c, 0x89, + 0x02, 0xc0, 0x00, 0xb8, 0xe2, 0x04, 0x02, 0xc2, + 0x00, 0xba, 0xec, 0x11, 0x60, 0x60, 0x85, 0x49, + 0x0d, 0xf1, 0x11, 0xc6, 0xd2, 0x61, 0x91, 0x49, + 0xfd, 0xf0, 0x74, 0x60, 0x04, 0x48, 0x74, 0x88, + 0x08, 0xc6, 0x08, 0xc0, 0xc4, 0x98, 0x01, 0x18, + 0xc0, 0x88, 0x02, 0xc0, 0x00, 0xb8, 0x6e, 0x12, + 0x04, 0xe4, 0x0d, 0x00, 0x00, 0xd4, 0xd1, 0x49, + 0x3c, 0xf1, 0xd2, 0x49, 0x16, 0xf1, 0xd3, 0x49, + 0x18, 0xf1, 0xd4, 0x49, 0x19, 0xf1, 0xd5, 0x49, + 0x1a, 0xf1, 0xd6, 0x49, 0x1b, 0xf1, 0xd7, 0x49, + 0x1c, 0xf1, 0xd8, 0x49, 0x1d, 0xf1, 0xd9, 0x49, + 0x20, 0xf1, 0xda, 0x49, 0x23, 0xf1, 0xdb, 0x49, + 0x24, 0xf1, 0x02, 0xc4, 0x00, 0xbc, 0x20, 0x04, + 0xe5, 0x8e, 0x02, 0xc4, 0x00, 0xbc, 0x14, 0x02, + 0x02, 0xc4, 0x00, 0xbc, 0x16, 0x02, 0x02, 0xc4, + 0x00, 0xbc, 0x18, 0x02, 0x02, 0xc4, 0x00, 0xbc, + 0x1a, 0x02, 0x02, 0xc4, 0x00, 0xbc, 0x1c, 0x02, + 0x02, 0xc4, 0x00, 0xbc, 0x94, 0x02, 0x10, 0xc7, + 0xe0, 0x8e, 0x02, 0xc4, 0x00, 0xbc, 0x8a, 0x02, + 0x0b, 0xc7, 0xe4, 0x8e, 0x02, 0xc4, 0x00, 0xbc, + 0x88, 0x02, 0x02, 0xc4, 0x00, 0xbc, 0x6e, 0x02, + 0x02, 0xc4, 0x00, 0xbc, 0x5a, 0x02, 0x30, 0xe4, + 0x0c, 0xc3, 0x60, 0x64, 0xc5, 0x49, 0x04, 0xf1, + 0x74, 0x64, 0xc4, 0x48, 0x74, 0x8c, 0x06, 0xc3, + 0x64, 0x8e, 0x02, 0xc4, 0x00, 0xbc, 0x20, 0x04, + 0x00, 0xd8, 0x00, 0xe4, 0xb2, 0xc0, 0x00, 0x61, + 0x90, 0x49, 0x09, 0xf1, 0x8b, 0xc6, 0xca, 0x61, + 0x94, 0x49, 0x0e, 0xf1, 0xf6, 0xc6, 0xda, 0x60, + 0x81, 0x49, 0x0a, 0xf0, 0x65, 0x60, 0x03, 0x48, + 0x65, 0x88, 0xef, 0xc6, 0xdc, 0x60, 0x80, 0x48, + 0xdc, 0x88, 0x05, 0xc6, 0x00, 0xbe, 0x02, 0xc6, + 0x00, 0xbe, 0x36, 0x13, 0x4c, 0x17, 0x99, 0xc4, + 0x80, 0x65, 0xd0, 0x49, 0x04, 0xf1, 0xfa, 0x75, + 0x04, 0xc4, 0x00, 0xbc, 0x03, 0xc4, 0x00, 0xbc, + 0x9a, 0x00, 0xee, 0x01 }; + +static const u16 r8153_usb_patch_b_bp[] = { + 0xfc26, 0xa000, 0xfc28, 0x180c, 0xfc2a, 0x0506, 0xfc2c, 0x04E0, + 0xfc2e, 0x11E4, 0xfc30, 0x125C, 0xfc32, 0x0232, 0xfc34, 0x131E, + 0xfc36, 0x0098, 0xfc38, 0x00FF }; + +static const u8 r8153_pla_patch_b[] = { + 0x08, 0xe0, 0xea, 0xe0, 0xf2, 0xe0, 0x04, 0xe1, + 0x09, 0xe1, 0x0e, 0xe1, 0x46, 0xe1, 0xf7, 0xe1, + 0x14, 0xc2, 0x40, 0x73, 0xba, 0x48, 0x40, 0x9b, + 0x11, 0xc2, 0x40, 0x73, 0xb0, 0x49, 0x17, 0xf0, + 0xbf, 0x49, 0x03, 0xf1, 0x09, 0xc5, 0x00, 0xbd, + 0xb1, 0x49, 0x11, 0xf0, 0xb1, 0x48, 0x40, 0x9b, + 0x02, 0xc2, 0x00, 0xba, 0x1a, 0x17, 0x00, 0xe0, + 0x1e, 0xfc, 0xbc, 0xc0, 0xf0, 0xc0, 0xde, 0xe8, + 0x00, 0x80, 0x00, 0x20, 0x2c, 0x75, 0xd4, 0x49, + 0x12, 0xf1, 0x32, 0xe0, 0xf8, 0xc2, 0x46, 0x71, + 0xf7, 0xc2, 0x40, 0x73, 0xbe, 0x49, 0x03, 0xf1, + 0xf5, 0xc7, 0x02, 0xe0, 0xf2, 0xc7, 0x4f, 0x30, + 0x26, 0x62, 0xa1, 0x49, 0xf0, 0xf1, 0x22, 0x72, + 0xa0, 0x49, 0xed, 0xf1, 0x25, 0x25, 0x18, 0x1f, + 0x97, 0x30, 0x91, 0x30, 0x36, 0x9a, 0x2c, 0x75, + 0x3c, 0xc3, 0x60, 0x73, 0xb1, 0x49, 0x0d, 0xf1, + 0xdc, 0x21, 0xbc, 0x25, 0x30, 0xc6, 0xc0, 0x77, + 0x04, 0x13, 0x21, 0xf0, 0x03, 0x13, 0x22, 0xf0, + 0x02, 0x13, 0x23, 0xf0, 0x01, 0x13, 0x24, 0xf0, + 0x08, 0x13, 0x08, 0xf1, 0x2e, 0x73, 0xba, 0x21, + 0xbd, 0x25, 0x05, 0x13, 0x03, 0xf1, 0x24, 0xc5, + 0x00, 0xbd, 0xd4, 0x49, 0x03, 0xf1, 0x1c, 0xc5, + 0x00, 0xbd, 0xc4, 0xc6, 0xc6, 0x67, 0x2e, 0x75, + 0xd7, 0x22, 0xdd, 0x26, 0x05, 0x15, 0x1b, 0xf0, + 0x14, 0xc6, 0x00, 0xbe, 0x13, 0xc5, 0x00, 0xbd, + 0x12, 0xc5, 0x00, 0xbd, 0xf1, 0x49, 0xfb, 0xf1, + 0xef, 0xe7, 0xf4, 0x49, 0xfa, 0xf1, 0xec, 0xe7, + 0xf3, 0x49, 0xf7, 0xf1, 0xe9, 0xe7, 0xf2, 0x49, + 0xf4, 0xf1, 0xe6, 0xe7, 0xb6, 0xc0, 0x9e, 0x12, + 0xde, 0x11, 0x0a, 0x12, 0x3c, 0x13, 0x00, 0xa0, + 0xa0, 0xd1, 0x00, 0x00, 0xc0, 0x75, 0xd0, 0x49, + 0x46, 0xf0, 0x26, 0x72, 0xa7, 0x49, 0x43, 0xf0, + 0x22, 0x72, 0x25, 0x25, 0x20, 0x1f, 0x97, 0x30, + 0x91, 0x30, 0x40, 0x73, 0xf3, 0xc4, 0x1c, 0x40, + 0x04, 0xf0, 0xd7, 0x49, 0x05, 0xf1, 0x37, 0xe0, + 0x53, 0x48, 0xc0, 0x9d, 0x08, 0x02, 0x40, 0x66, + 0x64, 0x27, 0x06, 0x16, 0x30, 0xf1, 0x46, 0x63, + 0x3b, 0x13, 0x2d, 0xf1, 0x34, 0x9b, 0x18, 0x1b, + 0x93, 0x30, 0x2b, 0xc3, 0x10, 0x1c, 0x2b, 0xe8, + 0x01, 0x14, 0x25, 0xf1, 0x00, 0x1d, 0x26, 0x1a, + 0x8a, 0x30, 0x22, 0x73, 0xb5, 0x25, 0x0e, 0x0b, + 0x00, 0x1c, 0x2c, 0xe8, 0x1f, 0xc7, 0x27, 0x40, + 0x1a, 0xf1, 0x38, 0xe8, 0x32, 0x1f, 0x8f, 0x30, + 0x08, 0x1b, 0x24, 0xe8, 0x36, 0x72, 0x46, 0x77, + 0x00, 0x17, 0x0d, 0xf0, 0x13, 0xc3, 0x1f, 0x40, + 0x03, 0xf1, 0x00, 0x1f, 0x46, 0x9f, 0x44, 0x77, + 0x9f, 0x44, 0x5f, 0x44, 0x17, 0xe8, 0x0a, 0xc7, + 0x27, 0x40, 0x05, 0xf1, 0x02, 0xc3, 0x00, 0xbb, + 0xfa, 0x18, 0xb0, 0x18, 0xff, 0xc7, 0x00, 0xbf, + 0xb8, 0xcd, 0xff, 0xff, 0x02, 0x0c, 0x54, 0xa5, + 0xdc, 0xa5, 0x2f, 0x40, 0x05, 0xf1, 0x00, 0x14, + 0xfa, 0xf1, 0x01, 0x1c, 0x02, 0xe0, 0x00, 0x1c, + 0x80, 0xff, 0xb0, 0x49, 0x04, 0xf0, 0x01, 0x0b, + 0xd3, 0xa1, 0x03, 0xe0, 0x02, 0x0b, 0xd3, 0xa5, + 0x27, 0x31, 0x20, 0x37, 0x02, 0x0b, 0xd3, 0xa5, + 0x27, 0x31, 0x20, 0x37, 0x00, 0x13, 0xfb, 0xf1, + 0x80, 0xff, 0x22, 0x73, 0xb5, 0x25, 0x18, 0x1e, + 0xde, 0x30, 0xd9, 0x30, 0x64, 0x72, 0x11, 0x1e, + 0x68, 0x23, 0x16, 0x31, 0x80, 0xff, 0x08, 0xc2, + 0x40, 0x73, 0x3a, 0x48, 0x40, 0x9b, 0x06, 0xff, + 0x02, 0xc6, 0x00, 0xbe, 0x08, 0x16, 0x1e, 0xfc, + 0x2c, 0x75, 0xdc, 0x21, 0xbc, 0x25, 0x04, 0x13, + 0x0b, 0xf0, 0x03, 0x13, 0x09, 0xf0, 0x02, 0x13, + 0x07, 0xf0, 0x01, 0x13, 0x05, 0xf0, 0x08, 0x13, + 0x03, 0xf0, 0x04, 0xc3, 0x00, 0xbb, 0x03, 0xc3, + 0x00, 0xbb, 0x8c, 0x15, 0x76, 0x15, 0xa0, 0x64, + 0x40, 0x48, 0xa0, 0x8c, 0x02, 0xc4, 0x00, 0xbc, + 0x82, 0x00, 0xa0, 0x62, 0x21, 0x48, 0xa0, 0x8a, + 0x02, 0xc2, 0x00, 0xba, 0x40, 0x03, 0x33, 0xc5, + 0xa0, 0x74, 0xc0, 0x49, 0x1f, 0xf0, 0x30, 0xc5, + 0xa0, 0x73, 0x00, 0x13, 0x04, 0xf1, 0xa2, 0x73, + 0x00, 0x13, 0x14, 0xf0, 0x28, 0xc5, 0xa0, 0x74, + 0xc8, 0x49, 0x1b, 0xf1, 0x26, 0xc5, 0xa0, 0x76, + 0xa2, 0x74, 0x01, 0x06, 0x20, 0x37, 0xa0, 0x9e, + 0xa2, 0x9c, 0x1e, 0xc5, 0xa2, 0x73, 0x23, 0x40, + 0x10, 0xf8, 0x04, 0xf3, 0xa0, 0x73, 0x33, 0x40, + 0x0c, 0xf8, 0x15, 0xc5, 0xa0, 0x74, 0x41, 0x48, + 0xa0, 0x9c, 0x14, 0xc5, 0xa0, 0x76, 0x62, 0x48, + 0xe0, 0x48, 0xa0, 0x9e, 0x10, 0xc6, 0x00, 0xbe, + 0x0a, 0xc5, 0xa0, 0x74, 0x48, 0x48, 0xa0, 0x9c, + 0x0b, 0xc5, 0x20, 0x1e, 0xa0, 0x9e, 0xe5, 0x48, + 0xa0, 0x9e, 0xf0, 0xe7, 0xbc, 0xc0, 0xc8, 0xd2, + 0xcc, 0xd2, 0x28, 0xe4, 0xe6, 0x01, 0xf0, 0xc0, + 0x18, 0x89, 0x00, 0x1d, 0x3c, 0xc3, 0x64, 0x71, + 0x3c, 0xc0, 0x02, 0x99, 0x00, 0x61, 0x67, 0x11, + 0x3c, 0xf1, 0x69, 0x33, 0x35, 0xc0, 0x28, 0x40, + 0xf6, 0xf1, 0x34, 0xc0, 0x00, 0x19, 0x81, 0x1b, + 0x91, 0xe8, 0x31, 0xc0, 0x04, 0x1a, 0x84, 0x1b, + 0x8d, 0xe8, 0x82, 0xe8, 0xa3, 0x49, 0xfe, 0xf0, + 0x2b, 0xc0, 0x7e, 0xe8, 0xa1, 0x48, 0x28, 0xc0, + 0x84, 0x1b, 0x84, 0xe8, 0x00, 0x1d, 0x69, 0x33, + 0x00, 0x1e, 0x01, 0x06, 0xff, 0x18, 0x30, 0x40, + 0xfd, 0xf1, 0x19, 0xc0, 0x00, 0x76, 0x2e, 0x40, + 0xf7, 0xf1, 0x21, 0x48, 0x19, 0xc0, 0x84, 0x1b, + 0x75, 0xe8, 0x10, 0xc0, 0x69, 0xe8, 0xa1, 0x49, + 0xfd, 0xf0, 0x11, 0xc0, 0x00, 0x1a, 0x84, 0x1b, + 0x6d, 0xe8, 0x62, 0xe8, 0xa5, 0x49, 0xfe, 0xf0, + 0x09, 0xc0, 0x01, 0x19, 0x81, 0x1b, 0x66, 0xe8, + 0x54, 0xe0, 0x10, 0xd4, 0x88, 0xd3, 0xb8, 0x0b, + 0x50, 0xe8, 0x20, 0xb4, 0x10, 0xd8, 0x84, 0xd4, + 0xfd, 0xc0, 0x52, 0xe8, 0x48, 0x33, 0xf9, 0xc0, + 0x00, 0x61, 0x9c, 0x20, 0x9c, 0x24, 0xd0, 0x49, + 0x04, 0xf0, 0x04, 0x11, 0x02, 0xf1, 0x03, 0xe0, + 0x00, 0x11, 0x06, 0xf1, 0x5c, 0xc0, 0x00, 0x61, + 0x92, 0x48, 0x00, 0x89, 0x3a, 0xe0, 0x06, 0x11, + 0x06, 0xf1, 0x55, 0xc0, 0x00, 0x61, 0x11, 0x48, + 0x00, 0x89, 0x33, 0xe0, 0x05, 0x11, 0x08, 0xf1, + 0x4e, 0xc0, 0x00, 0x61, 0x91, 0x49, 0x04, 0xf0, + 0x91, 0x48, 0x00, 0x89, 0x11, 0xe0, 0xd9, 0xc0, + 0x00, 0x61, 0x98, 0x20, 0x98, 0x24, 0x25, 0x11, + 0x24, 0xf1, 0x44, 0xc0, 0x29, 0xe8, 0x95, 0x49, + 0x20, 0xf0, 0xcf, 0xc0, 0x00, 0x61, 0x98, 0x20, + 0x98, 0x24, 0x25, 0x11, 0x1a, 0xf1, 0x37, 0xc0, + 0x00, 0x61, 0x92, 0x49, 0x16, 0xf1, 0x12, 0x48, + 0x00, 0x89, 0x2f, 0xc0, 0x00, 0x19, 0x00, 0x89, + 0x2d, 0xc0, 0x01, 0x89, 0x2d, 0xc0, 0x04, 0x19, + 0x81, 0x1b, 0x1c, 0xe8, 0x2a, 0xc0, 0x14, 0x19, + 0x81, 0x1b, 0x18, 0xe8, 0x21, 0xc0, 0x0c, 0xe8, + 0x1f, 0xc0, 0x12, 0x48, 0x81, 0x1b, 0x12, 0xe8, + 0xae, 0xc3, 0x66, 0x71, 0xae, 0xc0, 0x02, 0x99, + 0x02, 0xc0, 0x00, 0xb8, 0x96, 0x07, 0x13, 0xc4, + 0x84, 0x98, 0x00, 0x1b, 0x86, 0x8b, 0x86, 0x73, + 0xbf, 0x49, 0xfe, 0xf1, 0x80, 0x71, 0x82, 0x72, + 0x80, 0xff, 0x09, 0xc4, 0x84, 0x98, 0x80, 0x99, + 0x82, 0x9a, 0x86, 0x8b, 0x86, 0x73, 0xbf, 0x49, + 0xfe, 0xf1, 0x80, 0xff, 0x08, 0xea, 0x30, 0xd4, + 0x10, 0xc0, 0x12, 0xe8, 0x8a, 0xd3, 0x28, 0xe4, + 0x2c, 0xe4, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00 }; + +static const u16 r8153_pla_patch_b_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x1154, 0xfc2a, 0x1606, 0xfc2c, 0x155a, + 0xfc2e, 0x0080, 0xfc30, 0x033c, 0xfc32, 0x01a0, 0xfc34, 0x0794, + 0xfc36, 0x0000, 0xfc38, 0x007f }; + +static const u16 r8153_ram_code_d[] = { + 0xB436, 0xB820, 0xB438, 0x0290, 0xB436, 0xA012, 0xB438, 0x0000, + 0xB436, 0xA014, 0xB438, 0x2c04, 0xB438, 0x2c07, 0xB438, 0x2c07, + 0xB438, 0x2c07, 0xB438, 0xa240, 0xB438, 0xa104, 0xB438, 0x2944, + 0xB436, 0xA01A, 0xB438, 0x0000, 0xB436, 0xA006, 0xB438, 0x0fff, + 0xB436, 0xA004, 0xB438, 0x0fff, 0xB436, 0xA002, 0xB438, 0x0fff, + 0xB436, 0xA000, 0xB438, 0x1943, 0xB436, 0xB820, 0xB438, 0x0210 }; + +static const u8 usb_patch_d[] = { + 0x08, 0xe0, 0x0e, 0xe0, 0x11, 0xe0, 0x24, 0xe0, + 0x2b, 0xe0, 0x33, 0xe0, 0x3a, 0xe0, 0x3c, 0xe0, + 0x1e, 0xc3, 0x70, 0x61, 0x12, 0x48, 0x70, 0x89, + 0x02, 0xc3, 0x00, 0xbb, 0x02, 0x17, 0x32, 0x19, + 0x02, 0xc3, 0x00, 0xbb, 0x44, 0x14, 0x30, 0x18, + 0x11, 0xc1, 0x05, 0xe8, 0x10, 0xc6, 0x02, 0xc2, + 0x00, 0xba, 0x94, 0x17, 0x02, 0xb4, 0x09, 0xc2, + 0x40, 0x99, 0x0e, 0x48, 0x42, 0x98, 0x42, 0x70, + 0x8e, 0x49, 0xfe, 0xf1, 0x02, 0xb0, 0x80, 0xff, + 0xc0, 0xd4, 0xe4, 0x40, 0x20, 0xd4, 0x30, 0x18, + 0x06, 0xc1, 0xf1, 0xef, 0xfc, 0xc7, 0x02, 0xc0, + 0x00, 0xb8, 0x38, 0x12, 0xe4, 0x4b, 0x0c, 0x61, + 0x92, 0x48, 0x93, 0x48, 0x95, 0x48, 0x96, 0x48, + 0x0c, 0x89, 0x02, 0xc0, 0x00, 0xb8, 0x0e, 0x06, + 0x30, 0x18, 0xf5, 0xc1, 0xe0, 0xef, 0x04, 0xc5, + 0x02, 0xc4, 0x00, 0xbc, 0x76, 0x3c, 0x1e, 0xfc, + 0x02, 0xc6, 0x00, 0xbe, 0x00, 0x00, 0x02, 0xc6, + 0x00, 0xbe, 0x00, 0x00 }; + +static const u16 r8153_usb_patch_d_bp[] = { + 0xfc26, 0xa000, 0xfc28, 0x16de, 0xfc2a, 0x1442, 0xfc2c, 0x1792, + 0xfc2e, 0x1236, 0xfc30, 0x0606, 0xfc32, 0x3c74, 0xfc34, 0x0000, + 0xfc36, 0x0000, 0xfc38, 0x003e }; + +static const u8 pla_patch_d[] = { + 0x03, 0xe0, 0x16, 0xe0, 0x30, 0xe0, 0x12, 0xc2, + 0x40, 0x73, 0xb0, 0x49, 0x08, 0xf0, 0xb8, 0x49, + 0x06, 0xf0, 0xb8, 0x48, 0x40, 0x9b, 0x0b, 0xc2, + 0x40, 0x76, 0x05, 0xe0, 0x02, 0x61, 0x02, 0xc3, + 0x00, 0xbb, 0x54, 0x08, 0x02, 0xc3, 0x00, 0xbb, + 0x64, 0x08, 0x98, 0xd3, 0x1e, 0xfc, 0xfe, 0xc0, + 0x02, 0x62, 0xa0, 0x48, 0x02, 0x8a, 0x00, 0x72, + 0xa0, 0x49, 0x11, 0xf0, 0x13, 0xc1, 0x20, 0x62, + 0x2e, 0x21, 0x2f, 0x25, 0x00, 0x71, 0x9f, 0x24, + 0x0a, 0x40, 0x09, 0xf0, 0x00, 0x71, 0x18, 0x48, + 0xa0, 0x49, 0x03, 0xf1, 0x9f, 0x48, 0x02, 0xe0, + 0x1f, 0x48, 0x00, 0x99, 0x02, 0xc2, 0x00, 0xba, + 0xac, 0x0c, 0x08, 0xe9, 0x36, 0xc0, 0x00, 0x61, + 0x9c, 0x20, 0x9c, 0x24, 0x33, 0xc0, 0x07, 0x11, + 0x05, 0xf1, 0x00, 0x61, 0x17, 0x48, 0x00, 0x89, + 0x0d, 0xe0, 0x04, 0x11, 0x0b, 0xf1, 0x00, 0x61, + 0x97, 0x49, 0x08, 0xf0, 0x97, 0x48, 0x00, 0x89, + 0x23, 0xc0, 0x0e, 0xe8, 0x12, 0x48, 0x81, 0x1b, + 0x15, 0xe8, 0x1f, 0xc0, 0x00, 0x61, 0x67, 0x11, + 0x04, 0xf0, 0x02, 0xc0, 0x00, 0xb8, 0x42, 0x09, + 0x02, 0xc0, 0x00, 0xb8, 0x90, 0x08, 0x13, 0xc4, + 0x84, 0x98, 0x00, 0x1b, 0x86, 0x8b, 0x86, 0x73, + 0xbf, 0x49, 0xfe, 0xf1, 0x80, 0x71, 0x82, 0x72, + 0x80, 0xff, 0x09, 0xc4, 0x84, 0x98, 0x80, 0x99, + 0x82, 0x9a, 0x86, 0x8b, 0x86, 0x73, 0xbf, 0x49, + 0xfe, 0xf1, 0x80, 0xff, 0x08, 0xea, 0x30, 0xd4, + 0x50, 0xe8, 0x8a, 0xd3 }; + +static const u16 r8153_pla_patch_d_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x0852, 0xfc2a, 0x0c92, 0xfc2c, 0x088c, + 0xfc2e, 0x0000, 0xfc30, 0x0000, 0xfc32, 0x0000, 0xfc34, 0x0000, + 0xfc36, 0x0000, 0xfc38, 0x0007 }; + +static const u8 usb_patch2_b[] = { + 0x10, 0xe0, 0x26, 0xe0, 0x3a, 0xe0, 0x58, 0xe0, + 0x6c, 0xe0, 0x85, 0xe0, 0xa5, 0xe0, 0xbe, 0xe0, + 0xd8, 0xe0, 0xdb, 0xe0, 0xf3, 0xe0, 0xf5, 0xe0, + 0xf7, 0xe0, 0xf9, 0xe0, 0xfb, 0xe0, 0xfd, 0xe0, + 0x16, 0xc0, 0x00, 0x75, 0xd1, 0x49, 0x0d, 0xf0, + 0x0f, 0xc0, 0x0f, 0xc5, 0x00, 0x1e, 0x08, 0x9e, + 0x0c, 0x9d, 0x0c, 0xc6, 0x0a, 0x9e, 0x8f, 0x1c, + 0x0e, 0x8c, 0x0e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, + 0x02, 0xc0, 0x00, 0xb8, 0x96, 0x31, 0x00, 0xdc, + 0x24, 0xe4, 0x80, 0x02, 0x34, 0xd3, 0xff, 0xc3, + 0x60, 0x72, 0xa1, 0x49, 0x0d, 0xf0, 0xf8, 0xc3, + 0xf8, 0xc2, 0x00, 0x1c, 0x68, 0x9c, 0xf6, 0xc4, + 0x6a, 0x9c, 0x6c, 0x9a, 0x8f, 0x1c, 0x6e, 0x8c, + 0x6e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, 0x04, 0xc0, + 0x02, 0xc2, 0x00, 0xba, 0xa8, 0x28, 0xf8, 0xc7, + 0xea, 0xc0, 0x00, 0x75, 0xd1, 0x49, 0x15, 0xf0, + 0x19, 0xc7, 0x17, 0xc2, 0xec, 0x9a, 0x00, 0x19, + 0xee, 0x89, 0xee, 0x71, 0x9f, 0x49, 0xfe, 0xf1, + 0xea, 0x71, 0x9f, 0x49, 0x0a, 0xf0, 0xd9, 0xc2, + 0xec, 0x9a, 0x00, 0x19, 0xe8, 0x99, 0x81, 0x19, + 0xee, 0x89, 0xee, 0x71, 0x9f, 0x49, 0xfe, 0xf1, + 0x06, 0xc3, 0x02, 0xc2, 0x00, 0xba, 0xf0, 0x1d, + 0x4c, 0xe8, 0x00, 0xdc, 0x00, 0xd4, 0xcb, 0xc0, + 0x00, 0x75, 0xd1, 0x49, 0x0d, 0xf0, 0xc4, 0xc0, + 0xc4, 0xc5, 0x00, 0x1e, 0x08, 0x9e, 0xc2, 0xc6, + 0x0a, 0x9e, 0x0c, 0x9d, 0x8f, 0x1c, 0x0e, 0x8c, + 0x0e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, 0x04, 0xc0, + 0x02, 0xc1, 0x00, 0xb9, 0xc4, 0x16, 0x20, 0xd4, + 0xb6, 0xc0, 0x00, 0x75, 0xd1, 0x48, 0x00, 0x9d, + 0xe5, 0xc7, 0xaf, 0xc2, 0xec, 0x9a, 0x00, 0x19, + 0xe8, 0x9a, 0x81, 0x19, 0xee, 0x89, 0xee, 0x71, + 0x9f, 0x49, 0xfe, 0xf1, 0x2c, 0xc1, 0xec, 0x99, + 0x81, 0x19, 0xee, 0x89, 0xee, 0x71, 0x9f, 0x49, + 0xfe, 0xf1, 0x04, 0xc3, 0x02, 0xc2, 0x00, 0xba, + 0x96, 0x1c, 0xc0, 0xd4, 0xc0, 0x88, 0x1e, 0xc6, + 0xc0, 0x70, 0x8f, 0x49, 0x0e, 0xf0, 0x8f, 0x48, + 0x93, 0xc6, 0xca, 0x98, 0x11, 0x18, 0xc8, 0x98, + 0x16, 0xc0, 0xcc, 0x98, 0x8f, 0x18, 0xce, 0x88, + 0xce, 0x70, 0x8f, 0x49, 0xfe, 0xf1, 0x0b, 0xe0, + 0x43, 0xc6, 0x00, 0x18, 0xc8, 0x98, 0x0b, 0xc0, + 0xcc, 0x98, 0x81, 0x18, 0xce, 0x88, 0xce, 0x70, + 0x8f, 0x49, 0xfe, 0xf1, 0x02, 0xc0, 0x00, 0xb8, + 0xf2, 0x19, 0x40, 0xd3, 0x20, 0xe4, 0x33, 0xc2, + 0x40, 0x71, 0x91, 0x48, 0x40, 0x99, 0x30, 0xc2, + 0x00, 0x19, 0x48, 0x99, 0xf8, 0xc1, 0x4c, 0x99, + 0x81, 0x19, 0x4e, 0x89, 0x4e, 0x71, 0x9f, 0x49, + 0xfe, 0xf1, 0x0b, 0xc1, 0x4c, 0x99, 0x81, 0x19, + 0x4e, 0x89, 0x4e, 0x71, 0x9f, 0x49, 0xfe, 0xf1, + 0x02, 0x71, 0x02, 0xc2, 0x00, 0xba, 0x0e, 0x34, + 0x24, 0xe4, 0x19, 0xc2, 0x40, 0x71, 0x91, 0x48, + 0x40, 0x99, 0x16, 0xc2, 0x00, 0x19, 0x48, 0x99, + 0xde, 0xc1, 0x4c, 0x99, 0x81, 0x19, 0x4e, 0x89, + 0x4e, 0x71, 0x9f, 0x49, 0xfe, 0xf1, 0xf1, 0xc1, + 0x4c, 0x99, 0x81, 0x19, 0x4e, 0x89, 0x4e, 0x71, + 0x9f, 0x49, 0xfe, 0xf1, 0x02, 0x71, 0x02, 0xc2, + 0x00, 0xba, 0x60, 0x33, 0x34, 0xd3, 0x00, 0xdc, + 0x1e, 0x89, 0x02, 0xc0, 0x00, 0xb8, 0xfa, 0x12, + 0x18, 0xc0, 0x00, 0x65, 0xd1, 0x49, 0x0e, 0xf0, + 0x11, 0xc0, 0x11, 0xc5, 0x00, 0x1e, 0x08, 0x9e, + 0x0c, 0x9d, 0x0e, 0xc6, 0x0a, 0x9e, 0x8f, 0x1c, + 0x0e, 0x8c, 0x0e, 0x74, 0xcf, 0x49, 0xfe, 0xf1, + 0x04, 0xc0, 0x02, 0xc2, 0x00, 0xba, 0xa0, 0x41, + 0x06, 0xd4, 0x00, 0xdc, 0x24, 0xe4, 0x80, 0x02, + 0x34, 0xd3, 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, + 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, 0x02, 0xc0, + 0x00, 0xb8, 0x00, 0x00, 0x02, 0xc0, 0x00, 0xb8, + 0x00, 0x00, 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, + 0x02, 0xc0, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00 }; + +static const u16 r8153b_usb_patch_b_bp[] = { + 0xfc26, 0xa000, 0xfc28, 0x2a20, 0xfc2a, 0x28a6, 0xfc2c, 0x1dee, + 0xfc2e, 0x16c2, 0xfc30, 0x1c94, 0xfc32, 0x19f0, 0xfc34, 0x340c, + 0xfc36, 0x335e, 0xfc38, 0x12f8, 0xfc3a, 0x419e, 0xfc3c, 0x0000, + 0xfc3e, 0x0000, 0xfc40, 0x0000, 0xfc42, 0x0000, 0xfc44, 0x0000, + 0xfc46, 0x0000, 0xfc48, 0x03ff }; + +static const u8 pla_patch2_b[] = { + 0x05, 0xe0, 0x1b, 0xe0, 0x2c, 0xe0, 0x60, 0xe0, + 0x73, 0xe0, 0x15, 0xc6, 0xc2, 0x64, 0xd2, 0x49, + 0x06, 0xf1, 0xc4, 0x48, 0xc5, 0x48, 0xc6, 0x48, + 0xc7, 0x48, 0x05, 0xe0, 0x44, 0x48, 0x45, 0x48, + 0x46, 0x48, 0x47, 0x48, 0xc2, 0x8c, 0xc0, 0x64, + 0x46, 0x48, 0xc0, 0x8c, 0x05, 0xc5, 0x02, 0xc4, + 0x00, 0xbc, 0x18, 0x02, 0x06, 0xdc, 0xb0, 0xc0, + 0x10, 0xc5, 0xa0, 0x77, 0xa0, 0x74, 0x46, 0x48, + 0x47, 0x48, 0xa0, 0x9c, 0x0b, 0xc5, 0xa0, 0x74, + 0x44, 0x48, 0x43, 0x48, 0xa0, 0x9c, 0x05, 0xc5, + 0xa0, 0x9f, 0x02, 0xc5, 0x00, 0xbd, 0x3c, 0x03, + 0x1c, 0xe8, 0x20, 0xe8, 0xd4, 0x49, 0x04, 0xf1, + 0xd5, 0x49, 0x20, 0xf1, 0x28, 0xe0, 0x2a, 0xc7, + 0xe0, 0x75, 0xda, 0x49, 0x14, 0xf0, 0x27, 0xc7, + 0xe0, 0x75, 0xdc, 0x49, 0x10, 0xf1, 0x24, 0xc7, + 0xe0, 0x75, 0x25, 0xc7, 0xe0, 0x74, 0x2c, 0x40, + 0x0a, 0xfa, 0x1f, 0xc7, 0xe4, 0x75, 0xd0, 0x49, + 0x09, 0xf1, 0x1c, 0xc5, 0xe6, 0x9d, 0x11, 0x1d, + 0xe4, 0x8d, 0x04, 0xe0, 0x16, 0xc7, 0x00, 0x1d, + 0xe4, 0x8d, 0xe0, 0x8e, 0x11, 0x1d, 0xe0, 0x8d, + 0x07, 0xe0, 0x0c, 0xc7, 0xe0, 0x75, 0xda, 0x48, + 0xe0, 0x9d, 0x0b, 0xc7, 0xe4, 0x8e, 0x02, 0xc4, + 0x00, 0xbc, 0x28, 0x03, 0x02, 0xc4, 0x00, 0xbc, + 0x14, 0x03, 0x12, 0xe8, 0x4e, 0xe8, 0x1c, 0xe6, + 0x20, 0xe4, 0x80, 0x02, 0xa4, 0xc0, 0x12, 0xc2, + 0x40, 0x73, 0xb0, 0x49, 0x08, 0xf0, 0xb8, 0x49, + 0x06, 0xf0, 0xb8, 0x48, 0x40, 0x9b, 0x0b, 0xc2, + 0x40, 0x76, 0x05, 0xe0, 0x02, 0x61, 0x02, 0xc3, + 0x00, 0xbb, 0x0a, 0x0a, 0x02, 0xc3, 0x00, 0xbb, + 0x1a, 0x0a, 0x98, 0xd3, 0x1e, 0xfc, 0xfe, 0xc0, + 0x02, 0x62, 0xa0, 0x48, 0x02, 0x8a, 0x00, 0x72, + 0xa0, 0x49, 0x11, 0xf0, 0x13, 0xc1, 0x20, 0x62, + 0x2e, 0x21, 0x2f, 0x25, 0x00, 0x71, 0x9f, 0x24, + 0x0a, 0x40, 0x09, 0xf0, 0x00, 0x71, 0x18, 0x48, + 0xa0, 0x49, 0x03, 0xf1, 0x9f, 0x48, 0x02, 0xe0, + 0x1f, 0x48, 0x00, 0x99, 0x02, 0xc2, 0x00, 0xba, + 0xda, 0x0e, 0x08, 0xe9 }; + +static const u16 r8153b_pla_patch_b_bp[] = { + 0xfc26, 0x8000, 0xfc28, 0x0216, 0xfc2a, 0x0332, 0xfc2c, 0x030c, + 0xfc2e, 0x0a08, 0xfc30, 0x0ec0, 0xfc32, 0x0000, 0xfc34, 0x0000, + 0xfc36, 0x0000, 0xfc38, 0x001e }; + +static void rtl_clear_bp(struct r8152 *tp, u16 type) +{ + u8 zeros[16] = {0}; + + switch (tp->version) { + case RTL_VER_01: + case RTL_VER_02: + case RTL_VER_07: + break; + case RTL_VER_03: + case RTL_VER_04: + case RTL_VER_05: + case RTL_VER_06: + r8152_ocp_write_byte(tp, type, PLA_BP_EN, 0); + break; + case RTL_VER_08: + case RTL_VER_09: + default: + if (type == MCU_TYPE_USB) { + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_BP2_EN, 0); + + r8152_generic_ocp_write(tp, USB_BP(8), 0xff, + sizeof(zeros), zeros, type); + } else { + r8152_ocp_write_byte(tp, MCU_TYPE_PLA, PLA_BP_EN, 0); + } + break; + } + + r8152_generic_ocp_write(tp, USB_BP(0), 0xff, sizeof(zeros), zeros, + type); + + mdelay(6); + + r8152_ocp_write_word(tp, type, PLA_BP_BA, 0); +} + +static void r8152b_set_dq_desc(struct r8152 *tp) +{ + u8 data; + + data = r8152_ocp_read_byte(tp, MCU_TYPE_USB, 0xd429); + data |= 0x80; + r8152_ocp_write_byte(tp, MCU_TYPE_USB, 0xd429, data); + r8152_ocp_write_word(tp, MCU_TYPE_USB, 0xc0ce, 0x0210); + data = r8152_ocp_read_byte(tp, MCU_TYPE_USB, 0xd429); + data &= ~0x80; + r8152_ocp_write_byte(tp, MCU_TYPE_USB, 0xd429, data); +} + +static void r8153_pre_ram_code(struct r8152 *tp, u16 patch_key) +{ + u64 start; + u16 data; + + data = r8152_ocp_reg_read(tp, 0xb820); + data |= 0x0010; + r8152_ocp_reg_write(tp, 0xb820, data); + + start = get_time_ns(); + do { + mdelay(2); + data = r8152_ocp_reg_read(tp, 0xb800) & 0x0040; + if (is_timeout(start, 10 * SECOND)) { + dev_dbg(&tp->dev->edev.dev, "pre_ram_code timeout!\n"); + break; + } + } while (!data); + + r8152_sram_write(tp, 0x8146, patch_key); + r8152_sram_write(tp, 0xb82e, 0x0001); +} + +static int r8153_post_ram_code(struct r8152 *tp) +{ + u16 data; + + r8152_sram_write(tp, 0x0000, 0x0000); + + data = r8152_ocp_reg_read(tp, 0xb82e); + data &= ~0x0001; + r8152_ocp_reg_write(tp, 0xb82e, data); + + r8152_sram_write(tp, 0x8146, 0x0000); + + data = r8152_ocp_reg_read(tp, 0xb820); + data &= ~0x0010; + r8152_ocp_reg_write(tp, 0xb820, data); + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, tp->ocp_base); + + return 0; +} + +static void r8153_wdt1_end(struct r8152 *tp) +{ + u64 start; + + start = get_time_ns(); + do { + if (!(r8152_ocp_read_byte(tp, MCU_TYPE_USB, 0xe404) & 1)) + break; + mdelay(2); + } while (!is_timeout(start, 208 * MSECOND)); +} + +void r8152b_firmware(struct r8152 *tp) +{ + int i; + + if (tp->version == RTL_VER_01) { + int i; + + r8152b_set_dq_desc(tp); + rtl_clear_bp(tp, MCU_TYPE_PLA); + + r8152_generic_ocp_write(tp, 0xf800, 0x3f, + sizeof(r8152b_pla_patch_a), + r8152b_pla_patch_a, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8152b_pla_patch_a_bp); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_PLA, + r8152b_pla_patch_a_bp[i], + r8152b_pla_patch_a_bp[i + 1]); + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_OCP_GPHY_BASE, + 0x2000); + r8152_ocp_write_word(tp, MCU_TYPE_PLA, 0xb092, 0x7070); + r8152_ocp_write_word(tp, MCU_TYPE_PLA, 0xb098, 0x0600); + for (i = 0; i < ARRAY_SIZE(r8152b_ram_code1); i++) + r8152_ocp_write_word(tp, MCU_TYPE_PLA, 0xb09a, + r8152b_ram_code1[i]); + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, 0xb098, 0x0200); + r8152_ocp_write_word(tp, MCU_TYPE_PLA, 0xb092, 0x7030); + } else if (tp->version == RTL_VER_02) { + rtl_clear_bp(tp, MCU_TYPE_PLA); + + r8152_generic_ocp_write(tp, 0xf800, 0xff, + sizeof(r8152b_pla_patch_a2), + r8152b_pla_patch_a2, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8152b_pla_patch_a2_bp); + i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_PLA, + r8152b_pla_patch_a2_bp[i], + r8152b_pla_patch_a2_bp[i + 1]); + } +} + +void r8153_firmware(struct r8152 *tp) +{ + int i; + + if (tp->version == RTL_VER_03) { + r8153_pre_ram_code(tp, 0x7000); + + for (i = 0; i < ARRAY_SIZE(r8153_ram_code_a); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_PLA, + r8153_ram_code_a[i], + r8153_ram_code_a[i + 1]); + + r8153_post_ram_code(tp); + } else if (tp->version == RTL_VER_04) { + r8153_pre_ram_code(tp, 0x7001); + + for (i = 0; i < ARRAY_SIZE(r8153_ram_code_bc); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_PLA, + r8153_ram_code_bc[i], + r8153_ram_code_bc[i + 1]); + + r8153_post_ram_code(tp); + + r8153_wdt1_end(tp); + + rtl_clear_bp(tp, MCU_TYPE_USB); + + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); + r8152_generic_ocp_write(tp, 0xf800, 0xff, + sizeof(r8153_usb_patch_b), + r8153_usb_patch_b, MCU_TYPE_USB); + + for (i = 0; i < ARRAY_SIZE(r8153_usb_patch_b_bp); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_USB, + r8153_usb_patch_b_bp[i], + r8153_usb_patch_b_bp[i + 1]); + + if (!(r8152_ocp_read_word(tp, MCU_TYPE_PLA, 0xd38e) & BIT(0))) { + r8152_ocp_write_word(tp, MCU_TYPE_PLA, 0xd38c, 0x0082); + r8152_ocp_write_word(tp, MCU_TYPE_PLA, 0xd38e, 0x0082); + } + + rtl_clear_bp(tp, MCU_TYPE_PLA); + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); + r8152_generic_ocp_write(tp, 0xf800, 0xff, + sizeof(r8153_pla_patch_b), + r8153_pla_patch_b, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8153_pla_patch_b_bp); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_PLA, + r8153_pla_patch_b_bp[i], + r8153_pla_patch_b_bp[i + 1]); + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, 0xd388, 0x08ca); + } else if (tp->version == RTL_VER_05) { + u32 ocp_data; + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, 0xcfca); + ocp_data &= ~0x4000; + r8152_ocp_write_word(tp, MCU_TYPE_USB, 0xcfca, ocp_data); + + r8153_pre_ram_code(tp, 0x7001); + + for (i = 0; i < ARRAY_SIZE(r8153_ram_code_bc); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_PLA, + r8153_ram_code_bc[i], + r8153_ram_code_bc[i + 1]); + + r8153_post_ram_code(tp); + + r8153_wdt1_end(tp); + + rtl_clear_bp(tp, MCU_TYPE_USB); + + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); + r8152_generic_ocp_write(tp, 0xf800, 0xff, + sizeof(r8153_usb_patch_c), + r8153_usb_patch_c, MCU_TYPE_USB); + + for (i = 0; i < ARRAY_SIZE(r8153_usb_patch_c_bp); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_USB, + r8153_usb_patch_c_bp[i], + r8153_usb_patch_c_bp[i + 1]); + + if (r8152_ocp_read_byte(tp, MCU_TYPE_USB, 0xcfef) & 1) { + r8152_ocp_write_word(tp, MCU_TYPE_USB, 0xfc30, 0x1578); + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, + 0x00ff); + } else { + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, + 0x00ef); + } + + rtl_clear_bp(tp, MCU_TYPE_PLA); + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); + r8152_generic_ocp_write(tp, 0xf800, 0xff, + sizeof(r8153_pla_patch_c), + r8153_pla_patch_c, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8153_pla_patch_c_bp); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_PLA, + r8153_pla_patch_c_bp[i], + r8153_pla_patch_c_bp[i + 1]); + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, 0xd388, 0x08ca); + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_EXTRA_STATUS, + U3P3_CHECK_EN | 4); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, 0xcfca); + ocp_data |= 0x4000; + r8152_ocp_write_word(tp, MCU_TYPE_USB, 0xcfca, ocp_data); + + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); + ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); + } else if (tp->version == RTL_VER_06) { + u32 ocp_data; + + r8153_pre_ram_code(tp, 0x7002); + + for (i = 0; i < ARRAY_SIZE(r8153_ram_code_d); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_PLA, + r8153_ram_code_d[i], + r8153_ram_code_d[i + 1]); + + r8153_post_ram_code(tp); + + rtl_clear_bp(tp, MCU_TYPE_USB); + + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); + r8152_generic_ocp_write(tp, 0xf800, 0xff, sizeof(usb_patch_d), + usb_patch_d, MCU_TYPE_USB); + + for (i = 0; i < ARRAY_SIZE(r8153_usb_patch_d_bp); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_USB, + r8153_usb_patch_d_bp[i], + r8153_usb_patch_d_bp[i + 1]); + + rtl_clear_bp(tp, MCU_TYPE_PLA); + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); + r8152_generic_ocp_write(tp, 0xf800, 0xff, sizeof(pla_patch_d), + pla_patch_d, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8153_pla_patch_d_bp); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_PLA, + r8153_pla_patch_d_bp[i], + r8153_pla_patch_d_bp[i + 1]); + + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); + ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, + USB_FW_FIX_EN1); + ocp_data |= FW_IP_RESET_EN; + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, + ocp_data); + } +} + +void r8153b_firmware(struct r8152 *tp) +{ + u32 ocp_data; + int i; + + if (tp->version != RTL_VER_09) + return; + + rtl_clear_bp(tp, MCU_TYPE_USB); + + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_BP_EN, 0x0000); + r8152_generic_ocp_write(tp, 0xe600, 0xff, sizeof(usb_patch2_b), + usb_patch2_b, MCU_TYPE_USB); + + for (i = 0; i < ARRAY_SIZE(r8153b_usb_patch_b_bp); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_USB, + r8153b_usb_patch_b_bp[i], + r8153b_usb_patch_b_bp[i + 1]); + + rtl_clear_bp(tp, MCU_TYPE_PLA); + + r8152_ocp_write_word(tp, MCU_TYPE_PLA, PLA_BP_EN, 0x0000); + r8152_generic_ocp_write(tp, 0xf800, 0xff, sizeof(pla_patch2_b), + pla_patch2_b, MCU_TYPE_PLA); + + for (i = 0; i < ARRAY_SIZE(r8153b_pla_patch_b_bp); i += 2) + r8152_ocp_write_word(tp, MCU_TYPE_PLA, + r8153b_pla_patch_b_bp[i], + r8153b_pla_patch_b_bp[i + 1]); + + ocp_data = r8152_ocp_read_byte(tp, MCU_TYPE_USB, USB_USB2PHY); + ocp_data |= USB2PHY_L1 | USB2PHY_SUSPEND; + r8152_ocp_write_byte(tp, MCU_TYPE_USB, USB_USB2PHY, ocp_data); + + ocp_data = r8152_ocp_read_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1); + ocp_data |= FW_IP_RESET_EN; + r8152_ocp_write_word(tp, MCU_TYPE_USB, USB_FW_FIX_EN1, ocp_data); +} diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 3c5bd1e4ee..b6f81cfab8 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -5,8 +5,8 @@ #include <command.h> #include <init.h> #include <net.h> -#include <usb/usb.h> -#include <usb/usbnet.h> +#include <linux/usb/usb.h> +#include <linux/usb/usbnet.h> #include <malloc.h> #include <asm/byteorder.h> #include <errno.h> diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 60e67ff1a2..3c3da3171b 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1,10 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <common.h> -#include <usb/usb.h> -#include <usb/usbnet.h> +#include <linux/usb/usb.h> +#include <linux/usb/usbnet.h> #include <asm/byteorder.h> #include <errno.h> #include <malloc.h> #include <linux/phy.h> +#include <dma.h> /* handles CDC Ethernet and many other network "bulk data" interfaces */ int usbnet_get_endpoints(struct usbnet *dev) @@ -77,8 +79,6 @@ int usbnet_get_endpoints(struct usbnet *dev) } EXPORT_SYMBOL(usbnet_get_endpoints); -char tx_buffer[4096]; - static int usbnet_send(struct eth_device *edev, void *eth_data, int data_length) { struct usbnet *dev = edev->priv; @@ -91,30 +91,30 @@ static int usbnet_send(struct eth_device *edev, void *eth_data, int data_length) * win32 driver (usually) and/or hardware quirks */ if(info->tx_fixup) { - if(info->tx_fixup(dev, eth_data, data_length, tx_buffer, &len)) { + if(info->tx_fixup(dev, eth_data, data_length, dev->tx_buf, &len)) { dev_dbg(&edev->dev, "can't tx_fixup packet"); return 0; } } else { len = data_length; - memmove(tx_buffer, (void*) eth_data, len); + memmove(dev->tx_buf, (void*) eth_data, len); } /* don't assume the hardware handles USB_ZERO_PACKET * NOTE: strictly conforming cdc-ether devices should expect * the ZLP here, but ignore the one-byte packet. */ - if ((len % dev->maxpacket) == 0) - tx_buffer[len++] = 0; + if ((len % dev->maxpacket) == 0) { + *(unsigned char *)(dev->tx_buf + len) = 0; + len++; + } - ret = usb_bulk_msg(dev->udev, dev->out, tx_buffer, len, &alen, 1000); + ret = usb_bulk_msg(dev->udev, dev->out, dev->tx_buf, len, &alen, 1000); dev_dbg(&edev->dev, "%s: ret: %d len: %d alen: %d\n", __func__, ret, len, alen); return ret; } -static char rx_buf[4096]; - static int usbnet_recv(struct eth_device *edev) { struct usbnet *dev = (struct usbnet*) edev->priv; @@ -125,15 +125,15 @@ static int usbnet_recv(struct eth_device *edev) len = dev->rx_urb_size; - ret = usb_bulk_msg(dev->udev, dev->in, rx_buf, len, &alen, 100); + ret = usb_bulk_msg(dev->udev, dev->in, dev->rx_buf, len, &alen, 2); if (ret) return ret; if (alen) { if (info->rx_fixup) - return info->rx_fixup(dev, rx_buf, alen); + return info->rx_fixup(dev, dev->rx_buf, alen); else - net_receive(edev, rx_buf, alen); + net_receive(edev, dev->rx_buf, alen); } return 0; @@ -159,13 +159,22 @@ static int usbnet_init(struct eth_device *edev) return 0; } +static void usbnet_adjust_link(struct eth_device *edev) +{ + struct usbnet *dev = (struct usbnet*)edev->priv; + struct driver_info *info = dev->driver_info; + + if (info->link_reset) + info->link_reset(dev); +} + static int usbnet_open(struct eth_device *edev) { struct usbnet *dev = (struct usbnet*)edev->priv; dev_dbg(&edev->dev, "%s\n",__func__); - return phy_device_connect(edev, &dev->miibus, dev->phy_addr, NULL, + return phy_device_connect(edev, &dev->miibus, dev->phy_addr, usbnet_adjust_link, 0, PHY_INTERFACE_MODE_NA); } @@ -190,11 +199,11 @@ int usbnet_probe(struct usb_device *usbdev, const struct usb_device_id *prod) edev = &undev->edev; undev->udev = usbdev; - edev->open = usbnet_open, - edev->init = usbnet_init, - edev->send = usbnet_send, - edev->recv = usbnet_recv, - edev->halt = usbnet_halt, + edev->open = usbnet_open; + edev->init = usbnet_init; + edev->send = usbnet_send; + edev->recv = usbnet_recv; + edev->halt = usbnet_halt; edev->priv = undev; edev->parent = &usbdev->dev; @@ -211,8 +220,23 @@ int usbnet_probe(struct usb_device *usbdev, const struct usb_device_id *prod) undev->rx_urb_size = 1514; /* FIXME: What to put here? */ undev->maxpacket = usb_maxpacket(undev->udev, undev->out); + undev->rx_buf = dma_alloc(undev->rx_urb_size); + if (!undev->rx_buf) { + status = -ENOMEM; + goto out1; + } + + undev->tx_buf = dma_alloc(4096); + if (!undev->tx_buf) { + status = -ENOMEM; + goto out1; + } + eth_register(edev); + slice_depends_on(eth_device_slice(edev), usb_device_slice(usbdev)); + slice_depends_on(mdiobus_slice(&undev->miibus), usb_device_slice(usbdev)); + return 0; out1: dev_dbg(&edev->dev, "err: %d\n", status); @@ -231,5 +255,7 @@ void usbnet_disconnect(struct usb_device *usbdev) eth_unregister(edev); + free(undev->rx_buf); + free(undev->tx_buf); free(undev); } diff --git a/drivers/net/virtio.c b/drivers/net/virtio.c new file mode 100644 index 0000000000..8605f67ae2 --- /dev/null +++ b/drivers/net/virtio.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi> + * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <driver.h> +#include <malloc.h> +#include <net.h> +#include <init.h> +#include <linux/virtio.h> +#include <linux/virtio_ring.h> +#include <uapi/linux/virtio_net.h> + +/* Amount of buffers to keep in the RX virtqueue */ +#define VIRTIO_NET_NUM_RX_BUFS 32 + +/* + * This value comes from the VirtIO spec: 1500 for maximum packet size, + * 14 for the Ethernet header, 12 for virtio_net_hdr. In total 1526 bytes. + */ +#define VIRTIO_NET_RX_BUF_SIZE 1526 + +struct virtio_net_priv { + union { + struct virtqueue *vqs[2]; + struct { + struct virtqueue *rx_vq; + struct virtqueue *tx_vq; + }; + }; + + char rx_buff[VIRTIO_NET_NUM_RX_BUFS][VIRTIO_NET_RX_BUF_SIZE]; + bool rx_running; + int net_hdr_len; + struct eth_device edev; + struct virtio_device *vdev; +}; + +static inline struct virtio_net_priv *to_priv(struct eth_device *edev) +{ + return container_of(edev, struct virtio_net_priv, edev); +} + +static int virtio_net_start(struct eth_device *edev) +{ + struct virtio_net_priv *priv = to_priv(edev); + struct virtio_sg sg; + struct virtio_sg *sgs[] = { &sg }; + int i; + + if (!priv->rx_running) { + /* receive buffer length is always 1526 */ + sg.length = VIRTIO_NET_RX_BUF_SIZE; + + /* setup the receive buffer address */ + for (i = 0; i < VIRTIO_NET_NUM_RX_BUFS; i++) { + sg.addr = priv->rx_buff[i]; + virtqueue_add(priv->rx_vq, sgs, 0, 1); + } + + virtqueue_kick(priv->rx_vq); + + /* setup the receive queue only once */ + priv->rx_running = true; + } + + return 0; +} + +static int virtio_net_send(struct eth_device *edev, void *packet, int length) +{ + struct virtio_net_priv *priv = to_priv(edev); + struct virtio_net_hdr_v1 hdr_v1; + struct virtio_net_hdr hdr; + struct virtio_sg hdr_sg; + struct virtio_sg data_sg = { packet, length }; + struct virtio_sg *sgs[] = { &hdr_sg, &data_sg }; + int ret; + + if (priv->net_hdr_len == sizeof(struct virtio_net_hdr)) + hdr_sg.addr = &hdr; + else + hdr_sg.addr = &hdr_v1; + + hdr_sg.length = priv->net_hdr_len; + memset(hdr_sg.addr, 0, priv->net_hdr_len); + + ret = virtqueue_add(priv->tx_vq, sgs, 2, 0); + if (ret) + return ret; + + virtqueue_kick(priv->tx_vq); + + while (1) { + if (virtqueue_get_buf(priv->tx_vq, NULL)) + break; + } + + return 0; +} + +static int virtio_net_recv(struct eth_device *edev) +{ + struct virtio_net_priv *priv = to_priv(edev); + struct virtio_sg sg; + struct virtio_sg *sgs[] = { &sg }; + unsigned int len; + void *buf; + + sg.addr = virtqueue_get_buf(priv->rx_vq, &len); + if (!sg.addr) + return -EAGAIN; + + sg.length = VIRTIO_NET_RX_BUF_SIZE; + + buf = sg.addr + priv->net_hdr_len; + len -= priv->net_hdr_len; + + net_receive(edev, buf, len); + + /* Put the buffer back to the rx ring */ + virtqueue_add(priv->rx_vq, sgs, 0, 1); + + return 0; +} + +static void virtio_net_stop(struct eth_device *dev) +{ + /* + * There is no way to stop the queue from running, unless we issue + * a reset to the virtio device, and re-do the queue initialization + * from the beginning. + */ +} + +static int virtio_net_write_hwaddr(struct eth_device *edev, const unsigned char *adr) +{ + struct virtio_net_priv *priv = to_priv(edev); + int i; + + /* + * v1.0 compliant device's MAC address is set through control channel, + * which we don't support for now. + */ + if (virtio_has_feature(priv->vdev, VIRTIO_F_VERSION_1)) + return -ENOSYS; + + for (i = 0; i < 6; i++) + virtio_cwrite8(priv->vdev, offsetof(struct virtio_net_config, mac) + i, adr[i]); + + return 0; +} + +static int virtio_net_read_rom_hwaddr(struct eth_device *edev, unsigned char *adr) +{ + struct virtio_net_priv *priv = to_priv(edev); + + virtio_cread_bytes(priv->vdev, offsetof(struct virtio_net_config, mac), adr, 6); + + return 0; +} + +static int virtio_net_probe(struct virtio_device *vdev) +{ + struct virtio_net_priv *priv; + struct eth_device *edev; + int ret; + + priv = xzalloc(sizeof(*priv)); + + vdev->priv = priv; + + /* + * For v1.0 compliant device, it always assumes the member + * 'num_buffers' exists in the struct virtio_net_hdr while + * the legacy driver only presented 'num_buffers' when + * VIRTIO_NET_F_MRG_RXBUF was negotiated. Without that feature + * the structure was 2 bytes shorter. + */ + if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) + priv->net_hdr_len = sizeof(struct virtio_net_hdr_v1); + else + priv->net_hdr_len = sizeof(struct virtio_net_hdr); + + ret = virtio_find_vqs(vdev, 2, priv->vqs); + if (ret < 0) + return ret; + + priv->vdev = vdev; + + edev = &priv->edev; + edev->priv = priv; + edev->parent = &vdev->dev; + + edev->open = virtio_net_start; + edev->send = virtio_net_send; + edev->recv = virtio_net_recv; + edev->halt = virtio_net_stop; + edev->get_ethaddr = virtio_net_read_rom_hwaddr; + edev->set_ethaddr = virtio_net_write_hwaddr; + + return eth_register(edev); +} + +static void virtio_net_remove(struct virtio_device *vdev) +{ + struct virtio_net_priv *priv = vdev->priv; + + vdev->config->reset(vdev); + eth_unregister(&priv->edev); + vdev->config->del_vqs(vdev); + + free(priv); +} + +/* + * For simplicity, the driver only negotiates the VIRTIO_NET_F_MAC feature. + * For the VIRTIO_NET_F_STATUS feature, we don't negotiate it, hence per spec + * we should assume the link is always active. + */ +static const u32 features[] = { + VIRTIO_NET_F_MAC +}; + +static const struct virtio_device_id id_table[] = { + { VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static struct virtio_driver virtio_net = { + .driver.name = "virtio_net", + .id_table = id_table, + .probe = virtio_net_probe, + .remove = virtio_net_remove, + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .feature_table_legacy = features, + .feature_table_size_legacy = ARRAY_SIZE(features), +}; +device_virtio_driver(virtio_net); diff --git a/drivers/net/xgmac.c b/drivers/net/xgmac.c deleted file mode 100644 index 136d788043..0000000000 --- a/drivers/net/xgmac.c +++ /dev/null @@ -1,734 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright 2010-2011 Calxeda, Inc. - */ - -#include <common.h> -#include <dma.h> -#include <net.h> -#include <clock.h> -#include <malloc.h> -#include <xfuncs.h> -#include <init.h> -#include <errno.h> -#include <io.h> -#include <linux/err.h> - -#define TX_NUM_DESC 1 -#define RX_NUM_DESC 32 - -#define ETH_BUF_SZ 2048 -#define TX_BUF_SZ (ETH_BUF_SZ * TX_NUM_DESC) -#define RX_BUF_SZ (ETH_BUF_SZ * RX_NUM_DESC) - -/* XGMAC Register definitions */ -#define XGMAC_CONTROL 0x00000000 /* MAC Configuration */ -#define XGMAC_FRAME_FILTER 0x00000004 /* MAC Frame Filter */ -#define XGMAC_FLOW_CTRL 0x00000018 /* MAC Flow Control */ -#define XGMAC_VLAN_TAG 0x0000001C /* VLAN Tags */ -#define XGMAC_VERSION 0x00000020 /* Version */ -#define XGMAC_VLAN_INCL 0x00000024 /* VLAN tag for tx frames */ -#define XGMAC_LPI_CTRL 0x00000028 /* LPI Control and Status */ -#define XGMAC_LPI_TIMER 0x0000002C /* LPI Timers Control */ -#define XGMAC_TX_PACE 0x00000030 /* Transmit Pace and Stretch */ -#define XGMAC_VLAN_HASH 0x00000034 /* VLAN Hash Table */ -#define XGMAC_DEBUG 0x00000038 /* Debug */ -#define XGMAC_INT_STAT 0x0000003C /* Interrupt and Control */ -#define XGMAC_ADDR_HIGH(reg) (0x00000040 + ((reg) * 8)) -#define XGMAC_ADDR_LOW(reg) (0x00000044 + ((reg) * 8)) -#define XGMAC_HASH(n) (0x00000300 + (n) * 4) /* HASH table regs */ -#define XGMAC_NUM_HASH 16 -#define XGMAC_OMR 0x00000400 -#define XGMAC_REMOTE_WAKE 0x00000700 /* Remote Wake-Up Frm Filter */ -#define XGMAC_PMT 0x00000704 /* PMT Control and Status */ -#define XGMAC_MMC_CTRL 0x00000800 /* XGMAC MMC Control */ -#define XGMAC_MMC_INTR_RX 0x00000804 /* Recieve Interrupt */ -#define XGMAC_MMC_INTR_TX 0x00000808 /* Transmit Interrupt */ -#define XGMAC_MMC_INTR_MASK_RX 0x0000080c /* Recieve Interrupt Mask */ -#define XGMAC_MMC_INTR_MASK_TX 0x00000810 /* Transmit Interrupt Mask */ - - -/* Hardware TX Statistics Counters */ -#define XGMAC_MMC_TXOCTET_GB_LO 0x00000814 -#define XGMAC_MMC_TXOCTET_GB_HI 0x00000818 -#define XGMAC_MMC_TXFRAME_GB_LO 0x0000081C -#define XGMAC_MMC_TXFRAME_GB_HI 0x00000820 -#define XGMAC_MMC_TXBCFRAME_G 0x00000824 -#define XGMAC_MMC_TXMCFRAME_G 0x0000082C -#define XGMAC_MMC_TXUCFRAME_GB 0x00000864 -#define XGMAC_MMC_TXMCFRAME_GB 0x0000086C -#define XGMAC_MMC_TXBCFRAME_GB 0x00000874 -#define XGMAC_MMC_TXUNDERFLOW 0x0000087C -#define XGMAC_MMC_TXOCTET_G_LO 0x00000884 -#define XGMAC_MMC_TXOCTET_G_HI 0x00000888 -#define XGMAC_MMC_TXFRAME_G_LO 0x0000088C -#define XGMAC_MMC_TXFRAME_G_HI 0x00000890 -#define XGMAC_MMC_TXPAUSEFRAME 0x00000894 -#define XGMAC_MMC_TXVLANFRAME 0x0000089C - -/* Hardware RX Statistics Counters */ -#define XGMAC_MMC_RXFRAME_GB_LO 0x00000900 -#define XGMAC_MMC_RXFRAME_GB_HI 0x00000904 -#define XGMAC_MMC_RXOCTET_GB_LO 0x00000908 -#define XGMAC_MMC_RXOCTET_GB_HI 0x0000090C -#define XGMAC_MMC_RXOCTET_G_LO 0x00000910 -#define XGMAC_MMC_RXOCTET_G_HI 0x00000914 -#define XGMAC_MMC_RXBCFRAME_G 0x00000918 -#define XGMAC_MMC_RXMCFRAME_G 0x00000920 -#define XGMAC_MMC_RXCRCERR 0x00000928 -#define XGMAC_MMC_RXRUNT 0x00000930 -#define XGMAC_MMC_RXJABBER 0x00000934 -#define XGMAC_MMC_RXUCFRAME_G 0x00000970 -#define XGMAC_MMC_RXLENGTHERR 0x00000978 -#define XGMAC_MMC_RXPAUSEFRAME 0x00000988 -#define XGMAC_MMC_RXOVERFLOW 0x00000990 -#define XGMAC_MMC_RXVLANFRAME 0x00000998 -#define XGMAC_MMC_RXWATCHDOG 0x000009a0 - -/* DMA Control and Status Registers */ -#define XGMAC_DMA_BUS_MODE 0x00000f00 /* Bus Mode */ -#define XGMAC_DMA_TX_POLL 0x00000f04 /* Transmit Poll Demand */ -#define XGMAC_DMA_RX_POLL 0x00000f08 /* Received Poll Demand */ -#define XGMAC_DMA_RX_BASE_ADDR 0x00000f0c /* Receive List Base */ -#define XGMAC_DMA_TX_BASE_ADDR 0x00000f10 /* Transmit List Base */ -#define XGMAC_DMA_STATUS 0x00000f14 /* Status Register */ -#define XGMAC_DMA_CONTROL 0x00000f18 /* Ctrl (Operational Mode) */ -#define XGMAC_DMA_INTR_ENA 0x00000f1c /* Interrupt Enable */ -#define XGMAC_DMA_MISS_FRAME_CTR 0x00000f20 /* Missed Frame Counter */ -#define XGMAC_DMA_RI_WDOG_TIMER 0x00000f24 /* RX Intr Watchdog Timer */ -#define XGMAC_DMA_AXI_BUS 0x00000f28 /* AXI Bus Mode */ -#define XGMAC_DMA_AXI_STATUS 0x00000f2C /* AXI Status */ -#define XGMAC_DMA_HW_FEATURE 0x00000f58 /* Enabled Hardware Features */ - -#define XGMAC_ADDR_AE 0x80000000 -#define XGMAC_MAX_FILTER_ADDR 31 - -/* PMT Control and Status */ -#define XGMAC_PMT_POINTER_RESET 0x80000000 -#define XGMAC_PMT_GLBL_UNICAST 0x00000200 -#define XGMAC_PMT_WAKEUP_RX_FRM 0x00000040 -#define XGMAC_PMT_MAGIC_PKT 0x00000020 -#define XGMAC_PMT_WAKEUP_FRM_EN 0x00000004 -#define XGMAC_PMT_MAGIC_PKT_EN 0x00000002 -#define XGMAC_PMT_POWERDOWN 0x00000001 - -#define XGMAC_CONTROL_SPD 0x40000000 /* Speed control */ -#define XGMAC_CONTROL_SPD_MASK 0x60000000 -#define XGMAC_CONTROL_SPD_1G 0x60000000 -#define XGMAC_CONTROL_SPD_2_5G 0x40000000 -#define XGMAC_CONTROL_SPD_10G 0x00000000 -#define XGMAC_CONTROL_SARC 0x10000000 /* Source Addr Insert/Replace */ -#define XGMAC_CONTROL_SARK_MASK 0x18000000 -#define XGMAC_CONTROL_CAR 0x04000000 /* CRC Addition/Replacement */ -#define XGMAC_CONTROL_CAR_MASK 0x06000000 -#define XGMAC_CONTROL_DP 0x01000000 /* Disable Padding */ -#define XGMAC_CONTROL_WD 0x00800000 /* Disable Watchdog on rx */ -#define XGMAC_CONTROL_JD 0x00400000 /* Jabber disable */ -#define XGMAC_CONTROL_JE 0x00100000 /* Jumbo frame */ -#define XGMAC_CONTROL_LM 0x00001000 /* Loop-back mode */ -#define XGMAC_CONTROL_IPC 0x00000400 /* Checksum Offload */ -#define XGMAC_CONTROL_ACS 0x00000080 /* Automatic Pad/FCS Strip */ -#define XGMAC_CONTROL_DDIC 0x00000010 /* Disable Deficit Idle Count */ -#define XGMAC_CONTROL_TE 0x00000008 /* Transmitter Enable */ -#define XGMAC_CONTROL_RE 0x00000004 /* Receiver Enable */ - -/* XGMAC Frame Filter defines */ -#define XGMAC_FRAME_FILTER_PR 0x00000001 /* Promiscuous Mode */ -#define XGMAC_FRAME_FILTER_HUC 0x00000002 /* Hash Unicast */ -#define XGMAC_FRAME_FILTER_HMC 0x00000004 /* Hash Multicast */ -#define XGMAC_FRAME_FILTER_DAIF 0x00000008 /* DA Inverse Filtering */ -#define XGMAC_FRAME_FILTER_PM 0x00000010 /* Pass all multicast */ -#define XGMAC_FRAME_FILTER_DBF 0x00000020 /* Disable Broadcast frames */ -#define XGMAC_FRAME_FILTER_SAIF 0x00000100 /* Inverse Filtering */ -#define XGMAC_FRAME_FILTER_SAF 0x00000200 /* Source Address Filter */ -#define XGMAC_FRAME_FILTER_HPF 0x00000400 /* Hash or perfect Filter */ -#define XGMAC_FRAME_FILTER_VHF 0x00000800 /* VLAN Hash Filter */ -#define XGMAC_FRAME_FILTER_VPF 0x00001000 /* VLAN Perfect Filter */ -#define XGMAC_FRAME_FILTER_RA 0x80000000 /* Receive all mode */ - -#define FIFO_MINUS_1K 0x0 -#define FIFO_MINUS_2K 0x1 -#define FIFO_MINUS_3K 0x2 -#define FIFO_MINUS_4K 0x3 -#define FIFO_MINUS_6K 0x4 -#define FIFO_MINUS_8K 0x5 -#define FIFO_MINUS_12K 0x6 -#define FIFO_MINUS_16K 0x7 - -/* XGMAC FLOW CTRL defines */ -#define XGMAC_FLOW_CTRL_PT_MASK 0xffff0000 /* Pause Time Mask */ -#define XGMAC_FLOW_CTRL_PT_SHIFT 16 -#define XGMAC_FLOW_CTRL_DZQP 0x00000080 /* Disable Zero-Quanta Phase */ -#define XGMAC_FLOW_CTRL_PLT 0x00000020 /* Pause Low Threshhold */ -#define XGMAC_FLOW_CTRL_PLT_SHIFT 4 -#define XGMAC_FLOW_CTRL_PLT_MASK 0x00000030 /* PLT MASK */ -#define XGMAC_FLOW_CTRL_UP 0x00000008 /* Unicast Pause Frame Detect */ -#define XGMAC_FLOW_CTRL_RFE 0x00000004 /* Rx Flow Control Enable */ -#define XGMAC_FLOW_CTRL_TFE 0x00000002 /* Tx Flow Control Enable */ -#define XGMAC_FLOW_CTRL_FCB_BPA 0x00000001 /* Flow Control Busy ... */ - -/* XGMAC_INT_STAT reg */ -#define XGMAC_INT_STAT_PMT 0x0080 /* PMT Interrupt Status */ -#define XGMAC_INT_STAT_LPI 0x0040 /* LPI Interrupt Status */ - -/* DMA Bus Mode register defines */ -#define DMA_BUS_MODE_SFT_RESET 0x00000001 /* Software Reset */ -#define DMA_BUS_MODE_DSL_MASK 0x0000007c /* Descriptor Skip Length */ -#define DMA_BUS_MODE_DSL_SHIFT 2 /* (in DWORDS) */ -#define DMA_BUS_MODE_ATDS 0x00000080 /* Alternate Descriptor Size */ - -/* Programmable burst length */ -#define DMA_BUS_MODE_PBL_MASK 0x00003f00 /* Programmable Burst Len */ -#define DMA_BUS_MODE_PBL_SHIFT 8 -#define DMA_BUS_MODE_FB 0x00010000 /* Fixed burst */ -#define DMA_BUS_MODE_RPBL_MASK 0x003e0000 /* Rx-Programmable Burst Len */ -#define DMA_BUS_MODE_RPBL_SHIFT 17 -#define DMA_BUS_MODE_USP 0x00800000 -#define DMA_BUS_MODE_8PBL 0x01000000 -#define DMA_BUS_MODE_AAL 0x02000000 - -#define DMA_AXIMODE_ENLPI 0x80000000 -#define DMA_AXIMODE_MGK 0x40000000 -#define DMA_AXIMODE_WROSR 0x00100000 -#define DMA_AXIMODE_WROSR_MASK 0x00F00000 -#define DMA_AXIMODE_WROSR_SHIFT 20 -#define DMA_AXIMODE_RDOSR 0x00010000 -#define DMA_AXIMODE_RDOSR_MASK 0x000F0000 -#define DMA_AXIMODE_RDOSR_SHIFT 16 -#define DMA_AXIMODE_AAL 0x00001000 -#define DMA_AXIMODE_BLEN256 0x00000080 -#define DMA_AXIMODE_BLEN128 0x00000040 -#define DMA_AXIMODE_BLEN64 0x00000020 -#define DMA_AXIMODE_BLEN32 0x00000010 -#define DMA_AXIMODE_BLEN16 0x00000008 -#define DMA_AXIMODE_BLEN8 0x00000004 -#define DMA_AXIMODE_BLEN4 0x00000002 -#define DMA_AXIMODE_UNDEF 0x00000001 - -/* DMA Bus Mode register defines */ -#define DMA_BUS_PR_RATIO_MASK 0x0000c000 /* Rx/Tx priority ratio */ -#define DMA_BUS_PR_RATIO_SHIFT 14 -#define DMA_BUS_FB 0x00010000 /* Fixed Burst */ - -/* DMA Control register defines */ -#define DMA_CONTROL_ST 0x00002000 /* Start/Stop Transmission */ -#define DMA_CONTROL_SR 0x00000002 /* Start/Stop Receive */ -#define DMA_CONTROL_DFF 0x01000000 /* Disable flush of rx frames */ - -/* DMA Normal interrupt */ -#define DMA_INTR_ENA_NIE 0x00010000 /* Normal Summary */ -#define DMA_INTR_ENA_AIE 0x00008000 /* Abnormal Summary */ -#define DMA_INTR_ENA_ERE 0x00004000 /* Early Receive */ -#define DMA_INTR_ENA_FBE 0x00002000 /* Fatal Bus Error */ -#define DMA_INTR_ENA_ETE 0x00000400 /* Early Transmit */ -#define DMA_INTR_ENA_RWE 0x00000200 /* Receive Watchdog */ -#define DMA_INTR_ENA_RSE 0x00000100 /* Receive Stopped */ -#define DMA_INTR_ENA_RUE 0x00000080 /* Receive Buffer Unavailable */ -#define DMA_INTR_ENA_RIE 0x00000040 /* Receive Interrupt */ -#define DMA_INTR_ENA_UNE 0x00000020 /* Tx Underflow */ -#define DMA_INTR_ENA_OVE 0x00000010 /* Receive Overflow */ -#define DMA_INTR_ENA_TJE 0x00000008 /* Transmit Jabber */ -#define DMA_INTR_ENA_TUE 0x00000004 /* Transmit Buffer Unavail */ -#define DMA_INTR_ENA_TSE 0x00000002 /* Transmit Stopped */ -#define DMA_INTR_ENA_TIE 0x00000001 /* Transmit Interrupt */ - -#define DMA_INTR_NORMAL (DMA_INTR_ENA_NIE | DMA_INTR_ENA_RIE | \ - DMA_INTR_ENA_TUE) - -#define DMA_INTR_ABNORMAL (DMA_INTR_ENA_AIE | DMA_INTR_ENA_FBE | \ - DMA_INTR_ENA_RWE | DMA_INTR_ENA_RSE | \ - DMA_INTR_ENA_RUE | DMA_INTR_ENA_UNE | \ - DMA_INTR_ENA_OVE | DMA_INTR_ENA_TJE | \ - DMA_INTR_ENA_TSE) - -/* DMA default interrupt mask */ -#define DMA_INTR_DEFAULT_MASK (DMA_INTR_NORMAL | DMA_INTR_ABNORMAL) - -/* DMA Status register defines */ -#define DMA_STATUS_GMI 0x08000000 /* MMC interrupt */ -#define DMA_STATUS_GLI 0x04000000 /* GMAC Line interface int */ -#define DMA_STATUS_EB_MASK 0x00380000 /* Error Bits Mask */ -#define DMA_STATUS_EB_TX_ABORT 0x00080000 /* Error Bits - TX Abort */ -#define DMA_STATUS_EB_RX_ABORT 0x00100000 /* Error Bits - RX Abort */ -#define DMA_STATUS_TS_MASK 0x00700000 /* Transmit Process State */ -#define DMA_STATUS_TS_SHIFT 20 -#define DMA_STATUS_RS_MASK 0x000e0000 /* Receive Process State */ -#define DMA_STATUS_RS_SHIFT 17 -#define DMA_STATUS_NIS 0x00010000 /* Normal Interrupt Summary */ -#define DMA_STATUS_AIS 0x00008000 /* Abnormal Interrupt Summary */ -#define DMA_STATUS_ERI 0x00004000 /* Early Receive Interrupt */ -#define DMA_STATUS_FBI 0x00002000 /* Fatal Bus Error Interrupt */ -#define DMA_STATUS_ETI 0x00000400 /* Early Transmit Interrupt */ -#define DMA_STATUS_RWT 0x00000200 /* Receive Watchdog Timeout */ -#define DMA_STATUS_RPS 0x00000100 /* Receive Process Stopped */ -#define DMA_STATUS_RU 0x00000080 /* Receive Buffer Unavailable */ -#define DMA_STATUS_RI 0x00000040 /* Receive Interrupt */ -#define DMA_STATUS_UNF 0x00000020 /* Transmit Underflow */ -#define DMA_STATUS_OVF 0x00000010 /* Receive Overflow */ -#define DMA_STATUS_TJT 0x00000008 /* Transmit Jabber Timeout */ -#define DMA_STATUS_TU 0x00000004 /* Transmit Buffer Unavail */ -#define DMA_STATUS_TPS 0x00000002 /* Transmit Process Stopped */ -#define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */ - -/* Common MAC defines */ -#define MAC_ENABLE_TX 0x00000008 /* Transmitter Enable */ -#define MAC_ENABLE_RX 0x00000004 /* Receiver Enable */ - -/* XGMAC Operation Mode Register */ -#define XGMAC_OMR_TSF 0x00200000 /* TX FIFO Store and Forward */ -#define XGMAC_OMR_FTF 0x00100000 /* Flush Transmit FIFO */ -#define XGMAC_OMR_TTC 0x00020000 /* Transmit Threshhold Ctrl */ -#define XGMAC_OMR_TTC_SHIFT 16 -#define XGMAC_OMR_TTC_MASK 0x00030000 -#define XGMAC_OMR_RFD 0x00006000 /* FC Deactivation Threshhold */ -#define XGMAC_OMR_RFD_SHIFT 12 -#define XGMAC_OMR_RFD_MASK 0x00007000 /* FC Deact Threshhold MASK */ -#define XGMAC_OMR_RFA 0x00000600 /* FC Activation Threshhold */ -#define XGMAC_OMR_RFA_SHIFT 9 -#define XGMAC_OMR_RFA_MASK 0x00000E00 /* FC Act Threshhold MASK */ -#define XGMAC_OMR_EFC 0x00000100 /* Enable Hardware FC */ -#define XGMAC_OMR_FEF 0x00000080 /* Forward Error Frames */ -#define XGMAC_OMR_DT 0x00000040 /* Drop TCP/IP csum Errors */ -#define XGMAC_OMR_RSF 0x00000020 /* RX FIFO Store and Forward */ -#define XGMAC_OMR_RTC_256 0x00000018 /* RX Threshhold Ctrl */ -#define XGMAC_OMR_RTC_MASK 0x00000018 /* RX Threshhold Ctrl MASK */ - -/* XGMAC HW Features Register */ -#define DMA_HW_FEAT_TXCOESEL 0x00010000 /* TX Checksum offload */ - -#define XGMAC_MMC_CTRL_CNT_FRZ 0x00000008 - -/* XGMAC Descriptor Defines */ -#define MAX_DESC_BUF_SZ (0x2000 - 8) - -#define RXDESC_EXT_STATUS 0x00000001 -#define RXDESC_CRC_ERR 0x00000002 -#define RXDESC_RX_ERR 0x00000008 -#define RXDESC_RX_WDOG 0x00000010 -#define RXDESC_FRAME_TYPE 0x00000020 -#define RXDESC_GIANT_FRAME 0x00000080 -#define RXDESC_LAST_SEG 0x00000100 -#define RXDESC_FIRST_SEG 0x00000200 -#define RXDESC_VLAN_FRAME 0x00000400 -#define RXDESC_OVERFLOW_ERR 0x00000800 -#define RXDESC_LENGTH_ERR 0x00001000 -#define RXDESC_SA_FILTER_FAIL 0x00002000 -#define RXDESC_DESCRIPTOR_ERR 0x00004000 -#define RXDESC_ERROR_SUMMARY 0x00008000 -#define RXDESC_FRAME_LEN_OFFSET 16 -#define RXDESC_FRAME_LEN_MASK 0x3fff0000 -#define RXDESC_DA_FILTER_FAIL 0x40000000 - -#define RXDESC1_END_RING 0x00008000 - -#define RXDESC_IP_PAYLOAD_MASK 0x00000003 -#define RXDESC_IP_PAYLOAD_UDP 0x00000001 -#define RXDESC_IP_PAYLOAD_TCP 0x00000002 -#define RXDESC_IP_PAYLOAD_ICMP 0x00000003 -#define RXDESC_IP_HEADER_ERR 0x00000008 -#define RXDESC_IP_PAYLOAD_ERR 0x00000010 -#define RXDESC_IPV4_PACKET 0x00000040 -#define RXDESC_IPV6_PACKET 0x00000080 -#define TXDESC_UNDERFLOW_ERR 0x00000001 -#define TXDESC_JABBER_TIMEOUT 0x00000002 -#define TXDESC_LOCAL_FAULT 0x00000004 -#define TXDESC_REMOTE_FAULT 0x00000008 -#define TXDESC_VLAN_FRAME 0x00000010 -#define TXDESC_FRAME_FLUSHED 0x00000020 -#define TXDESC_IP_HEADER_ERR 0x00000040 -#define TXDESC_PAYLOAD_CSUM_ERR 0x00000080 -#define TXDESC_ERROR_SUMMARY 0x00008000 -#define TXDESC_SA_CTRL_INSERT 0x00040000 -#define TXDESC_SA_CTRL_REPLACE 0x00080000 -#define TXDESC_2ND_ADDR_CHAINED 0x00100000 -#define TXDESC_END_RING 0x00200000 -#define TXDESC_CSUM_IP 0x00400000 -#define TXDESC_CSUM_IP_PAYLD 0x00800000 -#define TXDESC_CSUM_ALL 0x00C00000 -#define TXDESC_CRC_EN_REPLACE 0x01000000 -#define TXDESC_CRC_EN_APPEND 0x02000000 -#define TXDESC_DISABLE_PAD 0x04000000 -#define TXDESC_FIRST_SEG 0x10000000 -#define TXDESC_LAST_SEG 0x20000000 -#define TXDESC_INTERRUPT 0x40000000 - -#define DESC_OWN 0x80000000 -#define DESC_BUFFER1_SZ_MASK 0x00001fff -#define DESC_BUFFER2_SZ_MASK 0x1fff0000 -#define DESC_BUFFER2_SZ_OFFSET 16 - -struct xgmac_dma_desc { - __le32 flags; - __le32 buf_size; - __le32 buf1_addr; /* Buffer 1 Address Pointer */ - __le32 buf2_addr; /* Buffer 2 Address Pointer */ - __le32 ext_status; - __le32 res[3]; -}; - -struct xgmac_priv { - struct xgmac_dma_desc *rx_chain; - struct xgmac_dma_desc *tx_chain; - char *rxbuffer; - - u32 tx_currdesc; - u32 rx_currdesc; - - void __iomem *base; - - struct eth_device edev; - struct device_d *dev; -}; - -/* XGMAC Descriptor Access Helpers */ -static inline void desc_set_buf_len(struct xgmac_dma_desc *p, u32 buf_sz) -{ - if (buf_sz > MAX_DESC_BUF_SZ) - p->buf_size = cpu_to_le32(MAX_DESC_BUF_SZ | - (buf_sz - MAX_DESC_BUF_SZ) << DESC_BUFFER2_SZ_OFFSET); - else - p->buf_size = cpu_to_le32(buf_sz); -} - -static inline int desc_get_buf_len(struct xgmac_dma_desc *p) -{ - u32 len = le32_to_cpu(p->buf_size); - return (len & DESC_BUFFER1_SZ_MASK) + - ((len & DESC_BUFFER2_SZ_MASK) >> DESC_BUFFER2_SZ_OFFSET); -} - -static inline void desc_init_rx_desc(struct xgmac_dma_desc *p, int ring_size, - int buf_sz) -{ - struct xgmac_dma_desc *end = p + ring_size - 1; - - memset(p, 0, sizeof(*p) * ring_size); - - for (; p <= end; p++) - desc_set_buf_len(p, buf_sz); - - end->buf_size |= cpu_to_le32(RXDESC1_END_RING); -} - -static inline void desc_init_tx_desc(struct xgmac_dma_desc *p, u32 ring_size) -{ - memset(p, 0, sizeof(*p) * ring_size); - p[ring_size - 1].flags = cpu_to_le32(TXDESC_END_RING); -} - -static inline int desc_get_owner(struct xgmac_dma_desc *p) -{ - return le32_to_cpu(p->flags) & DESC_OWN; -} - -static inline void desc_set_rx_owner(struct xgmac_dma_desc *p) -{ - /* Clear all fields and set the owner */ - p->flags = cpu_to_le32(DESC_OWN); -} - -static inline void desc_set_tx_owner(struct xgmac_dma_desc *p, u32 flags) -{ - u32 tmpflags = le32_to_cpu(p->flags); - tmpflags &= TXDESC_END_RING; - tmpflags |= flags | DESC_OWN; - p->flags = cpu_to_le32(tmpflags); -} - -static inline void *desc_get_buf_addr(struct xgmac_dma_desc *p) -{ - return (void *)le32_to_cpu(p->buf1_addr); -} - -static inline void desc_set_buf_addr(struct xgmac_dma_desc *p, - void *paddr, int len) -{ - p->buf1_addr = cpu_to_le32(paddr); - if (len > MAX_DESC_BUF_SZ) - p->buf2_addr = cpu_to_le32(paddr + MAX_DESC_BUF_SZ); -} - -static inline void desc_set_buf_addr_and_size(struct xgmac_dma_desc *p, - void *paddr, int len) -{ - desc_set_buf_len(p, len); - desc_set_buf_addr(p, paddr, len); -} - -static inline int desc_get_rx_frame_len(struct xgmac_dma_desc *p) -{ - u32 data = le32_to_cpu(p->flags); - u32 len = (data & RXDESC_FRAME_LEN_MASK) >> RXDESC_FRAME_LEN_OFFSET; - if (data & RXDESC_FRAME_TYPE) - len -= 4; - - return len; -} - -/* - * Initialize a descriptor ring. Calxeda XGMAC is configured to use - * advanced descriptors. - */ - -static void init_rx_desc(struct xgmac_priv *priv) -{ - struct xgmac_dma_desc *rxdesc = priv->rx_chain; - void *rxbuffer = priv->rxbuffer; - int i; - - desc_init_rx_desc(rxdesc, RX_NUM_DESC, ETH_BUF_SZ); - writel((ulong)rxdesc, priv->base + XGMAC_DMA_RX_BASE_ADDR); - - for (i = 0; i < RX_NUM_DESC; i++) { - desc_set_buf_addr(rxdesc + i, rxbuffer + (i * ETH_BUF_SZ), - ETH_BUF_SZ); - desc_set_rx_owner(rxdesc + i); - } -} - -static void init_tx_desc(struct xgmac_priv *priv) -{ - desc_init_tx_desc(priv->tx_chain, TX_NUM_DESC); - writel((ulong)priv->tx_chain, priv->base + XGMAC_DMA_TX_BASE_ADDR); -} - -static int xgmac_reset(struct eth_device *dev) -{ - struct xgmac_priv *priv = dev->priv; - int ret; - u32 value; - - value = readl(priv->base + XGMAC_CONTROL) & XGMAC_CONTROL_SPD_MASK; - - writel(DMA_BUS_MODE_SFT_RESET, priv->base + XGMAC_DMA_BUS_MODE); - - ret = wait_on_timeout(100 * MSECOND, - !(readl(priv->base + XGMAC_DMA_BUS_MODE) & DMA_BUS_MODE_SFT_RESET)); - - writel(value, priv->base + XGMAC_CONTROL); - - return ret; -} - -static int xgmac_open(struct eth_device *edev) -{ - struct xgmac_priv *priv = edev->priv; - int value; - int ret; - - ret = xgmac_reset(edev); - if (ret) - return ret; - - /* set the AXI bus modes */ - value = DMA_BUS_MODE_ATDS | - (16 << DMA_BUS_MODE_PBL_SHIFT) | - DMA_BUS_MODE_FB | DMA_BUS_MODE_AAL; - writel(value, priv->base + XGMAC_DMA_BUS_MODE); - - value = DMA_AXIMODE_AAL | DMA_AXIMODE_BLEN16 | - DMA_AXIMODE_BLEN8 | DMA_AXIMODE_BLEN4; - writel(value, priv->base + XGMAC_DMA_AXI_BUS); - - /* set flow control parameters and store and forward mode */ - value = (FIFO_MINUS_12K << XGMAC_OMR_RFD_SHIFT) | - (FIFO_MINUS_4K << XGMAC_OMR_RFA_SHIFT) | - XGMAC_OMR_EFC | XGMAC_OMR_TSF | XGMAC_OMR_RSF; - writel(value, priv->base + XGMAC_OMR); - - /* enable pause frames */ - value = (1024 << XGMAC_FLOW_CTRL_PT_SHIFT) | - (1 << XGMAC_FLOW_CTRL_PLT_SHIFT) | - XGMAC_FLOW_CTRL_UP | XGMAC_FLOW_CTRL_RFE | XGMAC_FLOW_CTRL_TFE; - writel(value, priv->base + XGMAC_FLOW_CTRL); - - /* Initialize the descriptor chains */ - init_rx_desc(priv); - init_tx_desc(priv); - - /* must set to 0, or when started up will cause issues */ - priv->tx_currdesc = 0; - priv->rx_currdesc = 0; - - /* set default core values */ - value = readl(priv->base + XGMAC_CONTROL); - value &= XGMAC_CONTROL_SPD_MASK; - value |= XGMAC_CONTROL_DDIC | XGMAC_CONTROL_ACS | - XGMAC_CONTROL_IPC | XGMAC_CONTROL_CAR; - - /* Everything is ready enable both mac and DMA */ - value |= XGMAC_CONTROL_RE | XGMAC_CONTROL_TE; - writel(value, priv->base + XGMAC_CONTROL); - - value = readl(priv->base + XGMAC_DMA_CONTROL); - value |= DMA_CONTROL_SR | DMA_CONTROL_ST; - writel(value, priv->base + XGMAC_DMA_CONTROL); - - return 0; -} - -static int xgmac_send(struct eth_device *edev, void *packet, int length) -{ - struct xgmac_priv *priv = edev->priv; - u32 currdesc = priv->tx_currdesc; - struct xgmac_dma_desc *txdesc = &priv->tx_chain[currdesc]; - int ret; - - dma_sync_single_for_device((unsigned long)packet, length, DMA_TO_DEVICE); - desc_set_buf_addr_and_size(txdesc, packet, length); - desc_set_tx_owner(txdesc, TXDESC_FIRST_SEG | - TXDESC_LAST_SEG | TXDESC_CRC_EN_APPEND); - - /* write poll demand */ - writel(1, priv->base + XGMAC_DMA_TX_POLL); - - ret = wait_on_timeout(1 * SECOND, !desc_get_owner(txdesc)); - dma_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE); - if (ret) { - dev_err(priv->dev, "TX timeout\n"); - return ret; - } - - priv->tx_currdesc = (currdesc + 1) & (TX_NUM_DESC - 1); - return 0; -} - -static int xgmac_recv(struct eth_device *edev) -{ - struct xgmac_priv *priv = edev->priv; - u32 currdesc = priv->rx_currdesc; - struct xgmac_dma_desc *rxdesc = &priv->rx_chain[currdesc]; - int length = 0; - void *buf_addr; - - /* check if the host has the desc */ - if (desc_get_owner(rxdesc)) - return -1; /* something bad happened */ - - length = desc_get_rx_frame_len(rxdesc); - buf_addr = desc_get_buf_addr(rxdesc); - - dma_sync_single_for_cpu((unsigned long)buf_addr, length, DMA_FROM_DEVICE); - net_receive(edev, buf_addr, length); - dma_sync_single_for_device((unsigned long)buf_addr, length, - DMA_FROM_DEVICE); - - /* set descriptor back to owned by XGMAC */ - desc_set_rx_owner(rxdesc); - writel(1, priv->base + XGMAC_DMA_RX_POLL); - - priv->rx_currdesc = (currdesc + 1) & (RX_NUM_DESC - 1); - - return length; -} - -static void xgmac_halt(struct eth_device *edev) -{ - struct xgmac_priv *priv = edev->priv; - int value; - - /* Disable TX/RX */ - value = readl(priv->base + XGMAC_CONTROL); - value &= ~(XGMAC_CONTROL_RE | XGMAC_CONTROL_TE); - writel(value, priv->base + XGMAC_CONTROL); - - /* Disable DMA */ - value = readl(priv->base + XGMAC_DMA_CONTROL); - value &= ~(DMA_CONTROL_SR | DMA_CONTROL_ST); - writel(value, priv->base + XGMAC_DMA_CONTROL); - - /* must set to 0, or when started up will cause issues */ - priv->tx_currdesc = 0; - priv->rx_currdesc = 0; -} - -static int xgmac_get_ethaddr(struct eth_device *edev, unsigned char *addr) -{ - struct xgmac_priv *priv = edev->priv; - u32 hi_addr, lo_addr; - - /* Read the MAC address from the hardware */ - hi_addr = readl(priv->base + XGMAC_ADDR_HIGH(0)); - lo_addr = readl(priv->base + XGMAC_ADDR_LOW(0)); - - /* Extract the MAC address from the high and low words */ - addr[0] = lo_addr & 0xff; - addr[1] = (lo_addr >> 8) & 0xff; - addr[2] = (lo_addr >> 16) & 0xff; - addr[3] = (lo_addr >> 24) & 0xff; - addr[4] = hi_addr & 0xff; - addr[5] = (hi_addr >> 8) & 0xff; - - return 0; -} - -static int xgmac_set_ethaddr(struct eth_device *dev, const unsigned char *addr) -{ - struct xgmac_priv *priv = dev->priv; - u32 data; - - data = (addr[5] << 8) | addr[4]; - writel(data, priv->base + XGMAC_ADDR_HIGH(0)); - data = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; - writel(data, priv->base + XGMAC_ADDR_LOW(0)); - - return 0; -} - -static int hb_xgmac_probe(struct device_d *dev) -{ - struct resource *iores; - struct eth_device *edev; - struct xgmac_priv *priv; - void __iomem *base; - - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - base = IOMEM(iores->start); - - /* check hardware version */ - if (readl(base + XGMAC_VERSION) != 0x1012) - return -EINVAL; - - priv = xzalloc(sizeof(*priv)); - - priv->dev = dev; - priv->base = base; - - priv->rxbuffer = dma_alloc_coherent(RX_BUF_SZ, DMA_ADDRESS_BROKEN); - priv->rx_chain = dma_alloc_coherent(RX_NUM_DESC * sizeof(struct xgmac_dma_desc), - DMA_ADDRESS_BROKEN); - priv->tx_chain = dma_alloc_coherent(TX_NUM_DESC * sizeof(struct xgmac_dma_desc), - DMA_ADDRESS_BROKEN); - - edev = &priv->edev; - edev->priv = priv; - - edev->open = xgmac_open; - edev->send = xgmac_send; - edev->recv = xgmac_recv; - edev->halt = xgmac_halt; - edev->get_ethaddr = xgmac_get_ethaddr; - edev->set_ethaddr = xgmac_set_ethaddr; - edev->parent = dev; - - eth_register(edev); - - return 0; -} - -static __maybe_unused struct of_device_id xgmac_dt_ids[] = { - { - .compatible = "calxeda,hb-xgmac", - }, { - /* sentinel */ - } -}; - -static struct driver_d hb_xgmac_driver = { - .name = "hb-xgmac", - .probe = hb_xgmac_probe, - .of_compatible = DRV_OF_COMPAT(xgmac_dt_ids), -}; -device_platform_driver(hb_xgmac_driver); |