162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004-2007 Texas Instruments 662306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 762306a36Sopenharmony_ci * Contact: Felipe Balbi <felipe.balbi@nokia.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Current status: 1062306a36Sopenharmony_ci * - HS USB ULPI mode works. 1162306a36Sopenharmony_ci * - 3-pin mode support may be added in future. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/interrupt.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/workqueue.h> 2062306a36Sopenharmony_ci#include <linux/io.h> 2162306a36Sopenharmony_ci#include <linux/delay.h> 2262306a36Sopenharmony_ci#include <linux/usb/otg.h> 2362306a36Sopenharmony_ci#include <linux/phy/phy.h> 2462306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2562306a36Sopenharmony_ci#include <linux/usb/musb.h> 2662306a36Sopenharmony_ci#include <linux/usb/ulpi.h> 2762306a36Sopenharmony_ci#include <linux/mfd/twl.h> 2862306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2962306a36Sopenharmony_ci#include <linux/err.h> 3062306a36Sopenharmony_ci#include <linux/slab.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* Register defines */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define MCPC_CTRL 0x30 3562306a36Sopenharmony_ci#define MCPC_CTRL_RTSOL (1 << 7) 3662306a36Sopenharmony_ci#define MCPC_CTRL_EXTSWR (1 << 6) 3762306a36Sopenharmony_ci#define MCPC_CTRL_EXTSWC (1 << 5) 3862306a36Sopenharmony_ci#define MCPC_CTRL_VOICESW (1 << 4) 3962306a36Sopenharmony_ci#define MCPC_CTRL_OUT64K (1 << 3) 4062306a36Sopenharmony_ci#define MCPC_CTRL_RTSCTSSW (1 << 2) 4162306a36Sopenharmony_ci#define MCPC_CTRL_HS_UART (1 << 0) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define MCPC_IO_CTRL 0x33 4462306a36Sopenharmony_ci#define MCPC_IO_CTRL_MICBIASEN (1 << 5) 4562306a36Sopenharmony_ci#define MCPC_IO_CTRL_CTS_NPU (1 << 4) 4662306a36Sopenharmony_ci#define MCPC_IO_CTRL_RXD_PU (1 << 3) 4762306a36Sopenharmony_ci#define MCPC_IO_CTRL_TXDTYP (1 << 2) 4862306a36Sopenharmony_ci#define MCPC_IO_CTRL_CTSTYP (1 << 1) 4962306a36Sopenharmony_ci#define MCPC_IO_CTRL_RTSTYP (1 << 0) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define MCPC_CTRL2 0x36 5262306a36Sopenharmony_ci#define MCPC_CTRL2_MCPC_CK_EN (1 << 0) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define OTHER_FUNC_CTRL 0x80 5562306a36Sopenharmony_ci#define OTHER_FUNC_CTRL_BDIS_ACON_EN (1 << 4) 5662306a36Sopenharmony_ci#define OTHER_FUNC_CTRL_FIVEWIRE_MODE (1 << 2) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define OTHER_IFC_CTRL 0x83 5962306a36Sopenharmony_ci#define OTHER_IFC_CTRL_OE_INT_EN (1 << 6) 6062306a36Sopenharmony_ci#define OTHER_IFC_CTRL_CEA2011_MODE (1 << 5) 6162306a36Sopenharmony_ci#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN (1 << 4) 6262306a36Sopenharmony_ci#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT (1 << 3) 6362306a36Sopenharmony_ci#define OTHER_IFC_CTRL_HIZ_ULPI (1 << 2) 6462306a36Sopenharmony_ci#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define OTHER_INT_EN_RISE 0x86 6762306a36Sopenharmony_ci#define OTHER_INT_EN_FALL 0x89 6862306a36Sopenharmony_ci#define OTHER_INT_STS 0x8C 6962306a36Sopenharmony_ci#define OTHER_INT_LATCH 0x8D 7062306a36Sopenharmony_ci#define OTHER_INT_VB_SESS_VLD (1 << 7) 7162306a36Sopenharmony_ci#define OTHER_INT_DM_HI (1 << 6) /* not valid for "latch" reg */ 7262306a36Sopenharmony_ci#define OTHER_INT_DP_HI (1 << 5) /* not valid for "latch" reg */ 7362306a36Sopenharmony_ci#define OTHER_INT_BDIS_ACON (1 << 3) /* not valid for "fall" regs */ 7462306a36Sopenharmony_ci#define OTHER_INT_MANU (1 << 1) 7562306a36Sopenharmony_ci#define OTHER_INT_ABNORMAL_STRESS (1 << 0) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define ID_STATUS 0x96 7862306a36Sopenharmony_ci#define ID_RES_FLOAT (1 << 4) 7962306a36Sopenharmony_ci#define ID_RES_440K (1 << 3) 8062306a36Sopenharmony_ci#define ID_RES_200K (1 << 2) 8162306a36Sopenharmony_ci#define ID_RES_102K (1 << 1) 8262306a36Sopenharmony_ci#define ID_RES_GND (1 << 0) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define POWER_CTRL 0xAC 8562306a36Sopenharmony_ci#define POWER_CTRL_OTG_ENAB (1 << 5) 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define OTHER_IFC_CTRL2 0xAF 8862306a36Sopenharmony_ci#define OTHER_IFC_CTRL2_ULPI_STP_LOW (1 << 4) 8962306a36Sopenharmony_ci#define OTHER_IFC_CTRL2_ULPI_TXEN_POL (1 << 3) 9062306a36Sopenharmony_ci#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2) 9162306a36Sopenharmony_ci#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK (3 << 0) /* bits 0 and 1 */ 9262306a36Sopenharmony_ci#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N (0 << 0) 9362306a36Sopenharmony_ci#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N (1 << 0) 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define REG_CTRL_EN 0xB2 9662306a36Sopenharmony_ci#define REG_CTRL_ERROR 0xB5 9762306a36Sopenharmony_ci#define ULPI_I2C_CONFLICT_INTEN (1 << 0) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#define OTHER_FUNC_CTRL2 0xB8 10062306a36Sopenharmony_ci#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* following registers do not have separate _clr and _set registers */ 10362306a36Sopenharmony_ci#define VBUS_DEBOUNCE 0xC0 10462306a36Sopenharmony_ci#define ID_DEBOUNCE 0xC1 10562306a36Sopenharmony_ci#define VBAT_TIMER 0xD3 10662306a36Sopenharmony_ci#define PHY_PWR_CTRL 0xFD 10762306a36Sopenharmony_ci#define PHY_PWR_PHYPWD (1 << 0) 10862306a36Sopenharmony_ci#define PHY_CLK_CTRL 0xFE 10962306a36Sopenharmony_ci#define PHY_CLK_CTRL_CLOCKGATING_EN (1 << 2) 11062306a36Sopenharmony_ci#define PHY_CLK_CTRL_CLK32K_EN (1 << 1) 11162306a36Sopenharmony_ci#define REQ_PHY_DPLL_CLK (1 << 0) 11262306a36Sopenharmony_ci#define PHY_CLK_CTRL_STS 0xFF 11362306a36Sopenharmony_ci#define PHY_DPLL_CLK (1 << 0) 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* In module TWL_MODULE_PM_MASTER */ 11662306a36Sopenharmony_ci#define STS_HW_CONDITIONS 0x0F 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* In module TWL_MODULE_PM_RECEIVER */ 11962306a36Sopenharmony_ci#define VUSB_DEDICATED1 0x7D 12062306a36Sopenharmony_ci#define VUSB_DEDICATED2 0x7E 12162306a36Sopenharmony_ci#define VUSB1V5_DEV_GRP 0x71 12262306a36Sopenharmony_ci#define VUSB1V5_TYPE 0x72 12362306a36Sopenharmony_ci#define VUSB1V5_REMAP 0x73 12462306a36Sopenharmony_ci#define VUSB1V8_DEV_GRP 0x74 12562306a36Sopenharmony_ci#define VUSB1V8_TYPE 0x75 12662306a36Sopenharmony_ci#define VUSB1V8_REMAP 0x76 12762306a36Sopenharmony_ci#define VUSB3V1_DEV_GRP 0x77 12862306a36Sopenharmony_ci#define VUSB3V1_TYPE 0x78 12962306a36Sopenharmony_ci#define VUSB3V1_REMAP 0x79 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* In module TWL4030_MODULE_INTBR */ 13262306a36Sopenharmony_ci#define PMBR1 0x0D 13362306a36Sopenharmony_ci#define GPIO_USB_4PIN_ULPI_2430C (3 << 0) 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic irqreturn_t twl4030_usb_irq(int irq, void *_twl); 13662306a36Sopenharmony_ci/* 13762306a36Sopenharmony_ci * If VBUS is valid or ID is ground, then we know a 13862306a36Sopenharmony_ci * cable is present and we need to be runtime-enabled 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_cistatic inline bool cable_present(enum musb_vbus_id_status stat) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci return stat == MUSB_VBUS_VALID || 14362306a36Sopenharmony_ci stat == MUSB_ID_GROUND; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistruct twl4030_usb { 14762306a36Sopenharmony_ci struct usb_phy phy; 14862306a36Sopenharmony_ci struct device *dev; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* TWL4030 internal USB regulator supplies */ 15162306a36Sopenharmony_ci struct regulator *usb1v5; 15262306a36Sopenharmony_ci struct regulator *usb1v8; 15362306a36Sopenharmony_ci struct regulator *usb3v1; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* for vbus reporting with irqs disabled */ 15662306a36Sopenharmony_ci struct mutex lock; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* pin configuration */ 15962306a36Sopenharmony_ci enum twl4030_usb_mode usb_mode; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci int irq; 16262306a36Sopenharmony_ci enum musb_vbus_id_status linkstat; 16362306a36Sopenharmony_ci atomic_t connected; 16462306a36Sopenharmony_ci bool vbus_supplied; 16562306a36Sopenharmony_ci bool musb_mailbox_pending; 16662306a36Sopenharmony_ci unsigned long runtime_suspended:1; 16762306a36Sopenharmony_ci unsigned long needs_resume:1; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci struct delayed_work id_workaround_work; 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci/* internal define on top of container_of */ 17362306a36Sopenharmony_ci#define phy_to_twl(x) container_of((x), struct twl4030_usb, phy) 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl, 17862306a36Sopenharmony_ci u8 module, u8 data, u8 address) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci u8 check = 0xFF; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if ((twl_i2c_write_u8(module, data, address) >= 0) && 18362306a36Sopenharmony_ci (twl_i2c_read_u8(module, &check, address) >= 0) && 18462306a36Sopenharmony_ci (check == data)) 18562306a36Sopenharmony_ci return 0; 18662306a36Sopenharmony_ci dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", 18762306a36Sopenharmony_ci 1, module, address, check, data); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Failed once: Try again */ 19062306a36Sopenharmony_ci if ((twl_i2c_write_u8(module, data, address) >= 0) && 19162306a36Sopenharmony_ci (twl_i2c_read_u8(module, &check, address) >= 0) && 19262306a36Sopenharmony_ci (check == data)) 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n", 19562306a36Sopenharmony_ci 2, module, address, check, data); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Failed again: Return error */ 19862306a36Sopenharmony_ci return -EBUSY; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci#define twl4030_usb_write_verify(twl, address, data) \ 20262306a36Sopenharmony_ci twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address)) 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic inline int twl4030_usb_write(struct twl4030_usb *twl, 20562306a36Sopenharmony_ci u8 address, u8 data) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci int ret = 0; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address); 21062306a36Sopenharmony_ci if (ret < 0) 21162306a36Sopenharmony_ci dev_dbg(twl->dev, 21262306a36Sopenharmony_ci "TWL4030:USB:Write[0x%x] Error %d\n", address, ret); 21362306a36Sopenharmony_ci return ret; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci u8 data; 21962306a36Sopenharmony_ci int ret = 0; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci ret = twl_i2c_read_u8(module, &data, address); 22262306a36Sopenharmony_ci if (ret >= 0) 22362306a36Sopenharmony_ci ret = data; 22462306a36Sopenharmony_ci else 22562306a36Sopenharmony_ci dev_dbg(twl->dev, 22662306a36Sopenharmony_ci "TWL4030:readb[0x%x,0x%x] Error %d\n", 22762306a36Sopenharmony_ci module, address, ret); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return ret; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci return twl4030_readb(twl, TWL_MODULE_USB, address); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic inline int 24062306a36Sopenharmony_citwl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci return twl4030_usb_write(twl, ULPI_SET(reg), bits); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic inline int 24662306a36Sopenharmony_citwl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci return twl4030_usb_write(twl, ULPI_CLR(reg), bits); 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/ 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic bool twl4030_is_driving_vbus(struct twl4030_usb *twl) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci int ret; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci ret = twl4030_usb_read(twl, PHY_CLK_CTRL_STS); 25862306a36Sopenharmony_ci if (ret < 0 || !(ret & PHY_DPLL_CLK)) 25962306a36Sopenharmony_ci /* 26062306a36Sopenharmony_ci * if clocks are off, registers are not updated, 26162306a36Sopenharmony_ci * but we can assume we don't drive VBUS in this case 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci return false; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci ret = twl4030_usb_read(twl, ULPI_OTG_CTRL); 26662306a36Sopenharmony_ci if (ret < 0) 26762306a36Sopenharmony_ci return false; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic enum musb_vbus_id_status 27362306a36Sopenharmony_ci twl4030_usb_linkstat(struct twl4030_usb *twl) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci int status; 27662306a36Sopenharmony_ci enum musb_vbus_id_status linkstat = MUSB_UNKNOWN; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci twl->vbus_supplied = false; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* 28162306a36Sopenharmony_ci * For ID/VBUS sensing, see manual section 15.4.8 ... 28262306a36Sopenharmony_ci * except when using only battery backup power, two 28362306a36Sopenharmony_ci * comparators produce VBUS_PRES and ID_PRES signals, 28462306a36Sopenharmony_ci * which don't match docs elsewhere. But ... BIT(7) 28562306a36Sopenharmony_ci * and BIT(2) of STS_HW_CONDITIONS, respectively, do 28662306a36Sopenharmony_ci * seem to match up. If either is true the USB_PRES 28762306a36Sopenharmony_ci * signal is active, the OTG module is activated, and 28862306a36Sopenharmony_ci * its interrupt may be raised (may wake the system). 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS); 29162306a36Sopenharmony_ci if (status < 0) 29262306a36Sopenharmony_ci dev_err(twl->dev, "USB link status err %d\n", status); 29362306a36Sopenharmony_ci else if (status & (BIT(7) | BIT(2))) { 29462306a36Sopenharmony_ci if (status & BIT(7)) { 29562306a36Sopenharmony_ci if (twl4030_is_driving_vbus(twl)) 29662306a36Sopenharmony_ci status &= ~BIT(7); 29762306a36Sopenharmony_ci else 29862306a36Sopenharmony_ci twl->vbus_supplied = true; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (status & BIT(2)) 30262306a36Sopenharmony_ci linkstat = MUSB_ID_GROUND; 30362306a36Sopenharmony_ci else if (status & BIT(7)) 30462306a36Sopenharmony_ci linkstat = MUSB_VBUS_VALID; 30562306a36Sopenharmony_ci else 30662306a36Sopenharmony_ci linkstat = MUSB_VBUS_OFF; 30762306a36Sopenharmony_ci } else { 30862306a36Sopenharmony_ci if (twl->linkstat != MUSB_UNKNOWN) 30962306a36Sopenharmony_ci linkstat = MUSB_VBUS_OFF; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci kobject_uevent(&twl->dev->kobj, linkstat == MUSB_VBUS_VALID 31362306a36Sopenharmony_ci ? KOBJ_ONLINE : KOBJ_OFFLINE); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n", 31662306a36Sopenharmony_ci status, status, linkstat); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* REVISIT this assumes host and peripheral controllers 31962306a36Sopenharmony_ci * are registered, and that both are active... 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return linkstat; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci twl->usb_mode = mode; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci switch (mode) { 33062306a36Sopenharmony_ci case T2_USB_MODE_ULPI: 33162306a36Sopenharmony_ci twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL, 33262306a36Sopenharmony_ci ULPI_IFC_CTRL_CARKITMODE); 33362306a36Sopenharmony_ci twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); 33462306a36Sopenharmony_ci twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL, 33562306a36Sopenharmony_ci ULPI_FUNC_CTRL_XCVRSEL_MASK | 33662306a36Sopenharmony_ci ULPI_FUNC_CTRL_OPMODE_MASK); 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci case -1: 33962306a36Sopenharmony_ci /* FIXME: power on defaults */ 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci default: 34262306a36Sopenharmony_ci dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", 34362306a36Sopenharmony_ci mode); 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic void twl4030_i2c_access(struct twl4030_usb *twl, int on) 34962306a36Sopenharmony_ci{ 35062306a36Sopenharmony_ci unsigned long timeout; 35162306a36Sopenharmony_ci int val = twl4030_usb_read(twl, PHY_CLK_CTRL); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (val >= 0) { 35462306a36Sopenharmony_ci if (on) { 35562306a36Sopenharmony_ci /* enable DPLL to access PHY registers over I2C */ 35662306a36Sopenharmony_ci val |= REQ_PHY_DPLL_CLK; 35762306a36Sopenharmony_ci WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, 35862306a36Sopenharmony_ci (u8)val) < 0); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci timeout = jiffies + HZ; 36162306a36Sopenharmony_ci while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & 36262306a36Sopenharmony_ci PHY_DPLL_CLK) 36362306a36Sopenharmony_ci && time_before(jiffies, timeout)) 36462306a36Sopenharmony_ci udelay(10); 36562306a36Sopenharmony_ci if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) & 36662306a36Sopenharmony_ci PHY_DPLL_CLK)) 36762306a36Sopenharmony_ci dev_err(twl->dev, "Timeout setting T2 HSUSB " 36862306a36Sopenharmony_ci "PHY DPLL clock\n"); 36962306a36Sopenharmony_ci } else { 37062306a36Sopenharmony_ci /* let ULPI control the DPLL clock */ 37162306a36Sopenharmony_ci val &= ~REQ_PHY_DPLL_CLK; 37262306a36Sopenharmony_ci WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL, 37362306a36Sopenharmony_ci (u8)val) < 0); 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic void __twl4030_phy_power(struct twl4030_usb *twl, int on) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (on) 38362306a36Sopenharmony_ci pwr &= ~PHY_PWR_PHYPWD; 38462306a36Sopenharmony_ci else 38562306a36Sopenharmony_ci pwr |= PHY_PWR_PHYPWD; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0); 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic int twl4030_usb_runtime_suspend(struct device *dev); 39162306a36Sopenharmony_cistatic int twl4030_usb_runtime_resume(struct device *dev); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int __maybe_unused twl4030_usb_suspend(struct device *dev) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci struct twl4030_usb *twl = dev_get_drvdata(dev); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* 39862306a36Sopenharmony_ci * we need enabled runtime on resume, 39962306a36Sopenharmony_ci * so turn irq off here, so we do not get it early 40062306a36Sopenharmony_ci * note: wakeup on usb plug works independently of this 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 40362306a36Sopenharmony_ci disable_irq(twl->irq); 40462306a36Sopenharmony_ci if (!twl->runtime_suspended && !atomic_read(&twl->connected)) { 40562306a36Sopenharmony_ci twl4030_usb_runtime_suspend(dev); 40662306a36Sopenharmony_ci twl->needs_resume = 1; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int __maybe_unused twl4030_usb_resume(struct device *dev) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci struct twl4030_usb *twl = dev_get_drvdata(dev); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 41762306a36Sopenharmony_ci enable_irq(twl->irq); 41862306a36Sopenharmony_ci if (twl->needs_resume) 41962306a36Sopenharmony_ci twl4030_usb_runtime_resume(dev); 42062306a36Sopenharmony_ci /* check whether cable status changed */ 42162306a36Sopenharmony_ci twl4030_usb_irq(0, twl); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci twl->runtime_suspended = 0; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct twl4030_usb *twl = dev_get_drvdata(dev); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci __twl4030_phy_power(twl, 0); 43562306a36Sopenharmony_ci regulator_disable(twl->usb1v5); 43662306a36Sopenharmony_ci regulator_disable(twl->usb1v8); 43762306a36Sopenharmony_ci regulator_disable(twl->usb3v1); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci twl->runtime_suspended = 1; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int __maybe_unused twl4030_usb_runtime_resume(struct device *dev) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci struct twl4030_usb *twl = dev_get_drvdata(dev); 44762306a36Sopenharmony_ci int res; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci res = regulator_enable(twl->usb3v1); 45262306a36Sopenharmony_ci if (res) 45362306a36Sopenharmony_ci dev_err(twl->dev, "Failed to enable usb3v1\n"); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci res = regulator_enable(twl->usb1v8); 45662306a36Sopenharmony_ci if (res) 45762306a36Sopenharmony_ci dev_err(twl->dev, "Failed to enable usb1v8\n"); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci /* 46062306a36Sopenharmony_ci * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP 46162306a36Sopenharmony_ci * in twl4030) resets the VUSB_DEDICATED2 register. This reset 46262306a36Sopenharmony_ci * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to 46362306a36Sopenharmony_ci * SLEEP. We work around this by clearing the bit after usv3v1 46462306a36Sopenharmony_ci * is re-activated. This ensures that VUSB3V1 is really active. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci res = regulator_enable(twl->usb1v5); 46962306a36Sopenharmony_ci if (res) 47062306a36Sopenharmony_ci dev_err(twl->dev, "Failed to enable usb1v5\n"); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci __twl4030_phy_power(twl, 1); 47362306a36Sopenharmony_ci twl4030_usb_write(twl, PHY_CLK_CTRL, 47462306a36Sopenharmony_ci twl4030_usb_read(twl, PHY_CLK_CTRL) | 47562306a36Sopenharmony_ci (PHY_CLK_CTRL_CLOCKGATING_EN | 47662306a36Sopenharmony_ci PHY_CLK_CTRL_CLK32K_EN)); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci twl4030_i2c_access(twl, 1); 47962306a36Sopenharmony_ci twl4030_usb_set_mode(twl, twl->usb_mode); 48062306a36Sopenharmony_ci if (twl->usb_mode == T2_USB_MODE_ULPI) 48162306a36Sopenharmony_ci twl4030_i2c_access(twl, 0); 48262306a36Sopenharmony_ci /* 48362306a36Sopenharmony_ci * According to the TPS65950 TRM, there has to be at least 50ms 48462306a36Sopenharmony_ci * delay between setting POWER_CTRL_OTG_ENAB and enabling charging 48562306a36Sopenharmony_ci * so wait here so that a fully enabled phy can be expected after 48662306a36Sopenharmony_ci * resume 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci msleep(50); 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int twl4030_phy_power_off(struct phy *phy) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct twl4030_usb *twl = phy_get_drvdata(phy); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic int twl4030_phy_power_on(struct phy *phy) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct twl4030_usb *twl = phy_get_drvdata(phy); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci dev_dbg(twl->dev, "%s\n", __func__); 50662306a36Sopenharmony_ci pm_runtime_get_sync(twl->dev); 50762306a36Sopenharmony_ci schedule_delayed_work(&twl->id_workaround_work, HZ); 50862306a36Sopenharmony_ci pm_runtime_mark_last_busy(twl->dev); 50962306a36Sopenharmony_ci pm_runtime_put_autosuspend(twl->dev); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int twl4030_usb_ldo_init(struct twl4030_usb *twl) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci /* Enable writing to power configuration registers */ 51762306a36Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1, 51862306a36Sopenharmony_ci TWL4030_PM_MASTER_PROTECT_KEY); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2, 52162306a36Sopenharmony_ci TWL4030_PM_MASTER_PROTECT_KEY); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/ 52462306a36Sopenharmony_ci /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/ 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* input to VUSB3V1 LDO is from VBAT, not VBUS */ 52762306a36Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Initialize 3.1V regulator */ 53062306a36Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci twl->usb3v1 = devm_regulator_get(twl->dev, "usb3v1"); 53362306a36Sopenharmony_ci if (IS_ERR(twl->usb3v1)) 53462306a36Sopenharmony_ci return -ENODEV; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* Initialize 1.5V regulator */ 53962306a36Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci twl->usb1v5 = devm_regulator_get(twl->dev, "usb1v5"); 54262306a36Sopenharmony_ci if (IS_ERR(twl->usb1v5)) 54362306a36Sopenharmony_ci return -ENODEV; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* Initialize 1.8V regulator */ 54862306a36Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci twl->usb1v8 = devm_regulator_get(twl->dev, "usb1v8"); 55162306a36Sopenharmony_ci if (IS_ERR(twl->usb1v8)) 55262306a36Sopenharmony_ci return -ENODEV; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* disable access to power configuration registers */ 55762306a36Sopenharmony_ci twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0, 55862306a36Sopenharmony_ci TWL4030_PM_MASTER_PROTECT_KEY); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci return 0; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic ssize_t vbus_show(struct device *dev, 56462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct twl4030_usb *twl = dev_get_drvdata(dev); 56762306a36Sopenharmony_ci int ret = -EINVAL; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci mutex_lock(&twl->lock); 57062306a36Sopenharmony_ci ret = sprintf(buf, "%s\n", 57162306a36Sopenharmony_ci twl->vbus_supplied ? "on" : "off"); 57262306a36Sopenharmony_ci mutex_unlock(&twl->lock); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return ret; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(vbus); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic irqreturn_t twl4030_usb_irq(int irq, void *_twl) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct twl4030_usb *twl = _twl; 58162306a36Sopenharmony_ci enum musb_vbus_id_status status; 58262306a36Sopenharmony_ci int err; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci status = twl4030_usb_linkstat(twl); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci mutex_lock(&twl->lock); 58762306a36Sopenharmony_ci twl->linkstat = status; 58862306a36Sopenharmony_ci mutex_unlock(&twl->lock); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (cable_present(status)) { 59162306a36Sopenharmony_ci if (atomic_add_unless(&twl->connected, 1, 1)) { 59262306a36Sopenharmony_ci dev_dbg(twl->dev, "%s: cable connected %i\n", 59362306a36Sopenharmony_ci __func__, status); 59462306a36Sopenharmony_ci pm_runtime_get_sync(twl->dev); 59562306a36Sopenharmony_ci twl->musb_mailbox_pending = true; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci } else { 59862306a36Sopenharmony_ci if (atomic_add_unless(&twl->connected, -1, 0)) { 59962306a36Sopenharmony_ci dev_dbg(twl->dev, "%s: cable disconnected %i\n", 60062306a36Sopenharmony_ci __func__, status); 60162306a36Sopenharmony_ci pm_runtime_mark_last_busy(twl->dev); 60262306a36Sopenharmony_ci pm_runtime_put_autosuspend(twl->dev); 60362306a36Sopenharmony_ci twl->musb_mailbox_pending = true; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci if (twl->musb_mailbox_pending) { 60762306a36Sopenharmony_ci err = musb_mailbox(status); 60862306a36Sopenharmony_ci if (!err) 60962306a36Sopenharmony_ci twl->musb_mailbox_pending = false; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* don't schedule during sleep - irq works right then */ 61362306a36Sopenharmony_ci if (status == MUSB_ID_GROUND && pm_runtime_active(twl->dev)) { 61462306a36Sopenharmony_ci cancel_delayed_work(&twl->id_workaround_work); 61562306a36Sopenharmony_ci schedule_delayed_work(&twl->id_workaround_work, HZ); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (irq) 61962306a36Sopenharmony_ci sysfs_notify(&twl->dev->kobj, NULL, "vbus"); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return IRQ_HANDLED; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic void twl4030_id_workaround_work(struct work_struct *work) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci struct twl4030_usb *twl = container_of(work, struct twl4030_usb, 62762306a36Sopenharmony_ci id_workaround_work.work); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci twl4030_usb_irq(0, twl); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic int twl4030_phy_init(struct phy *phy) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct twl4030_usb *twl = phy_get_drvdata(phy); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci pm_runtime_get_sync(twl->dev); 63762306a36Sopenharmony_ci twl->linkstat = MUSB_UNKNOWN; 63862306a36Sopenharmony_ci schedule_delayed_work(&twl->id_workaround_work, HZ); 63962306a36Sopenharmony_ci pm_runtime_mark_last_busy(twl->dev); 64062306a36Sopenharmony_ci pm_runtime_put_autosuspend(twl->dev); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci return 0; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic int twl4030_set_peripheral(struct usb_otg *otg, 64662306a36Sopenharmony_ci struct usb_gadget *gadget) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci if (!otg) 64962306a36Sopenharmony_ci return -ENODEV; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci otg->gadget = gadget; 65262306a36Sopenharmony_ci if (!gadget) 65362306a36Sopenharmony_ci otg->state = OTG_STATE_UNDEFINED; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return 0; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci if (!otg) 66162306a36Sopenharmony_ci return -ENODEV; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci otg->host = host; 66462306a36Sopenharmony_ci if (!host) 66562306a36Sopenharmony_ci otg->state = OTG_STATE_UNDEFINED; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci return 0; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic const struct phy_ops ops = { 67162306a36Sopenharmony_ci .init = twl4030_phy_init, 67262306a36Sopenharmony_ci .power_on = twl4030_phy_power_on, 67362306a36Sopenharmony_ci .power_off = twl4030_phy_power_off, 67462306a36Sopenharmony_ci .owner = THIS_MODULE, 67562306a36Sopenharmony_ci}; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic const struct dev_pm_ops twl4030_usb_pm_ops = { 67862306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend, 67962306a36Sopenharmony_ci twl4030_usb_runtime_resume, NULL) 68062306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(twl4030_usb_suspend, twl4030_usb_resume) 68162306a36Sopenharmony_ci}; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic int twl4030_usb_probe(struct platform_device *pdev) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev); 68662306a36Sopenharmony_ci struct twl4030_usb *twl; 68762306a36Sopenharmony_ci struct phy *phy; 68862306a36Sopenharmony_ci int status, err; 68962306a36Sopenharmony_ci struct usb_otg *otg; 69062306a36Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 69162306a36Sopenharmony_ci struct phy_provider *phy_provider; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); 69462306a36Sopenharmony_ci if (!twl) 69562306a36Sopenharmony_ci return -ENOMEM; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (np) 69862306a36Sopenharmony_ci of_property_read_u32(np, "usb_mode", 69962306a36Sopenharmony_ci (enum twl4030_usb_mode *)&twl->usb_mode); 70062306a36Sopenharmony_ci else if (pdata) { 70162306a36Sopenharmony_ci twl->usb_mode = pdata->usb_mode; 70262306a36Sopenharmony_ci } else { 70362306a36Sopenharmony_ci dev_err(&pdev->dev, "twl4030 initialized without pdata\n"); 70462306a36Sopenharmony_ci return -EINVAL; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); 70862306a36Sopenharmony_ci if (!otg) 70962306a36Sopenharmony_ci return -ENOMEM; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci twl->dev = &pdev->dev; 71262306a36Sopenharmony_ci twl->irq = platform_get_irq(pdev, 0); 71362306a36Sopenharmony_ci twl->vbus_supplied = false; 71462306a36Sopenharmony_ci twl->linkstat = MUSB_UNKNOWN; 71562306a36Sopenharmony_ci twl->musb_mailbox_pending = false; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci twl->phy.dev = twl->dev; 71862306a36Sopenharmony_ci twl->phy.label = "twl4030"; 71962306a36Sopenharmony_ci twl->phy.otg = otg; 72062306a36Sopenharmony_ci twl->phy.type = USB_PHY_TYPE_USB2; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci otg->usb_phy = &twl->phy; 72362306a36Sopenharmony_ci otg->set_host = twl4030_set_host; 72462306a36Sopenharmony_ci otg->set_peripheral = twl4030_set_peripheral; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci phy = devm_phy_create(twl->dev, NULL, &ops); 72762306a36Sopenharmony_ci if (IS_ERR(phy)) { 72862306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Failed to create PHY\n"); 72962306a36Sopenharmony_ci return PTR_ERR(phy); 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci phy_set_drvdata(phy, twl); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci phy_provider = devm_of_phy_provider_register(twl->dev, 73562306a36Sopenharmony_ci of_phy_simple_xlate); 73662306a36Sopenharmony_ci if (IS_ERR(phy_provider)) 73762306a36Sopenharmony_ci return PTR_ERR(phy_provider); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* init mutex for workqueue */ 74062306a36Sopenharmony_ci mutex_init(&twl->lock); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci err = twl4030_usb_ldo_init(twl); 74562306a36Sopenharmony_ci if (err) { 74662306a36Sopenharmony_ci dev_err(&pdev->dev, "ldo init failed\n"); 74762306a36Sopenharmony_ci return err; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci usb_add_phy_dev(&twl->phy); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci platform_set_drvdata(pdev, twl); 75262306a36Sopenharmony_ci if (device_create_file(&pdev->dev, &dev_attr_vbus)) 75362306a36Sopenharmony_ci dev_warn(&pdev->dev, "could not create sysfs file\n"); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 75862306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); 75962306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 76062306a36Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* Our job is to use irqs and status from the power module 76362306a36Sopenharmony_ci * to keep the transceiver disabled when nothing's connected. 76462306a36Sopenharmony_ci * 76562306a36Sopenharmony_ci * FIXME we actually shouldn't start enabling it until the 76662306a36Sopenharmony_ci * USB controller drivers have said they're ready, by calling 76762306a36Sopenharmony_ci * set_host() and/or set_peripheral() ... OTG_capable boards 76862306a36Sopenharmony_ci * need both handles, otherwise just one suffices. 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ci status = devm_request_threaded_irq(twl->dev, twl->irq, NULL, 77162306a36Sopenharmony_ci twl4030_usb_irq, IRQF_TRIGGER_FALLING | 77262306a36Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl); 77362306a36Sopenharmony_ci if (status < 0) { 77462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n", 77562306a36Sopenharmony_ci twl->irq, status); 77662306a36Sopenharmony_ci return status; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (pdata) 78062306a36Sopenharmony_ci err = phy_create_lookup(phy, "usb", "musb-hdrc.0"); 78162306a36Sopenharmony_ci if (err) 78262306a36Sopenharmony_ci return err; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci pm_runtime_mark_last_busy(&pdev->dev); 78562306a36Sopenharmony_ci pm_runtime_put_autosuspend(twl->dev); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci dev_info(&pdev->dev, "Initialized TWL4030 USB module\n"); 78862306a36Sopenharmony_ci return 0; 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic void twl4030_usb_remove(struct platform_device *pdev) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct twl4030_usb *twl = platform_get_drvdata(pdev); 79462306a36Sopenharmony_ci int val; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci usb_remove_phy(&twl->phy); 79762306a36Sopenharmony_ci pm_runtime_get_sync(twl->dev); 79862306a36Sopenharmony_ci cancel_delayed_work_sync(&twl->id_workaround_work); 79962306a36Sopenharmony_ci device_remove_file(twl->dev, &dev_attr_vbus); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* set transceiver mode to power on defaults */ 80262306a36Sopenharmony_ci twl4030_usb_set_mode(twl, -1); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci /* idle ulpi before powering off */ 80562306a36Sopenharmony_ci if (cable_present(twl->linkstat)) 80662306a36Sopenharmony_ci pm_runtime_put_noidle(twl->dev); 80762306a36Sopenharmony_ci pm_runtime_mark_last_busy(twl->dev); 80862306a36Sopenharmony_ci pm_runtime_dont_use_autosuspend(&pdev->dev); 80962306a36Sopenharmony_ci pm_runtime_put_sync(twl->dev); 81062306a36Sopenharmony_ci pm_runtime_disable(twl->dev); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci /* autogate 60MHz ULPI clock, 81362306a36Sopenharmony_ci * clear dpll clock request for i2c access, 81462306a36Sopenharmony_ci * disable 32KHz 81562306a36Sopenharmony_ci */ 81662306a36Sopenharmony_ci val = twl4030_usb_read(twl, PHY_CLK_CTRL); 81762306a36Sopenharmony_ci if (val >= 0) { 81862306a36Sopenharmony_ci val |= PHY_CLK_CTRL_CLOCKGATING_EN; 81962306a36Sopenharmony_ci val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK); 82062306a36Sopenharmony_ci twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci /* disable complete OTG block */ 82462306a36Sopenharmony_ci twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci#ifdef CONFIG_OF 82862306a36Sopenharmony_cistatic const struct of_device_id twl4030_usb_id_table[] = { 82962306a36Sopenharmony_ci { .compatible = "ti,twl4030-usb" }, 83062306a36Sopenharmony_ci {} 83162306a36Sopenharmony_ci}; 83262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, twl4030_usb_id_table); 83362306a36Sopenharmony_ci#endif 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic struct platform_driver twl4030_usb_driver = { 83662306a36Sopenharmony_ci .probe = twl4030_usb_probe, 83762306a36Sopenharmony_ci .remove_new = twl4030_usb_remove, 83862306a36Sopenharmony_ci .driver = { 83962306a36Sopenharmony_ci .name = "twl4030_usb", 84062306a36Sopenharmony_ci .pm = &twl4030_usb_pm_ops, 84162306a36Sopenharmony_ci .of_match_table = of_match_ptr(twl4030_usb_id_table), 84262306a36Sopenharmony_ci }, 84362306a36Sopenharmony_ci}; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_cistatic int __init twl4030_usb_init(void) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci return platform_driver_register(&twl4030_usb_driver); 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_cisubsys_initcall(twl4030_usb_init); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic void __exit twl4030_usb_exit(void) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci platform_driver_unregister(&twl4030_usb_driver); 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_cimodule_exit(twl4030_usb_exit); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ciMODULE_ALIAS("platform:twl4030_usb"); 85862306a36Sopenharmony_ciMODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation"); 85962306a36Sopenharmony_ciMODULE_DESCRIPTION("TWL4030 USB transceiver driver"); 86062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 861