diff --git a/drivers/net/ipq40xx/ipq40xx_mdio.c b/drivers/net/ipq40xx/ipq40xx_mdio.c new file mode 100644 index 0000000000..702ba6334b --- /dev/null +++ b/drivers/net/ipq40xx/ipq40xx_mdio.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2015-2016, 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 +#include +#include +#include +#include +#include "ipq40xx_mdio.h" + +struct ipq40xx_mdio_data { + struct mii_bus *bus; + int phy_irq[PHY_MAX_ADDR]; +}; + +static int ipq40xx_mdio_wait_busy(void) +{ + int i; + u32 busy; + for (i = 0; i < IPQ40XX_MDIO_RETRY; i++) { + udelay(IPQ40XX_MDIO_DELAY); + busy = readl(IPQ40XX_MDIO_BASE + + MDIO_CTRL_4_REG) & + MDIO_CTRL_4_ACCESS_BUSY; + + if (!busy) + return 0; + udelay(IPQ40XX_MDIO_DELAY); + } + printf("%s: MDIO operation timed out\n", + __func__); + return -ETIMEDOUT; +} + +int ipq40xx_mdio_write(int mii_id, int regnum, u16 value) +{ + if (ipq40xx_mdio_wait_busy()) + return -ETIMEDOUT; + /* Issue the phy addreass and reg */ + writel((mii_id << 8 | regnum), + IPQ40XX_MDIO_BASE + MDIO_CTRL_1_REG); + + /* Issue a write data */ + writel(value, IPQ40XX_MDIO_BASE + MDIO_CTRL_2_REG); + + /* Issue write command */ + writel((MDIO_CTRL_4_ACCESS_START | + MDIO_CTRL_4_ACCESS_CODE_WRITE), + (IPQ40XX_MDIO_BASE + MDIO_CTRL_4_REG)); + + /* Wait for write complete */ + + if (ipq40xx_mdio_wait_busy()) + return -ETIMEDOUT; + + return 0; +} + +int ipq40xx_mdio_read(int mii_id, int regnum, ushort *data) +{ + u32 val; + if (ipq40xx_mdio_wait_busy()) + return -ETIMEDOUT; + + /* Issue the phy address and reg */ + writel((mii_id << 8) | regnum, + IPQ40XX_MDIO_BASE + MDIO_CTRL_1_REG); + + /* issue read command */ + writel((MDIO_CTRL_4_ACCESS_START | + MDIO_CTRL_4_ACCESS_CODE_READ), + (IPQ40XX_MDIO_BASE + MDIO_CTRL_4_REG)); + + if (ipq40xx_mdio_wait_busy()) + return -ETIMEDOUT; + + /* Read data */ + val = readl(IPQ40XX_MDIO_BASE + MDIO_CTRL_3_REG); + + if (data != NULL) + *data = val; + + return val; +} + +int ipq40xx_phy_write(struct mii_dev *bus, + int addr, int dev_addr, + int regnum, ushort value) +{ + return ipq40xx_mdio_write( + addr, regnum, value); +} + +int ipq40xx_phy_read(struct mii_dev *bus, + int addr, int dev_addr, int regnum) +{ + return ipq40xx_mdio_read( + addr, regnum, NULL); +} + +int ipq40xx_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 = ipq40xx_phy_read; + bus->write = ipq40xx_phy_write; + bus->reset = NULL; + sprintf(bus->name, name); + return mdio_register(bus); +} + diff --git a/drivers/net/ipq40xx/ipq40xx_mdio.h b/drivers/net/ipq40xx/ipq40xx_mdio.h new file mode 100644 index 0000000000..16b4a88c05 --- /dev/null +++ b/drivers/net/ipq40xx/ipq40xx_mdio.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2015-2016, 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. +*/ +#ifndef _IPQ40XX_MDIO_H +#define _IPQ40XX_MDIO_H + +#define IPQ40XX_MDIO_BASE 0x90000 +#define MDIO_CTRL_0_REG 0x40 +#define MDIO_CTRL_1_REG 0x44 +#define MDIO_CTRL_2_REG 0x48 +#define MDIO_CTRL_3_REG 0x4c +#define MDIO_CTRL_4_REG 0x50 +#define MDIO_CTRL_4_ACCESS_BUSY (1 << 16) +#define MDIO_CTRL_4_ACCESS_START (1 << 8) +#define MDIO_CTRL_4_ACCESS_CODE_READ 0 +#define MDIO_CTRL_4_ACCESS_CODE_WRITE 1 + +#define IPQ40XX_MDIO_RETRY 1000 +#define IPQ40XX_MDIO_DELAY 5 +#endif /* End _IPQ40XX_MDIO_H */