From patchwork Mon Nov 26 21:27:35 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Albert ARIBAUD X-Patchwork-Id: 202031 X-Patchwork-Delegate: prafulla@marvell.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id C8FA02C007C for ; Tue, 27 Nov 2012 08:28:23 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 5DAB94A1D6; Mon, 26 Nov 2012 22:28:22 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id G5TeEGqGT1jH; Mon, 26 Nov 2012 22:28:22 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id AC7B04A1CB; Mon, 26 Nov 2012 22:28:18 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id C61674A1E5 for ; Mon, 26 Nov 2012 22:28:14 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id HAE-nj3QYyzy for ; Mon, 26 Nov 2012 22:28:12 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from smtp1-g21.free.fr (smtp1-g21.free.fr [212.27.42.1]) by theia.denx.de (Postfix) with ESMTP id 215F64A1D2 for ; Mon, 26 Nov 2012 22:28:04 +0100 (CET) Received: from localhost.localdomain (unknown [IPv6:2a01:e35:2eb9:20:f5de:28ca:e3c0:90ec]) (Authenticated sender: albert.aribaud) by smtp1-g21.free.fr (Postfix) with ESMTPSA id DFC5D940011; Mon, 26 Nov 2012 22:27:54 +0100 (CET) From: Albert ARIBAUD To: U-Boot Date: Mon, 26 Nov 2012 22:27:35 +0100 Message-Id: <1353965257-4400-2-git-send-email-albert.u.boot@aribaud.net> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1353965257-4400-1-git-send-email-albert.u.boot@aribaud.net> References: <1352071924-20268-1-git-send-email-albert.u.boot@aribaud.net> <1353965257-4400-1-git-send-email-albert.u.boot@aribaud.net> Subject: [U-Boot] [PATCH v5 1/3] mv88e61xx: refactor PHY and SWITCH level-code X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.11 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de Signed-off-by: Albert ARIBAUD --- Changes in v5: - Fixes to the initialization sequence - Added a configurable read/write command drivers/net/phy/mv88e61xx.c | 495 +++++++++++++++++++++++++++---------------- drivers/net/phy/mv88e61xx.h | 39 ++-- include/netdev.h | 21 +- 3 files changed, 358 insertions(+), 197 deletions(-) diff --git a/drivers/net/phy/mv88e61xx.c b/drivers/net/phy/mv88e61xx.c index 483a920..e8da66d 100644 --- a/drivers/net/phy/mv88e61xx.c +++ b/drivers/net/phy/mv88e61xx.c @@ -26,6 +26,14 @@ #include #include "mv88e61xx.h" +/* + * Uncomment either of the following line for local debug control; + * otherwise global debug control will apply. + */ + +/* #undef DEBUG */ +/* #define DEBUG */ + #ifdef CONFIG_MV88E61XX_MULTICHIP_ADRMODE /* Chip Address mode * The Switch support two modes of operation @@ -52,7 +60,8 @@ static int mv88e61xx_busychk_multic(char *name, u32 devaddr) return 0; } -static void mv88e61xx_wr_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 data) +static void mv88e61xx_switch_write(char *name, u32 phy_adr, + u32 reg_ofs, u16 data) { u16 mii_dev_addr; @@ -70,7 +79,8 @@ static void mv88e61xx_wr_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 data) 15)); } -static void mv88e61xx_rd_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 * data) +static void mv88e61xx_switch_read(char *name, u32 phy_adr, + u32 reg_ofs, u16 *data) { u16 mii_dev_addr; @@ -90,110 +100,51 @@ static void mv88e61xx_rd_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 * data) } #endif /* CONFIG_MV88E61XX_MULTICHIP_ADRMODE */ -static void mv88e61xx_port_vlan_config(struct mv88e61xx_config *swconfig, - u32 max_prtnum, u32 ports_ofs) -{ - u32 prt; - u16 reg; - char *name = swconfig->name; - u32 cpu_port = swconfig->cpuport; - u32 port_mask = swconfig->ports_enabled; - enum mv88e61xx_cfg_vlan vlancfg = swconfig->vlancfg; - - /* be sure all ports are disabled */ - for (prt = 0; prt < max_prtnum; prt++) { - RD_PHY(name, ports_ofs + prt, MV88E61XX_PRT_CTRL_REG, ®); - reg &= ~0x3; - WR_PHY(name, ports_ofs + prt, MV88E61XX_PRT_CTRL_REG, reg); - - if (!(cpu_port & (1 << prt))) - continue; - /* Set CPU port VID to 0x1 */ - RD_PHY(name, (ports_ofs + prt), MV88E61XX_PRT_VID_REG, ®); - reg &= ~0xfff; - reg |= 0x1; - WR_PHY(name, (ports_ofs + prt), MV88E61XX_PRT_VID_REG, reg); - } - - /* Setting Port default priority for all ports to zero */ - for (prt = 0; prt < max_prtnum; prt++) { - RD_PHY(name, ports_ofs + prt, MV88E61XX_PRT_VID_REG, ®); - reg &= ~0xc000; - WR_PHY(name, ports_ofs + prt, MV88E61XX_PRT_VID_REG, reg); - } - /* Setting VID and VID map for all ports except CPU port */ - for (prt = 0; prt < max_prtnum; prt++) { - /* only for enabled ports */ - if ((1 << prt) & port_mask) { - /* skip CPU port */ - if ((1 << prt) & cpu_port) { - /* - * Set Vlan map table for cpu_port to see - * all ports - */ - RD_PHY(name, (ports_ofs + prt), - MV88E61XX_PRT_VMAP_REG, ®); - reg &= ~((1 << max_prtnum) - 1); - reg |= port_mask & ~(1 << prt); - WR_PHY(name, (ports_ofs + prt), - MV88E61XX_PRT_VMAP_REG, reg); - } else { - - /* - * set Ports VLAN Mapping. - * port prt <--> cpu_port VLAN #prt+1. - */ - RD_PHY(name, ports_ofs + prt, - MV88E61XX_PRT_VID_REG, ®); - reg &= ~0x0fff; - reg |= (prt + 1); - WR_PHY(name, ports_ofs + prt, - MV88E61XX_PRT_VID_REG, reg); - - RD_PHY(name, ports_ofs + prt, - MV88E61XX_PRT_VMAP_REG, ®); - if (vlancfg == MV88E61XX_VLANCFG_DEFAULT) { - /* - * all any port can send frames to all other ports - * ref: sec 3.2.1.1 of datasheet - */ - reg |= 0x03f; - reg &= ~(1 << prt); - } else if (vlancfg == MV88E61XX_VLANCFG_ROUTER) { - /* - * all other ports can send frames to CPU port only - * ref: sec 3.2.1.2 of datasheet - */ - reg &= ~((1 << max_prtnum) - 1); - reg |= cpu_port; - } - WR_PHY(name, ports_ofs + prt, - MV88E61XX_PRT_VMAP_REG, reg); - } - } - } +/* + * Convenience macros for switch device/port reads/writes + * These macros output valid 'mv88e61xx' U_BOOT_CMDs + */ - /* - * enable only appropriate ports to forwarding mode - * and disable the others - */ - for (prt = 0; prt < max_prtnum; prt++) { - if ((1 << prt) & port_mask) { - RD_PHY(name, ports_ofs + prt, - MV88E61XX_PRT_CTRL_REG, ®); - reg |= 0x3; - WR_PHY(name, ports_ofs + prt, - MV88E61XX_PRT_CTRL_REG, reg); - } else { - /* Disable port */ - RD_PHY(name, ports_ofs + prt, - MV88E61XX_PRT_CTRL_REG, ®); - reg &= ~0x3; - WR_PHY(name, ports_ofs + prt, - MV88E61XX_PRT_CTRL_REG, reg); - } - } +#ifndef DEBUG +#define WR_SWITCH_REG wr_switch_reg +#define RD_SWITCH_REG rd_switch_reg +#define WR_SWITCH_PORT_REG(n, p, r, d) \ + WR_SWITCH_REG(n, (MV88E61XX_PRT_OFST+p), r, d) +#define RD_SWITCH_PORT_REG(n, p, r, d) \ + RD_SWITCH_REG(n, (MV88E61XX_PRT_OFST+p), r, d) +#else +static void WR_SWITCH_REG(char *name, u32 dev_adr, u32 reg_ofs, u16 data) +{ + printf("mv88e61xx %s dev %02x reg %02x write %04x\n", + name, dev_adr, reg_ofs, data); + wr_switch_reg(name, dev_adr, reg_ofs, data); } +static void RD_SWITCH_REG(char *name, u32 dev_adr, u32 reg_ofs, u16 *data) +{ + rd_switch_reg(name, dev_adr, reg_ofs, data); + printf("mv88e61xx %s dev %02x reg %02x read %04x\n", + name, dev_adr, reg_ofs, *data); +} +static void WR_SWITCH_PORT_REG(char *name, u32 prt_adr, u32 reg_ofs, + u16 data) +{ + printf("mv88e61xx %s port %02x reg %02x write %04x\n", + name, prt_adr, reg_ofs, data); + wr_switch_reg(name, (MV88E61XX_PRT_OFST+prt_adr), reg_ofs, data); +} +static void RD_SWITCH_PORT_REG(char *name, u32 prt_adr, u32 reg_ofs, + u16 *data) +{ + rd_switch_reg(name, (MV88E61XX_PRT_OFST+prt_adr), reg_ofs, data); + printf("mv88e61xx %s port %02x reg %02x read %04x\n", + name, prt_adr, reg_ofs, *data); +} +#endif + +/* + * Local functions to read/write registers on the switch PHYs. + * NOTE! This goes through switch, not direct miiphy, writes and reads! + */ /* * Make sure SMIBusy bit cleared before another @@ -204,7 +155,7 @@ static int mv88e61xx_busychk(char *name) u16 reg = 0; u32 timeout = MV88E61XX_PHY_TIMEOUT; do { - RD_PHY(name, MV88E61XX_GLB2REG_DEVADR, + rd_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_CMD, ®); if (timeout-- == 0) { printf("SMI busy timeout\n"); @@ -214,34 +165,110 @@ static int mv88e61xx_busychk(char *name) return 0; } +static inline int mv88e61xx_switch_miiphy_write(char *name, u32 phy, + u32 reg, u16 data) +{ + /* write switch data reg then cmd reg then check completion */ + wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, + data); + wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_CMD, + (MV88E61XX_PHY_WRITE_CMD | (phy << 5) | reg)); + return mv88e61xx_busychk(name); +} + +static inline int mv88e61xx_switch_miiphy_read(char *name, u32 phy, + u32 reg, u16 *data) +{ + /* write switch cmd reg, check for completion */ + wr_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_CMD, + (MV88E61XX_PHY_READ_CMD | (phy << 5) | reg)); + if (mv88e61xx_busychk(name)) + return -1; + /* read switch data reg and return success */ + rd_switch_reg(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, data); + return 0; +} + +/* + * Convenience macros for switch PHY reads/writes + */ + +#ifndef DEBUG +#define WR_SWITCH_PHY_REG mv88e61xx_switch_miiphy_write +#define RD_SWITCH_PHY_REG mv88e61xx_switch_miiphy_read +#else +static inline int WR_SWITCH_PHY_REG(char *name, u32 phy_adr, + u32 reg_ofs, u16 data) +{ + int r = mv88e61xx_switch_miiphy_write(name, phy_adr, reg_ofs, data); + if (r) + printf("** ERROR writing mv88e61xx %s phy %02x reg %02x\n", + name, phy_adr, reg_ofs); + else + printf("mv88e61xx %s phy %02x reg %02x write %04x\n", + name, phy_adr, reg_ofs, data); + return r; +} +static inline int RD_SWITCH_PHY_REG(char *name, u32 phy_adr, + u32 reg_ofs, u16 *data) +{ + int r = mv88e61xx_switch_miiphy_read(name, phy_adr, reg_ofs, data); + if (r) + printf("** ERROR reading mv88e61xx %s phy %02x reg %02x\n", + name, phy_adr, reg_ofs); + else + printf("mv88e61xx %s phy %02x reg %02x read %04x\n", + name, phy_adr, reg_ofs, *data); + return r; +} +#endif + +static void mv88e61xx_port_vlan_config(struct mv88e61xx_config *swconfig) +{ + u32 prt; + u16 reg; + char *name = swconfig->name; + u32 port_mask = swconfig->ports_enabled; + + /* apply internal vlan config */ + for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { + /* only for enabled ports */ + if ((1 << prt) & port_mask) { + /* take vlan map from swconfig */ + u8 vlanmap = swconfig->vlancfg[prt]; + /* remove disabled ports from vlan map */ + vlanmap &= swconfig->ports_enabled; + /* apply vlan map to port */ + RD_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_VMAP_REG, ®); + reg &= ~((1 << MV88E61XX_MAX_PORTS_NUM) - 1); + reg |= vlanmap; + WR_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_VMAP_REG, reg); + } + } +} + /* * Power up the specified port and reset PHY */ -static int mv88361xx_powerup(struct mv88e61xx_config *swconfig, u32 prt) +static int mv88361xx_powerup(struct mv88e61xx_config *swconfig, u32 phy) { char *name = swconfig->name; - /* Write Copper Specific control reg1 (0x14) for- + /* Write Copper Specific control reg1 (0x10) for- * Enable Phy power up * Energy Detect on (sense&Xmit NLP Periodically * reset other settings default */ - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, 0x3360); - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, - MV88E61XX_PHY_CMD, (0x9410 | (prt << 5))); - - if (mv88e61xx_busychk(name)) + if (WR_SWITCH_PHY_REG(name, phy, 0x10, 0x3360)) return -1; /* Write PHY ctrl reg (0x0) to apply * Phy reset (set bit 15 low) * reset other default values */ - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, 0x1140); - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, - MV88E61XX_PHY_CMD, (0x9400 | (prt << 5))); - - if (mv88e61xx_busychk(name)) + if (WR_SWITCH_PHY_REG(name, phy, 0x00, 0x9140)) return -1; return 0; @@ -256,48 +283,26 @@ static int mv88361xx_powerup(struct mv88e61xx_config *swconfig, u32 prt) * to setup PHY LEDs default configuration to detect 10/100/1000Mb/s * Link status */ -static int mv88361xx_led_init(struct mv88e61xx_config *swconfig, u32 prt) +static int mv88361xx_led_init(struct mv88e61xx_config *swconfig, u32 phy) { char *name = swconfig->name; - u16 reg; if (swconfig->led_init != MV88E61XX_LED_INIT_EN) return 0; /* set page address to 3 */ - reg = 3; - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, - MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | - 1 << MV88E61XX_MODE_OFST | - 1 << MV88E61XX_OP_OFST | - prt << MV88E61XX_ADDR_OFST | 22)); - - if (mv88e61xx_busychk(name)) + if (WR_SWITCH_PHY_REG(name, phy, 0x16, 0x0003)) return -1; - /* set LED Func Ctrl reg */ - reg = 1; /* LED[0] On-Link, Blink-Activity, Off-NoLink */ - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, - MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | - 1 << MV88E61XX_MODE_OFST | - 1 << MV88E61XX_OP_OFST | - prt << MV88E61XX_ADDR_OFST | 16)); - - if (mv88e61xx_busychk(name)) + /* + * set LED Func Ctrl reg + * value 0x0001 = LED[0] On-Link, Blink-Activity, Off-NoLink + */ + if (WR_SWITCH_PHY_REG(name, phy, 0x10, 0x0001)) return -1; /* set page address to 0 */ - reg = 0; - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, - MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | - 1 << MV88E61XX_MODE_OFST | - 1 << MV88E61XX_OP_OFST | - prt << MV88E61XX_ADDR_OFST | 22)); - - if (mv88e61xx_busychk(name)) + if (WR_SWITCH_PHY_REG(name, phy, 0x16, 0x0000)) return -1; return 0; @@ -312,23 +317,15 @@ static int mv88361xx_led_init(struct mv88e61xx_config *swconfig, u32 prt) * This is optional settings may be needed on some boards * for PHY<->magnetics h/w tuning */ -static int mv88361xx_reverse_mdipn(struct mv88e61xx_config *swconfig, u32 prt) +static int mv88361xx_reverse_mdipn(struct mv88e61xx_config *swconfig, u32 phy) { char *name = swconfig->name; - u16 reg; if (swconfig->mdip != MV88E61XX_MDIP_REVERSE) return 0; - reg = 0x0f; /*Reverse MDIP/N[3:0] bits */ - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, MV88E61XX_PHY_DATA, reg); - WR_PHY(name, MV88E61XX_GLB2REG_DEVADR, - MV88E61XX_PHY_CMD, (1 << MV88E61XX_BUSY_OFST | - 1 << MV88E61XX_MODE_OFST | - 1 << MV88E61XX_OP_OFST | - prt << MV88E61XX_ADDR_OFST | 20)); - - if (mv88e61xx_busychk(name)) + /*Reverse MDIP/N[3:0] bits */ + if (WR_SWITCH_PHY_REG(name, phy, 0x14, 0x000f)) return -1; return 0; @@ -343,6 +340,7 @@ int mv88e61xx_switch_initialize(struct mv88e61xx_config *swconfig) u16 reg; char *idstr; char *name = swconfig->name; + int time; if (miiphy_set_current_dev(name)) { printf("%s failed\n", __FUNCTION__); @@ -354,7 +352,7 @@ int mv88e61xx_switch_initialize(struct mv88e61xx_config *swconfig) printf("Invalid cpu port config, using default port5\n"); } - RD_PHY(name, MV88E61XX_PRT_OFST, MII_PHYSID2, ®); + RD_SWITCH_PORT_REG(name, 0, MII_PHYSID2, ®); switch (reg &= 0xfff0) { case 0x1610: idstr = "88E6161"; @@ -373,46 +371,183 @@ int mv88e61xx_switch_initialize(struct mv88e61xx_config *swconfig) break; } - /* Port based VLANs configuration */ - if ((swconfig->vlancfg == MV88E61XX_VLANCFG_DEFAULT) - || (swconfig->vlancfg == MV88E61XX_VLANCFG_ROUTER)) - mv88e61xx_port_vlan_config(swconfig, MV88E61XX_MAX_PORTS_NUM, - MV88E61XX_PRT_OFST); - else { - printf("Unsupported mode %s failed\n", __FUNCTION__); - return -1; + /* be sure all ports are disabled */ + for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { + RD_SWITCH_PORT_REG(name, prt, MV88E61XX_PRT_CTRL_REG, ®); + reg &= ~0x3; + WR_SWITCH_PORT_REG(name, prt, MV88E61XX_PRT_CTRL_REG, reg); } + /* wait 2 ms for queues to drain */ + udelay(2000); + + /* reset switch */ + RD_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGCR, ®); + reg |= 0x8000; + WR_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGCR, reg); + + /* wait up to 1 second for switch reset complete */ + for (time = 1000; time; time--) { + RD_SWITCH_REG(name, MV88E61XX_GLBREG_DEVADR, MV88E61XX_SGSR, + ®); + if ((reg & 0xc800) == 0xc800) + break; + udelay(1000); + } + if (!time) + return -1; + + /* Port based VLANs configuration */ + mv88e61xx_port_vlan_config(swconfig); + if (swconfig->rgmii_delay == MV88E61XX_RGMII_DELAY_EN) { /* * Enable RGMII delay on Tx and Rx for CPU port * Ref: sec 9.5 of chip datasheet-02 */ - WR_PHY(name, MV88E61XX_PRT_OFST + 5, - MV88E61XX_RGMII_TIMECTRL_REG, 0x18); - WR_PHY(name, MV88E61XX_PRT_OFST + 4, - MV88E61XX_RGMII_TIMECTRL_REG, 0xc1e7); + /*Force port link down */ + WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x10); + /* configure port RGMII delay */ + WR_SWITCH_PORT_REG(name, 4, + MV88E61XX_RGMII_TIMECTRL_REG, 0x81e7); + RD_SWITCH_PORT_REG(name, 5, + MV88E61XX_RGMII_TIMECTRL_REG, ®); + WR_SWITCH_PORT_REG(name, 5, + MV88E61XX_RGMII_TIMECTRL_REG, reg | 0x18); + WR_SWITCH_PORT_REG(name, 4, + MV88E61XX_RGMII_TIMECTRL_REG, 0xc1e7); + /* Force port to RGMII FDX 1000Base then up */ + WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x1e); + WR_SWITCH_PORT_REG(name, 5, MV88E61XX_PCS_CTRL_REG, 0x3e); } for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) { - if (!((1 << prt) & swconfig->cpuport)) { - if (mv88361xx_led_init(swconfig, prt)) + /* configure port's PHY */ + if (!((1 << prt) & swconfig->cpuport)) { + /* port 4 has phy 6, not 4 */ + int phy = (prt == 4) ? 6 : prt; + if (mv88361xx_powerup(swconfig, phy)) return -1; - if (mv88361xx_reverse_mdipn(swconfig, prt)) + if (mv88361xx_reverse_mdipn(swconfig, phy)) return -1; - if (mv88361xx_powerup(swconfig, prt)) + if (mv88361xx_led_init(swconfig, phy)) return -1; } + /* set port VID to port+1 except for cpu port */ + if (!((1 << prt) & swconfig->cpuport)) { + RD_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_VID_REG, ®); + WR_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_VID_REG, + (reg & ~1023) | (prt+1)); + } + /*Program port state */ - RD_PHY(name, MV88E61XX_PRT_OFST + prt, - MV88E61XX_PRT_CTRL_REG, ®); - WR_PHY(name, MV88E61XX_PRT_OFST + prt, - MV88E61XX_PRT_CTRL_REG, - reg | (swconfig->portstate & 0x03)); + RD_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_CTRL_REG, ®); + WR_SWITCH_PORT_REG(name, prt, + MV88E61XX_PRT_CTRL_REG, + reg | (swconfig->portstate & 0x03)); + } printf("%s Initialized on %s\n", idstr, name); return 0; } + +#ifdef CONFIG_MV88E61XX_CMD +static int +do_switch(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *name, *endp; + int write = 0; + enum { dev, prt, phy } target = dev; + u32 addrlo, addrhi, addr; + u32 reglo, reghi, reg; + u16 data, rdata; + + if (argc < 7) + return -1; + + name = argv[1]; + + if (strcmp(argv[2], "phy") == 0) + target = phy; + else if (strcmp(argv[2], "port") == 0) + target = prt; + else if (strcmp(argv[2], "dev") != 0) + return 1; + + addrlo = simple_strtoul(argv[3], &endp, 16); + + if (!*endp) { + addrhi = addrlo; + } else { + while (*endp < '0' || *endp > '9') + endp++; + addrhi = simple_strtoul(endp, NULL, 16); + } + + reglo = simple_strtoul(argv[5], &endp, 16); + if (!*endp) { + reghi = reglo; + } else { + while (*endp < '0' || *endp > '9') + endp++; + reghi = simple_strtoul(endp, NULL, 16); + } + + if (strcmp(argv[6], "write") == 0) + write = 1; + else if (strcmp(argv[6], "read") != 0) + return 1; + + data = simple_strtoul(argv[7], NULL, 16); + + for (addr = addrlo; addr <= addrhi; addr++) { + for (reg = reglo; reg <= reghi; reg++) { + if (write) { + if (target == phy) + mv88e61xx_switch_miiphy_write( + name, addr, reg, data); + else if (target == prt) + wr_switch_reg(name, + addr+MV88E61XX_PRT_OFST, + reg, data); + else + wr_switch_reg(name, addr, reg, data); + } else { + if (target == phy) + mv88e61xx_switch_miiphy_read( + name, addr, reg, &rdata); + else if (target == prt) + rd_switch_reg(name, + addr+MV88E61XX_PRT_OFST, + reg, &rdata); + else + rd_switch_reg(name, addr, reg, &rdata); + printf("%s %s %s %02x %s %02x %s %04x\n", + argv[0], argv[1], argv[2], addr, + argv[4], reg, argv[6], rdata); + if (write && argc == 7 && rdata != data) + return 1; + } + } + } + return 0; +} + +U_BOOT_CMD(mv88e61xx, 8, 0, do_switch, + "Read or write mv88e61xx switch registers", + " dev|port|phy reg write \n" + " dev|port|phy reg read []\n" + " - read/write switch device, port or phy at (addr,reg)\n" + " addr=0..0x1C for dev, 0..5 for port or phy.\n" + " reg=0..0x1F.\n" + " data=0..0xFFFF (tested if present against actual read).\n" + " All numeric parameters are assumed to be hex.\n" + " and < arguments can be ranges (x..y)" +); +#endif /* CONFIG_MV88E61XX_CMD */ diff --git a/drivers/net/phy/mv88e61xx.h b/drivers/net/phy/mv88e61xx.h index 57762b6..55ded7e 100644 --- a/drivers/net/phy/mv88e61xx.h +++ b/drivers/net/phy/mv88e61xx.h @@ -28,35 +28,50 @@ #include #define MV88E61XX_CPU_PORT 0x5 -#define MV88E61XX_MAX_PORTS_NUM 0x6 #define MV88E61XX_PHY_TIMEOUT 100000 -#define MV88E61XX_PRT_STS_REG 0x1 +/* port dev-addr (= port + 0x10) */ +#define MV88E61XX_PRT_OFST 0x10 +/* port registers */ +#define MV88E61XX_PCS_CTRL_REG 0x1 #define MV88E61XX_PRT_CTRL_REG 0x4 #define MV88E61XX_PRT_VMAP_REG 0x6 #define MV88E61XX_PRT_VID_REG 0x7 +#define MV88E61XX_RGMII_TIMECTRL_REG 0x1A -#define MV88E61XX_PRT_OFST 0x10 +/* global registers dev-addr */ +#define MV88E61XX_GLBREG_DEVADR 0x1B +/* global registers */ +#define MV88E61XX_SGSR 0x00 +#define MV88E61XX_SGCR 0x04 + +/* global 2 registers dev-addr */ +#define MV88E61XX_GLB2REG_DEVADR 0x1C +/* global 2 registers */ #define MV88E61XX_PHY_CMD 0x18 #define MV88E61XX_PHY_DATA 0x19 -#define MV88E61XX_RGMII_TIMECTRL_REG 0x1A -#define MV88E61XX_GLB2REG_DEVADR 0x1C +/* global 2 phy commands */ +#define MV88E61XX_PHY_WRITE_CMD 0x9400 +#define MV88E61XX_PHY_READ_CMD 0x9800 #define MV88E61XX_BUSY_OFST 15 #define MV88E61XX_MODE_OFST 12 -#define MV88E61XX_OP_OFST 10 +#define MV88E61XX_OP_OFST 10 #define MV88E61XX_ADDR_OFST 5 #ifdef CONFIG_MV88E61XX_MULTICHIP_ADRMODE static int mv88e61xx_busychk_multic(char *name, u32 devaddr); -static void mv88e61xx_wr_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 data); -static void mv88e61xx_rd_phy(char *name, u32 phy_adr, u32 reg_ofs, u16 * data); -#define WR_PHY mv88e61xx_wr_phy -#define RD_PHY mv88e61xx_rd_phy +static void mv88e61xx_switch_write(char *name, u32 phy_adr, + u32 reg_ofs, u16 data); +static void mv88e61xx_switch_read(char *name, u32 phy_adr, + u32 reg_ofs, u16 *data); +#define wr_switch_reg mv88e61xx_switch_write +#define rd_switch_reg mv88e61xx_switch_read #else -#define WR_PHY miiphy_write -#define RD_PHY miiphy_read +/* switch appears a s simple PHY and can thus use miiphy */ +#define wr_switch_reg miiphy_write +#define rd_switch_reg miiphy_read #endif /* CONFIG_MV88E61XX_MULTICHIP_ADRMODE */ #endif /* _MV88E61XX_H */ diff --git a/include/netdev.h b/include/netdev.h index b8d303d..7f158d4 100644 --- a/include/netdev.h +++ b/include/netdev.h @@ -163,10 +163,9 @@ static inline int pci_eth_init(bd_t *bis) * the stuct and enums here are used to specify switch configuration params */ #if defined(CONFIG_MV88E61XX_SWITCH) -enum mv88e61xx_cfg_vlan { - MV88E61XX_VLANCFG_DEFAULT, - MV88E61XX_VLANCFG_ROUTER -}; + +/* constants for any 88E61xx switch */ +#define MV88E61XX_MAX_PORTS_NUM 6 enum mv88e61xx_cfg_mdip { MV88E61XX_MDIP_NOCHANGE, @@ -192,7 +191,7 @@ enum mv88e61xx_cfg_prtstt { struct mv88e61xx_config { char *name; - enum mv88e61xx_cfg_vlan vlancfg; + u8 vlancfg[MV88E61XX_MAX_PORTS_NUM]; enum mv88e61xx_cfg_rgmiid rgmii_delay; enum mv88e61xx_cfg_prtstt portstate; enum mv88e61xx_cfg_ledinit led_init; @@ -201,6 +200,18 @@ struct mv88e61xx_config { u8 cpuport; }; +/* + * Common mappings for Internal VLANs + * These mappings consider that all ports are useable; the driver + * will mask inexistent/unused ports. + */ + +/* Switch mode : routes any port to any port */ +#define MV88E61XX_VLANCFG_SWITCH { 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F } + +/* Router mode: routes only CPU port 5 to/from non-CPU ports 0-4 */ +#define MV88E61XX_VLANCFG_ROUTER { 0x20, 0x20, 0x20, 0x20, 0x20, 0x1F } + int mv88e61xx_switch_initialize(struct mv88e61xx_config *swconfig); #endif /* CONFIG_MV88E61XX_SWITCH */