From 5e9d9abe08745b54262fb26e2d6fcccbea61e409 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Fri, 27 Apr 2018 14:52:57 +0200 Subject: tsec: Fix reading phy registers from DT Bus translations should be applied when reading the address of the sgmii phy registers from the DT. Use ofnode_get_addr_index instead of the plain ofnode_read_u32_default to fix this. Signed-off-by: Mario Six Acked-by: Joe Hershberger --- drivers/net/tsec.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 37840420fa..03a46da2f8 100644 --- a/drivers/net/tsec.c +++ b/drivers/net/tsec.c @@ -796,8 +796,9 @@ int tsec_probe(struct udevice *dev) parent = ofnode_get_parent(phandle_args.node); if (ofnode_valid(parent)) { - int reg = ofnode_read_u32_default(parent, "reg", 0); - priv->phyregs_sgmii = (struct tsec_mii_mng *)(reg + 0x520); + int reg = ofnode_get_addr_index(parent, 0); + + priv->phyregs_sgmii = (struct tsec_mii_mng *)reg; } else { debug("No parent node for PHY?\n"); return -ENOENT; -- cgit From 6e35686d893a1dea647302b3b2b41ea5a6195d1c Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 1 May 2018 16:33:55 -0500 Subject: net: sunxi: Correct MAC address register order Put the enetaddr data in the same order as it was before the change in commit ace1520cb5fc ("net: sunxi-emac: Write HW address via function") Reported-by: Udo Maslo Signed-off-by: Joe Hershberger --- drivers/net/sunxi_emac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c index d20b808c12..8dbd3c50c1 100644 --- a/drivers/net/sunxi_emac.c +++ b/drivers/net/sunxi_emac.c @@ -334,8 +334,8 @@ static int _sunxi_write_hwaddr(struct emac_eth_dev *priv, u8 *enetaddr) enetaddr_lo = enetaddr[2] | (enetaddr[1] << 8) | (enetaddr[0] << 16); enetaddr_hi = enetaddr[5] | (enetaddr[4] << 8) | (enetaddr[3] << 16); - writel(enetaddr_hi, ®s->mac_a1); - writel(enetaddr_lo, ®s->mac_a0); + writel(enetaddr_hi, ®s->mac_a0); + writel(enetaddr_lo, ®s->mac_a1); return 0; } -- cgit From 552e7c57d035792c8939d86f276624e2614b936b Mon Sep 17 00:00:00 2001 From: Vicentiu Galanopulo Date: Wed, 2 May 2018 06:23:38 -0500 Subject: net/phy/cortina: Add support for CS4223 PHY Add support for Cortina CS4223 10G PHY - As per the CS4223 specs, an EEPROM module is connected to the PHY. At startup the PHY reads the firmware line and tries to load the firmware into the internal memory. - This driver reads the EEPROM status and checks if firmware has been loaded Signed-off-by: Vicentiu Galanopulo Acked-by: Joe Hershberger --- drivers/net/phy/cortina.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c index 9b60d1aac5..a04a118f90 100644 --- a/drivers/net/phy/cortina.c +++ b/drivers/net/phy/cortina.c @@ -284,6 +284,38 @@ int cs4340_startup(struct phy_device *phydev) return 0; } +int cs4223_phy_init(struct phy_device *phydev) +{ + int reg_value; + + reg_value = phy_read(phydev, 0x00, CS4223_EEPROM_STATUS); + if (!(reg_value & CS4223_EEPROM_FIRMWARE_LOADDONE)) { + printf("%s CS4223 Firmware not present in EERPOM\n", __func__); + return -ENOSYS; + } + + return 0; +} + +int cs4223_config(struct phy_device *phydev) +{ + return cs4223_phy_init(phydev); +} + +int cs4223_probe(struct phy_device *phydev) +{ + phydev->flags = PHY_FLAG_BROKEN_RESET; + return 0; +} + +int cs4223_startup(struct phy_device *phydev) +{ + phydev->link = 1; + phydev->speed = SPEED_10000; + phydev->duplex = DUPLEX_FULL; + return 0; +} + struct phy_driver cs4340_driver = { .name = "Cortina CS4315/CS4340", .uid = PHY_UID_CS4340, @@ -298,9 +330,23 @@ struct phy_driver cs4340_driver = { .shutdown = &gen10g_shutdown, }; +struct phy_driver cs4223_driver = { + .name = "Cortina CS4223", + .uid = PHY_UID_CS4223, + .mask = 0x0ffff00f, + .features = PHY_10G_FEATURES, + .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS | + MDIO_DEVS_AN), + .config = &cs4223_config, + .probe = &cs4223_probe, + .startup = &cs4223_startup, + .shutdown = &gen10g_shutdown, +}; + int phy_cortina_init(void) { phy_register(&cs4340_driver); + phy_register(&cs4223_driver); return 0; } @@ -319,7 +365,7 @@ int get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id) return -EIO; *phy_id |= (phy_reg & 0xffff); - if (*phy_id == PHY_UID_CS4340) + if ((*phy_id == PHY_UID_CS4340) || (*phy_id == PHY_UID_CS4223)) return 0; /* -- cgit From 286bea2e85a73602624b8dc05dd0dfac8e7e4263 Mon Sep 17 00:00:00 2001 From: Alex Kiernan Date: Sat, 12 May 2018 07:30:02 +0000 Subject: net: cpsw: ti: Reap completed packets before stopping interface If you send a final packet just before stopping the interface (e.g. a final ACK as part of the UDP fastboot protocol), then that packet isn't reliably delivered onto the wire. Reap packets prior to stopping the interface to ensure any which are in-flight make it out. Also remove buffer and len from the call to cpdma_process() as we weren't using them on their return. Signed-off-by: Alex Kiernan Acked-by: Joe Hershberger --- drivers/net/cpsw.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index e2395dbeb9..9919d3919f 100644 --- a/drivers/net/cpsw.c +++ b/drivers/net/cpsw.c @@ -910,8 +910,22 @@ out: return ret; } +static int cpsw_reap_completed_packets(struct cpsw_priv *priv) +{ + int timeout = CPDMA_TIMEOUT; + + /* reap completed packets */ + while (timeout-- && + (cpdma_process(priv, &priv->tx_chan, NULL, NULL) >= 0)) + ; + + return timeout; +} + static void _cpsw_halt(struct cpsw_priv *priv) { + cpsw_reap_completed_packets(priv); + writel(0, priv->dma_regs + CPDMA_TXCONTROL); writel(0, priv->dma_regs + CPDMA_RXCONTROL); @@ -925,18 +939,12 @@ static void _cpsw_halt(struct cpsw_priv *priv) static int _cpsw_send(struct cpsw_priv *priv, void *packet, int length) { - void *buffer; - int len; - int timeout = CPDMA_TIMEOUT; + int timeout; flush_dcache_range((unsigned long)packet, (unsigned long)packet + ALIGN(length, PKTALIGN)); - /* first reap completed packets */ - while (timeout-- && - (cpdma_process(priv, &priv->tx_chan, &buffer, &len) >= 0)) - ; - + timeout = cpsw_reap_completed_packets(priv); if (timeout == -1) { printf("cpdma_process timeout\n"); return -ETIMEDOUT; -- cgit From 98017a1fb5c0add55249fa08f8982452a3ab31af Mon Sep 17 00:00:00 2001 From: Radu Bulie Date: Mon, 21 May 2018 10:02:09 -0500 Subject: drivers/net/vsc9953: Initialize action RAM in VCAP complex VCAP tables must be initialized even if no advanced classification is used. If no initialization is performed, then ECC error will be observed by the user when the first packet enters the l2switch. The error is marked in MPIC_EISR0 -bit 29 which means - Internal RAM multi-bit ECC error. This patch fixes the aforementioned ECC error by performing the initialization of VCAP tables. Signed-off-by: Radu Bulie Acked-by: Joe Hershberger --- drivers/net/vsc9953.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) (limited to 'drivers') diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 5d196cfb3f..f17839c70f 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -2468,6 +2468,139 @@ void vsc9953_default_configuration(void) debug("VSC9953: failed to set default aggregation code mode\n"); } +static void vcap_entry2cache_init(u32 target, u32 entry_words) +{ + int i; + + for (i = 0; i < entry_words; i++) { + out_le32((unsigned int *)(VSC9953_OFFSET + + VSC9953_VCAP_CACHE_ENTRY_DAT(target, i)), 0x00); + out_le32((unsigned int *)(VSC9953_OFFSET + + VSC9953_VCAP_CACHE_MASK_DAT(target, i)), 0xFF); + } + + out_le32((unsigned int *)(VSC9953_OFFSET + + VSC9953_VCAP_CACHE_TG_DAT(target)), 0x00); + out_le32((unsigned int *)(VSC9953_OFFSET + + VSC9953_VCAP_CFG_MV_CFG(target)), + VSC9953_VCAP_CFG_MV_CFG_SIZE(entry_words)); +} + +static void vcap_action2cache_init(u32 target, u32 action_words, + u32 counter_words) +{ + int i; + + for (i = 0; i < action_words; i++) + out_le32((unsigned int *)(VSC9953_OFFSET + + VSC9953_VCAP_CACHE_ACTION_DAT(target, i)), 0x00); + + for (i = 0; i < counter_words; i++) + out_le32((unsigned int *)(VSC9953_OFFSET + + VSC9953_VCAP_CACHE_CNT_DAT(target, i)), 0x00); +} + +static int vcap_cmd(u32 target, u16 ix, int cmd, int sel, int entry_count) +{ + u32 tgt = target; + u32 value = (VSC9953_VCAP_UPDATE_CTRL_UPDATE_CMD(cmd) | + VSC9953_VCAP_UPDATE_CTRL_UPDATE_ADDR(ix) | + VSC9953_VCAP_UPDATE_CTRL_UPDATE_SHOT); + + if ((sel & TCAM_SEL_ENTRY) && ix >= entry_count) + return CMD_RET_FAILURE; + + if (!(sel & TCAM_SEL_ENTRY)) + value |= VSC9953_VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS; + + if (!(sel & TCAM_SEL_ACTION)) + value |= VSC9953_VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS; + + if (!(sel & TCAM_SEL_COUNTER)) + value |= VSC9953_VCAP_UPDATE_CTRL_UPDATE_CNT_DIS; + + out_le32((unsigned int *)(VSC9953_OFFSET + + VSC9953_VCAP_CFG_UPDATE_CTRL(tgt)), value); + + do { + value = in_le32((unsigned int *)(VSC9953_OFFSET + + VSC9953_VCAP_CFG_UPDATE_CTRL(tgt))); + + } while (value & VSC9953_VCAP_UPDATE_CTRL_UPDATE_SHOT); + + return CMD_RET_SUCCESS; +} + +static void vsc9953_vcap_init(void) +{ + u32 tgt = VSC9953_ES0; + int cmd_ret; + + /* write entries */ + vcap_entry2cache_init(tgt, ENTRY_WORDS_ES0); + cmd_ret = vcap_cmd(tgt, 0, TCAM_CMD_INITIALIZE, TCAM_SEL_ENTRY, + ENTRY_WORDS_ES0); + if (cmd_ret != CMD_RET_SUCCESS) + debug("VSC9953:%d invalid TCAM_SEL_ENTRY\n", + __LINE__); + + /* write actions and counters */ + vcap_action2cache_init(tgt, BITS_TO_DWORD(ES0_ACT_WIDTH), + BITS_TO_DWORD(ES0_CNT_WIDTH)); + out_le32((unsigned int *)(VSC9953_OFFSET + + VSC9953_VCAP_CFG_MV_CFG(tgt)), + VSC9953_VCAP_CFG_MV_CFG_SIZE(ES0_ACT_COUNT)); + cmd_ret = vcap_cmd(tgt, 0, TCAM_CMD_INITIALIZE, + TCAM_SEL_ACTION | TCAM_SEL_COUNTER, ENTRY_WORDS_ES0); + if (cmd_ret != CMD_RET_SUCCESS) + debug("VSC9953:%d invalid TCAM_SEL_ACTION | TCAM_SEL_COUNTER\n", + __LINE__); + + tgt = VSC9953_IS1; + + /* write entries */ + vcap_entry2cache_init(tgt, ENTRY_WORDS_IS1); + cmd_ret = vcap_cmd(tgt, 0, TCAM_CMD_INITIALIZE, TCAM_SEL_ENTRY, + ENTRY_WORDS_IS1); + if (cmd_ret != CMD_RET_SUCCESS) + debug("VSC9953:%d invalid TCAM_SEL_ENTRY\n", + __LINE__); + + /* write actions and counters */ + vcap_action2cache_init(tgt, BITS_TO_DWORD(IS1_ACT_WIDTH), + BITS_TO_DWORD(IS1_CNT_WIDTH)); + out_le32((unsigned int *)(VSC9953_OFFSET + + VSC9953_VCAP_CFG_MV_CFG(tgt)), + VSC9953_VCAP_CFG_MV_CFG_SIZE(IS1_ACT_COUNT)); + cmd_ret = vcap_cmd(tgt, 0, TCAM_CMD_INITIALIZE, + TCAM_SEL_ACTION | TCAM_SEL_COUNTER, ENTRY_WORDS_IS1); + if (cmd_ret != CMD_RET_SUCCESS) + debug("VSC9953:%d invalid TCAM_SEL_ACTION | TCAM_SEL_COUNTER\n", + __LINE__); + + tgt = VSC9953_IS2; + + /* write entries */ + vcap_entry2cache_init(tgt, ENTRY_WORDS_IS2); + cmd_ret = vcap_cmd(tgt, 0, TCAM_CMD_INITIALIZE, TCAM_SEL_ENTRY, + ENTRY_WORDS_IS2); + if (cmd_ret != CMD_RET_SUCCESS) + debug("VSC9953:%d invalid selection: TCAM_SEL_ENTRY\n", + __LINE__); + + /* write actions and counters */ + vcap_action2cache_init(tgt, BITS_TO_DWORD(IS2_ACT_WIDTH), + BITS_TO_DWORD(IS2_CNT_WIDTH)); + out_le32((unsigned int *)(VSC9953_OFFSET + + VSC9953_VCAP_CFG_MV_CFG(tgt)), + VSC9953_VCAP_CFG_MV_CFG_SIZE(IS2_ACT_COUNT)); + cmd_ret = vcap_cmd(tgt, 0, TCAM_CMD_INITIALIZE, + TCAM_SEL_ACTION | TCAM_SEL_COUNTER, ENTRY_WORDS_IS2); + if (cmd_ret != CMD_RET_SUCCESS) + debug("VSC9953:%d invalid TCAM_SEL_ACTION | TCAM_SEL_COUNTER\n", + __LINE__); +} + void vsc9953_init(bd_t *bis) { u32 i; @@ -2604,6 +2737,7 @@ void vsc9953_init(bd_t *bis) } } + vsc9953_vcap_init(); vsc9953_default_configuration(); #ifdef CONFIG_CMD_ETHSW -- cgit From a8927795efff1d5fc76a2bf6f73112751eb8e5cb Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Thu, 24 May 2018 19:24:37 +0900 Subject: net: add Socionext AVE ethernet driver support Add driver for Socionext AVE ethernet controller that includes MAC and MDIO bus supporting RGMII/RMII modes. The driver behaves the ethernet driver model (DM_ETH) with devicetree. Signed-off-by: Kunihiko Hayashi Signed-off-by: Masahiro Yamada Acked-by: Joe Hershberger --- drivers/net/Kconfig | 10 + drivers/net/Makefile | 1 + drivers/net/sni_ave.c | 995 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1006 insertions(+) create mode 100644 drivers/net/sni_ave.c (limited to 'drivers') diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index f2cc75f494..e88f056d84 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -362,6 +362,16 @@ config MPC8XX_FEC This driver implements support for the Fast Ethernet Controller on MPC8XX +config SNI_AVE + bool "Socionext AVE Ethernet support" + depends on DM_ETH && ARCH_UNIPHIER + select PHYLIB + select SYSCON + select REGMAP + help + This driver implements support for the Socionext AVE Ethernet + controller, as found on the Socionext UniPhier family. + config ETHER_ON_FEC1 bool "FEC1" depends on MPC8XX_FEC diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 584bfdf2f9..058dd00768 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -70,3 +70,4 @@ obj-$(CONFIG_VSC9953) += vsc9953.o obj-$(CONFIG_PIC32_ETH) += pic32_mdio.o pic32_eth.o obj-$(CONFIG_DWC_ETH_QOS) += dwc_eth_qos.o obj-$(CONFIG_FSL_PFE) += pfe_eth/ +obj-$(CONFIG_SNI_AVE) += sni_ave.o diff --git a/drivers/net/sni_ave.c b/drivers/net/sni_ave.c new file mode 100644 index 0000000000..ba51ea5e38 --- /dev/null +++ b/drivers/net/sni_ave.c @@ -0,0 +1,995 @@ +// SPDX-License-Identifier: GPL-2.0+ +/** + * sni_ave.c - Socionext UniPhier AVE ethernet driver + * Copyright 2016-2018 Socionext inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AVE_GRST_DELAY_MSEC 40 +#define AVE_MIN_XMITSIZE 60 +#define AVE_SEND_TIMEOUT_COUNT 1000 +#define AVE_MDIO_TIMEOUT_USEC 10000 +#define AVE_HALT_TIMEOUT_USEC 10000 + +/* General Register Group */ +#define AVE_IDR 0x000 /* ID */ +#define AVE_VR 0x004 /* Version */ +#define AVE_GRR 0x008 /* Global Reset */ +#define AVE_CFGR 0x00c /* Configuration */ + +/* Interrupt Register Group */ +#define AVE_GIMR 0x100 /* Global Interrupt Mask */ +#define AVE_GISR 0x104 /* Global Interrupt Status */ + +/* MAC Register Group */ +#define AVE_TXCR 0x200 /* TX Setup */ +#define AVE_RXCR 0x204 /* RX Setup */ +#define AVE_RXMAC1R 0x208 /* MAC address (lower) */ +#define AVE_RXMAC2R 0x20c /* MAC address (upper) */ +#define AVE_MDIOCTR 0x214 /* MDIO Control */ +#define AVE_MDIOAR 0x218 /* MDIO Address */ +#define AVE_MDIOWDR 0x21c /* MDIO Data */ +#define AVE_MDIOSR 0x220 /* MDIO Status */ +#define AVE_MDIORDR 0x224 /* MDIO Rd Data */ + +/* Descriptor Control Register Group */ +#define AVE_DESCC 0x300 /* Descriptor Control */ +#define AVE_TXDC 0x304 /* TX Descriptor Configuration */ +#define AVE_RXDC 0x308 /* RX Descriptor Ring0 Configuration */ +#define AVE_IIRQC 0x34c /* Interval IRQ Control */ + +/* 64bit descriptor memory */ +#define AVE_DESC_SIZE_64 12 /* Descriptor Size */ +#define AVE_TXDM_64 0x1000 /* Tx Descriptor Memory */ +#define AVE_RXDM_64 0x1c00 /* Rx Descriptor Memory */ + +/* 32bit descriptor memory */ +#define AVE_DESC_SIZE_32 8 /* Descriptor Size */ +#define AVE_TXDM_32 0x1000 /* Tx Descriptor Memory */ +#define AVE_RXDM_32 0x1800 /* Rx Descriptor Memory */ + +/* RMII Bridge Register Group */ +#define AVE_RSTCTRL 0x8028 /* Reset control */ +#define AVE_RSTCTRL_RMIIRST BIT(16) +#define AVE_LINKSEL 0x8034 /* Link speed setting */ +#define AVE_LINKSEL_100M BIT(0) + +/* AVE_GRR */ +#define AVE_GRR_PHYRST BIT(4) /* Reset external PHY */ +#define AVE_GRR_GRST BIT(0) /* Reset all MAC */ + +/* AVE_CFGR */ +#define AVE_CFGR_MII BIT(27) /* Func mode (1:MII/RMII, 0:RGMII) */ + +/* AVE_GISR (common with GIMR) */ +#define AVE_GIMR_CLR 0 +#define AVE_GISR_CLR GENMASK(31, 0) + +/* AVE_TXCR */ +#define AVE_TXCR_FLOCTR BIT(18) /* Flow control */ +#define AVE_TXCR_TXSPD_1G BIT(17) +#define AVE_TXCR_TXSPD_100 BIT(16) + +/* AVE_RXCR */ +#define AVE_RXCR_RXEN BIT(30) /* Rx enable */ +#define AVE_RXCR_FDUPEN BIT(22) /* Interface mode */ +#define AVE_RXCR_FLOCTR BIT(21) /* Flow control */ + +/* AVE_MDIOCTR */ +#define AVE_MDIOCTR_RREQ BIT(3) /* Read request */ +#define AVE_MDIOCTR_WREQ BIT(2) /* Write request */ + +/* AVE_MDIOSR */ +#define AVE_MDIOSR_STS BIT(0) /* access status */ + +/* AVE_DESCC */ +#define AVE_DESCC_RXDSTPSTS BIT(20) +#define AVE_DESCC_RD0 BIT(8) /* Enable Rx descriptor Ring0 */ +#define AVE_DESCC_RXDSTP BIT(4) /* Pause Rx descriptor */ +#define AVE_DESCC_TD BIT(0) /* Enable Tx descriptor */ + +/* AVE_TXDC/RXDC */ +#define AVE_DESC_SIZE(priv, num) \ + ((num) * ((priv)->data->is_desc_64bit ? AVE_DESC_SIZE_64 : \ + AVE_DESC_SIZE_32)) + +/* Command status for descriptor */ +#define AVE_STS_OWN BIT(31) /* Descriptor ownership */ +#define AVE_STS_OK BIT(27) /* Normal transmit */ +#define AVE_STS_1ST BIT(26) /* Head of buffer chain */ +#define AVE_STS_LAST BIT(25) /* Tail of buffer chain */ +#define AVE_STS_PKTLEN_TX_MASK GENMASK(15, 0) +#define AVE_STS_PKTLEN_RX_MASK GENMASK(10, 0) + +#define AVE_DESC_OFS_CMDSTS 0 +#define AVE_DESC_OFS_ADDRL 4 +#define AVE_DESC_OFS_ADDRU 8 + +/* Parameter for ethernet frame */ +#define AVE_RXCR_MTU 1518 + +/* SG */ +#define SG_ETPINMODE 0x540 +#define SG_ETPINMODE_EXTPHY BIT(1) /* for LD11 */ +#define SG_ETPINMODE_RMII(ins) BIT(ins) + +#define AVE_MAX_CLKS 4 +#define AVE_MAX_RSTS 2 + +enum desc_id { + AVE_DESCID_TX, + AVE_DESCID_RX, +}; + +struct ave_private { + phys_addr_t iobase; + unsigned int nclks; + struct clk clk[AVE_MAX_CLKS]; + unsigned int nrsts; + struct reset_ctl rst[AVE_MAX_RSTS]; + struct regmap *regmap; + unsigned int regmap_arg; + + struct mii_dev *bus; + struct phy_device *phydev; + int phy_mode; + int max_speed; + + int rx_pos; + int rx_siz; + int rx_off; + int tx_num; + + u8 tx_adj_packetbuf[PKTSIZE_ALIGN + PKTALIGN]; + void *tx_adj_buf; + + const struct ave_soc_data *data; +}; + +struct ave_soc_data { + bool is_desc_64bit; + const char *clock_names[AVE_MAX_CLKS]; + const char *reset_names[AVE_MAX_RSTS]; + int (*get_pinmode)(struct ave_private *priv); +}; + +static u32 ave_desc_read(struct ave_private *priv, enum desc_id id, int entry, + int offset) +{ + int desc_size; + u32 addr; + + if (priv->data->is_desc_64bit) { + desc_size = AVE_DESC_SIZE_64; + addr = (id == AVE_DESCID_TX) ? AVE_TXDM_64 : AVE_RXDM_64; + } else { + desc_size = AVE_DESC_SIZE_32; + addr = (id == AVE_DESCID_TX) ? AVE_TXDM_32 : AVE_RXDM_32; + } + + addr += entry * desc_size + offset; + + return readl(priv->iobase + addr); +} + +static u32 ave_desc_read_cmdsts(struct ave_private *priv, enum desc_id id, + int entry) +{ + return ave_desc_read(priv, id, entry, AVE_DESC_OFS_CMDSTS); +} + +static void ave_desc_write(struct ave_private *priv, enum desc_id id, + int entry, int offset, u32 val) +{ + int desc_size; + u32 addr; + + if (priv->data->is_desc_64bit) { + desc_size = AVE_DESC_SIZE_64; + addr = (id == AVE_DESCID_TX) ? AVE_TXDM_64 : AVE_RXDM_64; + } else { + desc_size = AVE_DESC_SIZE_32; + addr = (id == AVE_DESCID_TX) ? AVE_TXDM_32 : AVE_RXDM_32; + } + + addr += entry * desc_size + offset; + writel(val, priv->iobase + addr); +} + +static void ave_desc_write_cmdsts(struct ave_private *priv, enum desc_id id, + int entry, u32 val) +{ + ave_desc_write(priv, id, entry, AVE_DESC_OFS_CMDSTS, val); +} + +static void ave_desc_write_addr(struct ave_private *priv, enum desc_id id, + int entry, uintptr_t paddr) +{ + ave_desc_write(priv, id, entry, + AVE_DESC_OFS_ADDRL, lower_32_bits(paddr)); + if (priv->data->is_desc_64bit) + ave_desc_write(priv, id, entry, + AVE_DESC_OFS_ADDRU, upper_32_bits(paddr)); +} + +static void ave_cache_invalidate(uintptr_t vaddr, int len) +{ + invalidate_dcache_range(rounddown(vaddr, ARCH_DMA_MINALIGN), + roundup(vaddr + len, ARCH_DMA_MINALIGN)); +} + +static void ave_cache_flush(uintptr_t vaddr, int len) +{ + flush_dcache_range(rounddown(vaddr, ARCH_DMA_MINALIGN), + roundup(vaddr + len, ARCH_DMA_MINALIGN)); +} + +static int ave_mdiobus_read(struct mii_dev *bus, + int phyid, int devad, int regnum) +{ + struct ave_private *priv = bus->priv; + u32 mdioctl, mdiosr; + int ret; + + /* write address */ + writel((phyid << 8) | regnum, priv->iobase + AVE_MDIOAR); + + /* read request */ + mdioctl = readl(priv->iobase + AVE_MDIOCTR); + writel(mdioctl | AVE_MDIOCTR_RREQ, priv->iobase + AVE_MDIOCTR); + + ret = readl_poll_timeout(priv->iobase + AVE_MDIOSR, mdiosr, + !(mdiosr & AVE_MDIOSR_STS), + AVE_MDIO_TIMEOUT_USEC); + if (ret) { + pr_err("%s: failed to read from mdio (phy:%d reg:%x)\n", + priv->phydev->dev->name, phyid, regnum); + return ret; + } + + return readl(priv->iobase + AVE_MDIORDR) & GENMASK(15, 0); +} + +static int ave_mdiobus_write(struct mii_dev *bus, + int phyid, int devad, int regnum, u16 val) +{ + struct ave_private *priv = bus->priv; + u32 mdioctl, mdiosr; + int ret; + + /* write address */ + writel((phyid << 8) | regnum, priv->iobase + AVE_MDIOAR); + + /* write data */ + writel(val, priv->iobase + AVE_MDIOWDR); + + /* write request */ + mdioctl = readl(priv->iobase + AVE_MDIOCTR); + writel((mdioctl | AVE_MDIOCTR_WREQ) & ~AVE_MDIOCTR_RREQ, + priv->iobase + AVE_MDIOCTR); + + ret = readl_poll_timeout(priv->iobase + AVE_MDIOSR, mdiosr, + !(mdiosr & AVE_MDIOSR_STS), + AVE_MDIO_TIMEOUT_USEC); + if (ret) + pr_err("%s: failed to write to mdio (phy:%d reg:%x)\n", + priv->phydev->dev->name, phyid, regnum); + + return ret; +} + +static int ave_adjust_link(struct ave_private *priv) +{ + struct phy_device *phydev = priv->phydev; + struct eth_pdata *pdata = dev_get_platdata(phydev->dev); + u32 val, txcr, rxcr, rxcr_org; + u16 rmt_adv = 0, lcl_adv = 0; + u8 cap; + + /* set RGMII speed */ + val = readl(priv->iobase + AVE_TXCR); + val &= ~(AVE_TXCR_TXSPD_100 | AVE_TXCR_TXSPD_1G); + + if (phy_interface_is_rgmii(phydev) && phydev->speed == SPEED_1000) + val |= AVE_TXCR_TXSPD_1G; + else if (phydev->speed == SPEED_100) + val |= AVE_TXCR_TXSPD_100; + + writel(val, priv->iobase + AVE_TXCR); + + /* set RMII speed (100M/10M only) */ + if (!phy_interface_is_rgmii(phydev)) { + val = readl(priv->iobase + AVE_LINKSEL); + if (phydev->speed == SPEED_10) + val &= ~AVE_LINKSEL_100M; + else + val |= AVE_LINKSEL_100M; + writel(val, priv->iobase + AVE_LINKSEL); + } + + /* check current RXCR/TXCR */ + rxcr = readl(priv->iobase + AVE_RXCR); + txcr = readl(priv->iobase + AVE_TXCR); + rxcr_org = rxcr; + + if (phydev->duplex) { + rxcr |= AVE_RXCR_FDUPEN; + + if (phydev->pause) + rmt_adv |= LPA_PAUSE_CAP; + if (phydev->asym_pause) + rmt_adv |= LPA_PAUSE_ASYM; + if (phydev->advertising & ADVERTISED_Pause) + lcl_adv |= ADVERTISE_PAUSE_CAP; + if (phydev->advertising & ADVERTISED_Asym_Pause) + lcl_adv |= ADVERTISE_PAUSE_ASYM; + + cap = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); + if (cap & FLOW_CTRL_TX) + txcr |= AVE_TXCR_FLOCTR; + else + txcr &= ~AVE_TXCR_FLOCTR; + if (cap & FLOW_CTRL_RX) + rxcr |= AVE_RXCR_FLOCTR; + else + rxcr &= ~AVE_RXCR_FLOCTR; + } else { + rxcr &= ~AVE_RXCR_FDUPEN; + rxcr &= ~AVE_RXCR_FLOCTR; + txcr &= ~AVE_TXCR_FLOCTR; + } + + if (rxcr_org != rxcr) { + /* disable Rx mac */ + writel(rxcr & ~AVE_RXCR_RXEN, priv->iobase + AVE_RXCR); + /* change and enable TX/Rx mac */ + writel(txcr, priv->iobase + AVE_TXCR); + writel(rxcr, priv->iobase + AVE_RXCR); + } + + pr_notice("%s: phy:%s speed:%d mac:%pM\n", + phydev->dev->name, phydev->drv->name, phydev->speed, + pdata->enetaddr); + + return phydev->link; +} + +static int ave_mdiobus_init(struct ave_private *priv, const char *name) +{ + struct mii_dev *bus = mdio_alloc(); + + if (!bus) + return -ENOMEM; + + bus->read = ave_mdiobus_read; + bus->write = ave_mdiobus_write; + snprintf(bus->name, sizeof(bus->name), "%s", name); + bus->priv = priv; + + return mdio_register(bus); +} + +static int ave_phy_init(struct ave_private *priv, void *dev) +{ + struct phy_device *phydev; + int mask = GENMASK(31, 0), ret; + + phydev = phy_find_by_mask(priv->bus, mask, priv->phy_mode); + if (!phydev) + return -ENODEV; + + phy_connect_dev(phydev, dev); + + phydev->supported &= PHY_GBIT_FEATURES; + if (priv->max_speed) { + ret = phy_set_supported(phydev, priv->max_speed); + if (ret) + return ret; + } + phydev->advertising = phydev->supported; + + priv->phydev = phydev; + phy_config(phydev); + + return 0; +} + +static void ave_stop(struct udevice *dev) +{ + struct ave_private *priv = dev_get_priv(dev); + u32 val; + int ret; + + val = readl(priv->iobase + AVE_GRR); + if (val) + return; + + val = readl(priv->iobase + AVE_RXCR); + val &= ~AVE_RXCR_RXEN; + writel(val, priv->iobase + AVE_RXCR); + + writel(0, priv->iobase + AVE_DESCC); + ret = readl_poll_timeout(priv->iobase + AVE_DESCC, val, !val, + AVE_HALT_TIMEOUT_USEC); + if (ret) + pr_warn("%s: halt timeout\n", priv->phydev->dev->name); + + writel(AVE_GRR_GRST, priv->iobase + AVE_GRR); + + phy_shutdown(priv->phydev); +} + +static void ave_reset(struct ave_private *priv) +{ + u32 val; + + /* reset RMII register */ + val = readl(priv->iobase + AVE_RSTCTRL); + val &= ~AVE_RSTCTRL_RMIIRST; + writel(val, priv->iobase + AVE_RSTCTRL); + + /* assert reset */ + writel(AVE_GRR_GRST | AVE_GRR_PHYRST, priv->iobase + AVE_GRR); + mdelay(AVE_GRST_DELAY_MSEC); + + /* 1st, negate PHY reset only */ + writel(AVE_GRR_GRST, priv->iobase + AVE_GRR); + mdelay(AVE_GRST_DELAY_MSEC); + + /* negate reset */ + writel(0, priv->iobase + AVE_GRR); + mdelay(AVE_GRST_DELAY_MSEC); + + /* negate RMII register */ + val = readl(priv->iobase + AVE_RSTCTRL); + val |= AVE_RSTCTRL_RMIIRST; + writel(val, priv->iobase + AVE_RSTCTRL); +} + +static int ave_start(struct udevice *dev) +{ + struct ave_private *priv = dev_get_priv(dev); + uintptr_t paddr; + u32 val; + int i; + + ave_reset(priv); + + priv->rx_pos = 0; + priv->rx_off = 2; /* RX data has 2byte offsets */ + priv->tx_num = 0; + priv->tx_adj_buf = + (void *)roundup((uintptr_t)&priv->tx_adj_packetbuf[0], + PKTALIGN); + priv->rx_siz = (PKTSIZE_ALIGN - priv->rx_off); + + val = 0; + if (priv->phy_mode != PHY_INTERFACE_MODE_RGMII) + val |= AVE_CFGR_MII; + writel(val, priv->iobase + AVE_CFGR); + + /* use one descriptor for Tx */ + writel(AVE_DESC_SIZE(priv, 1) << 16, priv->iobase + AVE_TXDC); + ave_desc_write_cmdsts(priv, AVE_DESCID_TX, 0, 0); + ave_desc_write_addr(priv, AVE_DESCID_TX, 0, 0); + + /* use PKTBUFSRX descriptors for Rx */ + writel(AVE_DESC_SIZE(priv, PKTBUFSRX) << 16, priv->iobase + AVE_RXDC); + for (i = 0; i < PKTBUFSRX; i++) { + paddr = (uintptr_t)net_rx_packets[i]; + ave_cache_flush(paddr, priv->rx_siz + priv->rx_off); + ave_desc_write_addr(priv, AVE_DESCID_RX, i, paddr); + ave_desc_write_cmdsts(priv, AVE_DESCID_RX, i, priv->rx_siz); + } + + writel(AVE_GISR_CLR, priv->iobase + AVE_GISR); + writel(AVE_GIMR_CLR, priv->iobase + AVE_GIMR); + + writel(AVE_RXCR_RXEN | AVE_RXCR_FDUPEN | AVE_RXCR_FLOCTR | AVE_RXCR_MTU, + priv->iobase + AVE_RXCR); + writel(AVE_DESCC_RD0 | AVE_DESCC_TD, priv->iobase + AVE_DESCC); + + phy_startup(priv->phydev); + ave_adjust_link(priv); + + return 0; +} + +static int ave_write_hwaddr(struct udevice *dev) +{ + struct ave_private *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + u8 *mac = pdata->enetaddr; + + writel(mac[0] | mac[1] << 8 | mac[2] << 16 | mac[3] << 24, + priv->iobase + AVE_RXMAC1R); + writel(mac[4] | mac[5] << 8, priv->iobase + AVE_RXMAC2R); + + return 0; +} + +static int ave_send(struct udevice *dev, void *packet, int length) +{ + struct ave_private *priv = dev_get_priv(dev); + u32 val; + void *ptr = packet; + int count; + + /* adjust alignment for descriptor */ + if ((uintptr_t)ptr & 0x3) { + memcpy(priv->tx_adj_buf, (const void *)ptr, length); + ptr = priv->tx_adj_buf; + } + + /* padding for minimum length */ + if (length < AVE_MIN_XMITSIZE) { + memset(ptr + length, 0, AVE_MIN_XMITSIZE - length); + length = AVE_MIN_XMITSIZE; + } + + /* check ownership and wait for previous xmit done */ + count = AVE_SEND_TIMEOUT_COUNT; + do { + val = ave_desc_read_cmdsts(priv, AVE_DESCID_TX, 0); + } while ((val & AVE_STS_OWN) && --count); + if (!count) + return -ETIMEDOUT; + + ave_cache_flush((uintptr_t)ptr, length); + ave_desc_write_addr(priv, AVE_DESCID_TX, 0, (uintptr_t)ptr); + + val = AVE_STS_OWN | AVE_STS_1ST | AVE_STS_LAST | + (length & AVE_STS_PKTLEN_TX_MASK); + ave_desc_write_cmdsts(priv, AVE_DESCID_TX, 0, val); + priv->tx_num++; + + count = AVE_SEND_TIMEOUT_COUNT; + do { + val = ave_desc_read_cmdsts(priv, AVE_DESCID_TX, 0); + } while ((val & AVE_STS_OWN) && --count); + if (!count) + return -ETIMEDOUT; + + if (!(val & AVE_STS_OK)) + pr_warn("%s: bad send packet status:%08x\n", + priv->phydev->dev->name, le32_to_cpu(val)); + + return 0; +} + +static int ave_recv(struct udevice *dev, int flags, uchar **packetp) +{ + struct ave_private *priv = dev_get_priv(dev); + uchar *ptr; + int length = 0; + u32 cmdsts; + + while (1) { + cmdsts = ave_desc_read_cmdsts(priv, AVE_DESCID_RX, + priv->rx_pos); + if (!(cmdsts & AVE_STS_OWN)) + /* hardware ownership, no received packets */ + return -EAGAIN; + + ptr = net_rx_packets[priv->rx_pos] + priv->rx_off; + if (cmdsts & AVE_STS_OK) + break; + + pr_warn("%s: bad packet[%d] status:%08x ptr:%p\n", + priv->phydev->dev->name, priv->rx_pos, + le32_to_cpu(cmdsts), ptr); + } + + length = cmdsts & AVE_STS_PKTLEN_RX_MASK; + + /* invalidate after DMA is done */ + ave_cache_invalidate((uintptr_t)ptr, length); + *packetp = ptr; + + return length; +} + +static int ave_free_packet(struct udevice *dev, uchar *packet, int length) +{ + struct ave_private *priv = dev_get_priv(dev); + + ave_cache_flush((uintptr_t)net_rx_packets[priv->rx_pos], + priv->rx_siz + priv->rx_off); + + ave_desc_write_cmdsts(priv, AVE_DESCID_RX, + priv->rx_pos, priv->rx_siz); + + if (++priv->rx_pos >= PKTBUFSRX) + priv->rx_pos = 0; + + return 0; +} + +static int ave_pro4_get_pinmode(struct ave_private *priv) +{ + u32 reg, mask, val = 0; + + if (priv->regmap_arg > 0) + return -EINVAL; + + mask = SG_ETPINMODE_RMII(0); + + switch (priv->phy_mode) { + case PHY_INTERFACE_MODE_RMII: + val = SG_ETPINMODE_RMII(0); + break; + case PHY_INTERFACE_MODE_MII: + case PHY_INTERFACE_MODE_RGMII: + break; + default: + return -EINVAL; + } + + regmap_read(priv->regmap, SG_ETPINMODE, ®); + reg &= ~mask; + reg |= val; + regmap_write(priv->regmap, SG_ETPINMODE, reg); + + return 0; +} + +static int ave_ld11_get_pinmode(struct ave_private *priv) +{ + u32 reg, mask, val = 0; + + if (priv->regmap_arg > 0) + return -EINVAL; + + mask = SG_ETPINMODE_EXTPHY | SG_ETPINMODE_RMII(0); + + switch (priv->phy_mode) { + case PHY_INTERFACE_MODE_INTERNAL: + break; + case PHY_INTERFACE_MODE_RMII: + val = SG_ETPINMODE_EXTPHY | SG_ETPINMODE_RMII(0); + break; + default: + return -EINVAL; + } + + regmap_read(priv->regmap, SG_ETPINMODE, ®); + reg &= ~mask; + reg |= val; + regmap_write(priv->regmap, SG_ETPINMODE, reg); + + return 0; +} + +static int ave_ld20_get_pinmode(struct ave_private *priv) +{ + u32 reg, mask, val = 0; + + if (priv->regmap_arg > 0) + return -EINVAL; + + mask = SG_ETPINMODE_RMII(0); + + switch (priv->phy_mode) { + case PHY_INTERFACE_MODE_RMII: + val = SG_ETPINMODE_RMII(0); + break; + case PHY_INTERFACE_MODE_RGMII: + break; + default: + return -EINVAL; + } + + regmap_read(priv->regmap, SG_ETPINMODE, ®); + reg &= ~mask; + reg |= val; + regmap_write(priv->regmap, SG_ETPINMODE, reg); + + return 0; +} + +static int ave_pxs3_get_pinmode(struct ave_private *priv) +{ + u32 reg, mask, val = 0; + + if (priv->regmap_arg > 1) + return -EINVAL; + + mask = SG_ETPINMODE_RMII(priv->regmap_arg); + + switch (priv->phy_mode) { + case PHY_INTERFACE_MODE_RMII: + val = SG_ETPINMODE_RMII(priv->regmap_arg); + break; + case PHY_INTERFACE_MODE_RGMII: + break; + default: + return -EINVAL; + } + + regmap_read(priv->regmap, SG_ETPINMODE, ®); + reg &= ~mask; + reg |= val; + regmap_write(priv->regmap, SG_ETPINMODE, reg); + + return 0; +} + +static int ave_ofdata_to_platdata(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct ave_private *priv = dev_get_priv(dev); + struct ofnode_phandle_args args; + const char *phy_mode; + const u32 *valp; + int ret, nc, nr; + const char *name; + + priv->data = (const struct ave_soc_data *)dev_get_driver_data(dev); + if (!priv->data) + return -EINVAL; + + pdata->iobase = devfdt_get_addr(dev); + pdata->phy_interface = -1; + phy_mode = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "phy-mode", + NULL); + if (phy_mode) + pdata->phy_interface = phy_get_interface_by_name(phy_mode); + if (pdata->phy_interface == -1) { + dev_err(dev, "Invalid PHY interface '%s'\n", phy_mode); + return -EINVAL; + } + + pdata->max_speed = 0; + valp = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), "max-speed", + NULL); + if (valp) + pdata->max_speed = fdt32_to_cpu(*valp); + + for (nc = 0; nc < AVE_MAX_CLKS; nc++) { + name = priv->data->clock_names[nc]; + if (!name) + break; + ret = clk_get_by_name(dev, name, &priv->clk[nc]); + if (ret) { + dev_err(dev, "Failed to get clocks property: %d\n", + ret); + goto out_clk_free; + } + priv->nclks++; + } + + for (nr = 0; nr < AVE_MAX_RSTS; nr++) { + name = priv->data->reset_names[nr]; + if (!name) + break; + ret = reset_get_by_name(dev, name, &priv->rst[nr]); + if (ret) { + dev_err(dev, "Failed to get resets property: %d\n", + ret); + goto out_reset_free; + } + priv->nrsts++; + } + + ret = dev_read_phandle_with_args(dev, "socionext,syscon-phy-mode", + NULL, 1, 0, &args); + if (ret) { + dev_err(dev, "Failed to get syscon-phy-mode property: %d\n", + ret); + goto out_reset_free; + } + + priv->regmap = syscon_node_to_regmap(args.node); + if (IS_ERR(priv->regmap)) { + ret = PTR_ERR(priv->regmap); + dev_err(dev, "can't get syscon: %d\n", ret); + goto out_reset_free; + } + + if (args.args_count != 1) { + ret = -EINVAL; + dev_err(dev, "Invalid argument of syscon-phy-mode\n"); + goto out_reset_free; + } + + priv->regmap_arg = args.args[0]; + + return 0; + +out_reset_free: + while (--nr >= 0) + reset_free(&priv->rst[nr]); +out_clk_free: + while (--nc >= 0) + clk_free(&priv->clk[nc]); + + return ret; +} + +static int ave_probe(struct udevice *dev) +{ + struct eth_pdata *pdata = dev_get_platdata(dev); + struct ave_private *priv = dev_get_priv(dev); + int ret, nc, nr; + + priv->data = (const struct ave_soc_data *)dev_get_driver_data(dev); + if (!priv->data) + return -EINVAL; + + priv->iobase = pdata->iobase; + priv->phy_mode = pdata->phy_interface; + priv->max_speed = pdata->max_speed; + + ret = priv->data->get_pinmode(priv); + if (ret) { + dev_err(dev, "Invalid phy-mode\n"); + return -EINVAL; + } + + for (nc = 0; nc < priv->nclks; nc++) { + ret = clk_enable(&priv->clk[nc]); + if (ret) { + dev_err(dev, "Failed to enable clk: %d\n", ret); + goto out_clk_release; + } + } + + for (nr = 0; nr < priv->nrsts; nr++) { + ret = reset_deassert(&priv->rst[nr]); + if (ret) { + dev_err(dev, "Failed to deassert reset: %d\n", ret); + goto out_reset_release; + } + } + + ave_reset(priv); + + ret = ave_mdiobus_init(priv, dev->name); + if (ret) { + dev_err(dev, "Failed to initialize mdiobus: %d\n", ret); + goto out_reset_release; + } + + priv->bus = miiphy_get_dev_by_name(dev->name); + + ret = ave_phy_init(priv, dev); + if (ret) { + dev_err(dev, "Failed to initialize phy: %d\n", ret); + goto out_mdiobus_release; + } + + return 0; + +out_mdiobus_release: + mdio_unregister(priv->bus); + mdio_free(priv->bus); +out_reset_release: + reset_release_all(priv->rst, nr); +out_clk_release: + clk_release_all(priv->clk, nc); + + return ret; +} + +static int ave_remove(struct udevice *dev) +{ + struct ave_private *priv = dev_get_priv(dev); + + free(priv->phydev); + mdio_unregister(priv->bus); + mdio_free(priv->bus); + reset_release_all(priv->rst, priv->nrsts); + clk_release_all(priv->clk, priv->nclks); + + return 0; +} + +static const struct eth_ops ave_ops = { + .start = ave_start, + .stop = ave_stop, + .send = ave_send, + .recv = ave_recv, + .free_pkt = ave_free_packet, + .write_hwaddr = ave_write_hwaddr, +}; + +static const struct ave_soc_data ave_pro4_data = { + .is_desc_64bit = false, + .clock_names = { + "gio", "ether", "ether-gb", "ether-phy", + }, + .reset_names = { + "gio", "ether", + }, + .get_pinmode = ave_pro4_get_pinmode, +}; + +static const struct ave_soc_data ave_pxs2_data = { + .is_desc_64bit = false, + .clock_names = { + "ether", + }, + .reset_names = { + "ether", + }, + .get_pinmode = ave_pro4_get_pinmode, +}; + +static const struct ave_soc_data ave_ld11_data = { + .is_desc_64bit = false, + .clock_names = { + "ether", + }, + .reset_names = { + "ether", + }, + .get_pinmode = ave_ld11_get_pinmode, +}; + +static const struct ave_soc_data ave_ld20_data = { + .is_desc_64bit = true, + .clock_names = { + "ether", + }, + .reset_names = { + "ether", + }, + .get_pinmode = ave_ld20_get_pinmode, +}; + +static const struct ave_soc_data ave_pxs3_data = { + .is_desc_64bit = false, + .clock_names = { + "ether", + }, + .reset_names = { + "ether", + }, + .get_pinmode = ave_pxs3_get_pinmode, +}; + +static const struct udevice_id ave_ids[] = { + { + .compatible = "socionext,uniphier-pro4-ave4", + .data = (ulong)&ave_pro4_data, + }, + { + .compatible = "socionext,uniphier-pxs2-ave4", + .data = (ulong)&ave_pxs2_data, + }, + { + .compatible = "socionext,uniphier-ld11-ave4", + .data = (ulong)&ave_ld11_data, + }, + { + .compatible = "socionext,uniphier-ld20-ave4", + .data = (ulong)&ave_ld20_data, + }, + { + .compatible = "socionext,uniphier-pxs3-ave4", + .data = (ulong)&ave_pxs3_data, + }, + { /* Sentinel */ } +}; + +U_BOOT_DRIVER(ave) = { + .name = "ave", + .id = UCLASS_ETH, + .of_match = ave_ids, + .probe = ave_probe, + .remove = ave_remove, + .ofdata_to_platdata = ave_ofdata_to_platdata, + .ops = &ave_ops, + .priv_auto_alloc_size = sizeof(struct ave_private), + .platdata_auto_alloc_size = sizeof(struct eth_pdata), +}; -- cgit From 199b27bb70e72819782095cd3364a2245137c3eb Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 30 May 2018 08:52:29 +0300 Subject: mvebu: neta: align DMA buffers This makes sure the DMA buffers are properly aligned for the hardware. Reviewed-by: Stefan Roese Signed-off-by: Jon Nettleton Signed-off-by: Baruch Siach Acked-by: Joe Hershberger --- drivers/net/mvneta.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mvneta.c b/drivers/net/mvneta.c index 7036b517b4..45e5eda955 100644 --- a/drivers/net/mvneta.c +++ b/drivers/net/mvneta.c @@ -1025,6 +1025,8 @@ static int mvneta_rxq_init(struct mvneta_port *pp, if (rxq->descs == NULL) return -ENOMEM; + WARN_ON(rxq->descs != PTR_ALIGN(rxq->descs, ARCH_DMA_MINALIGN)); + rxq->last_desc = rxq->size - 1; /* Set Rx descriptors queue starting address */ @@ -1061,6 +1063,8 @@ static int mvneta_txq_init(struct mvneta_port *pp, if (txq->descs == NULL) return -ENOMEM; + WARN_ON(txq->descs != PTR_ALIGN(txq->descs, ARCH_DMA_MINALIGN)); + txq->last_desc = txq->size - 1; /* Set maximum bandwidth for enabled TXQs */ @@ -1694,18 +1698,20 @@ static int mvneta_probe(struct udevice *dev) * be active. Make this area DMA safe by disabling the D-cache */ if (!buffer_loc.tx_descs) { + u32 size; + /* Align buffer area for descs and rx_buffers to 1MiB */ bd_space = memalign(1 << MMU_SECTION_SHIFT, BD_SPACE); mmu_set_region_dcache_behaviour((phys_addr_t)bd_space, BD_SPACE, DCACHE_OFF); buffer_loc.tx_descs = (struct mvneta_tx_desc *)bd_space; + size = roundup(MVNETA_MAX_TXD * sizeof(struct mvneta_tx_desc), + ARCH_DMA_MINALIGN); buffer_loc.rx_descs = (struct mvneta_rx_desc *) - ((phys_addr_t)bd_space + - MVNETA_MAX_TXD * sizeof(struct mvneta_tx_desc)); - buffer_loc.rx_buffers = (phys_addr_t) - (bd_space + - MVNETA_MAX_TXD * sizeof(struct mvneta_tx_desc) + - MVNETA_MAX_RXD * sizeof(struct mvneta_rx_desc)); + ((phys_addr_t)bd_space + size); + size += roundup(MVNETA_MAX_RXD * sizeof(struct mvneta_rx_desc), + ARCH_DMA_MINALIGN); + buffer_loc.rx_buffers = (phys_addr_t)(bd_space + size); } pp->base = (void __iomem *)pdata->iobase; -- cgit From 3cb51dad0dff5de5f4fff25639d2a88efead0691 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Sun, 3 Jun 2018 16:21:26 +1200 Subject: net: phy: mv88e61xx: Force CPU port link up When connecting to from a CPU direct to a 88e6097 typically RGMII is used. In order for traffic to actually pass we need to force the link up so the CPU MAC on the other end will see the link. Signed-off-by: Chris Packham Acked-by: Joe Hershberger --- drivers/net/phy/mv88e61xx.c | 51 ++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/net/phy/mv88e61xx.c b/drivers/net/phy/mv88e61xx.c index 17040bd6cc..ea54a15310 100644 --- a/drivers/net/phy/mv88e61xx.c +++ b/drivers/net/phy/mv88e61xx.c @@ -705,6 +705,31 @@ unforce: return res; } +static int mv88e61xx_fixed_port_setup(struct phy_device *phydev, u8 port) +{ + int val; + + val = mv88e61xx_port_read(phydev, port, PORT_REG_PHYS_CTRL); + if (val < 0) + return val; + + val &= ~(PORT_REG_PHYS_CTRL_SPD_MASK | + PORT_REG_PHYS_CTRL_FC_VALUE); + val |= PORT_REG_PHYS_CTRL_PCS_AN_EN | + PORT_REG_PHYS_CTRL_PCS_AN_RST | + PORT_REG_PHYS_CTRL_FC_FORCE | + PORT_REG_PHYS_CTRL_DUPLEX_VALUE | + PORT_REG_PHYS_CTRL_DUPLEX_FORCE | + PORT_REG_PHYS_CTRL_SPD1000; + + if (port == CONFIG_MV88E61XX_CPU_PORT) + val |= PORT_REG_PHYS_CTRL_LINK_VALUE | + PORT_REG_PHYS_CTRL_LINK_FORCE; + + return mv88e61xx_port_write(phydev, port, PORT_REG_PHYS_CTRL, + val); +} + static int mv88e61xx_set_cpu_port(struct phy_device *phydev) { int val; @@ -748,6 +773,11 @@ static int mv88e61xx_set_cpu_port(struct phy_device *phydev) if (val < 0) return val; } + } else { + val = mv88e61xx_fixed_port_setup(phydev, + CONFIG_MV88E61XX_CPU_PORT); + if (val < 0) + return val; } return 0; @@ -810,27 +840,6 @@ static int mv88e61xx_phy_setup(struct phy_device *phydev, u8 phy) return 0; } -static int mv88e61xx_fixed_port_setup(struct phy_device *phydev, u8 port) -{ - int val; - - val = mv88e61xx_port_read(phydev, port, PORT_REG_PHYS_CTRL); - if (val < 0) - return val; - - val &= ~(PORT_REG_PHYS_CTRL_SPD_MASK | - PORT_REG_PHYS_CTRL_FC_VALUE); - val |= PORT_REG_PHYS_CTRL_PCS_AN_EN | - PORT_REG_PHYS_CTRL_PCS_AN_RST | - PORT_REG_PHYS_CTRL_FC_FORCE | - PORT_REG_PHYS_CTRL_DUPLEX_VALUE | - PORT_REG_PHYS_CTRL_DUPLEX_FORCE | - PORT_REG_PHYS_CTRL_SPD1000; - - return mv88e61xx_port_write(phydev, port, PORT_REG_PHYS_CTRL, - val); -} - static int mv88e61xx_phy_config_port(struct phy_device *phydev, u8 phy) { int val; -- cgit From c61221948c30710f2316b264f61efbf61c92a4aa Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Mon, 4 Jun 2018 12:17:33 +0200 Subject: net: designware: set the PS bit when resetting DMA bus in MII configuration On the SPEAr600 SoC, which has the dwmac1000 variant of the IP block, the DMA reset never succeeds when a MII PHY is used (no problem with a GMII PHY). The designware_eth_init() function sets the DMAMAC_SRST bit in the DMA_BUS_MODE register, and then polls until this bit clears. When a MII PHY is used, with the current driver, this bit never clears and the driver therefore doesn't work. The reason is that the PS bit of the GMAC_CONTROL register should be correctly configured for the DMA reset to work. When the PS bit is 0, it tells the MAC we have a GMII PHY, when the PS bit is 1, it tells the MAC we have a MII PHY. Doing a DMA reset clears all registers, so the PS bit is cleared as well. This makes the DMA reset work fine with a GMII PHY. However, with MII PHY, the PS bit should be set. We have identified this issue thanks to two SPEAr600 platform: - One equipped with a GMII PHY, with which the existing driver was working fine. - One equipped with a MII PHY, where the current driver fails because the DMA reset times out. Note: Taken from https://www.spinics.net/lists/netdev/msg432578.html Signed-off-by: Quentin Schulz Acked-by: Joe Hershberger --- drivers/net/designware.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/net/designware.c b/drivers/net/designware.c index cf125210d8..10a87096b7 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -280,6 +280,15 @@ int designware_eth_init(struct dw_eth_dev *priv, u8 *enetaddr) writel(readl(&dma_p->busmode) | DMAMAC_SRST, &dma_p->busmode); + /* + * When a MII PHY is used, we must set the PS bit for the DMA + * reset to succeed. + */ + if (priv->phydev->interface == PHY_INTERFACE_MODE_MII) + writel(readl(&mac_p->conf) | MII_PORTSELECT, &mac_p->conf); + else + writel(readl(&mac_p->conf) & ~MII_PORTSELECT, &mac_p->conf); + start = get_timer(0); while (readl(&dma_p->busmode) & DMAMAC_SRST) { if (get_timer(start) >= CONFIG_MACRESET_TIMEOUT) { -- cgit From 5194ed7edca2ca4eaa2a48a6ef248432e3a4421e Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Sat, 9 Jun 2018 20:46:16 +1200 Subject: net: mvgbe: extract common code for SMI wait Combine repeated code from smi_reg_read/smi_reg_write into a common function smi_wait_ready. Reviewed-by: Stefan Roese Signed-off-by: Chris Packham Acked-by: Joe Hershberger --- drivers/net/mvgbe.c | 47 +++++++++++++++++++++++------------------------ drivers/net/mvgbe.h | 1 + 2 files changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/net/mvgbe.c b/drivers/net/mvgbe.c index 4e1aff6e3a..e6585ef8b3 100644 --- a/drivers/net/mvgbe.c +++ b/drivers/net/mvgbe.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -40,10 +41,24 @@ DECLARE_GLOBAL_DATA_PTR; #define MVGBE_SMI_REG (((struct mvgbe_registers *)MVGBE0_BASE)->smi) #if defined(CONFIG_PHYLIB) || defined(CONFIG_MII) || defined(CONFIG_CMD_MII) +static int smi_wait_ready(struct mvgbe_device *dmvgbe) +{ + int ret; + + ret = wait_for_bit_le32(&MVGBE_SMI_REG, MVGBE_PHY_SMI_BUSY_MASK, false, + MVGBE_PHY_SMI_TIMEOUT_MS, false); + if (ret) { + printf("Error: SMI busy timeout\n"); + return ret; + } + + return 0; +} + /* * smi_reg_read - miiphy_read callback function. * - * Returns 16bit phy register value, or 0xffff on error + * Returns 16bit phy register value, or -EFAULT on error */ static int smi_reg_read(struct mii_dev *bus, int phy_adr, int devad, int reg_ofs) @@ -74,16 +89,9 @@ static int smi_reg_read(struct mii_dev *bus, int phy_adr, int devad, return -EFAULT; } - timeout = MVGBE_PHY_SMI_TIMEOUT; /* wait till the SMI is not busy */ - do { - /* read smi register */ - smi_reg = MVGBE_REG_RD(MVGBE_SMI_REG); - if (timeout-- == 0) { - printf("Err..(%s) SMI busy timeout\n", __func__); - return -EFAULT; - } - } while (smi_reg & MVGBE_PHY_SMI_BUSY_MASK); + if (smi_wait_ready(dmvgbe) < 0) + return -EFAULT; /* fill the phy address and regiser offset and read opcode */ smi_reg = (phy_adr << MVGBE_PHY_SMI_DEV_ADDR_OFFS) @@ -119,10 +127,9 @@ static int smi_reg_read(struct mii_dev *bus, int phy_adr, int devad, } /* - * smi_reg_write - imiiphy_write callback function. + * smi_reg_write - miiphy_write callback function. * - * Returns 0 if write succeed, -EINVAL on bad parameters - * -ETIME on timeout + * Returns 0 if write succeed, -EFAULT on error */ static int smi_reg_write(struct mii_dev *bus, int phy_adr, int devad, int reg_ofs, u16 data) @@ -131,7 +138,6 @@ static int smi_reg_write(struct mii_dev *bus, int phy_adr, int devad, struct mvgbe_device *dmvgbe = to_mvgbe(dev); struct mvgbe_registers *regs = dmvgbe->regs; u32 smi_reg; - u32 timeout; /* Phyadr write request*/ if (phy_adr == MV_PHY_ADR_REQUEST && @@ -147,19 +153,12 @@ static int smi_reg_write(struct mii_dev *bus, int phy_adr, int devad, } if (reg_ofs > PHYREG_MASK) { printf("Err..(%s) Invalid register offset\n", __func__); - return -EINVAL; + return -EFAULT; } /* wait till the SMI is not busy */ - timeout = MVGBE_PHY_SMI_TIMEOUT; - do { - /* read smi register */ - smi_reg = MVGBE_REG_RD(MVGBE_SMI_REG); - if (timeout-- == 0) { - printf("Err..(%s) SMI busy timeout\n", __func__); - return -ETIME; - } - } while (smi_reg & MVGBE_PHY_SMI_BUSY_MASK); + if (smi_wait_ready(dmvgbe) < 0) + return -EFAULT; /* fill the phy addr and reg offset and write opcode and data */ smi_reg = (data << MVGBE_PHY_SMI_DATA_OFFS); diff --git a/drivers/net/mvgbe.h b/drivers/net/mvgbe.h index c20d1d7edf..1dc9bbea2f 100644 --- a/drivers/net/mvgbe.h +++ b/drivers/net/mvgbe.h @@ -216,6 +216,7 @@ /* SMI register fields */ #define MVGBE_PHY_SMI_TIMEOUT 10000 +#define MVGBE_PHY_SMI_TIMEOUT_MS 1000 #define MVGBE_PHY_SMI_DATA_OFFS 0 /* Data */ #define MVGBE_PHY_SMI_DATA_MASK (0xffff << MVGBE_PHY_SMI_DATA_OFFS) #define MVGBE_PHY_SMI_DEV_ADDR_OFFS 16 /* PHY device address */ -- cgit From b33d4a5fc7d2c67d11d936351c05856f0696d306 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 13 Jun 2018 10:00:30 +0200 Subject: net: zynq_gem: Fix return type for phy...() wait_for_bit_le32 returns negative value on failure. Fix phy...() to handle these failures properly. Signed-off-by: Michal Simek Acked-by: Joe Hershberger --- drivers/net/zynq_gem.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index a218c92314..d1436807ff 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -185,7 +185,7 @@ struct zynq_gem_priv { bool int_pcs; }; -static u32 phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, +static int phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, u32 op, u16 *data) { u32 mgtcr; @@ -216,10 +216,10 @@ static u32 phy_setup_op(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, return 0; } -static u32 phyread(struct zynq_gem_priv *priv, u32 phy_addr, +static int phyread(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, u16 *val) { - u32 ret; + int ret; ret = phy_setup_op(priv, phy_addr, regnum, ZYNQ_GEM_PHYMNTNC_OP_R_MASK, val); @@ -231,7 +231,7 @@ static u32 phyread(struct zynq_gem_priv *priv, u32 phy_addr, return ret; } -static u32 phywrite(struct zynq_gem_priv *priv, u32 phy_addr, +static int phywrite(struct zynq_gem_priv *priv, u32 phy_addr, u32 regnum, u16 data) { debug("%s: phy_addr %d, regnum 0x%x, data 0x%x\n", __func__, phy_addr, -- cgit From 7674b64d788e0e7a65f26a1cc81d583a54987282 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 13 Jun 2018 10:33:49 +0200 Subject: net: zynq_gem: Initialize phyreg variable In case of phyread()/phy_setup_op() timeout code is working with uninitialized phyreg variable. Initialize this variable to make sure that code it not working with random value. Signed-off-by: Michal Simek Acked-by: Joe Hershberger --- drivers/net/zynq_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index d1436807ff..14564e365e 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -244,7 +244,7 @@ static int phywrite(struct zynq_gem_priv *priv, u32 phy_addr, static int phy_detection(struct udevice *dev) { int i; - u16 phyreg; + u16 phyreg = 0; struct zynq_gem_priv *priv = dev->priv; if (priv->phyaddr != -1) { -- cgit From 5b2c9a6ce3ce66796e8c375133da8340c7ab2adc Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 13 Jun 2018 15:20:35 +0200 Subject: net: gem: Check return value from memalign/malloc Functions can return NULL in case of error that's why checking return value is needed. Signed-off-by: Michal Simek Acked-by: Joe Hershberger --- drivers/net/zynq_gem.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 14564e365e..a817f2e5d6 100644 --- a/drivers/net/zynq_gem.c +++ b/drivers/net/zynq_gem.c @@ -633,10 +633,16 @@ static int zynq_gem_probe(struct udevice *dev) /* Align rxbuffers to ARCH_DMA_MINALIGN */ priv->rxbuffers = memalign(ARCH_DMA_MINALIGN, RX_BUF * PKTSIZE_ALIGN); + if (!priv->rxbuffers) + return -ENOMEM; + memset(priv->rxbuffers, 0, RX_BUF * PKTSIZE_ALIGN); /* Align bd_space to MMU_SECTION_SHIFT */ bd_space = memalign(1 << MMU_SECTION_SHIFT, BD_SPACE); + if (!bd_space) + return -ENOMEM; + mmu_set_region_dcache_behaviour((phys_addr_t)bd_space, BD_SPACE, DCACHE_OFF); -- cgit