brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · c843923 Raw
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