diff options
Diffstat (limited to 'drivers/net/ethernet/hisilicon/hns_mdio.c')
| -rw-r--r-- | drivers/net/ethernet/hisilicon/hns_mdio.c | 245 |
1 files changed, 158 insertions, 87 deletions
diff --git a/drivers/net/ethernet/hisilicon/hns_mdio.c b/drivers/net/ethernet/hisilicon/hns_mdio.c index 017e08452d8c..6812be8dc64f 100644 --- a/drivers/net/ethernet/hisilicon/hns_mdio.c +++ b/drivers/net/ethernet/hisilicon/hns_mdio.c @@ -1,10 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2014-2015 Hisilicon Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. */ #include <linux/acpi.h> @@ -39,7 +35,7 @@ struct hns_mdio_sc_reg { }; struct hns_mdio_device { - void *vbase; /* mdio reg base address */ + u8 __iomem *vbase; /* mdio reg base address */ struct regmap *subctrl_vbase; struct hns_mdio_sc_reg sc_reg; }; @@ -96,21 +92,17 @@ enum mdio_c45_op_seq { #define MDIO_SC_CLK_ST 0x531C #define MDIO_SC_RESET_ST 0x5A1C -static void mdio_write_reg(void *base, u32 reg, u32 value) +static void mdio_write_reg(u8 __iomem *base, u32 reg, u32 value) { - u8 __iomem *reg_addr = (u8 __iomem *)base; - - writel_relaxed(value, reg_addr + reg); + writel_relaxed(value, base + reg); } #define MDIO_WRITE_REG(a, reg, value) \ mdio_write_reg((a)->vbase, (reg), (value)) -static u32 mdio_read_reg(void *base, u32 reg) +static u32 mdio_read_reg(u8 __iomem *base, u32 reg) { - u8 __iomem *reg_addr = (u8 __iomem *)base; - - return readl_relaxed(reg_addr + reg); + return readl_relaxed(base + reg); } #define mdio_set_field(origin, mask, shift, val) \ @@ -121,7 +113,7 @@ static u32 mdio_read_reg(void *base, u32 reg) #define mdio_get_field(origin, mask, shift) (((origin) >> (shift)) & (mask)) -static void mdio_set_reg_field(void *base, u32 reg, u32 mask, u32 shift, +static void mdio_set_reg_field(u8 __iomem *base, u32 reg, u32 mask, u32 shift, u32 val) { u32 origin = mdio_read_reg(base, reg); @@ -133,7 +125,7 @@ static void mdio_set_reg_field(void *base, u32 reg, u32 mask, u32 shift, #define MDIO_SET_REG_FIELD(dev, reg, mask, shift, val) \ mdio_set_reg_field((dev)->vbase, (reg), (mask), (shift), (val)) -static u32 mdio_get_reg_field(void *base, u32 reg, u32 mask, u32 shift) +static u32 mdio_get_reg_field(u8 __iomem *base, u32 reg, u32 mask, u32 shift) { u32 origin; @@ -156,11 +148,15 @@ static int mdio_sc_cfg_reg_write(struct hns_mdio_device *mdio_dev, { u32 time_cnt; u32 reg_value; + int ret; regmap_write(mdio_dev->subctrl_vbase, cfg_reg, set_val); for (time_cnt = MDIO_TIMEOUT; time_cnt; time_cnt--) { - regmap_read(mdio_dev->subctrl_vbase, st_reg, ®_value); + ret = regmap_read(mdio_dev->subctrl_vbase, st_reg, ®_value); + if (ret) + return ret; + reg_value &= st_msk; if ((!!check_st) == (!!reg_value)) break; @@ -178,7 +174,7 @@ static int hns_mdio_wait_ready(struct mii_bus *bus) u32 cmd_reg_value; int i; - /* waitting for MDIO_COMMAND_REG 's mdio_start==0 */ + /* waiting for MDIO_COMMAND_REG's mdio_start==0 */ /* after that can do read or write*/ for (i = 0; i < MDIO_TIMEOUT; i++) { cmd_reg_value = MDIO_GET_REG_BIT(mdio_dev, @@ -210,29 +206,27 @@ static void hns_mdio_cmd_write(struct hns_mdio_device *mdio_dev, } /** - * hns_mdio_write - access phy register + * hns_mdio_write_c22 - access phy register * @bus: mdio bus * @phy_id: phy id * @regnum: register num - * @value: register value + * @data: register value * * Return 0 on success, negative on failure */ -static int hns_mdio_write(struct mii_bus *bus, - int phy_id, int regnum, u16 data) +static int hns_mdio_write_c22(struct mii_bus *bus, + int phy_id, int regnum, u16 data) { - int ret; - struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; - u8 devad = ((regnum >> 16) & 0x1f); - u8 is_c45 = !!(regnum & MII_ADDR_C45); + struct hns_mdio_device *mdio_dev = bus->priv; u16 reg = (u16)(regnum & 0xffff); - u8 op; u16 cmd_reg_cfg; + int ret; + u8 op; dev_dbg(&bus->dev, "mdio write %s,base is %p\n", bus->id, mdio_dev->vbase); - dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x, write data=%d\n", - phy_id, is_c45, devad, reg, data); + dev_dbg(&bus->dev, "phy id=%d, reg=%#x, write data=%d\n", + phy_id, reg, data); /* wait for ready */ ret = hns_mdio_wait_ready(bus); @@ -241,59 +235,91 @@ static int hns_mdio_write(struct mii_bus *bus, return ret; } - if (!is_c45) { - cmd_reg_cfg = reg; - op = MDIO_C22_WRITE; - } else { - /* config the cmd-reg to write addr*/ - MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, - MDIO_ADDR_DATA_S, reg); + cmd_reg_cfg = reg; + op = MDIO_C22_WRITE; + + MDIO_SET_REG_FIELD(mdio_dev, MDIO_WDATA_REG, MDIO_WDATA_DATA_M, + MDIO_WDATA_DATA_S, data); - hns_mdio_cmd_write(mdio_dev, is_c45, - MDIO_C45_WRITE_ADDR, phy_id, devad); + hns_mdio_cmd_write(mdio_dev, false, op, phy_id, cmd_reg_cfg); - /* check for read or write opt is finished */ - ret = hns_mdio_wait_ready(bus); - if (ret) { - dev_err(&bus->dev, "MDIO bus is busy\n"); - return ret; - } + return 0; +} - /* config the data needed writing */ - cmd_reg_cfg = devad; - op = MDIO_C45_WRITE_DATA; +/** + * hns_mdio_write_c45 - access phy register + * @bus: mdio bus + * @phy_id: phy id + * @devad: device address to read + * @regnum: register num + * @data: register value + * + * Return 0 on success, negative on failure + */ +static int hns_mdio_write_c45(struct mii_bus *bus, int phy_id, int devad, + int regnum, u16 data) +{ + struct hns_mdio_device *mdio_dev = bus->priv; + u16 reg = (u16)(regnum & 0xffff); + u16 cmd_reg_cfg; + int ret; + u8 op; + + dev_dbg(&bus->dev, "mdio write %s,base is %p\n", + bus->id, mdio_dev->vbase); + dev_dbg(&bus->dev, "phy id=%d, devad=%d, reg=%#x, write data=%d\n", + phy_id, devad, reg, data); + + /* wait for ready */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; } + /* config the cmd-reg to write addr*/ + MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, + MDIO_ADDR_DATA_S, reg); + + hns_mdio_cmd_write(mdio_dev, true, MDIO_C45_WRITE_ADDR, phy_id, devad); + + /* check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + /* config the data needed writing */ + cmd_reg_cfg = devad; + op = MDIO_C45_WRITE_DATA; + MDIO_SET_REG_FIELD(mdio_dev, MDIO_WDATA_REG, MDIO_WDATA_DATA_M, MDIO_WDATA_DATA_S, data); - hns_mdio_cmd_write(mdio_dev, is_c45, op, phy_id, cmd_reg_cfg); + hns_mdio_cmd_write(mdio_dev, true, op, phy_id, cmd_reg_cfg); return 0; } /** - * hns_mdio_read - access phy register + * hns_mdio_read_c22 - access phy register * @bus: mdio bus * @phy_id: phy id * @regnum: register num - * @value: register value * * Return phy register value */ -static int hns_mdio_read(struct mii_bus *bus, int phy_id, int regnum) +static int hns_mdio_read_c22(struct mii_bus *bus, int phy_id, int regnum) { - int ret; - u16 reg_val = 0; - u8 devad = ((regnum >> 16) & 0x1f); - u8 is_c45 = !!(regnum & MII_ADDR_C45); + struct hns_mdio_device *mdio_dev = bus->priv; u16 reg = (u16)(regnum & 0xffff); - struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; + u16 reg_val; + int ret; dev_dbg(&bus->dev, "mdio read %s,base is %p\n", bus->id, mdio_dev->vbase); - dev_dbg(&bus->dev, "phy id=%d, is_c45=%d, devad=%d, reg=%#x!\n", - phy_id, is_c45, devad, reg); + dev_dbg(&bus->dev, "phy id=%d, reg=%#x!\n", phy_id, reg); /* Step 1: wait for ready */ ret = hns_mdio_wait_ready(bus); @@ -302,29 +328,74 @@ static int hns_mdio_read(struct mii_bus *bus, int phy_id, int regnum) return ret; } - if (!is_c45) { - hns_mdio_cmd_write(mdio_dev, is_c45, - MDIO_C22_READ, phy_id, reg); - } else { - MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, - MDIO_ADDR_DATA_S, reg); + hns_mdio_cmd_write(mdio_dev, false, MDIO_C22_READ, phy_id, reg); - /* Step 2; config the cmd-reg to write addr*/ - hns_mdio_cmd_write(mdio_dev, is_c45, - MDIO_C45_WRITE_ADDR, phy_id, devad); + /* Step 2: waiting for MDIO_COMMAND_REG 's mdio_start==0,*/ + /* check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } - /* Step 3: check for read or write opt is finished */ - ret = hns_mdio_wait_ready(bus); - if (ret) { - dev_err(&bus->dev, "MDIO bus is busy\n"); - return ret; - } + reg_val = MDIO_GET_REG_BIT(mdio_dev, MDIO_STA_REG, MDIO_STATE_STA_B); + if (reg_val) { + dev_err(&bus->dev, " ERROR! MDIO Read failed!\n"); + return -EBUSY; + } + + /* Step 3; get out data*/ + reg_val = (u16)MDIO_GET_REG_FIELD(mdio_dev, MDIO_RDATA_REG, + MDIO_RDATA_DATA_M, MDIO_RDATA_DATA_S); + + return reg_val; +} + +/** + * hns_mdio_read_c45 - access phy register + * @bus: mdio bus + * @phy_id: phy id + * @devad: device address to read + * @regnum: register num + * + * Return phy register value + */ +static int hns_mdio_read_c45(struct mii_bus *bus, int phy_id, int devad, + int regnum) +{ + struct hns_mdio_device *mdio_dev = bus->priv; + u16 reg = (u16)(regnum & 0xffff); + u16 reg_val; + int ret; - hns_mdio_cmd_write(mdio_dev, is_c45, - MDIO_C45_WRITE_ADDR, phy_id, devad); + dev_dbg(&bus->dev, "mdio read %s,base is %p\n", + bus->id, mdio_dev->vbase); + dev_dbg(&bus->dev, "phy id=%d, devad=%d, reg=%#x!\n", + phy_id, devad, reg); + + /* Step 1: wait for ready */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; } - /* Step 5: waitting for MDIO_COMMAND_REG 's mdio_start==0,*/ + MDIO_SET_REG_FIELD(mdio_dev, MDIO_ADDR_REG, MDIO_ADDR_DATA_M, + MDIO_ADDR_DATA_S, reg); + + /* Step 2; config the cmd-reg to write addr*/ + hns_mdio_cmd_write(mdio_dev, true, MDIO_C45_WRITE_ADDR, phy_id, devad); + + /* Step 3: check for read or write opt is finished */ + ret = hns_mdio_wait_ready(bus); + if (ret) { + dev_err(&bus->dev, "MDIO bus is busy\n"); + return ret; + } + + hns_mdio_cmd_write(mdio_dev, true, MDIO_C45_READ, phy_id, devad); + + /* Step 5: waiting for MDIO_COMMAND_REG 's mdio_start==0,*/ /* check for read or write opt is finished */ ret = hns_mdio_wait_ready(bus); if (ret) { @@ -353,13 +424,13 @@ static int hns_mdio_read(struct mii_bus *bus, int phy_id, int regnum) */ static int hns_mdio_reset(struct mii_bus *bus) { - struct hns_mdio_device *mdio_dev = (struct hns_mdio_device *)bus->priv; + struct hns_mdio_device *mdio_dev = bus->priv; const struct hns_mdio_sc_reg *sc_reg; int ret; if (dev_of_node(bus->parent)) { if (!mdio_dev->subctrl_vbase) { - dev_err(&bus->dev, "mdio sys ctl reg has not maped\n"); + dev_err(&bus->dev, "mdio sys ctl reg has not mapped\n"); return -ENODEV; } @@ -425,8 +496,7 @@ static int hns_mdio_probe(struct platform_device *pdev) { struct hns_mdio_device *mdio_dev; struct mii_bus *new_bus; - struct resource *res; - int ret = -ENODEV; + int ret; if (!pdev) { dev_err(NULL, "pdev is NULL!\r\n"); @@ -444,14 +514,15 @@ static int hns_mdio_probe(struct platform_device *pdev) } new_bus->name = MDIO_BUS_NAME; - new_bus->read = hns_mdio_read; - new_bus->write = hns_mdio_write; + new_bus->read = hns_mdio_read_c22; + new_bus->write = hns_mdio_write_c22; + new_bus->read_c45 = hns_mdio_read_c45; + new_bus->write_c45 = hns_mdio_write_c45; new_bus->reset = hns_mdio_reset; new_bus->priv = mdio_dev; new_bus->parent = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mdio_dev->vbase = devm_ioremap_resource(&pdev->dev, res); + mdio_dev->vbase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mdio_dev->vbase)) { ret = PTR_ERR(mdio_dev->vbase); return ret; @@ -504,6 +575,7 @@ static int hns_mdio_probe(struct platform_device *pdev) MDIO_SC_RESET_ST; } } + of_node_put(reg_args.np); } else { dev_warn(&pdev->dev, "find syscon ret = %#x\n", ret); mdio_dev->subctrl_vbase = NULL; @@ -539,7 +611,7 @@ static int hns_mdio_probe(struct platform_device *pdev) * * Return 0 on success, negative on failure */ -static int hns_mdio_remove(struct platform_device *pdev) +static void hns_mdio_remove(struct platform_device *pdev) { struct mii_bus *bus; @@ -547,7 +619,6 @@ static int hns_mdio_remove(struct platform_device *pdev) mdiobus_unregister(bus); platform_set_drvdata(pdev, NULL); - return 0; } static const struct of_device_id hns_mdio_match[] = { @@ -569,7 +640,7 @@ static struct platform_driver hns_mdio_driver = { .driver = { .name = MDIO_DRV_NAME, .of_match_table = hns_mdio_match, - .acpi_match_table = ACPI_PTR(hns_mdio_acpi_match), + .acpi_match_table = hns_mdio_acpi_match, }, }; |
