mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-06 13:14:27 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
301 lines
6.1 KiB
C
301 lines
6.1 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* (C) Copyright 2012
|
|
* Valentin Lontgchamp, Keymile AG, valentin.longchamp@keymile.com
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <miiphy.h>
|
|
#include <linux/errno.h>
|
|
#include <mv88e6352.h>
|
|
|
|
#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"
|
|
);
|