blob: 9703dbfaf1ac68ef101b433a5534d27839f40725 [file] [log] [blame]
/*
* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <common.h>
#include <command.h>
#include <miiphy.h>
#include <phy.h>
#include <asm/io.h>
#include <errno.h>
#include "ipq_mdio.h"
struct ipq_mdio_data {
struct mii_bus *bus;
int phy_irq[PHY_MAX_ADDR];
};
static int ipq_mdio_wait_busy(void)
{
int i;
u32 busy;
for (i = 0; i < IPQ_MDIO_RETRY; i++) {
udelay(IPQ_MDIO_DELAY);
busy = readl(IPQ_MDIO_BASE +
MDIO_CTRL_4_REG) &
MDIO_CTRL_4_ACCESS_BUSY;
if (!busy)
return 0;
udelay(IPQ_MDIO_DELAY);
}
printf("%s: MDIO operation timed out\n",
__func__);
return -ETIMEDOUT;
}
int ipq_mdio_write(int mii_id, int regnum, u16 value)
{
u32 cmd;
if (ipq_mdio_wait_busy())
return -ETIMEDOUT;
if (regnum & MII_ADDR_C45) {
unsigned int mmd = (regnum >> 16) & 0x1F;
unsigned int reg = regnum & 0xFFFF;
writel(CTRL_0_REG_C45_DEFAULT_VALUE,
IPQ_MDIO_BASE + MDIO_CTRL_0_REG);
/* Issue the phy address and reg */
writel((mii_id << 8) | mmd,
IPQ_MDIO_BASE + MDIO_CTRL_1_REG);
writel(reg, IPQ_MDIO_BASE + MDIO_CTRL_2_REG);
/* issue read command */
cmd = MDIO_CTRL_4_ACCESS_START | MDIO_CTRL_4_ACCESS_CODE_C45_ADDR;
writel(cmd, IPQ_MDIO_BASE + MDIO_CTRL_4_REG);
if (ipq_mdio_wait_busy())
return -ETIMEDOUT;
} else {
writel(CTRL_0_REG_DEFAULT_VALUE,
IPQ_MDIO_BASE + MDIO_CTRL_0_REG);
/* Issue the phy addreass and reg */
writel((mii_id << 8 | regnum),
IPQ_MDIO_BASE + MDIO_CTRL_1_REG);
}
/* Issue a write data */
writel(value, IPQ_MDIO_BASE + MDIO_CTRL_2_REG);
if (regnum & MII_ADDR_C45) {
cmd = MDIO_CTRL_4_ACCESS_START | MDIO_CTRL_4_ACCESS_CODE_C45_WRITE ;
} else {
cmd = MDIO_CTRL_4_ACCESS_START | MDIO_CTRL_4_ACCESS_CODE_WRITE ;
}
writel(cmd, IPQ_MDIO_BASE + MDIO_CTRL_4_REG);
/* Wait for write complete */
if (ipq_mdio_wait_busy())
return -ETIMEDOUT;
return 0;
}
int ipq_mdio_read(int mii_id, int regnum, ushort *data)
{
u32 val,cmd;
if (ipq_mdio_wait_busy())
return -ETIMEDOUT;
if (regnum & MII_ADDR_C45) {
unsigned int mmd = (regnum >> 16) & 0x1F;
unsigned int reg = regnum & 0xFFFF;
writel(CTRL_0_REG_C45_DEFAULT_VALUE,
IPQ_MDIO_BASE + MDIO_CTRL_0_REG);
/* Issue the phy address and reg */
writel((mii_id << 8) | mmd,
IPQ_MDIO_BASE + MDIO_CTRL_1_REG);
writel(reg, IPQ_MDIO_BASE + MDIO_CTRL_2_REG);
/* issue read command */
cmd = MDIO_CTRL_4_ACCESS_START | MDIO_CTRL_4_ACCESS_CODE_C45_ADDR;
} else {
writel(CTRL_0_REG_DEFAULT_VALUE,
IPQ_MDIO_BASE + MDIO_CTRL_0_REG);
/* Issue the phy address and reg */
writel((mii_id << 8 | regnum ) ,
IPQ_MDIO_BASE + MDIO_CTRL_1_REG);
/* issue read command */
cmd = MDIO_CTRL_4_ACCESS_START | MDIO_CTRL_4_ACCESS_CODE_READ ;
}
/* issue read command */
writel(cmd, IPQ_MDIO_BASE + MDIO_CTRL_4_REG);
if (ipq_mdio_wait_busy())
return -ETIMEDOUT;
if (regnum & MII_ADDR_C45) {
cmd = MDIO_CTRL_4_ACCESS_START | MDIO_CTRL_4_ACCESS_CODE_C45_READ;
writel(cmd, IPQ_MDIO_BASE + MDIO_CTRL_4_REG);
if (ipq_mdio_wait_busy())
return -ETIMEDOUT;
}
/* Read data */
val = readl(IPQ_MDIO_BASE + MDIO_CTRL_3_REG);
if (data != NULL)
*data = val;
return val;
}
int ipq_phy_write(struct mii_dev *bus,
int addr, int dev_addr,
int regnum, ushort value)
{
return ipq_mdio_write(addr, regnum, value);
}
int ipq_phy_read(struct mii_dev *bus,
int addr, int dev_addr, int regnum)
{
return ipq_mdio_read(addr, regnum, NULL);
}
int ipq_sw_mdio_init(const char *name)
{
struct mii_dev *bus = mdio_alloc();
if(!bus) {
printf("Failed to allocate IPQ MDIO bus\n");
return -1;
}
bus->read = ipq_phy_read;
bus->write = ipq_phy_write;
bus->reset = NULL;
snprintf(bus->name, MDIO_NAME_LEN, name);
return mdio_register(bus);
}
static int do_ipq_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
char op[2];
unsigned int addr = 0, reg = 0;
unsigned short data = 0;
if (argc < 2)
return CMD_RET_USAGE;
op[0] = argv[1][0];
if (strlen(argv[1]) > 1)
op[1] = argv[1][1];
else
op[1] = '\0';
if (argc >= 3)
addr = simple_strtoul(argv[2], NULL, 16);
if (argc >= 4)
reg = simple_strtoul(argv[3], NULL, 16);
if (argc >= 5)
data = simple_strtoul(argv[4], NULL, 16);
if (op[0] == 'r') {
data = ipq_mdio_read(addr, reg, NULL);
printf("0x%x\n", data);
} else if (op[0] == 'w') {
ipq_mdio_write(addr, reg, data);
} else {
return CMD_RET_USAGE;
}
return 0;
}
U_BOOT_CMD(
ipq_mdio, 5, 1, do_ipq_mdio,
"IPQ mdio utility commands",
"ipq_mdio read <addr> <reg> - read IPQ MDIO PHY <addr> register <reg>\n"
"ipq_mdio write <addr> <reg> <data> - write IPQ MDIO PHY <addr> register <reg>\n"
"Addr and/or reg may be ranges, e.g. 0-7."
);