Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
  3 *
  4 * This program is free software; you can redistribute it and/or modify it
  5 * under the terms and conditions of the GNU General Public License,
  6 * version 2, as published by the Free Software Foundation.
  7 */
  8
  9#include <linux/module.h>
 10#include <linux/platform_device.h>
 11#include <linux/clk.h>
 12#include <linux/delay.h>
 13#include <linux/err.h>
 14#include <linux/io.h>
 15#include <linux/usb/otg.h>
 16#include <linux/platform_data/mv_usb.h>
 17
 18#include "phy-mv-u3d-usb.h"
 19
 20/*
 21 * struct mv_u3d_phy - transceiver driver state
 22 * @phy: transceiver structure
 23 * @dev: The parent device supplied to the probe function
 24 * @clk: usb phy clock
 25 * @base: usb phy register memory base
 26 */
 27struct mv_u3d_phy {
 28	struct usb_phy	phy;
 29	struct mv_usb_platform_data *plat;
 30	struct device	*dev;
 31	struct clk	*clk;
 32	void __iomem	*base;
 33};
 34
 35static u32 mv_u3d_phy_read(void __iomem *base, u32 reg)
 36{
 37	void __iomem *addr, *data;
 38
 39	addr = base;
 40	data = base + 0x4;
 41
 42	writel_relaxed(reg, addr);
 43	return readl_relaxed(data);
 44}
 45
 46static void mv_u3d_phy_set(void __iomem *base, u32 reg, u32 value)
 47{
 48	void __iomem *addr, *data;
 49	u32 tmp;
 50
 51	addr = base;
 52	data = base + 0x4;
 53
 54	writel_relaxed(reg, addr);
 55	tmp = readl_relaxed(data);
 56	tmp |= value;
 57	writel_relaxed(tmp, data);
 58}
 59
 60static void mv_u3d_phy_clear(void __iomem *base, u32 reg, u32 value)
 61{
 62	void __iomem *addr, *data;
 63	u32 tmp;
 64
 65	addr = base;
 66	data = base + 0x4;
 67
 68	writel_relaxed(reg, addr);
 69	tmp = readl_relaxed(data);
 70	tmp &= ~value;
 71	writel_relaxed(tmp, data);
 72}
 73
 74static void mv_u3d_phy_write(void __iomem *base, u32 reg, u32 value)
 75{
 76	void __iomem *addr, *data;
 77
 78	addr = base;
 79	data = base + 0x4;
 80
 81	writel_relaxed(reg, addr);
 82	writel_relaxed(value, data);
 83}
 84
 85static void mv_u3d_phy_shutdown(struct usb_phy *phy)
 86{
 87	struct mv_u3d_phy *mv_u3d_phy;
 88	void __iomem *base;
 89	u32 val;
 90
 91	mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy);
 92	base = mv_u3d_phy->base;
 93
 94	/* Power down Reference Analog current, bit 15
 95	 * Power down PLL, bit 14
 96	 * Power down Receiver, bit 13
 97	 * Power down Transmitter, bit 12
 98	 * of USB3_POWER_PLL_CONTROL register
 99	 */
100	val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL);
101	val &= ~(USB3_POWER_PLL_CONTROL_PU);
102	mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val);
103
104	if (mv_u3d_phy->clk)
105		clk_disable(mv_u3d_phy->clk);
106}
107
108static int mv_u3d_phy_init(struct usb_phy *phy)
109{
110	struct mv_u3d_phy *mv_u3d_phy;
111	void __iomem *base;
112	u32 val, count;
113
114	/* enable usb3 phy */
115	mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy);
116
117	if (mv_u3d_phy->clk)
118		clk_enable(mv_u3d_phy->clk);
119
120	base = mv_u3d_phy->base;
121
122	val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL);
123	val &= ~(USB3_POWER_PLL_CONTROL_PU_MASK);
124	val |= 0xF << USB3_POWER_PLL_CONTROL_PU_SHIFT;
125	mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val);
126	udelay(100);
127
128	mv_u3d_phy_write(base, USB3_RESET_CONTROL,
129			USB3_RESET_CONTROL_RESET_PIPE);
130	udelay(100);
131
132	mv_u3d_phy_write(base, USB3_RESET_CONTROL,
133			USB3_RESET_CONTROL_RESET_PIPE
134			| USB3_RESET_CONTROL_RESET_PHY);
135	udelay(100);
136
137	val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL);
138	val &= ~(USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK
139		| USB3_POWER_PLL_CONTROL_PHY_MODE_MASK);
140	val |=  (USB3_PLL_25MHZ << USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT)
141		| (0x5 << USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT);
142	mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val);
143	udelay(100);
144
145	mv_u3d_phy_clear(base, USB3_KVCO_CALI_CONTROL,
146		USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK);
147	udelay(100);
148
149	val = mv_u3d_phy_read(base, USB3_SQUELCH_FFE);
150	val &= ~(USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK
151		| USB3_SQUELCH_FFE_FFE_RES_SEL_MASK
152		| USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK);
153	val |= ((0xD << USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT)
154		| (0x7 << USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT)
155		| (0x8 << USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT));
156	mv_u3d_phy_write(base, USB3_SQUELCH_FFE, val);
157	udelay(100);
158
159	val = mv_u3d_phy_read(base, USB3_GEN1_SET0);
160	val &= ~USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK;
161	val |= 1 << USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT;
162	mv_u3d_phy_write(base, USB3_GEN1_SET0, val);
163	udelay(100);
164
165	val = mv_u3d_phy_read(base, USB3_GEN2_SET0);
166	val &= ~(USB3_GEN2_SET0_G2_TX_AMP_MASK
167		| USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK
168		| USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK);
169	val |= ((0x14 << USB3_GEN2_SET0_G2_TX_AMP_SHIFT)
170		| (1 << USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT)
171		| (0xA << USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT)
172		| (1 << USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT));
173	mv_u3d_phy_write(base, USB3_GEN2_SET0, val);
174	udelay(100);
175
176	mv_u3d_phy_read(base, USB3_TX_EMPPH);
177	val &= ~(USB3_TX_EMPPH_AMP_MASK
178		| USB3_TX_EMPPH_EN_MASK
179		| USB3_TX_EMPPH_AMP_FORCE_MASK
180		| USB3_TX_EMPPH_PAR1_MASK
181		| USB3_TX_EMPPH_PAR2_MASK);
182	val |= ((0xB << USB3_TX_EMPPH_AMP_SHIFT)
183		| (1 << USB3_TX_EMPPH_EN_SHIFT)
184		| (1 << USB3_TX_EMPPH_AMP_FORCE_SHIFT)
185		| (0x1C << USB3_TX_EMPPH_PAR1_SHIFT)
186		| (1 << USB3_TX_EMPPH_PAR2_SHIFT));
187
188	mv_u3d_phy_write(base, USB3_TX_EMPPH, val);
189	udelay(100);
190
191	val = mv_u3d_phy_read(base, USB3_GEN2_SET1);
192	val &= ~(USB3_GEN2_SET1_G2_RX_SELMUPI_MASK
193		| USB3_GEN2_SET1_G2_RX_SELMUPF_MASK
194		| USB3_GEN2_SET1_G2_RX_SELMUFI_MASK
195		| USB3_GEN2_SET1_G2_RX_SELMUFF_MASK);
196	val |= ((1 << USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT)
197		| (1 << USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT)
198		| (1 << USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT)
199		| (1 << USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT));
200	mv_u3d_phy_write(base, USB3_GEN2_SET1, val);
201	udelay(100);
202
203	val = mv_u3d_phy_read(base, USB3_DIGITAL_LOOPBACK_EN);
204	val &= ~USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK;
205	val |= 1 << USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT;
206	mv_u3d_phy_write(base, USB3_DIGITAL_LOOPBACK_EN, val);
207	udelay(100);
208
209	val = mv_u3d_phy_read(base, USB3_IMPEDANCE_TX_SSC);
210	val &= ~USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK;
211	val |= 0xC << USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT;
212	mv_u3d_phy_write(base, USB3_IMPEDANCE_TX_SSC, val);
213	udelay(100);
214
215	val = mv_u3d_phy_read(base, USB3_IMPEDANCE_CALI_CTRL);
216	val &= ~USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK;
217	val |= 0x4 << USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT;
218	mv_u3d_phy_write(base, USB3_IMPEDANCE_CALI_CTRL, val);
219	udelay(100);
220
221	val = mv_u3d_phy_read(base, USB3_PHY_ISOLATION_MODE);
222	val &= ~(USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK
223		| USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK
224		| USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK);
225	val |= ((1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT)
226		| (1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT));
227	mv_u3d_phy_write(base, USB3_PHY_ISOLATION_MODE, val);
228	udelay(100);
229
230	val = mv_u3d_phy_read(base, USB3_TXDETRX);
231	val &= ~(USB3_TXDETRX_VTHSEL_MASK);
232	val |= 0x1 << USB3_TXDETRX_VTHSEL_SHIFT;
233	mv_u3d_phy_write(base, USB3_TXDETRX, val);
234	udelay(100);
235
236	dev_dbg(mv_u3d_phy->dev, "start calibration\n");
237
238calstart:
239	/* Perform Manual Calibration */
240	mv_u3d_phy_set(base, USB3_KVCO_CALI_CONTROL,
241		1 << USB3_KVCO_CALI_CONTROL_CAL_START_SHIFT);
242
243	mdelay(1);
244
245	count = 0;
246	while (1) {
247		val = mv_u3d_phy_read(base, USB3_KVCO_CALI_CONTROL);
248		if (val & (1 << USB3_KVCO_CALI_CONTROL_CAL_DONE_SHIFT))
249			break;
250		else if (count > 50) {
251			dev_dbg(mv_u3d_phy->dev, "calibration failure, retry...\n");
252			goto calstart;
253		}
254		count++;
255		mdelay(1);
256	}
257
258	/* active PIPE interface */
259	mv_u3d_phy_write(base, USB3_PIPE_SM_CTRL,
260		1 << USB3_PIPE_SM_CTRL_PHY_INIT_DONE);
261
262	return 0;
263}
264
265static int mv_u3d_phy_probe(struct platform_device *pdev)
266{
267	struct mv_u3d_phy *mv_u3d_phy;
268	struct mv_usb_platform_data *pdata;
269	struct device *dev = &pdev->dev;
270	struct resource *res;
271	void __iomem	*phy_base;
272	int	ret;
273
274	pdata = dev_get_platdata(&pdev->dev);
275	if (!pdata) {
276		dev_err(&pdev->dev, "%s: no platform data defined\n", __func__);
277		return -EINVAL;
278	}
279
280	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
281	phy_base = devm_ioremap_resource(dev, res);
282	if (IS_ERR(phy_base))
283		return PTR_ERR(phy_base);
284
285	mv_u3d_phy = devm_kzalloc(dev, sizeof(*mv_u3d_phy), GFP_KERNEL);
286	if (!mv_u3d_phy)
287		return -ENOMEM;
288
289	mv_u3d_phy->dev			= &pdev->dev;
290	mv_u3d_phy->plat		= pdata;
291	mv_u3d_phy->base		= phy_base;
292	mv_u3d_phy->phy.dev		= mv_u3d_phy->dev;
293	mv_u3d_phy->phy.label		= "mv-u3d-phy";
294	mv_u3d_phy->phy.init		= mv_u3d_phy_init;
295	mv_u3d_phy->phy.shutdown	= mv_u3d_phy_shutdown;
296
297	ret = usb_add_phy(&mv_u3d_phy->phy, USB_PHY_TYPE_USB3);
298	if (ret)
299		goto err;
300
301	if (!mv_u3d_phy->clk)
302		mv_u3d_phy->clk = clk_get(mv_u3d_phy->dev, "u3dphy");
303
304	platform_set_drvdata(pdev, mv_u3d_phy);
305
306	dev_info(&pdev->dev, "Initialized Marvell USB 3.0 PHY\n");
307err:
308	return ret;
309}
310
311static int mv_u3d_phy_remove(struct platform_device *pdev)
312{
313	struct mv_u3d_phy *mv_u3d_phy = platform_get_drvdata(pdev);
314
315	usb_remove_phy(&mv_u3d_phy->phy);
316
317	if (mv_u3d_phy->clk) {
318		clk_put(mv_u3d_phy->clk);
319		mv_u3d_phy->clk = NULL;
320	}
321
322	return 0;
323}
324
325static struct platform_driver mv_u3d_phy_driver = {
326	.probe		= mv_u3d_phy_probe,
327	.remove		= mv_u3d_phy_remove,
328	.driver		= {
329		.name	= "mv-u3d-phy",
330		.owner	= THIS_MODULE,
331	},
332};
333
334module_platform_driver(mv_u3d_phy_driver);
335MODULE_DESCRIPTION("Marvell USB 3.0 PHY controller");
336MODULE_AUTHOR("Yu Xu <yuxu@marvell.com>");
337MODULE_LICENSE("GPL");
338MODULE_ALIAS("platform:mv-u3d-phy");