From 66458c49aa14ebc9ba2e4f9b6a323b8ff122807b Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 2 Aug 2019 10:33:28 +0200 Subject: mediatek: add v4.19 support Bump the target to v4.19. Add a patch with additional eth driver fixes/features that MTK provided aswell as the driver for the new mt7530 switch. Signed-off-by: John Crispin --- .../drivers/net/phy/mtk/mt753x/mt753x_mdio.c | 740 +++++++++++++++++++++ 1 file changed, 740 insertions(+) create mode 100644 target/linux/mediatek/files-4.19/drivers/net/phy/mtk/mt753x/mt753x_mdio.c (limited to 'target/linux/mediatek/files-4.19/drivers/net/phy/mtk/mt753x/mt753x_mdio.c') diff --git a/target/linux/mediatek/files-4.19/drivers/net/phy/mtk/mt753x/mt753x_mdio.c b/target/linux/mediatek/files-4.19/drivers/net/phy/mtk/mt753x/mt753x_mdio.c new file mode 100644 index 0000000000..695713eed4 --- /dev/null +++ b/target/linux/mediatek/files-4.19/drivers/net/phy/mtk/mt753x/mt753x_mdio.c @@ -0,0 +1,740 @@ +/* + * Driver for MediaTek MT753x gigabit switch + * + * Copyright (C) 2018 MediaTek Inc. All Rights Reserved. + * + * Author: Weijie Gao + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt753x.h" +#include "mt753x_swconfig.h" +#include "mt753x_regs.h" +#include "mt753x_nl.h" +#include "mt7530.h" +#include "mt7531.h" + +static u32 mt753x_id; +struct list_head mt753x_devs; +static DEFINE_MUTEX(mt753x_devs_lock); + +static struct mt753x_sw_id *mt753x_sw_ids[] = { + &mt7530_id, + &mt7531_id, +}; + +u32 mt753x_reg_read(struct gsw_mt753x *gsw, u32 reg) +{ + u32 high, low; + + mutex_lock(&gsw->host_bus->mdio_lock); + + gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, + (reg & MT753X_REG_PAGE_ADDR_M) >> MT753X_REG_PAGE_ADDR_S); + + low = gsw->host_bus->read(gsw->host_bus, gsw->smi_addr, + (reg & MT753X_REG_ADDR_M) >> MT753X_REG_ADDR_S); + + high = gsw->host_bus->read(gsw->host_bus, gsw->smi_addr, 0x10); + + mutex_unlock(&gsw->host_bus->mdio_lock); + + return (high << 16) | (low & 0xffff); +} + +void mt753x_reg_write(struct gsw_mt753x *gsw, u32 reg, u32 val) +{ + mutex_lock(&gsw->host_bus->mdio_lock); + + gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x1f, + (reg & MT753X_REG_PAGE_ADDR_M) >> MT753X_REG_PAGE_ADDR_S); + + gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, + (reg & MT753X_REG_ADDR_M) >> MT753X_REG_ADDR_S, val & 0xffff); + + gsw->host_bus->write(gsw->host_bus, gsw->smi_addr, 0x10, val >> 16); + + mutex_unlock(&gsw->host_bus->mdio_lock); +} + +/* Indirect MDIO clause 22/45 access */ +static int mt753x_mii_rw(struct gsw_mt753x *gsw, int phy, int reg, u16 data, + u32 cmd, u32 st) +{ + ktime_t timeout; + u32 val, timeout_us; + int ret = 0; + + timeout_us = 100000; + timeout = ktime_add_us(ktime_get(), timeout_us); + while (1) { + val = mt753x_reg_read(gsw, PHY_IAC); + + if ((val & PHY_ACS_ST) == 0) + break; + + if (ktime_compare(ktime_get(), timeout) > 0) + return -ETIMEDOUT; + } + + val = (st << MDIO_ST_S) | + ((cmd << MDIO_CMD_S) & MDIO_CMD_M) | + ((phy << MDIO_PHY_ADDR_S) & MDIO_PHY_ADDR_M) | + ((reg << MDIO_REG_ADDR_S) & MDIO_REG_ADDR_M); + + if (cmd == MDIO_CMD_WRITE || cmd == MDIO_CMD_ADDR) + val |= data & MDIO_RW_DATA_M; + + mt753x_reg_write(gsw, PHY_IAC, val | PHY_ACS_ST); + + timeout_us = 100000; + timeout = ktime_add_us(ktime_get(), timeout_us); + while (1) { + val = mt753x_reg_read(gsw, PHY_IAC); + + if ((val & PHY_ACS_ST) == 0) + break; + + if (ktime_compare(ktime_get(), timeout) > 0) + return -ETIMEDOUT; + } + + if (cmd == MDIO_CMD_READ || cmd == MDIO_CMD_READ_C45) { + val = mt753x_reg_read(gsw, PHY_IAC); + ret = val & MDIO_RW_DATA_M; + } + + return ret; +} + +int mt753x_mii_read(struct gsw_mt753x *gsw, int phy, int reg) +{ + int val; + + if (phy < MT753X_NUM_PHYS) + phy = (gsw->phy_base + phy) & MT753X_SMI_ADDR_MASK; + + mutex_lock(&gsw->mii_lock); + val = mt753x_mii_rw(gsw, phy, reg, 0, MDIO_CMD_READ, MDIO_ST_C22); + mutex_unlock(&gsw->mii_lock); + + return val; +} + +void mt753x_mii_write(struct gsw_mt753x *gsw, int phy, int reg, u16 val) +{ + if (phy < MT753X_NUM_PHYS) + phy = (gsw->phy_base + phy) & MT753X_SMI_ADDR_MASK; + + mutex_lock(&gsw->mii_lock); + mt753x_mii_rw(gsw, phy, reg, val, MDIO_CMD_WRITE, MDIO_ST_C22); + mutex_unlock(&gsw->mii_lock); +} + +int mt753x_mmd_read(struct gsw_mt753x *gsw, int addr, int devad, u16 reg) +{ + int val; + + if (addr < MT753X_NUM_PHYS) + addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; + + mutex_lock(&gsw->mii_lock); + mt753x_mii_rw(gsw, addr, devad, reg, MDIO_CMD_ADDR, MDIO_ST_C45); + val = mt753x_mii_rw(gsw, addr, devad, 0, MDIO_CMD_READ_C45, + MDIO_ST_C45); + mutex_unlock(&gsw->mii_lock); + + return val; +} + +void mt753x_mmd_write(struct gsw_mt753x *gsw, int addr, int devad, u16 reg, + u16 val) +{ + if (addr < MT753X_NUM_PHYS) + addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; + + mutex_lock(&gsw->mii_lock); + mt753x_mii_rw(gsw, addr, devad, reg, MDIO_CMD_ADDR, MDIO_ST_C45); + mt753x_mii_rw(gsw, addr, devad, val, MDIO_CMD_WRITE, MDIO_ST_C45); + mutex_unlock(&gsw->mii_lock); +} + +int mt753x_mmd_ind_read(struct gsw_mt753x *gsw, int addr, int devad, u16 reg) +{ + u16 val; + + if (addr < MT753X_NUM_PHYS) + addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; + + mutex_lock(&gsw->mii_lock); + + mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, + (MMD_ADDR << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), + MDIO_CMD_WRITE, MDIO_ST_C22); + + mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, reg, + MDIO_CMD_WRITE, MDIO_ST_C22); + + mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, + (MMD_DATA << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), + MDIO_CMD_WRITE, MDIO_ST_C22); + + val = mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, 0, + MDIO_CMD_READ, MDIO_ST_C22); + + mutex_unlock(&gsw->mii_lock); + + return val; +} + +void mt753x_mmd_ind_write(struct gsw_mt753x *gsw, int addr, int devad, u16 reg, + u16 val) +{ + if (addr < MT753X_NUM_PHYS) + addr = (gsw->phy_base + addr) & MT753X_SMI_ADDR_MASK; + + mutex_lock(&gsw->mii_lock); + + mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, + (MMD_ADDR << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), + MDIO_CMD_WRITE, MDIO_ST_C22); + + mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, reg, + MDIO_CMD_WRITE, MDIO_ST_C22); + + mt753x_mii_rw(gsw, addr, MII_MMD_ACC_CTL_REG, + (MMD_DATA << MMD_CMD_S) | + ((devad << MMD_DEVAD_S) & MMD_DEVAD_M), + MDIO_CMD_WRITE, MDIO_ST_C22); + + mt753x_mii_rw(gsw, addr, MII_MMD_ADDR_DATA_REG, val, + MDIO_CMD_WRITE, MDIO_ST_C22); + + mutex_unlock(&gsw->mii_lock); +} + +static void mt753x_load_port_cfg(struct gsw_mt753x *gsw) +{ + struct device_node *port_np; + struct device_node *fixed_link_node; + struct mt753x_port_cfg *port_cfg; + u32 port; + + for_each_child_of_node(gsw->dev->of_node, port_np) { + if (!of_device_is_compatible(port_np, "mediatek,mt753x-port")) + continue; + + if (!of_device_is_available(port_np)) + continue; + + if (of_property_read_u32(port_np, "reg", &port)) + continue; + + switch (port) { + case 5: + port_cfg = &gsw->port5_cfg; + break; + case 6: + port_cfg = &gsw->port6_cfg; + break; + default: + continue; + } + + if (port_cfg->enabled) { + dev_info(gsw->dev, "duplicated node for port%d\n", + port_cfg->phy_mode); + continue; + } + + port_cfg->np = port_np; + + port_cfg->phy_mode = of_get_phy_mode(port_np); + if (port_cfg->phy_mode < 0) { + dev_info(gsw->dev, "incorrect phy-mode %d\n", port); + continue; + } + + fixed_link_node = of_get_child_by_name(port_np, "fixed-link"); + if (fixed_link_node) { + u32 speed; + + port_cfg->force_link = 1; + port_cfg->duplex = of_property_read_bool( + fixed_link_node, + "full-duplex"); + + if (of_property_read_u32(fixed_link_node, "speed", + &speed)) { + speed = 0; + continue; + } + + of_node_put(fixed_link_node); + + switch (speed) { + case 10: + port_cfg->speed = MAC_SPD_10; + break; + case 100: + port_cfg->speed = MAC_SPD_100; + break; + case 1000: + port_cfg->speed = MAC_SPD_1000; + break; + case 2500: + port_cfg->speed = MAC_SPD_2500; + break; + default: + dev_info(gsw->dev, "incorrect speed %d\n", + speed); + continue; + } + } + + port_cfg->enabled = 1; + } +} + +static void mt753x_add_gsw(struct gsw_mt753x *gsw) +{ + mutex_lock(&mt753x_devs_lock); + gsw->id = mt753x_id++; + INIT_LIST_HEAD(&gsw->list); + list_add_tail(&gsw->list, &mt753x_devs); + mutex_unlock(&mt753x_devs_lock); +} + +static void mt753x_remove_gsw(struct gsw_mt753x *gsw) +{ + mutex_lock(&mt753x_devs_lock); + list_del(&gsw->list); + mutex_unlock(&mt753x_devs_lock); +} + +struct gsw_mt753x *mt753x_get_gsw(u32 id) +{ + struct gsw_mt753x *dev; + + mutex_lock(&mt753x_devs_lock); + + list_for_each_entry(dev, &mt753x_devs, list) { + if (dev->id == id) + return dev; + } + + mutex_unlock(&mt753x_devs_lock); + + return NULL; +} + +struct gsw_mt753x *mt753x_get_first_gsw(void) +{ + struct gsw_mt753x *dev; + + mutex_lock(&mt753x_devs_lock); + + list_for_each_entry(dev, &mt753x_devs, list) + return dev; + + mutex_unlock(&mt753x_devs_lock); + + return NULL; +} + +void mt753x_put_gsw(void) +{ + mutex_unlock(&mt753x_devs_lock); +} + +void mt753x_lock_gsw(void) +{ + mutex_lock(&mt753x_devs_lock); +} + +static int mt753x_hw_reset(struct gsw_mt753x *gsw) +{ + struct device_node *np = gsw->dev->of_node; + struct reset_control *rstc; + int mcm; + int ret = -EINVAL; + + mcm = of_property_read_bool(np, "mediatek,mcm"); + if (mcm) { + rstc = devm_reset_control_get(gsw->dev, "mcm"); + ret = IS_ERR(rstc); + if (IS_ERR(rstc)) { + dev_err(gsw->dev, "Missing reset ctrl of switch\n"); + return ret; + } + + reset_control_assert(rstc); + msleep(30); + reset_control_deassert(rstc); + + gsw->reset_pin = -1; + return 0; + } + + gsw->reset_pin = of_get_named_gpio(np, "reset-gpios", 0); + if (gsw->reset_pin < 0) { + dev_err(gsw->dev, "Missing reset pin of switch\n"); + return ret; + } + + ret = devm_gpio_request(gsw->dev, gsw->reset_pin, "mt753x-reset"); + if (ret) { + dev_info(gsw->dev, "Failed to request gpio %d\n", + gsw->reset_pin); + return ret; + } + + gpio_direction_output(gsw->reset_pin, 0); + msleep(30); + gpio_set_value(gsw->reset_pin, 1); + msleep(500); + + return 0; +} + +static int mt753x_mdio_read(struct mii_bus *bus, int addr, int reg) +{ + struct gsw_mt753x *gsw = bus->priv; + + return gsw->mii_read(gsw, addr, reg); +} + +static int mt753x_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val) +{ + struct gsw_mt753x *gsw = bus->priv; + + gsw->mii_write(gsw, addr, reg, val); + + return 0; +} + +static const struct net_device_ops mt753x_dummy_netdev_ops = { +}; + +static void mt753x_phy_link_handler(struct net_device *dev) +{ + struct mt753x_phy *phy = container_of(dev, struct mt753x_phy, netdev); + struct phy_device *phydev = phy->phydev; + struct gsw_mt753x *gsw = phy->gsw; + u32 port = phy - gsw->phys; + + if (phydev->link) { + dev_info(gsw->dev, + "Port %d Link is Up - %s/%s - flow control %s\n", + port, phy_speed_to_str(phydev->speed), + (phydev->duplex == DUPLEX_FULL) ? "Full" : "Half", + phydev->pause ? "rx/tx" : "off"); + } else { + dev_info(gsw->dev, "Port %d Link is Down\n", port); + } +} + +static void mt753x_connect_internal_phys(struct gsw_mt753x *gsw, + struct device_node *mii_np) +{ + struct device_node *phy_np; + struct mt753x_phy *phy; + int phy_mode; + u32 phyad; + + if (!mii_np) + return; + + for_each_child_of_node(mii_np, phy_np) { + if (of_property_read_u32(phy_np, "reg", &phyad)) + continue; + + if (phyad >= MT753X_NUM_PHYS) + continue; + + phy_mode = of_get_phy_mode(phy_np); + if (phy_mode < 0) { + dev_info(gsw->dev, "incorrect phy-mode %d for PHY %d\n", + phy_mode, phyad); + continue; + } + + phy = &gsw->phys[phyad]; + phy->gsw = gsw; + + init_dummy_netdev(&phy->netdev); + phy->netdev.netdev_ops = &mt753x_dummy_netdev_ops; + + phy->phydev = of_phy_connect(&phy->netdev, phy_np, + mt753x_phy_link_handler, 0, phy_mode); + if (!phy->phydev) { + dev_info(gsw->dev, "could not connect to PHY %d\n", + phyad); + continue; + } + + phy_start(phy->phydev); + } +} + +static void mt753x_disconnect_internal_phys(struct gsw_mt753x *gsw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(gsw->phys); i++) { + if (gsw->phys[i].phydev) { + phy_stop(gsw->phys[i].phydev); + phy_disconnect(gsw->phys[i].phydev); + gsw->phys[i].phydev = NULL; + } + } +} + +static int mt753x_mdio_register(struct gsw_mt753x *gsw) +{ + struct device_node *mii_np; + int i, ret; + + mii_np = of_get_child_by_name(gsw->dev->of_node, "mdio-bus"); + if (mii_np && !of_device_is_available(mii_np)) { + ret = -ENODEV; + goto err_put_node; + } + + gsw->gphy_bus = devm_mdiobus_alloc(gsw->dev); + if (!gsw->gphy_bus) { + ret = -ENOMEM; + goto err_put_node; + } + + gsw->gphy_bus->name = "mt753x_mdio"; + gsw->gphy_bus->read = mt753x_mdio_read; + gsw->gphy_bus->write = mt753x_mdio_write; + gsw->gphy_bus->priv = gsw; + gsw->gphy_bus->parent = gsw->dev; + gsw->gphy_bus->phy_mask = BIT(MT753X_NUM_PHYS) - 1; + + for (i = 0; i < PHY_MAX_ADDR; i++) + gsw->gphy_bus->irq[i] = PHY_POLL; + + if (mii_np) + snprintf(gsw->gphy_bus->id, MII_BUS_ID_SIZE, "%s@%s", + mii_np->name, gsw->dev->of_node->name); + else + snprintf(gsw->gphy_bus->id, MII_BUS_ID_SIZE, "mdio@%s", + gsw->dev->of_node->name); + + ret = of_mdiobus_register(gsw->gphy_bus, mii_np); + + if (ret) { + devm_mdiobus_free(gsw->dev, gsw->gphy_bus); + gsw->gphy_bus = NULL; + } else { + if (gsw->phy_status_poll) + mt753x_connect_internal_phys(gsw, mii_np); + } + +err_put_node: + if (mii_np) + of_node_put(mii_np); + + return ret; +} + +static irqreturn_t mt753x_irq_handler(int irq, void *dev) +{ + struct gsw_mt753x *gsw = dev; + + disable_irq_nosync(gsw->irq); + + schedule_work(&gsw->irq_worker); + + return IRQ_HANDLED; +} + +static int mt753x_probe(struct platform_device *pdev) +{ + struct gsw_mt753x *gsw; + struct mt753x_sw_id *sw; + struct device_node *np = pdev->dev.of_node; + struct device_node *mdio; + struct mii_bus *mdio_bus; + int ret = -EINVAL; + struct chip_rev rev; + int i; + + mdio = of_parse_phandle(np, "mediatek,mdio", 0); + if (!mdio) + return -EINVAL; + + mdio_bus = of_mdio_find_bus(mdio); + if (!mdio_bus) + return -EPROBE_DEFER; + + gsw = devm_kzalloc(&pdev->dev, sizeof(struct gsw_mt753x), GFP_KERNEL); + if (!gsw) + return -ENOMEM; + + gsw->host_bus = mdio_bus; + gsw->dev = &pdev->dev; + mutex_init(&gsw->mii_lock); + + /* Switch hard reset */ + mt753x_hw_reset(gsw); + + /* Fetch the SMI address dirst */ + if (of_property_read_u32(np, "mediatek,smi-addr", &gsw->smi_addr)) + gsw->smi_addr = MT753X_DFL_SMI_ADDR; + + /* Load MAC port configurations */ + mt753x_load_port_cfg(gsw); + + /* Check for valid switch and then initialize */ + for (i = 0; i < ARRAY_SIZE(mt753x_sw_ids); i++) { + if (!mt753x_sw_ids[i]->detect(gsw, &rev)) { + sw = mt753x_sw_ids[i]; + + gsw->name = rev.name; + gsw->model = sw->model; + + dev_info(gsw->dev, "Switch is MediaTek %s rev %d", + gsw->name, rev.rev); + + /* Initialize the switch */ + ret = sw->init(gsw); + if (ret) + goto fail; + + break; + } + } + + if (i >= ARRAY_SIZE(mt753x_sw_ids)) { + dev_err(gsw->dev, "No mt753x switch found\n"); + goto fail; + } + + gsw->irq = platform_get_irq(pdev, 0); + if (gsw->irq >= 0) { + ret = devm_request_irq(gsw->dev, gsw->irq, mt753x_irq_handler, + 0, dev_name(gsw->dev), gsw); + if (ret) { + dev_err(gsw->dev, "Failed to request irq %d\n", + gsw->irq); + goto fail; + } + + INIT_WORK(&gsw->irq_worker, mt753x_irq_worker); + } + + platform_set_drvdata(pdev, gsw); + + gsw->phy_status_poll = of_property_read_bool(gsw->dev->of_node, + "mediatek,phy-poll"); + + mt753x_add_gsw(gsw); + + mt753x_mdio_register(gsw); + +#ifdef CONFIG_SWCONFIG + mt753x_swconfig_init(gsw); +#endif + + if (sw->post_init) + sw->post_init(gsw); + + if (gsw->irq >= 0) + mt753x_irq_enable(gsw); + + return 0; + +fail: + devm_kfree(&pdev->dev, gsw); + + return ret; +} + +static int mt753x_remove(struct platform_device *pdev) +{ + struct gsw_mt753x *gsw = platform_get_drvdata(pdev); + + if (gsw->irq >= 0) + cancel_work_sync(&gsw->irq_worker); + + if (gsw->reset_pin >= 0) + devm_gpio_free(&pdev->dev, gsw->reset_pin); + +#ifdef CONFIG_SWCONFIG + mt753x_swconfig_destroy(gsw); +#endif + + mt753x_disconnect_internal_phys(gsw); + + mdiobus_unregister(gsw->gphy_bus); + + mt753x_remove_gsw(gsw); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id mt753x_ids[] = { + { .compatible = "mediatek,mt753x" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, mt753x_ids); + +static struct platform_driver mt753x_driver = { + .probe = mt753x_probe, + .remove = mt753x_remove, + .driver = { + .name = "mt753x", + .of_match_table = mt753x_ids, + }, +}; + +static int __init mt753x_init(void) +{ + int ret; + + INIT_LIST_HEAD(&mt753x_devs); + ret = platform_driver_register(&mt753x_driver); + + mt753x_nl_init(); + + return ret; +} +module_init(mt753x_init); + +static void __exit mt753x_exit(void) +{ + mt753x_nl_exit(); + + platform_driver_unregister(&mt753x_driver); +} +module_exit(mt753x_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Weijie Gao "); +MODULE_DESCRIPTION("Driver for MediaTek MT753x Gigabit Switch"); -- cgit v1.2.3