18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004-2007 Texas Instruments 68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 78c2ecf20Sopenharmony_ci * Contact: Felipe Balbi <felipe.balbi@nokia.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Current status: 108c2ecf20Sopenharmony_ci * - HS USB ULPI mode works. 118c2ecf20Sopenharmony_ci * - 3-pin mode support may be added in future. 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 198c2ecf20Sopenharmony_ci#include <linux/io.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/usb/otg.h> 228c2ecf20Sopenharmony_ci#include <linux/phy/phy.h> 238c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 248c2ecf20Sopenharmony_ci#include <linux/usb/musb.h> 258c2ecf20Sopenharmony_ci#include <linux/usb/ulpi.h> 268c2ecf20Sopenharmony_ci#include <linux/mfd/twl.h> 278c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 288c2ecf20Sopenharmony_ci#include <linux/err.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Register defines */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define MCPC_CTRL 0x30 348c2ecf20Sopenharmony_ci#define MCPC_CTRL_RTSOL (1 << 7) 358c2ecf20Sopenharmony_ci#define MCPC_CTRL_EXTSWR (1 << 6) 368c2ecf20Sopenharmony_ci#define MCPC_CTRL_EXTSWC (1 << 5) 378c2ecf20Sopenharmony_ci#define MCPC_CTRL_VOICESW (1 << 4) 388c2ecf20Sopenharmony_ci#define MCPC_CTRL_OUT64K (1 << 3) 398c2ecf20Sopenharmony_ci#define MCPC_CTRL_RTSCTSSW (1 << 2) 408c2ecf20Sopenharmony_ci#define MCPC_CTRL_HS_UART (1 << 0) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define MCPC_IO_CTRL 0x33 438c2ecf20Sopenharmony_ci#define MCPC_IO_CTRL_MICBIASEN (1 << 5) 448c2ecf20Sopenharmony_ci#define MCPC_IO_CTRL_CTS_NPU (1 << 4) 458c2ecf20Sopenharmony_ci#define MCPC_IO_CTRL_RXD_PU (1 << 3) 468c2ecf20Sopenharmony_ci#define MCPC_IO_CTRL_TXDTYP (1 << 2) 478c2ecf20Sopenharmony_ci#define MCPC_IO_CTRL_CTSTYP (1 << 1) 488c2ecf20Sopenharmony_ci#define MCPC_IO_CTRL_RTSTYP (1 << 0) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define MCPC_CTRL2 0x36 518c2ecf20Sopenharmony_ci#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define OTHER_FUNC_CTRL 0x80 548c2ecf20Sopenharmony_ci#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) 558c2ecf20Sopenharmony_ci#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL 0x83 588c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) 598c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) 608c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) 618c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) 628c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) 638c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define OTHER_INT_EN_RISE 0x86 668c2ecf20Sopenharmony_ci#define OTHER_INT_EN_FALL 0x89 678c2ecf20Sopenharmony_ci#define OTHER_INT_STS 0x8C 688c2ecf20Sopenharmony_ci#define OTHER_INT_LATCH 0x8D 698c2ecf20Sopenharmony_ci#define OTHER_INT_VB_SESS_VLD (1 << 7) 708c2ecf20Sopenharmony_ci#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ 718c2ecf20Sopenharmony_ci#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ 728c2ecf20Sopenharmony_ci#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ 738c2ecf20Sopenharmony_ci#define OTHER_INT_MANU (1 << 1) 748c2ecf20Sopenharmony_ci#define OTHER_INT_ABNORMAL_STRESS (1 << 0) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define ID_STATUS 0x96 778c2ecf20Sopenharmony_ci#define ID_RES_FLOAT (1 << 4) 788c2ecf20Sopenharmony_ci#define ID_RES_440K (1 << 3) 798c2ecf20Sopenharmony_ci#define ID_RES_200K (1 << 2) 808c2ecf20Sopenharmony_ci#define ID_RES_102K (1 << 1) 818c2ecf20Sopenharmony_ci#define ID_RES_GND (1 << 0) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define POWER_CTRL 0xAC 848c2ecf20Sopenharmony_ci#define POWER_CTRL_OTG_ENAB (1 << 5) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL2 0xAF 878c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) 888c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) 898c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) 908c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ 918c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) 928c2ecf20Sopenharmony_ci#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define REG_CTRL_EN 0xB2 958c2ecf20Sopenharmony_ci#define REG_CTRL_ERROR 0xB5 968c2ecf20Sopenharmony_ci#define ULPI_I2C_CONFLICT_INTEN (1 << 0) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define OTHER_FUNC_CTRL2 0xB8 998c2ecf20Sopenharmony_ci#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* following registers do not have separate _clr and _set registers */ 1028c2ecf20Sopenharmony_ci#define VBUS_DEBOUNCE 0xC0 1038c2ecf20Sopenharmony_ci#define ID_DEBOUNCE 0xC1 1048c2ecf20Sopenharmony_ci#define VBAT_TIMER 0xD3 1058c2ecf20Sopenharmony_ci#define PHY_PWR_CTRL 0xFD 1068c2ecf20Sopenharmony_ci#define PHY_PWR_PHYPWD (1 << 0) 1078c2ecf20Sopenharmony_ci#define PHY_CLK_CTRL 0xFE 1088c2ecf20Sopenharmony_ci#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) 1098c2ecf20Sopenharmony_ci#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) 1108c2ecf20Sopenharmony_ci#define REQ_PHY_DPLL_CLK (1 << 0) 1118c2ecf20Sopenharmony_ci#define PHY_CLK_CTRL_STS 0xFF 1128c2ecf20Sopenharmony_ci#define PHY_DPLL_CLK (1 << 0) 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* In module TWL_MODULE_PM_MASTER */ 1158c2ecf20Sopenharmony_ci#define STS_HW_CONDITIONS 0x0F 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* In module TWL_MODULE_PM_RECEIVER */ 1188c2ecf20Sopenharmony_ci#define VUSB_DEDICATED1 0x7D 1198c2ecf20Sopenharmony_ci#define VUSB_DEDICATED2 0x7E 1208c2ecf20Sopenharmony_ci#define VUSB1V5_DEV_GRP 0x71 1218c2ecf20Sopenharmony_ci#define VUSB1V5_TYPE 0x72 1228c2ecf20Sopenharmony_ci#define VUSB1V5_REMAP 0x73 1238c2ecf20Sopenharmony_ci#define VUSB1V8_DEV_GRP 0x74 1248c2ecf20Sopenharmony_ci#define VUSB1V8_TYPE 0x75 1258c2ecf20Sopenharmony_ci#define VUSB1V8_REMAP 0x76 1268c2ecf20Sopenharmony_ci#define VUSB3V1_DEV_GRP 0x77 1278c2ecf20Sopenharmony_ci#define VUSB3V1_TYPE 0x78 1288c2ecf20Sopenharmony_ci#define VUSB3V1_REMAP 0x79 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* In module TWL4030_MODULE_INTBR */ 1318c2ecf20Sopenharmony_ci#define PMBR1 0x0D 1328c2ecf20Sopenharmony_ci#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic irqreturn_t twl4030_usb_irq(int irq, void *_twl); 1358c2ecf20Sopenharmony_ci/* 1368c2ecf20Sopenharmony_ci * If VBUS is valid or ID is ground, then we know a 1378c2ecf20Sopenharmony_ci * cable is present and we need to be runtime-enabled 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic inline bool cable_present(enum musb_vbus_id_status stat) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci return stat == MUSB_VBUS_VALID || 1428c2ecf20Sopenharmony_ci stat == MUSB_ID_GROUND; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistruct twl4030_usb { 1468c2ecf20Sopenharmony_ci struct usb_phy phy; 1478c2ecf20Sopenharmony_ci struct device *dev; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* TWL4030 internal USB regulator supplies */ 1508c2ecf20Sopenharmony_ci struct regulator *usb1v5; 1518c2ecf20Sopenharmony_ci struct regulator *usb1v8; 1528c2ecf20Sopenharmony_ci struct regulator *usb3v1; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* for vbus reporting with irqs disabled */ 1558c2ecf20Sopenharmony_ci struct mutex lock; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* pin configuration */ 1588c2ecf20Sopenharmony_ci enum twl4030_usb_mode usb_mode; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci int irq; 1618c2ecf20Sopenharmony_ci enum musb_vbus_id_status linkstat; 1628c2ecf20Sopenharmony_ci atomic_t connected; 1638c2ecf20Sopenharmony_ci bool vbus_supplied; 1648c2ecf20Sopenharmony_ci bool musb_mailbox_pending; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci struct delayed_work id_workaround_work; 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* internal define on top of container_of */ 1708c2ecf20Sopenharmony_ci#define phy_to_twl(x) container_of((x), struct twl4030_usb, phy) 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl, 1758c2ecf20Sopenharmony_ci u8 module, u8 data, u8 address) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci u8 check = 0xFF; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if ((twl_i2c_write_u8(module, data, address) >= 0) && 1808c2ecf20Sopenharmony_ci (twl_i2c_read_u8(module, &check, address) >= 0) && 1818c2ecf20Sopenharmony_ci (check == data)) 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", 1848c2ecf20Sopenharmony_ci 1, module, address, check, data); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Failed once: Try again */ 1878c2ecf20Sopenharmony_ci if ((twl_i2c_write_u8(module, data, address) >= 0) && 1888c2ecf20Sopenharmony_ci (twl_i2c_read_u8(module, &check, address) >= 0) && 1898c2ecf20Sopenharmony_ci (check == data)) 1908c2ecf20Sopenharmony_ci return 0; 1918c2ecf20Sopenharmony_ci dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", 1928c2ecf20Sopenharmony_ci 2, module, address, check, data); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Failed again: Return error */ 1958c2ecf20Sopenharmony_ci return -EBUSY; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci#define twl4030_usb_write_verify(twl, address, data) \ 1998c2ecf20Sopenharmony_ci twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address)) 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic inline int twl4030_usb_write(struct twl4030_usb *twl, 2028c2ecf20Sopenharmony_ci u8 address, u8 data) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci int ret = 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address); 2078c2ecf20Sopenharmony_ci if (ret < 0) 2088c2ecf20Sopenharmony_ci dev_dbg(twl->dev, 2098c2ecf20Sopenharmony_ci "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci u8 data; 2168c2ecf20Sopenharmony_ci int ret = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ret = twl_i2c_read_u8(module, &data, address); 2198c2ecf20Sopenharmony_ci if (ret >= 0) 2208c2ecf20Sopenharmony_ci ret = data; 2218c2ecf20Sopenharmony_ci else 2228c2ecf20Sopenharmony_ci dev_dbg(twl->dev, 2238c2ecf20Sopenharmony_ci "TWL4030:readb[0x%x,0x%x] Error %d\n", 2248c2ecf20Sopenharmony_ci module, address, ret); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return ret; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci return twl4030_readb(twl, TWL_MODULE_USB, address); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic inline int 2378c2ecf20Sopenharmony_citwl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci return twl4030_usb_write(twl, ULPI_SET(reg), bits); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic inline int 2438c2ecf20Sopenharmony_citwl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci return twl4030_usb_write(twl, ULPI_CLR(reg), bits); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------------*/ 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic bool twl4030_is_driving_vbus(struct twl4030_usb *twl) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci int ret; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci ret = twl4030_usb_read(twl, PHY_CLK_CTRL_STS); 2558c2ecf20Sopenharmony_ci if (ret < 0 || !(ret & PHY_DPLL_CLK)) 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * if clocks are off, registers are not updated, 2588c2ecf20Sopenharmony_ci * but we can assume we don't drive VBUS in this case 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci return false; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci ret = twl4030_usb_read(twl, ULPI_OTG_CTRL); 2638c2ecf20Sopenharmony_ci if (ret < 0) 2648c2ecf20Sopenharmony_ci return false; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic enum musb_vbus_id_status 2708c2ecf20Sopenharmony_ci twl4030_usb_linkstat(struct twl4030_usb *twl) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci int status; 2738c2ecf20Sopenharmony_ci enum musb_vbus_id_status linkstat = MUSB_UNKNOWN; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci twl->vbus_supplied = false; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* 2788c2ecf20Sopenharmony_ci * For ID/VBUS sensing, see manual section 15.4.8 ... 2798c2ecf20Sopenharmony_ci * except when using only battery backup power, two 2808c2ecf20Sopenharmony_ci * comparators produce VBUS_PRES and ID_PRES signals, 2818c2ecf20Sopenharmony_ci * which don't match docs elsewhere. But ... BIT(7) 2828c2ecf20Sopenharmony_ci * and BIT(2) of STS_HW_CONDITIONS, respectively, do 2838c2ecf20Sopenharmony_ci * seem to match up. If either is true the USB_PRES 2848c2ecf20Sopenharmony_ci * signal is active, the OTG module is activated, and 2858c2ecf20Sopenharmony_ci * its interrupt may be raised (may wake the system). 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ci status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS); 2888c2ecf20Sopenharmony_ci if (status < 0) 2898c2ecf20Sopenharmony_ci dev_err(twl->dev, "USB link status err %d\n", status); 2908c2ecf20Sopenharmony_ci else if (status & (BIT(7) | BIT(2))) { 2918c2ecf20Sopenharmony_ci if (status & BIT(7)) { 2928c2ecf20Sopenharmony_ci if (twl4030_is_driving_vbus(twl)) 2938c2ecf20Sopenharmony_ci status &= ~BIT(7); 2948c2ecf20Sopenharmony_ci else 2958c2ecf20Sopenharmony_ci twl->vbus_supplied = true; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (status & BIT(2)) 2998c2ecf20Sopenharmony_ci linkstat = MUSB_ID_GROUND; 3008c2ecf20Sopenharmony_ci else if (status & BIT(7)) 3018c2ecf20Sopenharmony_ci linkstat = MUSB_VBUS_VALID; 3028c2ecf20Sopenharmony_ci else 3038c2ecf20Sopenharmony_ci linkstat = MUSB_VBUS_OFF; 3048c2ecf20Sopenharmony_ci } else { 3058c2ecf20Sopenharmony_ci if (twl->linkstat != MUSB_UNKNOWN) 3068c2ecf20Sopenharmony_ci linkstat = MUSB_VBUS_OFF; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci kobject_uevent(&twl->dev->kobj, linkstat == MUSB_VBUS_VALID 3108c2ecf20Sopenharmony_ci ? KOBJ_ONLINE : KOBJ_OFFLINE); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", 3138c2ecf20Sopenharmony_ci status, status, linkstat); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* REVISIT this assumes host and peripheral controllers 3168c2ecf20Sopenharmony_ci * are registered, and that both are active... 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return linkstat; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci twl->usb_mode = mode; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci switch (mode) { 3278c2ecf20Sopenharmony_ci case T2_USB_MODE_ULPI: 3288c2ecf20Sopenharmony_ci twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, 3298c2ecf20Sopenharmony_ci ULPI_IFC_CTRL_CARKITMODE); 3308c2ecf20Sopenharmony_ci twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); 3318c2ecf20Sopenharmony_ci twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, 3328c2ecf20Sopenharmony_ci ULPI_FUNC_CTRL_XCVRSEL_MASK | 3338c2ecf20Sopenharmony_ci ULPI_FUNC_CTRL_OPMODE_MASK); 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci case -1: 3368c2ecf20Sopenharmony_ci /* FIXME: power on defaults */ 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci default: 3398c2ecf20Sopenharmony_ci dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", 3408c2ecf20Sopenharmony_ci mode); 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic void twl4030_i2c_access(struct twl4030_usb *twl, int on) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci unsigned long timeout; 3488c2ecf20Sopenharmony_ci int val = twl4030_usb_read(twl, PHY_CLK_CTRL); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (val >= 0) { 3518c2ecf20Sopenharmony_ci if (on) { 3528c2ecf20Sopenharmony_ci /* enable DPLL to access PHY registers over I2C */ 3538c2ecf20Sopenharmony_ci val |= REQ_PHY_DPLL_CLK; 3548c2ecf20Sopenharmony_ci WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, 3558c2ecf20Sopenharmony_ci (u8)val) < 0); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci timeout = jiffies + HZ; 3588c2ecf20Sopenharmony_ci while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & 3598c2ecf20Sopenharmony_ci PHY_DPLL_CLK) 3608c2ecf20Sopenharmony_ci && time_before(jiffies, timeout)) 3618c2ecf20Sopenharmony_ci udelay(10); 3628c2ecf20Sopenharmony_ci if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & 3638c2ecf20Sopenharmony_ci PHY_DPLL_CLK)) 3648c2ecf20Sopenharmony_ci dev_err(twl->dev, "Timeout setting T2 HSUSB " 3658c2ecf20Sopenharmony_ci "PHY DPLL clock\n"); 3668c2ecf20Sopenharmony_ci } else { 3678c2ecf20Sopenharmony_ci /* let ULPI control the DPLL clock */ 3688c2ecf20Sopenharmony_ci val &= ~REQ_PHY_DPLL_CLK; 3698c2ecf20Sopenharmony_ci WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, 3708c2ecf20Sopenharmony_ci (u8)val) < 0); 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void __twl4030_phy_power(struct twl4030_usb *twl, int on) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (on) 3808c2ecf20Sopenharmony_ci pwr &= ~PHY_PWR_PHYPWD; 3818c2ecf20Sopenharmony_ci else 3828c2ecf20Sopenharmony_ci pwr |= PHY_PWR_PHYPWD; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int __maybe_unused twl4030_usb_suspend(struct device *dev) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct twl4030_usb *twl = dev_get_drvdata(dev); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* 3928c2ecf20Sopenharmony_ci * we need enabled runtime on resume, 3938c2ecf20Sopenharmony_ci * so turn irq off here, so we do not get it early 3948c2ecf20Sopenharmony_ci * note: wakeup on usb plug works independently of this 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 3978c2ecf20Sopenharmony_ci disable_irq(twl->irq); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int __maybe_unused twl4030_usb_resume(struct device *dev) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct twl4030_usb *twl = dev_get_drvdata(dev); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 4078c2ecf20Sopenharmony_ci enable_irq(twl->irq); 4088c2ecf20Sopenharmony_ci /* check whether cable status changed */ 4098c2ecf20Sopenharmony_ci twl4030_usb_irq(0, twl); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct twl4030_usb *twl = dev_get_drvdata(dev); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci __twl4030_phy_power(twl, 0); 4218c2ecf20Sopenharmony_ci regulator_disable(twl->usb1v5); 4228c2ecf20Sopenharmony_ci regulator_disable(twl->usb1v8); 4238c2ecf20Sopenharmony_ci regulator_disable(twl->usb3v1); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int __maybe_unused twl4030_usb_runtime_resume(struct device *dev) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct twl4030_usb *twl = dev_get_drvdata(dev); 4318c2ecf20Sopenharmony_ci int res; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci res = regulator_enable(twl->usb3v1); 4368c2ecf20Sopenharmony_ci if (res) 4378c2ecf20Sopenharmony_ci dev_err(twl->dev, "Failed to enable usb3v1\n"); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci res = regulator_enable(twl->usb1v8); 4408c2ecf20Sopenharmony_ci if (res) 4418c2ecf20Sopenharmony_ci dev_err(twl->dev, "Failed to enable usb1v8\n"); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* 4448c2ecf20Sopenharmony_ci * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP 4458c2ecf20Sopenharmony_ci * in twl4030) resets the VUSB_DEDICATED2 register. This reset 4468c2ecf20Sopenharmony_ci * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to 4478c2ecf20Sopenharmony_ci * SLEEP. We work around this by clearing the bit after usv3v1 4488c2ecf20Sopenharmony_ci * is re-activated. This ensures that VUSB3V1 is really active. 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci res = regulator_enable(twl->usb1v5); 4538c2ecf20Sopenharmony_ci if (res) 4548c2ecf20Sopenharmony_ci dev_err(twl->dev, "Failed to enable usb1v5\n"); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci __twl4030_phy_power(twl, 1); 4578c2ecf20Sopenharmony_ci twl4030_usb_write(twl, PHY_CLK_CTRL, 4588c2ecf20Sopenharmony_ci twl4030_usb_read(twl, PHY_CLK_CTRL) | 4598c2ecf20Sopenharmony_ci (PHY_CLK_CTRL_CLOCKGATING_EN | 4608c2ecf20Sopenharmony_ci PHY_CLK_CTRL_CLK32K_EN)); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci twl4030_i2c_access(twl, 1); 4638c2ecf20Sopenharmony_ci twl4030_usb_set_mode(twl, twl->usb_mode); 4648c2ecf20Sopenharmony_ci if (twl->usb_mode == T2_USB_MODE_ULPI) 4658c2ecf20Sopenharmony_ci twl4030_i2c_access(twl, 0); 4668c2ecf20Sopenharmony_ci /* 4678c2ecf20Sopenharmony_ci * According to the TPS65950 TRM, there has to be at least 50ms 4688c2ecf20Sopenharmony_ci * delay between setting POWER_CTRL_OTG_ENAB and enabling charging 4698c2ecf20Sopenharmony_ci * so wait here so that a fully enabled phy can be expected after 4708c2ecf20Sopenharmony_ci * resume 4718c2ecf20Sopenharmony_ci */ 4728c2ecf20Sopenharmony_ci msleep(50); 4738c2ecf20Sopenharmony_ci return 0; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic int twl4030_phy_power_off(struct phy *phy) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct twl4030_usb *twl = phy_get_drvdata(phy); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int twl4030_phy_power_on(struct phy *phy) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct twl4030_usb *twl = phy_get_drvdata(phy); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 4908c2ecf20Sopenharmony_ci pm_runtime_get_sync(twl->dev); 4918c2ecf20Sopenharmony_ci schedule_delayed_work(&twl->id_workaround_work, HZ); 4928c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(twl->dev); 4938c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(twl->dev); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic int twl4030_usb_ldo_init(struct twl4030_usb *twl) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci /* Enable writing to power configuration registers */ 5018c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, 5028c2ecf20Sopenharmony_ci TWL4030_PM_MASTER_PROTECT_KEY); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, 5058c2ecf20Sopenharmony_ci TWL4030_PM_MASTER_PROTECT_KEY); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/ 5088c2ecf20Sopenharmony_ci /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/ 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci /* input to VUSB3V1 LDO is from VBAT, not VBUS */ 5118c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci /* Initialize 3.1V regulator */ 5148c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci twl->usb3v1 = devm_regulator_get(twl->dev, "usb3v1"); 5178c2ecf20Sopenharmony_ci if (IS_ERR(twl->usb3v1)) 5188c2ecf20Sopenharmony_ci return -ENODEV; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* Initialize 1.5V regulator */ 5238c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci twl->usb1v5 = devm_regulator_get(twl->dev, "usb1v5"); 5268c2ecf20Sopenharmony_ci if (IS_ERR(twl->usb1v5)) 5278c2ecf20Sopenharmony_ci return -ENODEV; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* Initialize 1.8V regulator */ 5328c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci twl->usb1v8 = devm_regulator_get(twl->dev, "usb1v8"); 5358c2ecf20Sopenharmony_ci if (IS_ERR(twl->usb1v8)) 5368c2ecf20Sopenharmony_ci return -ENODEV; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* disable access to power configuration registers */ 5418c2ecf20Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, 5428c2ecf20Sopenharmony_ci TWL4030_PM_MASTER_PROTECT_KEY); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci return 0; 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic ssize_t twl4030_usb_vbus_show(struct device *dev, 5488c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct twl4030_usb *twl = dev_get_drvdata(dev); 5518c2ecf20Sopenharmony_ci int ret = -EINVAL; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci mutex_lock(&twl->lock); 5548c2ecf20Sopenharmony_ci ret = sprintf(buf, "%s\n", 5558c2ecf20Sopenharmony_ci twl->vbus_supplied ? "on" : "off"); 5568c2ecf20Sopenharmony_ci mutex_unlock(&twl->lock); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci return ret; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_cistatic DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic irqreturn_t twl4030_usb_irq(int irq, void *_twl) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct twl4030_usb *twl = _twl; 5658c2ecf20Sopenharmony_ci enum musb_vbus_id_status status; 5668c2ecf20Sopenharmony_ci int err; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci status = twl4030_usb_linkstat(twl); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci mutex_lock(&twl->lock); 5718c2ecf20Sopenharmony_ci twl->linkstat = status; 5728c2ecf20Sopenharmony_ci mutex_unlock(&twl->lock); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (cable_present(status)) { 5758c2ecf20Sopenharmony_ci if (atomic_add_unless(&twl->connected, 1, 1)) { 5768c2ecf20Sopenharmony_ci dev_dbg(twl->dev, "%s: cable connected %i\n", 5778c2ecf20Sopenharmony_ci __func__, status); 5788c2ecf20Sopenharmony_ci pm_runtime_get_sync(twl->dev); 5798c2ecf20Sopenharmony_ci twl->musb_mailbox_pending = true; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci } else { 5828c2ecf20Sopenharmony_ci if (atomic_add_unless(&twl->connected, -1, 0)) { 5838c2ecf20Sopenharmony_ci dev_dbg(twl->dev, "%s: cable disconnected %i\n", 5848c2ecf20Sopenharmony_ci __func__, status); 5858c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(twl->dev); 5868c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(twl->dev); 5878c2ecf20Sopenharmony_ci twl->musb_mailbox_pending = true; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci if (twl->musb_mailbox_pending) { 5918c2ecf20Sopenharmony_ci err = musb_mailbox(status); 5928c2ecf20Sopenharmony_ci if (!err) 5938c2ecf20Sopenharmony_ci twl->musb_mailbox_pending = false; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* don't schedule during sleep - irq works right then */ 5978c2ecf20Sopenharmony_ci if (status == MUSB_ID_GROUND && pm_runtime_active(twl->dev)) { 5988c2ecf20Sopenharmony_ci cancel_delayed_work(&twl->id_workaround_work); 5998c2ecf20Sopenharmony_ci schedule_delayed_work(&twl->id_workaround_work, HZ); 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (irq) 6038c2ecf20Sopenharmony_ci sysfs_notify(&twl->dev->kobj, NULL, "vbus"); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic void twl4030_id_workaround_work(struct work_struct *work) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct twl4030_usb *twl = container_of(work, struct twl4030_usb, 6118c2ecf20Sopenharmony_ci id_workaround_work.work); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci twl4030_usb_irq(0, twl); 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_cistatic int twl4030_phy_init(struct phy *phy) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct twl4030_usb *twl = phy_get_drvdata(phy); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci pm_runtime_get_sync(twl->dev); 6218c2ecf20Sopenharmony_ci twl->linkstat = MUSB_UNKNOWN; 6228c2ecf20Sopenharmony_ci schedule_delayed_work(&twl->id_workaround_work, HZ); 6238c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(twl->dev); 6248c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(twl->dev); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci return 0; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic int twl4030_set_peripheral(struct usb_otg *otg, 6308c2ecf20Sopenharmony_ci struct usb_gadget *gadget) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci if (!otg) 6338c2ecf20Sopenharmony_ci return -ENODEV; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci otg->gadget = gadget; 6368c2ecf20Sopenharmony_ci if (!gadget) 6378c2ecf20Sopenharmony_ci otg->state = OTG_STATE_UNDEFINED; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci return 0; 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cistatic int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci if (!otg) 6458c2ecf20Sopenharmony_ci return -ENODEV; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci otg->host = host; 6488c2ecf20Sopenharmony_ci if (!host) 6498c2ecf20Sopenharmony_ci otg->state = OTG_STATE_UNDEFINED; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return 0; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic const struct phy_ops ops = { 6558c2ecf20Sopenharmony_ci .init = twl4030_phy_init, 6568c2ecf20Sopenharmony_ci .power_on = twl4030_phy_power_on, 6578c2ecf20Sopenharmony_ci .power_off = twl4030_phy_power_off, 6588c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6598c2ecf20Sopenharmony_ci}; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic const struct dev_pm_ops twl4030_usb_pm_ops = { 6628c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend, 6638c2ecf20Sopenharmony_ci twl4030_usb_runtime_resume, NULL) 6648c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(twl4030_usb_suspend, twl4030_usb_resume) 6658c2ecf20Sopenharmony_ci}; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic int twl4030_usb_probe(struct platform_device *pdev) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev); 6708c2ecf20Sopenharmony_ci struct twl4030_usb *twl; 6718c2ecf20Sopenharmony_ci struct phy *phy; 6728c2ecf20Sopenharmony_ci int status, err; 6738c2ecf20Sopenharmony_ci struct usb_otg *otg; 6748c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 6758c2ecf20Sopenharmony_ci struct phy_provider *phy_provider; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); 6788c2ecf20Sopenharmony_ci if (!twl) 6798c2ecf20Sopenharmony_ci return -ENOMEM; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (np) 6828c2ecf20Sopenharmony_ci of_property_read_u32(np, "usb_mode", 6838c2ecf20Sopenharmony_ci (enum twl4030_usb_mode *)&twl->usb_mode); 6848c2ecf20Sopenharmony_ci else if (pdata) { 6858c2ecf20Sopenharmony_ci twl->usb_mode = pdata->usb_mode; 6868c2ecf20Sopenharmony_ci } else { 6878c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); 6888c2ecf20Sopenharmony_ci return -EINVAL; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); 6928c2ecf20Sopenharmony_ci if (!otg) 6938c2ecf20Sopenharmony_ci return -ENOMEM; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci twl->dev = &pdev->dev; 6968c2ecf20Sopenharmony_ci twl->irq = platform_get_irq(pdev, 0); 6978c2ecf20Sopenharmony_ci twl->vbus_supplied = false; 6988c2ecf20Sopenharmony_ci twl->linkstat = MUSB_UNKNOWN; 6998c2ecf20Sopenharmony_ci twl->musb_mailbox_pending = false; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci twl->phy.dev = twl->dev; 7028c2ecf20Sopenharmony_ci twl->phy.label = "twl4030"; 7038c2ecf20Sopenharmony_ci twl->phy.otg = otg; 7048c2ecf20Sopenharmony_ci twl->phy.type = USB_PHY_TYPE_USB2; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci otg->usb_phy = &twl->phy; 7078c2ecf20Sopenharmony_ci otg->set_host = twl4030_set_host; 7088c2ecf20Sopenharmony_ci otg->set_peripheral = twl4030_set_peripheral; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci phy = devm_phy_create(twl->dev, NULL, &ops); 7118c2ecf20Sopenharmony_ci if (IS_ERR(phy)) { 7128c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Failed to create PHY\n"); 7138c2ecf20Sopenharmony_ci return PTR_ERR(phy); 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci phy_set_drvdata(phy, twl); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci phy_provider = devm_of_phy_provider_register(twl->dev, 7198c2ecf20Sopenharmony_ci of_phy_simple_xlate); 7208c2ecf20Sopenharmony_ci if (IS_ERR(phy_provider)) 7218c2ecf20Sopenharmony_ci return PTR_ERR(phy_provider); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* init mutex for workqueue */ 7248c2ecf20Sopenharmony_ci mutex_init(&twl->lock); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci err = twl4030_usb_ldo_init(twl); 7298c2ecf20Sopenharmony_ci if (err) { 7308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "ldo init failed\n"); 7318c2ecf20Sopenharmony_ci return err; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci usb_add_phy_dev(&twl->phy); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, twl); 7368c2ecf20Sopenharmony_ci if (device_create_file(&pdev->dev, &dev_attr_vbus)) 7378c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "could not create sysfs file\n"); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 7428c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); 7438c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 7448c2ecf20Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci /* Our job is to use irqs and status from the power module 7478c2ecf20Sopenharmony_ci * to keep the transceiver disabled when nothing's connected. 7488c2ecf20Sopenharmony_ci * 7498c2ecf20Sopenharmony_ci * FIXME we actually shouldn't start enabling it until the 7508c2ecf20Sopenharmony_ci * USB controller drivers have said they're ready, by calling 7518c2ecf20Sopenharmony_ci * set_host() and/or set_peripheral() ... OTG_capable boards 7528c2ecf20Sopenharmony_ci * need both handles, otherwise just one suffices. 7538c2ecf20Sopenharmony_ci */ 7548c2ecf20Sopenharmony_ci status = devm_request_threaded_irq(twl->dev, twl->irq, NULL, 7558c2ecf20Sopenharmony_ci twl4030_usb_irq, IRQF_TRIGGER_FALLING | 7568c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl); 7578c2ecf20Sopenharmony_ci if (status < 0) { 7588c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", 7598c2ecf20Sopenharmony_ci twl->irq, status); 7608c2ecf20Sopenharmony_ci return status; 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (pdata) 7648c2ecf20Sopenharmony_ci err = phy_create_lookup(phy, "usb", "musb-hdrc.0"); 7658c2ecf20Sopenharmony_ci if (err) 7668c2ecf20Sopenharmony_ci return err; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 7698c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(twl->dev); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); 7728c2ecf20Sopenharmony_ci return 0; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic int twl4030_usb_remove(struct platform_device *pdev) 7768c2ecf20Sopenharmony_ci{ 7778c2ecf20Sopenharmony_ci struct twl4030_usb *twl = platform_get_drvdata(pdev); 7788c2ecf20Sopenharmony_ci int val; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci usb_remove_phy(&twl->phy); 7818c2ecf20Sopenharmony_ci pm_runtime_get_sync(twl->dev); 7828c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&twl->id_workaround_work); 7838c2ecf20Sopenharmony_ci device_remove_file(twl->dev, &dev_attr_vbus); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci /* set transceiver mode to power on defaults */ 7868c2ecf20Sopenharmony_ci twl4030_usb_set_mode(twl, -1); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci /* idle ulpi before powering off */ 7898c2ecf20Sopenharmony_ci if (cable_present(twl->linkstat)) 7908c2ecf20Sopenharmony_ci pm_runtime_put_noidle(twl->dev); 7918c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(twl->dev); 7928c2ecf20Sopenharmony_ci pm_runtime_dont_use_autosuspend(&pdev->dev); 7938c2ecf20Sopenharmony_ci pm_runtime_put_sync(twl->dev); 7948c2ecf20Sopenharmony_ci pm_runtime_disable(twl->dev); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci /* autogate 60MHz ULPI clock, 7978c2ecf20Sopenharmony_ci * clear dpll clock request for i2c access, 7988c2ecf20Sopenharmony_ci * disable 32KHz 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ci val = twl4030_usb_read(twl, PHY_CLK_CTRL); 8018c2ecf20Sopenharmony_ci if (val >= 0) { 8028c2ecf20Sopenharmony_ci val |= PHY_CLK_CTRL_CLOCKGATING_EN; 8038c2ecf20Sopenharmony_ci val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); 8048c2ecf20Sopenharmony_ci twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val); 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* disable complete OTG block */ 8088c2ecf20Sopenharmony_ci twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci return 0; 8118c2ecf20Sopenharmony_ci} 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 8148c2ecf20Sopenharmony_cistatic const struct of_device_id twl4030_usb_id_table[] = { 8158c2ecf20Sopenharmony_ci { .compatible = "ti,twl4030-usb" }, 8168c2ecf20Sopenharmony_ci {} 8178c2ecf20Sopenharmony_ci}; 8188c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, twl4030_usb_id_table); 8198c2ecf20Sopenharmony_ci#endif 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic struct platform_driver twl4030_usb_driver = { 8228c2ecf20Sopenharmony_ci .probe = twl4030_usb_probe, 8238c2ecf20Sopenharmony_ci .remove = twl4030_usb_remove, 8248c2ecf20Sopenharmony_ci .driver = { 8258c2ecf20Sopenharmony_ci .name = "twl4030_usb", 8268c2ecf20Sopenharmony_ci .pm = &twl4030_usb_pm_ops, 8278c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(twl4030_usb_id_table), 8288c2ecf20Sopenharmony_ci }, 8298c2ecf20Sopenharmony_ci}; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic int __init twl4030_usb_init(void) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci return platform_driver_register(&twl4030_usb_driver); 8348c2ecf20Sopenharmony_ci} 8358c2ecf20Sopenharmony_cisubsys_initcall(twl4030_usb_init); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cistatic void __exit twl4030_usb_exit(void) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci platform_driver_unregister(&twl4030_usb_driver); 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_cimodule_exit(twl4030_usb_exit); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:twl4030_usb"); 8448c2ecf20Sopenharmony_ciMODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation"); 8458c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TWL4030 USB transceiver driver"); 8468c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 847