u-boot/drivers/net/phy/mv88e6352.c
Tom Rini d678a59d2d Revert "Merge patch series "arm: dts: am62-beagleplay: Fix Beagleplay Ethernet""
When bringing in the series 'arm: dts: am62-beagleplay: Fix Beagleplay
Ethernet"' I failed to notice that b4 noticed it was based on next and
so took that as the base commit and merged that part of next to master.

This reverts commit c8ffd1356d, reversing
changes made to 2ee6f3a5f7.

Reported-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Tom Rini <trini@konsulko.com>
2024-05-19 08:16:36 -06:00

305 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 <command.h>
#include <log.h>
#include <miiphy.h>
#include <linux/delay.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, &reg);
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,
&reg);
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, &reg);
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,
&reg);
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, &reg);
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,
&reg);
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 = dectoul(argv[1], NULL);
port = dectoul(argv[2], NULL);
reg = dectoul(argv[3], NULL);
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 = dectoul(argv[1], NULL);
port = dectoul(argv[2], NULL);
reg = dectoul(argv[3], NULL);
value = hextoul(argv[4], NULL);
ret = sw_reg_write(name, phyaddr, port, reg, value);
return ret;
}
int do_mvsw_reg(struct cmd_tbl *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"
);