162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Motorola CPCAP PMIC USB PHY driver 462306a36Sopenharmony_ci * Copyright (C) 2017 Tony Lindgren <tony@atomide.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Some parts based on earlier Motorola Linux kernel tree code in 762306a36Sopenharmony_ci * board-mapphone-usb.c and cpcap-usb-det.c: 862306a36Sopenharmony_ci * Copyright (C) 2007 - 2011 Motorola, Inc. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/atomic.h> 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/err.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/of_platform.h> 1962306a36Sopenharmony_ci#include <linux/iio/consumer.h> 2062306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 2162306a36Sopenharmony_ci#include <linux/platform_device.h> 2262306a36Sopenharmony_ci#include <linux/regmap.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 2662306a36Sopenharmony_ci#include <linux/mfd/motorola-cpcap.h> 2762306a36Sopenharmony_ci#include <linux/phy/omap_usb.h> 2862306a36Sopenharmony_ci#include <linux/phy/phy.h> 2962306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 3062306a36Sopenharmony_ci#include <linux/usb/musb.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* CPCAP_REG_USBC1 register bits */ 3362306a36Sopenharmony_ci#define CPCAP_BIT_IDPULSE BIT(15) 3462306a36Sopenharmony_ci#define CPCAP_BIT_ID100KPU BIT(14) 3562306a36Sopenharmony_ci#define CPCAP_BIT_IDPUCNTRL BIT(13) 3662306a36Sopenharmony_ci#define CPCAP_BIT_IDPU BIT(12) 3762306a36Sopenharmony_ci#define CPCAP_BIT_IDPD BIT(11) 3862306a36Sopenharmony_ci#define CPCAP_BIT_VBUSCHRGTMR3 BIT(10) 3962306a36Sopenharmony_ci#define CPCAP_BIT_VBUSCHRGTMR2 BIT(9) 4062306a36Sopenharmony_ci#define CPCAP_BIT_VBUSCHRGTMR1 BIT(8) 4162306a36Sopenharmony_ci#define CPCAP_BIT_VBUSCHRGTMR0 BIT(7) 4262306a36Sopenharmony_ci#define CPCAP_BIT_VBUSPU BIT(6) 4362306a36Sopenharmony_ci#define CPCAP_BIT_VBUSPD BIT(5) 4462306a36Sopenharmony_ci#define CPCAP_BIT_DMPD BIT(4) 4562306a36Sopenharmony_ci#define CPCAP_BIT_DPPD BIT(3) 4662306a36Sopenharmony_ci#define CPCAP_BIT_DM1K5PU BIT(2) 4762306a36Sopenharmony_ci#define CPCAP_BIT_DP1K5PU BIT(1) 4862306a36Sopenharmony_ci#define CPCAP_BIT_DP150KPU BIT(0) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* CPCAP_REG_USBC2 register bits */ 5162306a36Sopenharmony_ci#define CPCAP_BIT_ZHSDRV1 BIT(15) 5262306a36Sopenharmony_ci#define CPCAP_BIT_ZHSDRV0 BIT(14) 5362306a36Sopenharmony_ci#define CPCAP_BIT_DPLLCLKREQ BIT(13) 5462306a36Sopenharmony_ci#define CPCAP_BIT_SE0CONN BIT(12) 5562306a36Sopenharmony_ci#define CPCAP_BIT_UARTTXTRI BIT(11) 5662306a36Sopenharmony_ci#define CPCAP_BIT_UARTSWAP BIT(10) 5762306a36Sopenharmony_ci#define CPCAP_BIT_UARTMUX1 BIT(9) 5862306a36Sopenharmony_ci#define CPCAP_BIT_UARTMUX0 BIT(8) 5962306a36Sopenharmony_ci#define CPCAP_BIT_ULPISTPLOW BIT(7) 6062306a36Sopenharmony_ci#define CPCAP_BIT_TXENPOL BIT(6) 6162306a36Sopenharmony_ci#define CPCAP_BIT_USBXCVREN BIT(5) 6262306a36Sopenharmony_ci#define CPCAP_BIT_USBCNTRL BIT(4) 6362306a36Sopenharmony_ci#define CPCAP_BIT_USBSUSPEND BIT(3) 6462306a36Sopenharmony_ci#define CPCAP_BIT_EMUMODE2 BIT(2) 6562306a36Sopenharmony_ci#define CPCAP_BIT_EMUMODE1 BIT(1) 6662306a36Sopenharmony_ci#define CPCAP_BIT_EMUMODE0 BIT(0) 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci/* CPCAP_REG_USBC3 register bits */ 6962306a36Sopenharmony_ci#define CPCAP_BIT_SPARE_898_15 BIT(15) 7062306a36Sopenharmony_ci#define CPCAP_BIT_IHSTX03 BIT(14) 7162306a36Sopenharmony_ci#define CPCAP_BIT_IHSTX02 BIT(13) 7262306a36Sopenharmony_ci#define CPCAP_BIT_IHSTX01 BIT(12) 7362306a36Sopenharmony_ci#define CPCAP_BIT_IHSTX0 BIT(11) 7462306a36Sopenharmony_ci#define CPCAP_BIT_IDPU_SPI BIT(10) 7562306a36Sopenharmony_ci#define CPCAP_BIT_UNUSED_898_9 BIT(9) 7662306a36Sopenharmony_ci#define CPCAP_BIT_VBUSSTBY_EN BIT(8) 7762306a36Sopenharmony_ci#define CPCAP_BIT_VBUSEN_SPI BIT(7) 7862306a36Sopenharmony_ci#define CPCAP_BIT_VBUSPU_SPI BIT(6) 7962306a36Sopenharmony_ci#define CPCAP_BIT_VBUSPD_SPI BIT(5) 8062306a36Sopenharmony_ci#define CPCAP_BIT_DMPD_SPI BIT(4) 8162306a36Sopenharmony_ci#define CPCAP_BIT_DPPD_SPI BIT(3) 8262306a36Sopenharmony_ci#define CPCAP_BIT_SUSPEND_SPI BIT(2) 8362306a36Sopenharmony_ci#define CPCAP_BIT_PU_SPI BIT(1) 8462306a36Sopenharmony_ci#define CPCAP_BIT_ULPI_SPI_SEL BIT(0) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistruct cpcap_usb_ints_state { 8762306a36Sopenharmony_ci bool id_ground; 8862306a36Sopenharmony_ci bool id_float; 8962306a36Sopenharmony_ci bool chrg_det; 9062306a36Sopenharmony_ci bool rvrs_chrg; 9162306a36Sopenharmony_ci bool vbusov; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci bool chrg_se1b; 9462306a36Sopenharmony_ci bool se0conn; 9562306a36Sopenharmony_ci bool rvrs_mode; 9662306a36Sopenharmony_ci bool chrgcurr1; 9762306a36Sopenharmony_ci bool vbusvld; 9862306a36Sopenharmony_ci bool sessvld; 9962306a36Sopenharmony_ci bool sessend; 10062306a36Sopenharmony_ci bool se1; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci bool battdetb; 10362306a36Sopenharmony_ci bool dm; 10462306a36Sopenharmony_ci bool dp; 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cienum cpcap_gpio_mode { 10862306a36Sopenharmony_ci CPCAP_DM_DP, 10962306a36Sopenharmony_ci CPCAP_MDM_RX_TX, 11062306a36Sopenharmony_ci CPCAP_UNKNOWN_DISABLED, /* Seems to disable USB lines */ 11162306a36Sopenharmony_ci CPCAP_OTG_DM_DP, 11262306a36Sopenharmony_ci}; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistruct cpcap_phy_ddata { 11562306a36Sopenharmony_ci struct regmap *reg; 11662306a36Sopenharmony_ci struct device *dev; 11762306a36Sopenharmony_ci struct usb_phy phy; 11862306a36Sopenharmony_ci struct delayed_work detect_work; 11962306a36Sopenharmony_ci struct pinctrl *pins; 12062306a36Sopenharmony_ci struct pinctrl_state *pins_ulpi; 12162306a36Sopenharmony_ci struct pinctrl_state *pins_utmi; 12262306a36Sopenharmony_ci struct pinctrl_state *pins_uart; 12362306a36Sopenharmony_ci struct gpio_desc *gpio[2]; 12462306a36Sopenharmony_ci struct iio_channel *vbus; 12562306a36Sopenharmony_ci struct iio_channel *id; 12662306a36Sopenharmony_ci struct regulator *vusb; 12762306a36Sopenharmony_ci atomic_t active; 12862306a36Sopenharmony_ci unsigned int vbus_provider:1; 12962306a36Sopenharmony_ci unsigned int docked:1; 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic bool cpcap_usb_vbus_valid(struct cpcap_phy_ddata *ddata) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci int error, value = 0; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci error = iio_read_channel_processed(ddata->vbus, &value); 13762306a36Sopenharmony_ci if (error >= 0) 13862306a36Sopenharmony_ci return value > 3900; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci dev_err(ddata->dev, "error reading VBUS: %i\n", error); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return false; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int cpcap_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci otg->host = host; 14862306a36Sopenharmony_ci if (!host) 14962306a36Sopenharmony_ci otg->state = OTG_STATE_UNDEFINED; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int cpcap_usb_phy_set_peripheral(struct usb_otg *otg, 15562306a36Sopenharmony_ci struct usb_gadget *gadget) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci otg->gadget = gadget; 15862306a36Sopenharmony_ci if (!gadget) 15962306a36Sopenharmony_ci otg->state = OTG_STATE_UNDEFINED; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return 0; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic const struct phy_ops ops = { 16562306a36Sopenharmony_ci .owner = THIS_MODULE, 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int cpcap_phy_get_ints_state(struct cpcap_phy_ddata *ddata, 16962306a36Sopenharmony_ci struct cpcap_usb_ints_state *s) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci int val, error; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci error = regmap_read(ddata->reg, CPCAP_REG_INTS1, &val); 17462306a36Sopenharmony_ci if (error) 17562306a36Sopenharmony_ci return error; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci s->id_ground = val & BIT(15); 17862306a36Sopenharmony_ci s->id_float = val & BIT(14); 17962306a36Sopenharmony_ci s->vbusov = val & BIT(11); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci error = regmap_read(ddata->reg, CPCAP_REG_INTS2, &val); 18262306a36Sopenharmony_ci if (error) 18362306a36Sopenharmony_ci return error; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci s->vbusvld = val & BIT(3); 18662306a36Sopenharmony_ci s->sessvld = val & BIT(2); 18762306a36Sopenharmony_ci s->sessend = val & BIT(1); 18862306a36Sopenharmony_ci s->se1 = val & BIT(0); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci error = regmap_read(ddata->reg, CPCAP_REG_INTS4, &val); 19162306a36Sopenharmony_ci if (error) 19262306a36Sopenharmony_ci return error; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci s->dm = val & BIT(1); 19562306a36Sopenharmony_ci s->dp = val & BIT(0); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return 0; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic int cpcap_usb_set_uart_mode(struct cpcap_phy_ddata *ddata); 20162306a36Sopenharmony_cistatic int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void cpcap_usb_try_musb_mailbox(struct cpcap_phy_ddata *ddata, 20462306a36Sopenharmony_ci enum musb_vbus_id_status status) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int error; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci error = musb_mailbox(status); 20962306a36Sopenharmony_ci if (!error) 21062306a36Sopenharmony_ci return; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci dev_dbg(ddata->dev, "%s: musb_mailbox failed: %i\n", 21362306a36Sopenharmony_ci __func__, error); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void cpcap_usb_detect(struct work_struct *work) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct cpcap_phy_ddata *ddata; 21962306a36Sopenharmony_ci struct cpcap_usb_ints_state s; 22062306a36Sopenharmony_ci bool vbus = false; 22162306a36Sopenharmony_ci int error; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ddata = container_of(work, struct cpcap_phy_ddata, detect_work.work); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci error = cpcap_phy_get_ints_state(ddata, &s); 22662306a36Sopenharmony_ci if (error) 22762306a36Sopenharmony_ci return; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci vbus = cpcap_usb_vbus_valid(ddata); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* We need to kick the VBUS as USB A-host */ 23262306a36Sopenharmony_ci if (s.id_ground && ddata->vbus_provider) { 23362306a36Sopenharmony_ci dev_dbg(ddata->dev, "still in USB A-host mode, kicking VBUS\n"); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 23862306a36Sopenharmony_ci CPCAP_BIT_VBUSSTBY_EN | 23962306a36Sopenharmony_ci CPCAP_BIT_VBUSEN_SPI, 24062306a36Sopenharmony_ci CPCAP_BIT_VBUSEN_SPI); 24162306a36Sopenharmony_ci if (error) 24262306a36Sopenharmony_ci goto out_err; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (vbus && s.id_ground && ddata->docked) { 24862306a36Sopenharmony_ci dev_dbg(ddata->dev, "still docked as A-host, signal ID down\n"); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* No VBUS needed with docks */ 25662306a36Sopenharmony_ci if (vbus && s.id_ground && !ddata->vbus_provider) { 25762306a36Sopenharmony_ci dev_dbg(ddata->dev, "connected to a dock\n"); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ddata->docked = true; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci error = cpcap_usb_set_usb_mode(ddata); 26262306a36Sopenharmony_ci if (error) 26362306a36Sopenharmony_ci goto out_err; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* 26862306a36Sopenharmony_ci * Force check state again after musb has reoriented, 26962306a36Sopenharmony_ci * otherwise devices won't enumerate after loading PHY 27062306a36Sopenharmony_ci * driver. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci schedule_delayed_work(&ddata->detect_work, 27362306a36Sopenharmony_ci msecs_to_jiffies(1000)); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (s.id_ground && !ddata->docked) { 27962306a36Sopenharmony_ci dev_dbg(ddata->dev, "id ground, USB host mode\n"); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci ddata->vbus_provider = true; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci error = cpcap_usb_set_usb_mode(ddata); 28462306a36Sopenharmony_ci if (error) 28562306a36Sopenharmony_ci goto out_err; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_ID_GROUND); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 29062306a36Sopenharmony_ci CPCAP_BIT_VBUSSTBY_EN | 29162306a36Sopenharmony_ci CPCAP_BIT_VBUSEN_SPI, 29262306a36Sopenharmony_ci CPCAP_BIT_VBUSEN_SPI); 29362306a36Sopenharmony_ci if (error) 29462306a36Sopenharmony_ci goto out_err; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 30062306a36Sopenharmony_ci CPCAP_BIT_VBUSSTBY_EN | 30162306a36Sopenharmony_ci CPCAP_BIT_VBUSEN_SPI, 0); 30262306a36Sopenharmony_ci if (error) 30362306a36Sopenharmony_ci goto out_err; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci vbus = cpcap_usb_vbus_valid(ddata); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Otherwise assume we're connected to a USB host */ 30862306a36Sopenharmony_ci if (vbus) { 30962306a36Sopenharmony_ci dev_dbg(ddata->dev, "connected to USB host\n"); 31062306a36Sopenharmony_ci error = cpcap_usb_set_usb_mode(ddata); 31162306a36Sopenharmony_ci if (error) 31262306a36Sopenharmony_ci goto out_err; 31362306a36Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_VALID); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci ddata->vbus_provider = false; 31962306a36Sopenharmony_ci ddata->docked = false; 32062306a36Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* Default to debug UART mode */ 32362306a36Sopenharmony_ci error = cpcap_usb_set_uart_mode(ddata); 32462306a36Sopenharmony_ci if (error) 32562306a36Sopenharmony_ci goto out_err; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci dev_dbg(ddata->dev, "set UART mode\n"); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ciout_err: 33262306a36Sopenharmony_ci dev_err(ddata->dev, "error setting cable state: %i\n", error); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic irqreturn_t cpcap_phy_irq_thread(int irq, void *data) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct cpcap_phy_ddata *ddata = data; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (!atomic_read(&ddata->active)) 34062306a36Sopenharmony_ci return IRQ_NONE; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci schedule_delayed_work(&ddata->detect_work, msecs_to_jiffies(1)); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return IRQ_HANDLED; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int cpcap_usb_init_irq(struct platform_device *pdev, 34862306a36Sopenharmony_ci struct cpcap_phy_ddata *ddata, 34962306a36Sopenharmony_ci const char *name) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci int irq, error; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, name); 35462306a36Sopenharmony_ci if (irq < 0) 35562306a36Sopenharmony_ci return -ENODEV; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci error = devm_request_threaded_irq(ddata->dev, irq, NULL, 35862306a36Sopenharmony_ci cpcap_phy_irq_thread, 35962306a36Sopenharmony_ci IRQF_SHARED | 36062306a36Sopenharmony_ci IRQF_ONESHOT, 36162306a36Sopenharmony_ci name, ddata); 36262306a36Sopenharmony_ci if (error) { 36362306a36Sopenharmony_ci dev_err(ddata->dev, "could not get irq %s: %i\n", 36462306a36Sopenharmony_ci name, error); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return error; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return 0; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic const char * const cpcap_phy_irqs[] = { 37362306a36Sopenharmony_ci /* REG_INT_0 */ 37462306a36Sopenharmony_ci "id_ground", "id_float", 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* REG_INT1 */ 37762306a36Sopenharmony_ci "se0conn", "vbusvld", "sessvld", "sessend", "se1", 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* REG_INT_3 */ 38062306a36Sopenharmony_ci "dm", "dp", 38162306a36Sopenharmony_ci}; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int cpcap_usb_init_interrupts(struct platform_device *pdev, 38462306a36Sopenharmony_ci struct cpcap_phy_ddata *ddata) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci int i, error; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cpcap_phy_irqs); i++) { 38962306a36Sopenharmony_ci error = cpcap_usb_init_irq(pdev, ddata, cpcap_phy_irqs[i]); 39062306a36Sopenharmony_ci if (error) 39162306a36Sopenharmony_ci return error; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/* 39862306a36Sopenharmony_ci * Optional pins and modes. At least Motorola mapphone devices 39962306a36Sopenharmony_ci * are using two GPIOs and dynamic pinctrl to multiplex PHY pins 40062306a36Sopenharmony_ci * to UART, ULPI or UTMI mode. 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int cpcap_usb_gpio_set_mode(struct cpcap_phy_ddata *ddata, 40462306a36Sopenharmony_ci enum cpcap_gpio_mode mode) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci if (!ddata->gpio[0] || !ddata->gpio[1]) 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci gpiod_set_value(ddata->gpio[0], mode & 1); 41062306a36Sopenharmony_ci gpiod_set_value(ddata->gpio[1], mode >> 1); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return 0; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic int cpcap_usb_set_uart_mode(struct cpcap_phy_ddata *ddata) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci int error; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Disable lines to prevent glitches from waking up mdm6600 */ 42062306a36Sopenharmony_ci error = cpcap_usb_gpio_set_mode(ddata, CPCAP_UNKNOWN_DISABLED); 42162306a36Sopenharmony_ci if (error) 42262306a36Sopenharmony_ci goto out_err; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci if (ddata->pins_uart) { 42562306a36Sopenharmony_ci error = pinctrl_select_state(ddata->pins, ddata->pins_uart); 42662306a36Sopenharmony_ci if (error) 42762306a36Sopenharmony_ci goto out_err; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC1, 43162306a36Sopenharmony_ci CPCAP_BIT_VBUSPD, 43262306a36Sopenharmony_ci CPCAP_BIT_VBUSPD); 43362306a36Sopenharmony_ci if (error) 43462306a36Sopenharmony_ci goto out_err; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2, 43762306a36Sopenharmony_ci 0xffff, CPCAP_BIT_UARTMUX0 | 43862306a36Sopenharmony_ci CPCAP_BIT_EMUMODE0); 43962306a36Sopenharmony_ci if (error) 44062306a36Sopenharmony_ci goto out_err; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 0x7fff, 44362306a36Sopenharmony_ci CPCAP_BIT_IDPU_SPI); 44462306a36Sopenharmony_ci if (error) 44562306a36Sopenharmony_ci goto out_err; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Enable UART mode */ 44862306a36Sopenharmony_ci error = cpcap_usb_gpio_set_mode(ddata, CPCAP_DM_DP); 44962306a36Sopenharmony_ci if (error) 45062306a36Sopenharmony_ci goto out_err; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciout_err: 45562306a36Sopenharmony_ci dev_err(ddata->dev, "%s failed with %i\n", __func__, error); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return error; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int cpcap_usb_set_usb_mode(struct cpcap_phy_ddata *ddata) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci int error; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* Disable lines to prevent glitches from waking up mdm6600 */ 46562306a36Sopenharmony_ci error = cpcap_usb_gpio_set_mode(ddata, CPCAP_UNKNOWN_DISABLED); 46662306a36Sopenharmony_ci if (error) 46762306a36Sopenharmony_ci return error; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (ddata->pins_utmi) { 47062306a36Sopenharmony_ci error = pinctrl_select_state(ddata->pins, ddata->pins_utmi); 47162306a36Sopenharmony_ci if (error) { 47262306a36Sopenharmony_ci dev_err(ddata->dev, "could not set usb mode: %i\n", 47362306a36Sopenharmony_ci error); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return error; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC1, 48062306a36Sopenharmony_ci CPCAP_BIT_VBUSPD, 0); 48162306a36Sopenharmony_ci if (error) 48262306a36Sopenharmony_ci goto out_err; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC3, 48562306a36Sopenharmony_ci CPCAP_BIT_PU_SPI | 48662306a36Sopenharmony_ci CPCAP_BIT_DMPD_SPI | 48762306a36Sopenharmony_ci CPCAP_BIT_DPPD_SPI | 48862306a36Sopenharmony_ci CPCAP_BIT_SUSPEND_SPI | 48962306a36Sopenharmony_ci CPCAP_BIT_ULPI_SPI_SEL, 0); 49062306a36Sopenharmony_ci if (error) 49162306a36Sopenharmony_ci goto out_err; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci error = regmap_update_bits(ddata->reg, CPCAP_REG_USBC2, 49462306a36Sopenharmony_ci CPCAP_BIT_USBXCVREN, 49562306a36Sopenharmony_ci CPCAP_BIT_USBXCVREN); 49662306a36Sopenharmony_ci if (error) 49762306a36Sopenharmony_ci goto out_err; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* Enable USB mode */ 50062306a36Sopenharmony_ci error = cpcap_usb_gpio_set_mode(ddata, CPCAP_OTG_DM_DP); 50162306a36Sopenharmony_ci if (error) 50262306a36Sopenharmony_ci goto out_err; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ciout_err: 50762306a36Sopenharmony_ci dev_err(ddata->dev, "%s failed with %i\n", __func__, error); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return error; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cistatic int cpcap_usb_init_optional_pins(struct cpcap_phy_ddata *ddata) 51362306a36Sopenharmony_ci{ 51462306a36Sopenharmony_ci ddata->pins = devm_pinctrl_get(ddata->dev); 51562306a36Sopenharmony_ci if (IS_ERR(ddata->pins)) { 51662306a36Sopenharmony_ci dev_info(ddata->dev, "default pins not configured: %ld\n", 51762306a36Sopenharmony_ci PTR_ERR(ddata->pins)); 51862306a36Sopenharmony_ci ddata->pins = NULL; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return 0; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ddata->pins_ulpi = pinctrl_lookup_state(ddata->pins, "ulpi"); 52462306a36Sopenharmony_ci if (IS_ERR(ddata->pins_ulpi)) { 52562306a36Sopenharmony_ci dev_info(ddata->dev, "ulpi pins not configured\n"); 52662306a36Sopenharmony_ci ddata->pins_ulpi = NULL; 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci ddata->pins_utmi = pinctrl_lookup_state(ddata->pins, "utmi"); 53062306a36Sopenharmony_ci if (IS_ERR(ddata->pins_utmi)) { 53162306a36Sopenharmony_ci dev_info(ddata->dev, "utmi pins not configured\n"); 53262306a36Sopenharmony_ci ddata->pins_utmi = NULL; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci ddata->pins_uart = pinctrl_lookup_state(ddata->pins, "uart"); 53662306a36Sopenharmony_ci if (IS_ERR(ddata->pins_uart)) { 53762306a36Sopenharmony_ci dev_info(ddata->dev, "uart pins not configured\n"); 53862306a36Sopenharmony_ci ddata->pins_uart = NULL; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci if (ddata->pins_uart) 54262306a36Sopenharmony_ci return pinctrl_select_state(ddata->pins, ddata->pins_uart); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic void cpcap_usb_init_optional_gpios(struct cpcap_phy_ddata *ddata) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci int i; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 55262306a36Sopenharmony_ci ddata->gpio[i] = devm_gpiod_get_index(ddata->dev, "mode", 55362306a36Sopenharmony_ci i, GPIOD_OUT_HIGH); 55462306a36Sopenharmony_ci if (IS_ERR(ddata->gpio[i])) { 55562306a36Sopenharmony_ci dev_info(ddata->dev, "no mode change GPIO%i: %li\n", 55662306a36Sopenharmony_ci i, PTR_ERR(ddata->gpio[i])); 55762306a36Sopenharmony_ci ddata->gpio[i] = NULL; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int cpcap_usb_init_iio(struct cpcap_phy_ddata *ddata) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci enum iio_chan_type type; 56562306a36Sopenharmony_ci int error; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci ddata->vbus = devm_iio_channel_get(ddata->dev, "vbus"); 56862306a36Sopenharmony_ci if (IS_ERR(ddata->vbus)) { 56962306a36Sopenharmony_ci error = PTR_ERR(ddata->vbus); 57062306a36Sopenharmony_ci goto out_err; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (!ddata->vbus->indio_dev) { 57462306a36Sopenharmony_ci error = -ENXIO; 57562306a36Sopenharmony_ci goto out_err; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci error = iio_get_channel_type(ddata->vbus, &type); 57962306a36Sopenharmony_ci if (error < 0) 58062306a36Sopenharmony_ci goto out_err; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (type != IIO_VOLTAGE) { 58362306a36Sopenharmony_ci error = -EINVAL; 58462306a36Sopenharmony_ci goto out_err; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ciout_err: 59062306a36Sopenharmony_ci dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n", 59162306a36Sopenharmony_ci error); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return error; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci#ifdef CONFIG_OF 59762306a36Sopenharmony_cistatic const struct of_device_id cpcap_usb_phy_id_table[] = { 59862306a36Sopenharmony_ci { 59962306a36Sopenharmony_ci .compatible = "motorola,cpcap-usb-phy", 60062306a36Sopenharmony_ci }, 60162306a36Sopenharmony_ci { 60262306a36Sopenharmony_ci .compatible = "motorola,mapphone-cpcap-usb-phy", 60362306a36Sopenharmony_ci }, 60462306a36Sopenharmony_ci {}, 60562306a36Sopenharmony_ci}; 60662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cpcap_usb_phy_id_table); 60762306a36Sopenharmony_ci#endif 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int cpcap_usb_phy_probe(struct platform_device *pdev) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct cpcap_phy_ddata *ddata; 61262306a36Sopenharmony_ci struct phy *generic_phy; 61362306a36Sopenharmony_ci struct phy_provider *phy_provider; 61462306a36Sopenharmony_ci struct usb_otg *otg; 61562306a36Sopenharmony_ci const struct of_device_id *of_id; 61662306a36Sopenharmony_ci int error; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci of_id = of_match_device(of_match_ptr(cpcap_usb_phy_id_table), 61962306a36Sopenharmony_ci &pdev->dev); 62062306a36Sopenharmony_ci if (!of_id) 62162306a36Sopenharmony_ci return -EINVAL; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); 62462306a36Sopenharmony_ci if (!ddata) 62562306a36Sopenharmony_ci return -ENOMEM; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci ddata->reg = dev_get_regmap(pdev->dev.parent, NULL); 62862306a36Sopenharmony_ci if (!ddata->reg) 62962306a36Sopenharmony_ci return -ENODEV; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); 63262306a36Sopenharmony_ci if (!otg) 63362306a36Sopenharmony_ci return -ENOMEM; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci ddata->dev = &pdev->dev; 63662306a36Sopenharmony_ci ddata->phy.dev = ddata->dev; 63762306a36Sopenharmony_ci ddata->phy.label = "cpcap_usb_phy"; 63862306a36Sopenharmony_ci ddata->phy.otg = otg; 63962306a36Sopenharmony_ci ddata->phy.type = USB_PHY_TYPE_USB2; 64062306a36Sopenharmony_ci otg->set_host = cpcap_usb_phy_set_host; 64162306a36Sopenharmony_ci otg->set_peripheral = cpcap_usb_phy_set_peripheral; 64262306a36Sopenharmony_ci otg->usb_phy = &ddata->phy; 64362306a36Sopenharmony_ci INIT_DELAYED_WORK(&ddata->detect_work, cpcap_usb_detect); 64462306a36Sopenharmony_ci platform_set_drvdata(pdev, ddata); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ddata->vusb = devm_regulator_get(&pdev->dev, "vusb"); 64762306a36Sopenharmony_ci if (IS_ERR(ddata->vusb)) 64862306a36Sopenharmony_ci return PTR_ERR(ddata->vusb); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci error = regulator_enable(ddata->vusb); 65162306a36Sopenharmony_ci if (error) 65262306a36Sopenharmony_ci return error; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci generic_phy = devm_phy_create(ddata->dev, NULL, &ops); 65562306a36Sopenharmony_ci if (IS_ERR(generic_phy)) { 65662306a36Sopenharmony_ci error = PTR_ERR(generic_phy); 65762306a36Sopenharmony_ci goto out_reg_disable; 65862306a36Sopenharmony_ci } 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci phy_set_drvdata(generic_phy, ddata); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci phy_provider = devm_of_phy_provider_register(ddata->dev, 66362306a36Sopenharmony_ci of_phy_simple_xlate); 66462306a36Sopenharmony_ci if (IS_ERR(phy_provider)) { 66562306a36Sopenharmony_ci error = PTR_ERR(phy_provider); 66662306a36Sopenharmony_ci goto out_reg_disable; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci error = cpcap_usb_init_optional_pins(ddata); 67062306a36Sopenharmony_ci if (error) 67162306a36Sopenharmony_ci goto out_reg_disable; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci cpcap_usb_init_optional_gpios(ddata); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci error = cpcap_usb_init_iio(ddata); 67662306a36Sopenharmony_ci if (error) 67762306a36Sopenharmony_ci goto out_reg_disable; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci error = cpcap_usb_init_interrupts(pdev, ddata); 68062306a36Sopenharmony_ci if (error) 68162306a36Sopenharmony_ci goto out_reg_disable; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci usb_add_phy_dev(&ddata->phy); 68462306a36Sopenharmony_ci atomic_set(&ddata->active, 1); 68562306a36Sopenharmony_ci schedule_delayed_work(&ddata->detect_work, msecs_to_jiffies(1)); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci return 0; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ciout_reg_disable: 69062306a36Sopenharmony_ci regulator_disable(ddata->vusb); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return error; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic void cpcap_usb_phy_remove(struct platform_device *pdev) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct cpcap_phy_ddata *ddata = platform_get_drvdata(pdev); 69862306a36Sopenharmony_ci int error; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci atomic_set(&ddata->active, 0); 70162306a36Sopenharmony_ci error = cpcap_usb_set_uart_mode(ddata); 70262306a36Sopenharmony_ci if (error) 70362306a36Sopenharmony_ci dev_err(ddata->dev, "could not set UART mode\n"); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci cpcap_usb_try_musb_mailbox(ddata, MUSB_VBUS_OFF); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci usb_remove_phy(&ddata->phy); 70862306a36Sopenharmony_ci cancel_delayed_work_sync(&ddata->detect_work); 70962306a36Sopenharmony_ci regulator_disable(ddata->vusb); 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic struct platform_driver cpcap_usb_phy_driver = { 71362306a36Sopenharmony_ci .probe = cpcap_usb_phy_probe, 71462306a36Sopenharmony_ci .remove_new = cpcap_usb_phy_remove, 71562306a36Sopenharmony_ci .driver = { 71662306a36Sopenharmony_ci .name = "cpcap-usb-phy", 71762306a36Sopenharmony_ci .of_match_table = of_match_ptr(cpcap_usb_phy_id_table), 71862306a36Sopenharmony_ci }, 71962306a36Sopenharmony_ci}; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_cimodule_platform_driver(cpcap_usb_phy_driver); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ciMODULE_ALIAS("platform:cpcap_usb"); 72462306a36Sopenharmony_ciMODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); 72562306a36Sopenharmony_ciMODULE_DESCRIPTION("CPCAP usb phy driver"); 72662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 727