18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Motorola CPCAP PMIC USB PHY driver 38c2ecf20Sopenharmony_ci * Copyright (C) 2017 Tony Lindgren <tony@atomide.com> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Some parts based on earlier Motorola Linux kernel tree code in 68c2ecf20Sopenharmony_ci * board-mapphone-usb.c and cpcap-usb-det.c: 78c2ecf20Sopenharmony_ci * Copyright (C) 2007 - 2011 Motorola, Inc. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 108c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as 118c2ecf20Sopenharmony_ci * published by the Free Software Foundation version 2. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 148c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 158c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 168c2ecf20Sopenharmony_ci * GNU General Public License for more details. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/atomic.h> 208c2ecf20Sopenharmony_ci#include <linux/clk.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/err.h> 238c2ecf20Sopenharmony_ci#include <linux/io.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <linux/of.h> 268c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 278c2ecf20Sopenharmony_ci#include <linux/iio/consumer.h> 288c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 298c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 308c2ecf20Sopenharmony_ci#include <linux/regmap.h> 318c2ecf20Sopenharmony_ci#include <linux/slab.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 348c2ecf20Sopenharmony_ci#include <linux/mfd/motorola-cpcap.h> 358c2ecf20Sopenharmony_ci#include <linux/phy/omap_usb.h> 368c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 378c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 388c2ecf20Sopenharmony_ci#include <linux/usb/musb.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* CPCAP_REG_USBC1 register bits */ 418c2ecf20Sopenharmony_ci#define CPCAP_BIT_IDPULSE BIT(15) 428c2ecf20Sopenharmony_ci#define CPCAP_BIT_ID100KPU BIT(14) 438c2ecf20Sopenharmony_ci#define CPCAP_BIT_IDPUCNTRL BIT(13) 448c2ecf20Sopenharmony_ci#define CPCAP_BIT_IDPU BIT(12) 458c2ecf20Sopenharmony_ci#define CPCAP_BIT_IDPD BIT(11) 468c2ecf20Sopenharmony_ci#define CPCAP_BIT_VBUSCHRGTMR3 BIT(10) 478c2ecf20Sopenharmony_ci#define CPCAP_BIT_VBUSCHRGTMR2 BIT(9) 488c2ecf20Sopenharmony_ci#define CPCAP_BIT_VBUSCHRGTMR1 BIT(8) 498c2ecf20Sopenharmony_ci#define CPCAP_BIT_VBUSCHRGTMR0 BIT(7) 508c2ecf20Sopenharmony_ci#define CPCAP_BIT_VBUSPU BIT(6) 518c2ecf20Sopenharmony_ci#define CPCAP_BIT_VBUSPD BIT(5) 528c2ecf20Sopenharmony_ci#define CPCAP_BIT_DMPD BIT(4) 538c2ecf20Sopenharmony_ci#define CPCAP_BIT_DPPD BIT(3) 548c2ecf20Sopenharmony_ci#define CPCAP_BIT_DM1K5PU BIT(2) 558c2ecf20Sopenharmony_ci#define CPCAP_BIT_DP1K5PU BIT(1) 568c2ecf20Sopenharmony_ci#define CPCAP_BIT_DP150KPU BIT(0) 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* CPCAP_REG_USBC2 register bits */ 598c2ecf20Sopenharmony_ci#define CPCAP_BIT_ZHSDRV1 BIT(15) 608c2ecf20Sopenharmony_ci#define CPCAP_BIT_ZHSDRV0 BIT(14) 618c2ecf20Sopenharmony_ci#define CPCAP_BIT_DPLLCLKREQ BIT(13) 628c2ecf20Sopenharmony_ci#define CPCAP_BIT_SE0CONN BIT(12) 638c2ecf20Sopenharmony_ci#define CPCAP_BIT_UARTTXTRI BIT(11) 648c2ecf20Sopenharmony_ci#define CPCAP_BIT_UARTSWAP BIT(10) 658c2ecf20Sopenharmony_ci#define CPCAP_BIT_UARTMUX1 BIT(9) 668c2ecf20Sopenharmony_ci#define CPCAP_BIT_UARTMUX0 BIT(8) 678c2ecf20Sopenharmony_ci#define CPCAP_BIT_ULPISTPLOW BIT(7) 688c2ecf20Sopenharmony_ci#define CPCAP_BIT_TXENPOL BIT(6) 698c2ecf20Sopenharmony_ci#define CPCAP_BIT_USBXCVREN BIT(5) 708c2ecf20Sopenharmony_ci#define CPCAP_BIT_USBCNTRL BIT(4) 718c2ecf20Sopenharmony_ci#define CPCAP_BIT_USBSUSPEND BIT(3) 728c2ecf20Sopenharmony_ci#define CPCAP_BIT_EMUMODE2 BIT(2) 738c2ecf20Sopenharmony_ci#define CPCAP_BIT_EMUMODE1 BIT(1) 748c2ecf20Sopenharmony_ci#define CPCAP_BIT_EMUMODE0 BIT(0) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci/* CPCAP_REG_USBC3 register bits */ 778c2ecf20Sopenharmony_ci#define CPCAP_BIT_SPARE_898_15 BIT(15) 788c2ecf20Sopenharmony_ci#define CPCAP_BIT_IHSTX03 BIT(14) 798c2ecf20Sopenharmony_ci#define CPCAP_BIT_IHSTX02 BIT(13) 808c2ecf20Sopenharmony_ci#define CPCAP_BIT_IHSTX01 BIT(12) 818c2ecf20Sopenharmony_ci#define CPCAP_BIT_IHSTX0 BIT(11) 828c2ecf20Sopenharmony_ci#define CPCAP_BIT_IDPU_SPI BIT(10) 838c2ecf20Sopenharmony_ci#define CPCAP_BIT_UNUSED_898_9 BIT(9) 848c2ecf20Sopenharmony_ci#define CPCAP_BIT_VBUSSTBY_EN BIT(8) 858c2ecf20Sopenharmony_ci#define CPCAP_BIT_VBUSEN_SPI BIT(7) 868c2ecf20Sopenharmony_ci#define CPCAP_BIT_VBUSPU_SPI BIT(6) 878c2ecf20Sopenharmony_ci#define CPCAP_BIT_VBUSPD_SPI BIT(5) 888c2ecf20Sopenharmony_ci#define CPCAP_BIT_DMPD_SPI BIT(4) 898c2ecf20Sopenharmony_ci#define CPCAP_BIT_DPPD_SPI BIT(3) 908c2ecf20Sopenharmony_ci#define CPCAP_BIT_SUSPEND_SPI BIT(2) 918c2ecf20Sopenharmony_ci#define CPCAP_BIT_PU_SPI BIT(1) 928c2ecf20Sopenharmony_ci#define CPCAP_BIT_ULPI_SPI_SEL BIT(0) 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistruct cpcap_usb_ints_state { 958c2ecf20Sopenharmony_ci bool id_ground; 968c2ecf20Sopenharmony_ci bool id_float; 978c2ecf20Sopenharmony_ci bool chrg_det; 988c2ecf20Sopenharmony_ci bool rvrs_chrg; 998c2ecf20Sopenharmony_ci bool vbusov; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci bool chrg_se1b; 1028c2ecf20Sopenharmony_ci bool se0conn; 1038c2ecf20Sopenharmony_ci bool rvrs_mode; 1048c2ecf20Sopenharmony_ci bool chrgcurr1; 1058c2ecf20Sopenharmony_ci bool vbusvld; 1068c2ecf20Sopenharmony_ci bool sessvld; 1078c2ecf20Sopenharmony_ci bool sessend; 1088c2ecf20Sopenharmony_ci bool se1; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci bool battdetb; 1118c2ecf20Sopenharmony_ci bool dm; 1128c2ecf20Sopenharmony_ci bool dp; 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cienum cpcap_gpio_mode { 1168c2ecf20Sopenharmony_ci CPCAP_DM_DP, 1178c2ecf20Sopenharmony_ci CPCAP_MDM_RX_TX, 1188c2ecf20Sopenharmony_ci CPCAP_UNKNOWN_DISABLED, /* Seems to disable USB lines */ 1198c2ecf20Sopenharmony_ci CPCAP_OTG_DM_DP, 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistruct cpcap_phy_ddata { 1238c2ecf20Sopenharmony_ci struct regmap *reg; 1248c2ecf20Sopenharmony_ci struct device *dev; 1258c2ecf20Sopenharmony_ci struct usb_phy phy; 1268c2ecf20Sopenharmony_ci struct delayed_work detect_work; 1278c2ecf20Sopenharmony_ci struct pinctrl *pins; 1288c2ecf20Sopenharmony_ci struct pinctrl_state *pins_ulpi; 1298c2ecf20Sopenharmony_ci struct pinctrl_state *pins_utmi; 1308c2ecf20Sopenharmony_ci struct pinctrl_state *pins_uart; 1318c2ecf20Sopenharmony_ci struct gpio_desc *gpio[2]; 1328c2ecf20Sopenharmony_ci struct iio_channel *vbus; 1338c2ecf20Sopenharmony_ci struct iio_channel *id; 1348c2ecf20Sopenharmony_ci struct regulator *vusb; 1358c2ecf20Sopenharmony_ci atomic_t active; 1368c2ecf20Sopenharmony_ci unsigned int vbus_provider:1; 1378c2ecf20Sopenharmony_ci unsigned int docked:1; 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic bool cpcap_usb_vbus_valid(struct cpcap_phy_ddata *ddata) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int error, value = 0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci error = iio_read_channel_processed(ddata->vbus, &value); 1458c2ecf20Sopenharmony_ci if (error >= 0) 1468c2ecf20Sopenharmony_ci return value > 3900 ? true : false; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci dev_err(ddata->dev, "error reading VBUS: %i\n", error); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return false; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int cpcap_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci otg->host = host; 1568c2ecf20Sopenharmony_ci if (!host) 1578c2ecf20Sopenharmony_ci otg->state = OTG_STATE_UNDEFINED; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int cpcap_usb_phy_set_peripheral(struct usb_otg *otg, 1638c2ecf20Sopenharmony_ci struct usb_gadget *gadget) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci otg->gadget = gadget; 1668c2ecf20Sopenharmony_ci if (!gadget) 1678c2ecf20Sopenharmony_ci otg->state = OTG_STATE_UNDEFINED; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic const struct phy_ops ops = { 1738c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int cpcap_phy_get_ints_state(struct cpcap_phy_ddata *ddata, 1778c2ecf20Sopenharmony_ci struct cpcap_usb_ints_state *s) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci int val, error; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci error = regmap_read(ddata->reg, CPCAP_REG_INTS1, &val); 1828c2ecf20Sopenharmony_ci if (error) 1838c2ecf20Sopenharmony_ci return error; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci s->id_ground = val & BIT(15); 1868c2ecf20Sopenharmony_ci s->id_float = val & BIT(14); 1878c2ecf20Sopenharmony_ci s->vbusov = val & BIT(11); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci error = regmap_read(ddata->reg, CPCAP_REG_INTS2, &val); 1908c2ecf20Sopenharmony_ci if (error) 1918c2ecf20Sopenharmony_ci return error; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci s->vbusvld = val & BIT(3); 1948c2ecf20Sopenharmony_ci s->sessvld = val & BIT(2); 1958c2ecf20Sopenharmony_ci s->sessend = val & BIT(1); 1968c2ecf20Sopenharmony_ci s->se1 = val & BIT(0); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci error = regmap_read(ddata->reg, CPCAP_REG_INTS4, &val); 1998c2ecf20Sopenharmony_ci if (error) 2008c2ecf20Sopenharmony_ci return error; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci s->dm = val & BIT(1); 2038c2ecf20Sopenharmony_ci s->dp = val & BIT(0); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int cpcap_usb_set_uart_mode(struct cpcap_phy_ddata *ddata); 2098c2ecf20Sopenharmony_cistatic int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void cpcap_usb_try_musb_mailbox(struct cpcap_phy_ddata *ddata, 2128c2ecf20Sopenharmony_ci enum musb_vbus_id_status status) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci int error; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci error = musb_mailbox(status); 2178c2ecf20Sopenharmony_ci if (!error) 2188c2ecf20Sopenharmony_ci return; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci dev_dbg(ddata->dev, "%s: musb_mailbox failed: %i\n", 2218c2ecf20Sopenharmony_ci __func__, error); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void cpcap_usb_detect(struct work_struct *work) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct cpcap_phy_ddata *ddata; 2278c2ecf20Sopenharmony_ci struct cpcap_usb_ints_state s; 2288c2ecf20Sopenharmony_ci bool vbus = false; 2298c2ecf20Sopenharmony_ci int error; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ddata = container_of(work, struct cpcap_phy_ddata, detect_work.work); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci error = cpcap_phy_get_ints_state(ddata, &s); 2348c2ecf20Sopenharmony_ci if (error) 2358c2ecf20Sopenharmony_ci return; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci vbus = cpcap_usb_vbus_valid(ddata); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* We need to kick the VBUS as USB A-host */ 2408c2ecf20Sopenharmony_ci if (s.id_ground && ddata->vbus_provider) { 2418c2ecf20Sopenharmony_ci dev_dbg(ddata->dev, "still in USB A-host mode, kicking VBUS\n"); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 2468c2ecf20Sopenharmony_ci CPCAP_BIT_VBUSSTBY_EN | 2478c2ecf20Sopenharmony_ci CPCAP_BIT_VBUSEN_SPI, 2488c2ecf20Sopenharmony_ci CPCAP_BIT_VBUSEN_SPI); 2498c2ecf20Sopenharmony_ci if (error) 2508c2ecf20Sopenharmony_ci goto out_err; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (vbus && s.id_ground && ddata->docked) { 2568c2ecf20Sopenharmony_ci dev_dbg(ddata->dev, "still docked as A-host, signal ID down\n"); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci return; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* No VBUS needed with docks */ 2648c2ecf20Sopenharmony_ci if (vbus && s.id_ground && !ddata->vbus_provider) { 2658c2ecf20Sopenharmony_ci dev_dbg(ddata->dev, "connected to a dock\n"); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ddata->docked = true; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci error = cpcap_usb_set_usb_mode(ddata); 2708c2ecf20Sopenharmony_ci if (error) 2718c2ecf20Sopenharmony_ci goto out_err; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* 2768c2ecf20Sopenharmony_ci * Force check state again after musb has reoriented, 2778c2ecf20Sopenharmony_ci * otherwise devices won't enumerate after loading PHY 2788c2ecf20Sopenharmony_ci * driver. 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci schedule_delayed_work(&ddata->detect_work, 2818c2ecf20Sopenharmony_ci msecs_to_jiffies(1000)); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (s.id_ground && !ddata->docked) { 2878c2ecf20Sopenharmony_ci dev_dbg(ddata->dev, "id ground, USB host mode\n"); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ddata->vbus_provider = true; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci error = cpcap_usb_set_usb_mode(ddata); 2928c2ecf20Sopenharmony_ci if (error) 2938c2ecf20Sopenharmony_ci goto out_err; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 2988c2ecf20Sopenharmony_ci CPCAP_BIT_VBUSSTBY_EN | 2998c2ecf20Sopenharmony_ci CPCAP_BIT_VBUSEN_SPI, 3008c2ecf20Sopenharmony_ci CPCAP_BIT_VBUSEN_SPI); 3018c2ecf20Sopenharmony_ci if (error) 3028c2ecf20Sopenharmony_ci goto out_err; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci return; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 3088c2ecf20Sopenharmony_ci CPCAP_BIT_VBUSSTBY_EN | 3098c2ecf20Sopenharmony_ci CPCAP_BIT_VBUSEN_SPI, 0); 3108c2ecf20Sopenharmony_ci if (error) 3118c2ecf20Sopenharmony_ci goto out_err; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci vbus = cpcap_usb_vbus_valid(ddata); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* Otherwise assume we're connected to a USB host */ 3168c2ecf20Sopenharmony_ci if (vbus) { 3178c2ecf20Sopenharmony_ci dev_dbg(ddata->dev, "connected to USB host\n"); 3188c2ecf20Sopenharmony_ci error = cpcap_usb_set_usb_mode(ddata); 3198c2ecf20Sopenharmony_ci if (error) 3208c2ecf20Sopenharmony_ci goto out_err; 3218c2ecf20Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_VALID); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci ddata->vbus_provider = false; 3278c2ecf20Sopenharmony_ci ddata->docked = false; 3288c2ecf20Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Default to debug UART mode */ 3318c2ecf20Sopenharmony_ci error = cpcap_usb_set_uart_mode(ddata); 3328c2ecf20Sopenharmony_ci if (error) 3338c2ecf20Sopenharmony_ci goto out_err; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci dev_dbg(ddata->dev, "set UART mode\n"); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ciout_err: 3408c2ecf20Sopenharmony_ci dev_err(ddata->dev, "error setting cable state: %i\n", error); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic irqreturn_t cpcap_phy_irq_thread(int irq, void *data) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct cpcap_phy_ddata *ddata = data; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (!atomic_read(&ddata->active)) 3488c2ecf20Sopenharmony_ci return IRQ_NONE; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci schedule_delayed_work(&ddata->detect_work, msecs_to_jiffies(1)); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int cpcap_usb_init_irq(struct platform_device *pdev, 3568c2ecf20Sopenharmony_ci struct cpcap_phy_ddata *ddata, 3578c2ecf20Sopenharmony_ci const char *name) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci int irq, error; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci irq = platform_get_irq_byname(pdev, name); 3628c2ecf20Sopenharmony_ci if (irq < 0) 3638c2ecf20Sopenharmony_ci return -ENODEV; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci error = devm_request_threaded_irq(ddata->dev, irq, NULL, 3668c2ecf20Sopenharmony_ci cpcap_phy_irq_thread, 3678c2ecf20Sopenharmony_ci IRQF_SHARED | 3688c2ecf20Sopenharmony_ci IRQF_ONESHOT, 3698c2ecf20Sopenharmony_ci name, ddata); 3708c2ecf20Sopenharmony_ci if (error) { 3718c2ecf20Sopenharmony_ci dev_err(ddata->dev, "could not get irq %s: %i\n", 3728c2ecf20Sopenharmony_ci name, error); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return error; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic const char * const cpcap_phy_irqs[] = { 3818c2ecf20Sopenharmony_ci /* REG_INT_0 */ 3828c2ecf20Sopenharmony_ci "id_ground", "id_float", 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* REG_INT1 */ 3858c2ecf20Sopenharmony_ci "se0conn", "vbusvld", "sessvld", "sessend", "se1", 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* REG_INT_3 */ 3888c2ecf20Sopenharmony_ci "dm", "dp", 3898c2ecf20Sopenharmony_ci}; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int cpcap_usb_init_interrupts(struct platform_device *pdev, 3928c2ecf20Sopenharmony_ci struct cpcap_phy_ddata *ddata) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci int i, error; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cpcap_phy_irqs); i++) { 3978c2ecf20Sopenharmony_ci error = cpcap_usb_init_irq(pdev, ddata, cpcap_phy_irqs[i]); 3988c2ecf20Sopenharmony_ci if (error) 3998c2ecf20Sopenharmony_ci return error; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci/* 4068c2ecf20Sopenharmony_ci * Optional pins and modes. At least Motorola mapphone devices 4078c2ecf20Sopenharmony_ci * are using two GPIOs and dynamic pinctrl to multiplex PHY pins 4088c2ecf20Sopenharmony_ci * to UART, ULPI or UTMI mode. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int cpcap_usb_gpio_set_mode(struct cpcap_phy_ddata *ddata, 4128c2ecf20Sopenharmony_ci enum cpcap_gpio_mode mode) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci if (!ddata->gpio[0] || !ddata->gpio[1]) 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci gpiod_set_value(ddata->gpio[0], mode & 1); 4188c2ecf20Sopenharmony_ci gpiod_set_value(ddata->gpio[1], mode >> 1); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic int cpcap_usb_set_uart_mode(struct cpcap_phy_ddata *ddata) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci int error; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* Disable lines to prevent glitches from waking up mdm6600 */ 4288c2ecf20Sopenharmony_ci error = cpcap_usb_gpio_set_mode(ddata, CPCAP_UNKNOWN_DISABLED); 4298c2ecf20Sopenharmony_ci if (error) 4308c2ecf20Sopenharmony_ci goto out_err; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (ddata->pins_uart) { 4338c2ecf20Sopenharmony_ci error = pinctrl_select_state(ddata->pins, ddata->pins_uart); 4348c2ecf20Sopenharmony_ci if (error) 4358c2ecf20Sopenharmony_ci goto out_err; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC1, 4398c2ecf20Sopenharmony_ci CPCAP_BIT_VBUSPD, 4408c2ecf20Sopenharmony_ci CPCAP_BIT_VBUSPD); 4418c2ecf20Sopenharmony_ci if (error) 4428c2ecf20Sopenharmony_ci goto out_err; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2, 4458c2ecf20Sopenharmony_ci 0xffff, CPCAP_BIT_UARTMUX0 | 4468c2ecf20Sopenharmony_ci CPCAP_BIT_EMUMODE0); 4478c2ecf20Sopenharmony_ci if (error) 4488c2ecf20Sopenharmony_ci goto out_err; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 0x7fff, 4518c2ecf20Sopenharmony_ci CPCAP_BIT_IDPU_SPI); 4528c2ecf20Sopenharmony_ci if (error) 4538c2ecf20Sopenharmony_ci goto out_err; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* Enable UART mode */ 4568c2ecf20Sopenharmony_ci error = cpcap_usb_gpio_set_mode(ddata, CPCAP_DM_DP); 4578c2ecf20Sopenharmony_ci if (error) 4588c2ecf20Sopenharmony_ci goto out_err; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci return 0; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciout_err: 4638c2ecf20Sopenharmony_ci dev_err(ddata->dev, "%s failed with %i\n", __func__, error); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return error; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci int error; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* Disable lines to prevent glitches from waking up mdm6600 */ 4738c2ecf20Sopenharmony_ci error = cpcap_usb_gpio_set_mode(ddata, CPCAP_UNKNOWN_DISABLED); 4748c2ecf20Sopenharmony_ci if (error) 4758c2ecf20Sopenharmony_ci return error; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (ddata->pins_utmi) { 4788c2ecf20Sopenharmony_ci error = pinctrl_select_state(ddata->pins, ddata->pins_utmi); 4798c2ecf20Sopenharmony_ci if (error) { 4808c2ecf20Sopenharmony_ci dev_err(ddata->dev, "could not set usb mode: %i\n", 4818c2ecf20Sopenharmony_ci error); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci return error; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC1, 4888c2ecf20Sopenharmony_ci CPCAP_BIT_VBUSPD, 0); 4898c2ecf20Sopenharmony_ci if (error) 4908c2ecf20Sopenharmony_ci goto out_err; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 4938c2ecf20Sopenharmony_ci CPCAP_BIT_PU_SPI | 4948c2ecf20Sopenharmony_ci CPCAP_BIT_DMPD_SPI | 4958c2ecf20Sopenharmony_ci CPCAP_BIT_DPPD_SPI | 4968c2ecf20Sopenharmony_ci CPCAP_BIT_SUSPEND_SPI | 4978c2ecf20Sopenharmony_ci CPCAP_BIT_ULPI_SPI_SEL, 0); 4988c2ecf20Sopenharmony_ci if (error) 4998c2ecf20Sopenharmony_ci goto out_err; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2, 5028c2ecf20Sopenharmony_ci CPCAP_BIT_USBXCVREN, 5038c2ecf20Sopenharmony_ci CPCAP_BIT_USBXCVREN); 5048c2ecf20Sopenharmony_ci if (error) 5058c2ecf20Sopenharmony_ci goto out_err; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* Enable USB mode */ 5088c2ecf20Sopenharmony_ci error = cpcap_usb_gpio_set_mode(ddata, CPCAP_OTG_DM_DP); 5098c2ecf20Sopenharmony_ci if (error) 5108c2ecf20Sopenharmony_ci goto out_err; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return 0; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ciout_err: 5158c2ecf20Sopenharmony_ci dev_err(ddata->dev, "%s failed with %i\n", __func__, error); 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci return error; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic int cpcap_usb_init_optional_pins(struct cpcap_phy_ddata *ddata) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci ddata->pins = devm_pinctrl_get(ddata->dev); 5238c2ecf20Sopenharmony_ci if (IS_ERR(ddata->pins)) { 5248c2ecf20Sopenharmony_ci dev_info(ddata->dev, "default pins not configured: %ld\n", 5258c2ecf20Sopenharmony_ci PTR_ERR(ddata->pins)); 5268c2ecf20Sopenharmony_ci ddata->pins = NULL; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci return 0; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci ddata->pins_ulpi = pinctrl_lookup_state(ddata->pins, "ulpi"); 5328c2ecf20Sopenharmony_ci if (IS_ERR(ddata->pins_ulpi)) { 5338c2ecf20Sopenharmony_ci dev_info(ddata->dev, "ulpi pins not configured\n"); 5348c2ecf20Sopenharmony_ci ddata->pins_ulpi = NULL; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci ddata->pins_utmi = pinctrl_lookup_state(ddata->pins, "utmi"); 5388c2ecf20Sopenharmony_ci if (IS_ERR(ddata->pins_utmi)) { 5398c2ecf20Sopenharmony_ci dev_info(ddata->dev, "utmi pins not configured\n"); 5408c2ecf20Sopenharmony_ci ddata->pins_utmi = NULL; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci ddata->pins_uart = pinctrl_lookup_state(ddata->pins, "uart"); 5448c2ecf20Sopenharmony_ci if (IS_ERR(ddata->pins_uart)) { 5458c2ecf20Sopenharmony_ci dev_info(ddata->dev, "uart pins not configured\n"); 5468c2ecf20Sopenharmony_ci ddata->pins_uart = NULL; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (ddata->pins_uart) 5508c2ecf20Sopenharmony_ci return pinctrl_select_state(ddata->pins, ddata->pins_uart); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return 0; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic void cpcap_usb_init_optional_gpios(struct cpcap_phy_ddata *ddata) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci int i; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 5608c2ecf20Sopenharmony_ci ddata->gpio[i] = devm_gpiod_get_index(ddata->dev, "mode", 5618c2ecf20Sopenharmony_ci i, GPIOD_OUT_HIGH); 5628c2ecf20Sopenharmony_ci if (IS_ERR(ddata->gpio[i])) { 5638c2ecf20Sopenharmony_ci dev_info(ddata->dev, "no mode change GPIO%i: %li\n", 5648c2ecf20Sopenharmony_ci i, PTR_ERR(ddata->gpio[i])); 5658c2ecf20Sopenharmony_ci ddata->gpio[i] = NULL; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic int cpcap_usb_init_iio(struct cpcap_phy_ddata *ddata) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci enum iio_chan_type type; 5738c2ecf20Sopenharmony_ci int error; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci ddata->vbus = devm_iio_channel_get(ddata->dev, "vbus"); 5768c2ecf20Sopenharmony_ci if (IS_ERR(ddata->vbus)) { 5778c2ecf20Sopenharmony_ci error = PTR_ERR(ddata->vbus); 5788c2ecf20Sopenharmony_ci goto out_err; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (!ddata->vbus->indio_dev) { 5828c2ecf20Sopenharmony_ci error = -ENXIO; 5838c2ecf20Sopenharmony_ci goto out_err; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci error = iio_get_channel_type(ddata->vbus, &type); 5878c2ecf20Sopenharmony_ci if (error < 0) 5888c2ecf20Sopenharmony_ci goto out_err; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (type != IIO_VOLTAGE) { 5918c2ecf20Sopenharmony_ci error = -EINVAL; 5928c2ecf20Sopenharmony_ci goto out_err; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci return 0; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ciout_err: 5988c2ecf20Sopenharmony_ci dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n", 5998c2ecf20Sopenharmony_ci error); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci return error; 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 6058c2ecf20Sopenharmony_cistatic const struct of_device_id cpcap_usb_phy_id_table[] = { 6068c2ecf20Sopenharmony_ci { 6078c2ecf20Sopenharmony_ci .compatible = "motorola,cpcap-usb-phy", 6088c2ecf20Sopenharmony_ci }, 6098c2ecf20Sopenharmony_ci { 6108c2ecf20Sopenharmony_ci .compatible = "motorola,mapphone-cpcap-usb-phy", 6118c2ecf20Sopenharmony_ci }, 6128c2ecf20Sopenharmony_ci {}, 6138c2ecf20Sopenharmony_ci}; 6148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, cpcap_usb_phy_id_table); 6158c2ecf20Sopenharmony_ci#endif 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int cpcap_usb_phy_probe(struct platform_device *pdev) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct cpcap_phy_ddata *ddata; 6208c2ecf20Sopenharmony_ci struct phy *generic_phy; 6218c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 6228c2ecf20Sopenharmony_ci struct usb_otg *otg; 6238c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 6248c2ecf20Sopenharmony_ci int error; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci of_id = of_match_device(of_match_ptr(cpcap_usb_phy_id_table), 6278c2ecf20Sopenharmony_ci &pdev->dev); 6288c2ecf20Sopenharmony_ci if (!of_id) 6298c2ecf20Sopenharmony_ci return -EINVAL; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 6328c2ecf20Sopenharmony_ci if (!ddata) 6338c2ecf20Sopenharmony_ci return -ENOMEM; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci ddata->reg = dev_get_regmap(pdev->dev.parent, NULL); 6368c2ecf20Sopenharmony_ci if (!ddata->reg) 6378c2ecf20Sopenharmony_ci return -ENODEV; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); 6408c2ecf20Sopenharmony_ci if (!otg) 6418c2ecf20Sopenharmony_ci return -ENOMEM; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci ddata->dev = &pdev->dev; 6448c2ecf20Sopenharmony_ci ddata->phy.dev = ddata->dev; 6458c2ecf20Sopenharmony_ci ddata->phy.label = "cpcap_usb_phy"; 6468c2ecf20Sopenharmony_ci ddata->phy.otg = otg; 6478c2ecf20Sopenharmony_ci ddata->phy.type = USB_PHY_TYPE_USB2; 6488c2ecf20Sopenharmony_ci otg->set_host = cpcap_usb_phy_set_host; 6498c2ecf20Sopenharmony_ci otg->set_peripheral = cpcap_usb_phy_set_peripheral; 6508c2ecf20Sopenharmony_ci otg->usb_phy = &ddata->phy; 6518c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&ddata->detect_work, cpcap_usb_detect); 6528c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, ddata); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci ddata->vusb = devm_regulator_get(&pdev->dev, "vusb"); 6558c2ecf20Sopenharmony_ci if (IS_ERR(ddata->vusb)) 6568c2ecf20Sopenharmony_ci return PTR_ERR(ddata->vusb); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci error = regulator_enable(ddata->vusb); 6598c2ecf20Sopenharmony_ci if (error) 6608c2ecf20Sopenharmony_ci return error; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci generic_phy = devm_phy_create(ddata->dev, NULL, &ops); 6638c2ecf20Sopenharmony_ci if (IS_ERR(generic_phy)) { 6648c2ecf20Sopenharmony_ci error = PTR_ERR(generic_phy); 6658c2ecf20Sopenharmony_ci goto out_reg_disable; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci phy_set_drvdata(generic_phy, ddata); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(ddata->dev, 6718c2ecf20Sopenharmony_ci of_phy_simple_xlate); 6728c2ecf20Sopenharmony_ci if (IS_ERR(phy_provider)) { 6738c2ecf20Sopenharmony_ci error = PTR_ERR(phy_provider); 6748c2ecf20Sopenharmony_ci goto out_reg_disable; 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci error = cpcap_usb_init_optional_pins(ddata); 6788c2ecf20Sopenharmony_ci if (error) 6798c2ecf20Sopenharmony_ci goto out_reg_disable; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci cpcap_usb_init_optional_gpios(ddata); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci error = cpcap_usb_init_iio(ddata); 6848c2ecf20Sopenharmony_ci if (error) 6858c2ecf20Sopenharmony_ci goto out_reg_disable; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci error = cpcap_usb_init_interrupts(pdev, ddata); 6888c2ecf20Sopenharmony_ci if (error) 6898c2ecf20Sopenharmony_ci goto out_reg_disable; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci usb_add_phy_dev(&ddata->phy); 6928c2ecf20Sopenharmony_ci atomic_set(&ddata->active, 1); 6938c2ecf20Sopenharmony_ci schedule_delayed_work(&ddata->detect_work, msecs_to_jiffies(1)); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci return 0; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ciout_reg_disable: 6988c2ecf20Sopenharmony_ci regulator_disable(ddata->vusb); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci return error; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic int cpcap_usb_phy_remove(struct platform_device *pdev) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci struct cpcap_phy_ddata *ddata = platform_get_drvdata(pdev); 7068c2ecf20Sopenharmony_ci int error; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci atomic_set(&ddata->active, 0); 7098c2ecf20Sopenharmony_ci error = cpcap_usb_set_uart_mode(ddata); 7108c2ecf20Sopenharmony_ci if (error) 7118c2ecf20Sopenharmony_ci dev_err(ddata->dev, "could not set UART mode\n"); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci usb_remove_phy(&ddata->phy); 7168c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&ddata->detect_work); 7178c2ecf20Sopenharmony_ci regulator_disable(ddata->vusb); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci return 0; 7208c2ecf20Sopenharmony_ci} 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic struct platform_driver cpcap_usb_phy_driver = { 7238c2ecf20Sopenharmony_ci .probe = cpcap_usb_phy_probe, 7248c2ecf20Sopenharmony_ci .remove = cpcap_usb_phy_remove, 7258c2ecf20Sopenharmony_ci .driver = { 7268c2ecf20Sopenharmony_ci .name = "cpcap-usb-phy", 7278c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(cpcap_usb_phy_id_table), 7288c2ecf20Sopenharmony_ci }, 7298c2ecf20Sopenharmony_ci}; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_cimodule_platform_driver(cpcap_usb_phy_driver); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:cpcap_usb"); 7348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); 7358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("CPCAP usb phy driver"); 7368c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 737