11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2ba8b0ee8SPengcheng Li /* 3ba8b0ee8SPengcheng Li * HiSilicon INNO USB2 PHY Driver. 4ba8b0ee8SPengcheng Li * 5ba8b0ee8SPengcheng Li * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd. 6ba8b0ee8SPengcheng Li */ 7ba8b0ee8SPengcheng Li 8ba8b0ee8SPengcheng Li #include <linux/clk.h> 9ba8b0ee8SPengcheng Li #include <linux/delay.h> 10ba8b0ee8SPengcheng Li #include <linux/io.h> 11ba8b0ee8SPengcheng Li #include <linux/module.h> 127559e757SRob Herring #include <linux/of.h> 13ba8b0ee8SPengcheng Li #include <linux/phy/phy.h> 147559e757SRob Herring #include <linux/platform_device.h> 15ba8b0ee8SPengcheng Li #include <linux/reset.h> 16ba8b0ee8SPengcheng Li 17ba8b0ee8SPengcheng Li #define INNO_PHY_PORT_NUM 2 18ba8b0ee8SPengcheng Li #define REF_CLK_STABLE_TIME 100 /* unit:us */ 19ba8b0ee8SPengcheng Li #define UTMI_CLK_STABLE_TIME 200 /* unit:us */ 20ba8b0ee8SPengcheng Li #define TEST_CLK_STABLE_TIME 2 /* unit:ms */ 21ba8b0ee8SPengcheng Li #define PHY_CLK_STABLE_TIME 2 /* unit:ms */ 22ba8b0ee8SPengcheng Li #define UTMI_RST_COMPLETE_TIME 2 /* unit:ms */ 23ba8b0ee8SPengcheng Li #define POR_RST_COMPLETE_TIME 300 /* unit:us */ 243940ffc6SDavid Yang 253940ffc6SDavid Yang #define PHY_TYPE_0 0 263940ffc6SDavid Yang #define PHY_TYPE_1 1 273940ffc6SDavid Yang 28ba8b0ee8SPengcheng Li #define PHY_TEST_DATA GENMASK(7, 0) 293940ffc6SDavid Yang #define PHY_TEST_ADDR_OFFSET 8 303940ffc6SDavid Yang #define PHY0_TEST_ADDR GENMASK(15, 8) 313940ffc6SDavid Yang #define PHY0_TEST_PORT_OFFSET 16 323940ffc6SDavid Yang #define PHY0_TEST_PORT GENMASK(18, 16) 333940ffc6SDavid Yang #define PHY0_TEST_WREN BIT(21) 343940ffc6SDavid Yang #define PHY0_TEST_CLK BIT(22) /* rising edge active */ 353940ffc6SDavid Yang #define PHY0_TEST_RST BIT(23) /* low active */ 363940ffc6SDavid Yang #define PHY1_TEST_ADDR GENMASK(11, 8) 373940ffc6SDavid Yang #define PHY1_TEST_PORT_OFFSET 12 383940ffc6SDavid Yang #define PHY1_TEST_PORT BIT(12) 393940ffc6SDavid Yang #define PHY1_TEST_WREN BIT(13) 403940ffc6SDavid Yang #define PHY1_TEST_CLK BIT(14) /* rising edge active */ 413940ffc6SDavid Yang #define PHY1_TEST_RST BIT(15) /* low active */ 423940ffc6SDavid Yang 43ba8b0ee8SPengcheng Li #define PHY_CLK_ENABLE BIT(2) 44ba8b0ee8SPengcheng Li 45ba8b0ee8SPengcheng Li struct hisi_inno_phy_port { 46ba8b0ee8SPengcheng Li struct reset_control *utmi_rst; 47ba8b0ee8SPengcheng Li struct hisi_inno_phy_priv *priv; 48ba8b0ee8SPengcheng Li }; 49ba8b0ee8SPengcheng Li 50ba8b0ee8SPengcheng Li struct hisi_inno_phy_priv { 51ba8b0ee8SPengcheng Li void __iomem *mmio; 52ba8b0ee8SPengcheng Li struct clk *ref_clk; 53ba8b0ee8SPengcheng Li struct reset_control *por_rst; 543940ffc6SDavid Yang unsigned int type; 55ba8b0ee8SPengcheng Li struct hisi_inno_phy_port ports[INNO_PHY_PORT_NUM]; 56ba8b0ee8SPengcheng Li }; 57ba8b0ee8SPengcheng Li 58ba8b0ee8SPengcheng Li static void hisi_inno_phy_write_reg(struct hisi_inno_phy_priv *priv, 59ba8b0ee8SPengcheng Li u8 port, u32 addr, u32 data) 60ba8b0ee8SPengcheng Li { 61ba8b0ee8SPengcheng Li void __iomem *reg = priv->mmio; 62ba8b0ee8SPengcheng Li u32 val; 633940ffc6SDavid Yang u32 value; 64ba8b0ee8SPengcheng Li 653940ffc6SDavid Yang if (priv->type == PHY_TYPE_0) 66ba8b0ee8SPengcheng Li val = (data & PHY_TEST_DATA) | 673940ffc6SDavid Yang ((addr << PHY_TEST_ADDR_OFFSET) & PHY0_TEST_ADDR) | 683940ffc6SDavid Yang ((port << PHY0_TEST_PORT_OFFSET) & PHY0_TEST_PORT) | 693940ffc6SDavid Yang PHY0_TEST_WREN | PHY0_TEST_RST; 703940ffc6SDavid Yang else 713940ffc6SDavid Yang val = (data & PHY_TEST_DATA) | 723940ffc6SDavid Yang ((addr << PHY_TEST_ADDR_OFFSET) & PHY1_TEST_ADDR) | 733940ffc6SDavid Yang ((port << PHY1_TEST_PORT_OFFSET) & PHY1_TEST_PORT) | 743940ffc6SDavid Yang PHY1_TEST_WREN | PHY1_TEST_RST; 75ba8b0ee8SPengcheng Li writel(val, reg); 76ba8b0ee8SPengcheng Li 773940ffc6SDavid Yang value = val; 783940ffc6SDavid Yang if (priv->type == PHY_TYPE_0) 793940ffc6SDavid Yang value |= PHY0_TEST_CLK; 803940ffc6SDavid Yang else 813940ffc6SDavid Yang value |= PHY1_TEST_CLK; 823940ffc6SDavid Yang writel(value, reg); 83ba8b0ee8SPengcheng Li 84ba8b0ee8SPengcheng Li writel(val, reg); 85ba8b0ee8SPengcheng Li } 86ba8b0ee8SPengcheng Li 87ba8b0ee8SPengcheng Li static void hisi_inno_phy_setup(struct hisi_inno_phy_priv *priv) 88ba8b0ee8SPengcheng Li { 89ba8b0ee8SPengcheng Li /* The phy clk is controlled by the port0 register 0x06. */ 90ba8b0ee8SPengcheng Li hisi_inno_phy_write_reg(priv, 0, 0x06, PHY_CLK_ENABLE); 91ba8b0ee8SPengcheng Li msleep(PHY_CLK_STABLE_TIME); 92ba8b0ee8SPengcheng Li } 93ba8b0ee8SPengcheng Li 94ba8b0ee8SPengcheng Li static int hisi_inno_phy_init(struct phy *phy) 95ba8b0ee8SPengcheng Li { 96ba8b0ee8SPengcheng Li struct hisi_inno_phy_port *port = phy_get_drvdata(phy); 97ba8b0ee8SPengcheng Li struct hisi_inno_phy_priv *priv = port->priv; 98ba8b0ee8SPengcheng Li int ret; 99ba8b0ee8SPengcheng Li 100ba8b0ee8SPengcheng Li ret = clk_prepare_enable(priv->ref_clk); 101ba8b0ee8SPengcheng Li if (ret) 102ba8b0ee8SPengcheng Li return ret; 103ba8b0ee8SPengcheng Li udelay(REF_CLK_STABLE_TIME); 104ba8b0ee8SPengcheng Li 105ba8b0ee8SPengcheng Li reset_control_deassert(priv->por_rst); 106ba8b0ee8SPengcheng Li udelay(POR_RST_COMPLETE_TIME); 107ba8b0ee8SPengcheng Li 108ba8b0ee8SPengcheng Li /* Set up phy registers */ 109ba8b0ee8SPengcheng Li hisi_inno_phy_setup(priv); 110ba8b0ee8SPengcheng Li 111ba8b0ee8SPengcheng Li reset_control_deassert(port->utmi_rst); 112ba8b0ee8SPengcheng Li udelay(UTMI_RST_COMPLETE_TIME); 113ba8b0ee8SPengcheng Li 114ba8b0ee8SPengcheng Li return 0; 115ba8b0ee8SPengcheng Li } 116ba8b0ee8SPengcheng Li 117ba8b0ee8SPengcheng Li static int hisi_inno_phy_exit(struct phy *phy) 118ba8b0ee8SPengcheng Li { 119ba8b0ee8SPengcheng Li struct hisi_inno_phy_port *port = phy_get_drvdata(phy); 120ba8b0ee8SPengcheng Li struct hisi_inno_phy_priv *priv = port->priv; 121ba8b0ee8SPengcheng Li 122ba8b0ee8SPengcheng Li reset_control_assert(port->utmi_rst); 123ba8b0ee8SPengcheng Li reset_control_assert(priv->por_rst); 124ba8b0ee8SPengcheng Li clk_disable_unprepare(priv->ref_clk); 125ba8b0ee8SPengcheng Li 126ba8b0ee8SPengcheng Li return 0; 127ba8b0ee8SPengcheng Li } 128ba8b0ee8SPengcheng Li 129ba8b0ee8SPengcheng Li static const struct phy_ops hisi_inno_phy_ops = { 130ba8b0ee8SPengcheng Li .init = hisi_inno_phy_init, 131ba8b0ee8SPengcheng Li .exit = hisi_inno_phy_exit, 132ba8b0ee8SPengcheng Li .owner = THIS_MODULE, 133ba8b0ee8SPengcheng Li }; 134ba8b0ee8SPengcheng Li 135ba8b0ee8SPengcheng Li static int hisi_inno_phy_probe(struct platform_device *pdev) 136ba8b0ee8SPengcheng Li { 137ba8b0ee8SPengcheng Li struct device *dev = &pdev->dev; 138ba8b0ee8SPengcheng Li struct device_node *np = dev->of_node; 139ba8b0ee8SPengcheng Li struct hisi_inno_phy_priv *priv; 140ba8b0ee8SPengcheng Li struct phy_provider *provider; 141ba8b0ee8SPengcheng Li int i = 0; 142ba8b0ee8SPengcheng Li int ret; 143ba8b0ee8SPengcheng Li 144ba8b0ee8SPengcheng Li priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 145ba8b0ee8SPengcheng Li if (!priv) 146ba8b0ee8SPengcheng Li return -ENOMEM; 147ba8b0ee8SPengcheng Li 148fa093440SYueHaibing priv->mmio = devm_platform_ioremap_resource(pdev, 0); 149ba8b0ee8SPengcheng Li if (IS_ERR(priv->mmio)) { 150ba8b0ee8SPengcheng Li ret = PTR_ERR(priv->mmio); 151ba8b0ee8SPengcheng Li return ret; 152ba8b0ee8SPengcheng Li } 153ba8b0ee8SPengcheng Li 154ba8b0ee8SPengcheng Li priv->ref_clk = devm_clk_get(dev, NULL); 155ba8b0ee8SPengcheng Li if (IS_ERR(priv->ref_clk)) 156ba8b0ee8SPengcheng Li return PTR_ERR(priv->ref_clk); 157ba8b0ee8SPengcheng Li 158ba8b0ee8SPengcheng Li priv->por_rst = devm_reset_control_get_exclusive(dev, NULL); 159ba8b0ee8SPengcheng Li if (IS_ERR(priv->por_rst)) 160ba8b0ee8SPengcheng Li return PTR_ERR(priv->por_rst); 161ba8b0ee8SPengcheng Li 1623940ffc6SDavid Yang priv->type = (uintptr_t) of_device_get_match_data(dev); 1633940ffc6SDavid Yang 164*93cab07aSKrzysztof Kozlowski for_each_child_of_node_scoped(np, child) { 165ba8b0ee8SPengcheng Li struct reset_control *rst; 166ba8b0ee8SPengcheng Li struct phy *phy; 167ba8b0ee8SPengcheng Li 168ba8b0ee8SPengcheng Li rst = of_reset_control_get_exclusive(child, NULL); 169*93cab07aSKrzysztof Kozlowski if (IS_ERR(rst)) 170ba8b0ee8SPengcheng Li return PTR_ERR(rst); 17121b89120SWan Jiabing 172ba8b0ee8SPengcheng Li priv->ports[i].utmi_rst = rst; 173ba8b0ee8SPengcheng Li priv->ports[i].priv = priv; 174ba8b0ee8SPengcheng Li 175ba8b0ee8SPengcheng Li phy = devm_phy_create(dev, child, &hisi_inno_phy_ops); 176*93cab07aSKrzysztof Kozlowski if (IS_ERR(phy)) 177ba8b0ee8SPengcheng Li return PTR_ERR(phy); 178ba8b0ee8SPengcheng Li 179ba8b0ee8SPengcheng Li phy_set_bus_width(phy, 8); 180ba8b0ee8SPengcheng Li phy_set_drvdata(phy, &priv->ports[i]); 181ba8b0ee8SPengcheng Li i++; 182ba8b0ee8SPengcheng Li 18313c088cfSHarshit Mogalapalli if (i >= INNO_PHY_PORT_NUM) { 184ba8b0ee8SPengcheng Li dev_warn(dev, "Support %d ports in maximum\n", i); 185ba8b0ee8SPengcheng Li break; 186ba8b0ee8SPengcheng Li } 187ba8b0ee8SPengcheng Li } 188ba8b0ee8SPengcheng Li 189ba8b0ee8SPengcheng Li provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 190ba8b0ee8SPengcheng Li return PTR_ERR_OR_ZERO(provider); 191ba8b0ee8SPengcheng Li } 192ba8b0ee8SPengcheng Li 193ba8b0ee8SPengcheng Li static const struct of_device_id hisi_inno_phy_of_match[] = { 1943940ffc6SDavid Yang { .compatible = "hisilicon,inno-usb2-phy", 1953940ffc6SDavid Yang .data = (void *) PHY_TYPE_0 }, 1963940ffc6SDavid Yang { .compatible = "hisilicon,hi3798cv200-usb2-phy", 1973940ffc6SDavid Yang .data = (void *) PHY_TYPE_0 }, 1983940ffc6SDavid Yang { .compatible = "hisilicon,hi3798mv100-usb2-phy", 1993940ffc6SDavid Yang .data = (void *) PHY_TYPE_1 }, 200ba8b0ee8SPengcheng Li { }, 201ba8b0ee8SPengcheng Li }; 202ba8b0ee8SPengcheng Li MODULE_DEVICE_TABLE(of, hisi_inno_phy_of_match); 203ba8b0ee8SPengcheng Li 204ba8b0ee8SPengcheng Li static struct platform_driver hisi_inno_phy_driver = { 205ba8b0ee8SPengcheng Li .probe = hisi_inno_phy_probe, 206ba8b0ee8SPengcheng Li .driver = { 207ba8b0ee8SPengcheng Li .name = "hisi-inno-phy", 208ba8b0ee8SPengcheng Li .of_match_table = hisi_inno_phy_of_match, 209ba8b0ee8SPengcheng Li } 210ba8b0ee8SPengcheng Li }; 211ba8b0ee8SPengcheng Li module_platform_driver(hisi_inno_phy_driver); 212ba8b0ee8SPengcheng Li 213ba8b0ee8SPengcheng Li MODULE_DESCRIPTION("HiSilicon INNO USB2 PHY Driver"); 214ba8b0ee8SPengcheng Li MODULE_LICENSE("GPL v2"); 215