/* * (C) Copyright 2012 * Valentin Lontgchamp, Keymile AG, valentin.longchamp@keymile.com * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #define SMI_HDR ((0x8 | 0x1) << 12) #define SMI_BUSY_MASK (0x8000) #define SMIRD_OP (0x2 << 10) #define SMIWR_OP (0x1 << 10) #define SMI_MASK 0x1f #define PORT_SHIFT 5 #define COMMAND_REG 0 #define DATA_REG 1 /* global registers */ #define GLOBAL 0x1b #define GLOBAL_STATUS 0x00 #define PPU_STATE 0x8000 #define GLOBAL_CTRL 0x04 #define SW_RESET 0x8000 #define PPU_ENABLE 0x4000 static int sw_wait_rdy(const char *devname, u8 phy_addr) { u16 command; u32 timeout = 100; int ret; /* wait till the SMI is not busy */ do { /* read command register */ ret = miiphy_read(devname, phy_addr, COMMAND_REG, &command); if (ret < 0) { printf("%s: Error reading command register\n", __func__); return ret; } if (timeout-- == 0) { printf("Err..(%s) SMI busy timeout\n", __func__); return -EFAULT; } } while (command & SMI_BUSY_MASK); return 0; } static int sw_reg_read(const char *devname, u8 phy_addr, u8 port, u8 reg, u16 *data) { int ret; u16 command; ret = sw_wait_rdy(devname, phy_addr); if (ret) return ret; command = SMI_HDR | SMIRD_OP | ((port&SMI_MASK) << PORT_SHIFT) | (reg & SMI_MASK); debug("%s: write to command: %#x\n", __func__, command); ret = miiphy_write(devname, phy_addr, COMMAND_REG, command); if (ret) return ret; ret = sw_wait_rdy(devname, phy_addr); if (ret) return ret; ret = miiphy_read(devname, phy_addr, DATA_REG, data); return ret; } static int sw_reg_write(const char *devname, u8 phy_addr, u8 port, u8 reg, u16 data) { int ret; u16 value; ret = sw_wait_rdy(devname, phy_addr); if (ret) return ret; debug("%s: write to data: %#x\n", __func__, data); ret = miiphy_write(devname, phy_addr, DATA_REG, data); if (ret) return ret; value = SMI_HDR | SMIWR_OP | ((port & SMI_MASK) << PORT_SHIFT) | (reg & SMI_MASK); debug("%s: write to command: %#x\n", __func__, value); ret = miiphy_write(devname, phy_addr, COMMAND_REG, value); if (ret) return ret; ret = sw_wait_rdy(devname, phy_addr); if (ret) return ret; return 0; } static int ppu_enable(const char *devname, u8 phy_addr) { int i, ret = 0; u16 reg; ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, ®); if (ret) { printf("%s: Error reading global ctrl reg\n", __func__); return ret; } reg |= PPU_ENABLE; ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg); if (ret) { printf("%s: Error writing global ctrl reg\n", __func__); return ret; } for (i = 0; i < 1000; i++) { sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS, ®); if ((reg & 0xc000) == 0xc000) return 0; udelay(1000); } return -ETIMEDOUT; } static int ppu_disable(const char *devname, u8 phy_addr) { int i, ret = 0; u16 reg; ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, ®); if (ret) { printf("%s: Error reading global ctrl reg\n", __func__); return ret; } reg &= ~PPU_ENABLE; ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg); if (ret) { printf("%s: Error writing global ctrl reg\n", __func__); return ret; } for (i = 0; i < 1000; i++) { sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS, ®); if ((reg & 0xc000) != 0xc000) return 0; udelay(1000); } return -ETIMEDOUT; } int mv88e_sw_program(const char *devname, u8 phy_addr, struct mv88e_sw_reg *regs, int regs_nb) { int i, ret = 0; /* first we need to disable the PPU */ ret = ppu_disable(devname, phy_addr); if (ret) { printf("%s: Error disabling PPU\n", __func__); return ret; } for (i = 0; i < regs_nb; i++) { ret = sw_reg_write(devname, phy_addr, regs[i].port, regs[i].reg, regs[i].value); if (ret) { printf("%s: Error configuring switch\n", __func__); ppu_enable(devname, phy_addr); return ret; } } /* re-enable the PPU */ ret = ppu_enable(devname, phy_addr); if (ret) { printf("%s: Error enabling PPU\n", __func__); return ret; } return 0; } int mv88e_sw_reset(const char *devname, u8 phy_addr) { int i, ret = 0; u16 reg; ret = sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, ®); if (ret) { printf("%s: Error reading global ctrl reg\n", __func__); return ret; } reg = SW_RESET | PPU_ENABLE | 0x0400; ret = sw_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg); if (ret) { printf("%s: Error writing global ctrl reg\n", __func__); return ret; } for (i = 0; i < 1000; i++) { sw_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS, ®); if ((reg & 0xc800) != 0xc800) return 0; udelay(1000); } return -ETIMEDOUT; } int do_mvsw_reg_read(const char *name, int argc, char * const argv[]) { u16 value = 0, phyaddr, reg, port; int ret; phyaddr = simple_strtoul(argv[1], NULL, 10); port = simple_strtoul(argv[2], NULL, 10); reg = simple_strtoul(argv[3], NULL, 10); ret = sw_reg_read(name, phyaddr, port, reg, &value); printf("%#x\n", value); return ret; } int do_mvsw_reg_write(const char *name, int argc, char * const argv[]) { u16 value = 0, phyaddr, reg, port; int ret; phyaddr = simple_strtoul(argv[1], NULL, 10); port = simple_strtoul(argv[2], NULL, 10); reg = simple_strtoul(argv[3], NULL, 10); value = simple_strtoul(argv[4], NULL, 16); ret = sw_reg_write(name, phyaddr, port, reg, value); return ret; } int do_mvsw_reg(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int ret; const char *cmd, *ethname; if (argc < 2) return cmd_usage(cmdtp); cmd = argv[1]; --argc; ++argv; if (strcmp(cmd, "read") == 0) { if (argc < 5) return cmd_usage(cmdtp); ethname = argv[1]; --argc; ++argv; ret = do_mvsw_reg_read(ethname, argc, argv); } else if (strcmp(cmd, "write") == 0) { if (argc < 6) return cmd_usage(cmdtp); ethname = argv[1]; --argc; ++argv; ret = do_mvsw_reg_write(ethname, argc, argv); } else return cmd_usage(cmdtp); return ret; } U_BOOT_CMD( mvsw_reg, 7, 1, do_mvsw_reg, "marvell 88e6352 switch register access", "write ethname phyaddr port reg value\n" "mvsw_reg read ethname phyaddr port reg\n" );