162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Cadence USBSS and USBSSP DRD Driver. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2018-2020 Cadence. 662306a36Sopenharmony_ci * Copyright (C) 2019 Texas Instruments 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Pawel Laszczak <pawell@cadence.com> 962306a36Sopenharmony_ci * Roger Quadros <rogerq@ti.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/iopoll.h> 1662306a36Sopenharmony_ci#include <linux/usb/otg.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "drd.h" 1962306a36Sopenharmony_ci#include "core.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/** 2262306a36Sopenharmony_ci * cdns_set_mode - change mode of OTG Core 2362306a36Sopenharmony_ci * @cdns: pointer to context structure 2462306a36Sopenharmony_ci * @mode: selected mode from cdns_role 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Returns 0 on success otherwise negative errno 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistatic int cdns_set_mode(struct cdns *cdns, enum usb_dr_mode mode) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci void __iomem *override_reg; 3162306a36Sopenharmony_ci u32 reg; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci switch (mode) { 3462306a36Sopenharmony_ci case USB_DR_MODE_PERIPHERAL: 3562306a36Sopenharmony_ci break; 3662306a36Sopenharmony_ci case USB_DR_MODE_HOST: 3762306a36Sopenharmony_ci break; 3862306a36Sopenharmony_ci case USB_DR_MODE_OTG: 3962306a36Sopenharmony_ci dev_dbg(cdns->dev, "Set controller to OTG mode\n"); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (cdns->version == CDNSP_CONTROLLER_V2) 4262306a36Sopenharmony_ci override_reg = &cdns->otg_cdnsp_regs->override; 4362306a36Sopenharmony_ci else if (cdns->version == CDNS3_CONTROLLER_V1) 4462306a36Sopenharmony_ci override_reg = &cdns->otg_v1_regs->override; 4562306a36Sopenharmony_ci else 4662306a36Sopenharmony_ci override_reg = &cdns->otg_v0_regs->ctrl1; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci reg = readl(override_reg); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (cdns->version != CDNS3_CONTROLLER_V0) 5162306a36Sopenharmony_ci reg |= OVERRIDE_IDPULLUP; 5262306a36Sopenharmony_ci else 5362306a36Sopenharmony_ci reg |= OVERRIDE_IDPULLUP_V0; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci writel(reg, override_reg); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (cdns->version == CDNS3_CONTROLLER_V1) { 5862306a36Sopenharmony_ci /* 5962306a36Sopenharmony_ci * Enable work around feature built into the 6062306a36Sopenharmony_ci * controller to address issue with RX Sensitivity 6162306a36Sopenharmony_ci * est (EL_17) for USB2 PHY. The issue only occures 6262306a36Sopenharmony_ci * for 0x0002450D controller version. 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_ci if (cdns->phyrst_a_enable) { 6562306a36Sopenharmony_ci reg = readl(&cdns->otg_v1_regs->phyrst_cfg); 6662306a36Sopenharmony_ci reg |= PHYRST_CFG_PHYRST_A_ENABLE; 6762306a36Sopenharmony_ci writel(reg, &cdns->otg_v1_regs->phyrst_cfg); 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * Hardware specification says: "ID_VALUE must be valid within 7362306a36Sopenharmony_ci * 50ms after idpullup is set to '1" so driver must wait 7462306a36Sopenharmony_ci * 50ms before reading this pin. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci usleep_range(50000, 60000); 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci default: 7962306a36Sopenharmony_ci dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode); 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ciint cdns_get_id(struct cdns *cdns) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci int id; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE; 9162306a36Sopenharmony_ci dev_dbg(cdns->dev, "OTG ID: %d", id); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return id; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciint cdns_get_vbus(struct cdns *cdns) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci int vbus; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci vbus = !!(readl(&cdns->otg_regs->sts) & OTGSTS_VBUS_VALID); 10162306a36Sopenharmony_ci dev_dbg(cdns->dev, "OTG VBUS: %d", vbus); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return vbus; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_civoid cdns_clear_vbus(struct cdns *cdns) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci u32 reg; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (cdns->version != CDNSP_CONTROLLER_V2) 11162306a36Sopenharmony_ci return; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci reg = readl(&cdns->otg_cdnsp_regs->override); 11462306a36Sopenharmony_ci reg |= OVERRIDE_SESS_VLD_SEL; 11562306a36Sopenharmony_ci writel(reg, &cdns->otg_cdnsp_regs->override); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdns_clear_vbus); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_civoid cdns_set_vbus(struct cdns *cdns) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci u32 reg; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (cdns->version != CDNSP_CONTROLLER_V2) 12462306a36Sopenharmony_ci return; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci reg = readl(&cdns->otg_cdnsp_regs->override); 12762306a36Sopenharmony_ci reg &= ~OVERRIDE_SESS_VLD_SEL; 12862306a36Sopenharmony_ci writel(reg, &cdns->otg_cdnsp_regs->override); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdns_set_vbus); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cibool cdns_is_host(struct cdns *cdns) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci if (cdns->dr_mode == USB_DR_MODE_HOST) 13562306a36Sopenharmony_ci return true; 13662306a36Sopenharmony_ci else if (cdns_get_id(cdns) == CDNS3_ID_HOST) 13762306a36Sopenharmony_ci return true; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci return false; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cibool cdns_is_device(struct cdns *cdns) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) 14562306a36Sopenharmony_ci return true; 14662306a36Sopenharmony_ci else if (cdns->dr_mode == USB_DR_MODE_OTG) 14762306a36Sopenharmony_ci if (cdns_get_id(cdns) == CDNS3_ID_PERIPHERAL) 14862306a36Sopenharmony_ci return true; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return false; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/** 15462306a36Sopenharmony_ci * cdns_otg_disable_irq - Disable all OTG interrupts 15562306a36Sopenharmony_ci * @cdns: Pointer to controller context structure 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistatic void cdns_otg_disable_irq(struct cdns *cdns) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci if (cdns->version) 16062306a36Sopenharmony_ci writel(0, &cdns->otg_irq_regs->ien); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/** 16462306a36Sopenharmony_ci * cdns_otg_enable_irq - enable id and sess_valid interrupts 16562306a36Sopenharmony_ci * @cdns: Pointer to controller context structure 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_cistatic void cdns_otg_enable_irq(struct cdns *cdns) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT | 17062306a36Sopenharmony_ci OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_irq_regs->ien); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci/** 17462306a36Sopenharmony_ci * cdns_drd_host_on - start host. 17562306a36Sopenharmony_ci * @cdns: Pointer to controller context structure. 17662306a36Sopenharmony_ci * 17762306a36Sopenharmony_ci * Returns 0 on success otherwise negative errno. 17862306a36Sopenharmony_ci */ 17962306a36Sopenharmony_ciint cdns_drd_host_on(struct cdns *cdns) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci u32 val, ready_bit; 18262306a36Sopenharmony_ci int ret; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* Enable host mode. */ 18562306a36Sopenharmony_ci writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS, 18662306a36Sopenharmony_ci &cdns->otg_regs->cmd); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (cdns->version == CDNSP_CONTROLLER_V2) 18962306a36Sopenharmony_ci ready_bit = OTGSTS_CDNSP_XHCI_READY; 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci ready_bit = OTGSTS_CDNS3_XHCI_READY; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n"); 19462306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val, 19562306a36Sopenharmony_ci val & ready_bit, 1, 100000); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (ret) 19862306a36Sopenharmony_ci dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_HOST); 20162306a36Sopenharmony_ci phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST); 20262306a36Sopenharmony_ci return ret; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/** 20662306a36Sopenharmony_ci * cdns_drd_host_off - stop host. 20762306a36Sopenharmony_ci * @cdns: Pointer to controller context structure. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_civoid cdns_drd_host_off(struct cdns *cdns) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci u32 val; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | 21462306a36Sopenharmony_ci OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, 21562306a36Sopenharmony_ci &cdns->otg_regs->cmd); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* Waiting till H_IDLE state.*/ 21862306a36Sopenharmony_ci readl_poll_timeout_atomic(&cdns->otg_regs->state, val, 21962306a36Sopenharmony_ci !(val & OTGSTATE_HOST_STATE_MASK), 22062306a36Sopenharmony_ci 1, 2000000); 22162306a36Sopenharmony_ci phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID); 22262306a36Sopenharmony_ci phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/** 22662306a36Sopenharmony_ci * cdns_drd_gadget_on - start gadget. 22762306a36Sopenharmony_ci * @cdns: Pointer to controller context structure. 22862306a36Sopenharmony_ci * 22962306a36Sopenharmony_ci * Returns 0 on success otherwise negative errno 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ciint cdns_drd_gadget_on(struct cdns *cdns) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci u32 reg = OTGCMD_OTG_DIS; 23462306a36Sopenharmony_ci u32 ready_bit; 23562306a36Sopenharmony_ci int ret, val; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* switch OTG core */ 23862306a36Sopenharmony_ci writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n"); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (cdns->version == CDNSP_CONTROLLER_V2) 24362306a36Sopenharmony_ci ready_bit = OTGSTS_CDNSP_DEV_READY; 24462306a36Sopenharmony_ci else 24562306a36Sopenharmony_ci ready_bit = OTGSTS_CDNS3_DEV_READY; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val, 24862306a36Sopenharmony_ci val & ready_bit, 1, 100000); 24962306a36Sopenharmony_ci if (ret) { 25062306a36Sopenharmony_ci dev_err(cdns->dev, "timeout waiting for dev_ready\n"); 25162306a36Sopenharmony_ci return ret; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_DEVICE); 25562306a36Sopenharmony_ci phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE); 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdns_drd_gadget_on); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/** 26162306a36Sopenharmony_ci * cdns_drd_gadget_off - stop gadget. 26262306a36Sopenharmony_ci * @cdns: Pointer to controller context structure. 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_civoid cdns_drd_gadget_off(struct cdns *cdns) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci u32 val; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * Driver should wait at least 10us after disabling Device 27062306a36Sopenharmony_ci * before turning-off Device (DEV_BUS_DROP). 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci usleep_range(20, 30); 27362306a36Sopenharmony_ci writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | 27462306a36Sopenharmony_ci OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, 27562306a36Sopenharmony_ci &cdns->otg_regs->cmd); 27662306a36Sopenharmony_ci /* Waiting till DEV_IDLE state.*/ 27762306a36Sopenharmony_ci readl_poll_timeout_atomic(&cdns->otg_regs->state, val, 27862306a36Sopenharmony_ci !(val & OTGSTATE_DEV_STATE_MASK), 27962306a36Sopenharmony_ci 1, 2000000); 28062306a36Sopenharmony_ci phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID); 28162306a36Sopenharmony_ci phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdns_drd_gadget_off); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/** 28662306a36Sopenharmony_ci * cdns_init_otg_mode - initialize drd controller 28762306a36Sopenharmony_ci * @cdns: Pointer to controller context structure 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci * Returns 0 on success otherwise negative errno 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_cistatic int cdns_init_otg_mode(struct cdns *cdns) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci int ret; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci cdns_otg_disable_irq(cdns); 29662306a36Sopenharmony_ci /* clear all interrupts */ 29762306a36Sopenharmony_ci writel(~0, &cdns->otg_irq_regs->ivect); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ret = cdns_set_mode(cdns, USB_DR_MODE_OTG); 30062306a36Sopenharmony_ci if (ret) 30162306a36Sopenharmony_ci return ret; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci cdns_otg_enable_irq(cdns); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/** 30962306a36Sopenharmony_ci * cdns_drd_update_mode - initialize mode of operation 31062306a36Sopenharmony_ci * @cdns: Pointer to controller context structure 31162306a36Sopenharmony_ci * 31262306a36Sopenharmony_ci * Returns 0 on success otherwise negative errno 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ciint cdns_drd_update_mode(struct cdns *cdns) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci int ret; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci switch (cdns->dr_mode) { 31962306a36Sopenharmony_ci case USB_DR_MODE_PERIPHERAL: 32062306a36Sopenharmony_ci ret = cdns_set_mode(cdns, USB_DR_MODE_PERIPHERAL); 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci case USB_DR_MODE_HOST: 32362306a36Sopenharmony_ci ret = cdns_set_mode(cdns, USB_DR_MODE_HOST); 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci case USB_DR_MODE_OTG: 32662306a36Sopenharmony_ci ret = cdns_init_otg_mode(cdns); 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci default: 32962306a36Sopenharmony_ci dev_err(cdns->dev, "Unsupported mode of operation %d\n", 33062306a36Sopenharmony_ci cdns->dr_mode); 33162306a36Sopenharmony_ci return -EINVAL; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return ret; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic irqreturn_t cdns_drd_thread_irq(int irq, void *data) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct cdns *cdns = data; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci cdns_hw_role_switch(cdns); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci return IRQ_HANDLED; 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/** 34762306a36Sopenharmony_ci * cdns_drd_irq - interrupt handler for OTG events 34862306a36Sopenharmony_ci * 34962306a36Sopenharmony_ci * @irq: irq number for cdns core device 35062306a36Sopenharmony_ci * @data: structure of cdns 35162306a36Sopenharmony_ci * 35262306a36Sopenharmony_ci * Returns IRQ_HANDLED or IRQ_NONE 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_cistatic irqreturn_t cdns_drd_irq(int irq, void *data) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 35762306a36Sopenharmony_ci struct cdns *cdns = data; 35862306a36Sopenharmony_ci u32 reg; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (cdns->dr_mode != USB_DR_MODE_OTG) 36162306a36Sopenharmony_ci return IRQ_NONE; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (cdns->in_lpm) 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci reg = readl(&cdns->otg_irq_regs->ivect); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (!reg) 36962306a36Sopenharmony_ci return IRQ_NONE; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (reg & OTGIEN_ID_CHANGE_INT) { 37262306a36Sopenharmony_ci dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n", 37362306a36Sopenharmony_ci cdns_get_id(cdns)); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ret = IRQ_WAKE_THREAD; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci if (reg & (OTGIEN_VBUSVALID_RISE_INT | OTGIEN_VBUSVALID_FALL_INT)) { 37962306a36Sopenharmony_ci dev_dbg(cdns->dev, "OTG IRQ: new VBUS: %d\n", 38062306a36Sopenharmony_ci cdns_get_vbus(cdns)); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ret = IRQ_WAKE_THREAD; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci writel(~0, &cdns->otg_irq_regs->ivect); 38662306a36Sopenharmony_ci return ret; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ciint cdns_drd_init(struct cdns *cdns) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci void __iomem *regs; 39262306a36Sopenharmony_ci u32 state; 39362306a36Sopenharmony_ci int ret; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res); 39662306a36Sopenharmony_ci if (IS_ERR(regs)) 39762306a36Sopenharmony_ci return PTR_ERR(regs); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Detection of DRD version. Controller has been released 40062306a36Sopenharmony_ci * in three versions. All are very similar and are software compatible, 40162306a36Sopenharmony_ci * but they have same changes in register maps. 40262306a36Sopenharmony_ci * The first register in oldest version is command register and it's 40362306a36Sopenharmony_ci * read only. Driver should read 0 from it. On the other hand, in v1 40462306a36Sopenharmony_ci * and v2 the first register contains device ID number which is not 40562306a36Sopenharmony_ci * set to 0. Driver uses this fact to detect the proper version of 40662306a36Sopenharmony_ci * controller. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci cdns->otg_v0_regs = regs; 40962306a36Sopenharmony_ci if (!readl(&cdns->otg_v0_regs->cmd)) { 41062306a36Sopenharmony_ci cdns->version = CDNS3_CONTROLLER_V0; 41162306a36Sopenharmony_ci cdns->otg_v1_regs = NULL; 41262306a36Sopenharmony_ci cdns->otg_cdnsp_regs = NULL; 41362306a36Sopenharmony_ci cdns->otg_regs = regs; 41462306a36Sopenharmony_ci cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *) 41562306a36Sopenharmony_ci &cdns->otg_v0_regs->ien; 41662306a36Sopenharmony_ci writel(1, &cdns->otg_v0_regs->simulate); 41762306a36Sopenharmony_ci dev_dbg(cdns->dev, "DRD version v0 (%08x)\n", 41862306a36Sopenharmony_ci readl(&cdns->otg_v0_regs->version)); 41962306a36Sopenharmony_ci } else { 42062306a36Sopenharmony_ci cdns->otg_v0_regs = NULL; 42162306a36Sopenharmony_ci cdns->otg_v1_regs = regs; 42262306a36Sopenharmony_ci cdns->otg_cdnsp_regs = regs; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci cdns->otg_regs = (void __iomem *)&cdns->otg_v1_regs->cmd; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci state = readl(&cdns->otg_cdnsp_regs->did); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (OTG_CDNSP_CHECK_DID(state)) { 42962306a36Sopenharmony_ci cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *) 43062306a36Sopenharmony_ci &cdns->otg_cdnsp_regs->ien; 43162306a36Sopenharmony_ci cdns->version = CDNSP_CONTROLLER_V2; 43262306a36Sopenharmony_ci } else if (OTG_CDNS3_CHECK_DID(state)) { 43362306a36Sopenharmony_ci cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *) 43462306a36Sopenharmony_ci &cdns->otg_v1_regs->ien; 43562306a36Sopenharmony_ci writel(1, &cdns->otg_v1_regs->simulate); 43662306a36Sopenharmony_ci cdns->version = CDNS3_CONTROLLER_V1; 43762306a36Sopenharmony_ci } else { 43862306a36Sopenharmony_ci dev_err(cdns->dev, "not supporte DID=0x%08x\n", state); 43962306a36Sopenharmony_ci return -EINVAL; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci dev_dbg(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n", 44362306a36Sopenharmony_ci readl(&cdns->otg_v1_regs->did), 44462306a36Sopenharmony_ci readl(&cdns->otg_v1_regs->rid)); 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts)); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* Update dr_mode according to STRAP configuration. */ 45062306a36Sopenharmony_ci cdns->dr_mode = USB_DR_MODE_OTG; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if ((cdns->version == CDNSP_CONTROLLER_V2 && 45362306a36Sopenharmony_ci state == OTGSTS_CDNSP_STRAP_HOST) || 45462306a36Sopenharmony_ci (cdns->version != CDNSP_CONTROLLER_V2 && 45562306a36Sopenharmony_ci state == OTGSTS_STRAP_HOST)) { 45662306a36Sopenharmony_ci dev_dbg(cdns->dev, "Controller strapped to HOST\n"); 45762306a36Sopenharmony_ci cdns->dr_mode = USB_DR_MODE_HOST; 45862306a36Sopenharmony_ci } else if ((cdns->version == CDNSP_CONTROLLER_V2 && 45962306a36Sopenharmony_ci state == OTGSTS_CDNSP_STRAP_GADGET) || 46062306a36Sopenharmony_ci (cdns->version != CDNSP_CONTROLLER_V2 && 46162306a36Sopenharmony_ci state == OTGSTS_STRAP_GADGET)) { 46262306a36Sopenharmony_ci dev_dbg(cdns->dev, "Controller strapped to PERIPHERAL\n"); 46362306a36Sopenharmony_ci cdns->dr_mode = USB_DR_MODE_PERIPHERAL; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci ret = devm_request_threaded_irq(cdns->dev, cdns->otg_irq, 46762306a36Sopenharmony_ci cdns_drd_irq, 46862306a36Sopenharmony_ci cdns_drd_thread_irq, 46962306a36Sopenharmony_ci IRQF_SHARED, 47062306a36Sopenharmony_ci dev_name(cdns->dev), cdns); 47162306a36Sopenharmony_ci if (ret) { 47262306a36Sopenharmony_ci dev_err(cdns->dev, "couldn't get otg_irq\n"); 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci state = readl(&cdns->otg_regs->sts); 47762306a36Sopenharmony_ci if (OTGSTS_OTG_NRDY(state)) { 47862306a36Sopenharmony_ci dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n"); 47962306a36Sopenharmony_ci return -ENODEV; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return 0; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ciint cdns_drd_exit(struct cdns *cdns) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci cdns_otg_disable_irq(cdns); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci/* Indicate the cdns3 core was power lost before */ 49362306a36Sopenharmony_cibool cdns_power_is_lost(struct cdns *cdns) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci if (cdns->version == CDNS3_CONTROLLER_V0) { 49662306a36Sopenharmony_ci if (!(readl(&cdns->otg_v0_regs->simulate) & BIT(0))) 49762306a36Sopenharmony_ci return true; 49862306a36Sopenharmony_ci } else { 49962306a36Sopenharmony_ci if (!(readl(&cdns->otg_v1_regs->simulate) & BIT(0))) 50062306a36Sopenharmony_ci return true; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci return false; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cdns_power_is_lost); 505