diff options
Diffstat (limited to 'drivers/net')
99 files changed, 13244 insertions, 2503 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 803444cc44..13e9ff6924 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -40,6 +40,12 @@ config DRIVER_NET_AT91_ETHER depends on HAS_AT91_ETHER select PHYLIB +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 || COMPILE_TEST @@ -55,71 +61,84 @@ 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 || COMPILE_TEST + 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. config DRIVER_NET_DESIGNWARE_STARFIVE - bool "Designware Universal MAC ethernet driver for StarFive platforms" - depends on SOC_STARFIVE || COMPILE_TEST + 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 Univesal MAC 10M/100M/1G ethernet IP on StarFive. - -endif + 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. config DRIVER_NET_DESIGNWARE_ROCKCHIP - bool "Designware Universal MAC ethernet driver for Rockchip platforms" + 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 - -endif + This option enables support for the Ethernet MAC on different Rockchip SoCs config DRIVER_NET_DM9K bool "Davicom dm9k[E|A|B] ethernet driver" @@ -169,6 +188,15 @@ config DRIVER_NET_FEC_IMX 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 @@ -222,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 @@ -233,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. @@ -257,7 +285,7 @@ config DRIVER_NET_TAP 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" @@ -273,22 +301,6 @@ 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" - select PHYLIB - 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) - config DRIVER_NET_LITEETH bool "LiteX ethernet driver" select PHYLIB @@ -306,9 +318,17 @@ menuconfig DSA 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 + 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. @@ -329,6 +349,8 @@ config DRIVER_NET_SJA1105 - 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 fa3d4583a0..207345cfa3 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -6,6 +6,7 @@ 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_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 @@ -14,6 +15,7 @@ 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 @@ -23,9 +25,11 @@ 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 @@ -33,13 +37,13 @@ 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 6d1a90684c..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,7 +419,7 @@ 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, }; 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 73671ae2d4..31ca61a230 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -22,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 @@ -175,7 +175,7 @@ struct cpsw_slave { phy_interface_t phy_if; struct eth_device edev; struct cpsw_priv *cpsw; - struct device_d dev; + struct device dev; }; struct cpdma_desc { @@ -195,7 +195,7 @@ struct cpdma_chan { }; struct cpsw_priv { - struct device_d *dev; + struct device *dev; u32 version; struct cpsw_platform_data data; @@ -215,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; @@ -223,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; }; @@ -581,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; @@ -620,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) { @@ -653,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), @@ -801,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, int port) + void *sw_buffer, dma_addr_t hw_buffer, + int len, int port) { struct cpdma_desc *desc, *prev; u32 mode; @@ -820,10 +824,10 @@ static int cpdma_submit(struct cpsw_priv *priv, struct cpdma_chan *chan, (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) { @@ -850,7 +854,7 @@ done: } static int cpdma_process(struct cpsw_slave *slave, struct cpdma_chan *chan, - void **buffer, int *len) + void **buffer, dma_addr_t *dma, int *len) { struct cpdma_desc *desc = chan->head; struct cpsw_priv *priv = slave->cpsw; @@ -864,6 +868,8 @@ static int cpdma_process(struct cpsw_slave *slave, struct cpdma_chan *chan, if (len) *len = status & 0x7ff; + if (dma) + *dma = readl(&desc->hw_buffer); if (buffer) *buffer = (void *)readl(&desc->sw_buffer); @@ -915,7 +921,7 @@ static int cpsw_open(struct eth_device *edev) return 0; } -static int cpsw_setup(struct device_d *dev) +static int cpsw_setup(struct device *dev) { struct cpsw_priv *priv = dev->priv; int i, ret; @@ -979,8 +985,15 @@ static int cpsw_setup(struct device_d *dev) /* submit rx descs */ for (i = 0; i < PKTBUFSRX - 2; i++) { - ret = cpdma_submit(priv, &priv->rx_chan, NetRxPackets[i], - PKTSIZE, 0); + 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(dev, "error %d submitting rx desc\n", ret); break; @@ -1011,20 +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(slave, &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, + 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_sync_single_for_cpu((unsigned long)packet, length, DMA_TO_DEVICE); + dma_unmap_single(priv->dev, dma, length, DMA_TO_DEVICE); return ret; } @@ -1033,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(slave, &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, 0); + dma_sync_single_for_device(priv->dev, dma, len, DMA_FROM_DEVICE); + cpdma_submit(priv, &priv->rx_chan, buffer, dma, PKTSIZE, 0); } return 0; @@ -1062,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; @@ -1229,7 +1242,7 @@ static void cpsw_add_slave(struct cpsw_slave *slave, struct device_node *child, dev_warn(slave->cpsw->dev, "phy_id is deprecated, use phy-handle\n"); } - slave->dev.device_node = child; + slave->dev.of_node = child; slave->phy_id = phy_id[1]; slave->phy_if = of_get_phy_mode(child); slave->slave_num = i; @@ -1237,8 +1250,8 @@ static void cpsw_add_slave(struct cpsw_slave *slave, struct device_node *child, static int cpsw_legacy_probe_dt(struct cpsw_priv *priv) { - struct device_d *dev = priv->dev; - struct device_node *np = dev->device_node, *child; + 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); @@ -1265,8 +1278,8 @@ static int cpsw_legacy_probe_dt(struct cpsw_priv *priv) static int cpsw_switch_probe_dt(struct cpsw_priv *priv) { - struct device_d *dev = priv->dev; - struct device_node *np = dev->device_node, *child; + struct device *dev = priv->dev; + struct device_node *np = dev->of_node, *child; struct device_node *ports = NULL; int ret, i = 0; @@ -1298,7 +1311,7 @@ static int cpsw_switch_probe_dt(struct cpsw_priv *priv) static int cpsw_probe_dt(struct cpsw_priv *priv) { - struct device_d *dev = priv->dev; + struct device *dev = priv->dev; struct device_node *physel; int (*probe_slaves_dt)(struct cpsw_priv *priv); int ret, i = 0; @@ -1326,7 +1339,7 @@ static int cpsw_probe_dt(struct cpsw_priv *priv) return 0; } -static int cpsw_probe(struct device_d *dev) +static int cpsw_probe(struct device *dev) { struct resource *iores; struct cpsw_platform_data *data = (struct cpsw_platform_data *)dev->platform_data; @@ -1342,14 +1355,18 @@ static int cpsw_probe(struct device_d *dev) return PTR_ERR(iores); regs = IOMEM(iores->start); - ret = of_platform_populate(dev->device_node, NULL, dev); + 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; @@ -1410,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; @@ -1433,8 +1450,9 @@ static __maybe_unused struct of_device_id cpsw_dt_ids[] = { /* 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 afc275e81e..6936c844cd 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -156,7 +156,7 @@ static void rx_descs_init(struct eth_device *dev) else desc_p->dmamac_cntl |= DESC_RXCTRL_RXCHAIN; - dma_sync_single_for_cpu(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; } @@ -286,7 +286,7 @@ static int dwc_ether_send(struct eth_device *dev, void *packet, int length) } memcpy(dmamac_addr(desc_p), packet, length); - dma_sync_single_for_device(desc_p->dmamac_addr, 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(desc_p->dmamac_addr, length, + dma_sync_single_for_cpu(dev->parent, desc_p->dmamac_addr, length, DMA_TO_DEVICE); return 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(desc_p->dmamac_addr, + dma_sync_single_for_cpu(dev->parent, desc_p->dmamac_addr, length, DMA_FROM_DEVICE); net_receive(dev, dmamac_addr(desc_p), length); - dma_sync_single_for_device(desc_p->dmamac_addr, + 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; @@ -526,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 8f6234aec5..5b587fa59e 100644 --- a/drivers/net/designware.h +++ b/drivers/net/designware.h @@ -57,8 +57,8 @@ static inline dma_addr_t rx_dma_addr(struct dw_eth_dev *priv, + ((u8 *)desc - (u8 *)priv->rx_mac_descrtable_cpu); } -struct dw_eth_dev *dwc_drv_probe(struct device_d *dev); -void dwc_drv_remove(struct device_d *dev); +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 diff --git a/drivers/net/designware_eqos.c b/drivers/net/designware_eqos.c index 6b372e4274..ccce51b6af 100644 --- a/drivers/net/designware_eqos.c +++ b/drivers/net/designware_eqos.c @@ -9,7 +9,7 @@ #include <common.h> #include <init.h> #include <gpio.h> -#include <gpiod.h> +#include <linux/gpio/consumer.h> #include <dma.h> #include <net.h> #include <of_net.h> @@ -26,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 */ @@ -62,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) @@ -168,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) @@ -194,23 +196,23 @@ struct eqos_desc { #define MII_BUSY (1 << 0) -static int eqos_phy_reset(struct device_d *dev, struct eqos *eqos) +static int eqos_phy_reset(struct device *dev, struct eqos *eqos) { - int phy_reset; + struct gpio_desc *phy_reset; u32 delays[3] = { 0, 0, 0 }; - phy_reset = gpiod_get(dev, "snps,reset", GPIOF_OUT_INIT_ACTIVE); - - if (!gpio_is_valid(phy_reset)) - return 0; - - of_property_read_u32_array(dev->device_node, - "snps,reset-delays-us", - delays, ARRAY_SIZE(delays)); - - udelay(delays[1]); - gpio_set_active(phy_reset, false); - udelay(delays[2]); + 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; } @@ -347,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); @@ -357,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. */ @@ -384,7 +414,7 @@ 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; @@ -395,6 +425,10 @@ static int eqos_start(struct eth_device *edev) 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, @@ -405,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); @@ -588,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); @@ -620,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; } @@ -635,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); @@ -679,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; @@ -689,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; @@ -709,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"); @@ -720,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; @@ -756,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; @@ -774,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; } @@ -799,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; @@ -815,26 +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->device_node; + struct device_node *np = dev->of_node; struct mii_bus *miibus; struct resource *iores; struct eqos *eqos; @@ -867,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; @@ -874,9 +918,9 @@ int eqos_probe(struct device_d *dev, const struct eqos_ops *ops, void *priv) miibus->write = eqos_mdio_write; miibus->priv = eqos; - miibus->dev.device_node = of_get_compatible_child(np, "snps,dwmac-mdio"); - if (!miibus->dev.device_node) - miibus->dev.device_node = of_get_child_by_name(np, "mdio"); + 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) @@ -893,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 be7bead09e..951565e8f9 100644 --- a/drivers/net/designware_eqos.h +++ b/drivers/net/designware_eqos.h @@ -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 90fca1951d..fc9f0745f7 100644 --- a/drivers/net/designware_generic.c +++ b/drivers/net/designware_generic.c @@ -16,7 +16,7 @@ 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; @@ -35,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 index a3859dce0c..04e2b7f12d 100644 --- a/drivers/net/designware_rockchip.c +++ b/drivers/net/designware_rockchip.c @@ -4,7 +4,7 @@ #include <init.h> #include <dma.h> #include <net.h> -#include <regmap.h> +#include <linux/regmap.h> #include <of_net.h> #include <mfd/syscon.h> #include <linux/iopoll.h> @@ -33,7 +33,7 @@ struct eqos_rk_gmac { int bus_id; u32 tx_delay; u32 rx_delay; - struct device_d *dev; + struct device *dev; }; enum { @@ -101,7 +101,7 @@ 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_d *dev = priv->dev; + struct device *dev = priv->dev; u32 offset_con0, offset_con1; if (IS_ERR(priv->grf)) { @@ -127,7 +127,7 @@ static void rk3568_set_to_rgmii(struct eqos *eqos, static void rk3568_set_to_rmii(struct eqos *eqos) { struct eqos_rk_gmac *priv = to_rk_gmac(eqos); - struct device_d *dev = priv->dev; + struct device *dev = priv->dev; u32 offset_con1; if (IS_ERR(priv->grf)) { @@ -145,7 +145,7 @@ static void rk3568_set_to_rmii(struct eqos *eqos) static void rk3568_set_gmac_speed(struct eqos *eqos, int speed) { struct eqos_rk_gmac *priv = to_rk_gmac(eqos); - struct device_d *dev = priv->dev; + struct device *dev = priv->dev; unsigned long rate; int ret; @@ -185,7 +185,7 @@ static const struct rk_gmac_ops rk3568_ops = { static int rk_gmac_powerup(struct eqos *eqos) { struct eqos_rk_gmac *priv = to_rk_gmac(eqos); - struct device_d *dev = priv->dev; + struct device *dev = priv->dev; /*rmii or rgmii*/ switch (eqos->interface) { @@ -230,9 +230,9 @@ static void eqos_rk_adjust_link(struct eth_device *edev) eqos_adjust_link(edev); } -static int eqos_init_rk_gmac(struct device_d *dev, struct eqos *eqos) +static int eqos_init_rk_gmac(struct device *dev, struct eqos *eqos) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct eqos_rk_gmac *priv = to_rk_gmac(eqos); int i = 0, ret; const char *strings; @@ -306,7 +306,7 @@ static struct eqos_ops rk_gmac_ops = { .config_mac = EQOS_MAC_RXQ_CTRL0_RXQ0EN_ENABLED_AV, }; -static int rk_gmac_probe(struct device_d *dev) +static int rk_gmac_probe(struct device *dev) { return eqos_probe(dev, &rk_gmac_ops, xzalloc(sizeof(struct eqos_rk_gmac))); } @@ -319,8 +319,9 @@ static __maybe_unused struct of_device_id rk_gmac_compatible[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, rk_gmac_compatible); -static struct driver_d rk_gmac_driver = { +static struct driver rk_gmac_driver = { .name = "eqos-rockchip", .probe = rk_gmac_probe, .remove = eqos_remove, 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 index 3dc9d14e11..aff2cc10e1 100644 --- a/drivers/net/designware_starfive.c +++ b/drivers/net/designware_starfive.c @@ -6,6 +6,7 @@ #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> @@ -56,7 +57,7 @@ static struct dw_eth_drvdata starfive_drvdata = { .fix_mac_speed = dwmac_fixed_speed, }; -static int starfive_dwc_ether_probe(struct device_d *dev) +static int starfive_dwc_ether_probe(struct device *dev) { struct dw_eth_dev *dwc; struct regmap *regmap; @@ -67,7 +68,8 @@ static int starfive_dwc_ether_probe(struct device_d *dev) { .id = "tx" }, }; - regmap = syscon_regmap_lookup_by_phandle(dev->device_node, "starfive,sysmain"); + 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); @@ -101,8 +103,9 @@ 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_d starfive_dwc_ether_driver = { +static struct driver starfive_dwc_ether_driver = { .name = "starfive-designware_eth", .probe = starfive_dwc_ether_probe, .of_compatible = starfive_dwc_ether_compatible, diff --git a/drivers/net/designware_stm32.c b/drivers/net/designware_stm32.c index 0a5ced98f5..54dabcc8d3 100644 --- a/drivers/net/designware_stm32.c +++ b/drivers/net/designware_stm32.c @@ -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 0241b9ad16..86f97e853e 100644 --- a/drivers/net/designware_tegra186.c +++ b/drivers/net/designware_tegra186.c @@ -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; @@ -217,7 +217,7 @@ static int eqos_init_tegra186(struct device_d *dev, struct eqos *eqos) 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) @@ -283,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); @@ -308,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 index d9e629cefc..ccd7d87550 100644 --- a/drivers/net/dsa.c +++ b/drivers/net/dsa.c @@ -54,12 +54,12 @@ static int dsa_port_probe(struct eth_device *edev) { struct dsa_port *dp = edev->priv; struct dsa_switch *ds = dp->ds; - const struct dsa_ops *ops = ds->ops; + 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.device_node); + interface = of_get_phy_mode(dp->dev->of_node); ret = ops->port_probe(dp, dp->index, interface); if (ret) return ret; @@ -86,14 +86,14 @@ static int dsa_port_start(struct eth_device *edev) { struct dsa_port *dp = edev->priv; struct dsa_switch *ds = dp->ds; - const struct dsa_ops *ops = ds->ops; + 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.device_node); + 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 @@ -104,17 +104,13 @@ static int dsa_port_start(struct eth_device *edev) return ret; } - ret = phy_device_connect(edev, ds->slave_mii_bus, dp->index, NULL, 0, - interface); + 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); - ret = phy_wait_aneg_done(dp->edev.phydev); - if (ret) - return ret; - if (ops->port_enable) { ret = ops->port_enable(dp, dp->index, dp->edev.phydev); if (ret) @@ -126,6 +122,16 @@ static int dsa_port_start(struct eth_device *edev) 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); @@ -133,6 +139,11 @@ static int dsa_port_start(struct eth_device *edev) 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); } @@ -146,7 +157,7 @@ static void dsa_port_stop(struct eth_device *edev) { struct dsa_port *dp = edev->priv; struct dsa_switch *ds = dp->ds; - const struct dsa_ops *ops = ds->ops; + const struct dsa_switch_ops *ops = ds->ops; if (!dp->enabled) return; @@ -164,6 +175,7 @@ static void dsa_port_stop(struct eth_device *edev) ops->port_disable(dpc, ds->cpu_port, ds->cpu_port_fixed_phy); + eth_set_promisc(ds->edev_master, false); eth_close(ds->edev_master); } } @@ -172,7 +184,7 @@ 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_ops *ops = ds->ops; + const struct dsa_switch_ops *ops = ds->ops; void *tx_buf = ds->tx_buf; size_t full_length, stuff = 0; int ret; @@ -233,35 +245,36 @@ static int dsa_ether_get_ethaddr(struct eth_device *edev, unsigned char *adr) return edev_master->get_ethaddr(edev_master, adr); } -static int dsa_switch_register_edev(struct dsa_switch *ds, - struct device_node *dn, int port) +static struct dsa_port *dsa_port_alloc(struct dsa_switch *ds, + struct device_node *dn, int port) { - struct eth_device *edev; - struct device_d *dev; + struct device *dev; struct dsa_port *dp; - int ret; ds->dp[port] = xzalloc(sizeof(*dp)); - dp = ds->dp[port]; - dev = &dp->dev; - dev_set_name(dev, "dsa_port"); - dev->id = DEVICE_ID_DYNAMIC; - dev->parent = ds->dev; - dev->device_node = dn; + dev = of_platform_device_create(dn, ds->dev); + of_platform_device_dummy_drv(dev); + dp->dev = dev; + dp->ds = ds; + dp->index = port; - ret = register_device(dev); - if (ret) - return ret; + 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); - dp->ds = ds; - dp->index = port; edev = &dp->edev; edev->priv = dp; - edev->parent = dev; + edev->parent = dp->dev; edev->init = dsa_port_probe; edev->open = dsa_port_start; edev->send = dsa_port_send; @@ -277,7 +290,7 @@ 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_ops *ops = ds->ops; + const struct dsa_switch_ops *ops = ds->ops; struct dsa_port *dp; int ret, port; @@ -314,7 +327,6 @@ static int dsa_switch_register_master(struct dsa_switch *ds, { struct device_node *phy_node; struct phy_device *phydev; - struct dsa_port *dp; int ret; if (ds->edev_master) { @@ -345,9 +357,7 @@ static int dsa_switch_register_master(struct dsa_switch *ds, phydev->interface = of_get_phy_mode(np); - ds->dp[port] = xzalloc(sizeof(*dp)); - dp = ds->dp[port]; - dp->ds = ds; + dsa_port_alloc(ds, np, port); ds->cpu_port = port; ds->cpu_port_fixed_phy = phydev; @@ -383,15 +393,18 @@ static int dsa_switch_parse_ports_of(struct dsa_switch *ds, } if (reg >= ds->num_ports) { - dev_err(ds->dev, "port %pOF index %u exceeds num_ports (%u)\n", + 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) - dsa_switch_register_master(ds, port, master, reg); + if (master) { + ret = dsa_switch_register_master(ds, port, master, reg); + if (ret) + return ret; + } } /* Now we can register regular switch ports */ @@ -422,7 +435,7 @@ int dsa_register_switch(struct dsa_switch *ds) return -ENODEV; } - if (!ds->dev->device_node) + if (!ds->dev->of_node) return -ENODEV; if (!ds->num_ports || ds->num_ports > DSA_MAX_PORTS) { @@ -430,7 +443,7 @@ int dsa_register_switch(struct dsa_switch *ds) return -EINVAL; } - ret = dsa_switch_parse_ports_of(ds, ds->dev->device_node); + ret = dsa_switch_parse_ports_of(ds, ds->dev->of_node); if (ret) return ret; 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 1072bc54ef..effe0c6cff 100644 --- a/drivers/net/e1000/eeprom.c +++ b/drivers/net/e1000/eeprom.c @@ -410,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; @@ -495,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: @@ -502,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; @@ -513,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: @@ -800,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; } @@ -816,8 +816,8 @@ 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; 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/efi-snp.c b/drivers/net/efi-snp.c index 9cb7144187..476015f1c2 100644 --- a/drivers/net/efi-snp.c +++ b/drivers/net/efi-snp.c @@ -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) @@ -163,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; @@ -172,7 +173,7 @@ 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; } @@ -219,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)); @@ -285,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], diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c index b3cfb65c1c..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; }; /* @@ -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); @@ -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); } /* @@ -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/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 0c2d600d12..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 */ @@ -562,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)) @@ -574,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); } @@ -625,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; } @@ -730,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; @@ -762,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)); @@ -797,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) @@ -828,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 @@ -839,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) { @@ -861,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: @@ -902,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; @@ -935,6 +961,7 @@ static __maybe_unused struct of_device_id imx_fec_dt_ids[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, imx_fec_dt_ids); static struct platform_device_id imx_fec_ids[] = { { @@ -954,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 316eefe48f..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 */ @@ -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 4025664d0d..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; } -static 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 @@ static 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/fsl-fman.c b/drivers/net/fsl-fman.c index 4d72933105..5262928480 100644 --- a/drivers/net/fsl-fman.c +++ b/drivers/net/fsl-fman.c @@ -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 72ab86579b..c4c30377af 100644 --- a/drivers/net/ksz8864rmn.c +++ b/drivers/net/ksz8864rmn.c @@ -122,7 +122,7 @@ 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; @@ -187,7 +187,7 @@ static const struct platform_device_id ksz_ids[] = { { } }; -static struct driver_d micrel_switch_driver = { +static struct driver micrel_switch_driver = { .name = "ksz8864rmn", .probe = micrel_switch_probe, .id_table = ksz_ids, 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 index 779762fad5..1abea9d040 100644 --- a/drivers/net/ksz9477.c +++ b/drivers/net/ksz9477.c @@ -3,17 +3,17 @@ #include <common.h> #include <complete.h> #include <dsa.h> -#include <gpiod.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 KS_SPIOP_RD 3 -#define KS_SPIOP_WR 2 #define SPI_ADDR_SHIFT 24 -#define SPI_ADDR_MASK (BIT(SPI_ADDR_SHIFT) - 1) +#define SPI_ADDR_ALIGN 3 #define SPI_TURNAROUND_SHIFT 5 #define GBIT_SUPPORT BIT(0) @@ -21,131 +21,13 @@ #define IS_9893 BIT(2) #define KSZ9477_PHY_ERRATA BIT(3) -struct ksz_switch { - struct spi_device *spi; - struct dsa_switch ds; - struct device_d *dev; - int phy_port_cnt; - u32 chip_id; - u8 features; -}; - -static int ksz9477_spi_read_reg(struct spi_device *spi, u32 reg, u8 *val, - unsigned int len) -{ - u32 txbuf; - int ret; - - txbuf = reg & SPI_ADDR_MASK; - txbuf |= KS_SPIOP_RD << SPI_ADDR_SHIFT; - txbuf <<= SPI_TURNAROUND_SHIFT; - txbuf = cpu_to_be32(txbuf); - - ret = spi_write_then_read(spi, &txbuf, 4, val, len); - - return ret; -} - -static int ksz9477_spi_write_reg(struct spi_device *spi, u32 reg, u8 *val, - unsigned int len) -{ - u32 txbuf[2]; - - txbuf[0] = reg & SPI_ADDR_MASK; - txbuf[0] |= (KS_SPIOP_WR << SPI_ADDR_SHIFT); - txbuf[0] <<= SPI_TURNAROUND_SHIFT; - txbuf[0] = cpu_to_be32(*txbuf); - memcpy(&txbuf[1], val, len); - - return spi_write(spi, txbuf, 4 + len); -} - -static int ksz_read8(struct ksz_switch *priv, u32 reg, u8 *val) -{ - return ksz9477_spi_read_reg(priv->spi, reg, val, 1); -} - -static int ksz_write8(struct ksz_switch *priv, u32 reg, u8 value) -{ - return ksz9477_spi_write_reg(priv->spi, reg, &value, 1); -} - -static int ksz_read16(struct ksz_switch *priv, u32 reg, u16 *val) -{ - int ret = ksz9477_spi_read_reg(priv->spi, reg, (u8 *)val, 2); - - if (!ret) - *val = be16_to_cpu(*val); - - return ret; -} - -static int ksz_write16(struct ksz_switch *priv, u32 reg, u16 value) -{ - struct spi_device *spi = priv->spi; - - value = cpu_to_be16(value); - return ksz9477_spi_write_reg(spi, reg, (u8 *)&value, 2); -} - -static int ksz_read32(struct ksz_switch *priv, u32 reg, u32 *val) -{ - int ret = ksz9477_spi_read_reg(priv->spi, reg, (u8 *)val, 4); - - if (!ret) - *val = be32_to_cpu(*val); - - return ret; -} - -static int ksz_write32(struct ksz_switch *priv, u32 reg, u32 value) -{ - struct spi_device *spi = priv->spi; - - value = cpu_to_be32(value); - return ksz9477_spi_write_reg(spi, reg, (u8 *)&value, 4); -} - -static void ksz_cfg(struct ksz_switch *priv, u32 addr, u8 bits, bool set) -{ - u8 data; - - ksz_read8(priv, addr, &data); - if (set) - data |= bits; - else - data &= ~bits; - ksz_write8(priv, addr, data); -} - -static int ksz_pread8(struct ksz_switch *priv, int port, int reg, u8 *val) -{ - return ksz_read8(priv, PORT_CTRL_ADDR(port, reg), val); -} - -static int ksz_pwrite8(struct ksz_switch *priv, int port, int reg, u8 val) -{ - return ksz_write8(priv, PORT_CTRL_ADDR(port, reg), val); -} - -static int ksz_pread16(struct ksz_switch *priv, int port, int reg, u16 *val) -{ - return ksz_read16(priv, PORT_CTRL_ADDR(port, reg), val); -} - -static int ksz_pwrite16(struct ksz_switch *priv, int port, int reg, u16 val) -{ - return ksz_write16(priv, PORT_CTRL_ADDR(port, reg), val); -} - -static int ksz_pwrite32(struct ksz_switch *priv, int port, int reg, u32 val) -{ - return ksz_write32(priv, PORT_CTRL_ADDR(port, reg), val); -} +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_d *dev = ds->dev; + struct device *dev = ds->dev; struct ksz_switch *priv = dev_get_priv(dev); u16 val = 0xffff; @@ -160,7 +42,7 @@ static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg) static int ksz9477_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) { - struct device_d *dev = ds->dev; + struct device *dev = ds->dev; struct ksz_switch *priv = dev_get_priv(dev); /* No real PHY after this. */ @@ -200,7 +82,7 @@ static int ksz9477_switch_detect(struct ksz_switch *priv) id_lo = (u8)(id32 >> 8); if ((id_lo & 0xf) == 3) { /* Chip is from KSZ9893 design. */ - dev_info(priv->dev, "Found KSZ9893\n"); + dev_info(priv->dev, "Found KSZ9893 or compatible\n"); priv->features |= IS_9893; priv->features &= ~KSZ9477_PHY_ERRATA; @@ -293,12 +175,6 @@ static void ksz9477_phy_errata_setup(struct ksz_switch *priv, int port) */ ksz9477_port_mmd_write(priv, port, 0x1c, 0x04, 0x00d0); - /* Energy Efficient Ethernet (EEE) feature select must - * be manually disabled (except on KSZ8565 which is 100Mbit) - */ - if (priv->features & GBIT_SUPPORT) - ksz9477_port_mmd_write(priv, port, 0x07, 0x3c, 0x0000); - /* Register settings are required to meet data sheet * supply current specifications */ @@ -387,6 +263,8 @@ static int ksz_port_setup(struct ksz_switch *priv, int port, 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 { @@ -431,7 +309,7 @@ static int ksz_port_setup(struct ksz_switch *priv, int port, static int ksz_port_enable(struct dsa_port *dp, int port, struct phy_device *phy) { - struct device_d *dev = dp->ds->dev; + struct device *dev = dp->ds->dev; struct ksz_switch *priv = dev_get_priv(dev); u8 data8; int ret; @@ -479,7 +357,7 @@ static int ksz_recv(struct dsa_switch *ds, int *port, void *packet, int length) return 0; }; -static const struct dsa_ops ksz_dsa_ops = { +static const struct dsa_switch_ops ksz_dsa_ops = { .port_enable = ksz_port_enable, .xmit = ksz_xmit, .rcv = ksz_recv, @@ -503,10 +381,33 @@ static int ksz_default_setup(struct ksz_switch *priv) return 0; } -static int microchip_switch_probe(struct device_d *dev) +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; - int ret = 0, gpio; + struct gpio_desc *gpio; + int ret = 0; struct dsa_switch *ds; priv = xzalloc(sizeof(*priv)); @@ -514,22 +415,33 @@ static int microchip_switch_probe(struct device_d *dev) dev->priv = priv; priv->dev = dev; - priv->spi = (struct spi_device *)dev->type_data; - priv->spi->mode = SPI_MODE_0; - priv->spi->bits_per_word = 8; + 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; + } - gpio = gpiod_get(dev, "reset", GPIOF_OUT_INIT_ACTIVE); - if (gpio_is_valid(gpio)) { + 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); - gpio_set_active(gpio, false); + gpiod_set_value(gpio, false); } ksz_reset_switch(dev->priv); ret = ksz9477_switch_detect(dev->priv); if (ret) { - dev_err(&priv->spi->dev, "error detecting KSZ9477: %s\n", - strerror(-ret)); + dev_err(hw_dev, "error detecting KSZ9477: %pe\n", ERR_PTR(ret)); return -ENODEV; } @@ -548,19 +460,40 @@ static int microchip_switch_probe(struct device_d *dev) ksz_default_setup(priv); - return dsa_register_switch(ds); + 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_d microchip_switch_driver = { - .name = "ksz9477", +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); -device_spi_driver(microchip_switch_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 index 771f57eead..1781e26348 100644 --- a/drivers/net/liteeth.c +++ b/drivers/net/liteeth.c @@ -53,7 +53,7 @@ #define MAX_PKT_SIZE LITEETH_BUFFER_SIZE struct liteeth { - struct device_d *dev; + struct device *dev; struct eth_device edev; void __iomem *base; void __iomem *mdio_base; @@ -73,6 +73,8 @@ struct liteeth { int rx_slot; int num_rx_slots; void __iomem *rx_base; + + void *rx_buf; }; static inline void litex_write8(void __iomem *addr, u8 val) @@ -230,9 +232,9 @@ static int liteeth_eth_rx(struct eth_device *edev) rx_slot = litex_read8(priv->base + LITEETH_WRITER_SLOT); - memcpy(NetRxPackets[0], priv->rx_base + rx_slot * LITEETH_BUFFER_SIZE, len); + memcpy(priv->rx_buf, priv->rx_base + rx_slot * LITEETH_BUFFER_SIZE, len); - net_receive(edev, NetRxPackets[0], len); + net_receive(edev, priv->rx_buf, len); litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, reg); @@ -270,9 +272,9 @@ static int liteeth_set_ethaddr(struct eth_device *edev, return 0; } -static int liteeth_probe(struct device_d *dev) +static int liteeth_probe(struct device *dev) { - struct device_node *np = dev->device_node; + struct device_node *np = dev->of_node; struct eth_device *edev; void __iomem *buf_base; struct liteeth *priv; @@ -323,6 +325,8 @@ static int liteeth_probe(struct device_d *dev) 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; @@ -365,8 +369,9 @@ static const struct of_device_id liteeth_dt_ids[] = { }, { } }; +MODULE_DEVICE_TABLE(of, liteeth_dt_ids); -static struct driver_d liteeth_driver = { +static struct driver liteeth_driver = { .name = DRV_NAME, .probe = liteeth_probe, .of_compatible = DRV_OF_COMPAT(liteeth_dt_ids), diff --git a/drivers/net/macb.c b/drivers/net/macb.c index 511846122a..f5b2fa74dc 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -53,7 +53,7 @@ #define GEM_Q1_DESC_BYTES (sizeof(struct macb_dma_desc) * GEM_Q1_DESCS) struct macb_config { - int (*txclk_init)(struct device_d *dev, struct clk **tx_clk); + int (*txclk_init)(struct device *dev, struct clk **tx_clk); }; struct macb_device { @@ -63,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; @@ -74,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; @@ -104,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); @@ -115,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"); @@ -150,44 +157,40 @@ static void reclaim_rx_buffers(struct macb_device *macb, 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; 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,18 +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; 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); @@ -220,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; @@ -228,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); @@ -367,9 +366,9 @@ 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, (ulong)macb->gem_q1_descs, i - 1); @@ -377,7 +376,7 @@ static int gmac_init_dummy_tx_queues(struct macb_device *macb) 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; @@ -386,21 +385,26 @@ static void macb_init(struct macb_device *macb) * 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; @@ -413,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]); } @@ -442,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) @@ -460,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) @@ -730,7 +741,7 @@ static const struct clk_ops fu540_c000_ops = { .set_rate = fu540_macb_tx_set_rate, }; -static int fu540_c000_txclk_init(struct device_d *dev, struct clk **tx_clk) +static int fu540_c000_txclk_init(struct device *dev, struct clk **tx_clk) { struct clk *clk; struct resource *res; @@ -766,13 +777,13 @@ static int fu540_c000_txclk_init(struct device_d *dev, struct clk **tx_clk) return 0; } #else -static int fu540_c000_txclk_init(struct device_d *dev, struct clk **tx_clk) +static int fu540_c000_txclk_init(struct device *dev, struct clk **tx_clk) { return -ENOSYS; } #endif -static int macb_probe(struct device_d *dev) +static int macb_probe(struct device *dev) { struct resource *iores; struct eth_device *edev; @@ -780,6 +791,7 @@ static int macb_probe(struct device_d *dev) const char *pclk_name, *hclk_name; const struct macb_config *config = NULL; u32 ncfgr; + int ret; macb = xzalloc(sizeof(*macb)); edev = &macb->netdev; @@ -815,19 +827,19 @@ 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"; @@ -877,7 +889,7 @@ static int macb_probe(struct device_d *dev) clk_enable(macb->rxclk); if (config) { - int ret = config->txclk_init(dev, &macb->txclk); + ret = config->txclk_init(dev, &macb->txclk); if (ret) return ret; } @@ -891,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 */ @@ -905,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); @@ -916,11 +932,13 @@ 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 = { @@ -931,13 +949,15 @@ 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 cd20e1de27..8e12671801 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -35,6 +35,12 @@ config DP83TD510_PHY 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 @@ -50,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 diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 83f46f11d3..ce15e1bab7 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -6,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 @@ -18,4 +19,5 @@ 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 f13d574b30..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) diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index 93a8bb9df1..8d6b879a27 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -32,6 +32,9 @@ #define AT803X_DEBUG_REG_5 0x05 #define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) +#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. @@ -59,6 +62,9 @@ */ #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 @@ -128,6 +134,15 @@ static int at803x_disable_tx_delay(struct phy_device *phydev) 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); @@ -138,8 +153,8 @@ static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id) static int at803x_parse_dt(struct phy_device *phydev) { - const struct device_d *dev = &phydev->dev; - const struct device_node *node = dev->device_node; + 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; @@ -221,6 +236,12 @@ static int at803x_probe(struct phy_device *phydev) 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; @@ -229,14 +250,14 @@ static int at803x_clk_out_config(struct phy_device *phydev) if (!priv->clk_25m_mask) return 0; - val = phy_read_mmd_indirect(phydev, AT803X_MMD7_CLK25M, MDIO_MMD_AN); + 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_indirect(phydev, AT803X_MMD7_CLK25M, MDIO_MMD_AN, val); + phy_write_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, val); return 0; } @@ -270,7 +291,25 @@ static int at803x_config_init(struct phy_device *phydev) if (ret < 0) return ret; - return at803x_clk_out_config(phydev); + 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[] = { diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c index 2435df1b47..5dc5bac125 100644 --- a/drivers/net/phy/dp83867.c +++ b/drivers/net/phy/dp83867.c @@ -1,6 +1,5 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * 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. */ @@ -13,23 +12,43 @@ #include <linux/mdio.h> #define DP83867_PHY_ID 0x2000a231 -#define DP83867_DEVADDR MDIO_MMD_VEND2 +#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) @@ -52,53 +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) - -/* PHY STS bits */ -#define DP83867_PHYSTS_SPEED_1000 BIT(15) -#define DP83867_PHYSTS_SPEED_100 BIT(14) -#define DP83867_PHYSTS_DUPLEX_FULL BIT(13) +#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 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_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, @@ -107,39 +159,41 @@ 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_read_status(struct phy_device *phydev) { - int status; + int status = phy_read(phydev, MII_DP83867_PHYSTS); int ret; - ret = genphy_update_link(phydev); + ret = genphy_read_status(phydev); if (ret) return ret; - status = phy_read(phydev, MII_DP83867_PHYSTS); if (status < 0) return status; - phydev->speed = SPEED_10; - phydev->duplex = DUPLEX_HALF; + if (status & DP83867_PHYSTS_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; - if (status & DP83867_PHYSTS_SPEED_1000) + if (status & DP83867_PHYSTS_1000) phydev->speed = SPEED_1000; - else if (status & DP83867_PHYSTS_SPEED_100) + else if (status & DP83867_PHYSTS_100) phydev->speed = SPEED_100; - - if (status & DP83867_PHYSTS_DUPLEX_FULL) - phydev->duplex = DUPLEX_FULL; - - phydev->pause = phydev->asym_pause = 0; + else + phydev->speed = SPEED_10; return 0; } @@ -147,16 +201,70 @@ static int dp83867_read_status(struct phy_device *phydev) static int dp83867_config_port_mirroring(struct phy_device *phydev) { struct dp83867_private *dp83867 = phydev->priv; - u16 val; - - val = phy_read_mmd_indirect(phydev, DP83867_CFG4, DP83867_DEVADDR); 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; } @@ -164,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; + 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; + 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; @@ -201,125 +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; + } + + 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; + 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; - phydev->priv = dp83867; - ret = dp83867_of_init(phydev); + ret = dp83867_verify_rgmii_cfg(phydev); + if (ret) + return ret; + + /* 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 = 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; + + 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 (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); + ret = phy_write(phydev, MII_DP83867_PHYCTRL, val); + if (ret) + return ret; } if (phy_interface_is_rgmii(phydev)) { - val = DP83867_MDI_CROSSOVER_AUTO << DP83867_MDI_CROSSOVER | - dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT; + 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; - val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR); + /* 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); - 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: + 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); - } - } else if (phy_interface_is_sgmii(phydev)) { - phy_write(phydev, MII_BMCR, - BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000); + /* 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; - phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, - DP83867_DEVADDR, 0x0); + /* 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); - 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; + if (ret) + return ret; - phy_write(phydev, MII_DP83867_PHYCTRL, val); - phy_write(phydev, MII_DP83867_BISCR, 0x0); + 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)); } - val = phy_read(phydev, MII_DP83867_CFG2); - val |= DP83867_DOWNSHIFT_EN; - phy_write(phydev, MII_DP83867_CFG2, val); + 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); + /* 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, - .config_init = dp83867_config_init, - .read_status = dp83867_read_status, + .phy_id = DP83867_PHY_ID, + .phy_id_mask = 0xfffffff0, + .drv.name = "TI DP83867", + .features = PHY_GBIT_FEATURES, + + .probe = dp83867_probe, + .config_init = dp83867_config_init, + + .read_status = dp83867_read_status, }, }; - device_phy_drivers(dp83867_driver); + +MODULE_DESCRIPTION("Texas Instruments DP83867 PHY driver"); +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); +MODULE_LICENSE("GPL v2"); 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/marvell.c b/drivers/net/phy/marvell.c index d81632dfc7..c0b819b109 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -188,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; 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 80d2394f4b..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,7 +50,7 @@ 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 information from DT\n"); goto free_info; @@ -59,7 +59,7 @@ static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev) 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 information from DT\n"); goto free_info; @@ -68,7 +68,7 @@ static struct mdio_gpio_info *mdio_gpio_of_get_info(struct device_d *dev) 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 1e6278ef35..c4088c16ca 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -56,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, @@ -117,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 99d23ffedf..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; @@ -248,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; @@ -333,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; @@ -349,14 +415,18 @@ 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; } @@ -439,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)) @@ -470,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); @@ -524,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 cf593ee6a6..a203669353 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -128,8 +128,8 @@ static int kszphy_rmii_clk_sel(struct phy_device *phydev, bool val) /* 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_d *dev = &phydev->dev; - const struct device_node *of_node = dev->device_node ? : dev->parent->device_node; + 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)) { @@ -144,7 +144,7 @@ static int kszphy_led_mode(struct phy_device *phydev, int reg, int shift) static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val) { - const struct device_d *dev = &phydev->dev; + const struct device *dev = &phydev->dev; int rc, temp, shift; switch (reg) { @@ -179,7 +179,7 @@ out: */ static int kszphy_broadcast_disable(struct phy_device *phydev) { - const struct device_d *dev = &phydev->dev; + const struct device *dev = &phydev->dev; int ret; ret = phy_read(phydev, MII_KSZPHY_OMSO); @@ -196,7 +196,7 @@ out: static int kszphy_nand_tree_disable(struct phy_device *phydev) { - const struct device_d *dev = &phydev->dev; + const struct device *dev = &phydev->dev; int ret; ret = phy_read(phydev, MII_KSZPHY_OMSO); @@ -278,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" @@ -293,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, @@ -387,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, MDIO_MMD_WIS); + newval = phy_read_mmd(phydev, MDIO_MMD_WIS, reg); else newval = 0; @@ -401,15 +401,15 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev, << (field_sz * i)); } - phy_write_mmd_indirect(phydev, reg, MDIO_MMD_WIS, 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); } @@ -447,32 +447,32 @@ static int ksz9031_config_rgmii_delay(struct phy_device *phydev) return 0; } - phy_write_mmd_indirect(phydev, MII_KSZ9031RN_CONTROL_PAD_SKEW, 2, - FIELD_PREP(MII_KSZ9031RN_RX_CTL_M, rx) | - FIELD_PREP(MII_KSZ9031RN_TX_CTL_M, tx)); - - phy_write_mmd_indirect(phydev, MII_KSZ9031RN_RX_DATA_PAD_SKEW, 2, - 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_indirect(phydev, MII_KSZ9031RN_TX_DATA_PAD_SKEW, 2, - 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_indirect(phydev, MII_KSZ9031RN_CLK_PAD_SKEW, 2, - FIELD_PREP(MII_KSZ9031RN_GTX_CLK, tx_clk) | - FIELD_PREP(MII_KSZ9031RN_RX_CLK, rx_clk)); + 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", @@ -485,8 +485,8 @@ 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)) { @@ -546,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) @@ -554,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); @@ -611,8 +791,8 @@ static int ksz8873mll_config_init(struct phy_device *phydev) static int kszphy_probe(struct phy_device *phydev) { - struct device_d *dev = &phydev->dev; - struct device_node *np = dev->device_node; + 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; @@ -758,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", 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/chip.c b/drivers/net/phy/mv88e6xxx/chip.c index a7d707095b..b9b02c52f2 100644 --- a/drivers/net/phy/mv88e6xxx/chip.c +++ b/drivers/net/phy/mv88e6xxx/chip.c @@ -832,14 +832,9 @@ static int mv88e6xxx_eeprom_write(void *ctx, unsigned offset, const void *val, s 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; @@ -911,7 +906,8 @@ static int mv88e6xxx_probe(struct device_d *dev) .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))) @@ -941,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) @@ -965,8 +961,9 @@ static const struct of_device_id mv88e6xxx_of_match[] = { }, {}, }; +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 30fdac9a9f..aec6c2891f 100644 --- a/drivers/net/phy/mv88e6xxx/chip.h +++ b/drivers/net/phy/mv88e6xxx/chip.h @@ -50,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/port.c b/drivers/net/phy/mv88e6xxx/port.c index 93a789e658..29ea4ec882 100644 --- a/drivers/net/phy/mv88e6xxx/port.c +++ b/drivers/net/phy/mv88e6xxx/port.c @@ -564,8 +564,8 @@ 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; @@ -580,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; } @@ -659,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; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 60b5839b40..abd78b2c80 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -20,8 +20,6 @@ #include <linux/phy.h> #include <linux/err.h> -#define PHY_AN_TIMEOUT 10 - static struct phy_driver genphy_driver; /** @@ -330,7 +328,7 @@ 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; @@ -338,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); } @@ -358,7 +358,7 @@ static struct phy_device *of_mdio_find_phy(struct eth_device *edev) 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; @@ -367,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); } @@ -462,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; } @@ -675,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); @@ -819,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)); @@ -850,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); @@ -876,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; @@ -933,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; diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 4df3db1387..c23947b7cb 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -80,7 +80,7 @@ static int rtl8211c_config_init(struct phy_device *phydev) static int rtl8211f_config_init(struct phy_device *phydev) { - struct device_d *dev = &phydev->dev; + struct device *dev = &phydev->dev; u16 val_txdly, val_rxdly; int ret; @@ -155,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, 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 c133a542dc..5c91c10fea 100644 --- a/drivers/net/rtl8139.c +++ b/drivers/net/rtl8139.c @@ -10,8 +10,6 @@ #include <linux/phy.h> #include <linux/pci.h> -#include <asm/dma-mapping.h> - #define RTL8139_DEBUG #undef RTL8139_DEBUG @@ -375,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; } @@ -392,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); @@ -410,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); } @@ -531,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 341f5f240e..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 = - cpu_to_le32(BD_STAT_OWN | BD_STAT_EOR | PKT_BUF_SIZE); - else - priv->rx_desc[i].status = - cpu_to_le32(BD_STAT_OWN | PKT_BUF_SIZE); - - priv->rx_desc[i].buf_addr = - cpu_to_le32(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 = - cpu_to_le32(virt_to_phys(priv->tx_buf + entry * PKT_BUF_SIZE)); - - if (entry != (NUM_TX_DESC - 1)) { - priv->tx_desc[entry].status = - cpu_to_le32(BD_STAT_OWN | BD_STAT_FS | BD_STAT_LS | - ((packet_length > ETH_ZLEN) ? packet_length : ETH_ZLEN)); - } else { - priv->tx_desc[entry].status = - cpu_to_le32(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 (le32_to_cpu(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 ((le32_to_cpu(priv->rx_desc[entry].status) & BD_STAT_OWN) == 0) { - if (!(le32_to_cpu(priv->rx_desc[entry].status) & BD_STAT_RX_RES)) { - pkt_size = (le32_to_cpu(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 = cpu_to_le32(BD_STAT_OWN | - BD_STAT_EOR | PKT_BUF_SIZE); - else - priv->rx_desc[entry].status = - cpu_to_le32(BD_STAT_OWN | PKT_BUF_SIZE); - priv->rx_desc[entry].buf_addr = - cpu_to_le32(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 index e1161df31f..d88a5e2fcf 100644 --- a/drivers/net/sja1105.c +++ b/drivers/net/sja1105.c @@ -10,7 +10,7 @@ #include <common.h> #include <dsa.h> -#include <gpiod.h> +#include <linux/gpio/consumer.h> #include <linux/bitrev.h> #include <linux/if_vlan.h> #include <net.h> @@ -297,7 +297,7 @@ struct sja1105_private { u16 pvid[SJA1105_MAX_NUM_PORTS]; struct sja1105_xpcs_cfg xpcs_cfg[SJA1105_MAX_NUM_PORTS]; const struct sja1105_dcfg *dcfg; - struct device_d *dev; + struct device *dev; struct dsa_switch ds; struct spi_device *spidev; size_t max_xfer_len; @@ -1432,7 +1432,7 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv) .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_port_enable. */ + /* Will be overridden in sja1105_adjust_link. */ .speed = priv->dcfg->port_speed[SJA1105_SPEED_AUTO], .egress = true, .ingress = true, @@ -1963,7 +1963,7 @@ 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_d *dev = priv->dev; + struct device *dev = priv->dev; u64 speed; int rc = -EINVAL; @@ -2660,7 +2660,7 @@ static int sja1105_static_config_reload(struct sja1105_private *priv) static int sja1105_port_set_mode(struct dsa_port *dp, int port, phy_interface_t phy_mode) { - struct device_d *dev = dp->ds->dev; + struct device *dev = dp->ds->dev; struct sja1105_private *priv = dev_get_priv(dev); struct sja1105_xmii_params_entry *mii; @@ -2716,7 +2716,7 @@ unsupported: static int sja1105_port_pre_enable(struct dsa_port *dp, int port, phy_interface_t phy_mode) { - struct device_d *dev = dp->ds->dev; + struct device *dev = dp->ds->dev; struct sja1105_private *priv = dev_get_priv(dev); int ret; @@ -2727,14 +2727,16 @@ static int sja1105_port_pre_enable(struct dsa_port *dp, int port, return sja1105_static_config_reload(priv); } -static int sja1105_port_enable(struct dsa_port *dp, int port, - struct phy_device *phy) +static void sja1105_adjust_link(struct eth_device *edev) { - struct device_d *dev = dp->ds->dev; + 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; @@ -2742,7 +2744,7 @@ static int sja1105_port_enable(struct dsa_port *dp, int port, ret = sja1105_port_set_mode(dp, port, phy_mode); if (ret) - return ret; + goto error; /* Let the PHY handle the RGMII delays, if present. */ if (phy->phy_id == 0) { @@ -2758,7 +2760,7 @@ static int sja1105_port_enable(struct dsa_port *dp, int port, priv->rgmii_tx_delay[port]) && !priv->dcfg->setup_rgmii_delay) { dev_err(priv->dev, "Chip does not support internal RGMII delays\n"); - return -EINVAL; + return; } } @@ -2776,12 +2778,19 @@ static int sja1105_port_enable(struct dsa_port *dp, int port, mac[port].speed = priv->dcfg->port_speed[SJA1105_SPEED_10MBPS]; } else { - dev_err(priv->dev, "Invalid PHY speed %d on port %d\n", - phy->speed, port); - return -EINVAL; + mac[port].speed = priv->dcfg->port_speed[SJA1105_SPEED_AUTO]; + return; } - return sja1105_static_config_reload(priv); + 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) @@ -2814,9 +2823,9 @@ static int sja1105_rcv(struct dsa_switch *ds, int *port, void *packet, return 0; } -static const struct dsa_ops sja1105_dsa_ops = { +static const struct dsa_switch_ops sja1105_dsa_ops = { .port_pre_enable = sja1105_port_pre_enable, - .port_enable = sja1105_port_enable, + .adjust_link = sja1105_adjust_link, .xmit = sja1105_xmit, .rcv = sja1105_rcv, }; @@ -2871,26 +2880,27 @@ static int sja1105_check_device_id(struct sja1105_private *priv) } /* Configure the optional reset pin and bring up switch */ -static int sja1105_hw_reset(struct device_d *dev, unsigned int pulse_len, +static int sja1105_hw_reset(struct device *dev, unsigned int pulse_len, unsigned int startup_delay) { - int gpio; - - gpio = gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - if (gpio < 0) - return 0; - - 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); + 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_d *dev) +static int sja1105_probe(struct device *dev) { struct spi_device *spi = dev->type_data; const struct sja1105_dcfg *dcfg; @@ -2977,8 +2987,9 @@ static const struct of_device_id sja1105_ids[] = { { .compatible = "nxp,sja1110d", .data = &sja1110d_dcfg }, { } }; +MODULE_DEVICE_TABLE(of, sja1105_ids); -static struct driver_d sja1105_driver = { +static struct driver sja1105_driver = { .name = "sja1105", .probe = sja1105_probe, .of_compatible = DRV_OF_COMPAT(sja1105_ids), 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 1edc16ce44..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; } @@ -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 870f24ca96..6dc6a24aee 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -1,12 +1,12 @@ # 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 @@ -21,7 +21,6 @@ config USB_NET_AX88179_178A * Sitcomm LN-032 config NET_USB_SMSC95XX - select PHYLIB bool "SMSC95xx" config NET_USB_RTL8152 diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 28a3534bcf..9d34beab0d 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -3,8 +3,8 @@ #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> @@ -427,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); @@ -664,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 index fddb187144..c6108c488b 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -8,8 +8,8 @@ #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 <poller.h> @@ -469,7 +469,7 @@ static int ax88179_rx_fixup(struct usbnet *dev, void *buf, int len) __func__, frame_pos, pkt_len); net_receive(&dev->edev, buf + frame_pos, pkt_len); - + pkt_hdr++; frame_pos += ((pkt_len + 7) & 0xfff8) - 2; } diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 0928887784..2511c524cd 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -8,8 +8,8 @@ #include <linux/ethtool.h> #include <linux/mii.h> #include <linux/phy.h> -#include <usb/usb.h> -#include <usb/usbnet.h> +#include <linux/usb/usb.h> +#include <linux/usb/usbnet.h> #include "r8152.h" #define R8152_TX_BURST_SIZE 512 diff --git a/drivers/net/usb/r8152_fw.c b/drivers/net/usb/r8152_fw.c index ee5f7c48e4..a6d61ac0d6 100644 --- a/drivers/net/usb/r8152_fw.c +++ b/drivers/net/usb/r8152_fw.c @@ -2,8 +2,8 @@ /* Copyright (c) 2015 Realtek Semiconductor Corp. All rights reserved. */ #include <common.h> -#include <usb/usb.h> -#include <usb/usbnet.h> +#include <linux/usb/usb.h> +#include <linux/usb/usbnet.h> #include "r8152.h" static const u8 r8152b_pla_patch_a[] = { 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 4818079523..3c3da3171b 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1,7 +1,7 @@ // 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> |