Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1/*
  2 * Copyright (c) 2015, Linaro Limited
  3 *
  4 * This program is free software; you can redistribute it and/or modify
  5 * it under the terms of the GNU General Public License version 2 and
  6 * only version 2 as published by the Free Software Foundation.
  7 *
  8 * This program is distributed in the hope that it will be useful,
  9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 11 * GNU General Public License for more details.
 12 */
 13
 14#include <linux/clk.h>
 15#include <linux/delay.h>
 16#include <linux/device.h>
 17#include <linux/err.h>
 18#include <linux/extcon.h>
 19#include <linux/gpio/consumer.h>
 20#include <linux/io.h>
 21#include <linux/module.h>
 22#include <linux/of.h>
 23#include <linux/platform_device.h>
 24#include <linux/reboot.h>
 25#include <linux/regulator/consumer.h>
 26#include <linux/reset.h>
 27#include <linux/slab.h>
 28#include <linux/usb.h>
 29#include <linux/usb/ulpi.h>
 30
 31#define HSPHY_AHBBURST			0x0090
 32#define HSPHY_AHBMODE			0x0098
 33#define HSPHY_GENCONFIG			0x009c
 34#define HSPHY_GENCONFIG_2		0x00a0
 35
 36#define HSPHY_USBCMD			0x0140
 37#define HSPHY_ULPI_VIEWPORT		0x0170
 38#define HSPHY_CTRL			0x0240
 39
 40#define HSPHY_TXFIFO_IDLE_FORCE_DIS	BIT(4)
 41#define HSPHY_SESS_VLD_CTRL_EN		BIT(7)
 42#define HSPHY_POR_ASSERT		BIT(0)
 43#define HSPHY_RETEN			BIT(1)
 44
 45#define HSPHY_SESS_VLD_CTRL		BIT(25)
 46
 47#define ULPI_PWR_CLK_MNG_REG		0x88
 48#define ULPI_PWR_OTG_COMP_DISABLE	BIT(0)
 49
 50#define ULPI_MISC_A			0x96
 51#define ULPI_MISC_A_VBUSVLDEXTSEL	BIT(1)
 52#define ULPI_MISC_A_VBUSVLDEXT		BIT(0)
 53
 54#define HSPHY_3P3_MIN			3050000 /* uV */
 55#define HSPHY_3P3_MAX			3300000 /* uV */
 56
 57#define HSPHY_1P8_MIN			1800000 /* uV */
 58#define HSPHY_1P8_MAX			1800000 /* uV */
 59
 60#define HSPHY_VDD_MIN			5
 61#define HSPHY_VDD_MAX			7
 62
 63struct phy_8x16 {
 64	struct usb_phy			phy;
 65	void __iomem			*regs;
 66	struct clk			*core_clk;
 67	struct clk			*iface_clk;
 68	struct regulator_bulk_data	regulator[3];
 69
 70	struct reset_control		*phy_reset;
 71
 72	struct extcon_dev		*vbus_edev;
 73	struct notifier_block		vbus_notify;
 74
 75	struct gpio_desc		*switch_gpio;
 76	struct notifier_block		reboot_notify;
 77};
 78
 79static int phy_8x16_notify_connect(struct usb_phy *phy,
 80				   enum usb_device_speed speed)
 81{
 82	struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy);
 83	u32 val;
 84
 85	val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT;
 86	usb_phy_io_write(&qphy->phy, val, ULPI_SET(ULPI_MISC_A));
 87
 88	val = readl(qphy->regs + HSPHY_USBCMD);
 89	val |= HSPHY_SESS_VLD_CTRL;
 90	writel(val, qphy->regs + HSPHY_USBCMD);
 91
 92	return 0;
 93}
 94
 95static int phy_8x16_notify_disconnect(struct usb_phy *phy,
 96				      enum usb_device_speed speed)
 97{
 98	struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy);
 99	u32 val;
100
101	val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL;
102	usb_phy_io_write(&qphy->phy, val, ULPI_CLR(ULPI_MISC_A));
103
104	val = readl(qphy->regs + HSPHY_USBCMD);
105	val &= ~HSPHY_SESS_VLD_CTRL;
106	writel(val, qphy->regs + HSPHY_USBCMD);
107
108	return 0;
109}
110
111static int phy_8x16_vbus_on(struct phy_8x16 *qphy)
112{
113	phy_8x16_notify_connect(&qphy->phy, USB_SPEED_UNKNOWN);
114
115	/* Switch D+/D- lines to Device connector */
116	gpiod_set_value_cansleep(qphy->switch_gpio, 0);
117
118	return 0;
119}
120
121static int phy_8x16_vbus_off(struct phy_8x16 *qphy)
122{
123	phy_8x16_notify_disconnect(&qphy->phy, USB_SPEED_UNKNOWN);
124
125	/* Switch D+/D- lines to USB HUB */
126	gpiod_set_value_cansleep(qphy->switch_gpio, 1);
127
128	return 0;
129}
130
131static int phy_8x16_vbus_notify(struct notifier_block *nb, unsigned long event,
132				void *ptr)
133{
134	struct phy_8x16 *qphy = container_of(nb, struct phy_8x16, vbus_notify);
135
136	if (event)
137		phy_8x16_vbus_on(qphy);
138	else
139		phy_8x16_vbus_off(qphy);
140
141	return NOTIFY_DONE;
142}
143
144static int phy_8x16_init(struct usb_phy *phy)
145{
146	struct phy_8x16 *qphy = container_of(phy, struct phy_8x16, phy);
147	u32 val, init[] = {0x44, 0x6B, 0x24, 0x13};
148	u32 addr = ULPI_EXT_VENDOR_SPECIFIC;
149	int idx, state;
150
151	for (idx = 0; idx < ARRAY_SIZE(init); idx++)
152		usb_phy_io_write(phy, init[idx], addr + idx);
153
154	reset_control_reset(qphy->phy_reset);
155
156	/* Assert USB HSPHY_POR */
157	val = readl(qphy->regs + HSPHY_CTRL);
158	val |= HSPHY_POR_ASSERT;
159	writel(val, qphy->regs + HSPHY_CTRL);
160
161	/*
162	 * wait for minimum 10 microseconds as suggested in HPG.
163	 * Use a slightly larger value since the exact value didn't
164	 * work 100% of the time.
165	 */
166	usleep_range(12, 15);
167
168	/* Deassert USB HSPHY_POR */
169	val = readl(qphy->regs + HSPHY_CTRL);
170	val &= ~HSPHY_POR_ASSERT;
171	writel(val, qphy->regs + HSPHY_CTRL);
172
173	usleep_range(10, 15);
174
175	writel(0x00, qphy->regs + HSPHY_AHBBURST);
176	writel(0x08, qphy->regs + HSPHY_AHBMODE);
177
178	/* workaround for rx buffer collision issue */
179	val = readl(qphy->regs + HSPHY_GENCONFIG);
180	val &= ~HSPHY_TXFIFO_IDLE_FORCE_DIS;
181	writel(val, qphy->regs + HSPHY_GENCONFIG);
182
183	val = readl(qphy->regs + HSPHY_GENCONFIG_2);
184	val |= HSPHY_SESS_VLD_CTRL_EN;
185	writel(val, qphy->regs + HSPHY_GENCONFIG_2);
186
187	val = ULPI_PWR_OTG_COMP_DISABLE;
188	usb_phy_io_write(phy, val, ULPI_SET(ULPI_PWR_CLK_MNG_REG));
189
190	state = extcon_get_cable_state_(qphy->vbus_edev, EXTCON_USB);
191	if (state)
192		phy_8x16_vbus_on(qphy);
193	else
194		phy_8x16_vbus_off(qphy);
195
196	val = usb_phy_io_read(&qphy->phy, ULPI_FUNC_CTRL);
197	val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
198	val |= ULPI_FUNC_CTRL_OPMODE_NORMAL;
199	usb_phy_io_write(&qphy->phy, val, ULPI_FUNC_CTRL);
200
201	return 0;
202}
203
204static void phy_8x16_shutdown(struct usb_phy *phy)
205{
206	u32 val;
207
208	/* Put the controller in non-driving mode */
209	val = usb_phy_io_read(phy, ULPI_FUNC_CTRL);
210	val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
211	val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
212	usb_phy_io_write(phy, val, ULPI_FUNC_CTRL);
213}
214
215static int phy_8x16_read_devicetree(struct phy_8x16 *qphy)
216{
217	struct device *dev = qphy->phy.dev;
218	int ret;
219
220	qphy->core_clk = devm_clk_get(dev, "core");
221	if (IS_ERR(qphy->core_clk))
222		return PTR_ERR(qphy->core_clk);
223
224	qphy->iface_clk = devm_clk_get(dev, "iface");
225	if (IS_ERR(qphy->iface_clk))
226		return PTR_ERR(qphy->iface_clk);
227
228	qphy->regulator[0].supply = "v3p3";
229	qphy->regulator[1].supply = "v1p8";
230	qphy->regulator[2].supply = "vddcx";
231
232	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(qphy->regulator),
233				      qphy->regulator);
234	if (ret)
235		return ret;
236
237	qphy->phy_reset = devm_reset_control_get(dev, "phy");
238	if (IS_ERR(qphy->phy_reset))
239		return PTR_ERR(qphy->phy_reset);
240
241	qphy->switch_gpio = devm_gpiod_get_optional(dev, "switch",
242						   GPIOD_OUT_LOW);
243	if (IS_ERR(qphy->switch_gpio))
244		return PTR_ERR(qphy->switch_gpio);
245
246	return 0;
247}
248
249static int phy_8x16_reboot_notify(struct notifier_block *this,
250				  unsigned long code, void *unused)
251{
252	struct phy_8x16 *qphy;
253
254	qphy = container_of(this, struct phy_8x16, reboot_notify);
255
256	/*
257	 * Ensure that D+/D- lines are routed to uB connector, so
258	 * we could load bootloader/kernel at next reboot_notify
259	 */
260	gpiod_set_value_cansleep(qphy->switch_gpio, 0);
261	return NOTIFY_DONE;
262}
263
264static int phy_8x16_probe(struct platform_device *pdev)
265{
266	struct phy_8x16 *qphy;
267	struct resource *res;
268	struct usb_phy *phy;
269	int ret;
270
271	qphy = devm_kzalloc(&pdev->dev, sizeof(*qphy), GFP_KERNEL);
272	if (!qphy)
273		return -ENOMEM;
274
275	platform_set_drvdata(pdev, qphy);
276
277	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
278	if (!res)
279		return -EINVAL;
280
281	qphy->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
282	if (!qphy->regs)
283		return -ENOMEM;
284
285	phy			= &qphy->phy;
286	phy->dev		= &pdev->dev;
287	phy->label		= dev_name(&pdev->dev);
288	phy->init		= phy_8x16_init;
289	phy->shutdown		= phy_8x16_shutdown;
290	phy->notify_connect	= phy_8x16_notify_connect;
291	phy->notify_disconnect	= phy_8x16_notify_disconnect;
292	phy->io_priv		= qphy->regs + HSPHY_ULPI_VIEWPORT;
293	phy->io_ops		= &ulpi_viewport_access_ops;
294	phy->type		= USB_PHY_TYPE_USB2;
295
296	ret = phy_8x16_read_devicetree(qphy);
297	if (ret < 0)
298		return ret;
299
300	qphy->vbus_edev = extcon_get_edev_by_phandle(phy->dev, 0);
301	if (IS_ERR(qphy->vbus_edev))
302		return PTR_ERR(qphy->vbus_edev);
303
304	ret = clk_set_rate(qphy->core_clk, INT_MAX);
305	if (ret < 0)
306		dev_dbg(phy->dev, "Can't boost core clock\n");
307
308	ret = clk_prepare_enable(qphy->core_clk);
309	if (ret < 0)
310		return ret;
311
312	ret = clk_prepare_enable(qphy->iface_clk);
313	if (ret < 0)
314		goto off_core;
315
316	ret = regulator_bulk_enable(ARRAY_SIZE(qphy->regulator),
317				    qphy->regulator);
318	if (WARN_ON(ret))
319		goto off_clks;
320
321	qphy->vbus_notify.notifier_call = phy_8x16_vbus_notify;
322	ret = extcon_register_notifier(qphy->vbus_edev, EXTCON_USB,
323				       &qphy->vbus_notify);
324	if (ret < 0)
325		goto off_power;
326
327	ret = usb_add_phy_dev(&qphy->phy);
328	if (ret)
329		goto off_extcon;
330
331	qphy->reboot_notify.notifier_call = phy_8x16_reboot_notify;
332	register_reboot_notifier(&qphy->reboot_notify);
333
334	return 0;
335
336off_extcon:
337	extcon_unregister_notifier(qphy->vbus_edev, EXTCON_USB,
338				   &qphy->vbus_notify);
339off_power:
340	regulator_bulk_disable(ARRAY_SIZE(qphy->regulator), qphy->regulator);
341off_clks:
342	clk_disable_unprepare(qphy->iface_clk);
343off_core:
344	clk_disable_unprepare(qphy->core_clk);
345	return ret;
346}
347
348static int phy_8x16_remove(struct platform_device *pdev)
349{
350	struct phy_8x16 *qphy = platform_get_drvdata(pdev);
351
352	unregister_reboot_notifier(&qphy->reboot_notify);
353	extcon_unregister_notifier(qphy->vbus_edev, EXTCON_USB,
354				   &qphy->vbus_notify);
355
356	/*
357	 * Ensure that D+/D- lines are routed to uB connector, so
358	 * we could load bootloader/kernel at next reboot_notify
359	 */
360	gpiod_set_value_cansleep(qphy->switch_gpio, 0);
361
362	usb_remove_phy(&qphy->phy);
363
364	clk_disable_unprepare(qphy->iface_clk);
365	clk_disable_unprepare(qphy->core_clk);
366	regulator_bulk_disable(ARRAY_SIZE(qphy->regulator), qphy->regulator);
367	return 0;
368}
369
370static const struct of_device_id phy_8x16_dt_match[] = {
371	{ .compatible = "qcom,usb-8x16-phy" },
372	{ }
373};
374MODULE_DEVICE_TABLE(of, phy_8x16_dt_match);
375
376static struct platform_driver phy_8x16_driver = {
377	.probe	= phy_8x16_probe,
378	.remove = phy_8x16_remove,
379	.driver = {
380		.name = "phy-qcom-8x16-usb",
381		.of_match_table = phy_8x16_dt_match,
382	},
383};
384module_platform_driver(phy_8x16_driver);
385
386MODULE_LICENSE("GPL v2");
387MODULE_DESCRIPTION("Qualcomm APQ8016/MSM8916 chipsets USB transceiver driver");