xref: /linux/drivers/phy/motorola/phy-cpcap-usb.c (revision c771600c6af14749609b49565ffb4cac2959710d)
12aec85b2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26d6ce40fSTony Lindgren /*
36d6ce40fSTony Lindgren  * Motorola CPCAP PMIC USB PHY driver
46d6ce40fSTony Lindgren  * Copyright (C) 2017 Tony Lindgren <tony@atomide.com>
56d6ce40fSTony Lindgren  *
66d6ce40fSTony Lindgren  * Some parts based on earlier Motorola Linux kernel tree code in
76d6ce40fSTony Lindgren  * board-mapphone-usb.c and cpcap-usb-det.c:
86d6ce40fSTony Lindgren  * Copyright (C) 2007 - 2011 Motorola, Inc.
96d6ce40fSTony Lindgren  */
106d6ce40fSTony Lindgren 
116d6ce40fSTony Lindgren #include <linux/atomic.h>
126d6ce40fSTony Lindgren #include <linux/clk.h>
136d6ce40fSTony Lindgren #include <linux/delay.h>
146d6ce40fSTony Lindgren #include <linux/err.h>
156d6ce40fSTony Lindgren #include <linux/io.h>
166d6ce40fSTony Lindgren #include <linux/module.h>
176d6ce40fSTony Lindgren #include <linux/of.h>
186d6ce40fSTony Lindgren #include <linux/iio/consumer.h>
196d6ce40fSTony Lindgren #include <linux/pinctrl/consumer.h>
206d6ce40fSTony Lindgren #include <linux/platform_device.h>
216d6ce40fSTony Lindgren #include <linux/regmap.h>
226d6ce40fSTony Lindgren #include <linux/slab.h>
236d6ce40fSTony Lindgren 
246d6ce40fSTony Lindgren #include <linux/gpio/consumer.h>
256d6ce40fSTony Lindgren #include <linux/mfd/motorola-cpcap.h>
266d6ce40fSTony Lindgren #include <linux/phy/omap_usb.h>
276d6ce40fSTony Lindgren #include <linux/phy/phy.h>
286d6ce40fSTony Lindgren #include <linux/regulator/consumer.h>
296d6ce40fSTony Lindgren #include <linux/usb/musb.h>
306d6ce40fSTony Lindgren 
316d6ce40fSTony Lindgren /* CPCAP_REG_USBC1 register bits */
326d6ce40fSTony Lindgren #define CPCAP_BIT_IDPULSE		BIT(15)
336d6ce40fSTony Lindgren #define CPCAP_BIT_ID100KPU		BIT(14)
346d6ce40fSTony Lindgren #define CPCAP_BIT_IDPUCNTRL		BIT(13)
356d6ce40fSTony Lindgren #define CPCAP_BIT_IDPU			BIT(12)
366d6ce40fSTony Lindgren #define CPCAP_BIT_IDPD			BIT(11)
376d6ce40fSTony Lindgren #define CPCAP_BIT_VBUSCHRGTMR3		BIT(10)
386d6ce40fSTony Lindgren #define CPCAP_BIT_VBUSCHRGTMR2		BIT(9)
396d6ce40fSTony Lindgren #define CPCAP_BIT_VBUSCHRGTMR1		BIT(8)
406d6ce40fSTony Lindgren #define CPCAP_BIT_VBUSCHRGTMR0		BIT(7)
416d6ce40fSTony Lindgren #define CPCAP_BIT_VBUSPU		BIT(6)
426d6ce40fSTony Lindgren #define CPCAP_BIT_VBUSPD		BIT(5)
436d6ce40fSTony Lindgren #define CPCAP_BIT_DMPD			BIT(4)
446d6ce40fSTony Lindgren #define CPCAP_BIT_DPPD			BIT(3)
456d6ce40fSTony Lindgren #define CPCAP_BIT_DM1K5PU		BIT(2)
466d6ce40fSTony Lindgren #define CPCAP_BIT_DP1K5PU		BIT(1)
476d6ce40fSTony Lindgren #define CPCAP_BIT_DP150KPU		BIT(0)
486d6ce40fSTony Lindgren 
496d6ce40fSTony Lindgren /* CPCAP_REG_USBC2 register bits */
506d6ce40fSTony Lindgren #define CPCAP_BIT_ZHSDRV1		BIT(15)
516d6ce40fSTony Lindgren #define CPCAP_BIT_ZHSDRV0		BIT(14)
526d6ce40fSTony Lindgren #define CPCAP_BIT_DPLLCLKREQ		BIT(13)
536d6ce40fSTony Lindgren #define CPCAP_BIT_SE0CONN		BIT(12)
546d6ce40fSTony Lindgren #define CPCAP_BIT_UARTTXTRI		BIT(11)
556d6ce40fSTony Lindgren #define CPCAP_BIT_UARTSWAP		BIT(10)
566d6ce40fSTony Lindgren #define CPCAP_BIT_UARTMUX1		BIT(9)
576d6ce40fSTony Lindgren #define CPCAP_BIT_UARTMUX0		BIT(8)
586d6ce40fSTony Lindgren #define CPCAP_BIT_ULPISTPLOW		BIT(7)
596d6ce40fSTony Lindgren #define CPCAP_BIT_TXENPOL		BIT(6)
606d6ce40fSTony Lindgren #define CPCAP_BIT_USBXCVREN		BIT(5)
616d6ce40fSTony Lindgren #define CPCAP_BIT_USBCNTRL		BIT(4)
626d6ce40fSTony Lindgren #define CPCAP_BIT_USBSUSPEND		BIT(3)
636d6ce40fSTony Lindgren #define CPCAP_BIT_EMUMODE2		BIT(2)
646d6ce40fSTony Lindgren #define CPCAP_BIT_EMUMODE1		BIT(1)
656d6ce40fSTony Lindgren #define CPCAP_BIT_EMUMODE0		BIT(0)
666d6ce40fSTony Lindgren 
676d6ce40fSTony Lindgren /* CPCAP_REG_USBC3 register bits */
686d6ce40fSTony Lindgren #define CPCAP_BIT_SPARE_898_15		BIT(15)
696d6ce40fSTony Lindgren #define CPCAP_BIT_IHSTX03		BIT(14)
706d6ce40fSTony Lindgren #define CPCAP_BIT_IHSTX02		BIT(13)
716d6ce40fSTony Lindgren #define CPCAP_BIT_IHSTX01		BIT(12)
726d6ce40fSTony Lindgren #define CPCAP_BIT_IHSTX0		BIT(11)
736d6ce40fSTony Lindgren #define CPCAP_BIT_IDPU_SPI		BIT(10)
746d6ce40fSTony Lindgren #define CPCAP_BIT_UNUSED_898_9		BIT(9)
756d6ce40fSTony Lindgren #define CPCAP_BIT_VBUSSTBY_EN		BIT(8)
766d6ce40fSTony Lindgren #define CPCAP_BIT_VBUSEN_SPI		BIT(7)
776d6ce40fSTony Lindgren #define CPCAP_BIT_VBUSPU_SPI		BIT(6)
786d6ce40fSTony Lindgren #define CPCAP_BIT_VBUSPD_SPI		BIT(5)
796d6ce40fSTony Lindgren #define CPCAP_BIT_DMPD_SPI		BIT(4)
806d6ce40fSTony Lindgren #define CPCAP_BIT_DPPD_SPI		BIT(3)
816d6ce40fSTony Lindgren #define CPCAP_BIT_SUSPEND_SPI		BIT(2)
826d6ce40fSTony Lindgren #define CPCAP_BIT_PU_SPI		BIT(1)
836d6ce40fSTony Lindgren #define CPCAP_BIT_ULPI_SPI_SEL		BIT(0)
846d6ce40fSTony Lindgren 
856d6ce40fSTony Lindgren struct cpcap_usb_ints_state {
866d6ce40fSTony Lindgren 	bool id_ground;
876d6ce40fSTony Lindgren 	bool id_float;
886d6ce40fSTony Lindgren 	bool chrg_det;
896d6ce40fSTony Lindgren 	bool rvrs_chrg;
906d6ce40fSTony Lindgren 	bool vbusov;
916d6ce40fSTony Lindgren 
926d6ce40fSTony Lindgren 	bool chrg_se1b;
936d6ce40fSTony Lindgren 	bool se0conn;
946d6ce40fSTony Lindgren 	bool rvrs_mode;
956d6ce40fSTony Lindgren 	bool chrgcurr1;
966d6ce40fSTony Lindgren 	bool vbusvld;
976d6ce40fSTony Lindgren 	bool sessvld;
986d6ce40fSTony Lindgren 	bool sessend;
996d6ce40fSTony Lindgren 	bool se1;
1006d6ce40fSTony Lindgren 
1016d6ce40fSTony Lindgren 	bool battdetb;
1026d6ce40fSTony Lindgren 	bool dm;
1036d6ce40fSTony Lindgren 	bool dp;
1046d6ce40fSTony Lindgren };
1056d6ce40fSTony Lindgren 
1066d6ce40fSTony Lindgren enum cpcap_gpio_mode {
1076d6ce40fSTony Lindgren 	CPCAP_DM_DP,
1086d6ce40fSTony Lindgren 	CPCAP_MDM_RX_TX,
10963078b6bSTony Lindgren 	CPCAP_UNKNOWN_DISABLED,	/* Seems to disable USB lines */
1106d6ce40fSTony Lindgren 	CPCAP_OTG_DM_DP,
1116d6ce40fSTony Lindgren };
1126d6ce40fSTony Lindgren 
1136d6ce40fSTony Lindgren struct cpcap_phy_ddata {
1146d6ce40fSTony Lindgren 	struct regmap *reg;
1156d6ce40fSTony Lindgren 	struct device *dev;
1166d6ce40fSTony Lindgren 	struct usb_phy phy;
1176d6ce40fSTony Lindgren 	struct delayed_work detect_work;
1186d6ce40fSTony Lindgren 	struct pinctrl *pins;
1196d6ce40fSTony Lindgren 	struct pinctrl_state *pins_ulpi;
1206d6ce40fSTony Lindgren 	struct pinctrl_state *pins_utmi;
1216d6ce40fSTony Lindgren 	struct pinctrl_state *pins_uart;
1226d6ce40fSTony Lindgren 	struct gpio_desc *gpio[2];
1236d6ce40fSTony Lindgren 	struct iio_channel *vbus;
1246d6ce40fSTony Lindgren 	struct iio_channel *id;
1256d6ce40fSTony Lindgren 	struct regulator *vusb;
1266d6ce40fSTony Lindgren 	atomic_t active;
1279492535eSTony Lindgren 	unsigned int vbus_provider:1;
1289492535eSTony Lindgren 	unsigned int docked:1;
1296d6ce40fSTony Lindgren };
1306d6ce40fSTony Lindgren 
cpcap_usb_vbus_valid(struct cpcap_phy_ddata * ddata)1316d6ce40fSTony Lindgren static bool cpcap_usb_vbus_valid(struct cpcap_phy_ddata *ddata)
1326d6ce40fSTony Lindgren {
1336d6ce40fSTony Lindgren 	int error, value = 0;
1346d6ce40fSTony Lindgren 
1356d6ce40fSTony Lindgren 	error = iio_read_channel_processed(ddata->vbus, &value);
1366d6ce40fSTony Lindgren 	if (error >= 0)
137d68f2cb0SYang Li 		return value > 3900;
1386d6ce40fSTony Lindgren 
1396d6ce40fSTony Lindgren 	dev_err(ddata->dev, "error reading VBUS: %i\n", error);
1406d6ce40fSTony Lindgren 
1416d6ce40fSTony Lindgren 	return false;
1426d6ce40fSTony Lindgren }
1436d6ce40fSTony Lindgren 
cpcap_usb_phy_set_host(struct usb_otg * otg,struct usb_bus * host)1446d6ce40fSTony Lindgren static int cpcap_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host)
1456d6ce40fSTony Lindgren {
1466d6ce40fSTony Lindgren 	otg->host = host;
1476d6ce40fSTony Lindgren 	if (!host)
1486d6ce40fSTony Lindgren 		otg->state = OTG_STATE_UNDEFINED;
1496d6ce40fSTony Lindgren 
1506d6ce40fSTony Lindgren 	return 0;
1516d6ce40fSTony Lindgren }
1526d6ce40fSTony Lindgren 
cpcap_usb_phy_set_peripheral(struct usb_otg * otg,struct usb_gadget * gadget)1536d6ce40fSTony Lindgren static int cpcap_usb_phy_set_peripheral(struct usb_otg *otg,
1546d6ce40fSTony Lindgren 					struct usb_gadget *gadget)
1556d6ce40fSTony Lindgren {
1566d6ce40fSTony Lindgren 	otg->gadget = gadget;
1576d6ce40fSTony Lindgren 	if (!gadget)
1586d6ce40fSTony Lindgren 		otg->state = OTG_STATE_UNDEFINED;
1596d6ce40fSTony Lindgren 
1606d6ce40fSTony Lindgren 	return 0;
1616d6ce40fSTony Lindgren }
1626d6ce40fSTony Lindgren 
1636d6ce40fSTony Lindgren static const struct phy_ops ops = {
1646d6ce40fSTony Lindgren 	.owner		= THIS_MODULE,
1656d6ce40fSTony Lindgren };
1666d6ce40fSTony Lindgren 
cpcap_phy_get_ints_state(struct cpcap_phy_ddata * ddata,struct cpcap_usb_ints_state * s)1676d6ce40fSTony Lindgren static int cpcap_phy_get_ints_state(struct cpcap_phy_ddata *ddata,
1686d6ce40fSTony Lindgren 				    struct cpcap_usb_ints_state *s)
1696d6ce40fSTony Lindgren {
1706d6ce40fSTony Lindgren 	int val, error;
1716d6ce40fSTony Lindgren 
1726d6ce40fSTony Lindgren 	error = regmap_read(ddata->reg, CPCAP_REG_INTS1, &val);
1736d6ce40fSTony Lindgren 	if (error)
1746d6ce40fSTony Lindgren 		return error;
1756d6ce40fSTony Lindgren 
1766d6ce40fSTony Lindgren 	s->id_ground = val & BIT(15);
1776d6ce40fSTony Lindgren 	s->id_float = val & BIT(14);
1786d6ce40fSTony Lindgren 	s->vbusov = val & BIT(11);
1796d6ce40fSTony Lindgren 
1806d6ce40fSTony Lindgren 	error = regmap_read(ddata->reg, CPCAP_REG_INTS2, &val);
1816d6ce40fSTony Lindgren 	if (error)
1826d6ce40fSTony Lindgren 		return error;
1836d6ce40fSTony Lindgren 
1846d6ce40fSTony Lindgren 	s->vbusvld = val & BIT(3);
1856d6ce40fSTony Lindgren 	s->sessvld = val & BIT(2);
1866d6ce40fSTony Lindgren 	s->sessend = val & BIT(1);
1876d6ce40fSTony Lindgren 	s->se1 = val & BIT(0);
1886d6ce40fSTony Lindgren 
1896d6ce40fSTony Lindgren 	error = regmap_read(ddata->reg, CPCAP_REG_INTS4, &val);
1906d6ce40fSTony Lindgren 	if (error)
1916d6ce40fSTony Lindgren 		return error;
1926d6ce40fSTony Lindgren 
1936d6ce40fSTony Lindgren 	s->dm = val & BIT(1);
1946d6ce40fSTony Lindgren 	s->dp = val & BIT(0);
1956d6ce40fSTony Lindgren 
1966d6ce40fSTony Lindgren 	return 0;
1976d6ce40fSTony Lindgren }
1986d6ce40fSTony Lindgren 
1996d6ce40fSTony Lindgren static int cpcap_usb_set_uart_mode(struct cpcap_phy_ddata *ddata);
2006d6ce40fSTony Lindgren static int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata);
2016d6ce40fSTony Lindgren 
cpcap_usb_try_musb_mailbox(struct cpcap_phy_ddata * ddata,enum musb_vbus_id_status status)2024acb0200STony Lindgren static void cpcap_usb_try_musb_mailbox(struct cpcap_phy_ddata *ddata,
2034acb0200STony Lindgren 				       enum musb_vbus_id_status status)
2044acb0200STony Lindgren {
2054acb0200STony Lindgren 	int error;
2064acb0200STony Lindgren 
2074acb0200STony Lindgren 	error = musb_mailbox(status);
2084acb0200STony Lindgren 	if (!error)
2094acb0200STony Lindgren 		return;
2104acb0200STony Lindgren 
2114acb0200STony Lindgren 	dev_dbg(ddata->dev, "%s: musb_mailbox failed: %i\n",
2124acb0200STony Lindgren 		__func__, error);
2134acb0200STony Lindgren }
2144acb0200STony Lindgren 
cpcap_usb_detect(struct work_struct * work)2156d6ce40fSTony Lindgren static void cpcap_usb_detect(struct work_struct *work)
2166d6ce40fSTony Lindgren {
2176d6ce40fSTony Lindgren 	struct cpcap_phy_ddata *ddata;
2186d6ce40fSTony Lindgren 	struct cpcap_usb_ints_state s;
2196d6ce40fSTony Lindgren 	bool vbus = false;
2206d6ce40fSTony Lindgren 	int error;
2216d6ce40fSTony Lindgren 
2226d6ce40fSTony Lindgren 	ddata = container_of(work, struct cpcap_phy_ddata, detect_work.work);
2236d6ce40fSTony Lindgren 
2246d6ce40fSTony Lindgren 	error = cpcap_phy_get_ints_state(ddata, &s);
2256d6ce40fSTony Lindgren 	if (error)
2266d6ce40fSTony Lindgren 		return;
2276d6ce40fSTony Lindgren 
2289492535eSTony Lindgren 	vbus = cpcap_usb_vbus_valid(ddata);
2299492535eSTony Lindgren 
2309492535eSTony Lindgren 	/* We need to kick the VBUS as USB A-host */
2319492535eSTony Lindgren 	if (s.id_ground && ddata->vbus_provider) {
2329492535eSTony Lindgren 		dev_dbg(ddata->dev, "still in USB A-host mode, kicking VBUS\n");
2339492535eSTony Lindgren 
2349492535eSTony Lindgren 		cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND);
2359492535eSTony Lindgren 
2369492535eSTony Lindgren 		error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3,
2379492535eSTony Lindgren 					   CPCAP_BIT_VBUSSTBY_EN |
2389492535eSTony Lindgren 					   CPCAP_BIT_VBUSEN_SPI,
2399492535eSTony Lindgren 					   CPCAP_BIT_VBUSEN_SPI);
2409492535eSTony Lindgren 		if (error)
2419492535eSTony Lindgren 			goto out_err;
2429492535eSTony Lindgren 
2439492535eSTony Lindgren 		return;
2449492535eSTony Lindgren 	}
2459492535eSTony Lindgren 
2469492535eSTony Lindgren 	if (vbus && s.id_ground && ddata->docked) {
2479492535eSTony Lindgren 		dev_dbg(ddata->dev, "still docked as A-host, signal ID down\n");
2489492535eSTony Lindgren 
2499492535eSTony Lindgren 		cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND);
2509492535eSTony Lindgren 
2519492535eSTony Lindgren 		return;
2529492535eSTony Lindgren 	}
2539492535eSTony Lindgren 
2549492535eSTony Lindgren 	/* No VBUS needed with docks */
2559492535eSTony Lindgren 	if (vbus && s.id_ground && !ddata->vbus_provider) {
2569492535eSTony Lindgren 		dev_dbg(ddata->dev, "connected to a dock\n");
2579492535eSTony Lindgren 
2589492535eSTony Lindgren 		ddata->docked = true;
2599492535eSTony Lindgren 
2609492535eSTony Lindgren 		error = cpcap_usb_set_usb_mode(ddata);
2619492535eSTony Lindgren 		if (error)
2629492535eSTony Lindgren 			goto out_err;
2639492535eSTony Lindgren 
2649492535eSTony Lindgren 		cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND);
2659492535eSTony Lindgren 
2669492535eSTony Lindgren 		/*
2679492535eSTony Lindgren 		 * Force check state again after musb has reoriented,
2689492535eSTony Lindgren 		 * otherwise devices won't enumerate after loading PHY
2699492535eSTony Lindgren 		 * driver.
2709492535eSTony Lindgren 		 */
2719492535eSTony Lindgren 		schedule_delayed_work(&ddata->detect_work,
2729492535eSTony Lindgren 				      msecs_to_jiffies(1000));
2739492535eSTony Lindgren 
2749492535eSTony Lindgren 		return;
2759492535eSTony Lindgren 	}
2769492535eSTony Lindgren 
2779492535eSTony Lindgren 	if (s.id_ground && !ddata->docked) {
2786d6ce40fSTony Lindgren 		dev_dbg(ddata->dev, "id ground, USB host mode\n");
2799492535eSTony Lindgren 
2809492535eSTony Lindgren 		ddata->vbus_provider = true;
2819492535eSTony Lindgren 
2826d6ce40fSTony Lindgren 		error = cpcap_usb_set_usb_mode(ddata);
2836d6ce40fSTony Lindgren 		if (error)
2846d6ce40fSTony Lindgren 			goto out_err;
2856d6ce40fSTony Lindgren 
2864acb0200STony Lindgren 		cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND);
2876d6ce40fSTony Lindgren 
2886d6ce40fSTony Lindgren 		error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3,
2897f737861STony Lindgren 					   CPCAP_BIT_VBUSSTBY_EN |
2907f737861STony Lindgren 					   CPCAP_BIT_VBUSEN_SPI,
2917f737861STony Lindgren 					   CPCAP_BIT_VBUSEN_SPI);
2926d6ce40fSTony Lindgren 		if (error)
2936d6ce40fSTony Lindgren 			goto out_err;
2946d6ce40fSTony Lindgren 
2956d6ce40fSTony Lindgren 		return;
2966d6ce40fSTony Lindgren 	}
2976d6ce40fSTony Lindgren 
2986d6ce40fSTony Lindgren 	error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3,
2997f737861STony Lindgren 				   CPCAP_BIT_VBUSSTBY_EN |
3007f737861STony Lindgren 				   CPCAP_BIT_VBUSEN_SPI, 0);
3016d6ce40fSTony Lindgren 	if (error)
3026d6ce40fSTony Lindgren 		goto out_err;
3036d6ce40fSTony Lindgren 
3046d6ce40fSTony Lindgren 	vbus = cpcap_usb_vbus_valid(ddata);
3056d6ce40fSTony Lindgren 
3066d6ce40fSTony Lindgren 	/* Otherwise assume we're connected to a USB host */
3079492535eSTony Lindgren 	if (vbus) {
3086d6ce40fSTony Lindgren 		dev_dbg(ddata->dev, "connected to USB host\n");
3096d6ce40fSTony Lindgren 		error = cpcap_usb_set_usb_mode(ddata);
3106d6ce40fSTony Lindgren 		if (error)
3116d6ce40fSTony Lindgren 			goto out_err;
3124acb0200STony Lindgren 		cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_VALID);
3136d6ce40fSTony Lindgren 
3146d6ce40fSTony Lindgren 		return;
3156d6ce40fSTony Lindgren 	}
3166d6ce40fSTony Lindgren 
3179492535eSTony Lindgren 	ddata->vbus_provider = false;
3189492535eSTony Lindgren 	ddata->docked = false;
319049226b9STony Lindgren 	cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF);
320049226b9STony Lindgren 
3216d6ce40fSTony Lindgren 	/* Default to debug UART mode */
3226d6ce40fSTony Lindgren 	error = cpcap_usb_set_uart_mode(ddata);
3236d6ce40fSTony Lindgren 	if (error)
3246d6ce40fSTony Lindgren 		goto out_err;
3256d6ce40fSTony Lindgren 
3266d6ce40fSTony Lindgren 	dev_dbg(ddata->dev, "set UART mode\n");
3276d6ce40fSTony Lindgren 
3286d6ce40fSTony Lindgren 	return;
3296d6ce40fSTony Lindgren 
3306d6ce40fSTony Lindgren out_err:
3316d6ce40fSTony Lindgren 	dev_err(ddata->dev, "error setting cable state: %i\n", error);
3326d6ce40fSTony Lindgren }
3336d6ce40fSTony Lindgren 
cpcap_phy_irq_thread(int irq,void * data)3346d6ce40fSTony Lindgren static irqreturn_t cpcap_phy_irq_thread(int irq, void *data)
3356d6ce40fSTony Lindgren {
3366d6ce40fSTony Lindgren 	struct cpcap_phy_ddata *ddata = data;
3376d6ce40fSTony Lindgren 
3386d6ce40fSTony Lindgren 	if (!atomic_read(&ddata->active))
3396d6ce40fSTony Lindgren 		return IRQ_NONE;
3406d6ce40fSTony Lindgren 
3416d6ce40fSTony Lindgren 	schedule_delayed_work(&ddata->detect_work, msecs_to_jiffies(1));
3426d6ce40fSTony Lindgren 
3436d6ce40fSTony Lindgren 	return IRQ_HANDLED;
3446d6ce40fSTony Lindgren }
3456d6ce40fSTony Lindgren 
cpcap_usb_init_irq(struct platform_device * pdev,struct cpcap_phy_ddata * ddata,const char * name)3466d6ce40fSTony Lindgren static int cpcap_usb_init_irq(struct platform_device *pdev,
3476d6ce40fSTony Lindgren 			      struct cpcap_phy_ddata *ddata,
3486d6ce40fSTony Lindgren 			      const char *name)
3496d6ce40fSTony Lindgren {
3506d6ce40fSTony Lindgren 	int irq, error;
3516d6ce40fSTony Lindgren 
3526d6ce40fSTony Lindgren 	irq = platform_get_irq_byname(pdev, name);
353e796cc6aSArvind Yadav 	if (irq < 0)
3546d6ce40fSTony Lindgren 		return -ENODEV;
3556d6ce40fSTony Lindgren 
3566d6ce40fSTony Lindgren 	error = devm_request_threaded_irq(ddata->dev, irq, NULL,
3576d6ce40fSTony Lindgren 					  cpcap_phy_irq_thread,
35825d76fedSZou Wei 					  IRQF_SHARED |
35925d76fedSZou Wei 					  IRQF_ONESHOT,
3606d6ce40fSTony Lindgren 					  name, ddata);
3616d6ce40fSTony Lindgren 	if (error) {
3626d6ce40fSTony Lindgren 		dev_err(ddata->dev, "could not get irq %s: %i\n",
3636d6ce40fSTony Lindgren 			name, error);
3646d6ce40fSTony Lindgren 
3656d6ce40fSTony Lindgren 		return error;
3666d6ce40fSTony Lindgren 	}
3676d6ce40fSTony Lindgren 
3686d6ce40fSTony Lindgren 	return 0;
3696d6ce40fSTony Lindgren }
3706d6ce40fSTony Lindgren 
3716d6ce40fSTony Lindgren static const char * const cpcap_phy_irqs[] = {
3726d6ce40fSTony Lindgren 	/* REG_INT_0 */
3736d6ce40fSTony Lindgren 	"id_ground", "id_float",
3746d6ce40fSTony Lindgren 
3756d6ce40fSTony Lindgren 	/* REG_INT1 */
3766d6ce40fSTony Lindgren 	"se0conn", "vbusvld", "sessvld", "sessend", "se1",
3776d6ce40fSTony Lindgren 
3786d6ce40fSTony Lindgren 	/* REG_INT_3 */
3796d6ce40fSTony Lindgren 	"dm", "dp",
3806d6ce40fSTony Lindgren };
3816d6ce40fSTony Lindgren 
cpcap_usb_init_interrupts(struct platform_device * pdev,struct cpcap_phy_ddata * ddata)3826d6ce40fSTony Lindgren static int cpcap_usb_init_interrupts(struct platform_device *pdev,
3836d6ce40fSTony Lindgren 				     struct cpcap_phy_ddata *ddata)
3846d6ce40fSTony Lindgren {
3856d6ce40fSTony Lindgren 	int i, error;
3866d6ce40fSTony Lindgren 
3876d6ce40fSTony Lindgren 	for (i = 0; i < ARRAY_SIZE(cpcap_phy_irqs); i++) {
3886d6ce40fSTony Lindgren 		error = cpcap_usb_init_irq(pdev, ddata, cpcap_phy_irqs[i]);
3896d6ce40fSTony Lindgren 		if (error)
3906d6ce40fSTony Lindgren 			return error;
3916d6ce40fSTony Lindgren 	}
3926d6ce40fSTony Lindgren 
3936d6ce40fSTony Lindgren 	return 0;
3946d6ce40fSTony Lindgren }
3956d6ce40fSTony Lindgren 
3966d6ce40fSTony Lindgren /*
3976d6ce40fSTony Lindgren  * Optional pins and modes. At least Motorola mapphone devices
3986d6ce40fSTony Lindgren  * are using two GPIOs and dynamic pinctrl to multiplex PHY pins
3996d6ce40fSTony Lindgren  * to UART, ULPI or UTMI mode.
4006d6ce40fSTony Lindgren  */
4016d6ce40fSTony Lindgren 
cpcap_usb_gpio_set_mode(struct cpcap_phy_ddata * ddata,enum cpcap_gpio_mode mode)4026d6ce40fSTony Lindgren static int cpcap_usb_gpio_set_mode(struct cpcap_phy_ddata *ddata,
4036d6ce40fSTony Lindgren 				   enum cpcap_gpio_mode mode)
4046d6ce40fSTony Lindgren {
4056d6ce40fSTony Lindgren 	if (!ddata->gpio[0] || !ddata->gpio[1])
4066d6ce40fSTony Lindgren 		return 0;
4076d6ce40fSTony Lindgren 
4086d6ce40fSTony Lindgren 	gpiod_set_value(ddata->gpio[0], mode & 1);
4096d6ce40fSTony Lindgren 	gpiod_set_value(ddata->gpio[1], mode >> 1);
4106d6ce40fSTony Lindgren 
4116d6ce40fSTony Lindgren 	return 0;
4126d6ce40fSTony Lindgren }
4136d6ce40fSTony Lindgren 
cpcap_usb_set_uart_mode(struct cpcap_phy_ddata * ddata)4146d6ce40fSTony Lindgren static int cpcap_usb_set_uart_mode(struct cpcap_phy_ddata *ddata)
4156d6ce40fSTony Lindgren {
4166d6ce40fSTony Lindgren 	int error;
4176d6ce40fSTony Lindgren 
41863078b6bSTony Lindgren 	/* Disable lines to prevent glitches from waking up mdm6600 */
41963078b6bSTony Lindgren 	error = cpcap_usb_gpio_set_mode(ddata, CPCAP_UNKNOWN_DISABLED);
4206d6ce40fSTony Lindgren 	if (error)
4216d6ce40fSTony Lindgren 		goto out_err;
4226d6ce40fSTony Lindgren 
4236d6ce40fSTony Lindgren 	if (ddata->pins_uart) {
4246d6ce40fSTony Lindgren 		error = pinctrl_select_state(ddata->pins, ddata->pins_uart);
4256d6ce40fSTony Lindgren 		if (error)
4266d6ce40fSTony Lindgren 			goto out_err;
4276d6ce40fSTony Lindgren 	}
4286d6ce40fSTony Lindgren 
4296d6ce40fSTony Lindgren 	error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC1,
4306d6ce40fSTony Lindgren 				   CPCAP_BIT_VBUSPD,
4316d6ce40fSTony Lindgren 				   CPCAP_BIT_VBUSPD);
4326d6ce40fSTony Lindgren 	if (error)
4336d6ce40fSTony Lindgren 		goto out_err;
4346d6ce40fSTony Lindgren 
4356d6ce40fSTony Lindgren 	error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2,
4366d6ce40fSTony Lindgren 				   0xffff, CPCAP_BIT_UARTMUX0 |
4376d6ce40fSTony Lindgren 				   CPCAP_BIT_EMUMODE0);
4386d6ce40fSTony Lindgren 	if (error)
4396d6ce40fSTony Lindgren 		goto out_err;
4406d6ce40fSTony Lindgren 
4416d6ce40fSTony Lindgren 	error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 0x7fff,
4426d6ce40fSTony Lindgren 				   CPCAP_BIT_IDPU_SPI);
4436d6ce40fSTony Lindgren 	if (error)
4446d6ce40fSTony Lindgren 		goto out_err;
4456d6ce40fSTony Lindgren 
44663078b6bSTony Lindgren 	/* Enable UART mode */
44763078b6bSTony Lindgren 	error = cpcap_usb_gpio_set_mode(ddata, CPCAP_DM_DP);
44863078b6bSTony Lindgren 	if (error)
44963078b6bSTony Lindgren 		goto out_err;
45063078b6bSTony Lindgren 
4516d6ce40fSTony Lindgren 	return 0;
4526d6ce40fSTony Lindgren 
4536d6ce40fSTony Lindgren out_err:
4546d6ce40fSTony Lindgren 	dev_err(ddata->dev, "%s failed with %i\n", __func__, error);
4556d6ce40fSTony Lindgren 
4566d6ce40fSTony Lindgren 	return error;
4576d6ce40fSTony Lindgren }
4586d6ce40fSTony Lindgren 
cpcap_usb_set_usb_mode(struct cpcap_phy_ddata * ddata)4596d6ce40fSTony Lindgren static int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata)
4606d6ce40fSTony Lindgren {
4616d6ce40fSTony Lindgren 	int error;
4626d6ce40fSTony Lindgren 
46363078b6bSTony Lindgren 	/* Disable lines to prevent glitches from waking up mdm6600 */
46463078b6bSTony Lindgren 	error = cpcap_usb_gpio_set_mode(ddata, CPCAP_UNKNOWN_DISABLED);
4656d6ce40fSTony Lindgren 	if (error)
4666d6ce40fSTony Lindgren 		return error;
4676d6ce40fSTony Lindgren 
4686d6ce40fSTony Lindgren 	if (ddata->pins_utmi) {
4696d6ce40fSTony Lindgren 		error = pinctrl_select_state(ddata->pins, ddata->pins_utmi);
4706d6ce40fSTony Lindgren 		if (error) {
4716d6ce40fSTony Lindgren 			dev_err(ddata->dev, "could not set usb mode: %i\n",
4726d6ce40fSTony Lindgren 				error);
4736d6ce40fSTony Lindgren 
4746d6ce40fSTony Lindgren 			return error;
4756d6ce40fSTony Lindgren 		}
4766d6ce40fSTony Lindgren 	}
4776d6ce40fSTony Lindgren 
4786d6ce40fSTony Lindgren 	error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC1,
4796d6ce40fSTony Lindgren 				   CPCAP_BIT_VBUSPD, 0);
4806d6ce40fSTony Lindgren 	if (error)
4816d6ce40fSTony Lindgren 		goto out_err;
4826d6ce40fSTony Lindgren 
4836d6ce40fSTony Lindgren 	error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3,
4846d6ce40fSTony Lindgren 				   CPCAP_BIT_PU_SPI |
4856d6ce40fSTony Lindgren 				   CPCAP_BIT_DMPD_SPI |
4866d6ce40fSTony Lindgren 				   CPCAP_BIT_DPPD_SPI |
4876d6ce40fSTony Lindgren 				   CPCAP_BIT_SUSPEND_SPI |
4886d6ce40fSTony Lindgren 				   CPCAP_BIT_ULPI_SPI_SEL, 0);
4896d6ce40fSTony Lindgren 	if (error)
4906d6ce40fSTony Lindgren 		goto out_err;
4916d6ce40fSTony Lindgren 
4926d6ce40fSTony Lindgren 	error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2,
4936d6ce40fSTony Lindgren 				   CPCAP_BIT_USBXCVREN,
4946d6ce40fSTony Lindgren 				   CPCAP_BIT_USBXCVREN);
4956d6ce40fSTony Lindgren 	if (error)
4966d6ce40fSTony Lindgren 		goto out_err;
4976d6ce40fSTony Lindgren 
49863078b6bSTony Lindgren 	/* Enable USB mode */
49963078b6bSTony Lindgren 	error = cpcap_usb_gpio_set_mode(ddata, CPCAP_OTG_DM_DP);
50063078b6bSTony Lindgren 	if (error)
50163078b6bSTony Lindgren 		goto out_err;
50263078b6bSTony Lindgren 
5036d6ce40fSTony Lindgren 	return 0;
5046d6ce40fSTony Lindgren 
5056d6ce40fSTony Lindgren out_err:
5066d6ce40fSTony Lindgren 	dev_err(ddata->dev, "%s failed with %i\n", __func__, error);
5076d6ce40fSTony Lindgren 
5086d6ce40fSTony Lindgren 	return error;
5096d6ce40fSTony Lindgren }
5106d6ce40fSTony Lindgren 
cpcap_usb_init_optional_pins(struct cpcap_phy_ddata * ddata)5116d6ce40fSTony Lindgren static int cpcap_usb_init_optional_pins(struct cpcap_phy_ddata *ddata)
5126d6ce40fSTony Lindgren {
5136d6ce40fSTony Lindgren 	ddata->pins = devm_pinctrl_get(ddata->dev);
5146d6ce40fSTony Lindgren 	if (IS_ERR(ddata->pins)) {
5156d6ce40fSTony Lindgren 		dev_info(ddata->dev, "default pins not configured: %ld\n",
5166d6ce40fSTony Lindgren 			 PTR_ERR(ddata->pins));
5176d6ce40fSTony Lindgren 		ddata->pins = NULL;
518259a2400STony Lindgren 
519259a2400STony Lindgren 		return 0;
5206d6ce40fSTony Lindgren 	}
5216d6ce40fSTony Lindgren 
5226d6ce40fSTony Lindgren 	ddata->pins_ulpi = pinctrl_lookup_state(ddata->pins, "ulpi");
5236d6ce40fSTony Lindgren 	if (IS_ERR(ddata->pins_ulpi)) {
5246d6ce40fSTony Lindgren 		dev_info(ddata->dev, "ulpi pins not configured\n");
5256d6ce40fSTony Lindgren 		ddata->pins_ulpi = NULL;
5266d6ce40fSTony Lindgren 	}
5276d6ce40fSTony Lindgren 
5286d6ce40fSTony Lindgren 	ddata->pins_utmi = pinctrl_lookup_state(ddata->pins, "utmi");
5296d6ce40fSTony Lindgren 	if (IS_ERR(ddata->pins_utmi)) {
5306d6ce40fSTony Lindgren 		dev_info(ddata->dev, "utmi pins not configured\n");
5316d6ce40fSTony Lindgren 		ddata->pins_utmi = NULL;
5326d6ce40fSTony Lindgren 	}
5336d6ce40fSTony Lindgren 
5346d6ce40fSTony Lindgren 	ddata->pins_uart = pinctrl_lookup_state(ddata->pins, "uart");
5356d6ce40fSTony Lindgren 	if (IS_ERR(ddata->pins_uart)) {
5366d6ce40fSTony Lindgren 		dev_info(ddata->dev, "uart pins not configured\n");
5376d6ce40fSTony Lindgren 		ddata->pins_uart = NULL;
5386d6ce40fSTony Lindgren 	}
5396d6ce40fSTony Lindgren 
5406d6ce40fSTony Lindgren 	if (ddata->pins_uart)
5416d6ce40fSTony Lindgren 		return pinctrl_select_state(ddata->pins, ddata->pins_uart);
5426d6ce40fSTony Lindgren 
5436d6ce40fSTony Lindgren 	return 0;
5446d6ce40fSTony Lindgren }
5456d6ce40fSTony Lindgren 
cpcap_usb_init_optional_gpios(struct cpcap_phy_ddata * ddata)5466d6ce40fSTony Lindgren static void cpcap_usb_init_optional_gpios(struct cpcap_phy_ddata *ddata)
5476d6ce40fSTony Lindgren {
5486d6ce40fSTony Lindgren 	int i;
5496d6ce40fSTony Lindgren 
5506d6ce40fSTony Lindgren 	for (i = 0; i < 2; i++) {
5516d6ce40fSTony Lindgren 		ddata->gpio[i] = devm_gpiod_get_index(ddata->dev, "mode",
5526d6ce40fSTony Lindgren 						      i, GPIOD_OUT_HIGH);
5536d6ce40fSTony Lindgren 		if (IS_ERR(ddata->gpio[i])) {
5546d6ce40fSTony Lindgren 			dev_info(ddata->dev, "no mode change GPIO%i: %li\n",
5556d6ce40fSTony Lindgren 				 i, PTR_ERR(ddata->gpio[i]));
5566d6ce40fSTony Lindgren 			ddata->gpio[i] = NULL;
5576d6ce40fSTony Lindgren 		}
5586d6ce40fSTony Lindgren 	}
5596d6ce40fSTony Lindgren }
5606d6ce40fSTony Lindgren 
cpcap_usb_init_iio(struct cpcap_phy_ddata * ddata)5616d6ce40fSTony Lindgren static int cpcap_usb_init_iio(struct cpcap_phy_ddata *ddata)
5626d6ce40fSTony Lindgren {
5636d6ce40fSTony Lindgren 	enum iio_chan_type type;
5646d6ce40fSTony Lindgren 	int error;
5656d6ce40fSTony Lindgren 
5666d6ce40fSTony Lindgren 	ddata->vbus = devm_iio_channel_get(ddata->dev, "vbus");
5676d6ce40fSTony Lindgren 	if (IS_ERR(ddata->vbus)) {
5686d6ce40fSTony Lindgren 		error = PTR_ERR(ddata->vbus);
5696d6ce40fSTony Lindgren 		goto out_err;
5706d6ce40fSTony Lindgren 	}
5716d6ce40fSTony Lindgren 
5726d6ce40fSTony Lindgren 	if (!ddata->vbus->indio_dev) {
5736d6ce40fSTony Lindgren 		error = -ENXIO;
5746d6ce40fSTony Lindgren 		goto out_err;
5756d6ce40fSTony Lindgren 	}
5766d6ce40fSTony Lindgren 
5776d6ce40fSTony Lindgren 	error = iio_get_channel_type(ddata->vbus, &type);
5786d6ce40fSTony Lindgren 	if (error < 0)
5796d6ce40fSTony Lindgren 		goto out_err;
5806d6ce40fSTony Lindgren 
5816d6ce40fSTony Lindgren 	if (type != IIO_VOLTAGE) {
5826d6ce40fSTony Lindgren 		error = -EINVAL;
5836d6ce40fSTony Lindgren 		goto out_err;
5846d6ce40fSTony Lindgren 	}
5856d6ce40fSTony Lindgren 
5866d6ce40fSTony Lindgren 	return 0;
5876d6ce40fSTony Lindgren 
5886d6ce40fSTony Lindgren out_err:
5896d6ce40fSTony Lindgren 	dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n",
5906d6ce40fSTony Lindgren 		error);
5916d6ce40fSTony Lindgren 
5926d6ce40fSTony Lindgren 	return error;
5936d6ce40fSTony Lindgren }
5946d6ce40fSTony Lindgren 
5956d6ce40fSTony Lindgren #ifdef CONFIG_OF
5966d6ce40fSTony Lindgren static const struct of_device_id cpcap_usb_phy_id_table[] = {
5976d6ce40fSTony Lindgren 	{
5986d6ce40fSTony Lindgren 		.compatible = "motorola,cpcap-usb-phy",
5996d6ce40fSTony Lindgren 	},
6006d6ce40fSTony Lindgren 	{
6016d6ce40fSTony Lindgren 		.compatible = "motorola,mapphone-cpcap-usb-phy",
6026d6ce40fSTony Lindgren 	},
6036d6ce40fSTony Lindgren 	{},
6046d6ce40fSTony Lindgren };
6056d6ce40fSTony Lindgren MODULE_DEVICE_TABLE(of, cpcap_usb_phy_id_table);
6066d6ce40fSTony Lindgren #endif
6076d6ce40fSTony Lindgren 
cpcap_usb_phy_probe(struct platform_device * pdev)6086d6ce40fSTony Lindgren static int cpcap_usb_phy_probe(struct platform_device *pdev)
6096d6ce40fSTony Lindgren {
6106d6ce40fSTony Lindgren 	struct cpcap_phy_ddata *ddata;
6116d6ce40fSTony Lindgren 	struct phy *generic_phy;
6126d6ce40fSTony Lindgren 	struct phy_provider *phy_provider;
6136d6ce40fSTony Lindgren 	struct usb_otg *otg;
6146d6ce40fSTony Lindgren 	int error;
6156d6ce40fSTony Lindgren 
6166d6ce40fSTony Lindgren 	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
6176d6ce40fSTony Lindgren 	if (!ddata)
6186d6ce40fSTony Lindgren 		return -ENOMEM;
6196d6ce40fSTony Lindgren 
6206d6ce40fSTony Lindgren 	ddata->reg = dev_get_regmap(pdev->dev.parent, NULL);
6216d6ce40fSTony Lindgren 	if (!ddata->reg)
6226d6ce40fSTony Lindgren 		return -ENODEV;
6236d6ce40fSTony Lindgren 
6246d6ce40fSTony Lindgren 	otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
6256d6ce40fSTony Lindgren 	if (!otg)
6266d6ce40fSTony Lindgren 		return -ENOMEM;
6276d6ce40fSTony Lindgren 
6286d6ce40fSTony Lindgren 	ddata->dev = &pdev->dev;
6296d6ce40fSTony Lindgren 	ddata->phy.dev = ddata->dev;
6306d6ce40fSTony Lindgren 	ddata->phy.label = "cpcap_usb_phy";
6316d6ce40fSTony Lindgren 	ddata->phy.otg = otg;
6326d6ce40fSTony Lindgren 	ddata->phy.type = USB_PHY_TYPE_USB2;
6336d6ce40fSTony Lindgren 	otg->set_host = cpcap_usb_phy_set_host;
6346d6ce40fSTony Lindgren 	otg->set_peripheral = cpcap_usb_phy_set_peripheral;
6356d6ce40fSTony Lindgren 	otg->usb_phy = &ddata->phy;
6366d6ce40fSTony Lindgren 	INIT_DELAYED_WORK(&ddata->detect_work, cpcap_usb_detect);
6376d6ce40fSTony Lindgren 	platform_set_drvdata(pdev, ddata);
6386d6ce40fSTony Lindgren 
6396d6ce40fSTony Lindgren 	ddata->vusb = devm_regulator_get(&pdev->dev, "vusb");
6406d6ce40fSTony Lindgren 	if (IS_ERR(ddata->vusb))
6416d6ce40fSTony Lindgren 		return PTR_ERR(ddata->vusb);
6426d6ce40fSTony Lindgren 
6436d6ce40fSTony Lindgren 	error = regulator_enable(ddata->vusb);
6446d6ce40fSTony Lindgren 	if (error)
6456d6ce40fSTony Lindgren 		return error;
6466d6ce40fSTony Lindgren 
6476d6ce40fSTony Lindgren 	generic_phy = devm_phy_create(ddata->dev, NULL, &ops);
6486d6ce40fSTony Lindgren 	if (IS_ERR(generic_phy)) {
6496d6ce40fSTony Lindgren 		error = PTR_ERR(generic_phy);
650764257d9STony Lindgren 		goto out_reg_disable;
6516d6ce40fSTony Lindgren 	}
6526d6ce40fSTony Lindgren 
6536d6ce40fSTony Lindgren 	phy_set_drvdata(generic_phy, ddata);
6546d6ce40fSTony Lindgren 
6556d6ce40fSTony Lindgren 	phy_provider = devm_of_phy_provider_register(ddata->dev,
6566d6ce40fSTony Lindgren 						     of_phy_simple_xlate);
657764257d9STony Lindgren 	if (IS_ERR(phy_provider)) {
658764257d9STony Lindgren 		error = PTR_ERR(phy_provider);
659764257d9STony Lindgren 		goto out_reg_disable;
660764257d9STony Lindgren 	}
6616d6ce40fSTony Lindgren 
6626d6ce40fSTony Lindgren 	error = cpcap_usb_init_optional_pins(ddata);
6636d6ce40fSTony Lindgren 	if (error)
664764257d9STony Lindgren 		goto out_reg_disable;
6656d6ce40fSTony Lindgren 
6666d6ce40fSTony Lindgren 	cpcap_usb_init_optional_gpios(ddata);
6676d6ce40fSTony Lindgren 
6686d6ce40fSTony Lindgren 	error = cpcap_usb_init_iio(ddata);
6696d6ce40fSTony Lindgren 	if (error)
670764257d9STony Lindgren 		goto out_reg_disable;
6716d6ce40fSTony Lindgren 
6726d6ce40fSTony Lindgren 	error = cpcap_usb_init_interrupts(pdev, ddata);
6736d6ce40fSTony Lindgren 	if (error)
674764257d9STony Lindgren 		goto out_reg_disable;
6756d6ce40fSTony Lindgren 
6766d6ce40fSTony Lindgren 	usb_add_phy_dev(&ddata->phy);
6776d6ce40fSTony Lindgren 	atomic_set(&ddata->active, 1);
6786d6ce40fSTony Lindgren 	schedule_delayed_work(&ddata->detect_work, msecs_to_jiffies(1));
6796d6ce40fSTony Lindgren 
6806d6ce40fSTony Lindgren 	return 0;
681764257d9STony Lindgren 
682764257d9STony Lindgren out_reg_disable:
683764257d9STony Lindgren 	regulator_disable(ddata->vusb);
684764257d9STony Lindgren 
685764257d9STony Lindgren 	return error;
6866d6ce40fSTony Lindgren }
6876d6ce40fSTony Lindgren 
cpcap_usb_phy_remove(struct platform_device * pdev)688dd5c724eSUwe Kleine-König static void cpcap_usb_phy_remove(struct platform_device *pdev)
6896d6ce40fSTony Lindgren {
6906d6ce40fSTony Lindgren 	struct cpcap_phy_ddata *ddata = platform_get_drvdata(pdev);
6916d6ce40fSTony Lindgren 	int error;
6926d6ce40fSTony Lindgren 
6936d6ce40fSTony Lindgren 	atomic_set(&ddata->active, 0);
6946d6ce40fSTony Lindgren 	error = cpcap_usb_set_uart_mode(ddata);
6956d6ce40fSTony Lindgren 	if (error)
6966d6ce40fSTony Lindgren 		dev_err(ddata->dev, "could not set UART mode\n");
6976d6ce40fSTony Lindgren 
6984acb0200STony Lindgren 	cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF);
6996d6ce40fSTony Lindgren 
7006d6ce40fSTony Lindgren 	usb_remove_phy(&ddata->phy);
7016d6ce40fSTony Lindgren 	cancel_delayed_work_sync(&ddata->detect_work);
7026d6ce40fSTony Lindgren 	regulator_disable(ddata->vusb);
7036d6ce40fSTony Lindgren }
7046d6ce40fSTony Lindgren 
7056d6ce40fSTony Lindgren static struct platform_driver cpcap_usb_phy_driver = {
7066d6ce40fSTony Lindgren 	.probe		= cpcap_usb_phy_probe,
707*54234e3aSUwe Kleine-König 	.remove		= cpcap_usb_phy_remove,
7086d6ce40fSTony Lindgren 	.driver		= {
7096d6ce40fSTony Lindgren 		.name	= "cpcap-usb-phy",
7106d6ce40fSTony Lindgren 		.of_match_table = of_match_ptr(cpcap_usb_phy_id_table),
7116d6ce40fSTony Lindgren 	},
7126d6ce40fSTony Lindgren };
7136d6ce40fSTony Lindgren 
7146d6ce40fSTony Lindgren module_platform_driver(cpcap_usb_phy_driver);
7156d6ce40fSTony Lindgren 
7166d6ce40fSTony Lindgren MODULE_ALIAS("platform:cpcap_usb");
7176d6ce40fSTony Lindgren MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
7186d6ce40fSTony Lindgren MODULE_DESCRIPTION("CPCAP usb phy driver");
7196d6ce40fSTony Lindgren MODULE_LICENSE("GPL v2");
720