215 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * HiSilicon INNO USB2 PHY Driver.4 *5 * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.6 */7 8#include <linux/clk.h>9#include <linux/delay.h>10#include <linux/io.h>11#include <linux/module.h>12#include <linux/of.h>13#include <linux/phy/phy.h>14#include <linux/platform_device.h>15#include <linux/reset.h>16 17#define INNO_PHY_PORT_NUM 218#define REF_CLK_STABLE_TIME 100 /* unit:us */19#define UTMI_CLK_STABLE_TIME 200 /* unit:us */20#define TEST_CLK_STABLE_TIME 2 /* unit:ms */21#define PHY_CLK_STABLE_TIME 2 /* unit:ms */22#define UTMI_RST_COMPLETE_TIME 2 /* unit:ms */23#define POR_RST_COMPLETE_TIME 300 /* unit:us */24 25#define PHY_TYPE_0 026#define PHY_TYPE_1 127 28#define PHY_TEST_DATA GENMASK(7, 0)29#define PHY_TEST_ADDR_OFFSET 830#define PHY0_TEST_ADDR GENMASK(15, 8)31#define PHY0_TEST_PORT_OFFSET 1632#define PHY0_TEST_PORT GENMASK(18, 16)33#define PHY0_TEST_WREN BIT(21)34#define PHY0_TEST_CLK BIT(22) /* rising edge active */35#define PHY0_TEST_RST BIT(23) /* low active */36#define PHY1_TEST_ADDR GENMASK(11, 8)37#define PHY1_TEST_PORT_OFFSET 1238#define PHY1_TEST_PORT BIT(12)39#define PHY1_TEST_WREN BIT(13)40#define PHY1_TEST_CLK BIT(14) /* rising edge active */41#define PHY1_TEST_RST BIT(15) /* low active */42 43#define PHY_CLK_ENABLE BIT(2)44 45struct hisi_inno_phy_port {46 struct reset_control *utmi_rst;47 struct hisi_inno_phy_priv *priv;48};49 50struct hisi_inno_phy_priv {51 void __iomem *mmio;52 struct clk *ref_clk;53 struct reset_control *por_rst;54 unsigned int type;55 struct hisi_inno_phy_port ports[INNO_PHY_PORT_NUM];56};57 58static void hisi_inno_phy_write_reg(struct hisi_inno_phy_priv *priv,59 u8 port, u32 addr, u32 data)60{61 void __iomem *reg = priv->mmio;62 u32 val;63 u32 value;64 65 if (priv->type == PHY_TYPE_0)66 val = (data & PHY_TEST_DATA) |67 ((addr << PHY_TEST_ADDR_OFFSET) & PHY0_TEST_ADDR) |68 ((port << PHY0_TEST_PORT_OFFSET) & PHY0_TEST_PORT) |69 PHY0_TEST_WREN | PHY0_TEST_RST;70 else71 val = (data & PHY_TEST_DATA) |72 ((addr << PHY_TEST_ADDR_OFFSET) & PHY1_TEST_ADDR) |73 ((port << PHY1_TEST_PORT_OFFSET) & PHY1_TEST_PORT) |74 PHY1_TEST_WREN | PHY1_TEST_RST;75 writel(val, reg);76 77 value = val;78 if (priv->type == PHY_TYPE_0)79 value |= PHY0_TEST_CLK;80 else81 value |= PHY1_TEST_CLK;82 writel(value, reg);83 84 writel(val, reg);85}86 87static void hisi_inno_phy_setup(struct hisi_inno_phy_priv *priv)88{89 /* The phy clk is controlled by the port0 register 0x06. */90 hisi_inno_phy_write_reg(priv, 0, 0x06, PHY_CLK_ENABLE);91 msleep(PHY_CLK_STABLE_TIME);92}93 94static int hisi_inno_phy_init(struct phy *phy)95{96 struct hisi_inno_phy_port *port = phy_get_drvdata(phy);97 struct hisi_inno_phy_priv *priv = port->priv;98 int ret;99 100 ret = clk_prepare_enable(priv->ref_clk);101 if (ret)102 return ret;103 udelay(REF_CLK_STABLE_TIME);104 105 reset_control_deassert(priv->por_rst);106 udelay(POR_RST_COMPLETE_TIME);107 108 /* Set up phy registers */109 hisi_inno_phy_setup(priv);110 111 reset_control_deassert(port->utmi_rst);112 udelay(UTMI_RST_COMPLETE_TIME);113 114 return 0;115}116 117static int hisi_inno_phy_exit(struct phy *phy)118{119 struct hisi_inno_phy_port *port = phy_get_drvdata(phy);120 struct hisi_inno_phy_priv *priv = port->priv;121 122 reset_control_assert(port->utmi_rst);123 reset_control_assert(priv->por_rst);124 clk_disable_unprepare(priv->ref_clk);125 126 return 0;127}128 129static const struct phy_ops hisi_inno_phy_ops = {130 .init = hisi_inno_phy_init,131 .exit = hisi_inno_phy_exit,132 .owner = THIS_MODULE,133};134 135static int hisi_inno_phy_probe(struct platform_device *pdev)136{137 struct device *dev = &pdev->dev;138 struct device_node *np = dev->of_node;139 struct hisi_inno_phy_priv *priv;140 struct phy_provider *provider;141 int i = 0;142 int ret;143 144 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);145 if (!priv)146 return -ENOMEM;147 148 priv->mmio = devm_platform_ioremap_resource(pdev, 0);149 if (IS_ERR(priv->mmio)) {150 ret = PTR_ERR(priv->mmio);151 return ret;152 }153 154 priv->ref_clk = devm_clk_get(dev, NULL);155 if (IS_ERR(priv->ref_clk))156 return PTR_ERR(priv->ref_clk);157 158 priv->por_rst = devm_reset_control_get_exclusive(dev, NULL);159 if (IS_ERR(priv->por_rst))160 return PTR_ERR(priv->por_rst);161 162 priv->type = (uintptr_t) of_device_get_match_data(dev);163 164 for_each_child_of_node_scoped(np, child) {165 struct reset_control *rst;166 struct phy *phy;167 168 rst = of_reset_control_get_exclusive(child, NULL);169 if (IS_ERR(rst))170 return PTR_ERR(rst);171 172 priv->ports[i].utmi_rst = rst;173 priv->ports[i].priv = priv;174 175 phy = devm_phy_create(dev, child, &hisi_inno_phy_ops);176 if (IS_ERR(phy))177 return PTR_ERR(phy);178 179 phy_set_bus_width(phy, 8);180 phy_set_drvdata(phy, &priv->ports[i]);181 i++;182 183 if (i >= INNO_PHY_PORT_NUM) {184 dev_warn(dev, "Support %d ports in maximum\n", i);185 break;186 }187 }188 189 provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);190 return PTR_ERR_OR_ZERO(provider);191}192 193static const struct of_device_id hisi_inno_phy_of_match[] = {194 { .compatible = "hisilicon,inno-usb2-phy",195 .data = (void *) PHY_TYPE_0 },196 { .compatible = "hisilicon,hi3798cv200-usb2-phy",197 .data = (void *) PHY_TYPE_0 },198 { .compatible = "hisilicon,hi3798mv100-usb2-phy",199 .data = (void *) PHY_TYPE_1 },200 { },201};202MODULE_DEVICE_TABLE(of, hisi_inno_phy_of_match);203 204static struct platform_driver hisi_inno_phy_driver = {205 .probe = hisi_inno_phy_probe,206 .driver = {207 .name = "hisi-inno-phy",208 .of_match_table = hisi_inno_phy_of_match,209 }210};211module_platform_driver(hisi_inno_phy_driver);212 213MODULE_DESCRIPTION("HiSilicon INNO USB2 PHY Driver");214MODULE_LICENSE("GPL v2");215