From 2d8f25ed7b3626930b9fc950b20a1ec0be7abbe9 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Wed, 28 Mar 2018 14:38:49 +0200 Subject: [PATCH 01/19] net: Always align tx packets Make sure that TX packets are always cache-aligned. Signed-off-by: Mario Six Acked-by: Joe Hershberger --- net/arp.c | 3 ++- net/ping.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/net/arp.c b/net/arp.c index 990b771..b8a7168 100644 --- a/net/arp.c +++ b/net/arp.c @@ -182,7 +182,8 @@ void arp_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len) (net_read_ip(&arp->ar_spa).s_addr & net_netmask.s_addr)) udelay(5000); #endif - net_send_packet((uchar *)et, eth_hdr_size + ARP_HDR_SIZE); + memcpy(net_tx_packet, et, eth_hdr_size + ARP_HDR_SIZE); + net_send_packet(net_tx_packet, eth_hdr_size + ARP_HDR_SIZE); return; case ARPOP_REPLY: /* arp reply */ diff --git a/net/ping.c b/net/ping.c index 5464f2f..3e5461a 100644 --- a/net/ping.c +++ b/net/ping.c @@ -107,7 +107,8 @@ void ping_receive(struct ethernet_hdr *et, struct ip_udp_hdr *ip, int len) icmph->type = ICMP_ECHO_REPLY; icmph->checksum = 0; icmph->checksum = compute_ip_checksum(icmph, len - IP_HDR_SIZE); - net_send_packet((uchar *)et, eth_hdr_size + len); + memcpy(net_tx_packet, et, eth_hdr_size + len); + net_send_packet(net_tx_packet, eth_hdr_size + len); return; /* default: return;*/ From 3ce4304205eac2bc19b9fd67651d65f7d19d3cf2 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Fri, 27 Apr 2018 14:52:56 +0200 Subject: [PATCH 02/19] net: Initialize as many ethernet devices as possible On devices that have their first network interface provided by a FPGA, the initialization of further interfaces will fail if the FPGA is not yet programmed. This leads to problems during factory setup when the data is supposed to be loaded over secondary netowork interfaces. To avoid this, use the uclass_{first,next}_device_check functions to initialize as many ethernet devices as possible. Signed-off-by: Mario Six Acked-by: Joe Hershberger --- net/eth-uclass.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/eth-uclass.c b/net/eth-uclass.c index d20a1cf..e4b4922 100644 --- a/net/eth-uclass.c +++ b/net/eth-uclass.c @@ -395,7 +395,7 @@ int eth_initialize(void) * This is accomplished by attempting to probe each device and calling * their write_hwaddr() operation. */ - uclass_first_device(UCLASS_ETH, &dev); + uclass_first_device_check(UCLASS_ETH, &dev); if (!dev) { printf("No ethernet found.\n"); bootstage_error(BOOTSTAGE_ID_NET_ETH_START); @@ -424,7 +424,7 @@ int eth_initialize(void) eth_write_hwaddr(dev); - uclass_next_device(&dev); + uclass_next_device_check(&dev); num_devices++; } while (dev); From 5e9d9abe08745b54262fb26e2d6fcccbea61e409 Mon Sep 17 00:00:00 2001 From: Mario Six Date: Fri, 27 Apr 2018 14:52:57 +0200 Subject: [PATCH 03/19] 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(-) diff --git a/drivers/net/tsec.c b/drivers/net/tsec.c index 3784042..03a46da 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; From 6f0dc0ca31e60a35c6876db76d42499890575b53 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Mon, 30 Apr 2018 12:45:22 -0500 Subject: [PATCH 04/19] net: Express LINK_LOCAL dependency on LIB_RAND Signed-off-by: Joe Hershberger --- cmd/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/Kconfig b/cmd/Kconfig index e283cb9..1eb55e5 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -1219,6 +1219,7 @@ config CMD_DNS config CMD_LINK_LOCAL bool "linklocal" + select LIB_RAND help Acquire a network IP address using the link-local protocol From 6e35686d893a1dea647302b3b2b41ea5a6195d1c Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 1 May 2018 16:33:55 -0500 Subject: [PATCH 05/19] 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(-) diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c index d20b808..8dbd3c5 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; } From 552e7c57d035792c8939d86f276624e2614b936b Mon Sep 17 00:00:00 2001 From: Vicentiu Galanopulo Date: Wed, 2 May 2018 06:23:38 -0500 Subject: [PATCH 06/19] 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 ++++++++++++++++++++++++++++++++++++++++++++++- include/cortina.h | 4 ++++ include/phy.h | 1 + 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/cortina.c b/drivers/net/phy/cortina.c index 9b60d1a..a04a118 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; /* diff --git a/include/cortina.h b/include/cortina.h index 4cb0985..ba7fafe 100644 --- a/include/cortina.h +++ b/include/cortina.h @@ -64,6 +64,10 @@ #define VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLA 0x427 #define VILLA_LINE_SDS_COMMON_STX0_TX_OUTPUT_CTRLB 0x428 +/* Cortina CS4223 */ +#define CS4223_EEPROM_STATUS 0x5001 +#define CS4223_EEPROM_FIRMWARE_LOADDONE 0x1 + #define mseq_edc_bist_done (0x1<<0) #define mseq_edc_bist_fail (0x1<<8) diff --git a/include/phy.h b/include/phy.h index 52bf997..e8f10ab 100644 --- a/include/phy.h +++ b/include/phy.h @@ -314,6 +314,7 @@ static inline bool phy_interface_is_sgmii(struct phy_device *phydev) /* PHY UIDs for various PHYs that are referenced in external code */ #define PHY_UID_CS4340 0x13e51002 +#define PHY_UID_CS4223 0x03e57003 #define PHY_UID_TN2020 0x00a19410 #endif From 60177b26171a8f13be1b2d061db36784fc86a861 Mon Sep 17 00:00:00 2001 From: Leonid Iziumtsev Date: Tue, 8 May 2018 15:55:50 +0200 Subject: [PATCH 07/19] net: Protect net_state from reentrant net_loop() Global variable "net_state" is used in net_loop() state-machine. But it happens that some times the net_loop() can be called multiple times in the same call stack. For example when the netconsole is enabled and we print the message while some other net protocol is in action. Netconsole will overwrite the "net_state" and that will break the logic for earlier started protocol. To protect the state save and restore "net_state" variable each time when we enter and exit net_loop(). Signed-off-by: Leonid Iziumtsev Acked-by: Joe Hershberger --- net/net.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/net.c b/net/net.c index a4932f4..b4563a4 100644 --- a/net/net.c +++ b/net/net.c @@ -393,6 +393,7 @@ void net_init(void) int net_loop(enum proto_t protocol) { int ret = -EINVAL; + enum net_loop_state prev_net_state = net_state; net_restarted = 0; net_dev_exists = 0; @@ -430,6 +431,7 @@ restart: case 1: /* network not configured */ eth_halt(); + net_set_state(prev_net_state); return -ENODEV; case 2: @@ -655,6 +657,7 @@ done: net_set_udp_handler(NULL); net_set_icmp_handler(NULL); #endif + net_set_state(prev_net_state); return ret; } From 286bea2e85a73602624b8dc05dd0dfac8e7e4263 Mon Sep 17 00:00:00 2001 From: Alex Kiernan Date: Sat, 12 May 2018 07:30:02 +0000 Subject: [PATCH 08/19] 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(-) diff --git a/drivers/net/cpsw.c b/drivers/net/cpsw.c index e2395db..9919d39 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; From d48d40a09127dfcb1f073c3e44de4b858393ae9e Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Mon, 14 May 2018 08:34:36 -0700 Subject: [PATCH 09/19] net: nfs: don't fail when nfs_read_reply returns -NFS_RPC_DROP That can happen if duplicate UDP packet arrived, and that's not uncommon. Anyway, we ignore packets with rpc_id lower than last we sent for other requests, so it makes sense to do that for read request as well. Signed-off-by: Vasily Khoruzhick Acked-by: Joe Hershberger --- net/nfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/nfs.c b/net/nfs.c index 83ed0a7..9a16765 100644 --- a/net/nfs.c +++ b/net/nfs.c @@ -822,6 +822,8 @@ static void nfs_handler(uchar *pkt, unsigned dest, struct in_addr sip, case STATE_READ_REQ: rlen = nfs_read_reply(pkt, len); + if (rlen == -NFS_RPC_DROP) + break; net_set_timeout_handler(nfs_timeout, nfs_timeout_handler); if (rlen > 0) { nfs_offset += rlen; From 98017a1fb5c0add55249fa08f8982452a3ab31af Mon Sep 17 00:00:00 2001 From: Radu Bulie Date: Mon, 21 May 2018 10:02:09 -0500 Subject: [PATCH 10/19] 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 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/vsc9953.h | 70 ++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 5d196cf..f17839c 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 diff --git a/include/vsc9953.h b/include/vsc9953.h index bb7f8ec..fe072da 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -186,6 +186,76 @@ #define MIIMIND_OPR_PEND 0x00000004 +#define VSC9953_BITMASK(offset) ((BIT(offset)) - 1) +#define VSC9953_ENC_BITFIELD(target, offset, width) \ + (((target) & VSC9953_BITMASK(width)) << (offset)) + +#define VSC9953_IO_ADDR(target, offset) ((target) + (offset << 2)) + +#define VSC9953_IO_REG(target, offset) (VSC9953_IO_ADDR(target, offset)) +#define VSC9953_VCAP_CACHE_ENTRY_DAT(target, ri) \ + VSC9953_IO_REG(target, (0x2 + (ri))) + +#define VSC9953_VCAP_CACHE_MASK_DAT(target, ri) \ + VSC9953_IO_REG(target, (0x42 + (ri))) + +#define VSC9953_VCAP_CACHE_TG_DAT(target) VSC9953_IO_REG(target, 0xe2) +#define VSC9953_VCAP_CFG_MV_CFG(target) VSC9953_IO_REG(target, 0x1) +#define VSC9953_VCAP_CFG_MV_CFG_SIZE(target) \ + VSC9953_ENC_BITFIELD(target, 0, 16) + +#define VSC9953_VCAP_CFG_UPDATE_CTRL(target) VSC9953_IO_REG(target, 0x0) +#define VSC9953_VCAP_UPDATE_CTRL_UPDATE_CMD(target) \ + VSC9953_ENC_BITFIELD(target, 22, 3) + +#define VSC9953_VCAP_UPDATE_CTRL_UPDATE_ADDR(target) \ + VSC9953_ENC_BITFIELD(target, 3, 16) + +#define VSC9953_VCAP_UPDATE_CTRL_UPDATE_SHOT BIT(2) +#define VSC9953_VCAP_UPDATE_CTRL_UPDATE_ENTRY_DIS BIT(21) +#define VSC9953_VCAP_UPDATE_CTRL_UPDATE_ACTION_DIS BIT(20) +#define VSC9953_VCAP_UPDATE_CTRL_UPDATE_CNT_DIS BIT(19) +#define VSC9953_VCAP_CACHE_ACTION_DAT(target, ri) \ + VSC9953_IO_REG(target, (0x82 + (ri))) + +#define VSC9953_VCAP_CACHE_CNT_DAT(target, ri) \ + VSC9953_IO_REG(target, (0xc2 + (ri))) + +#define VSC9953_PORT_OFFSET 1 +#define VSC9953_IS1_CNT 256 +#define VSC9953_IS2_CNT 1024 +#define VSC9953_ES0_CNT 1024 + +#define BITS_TO_DWORD(in) (1 + (((in) - 1) / 32)) +#define ENTRY_WORDS_ES0 BITS_TO_DWORD(29) +#define ENTRY_WORDS_IS1 BITS_TO_DWORD(376) +#define ENTRY_WORDS_IS2 BITS_TO_DWORD(376) +#define ES0_ACT_WIDTH BITS_TO_DWORD(91) +#define ES0_CNT_WIDTH BITS_TO_DWORD(1) +#define IS1_ACT_WIDTH BITS_TO_DWORD(320) +#define IS1_CNT_WIDTH BITS_TO_DWORD(4) +#define IS2_ACT_WIDTH BITS_TO_DWORD(103 - 2 * VSC9953_PORT_OFFSET) +#define IS2_CNT_WIDTH BITS_TO_DWORD(4 * 32) +#define ES0_ACT_COUNT (VSC9953_ES0_CNT + VSC9953_MAX_PORTS) +#define IS1_ACT_COUNT (VSC9953_IS1_CNT + 1) +#define IS2_ACT_COUNT (VSC9953_IS2_CNT + VSC9953_MAX_PORTS + 2) + +/* TCAM entries */ +enum tcam_sel { + TCAM_SEL_ENTRY = BIT(0), + TCAM_SEL_ACTION = BIT(1), + TCAM_SEL_COUNTER = BIT(2), + TCAM_SEL_ALL = VSC9953_BITMASK(3), +}; + +enum tcam_cmd { + TCAM_CMD_WRITE = 0, + TCAM_CMD_READ = 1, + TCAM_CMD_MOVE_UP = 2, + TCAM_CMD_MOVE_DOWN = 3, + TCAM_CMD_INITIALIZE = 4, +}; + struct vsc9953_mdio_info { struct vsc9953_mii_mng *regs; char *name; From ff114e0f76b6f0937890ba72859acfb40afee329 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 18 May 2018 11:12:04 +0900 Subject: [PATCH 11/19] net: include/phy.h: add new mode for internal phy Add the new mode to indicate a built-in PHY. This will be used by UniPhier AVE ethernet driver. Signed-off-by: Kunihiko Hayashi Reviewed-by: Marek Vasut Acked-by: Joe Hershberger --- include/phy.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/phy.h b/include/phy.h index e8f10ab..7c3fc5c 100644 --- a/include/phy.h +++ b/include/phy.h @@ -65,6 +65,7 @@ typedef enum { PHY_INTERFACE_MODE_XAUI, PHY_INTERFACE_MODE_RXAUI, PHY_INTERFACE_MODE_SFI, + PHY_INTERFACE_MODE_INTERNAL, PHY_INTERFACE_MODE_NONE, /* Must be last */ PHY_INTERFACE_MODE_COUNT, @@ -87,6 +88,7 @@ static const char *phy_interface_strings[] = { [PHY_INTERFACE_MODE_XAUI] = "xaui", [PHY_INTERFACE_MODE_RXAUI] = "rxaui", [PHY_INTERFACE_MODE_SFI] = "sfi", + [PHY_INTERFACE_MODE_INTERNAL] = "internal", [PHY_INTERFACE_MODE_NONE] = "", }; From a8927795efff1d5fc76a2bf6f73112751eb8e5cb Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Thu, 24 May 2018 19:24:37 +0900 Subject: [PATCH 12/19] 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 diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index f2cc75f..e88f056 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 584bfdf..058dd00 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 0000000..ba51ea5 --- /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), +}; From 199b27bb70e72819782095cd3364a2245137c3eb Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Wed, 30 May 2018 08:52:29 +0300 Subject: [PATCH 13/19] 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(-) diff --git a/drivers/net/mvneta.c b/drivers/net/mvneta.c index 7036b51..45e5eda 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; From 3cb51dad0dff5de5f4fff25639d2a88efead0691 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Sun, 3 Jun 2018 16:21:26 +1200 Subject: [PATCH 14/19] 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(-) diff --git a/drivers/net/phy/mv88e61xx.c b/drivers/net/phy/mv88e61xx.c index 17040bd..ea54a15 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; From c61221948c30710f2316b264f61efbf61c92a4aa Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Mon, 4 Jun 2018 12:17:33 +0200 Subject: [PATCH 15/19] 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(+) diff --git a/drivers/net/designware.c b/drivers/net/designware.c index cf12521..10a8709 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) { From 5194ed7edca2ca4eaa2a48a6ef248432e3a4421e Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Sat, 9 Jun 2018 20:46:16 +1200 Subject: [PATCH 16/19] 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(-) diff --git a/drivers/net/mvgbe.c b/drivers/net/mvgbe.c index 4e1aff6..e6585ef 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 c20d1d7..1dc9bbe 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 */ From b33d4a5fc7d2c67d11d936351c05856f0696d306 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 13 Jun 2018 10:00:30 +0200 Subject: [PATCH 17/19] 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(-) diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index a218c92..d143680 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, From 7674b64d788e0e7a65f26a1cc81d583a54987282 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 13 Jun 2018 10:33:49 +0200 Subject: [PATCH 18/19] 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(-) diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index d143680..14564e3 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) { From 5b2c9a6ce3ce66796e8c375133da8340c7ab2adc Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 13 Jun 2018 15:20:35 +0200 Subject: [PATCH 19/19] 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(+) diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c index 14564e3..a817f2e 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);