Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2015 Linaro, Ltd.
  4 * Rob Herring <robh@kernel.org>
  5 *
  6 * Based on vendor driver:
  7 * Copyright (C) 2013 Marvell Inc.
  8 * Author: Chao Xie <xiechao.mail@gmail.com>
  9 */
 10
 11#include <linux/delay.h>
 12#include <linux/slab.h>
 13#include <linux/of.h>
 14#include <linux/io.h>
 15#include <linux/iopoll.h>
 16#include <linux/err.h>
 17#include <linux/clk.h>
 18#include <linux/module.h>
 19#include <linux/platform_device.h>
 20#include <linux/phy/phy.h>
 21
 22#define PHY_28NM_HSIC_CTRL			0x08
 23#define PHY_28NM_HSIC_IMPCAL_CAL		0x18
 24#define PHY_28NM_HSIC_PLL_CTRL01		0x1c
 25#define PHY_28NM_HSIC_PLL_CTRL2			0x20
 26#define PHY_28NM_HSIC_INT			0x28
 27
 28#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT		26
 29#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT		0
 30#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT		9
 31
 32#define PHY_28NM_HSIC_S2H_PU_PLL		BIT(10)
 33#define PHY_28NM_HSIC_H2S_PLL_LOCK		BIT(15)
 34#define PHY_28NM_HSIC_S2H_HSIC_EN		BIT(7)
 35#define S2H_DRV_SE0_4RESUME			BIT(14)
 36#define PHY_28NM_HSIC_H2S_IMPCAL_DONE		BIT(27)
 37
 38#define PHY_28NM_HSIC_CONNECT_INT		BIT(1)
 39#define PHY_28NM_HSIC_HS_READY_INT		BIT(2)
 40
 41struct mv_hsic_phy {
 42	struct phy		*phy;
 43	struct platform_device	*pdev;
 44	void __iomem		*base;
 45	struct clk		*clk;
 46};
 47
 48static int wait_for_reg(void __iomem *reg, u32 mask, u32 ms)
 49{
 50	u32 val;
 51
 52	return readl_poll_timeout(reg, val, ((val & mask) == mask),
 53				  1000, 1000 * ms);
 54}
 55
 56static int mv_hsic_phy_init(struct phy *phy)
 57{
 58	struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
 59	struct platform_device *pdev = mv_phy->pdev;
 60	void __iomem *base = mv_phy->base;
 61	int ret;
 62
 63	clk_prepare_enable(mv_phy->clk);
 64
 65	/* Set reference clock */
 66	writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT |
 67		0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT |
 68		0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT,
 69		base + PHY_28NM_HSIC_PLL_CTRL01);
 70
 71	/* Turn on PLL */
 72	writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) |
 73		PHY_28NM_HSIC_S2H_PU_PLL,
 74		base + PHY_28NM_HSIC_PLL_CTRL2);
 75
 76	/* Make sure PHY PLL is locked */
 77	ret = wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2,
 78			   PHY_28NM_HSIC_H2S_PLL_LOCK, 100);
 79	if (ret) {
 80		dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS.");
 81		clk_disable_unprepare(mv_phy->clk);
 82	}
 83
 84	return ret;
 85}
 86
 87static int mv_hsic_phy_power_on(struct phy *phy)
 88{
 89	struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
 90	struct platform_device *pdev = mv_phy->pdev;
 91	void __iomem *base = mv_phy->base;
 92	u32 reg;
 93	int ret;
 94
 95	reg = readl(base + PHY_28NM_HSIC_CTRL);
 96	/* Avoid SE0 state when resume for some device will take it as reset */
 97	reg &= ~S2H_DRV_SE0_4RESUME;
 98	reg |= PHY_28NM_HSIC_S2H_HSIC_EN;	/* Enable HSIC PHY */
 99	writel(reg, base + PHY_28NM_HSIC_CTRL);
100
101	/*
102	 *  Calibration Timing
103	 *		   ____________________________
104	 *  CAL START   ___|
105	 *			   ____________________
106	 *  CAL_DONE    ___________|
107	 *		   | 400us |
108	 */
109
110	/* Make sure PHY Calibration is ready */
111	ret = wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL,
112			   PHY_28NM_HSIC_H2S_IMPCAL_DONE, 100);
113	if (ret) {
114		dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS.");
115		return ret;
116	}
117
118	/* Waiting for HSIC connect int*/
119	ret = wait_for_reg(base + PHY_28NM_HSIC_INT,
120			   PHY_28NM_HSIC_CONNECT_INT, 200);
121	if (ret)
122		dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout.");
123
124	return ret;
125}
126
127static int mv_hsic_phy_power_off(struct phy *phy)
128{
129	struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
130	void __iomem *base = mv_phy->base;
131
132	writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN,
133		base + PHY_28NM_HSIC_CTRL);
134
135	return 0;
136}
137
138static int mv_hsic_phy_exit(struct phy *phy)
139{
140	struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
141	void __iomem *base = mv_phy->base;
142
143	/* Turn off PLL */
144	writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) &
145		~PHY_28NM_HSIC_S2H_PU_PLL,
146		base + PHY_28NM_HSIC_PLL_CTRL2);
147
148	clk_disable_unprepare(mv_phy->clk);
149	return 0;
150}
151
152
153static const struct phy_ops hsic_ops = {
154	.init		= mv_hsic_phy_init,
155	.power_on	= mv_hsic_phy_power_on,
156	.power_off	= mv_hsic_phy_power_off,
157	.exit		= mv_hsic_phy_exit,
158	.owner		= THIS_MODULE,
159};
160
161static int mv_hsic_phy_probe(struct platform_device *pdev)
162{
163	struct phy_provider *phy_provider;
164	struct mv_hsic_phy *mv_phy;
165
166	mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL);
167	if (!mv_phy)
168		return -ENOMEM;
169
170	mv_phy->pdev = pdev;
171
172	mv_phy->clk = devm_clk_get(&pdev->dev, NULL);
173	if (IS_ERR(mv_phy->clk)) {
174		dev_err(&pdev->dev, "failed to get clock.\n");
175		return PTR_ERR(mv_phy->clk);
176	}
177
178	mv_phy->base = devm_platform_ioremap_resource(pdev, 0);
179	if (IS_ERR(mv_phy->base))
180		return PTR_ERR(mv_phy->base);
181
182	mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops);
183	if (IS_ERR(mv_phy->phy))
184		return PTR_ERR(mv_phy->phy);
185
186	phy_set_drvdata(mv_phy->phy, mv_phy);
187
188	phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
189	return PTR_ERR_OR_ZERO(phy_provider);
190}
191
192static const struct of_device_id mv_hsic_phy_dt_match[] = {
193	{ .compatible = "marvell,pxa1928-hsic-phy", },
194	{},
195};
196MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match);
197
198static struct platform_driver mv_hsic_phy_driver = {
199	.probe	= mv_hsic_phy_probe,
200	.driver = {
201		.name   = "mv-hsic-phy",
202		.of_match_table = of_match_ptr(mv_hsic_phy_dt_match),
203	},
204};
205module_platform_driver(mv_hsic_phy_driver);
206
207MODULE_AUTHOR("Rob Herring <robh@kernel.org>");
208MODULE_DESCRIPTION("Marvell HSIC phy driver");
209MODULE_LICENSE("GPL v2");