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