1*f6ce6072SVignesh Raghavendra // SPDX-License-Identifier: GPL-2.0 2*f6ce6072SVignesh Raghavendra /* 3*f6ce6072SVignesh Raghavendra * Cadence USBSS DRD Driver. 4*f6ce6072SVignesh Raghavendra * 5*f6ce6072SVignesh Raghavendra * Copyright (C) 2018-2019 Cadence. 6*f6ce6072SVignesh Raghavendra * Copyright (C) 2019 Texas Instruments 7*f6ce6072SVignesh Raghavendra * 8*f6ce6072SVignesh Raghavendra * Author: Pawel Laszczak <pawell@cadence.com> 9*f6ce6072SVignesh Raghavendra * Roger Quadros <rogerq@ti.com> 10*f6ce6072SVignesh Raghavendra * 11*f6ce6072SVignesh Raghavendra * 12*f6ce6072SVignesh Raghavendra */ 13*f6ce6072SVignesh Raghavendra #include <dm.h> 14*f6ce6072SVignesh Raghavendra #include <linux/delay.h> 15*f6ce6072SVignesh Raghavendra #include <linux/iopoll.h> 16*f6ce6072SVignesh Raghavendra #include <linux/kernel.h> 17*f6ce6072SVignesh Raghavendra #include <linux/usb/otg.h> 18*f6ce6072SVignesh Raghavendra 19*f6ce6072SVignesh Raghavendra #include "gadget.h" 20*f6ce6072SVignesh Raghavendra #include "drd.h" 21*f6ce6072SVignesh Raghavendra #include "core.h" 22*f6ce6072SVignesh Raghavendra 23*f6ce6072SVignesh Raghavendra #define readl_poll_timeout_atomic readl_poll_timeout 24*f6ce6072SVignesh Raghavendra #define usleep_range(a, b) udelay((b)) 25*f6ce6072SVignesh Raghavendra /** 26*f6ce6072SVignesh Raghavendra * cdns3_set_mode - change mode of OTG Core 27*f6ce6072SVignesh Raghavendra * @cdns: pointer to context structure 28*f6ce6072SVignesh Raghavendra * @mode: selected mode from cdns_role 29*f6ce6072SVignesh Raghavendra * 30*f6ce6072SVignesh Raghavendra * Returns 0 on success otherwise negative errno 31*f6ce6072SVignesh Raghavendra */ 32*f6ce6072SVignesh Raghavendra int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode) 33*f6ce6072SVignesh Raghavendra { 34*f6ce6072SVignesh Raghavendra int ret = 0; 35*f6ce6072SVignesh Raghavendra u32 reg; 36*f6ce6072SVignesh Raghavendra 37*f6ce6072SVignesh Raghavendra switch (mode) { 38*f6ce6072SVignesh Raghavendra case USB_DR_MODE_PERIPHERAL: 39*f6ce6072SVignesh Raghavendra break; 40*f6ce6072SVignesh Raghavendra case USB_DR_MODE_HOST: 41*f6ce6072SVignesh Raghavendra break; 42*f6ce6072SVignesh Raghavendra case USB_DR_MODE_OTG: 43*f6ce6072SVignesh Raghavendra dev_dbg(cdns->dev, "Set controller to OTG mode\n"); 44*f6ce6072SVignesh Raghavendra if (cdns->version == CDNS3_CONTROLLER_V1) { 45*f6ce6072SVignesh Raghavendra reg = readl(&cdns->otg_v1_regs->override); 46*f6ce6072SVignesh Raghavendra reg |= OVERRIDE_IDPULLUP; 47*f6ce6072SVignesh Raghavendra writel(reg, &cdns->otg_v1_regs->override); 48*f6ce6072SVignesh Raghavendra } else { 49*f6ce6072SVignesh Raghavendra reg = readl(&cdns->otg_v0_regs->ctrl1); 50*f6ce6072SVignesh Raghavendra reg |= OVERRIDE_IDPULLUP_V0; 51*f6ce6072SVignesh Raghavendra writel(reg, &cdns->otg_v0_regs->ctrl1); 52*f6ce6072SVignesh Raghavendra } 53*f6ce6072SVignesh Raghavendra 54*f6ce6072SVignesh Raghavendra /* 55*f6ce6072SVignesh Raghavendra * Hardware specification says: "ID_VALUE must be valid within 56*f6ce6072SVignesh Raghavendra * 50ms after idpullup is set to '1" so driver must wait 57*f6ce6072SVignesh Raghavendra * 50ms before reading this pin. 58*f6ce6072SVignesh Raghavendra */ 59*f6ce6072SVignesh Raghavendra usleep_range(50000, 60000); 60*f6ce6072SVignesh Raghavendra break; 61*f6ce6072SVignesh Raghavendra default: 62*f6ce6072SVignesh Raghavendra dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode); 63*f6ce6072SVignesh Raghavendra return -EINVAL; 64*f6ce6072SVignesh Raghavendra } 65*f6ce6072SVignesh Raghavendra 66*f6ce6072SVignesh Raghavendra return ret; 67*f6ce6072SVignesh Raghavendra } 68*f6ce6072SVignesh Raghavendra 69*f6ce6072SVignesh Raghavendra int cdns3_get_id(struct cdns3 *cdns) 70*f6ce6072SVignesh Raghavendra { 71*f6ce6072SVignesh Raghavendra int id; 72*f6ce6072SVignesh Raghavendra 73*f6ce6072SVignesh Raghavendra id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE; 74*f6ce6072SVignesh Raghavendra dev_dbg(cdns->dev, "OTG ID: %d", id); 75*f6ce6072SVignesh Raghavendra 76*f6ce6072SVignesh Raghavendra return id; 77*f6ce6072SVignesh Raghavendra } 78*f6ce6072SVignesh Raghavendra 79*f6ce6072SVignesh Raghavendra int cdns3_get_vbus(struct cdns3 *cdns) 80*f6ce6072SVignesh Raghavendra { 81*f6ce6072SVignesh Raghavendra int vbus; 82*f6ce6072SVignesh Raghavendra 83*f6ce6072SVignesh Raghavendra vbus = !!(readl(&cdns->otg_regs->sts) & OTGSTS_VBUS_VALID); 84*f6ce6072SVignesh Raghavendra dev_dbg(cdns->dev, "OTG VBUS: %d", vbus); 85*f6ce6072SVignesh Raghavendra 86*f6ce6072SVignesh Raghavendra return vbus; 87*f6ce6072SVignesh Raghavendra } 88*f6ce6072SVignesh Raghavendra 89*f6ce6072SVignesh Raghavendra int cdns3_is_host(struct cdns3 *cdns) 90*f6ce6072SVignesh Raghavendra { 91*f6ce6072SVignesh Raghavendra if (cdns->dr_mode == USB_DR_MODE_HOST) 92*f6ce6072SVignesh Raghavendra return 1; 93*f6ce6072SVignesh Raghavendra else if (!cdns3_get_id(cdns)) 94*f6ce6072SVignesh Raghavendra return 1; 95*f6ce6072SVignesh Raghavendra 96*f6ce6072SVignesh Raghavendra return 0; 97*f6ce6072SVignesh Raghavendra } 98*f6ce6072SVignesh Raghavendra 99*f6ce6072SVignesh Raghavendra int cdns3_is_device(struct cdns3 *cdns) 100*f6ce6072SVignesh Raghavendra { 101*f6ce6072SVignesh Raghavendra if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) 102*f6ce6072SVignesh Raghavendra return 1; 103*f6ce6072SVignesh Raghavendra else if (cdns->dr_mode == USB_DR_MODE_OTG) 104*f6ce6072SVignesh Raghavendra if (cdns3_get_id(cdns)) 105*f6ce6072SVignesh Raghavendra return 1; 106*f6ce6072SVignesh Raghavendra 107*f6ce6072SVignesh Raghavendra return 0; 108*f6ce6072SVignesh Raghavendra } 109*f6ce6072SVignesh Raghavendra 110*f6ce6072SVignesh Raghavendra /** 111*f6ce6072SVignesh Raghavendra * cdns3_drd_switch_host - start/stop host 112*f6ce6072SVignesh Raghavendra * @cdns: Pointer to controller context structure 113*f6ce6072SVignesh Raghavendra * @on: 1 for start, 0 for stop 114*f6ce6072SVignesh Raghavendra * 115*f6ce6072SVignesh Raghavendra * Returns 0 on success otherwise negative errno 116*f6ce6072SVignesh Raghavendra */ 117*f6ce6072SVignesh Raghavendra int cdns3_drd_switch_host(struct cdns3 *cdns, int on) 118*f6ce6072SVignesh Raghavendra { 119*f6ce6072SVignesh Raghavendra int ret, val; 120*f6ce6072SVignesh Raghavendra u32 reg = OTGCMD_OTG_DIS; 121*f6ce6072SVignesh Raghavendra 122*f6ce6072SVignesh Raghavendra /* switch OTG core */ 123*f6ce6072SVignesh Raghavendra if (on) { 124*f6ce6072SVignesh Raghavendra writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd); 125*f6ce6072SVignesh Raghavendra 126*f6ce6072SVignesh Raghavendra dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n"); 127*f6ce6072SVignesh Raghavendra ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val, 128*f6ce6072SVignesh Raghavendra val & OTGSTS_XHCI_READY, 129*f6ce6072SVignesh Raghavendra 100000); 130*f6ce6072SVignesh Raghavendra if (ret) { 131*f6ce6072SVignesh Raghavendra dev_err(cdns->dev, "timeout waiting for xhci_ready\n"); 132*f6ce6072SVignesh Raghavendra return ret; 133*f6ce6072SVignesh Raghavendra } 134*f6ce6072SVignesh Raghavendra } else { 135*f6ce6072SVignesh Raghavendra writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | 136*f6ce6072SVignesh Raghavendra OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, 137*f6ce6072SVignesh Raghavendra &cdns->otg_regs->cmd); 138*f6ce6072SVignesh Raghavendra /* Waiting till H_IDLE state.*/ 139*f6ce6072SVignesh Raghavendra readl_poll_timeout_atomic(&cdns->otg_regs->state, val, 140*f6ce6072SVignesh Raghavendra !(val & OTGSTATE_HOST_STATE_MASK), 141*f6ce6072SVignesh Raghavendra 2000000); 142*f6ce6072SVignesh Raghavendra } 143*f6ce6072SVignesh Raghavendra 144*f6ce6072SVignesh Raghavendra return 0; 145*f6ce6072SVignesh Raghavendra } 146*f6ce6072SVignesh Raghavendra 147*f6ce6072SVignesh Raghavendra /** 148*f6ce6072SVignesh Raghavendra * cdns3_drd_switch_gadget - start/stop gadget 149*f6ce6072SVignesh Raghavendra * @cdns: Pointer to controller context structure 150*f6ce6072SVignesh Raghavendra * @on: 1 for start, 0 for stop 151*f6ce6072SVignesh Raghavendra * 152*f6ce6072SVignesh Raghavendra * Returns 0 on success otherwise negative errno 153*f6ce6072SVignesh Raghavendra */ 154*f6ce6072SVignesh Raghavendra int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on) 155*f6ce6072SVignesh Raghavendra { 156*f6ce6072SVignesh Raghavendra int ret, val; 157*f6ce6072SVignesh Raghavendra u32 reg = OTGCMD_OTG_DIS; 158*f6ce6072SVignesh Raghavendra 159*f6ce6072SVignesh Raghavendra /* switch OTG core */ 160*f6ce6072SVignesh Raghavendra if (on) { 161*f6ce6072SVignesh Raghavendra writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd); 162*f6ce6072SVignesh Raghavendra 163*f6ce6072SVignesh Raghavendra dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n"); 164*f6ce6072SVignesh Raghavendra 165*f6ce6072SVignesh Raghavendra ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val, 166*f6ce6072SVignesh Raghavendra val & OTGSTS_DEV_READY, 167*f6ce6072SVignesh Raghavendra 100000); 168*f6ce6072SVignesh Raghavendra if (ret) { 169*f6ce6072SVignesh Raghavendra dev_err(cdns->dev, "timeout waiting for dev_ready\n"); 170*f6ce6072SVignesh Raghavendra return ret; 171*f6ce6072SVignesh Raghavendra } 172*f6ce6072SVignesh Raghavendra } else { 173*f6ce6072SVignesh Raghavendra /* 174*f6ce6072SVignesh Raghavendra * driver should wait at least 10us after disabling Device 175*f6ce6072SVignesh Raghavendra * before turning-off Device (DEV_BUS_DROP) 176*f6ce6072SVignesh Raghavendra */ 177*f6ce6072SVignesh Raghavendra usleep_range(20, 30); 178*f6ce6072SVignesh Raghavendra writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP | 179*f6ce6072SVignesh Raghavendra OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF, 180*f6ce6072SVignesh Raghavendra &cdns->otg_regs->cmd); 181*f6ce6072SVignesh Raghavendra /* Waiting till DEV_IDLE state.*/ 182*f6ce6072SVignesh Raghavendra readl_poll_timeout_atomic(&cdns->otg_regs->state, val, 183*f6ce6072SVignesh Raghavendra !(val & OTGSTATE_DEV_STATE_MASK), 184*f6ce6072SVignesh Raghavendra 2000000); 185*f6ce6072SVignesh Raghavendra } 186*f6ce6072SVignesh Raghavendra 187*f6ce6072SVignesh Raghavendra return 0; 188*f6ce6072SVignesh Raghavendra } 189*f6ce6072SVignesh Raghavendra 190*f6ce6072SVignesh Raghavendra /** 191*f6ce6072SVignesh Raghavendra * cdns3_init_otg_mode - initialize drd controller 192*f6ce6072SVignesh Raghavendra * @cdns: Pointer to controller context structure 193*f6ce6072SVignesh Raghavendra * 194*f6ce6072SVignesh Raghavendra * Returns 0 on success otherwise negative errno 195*f6ce6072SVignesh Raghavendra */ 196*f6ce6072SVignesh Raghavendra static int cdns3_init_otg_mode(struct cdns3 *cdns) 197*f6ce6072SVignesh Raghavendra { 198*f6ce6072SVignesh Raghavendra int ret = 0; 199*f6ce6072SVignesh Raghavendra 200*f6ce6072SVignesh Raghavendra /* clear all interrupts */ 201*f6ce6072SVignesh Raghavendra writel(~0, &cdns->otg_regs->ivect); 202*f6ce6072SVignesh Raghavendra 203*f6ce6072SVignesh Raghavendra ret = cdns3_set_mode(cdns, USB_DR_MODE_OTG); 204*f6ce6072SVignesh Raghavendra if (ret) 205*f6ce6072SVignesh Raghavendra return ret; 206*f6ce6072SVignesh Raghavendra 207*f6ce6072SVignesh Raghavendra return ret; 208*f6ce6072SVignesh Raghavendra } 209*f6ce6072SVignesh Raghavendra 210*f6ce6072SVignesh Raghavendra /** 211*f6ce6072SVignesh Raghavendra * cdns3_drd_update_mode - initialize mode of operation 212*f6ce6072SVignesh Raghavendra * @cdns: Pointer to controller context structure 213*f6ce6072SVignesh Raghavendra * 214*f6ce6072SVignesh Raghavendra * Returns 0 on success otherwise negative errno 215*f6ce6072SVignesh Raghavendra */ 216*f6ce6072SVignesh Raghavendra int cdns3_drd_update_mode(struct cdns3 *cdns) 217*f6ce6072SVignesh Raghavendra { 218*f6ce6072SVignesh Raghavendra int ret = 0; 219*f6ce6072SVignesh Raghavendra 220*f6ce6072SVignesh Raghavendra switch (cdns->dr_mode) { 221*f6ce6072SVignesh Raghavendra case USB_DR_MODE_PERIPHERAL: 222*f6ce6072SVignesh Raghavendra ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL); 223*f6ce6072SVignesh Raghavendra break; 224*f6ce6072SVignesh Raghavendra case USB_DR_MODE_HOST: 225*f6ce6072SVignesh Raghavendra ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST); 226*f6ce6072SVignesh Raghavendra break; 227*f6ce6072SVignesh Raghavendra case USB_DR_MODE_OTG: 228*f6ce6072SVignesh Raghavendra ret = cdns3_init_otg_mode(cdns); 229*f6ce6072SVignesh Raghavendra break; 230*f6ce6072SVignesh Raghavendra default: 231*f6ce6072SVignesh Raghavendra dev_err(cdns->dev, "Unsupported mode of operation %d\n", 232*f6ce6072SVignesh Raghavendra cdns->dr_mode); 233*f6ce6072SVignesh Raghavendra return -EINVAL; 234*f6ce6072SVignesh Raghavendra } 235*f6ce6072SVignesh Raghavendra 236*f6ce6072SVignesh Raghavendra return ret; 237*f6ce6072SVignesh Raghavendra } 238*f6ce6072SVignesh Raghavendra 239*f6ce6072SVignesh Raghavendra int cdns3_drd_init(struct cdns3 *cdns) 240*f6ce6072SVignesh Raghavendra { 241*f6ce6072SVignesh Raghavendra void __iomem *regs; 242*f6ce6072SVignesh Raghavendra int ret = 0; 243*f6ce6072SVignesh Raghavendra u32 state; 244*f6ce6072SVignesh Raghavendra 245*f6ce6072SVignesh Raghavendra regs = dev_remap_addr_name(cdns->dev, "otg"); 246*f6ce6072SVignesh Raghavendra if (!regs) 247*f6ce6072SVignesh Raghavendra return -EINVAL; 248*f6ce6072SVignesh Raghavendra 249*f6ce6072SVignesh Raghavendra /* Detection of DRD version. Controller has been released 250*f6ce6072SVignesh Raghavendra * in two versions. Both are similar, but they have same changes 251*f6ce6072SVignesh Raghavendra * in register maps. 252*f6ce6072SVignesh Raghavendra * The first register in old version is command register and it's read 253*f6ce6072SVignesh Raghavendra * only, so driver should read 0 from it. On the other hand, in v1 254*f6ce6072SVignesh Raghavendra * the first register contains device ID number which is not set to 0. 255*f6ce6072SVignesh Raghavendra * Driver uses this fact to detect the proper version of 256*f6ce6072SVignesh Raghavendra * controller. 257*f6ce6072SVignesh Raghavendra */ 258*f6ce6072SVignesh Raghavendra cdns->otg_v0_regs = regs; 259*f6ce6072SVignesh Raghavendra if (!readl(&cdns->otg_v0_regs->cmd)) { 260*f6ce6072SVignesh Raghavendra cdns->version = CDNS3_CONTROLLER_V0; 261*f6ce6072SVignesh Raghavendra cdns->otg_v1_regs = NULL; 262*f6ce6072SVignesh Raghavendra cdns->otg_regs = regs; 263*f6ce6072SVignesh Raghavendra writel(1, &cdns->otg_v0_regs->simulate); 264*f6ce6072SVignesh Raghavendra dev_info(cdns->dev, "DRD version v0 (%08x)\n", 265*f6ce6072SVignesh Raghavendra readl(&cdns->otg_v0_regs->version)); 266*f6ce6072SVignesh Raghavendra } else { 267*f6ce6072SVignesh Raghavendra cdns->otg_v0_regs = NULL; 268*f6ce6072SVignesh Raghavendra cdns->otg_v1_regs = regs; 269*f6ce6072SVignesh Raghavendra cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd; 270*f6ce6072SVignesh Raghavendra cdns->version = CDNS3_CONTROLLER_V1; 271*f6ce6072SVignesh Raghavendra writel(1, &cdns->otg_v1_regs->simulate); 272*f6ce6072SVignesh Raghavendra dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n", 273*f6ce6072SVignesh Raghavendra readl(&cdns->otg_v1_regs->did), 274*f6ce6072SVignesh Raghavendra readl(&cdns->otg_v1_regs->rid)); 275*f6ce6072SVignesh Raghavendra } 276*f6ce6072SVignesh Raghavendra 277*f6ce6072SVignesh Raghavendra state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts)); 278*f6ce6072SVignesh Raghavendra 279*f6ce6072SVignesh Raghavendra /* Update dr_mode according to STRAP configuration. */ 280*f6ce6072SVignesh Raghavendra cdns->dr_mode = USB_DR_MODE_OTG; 281*f6ce6072SVignesh Raghavendra if (state == OTGSTS_STRAP_HOST) { 282*f6ce6072SVignesh Raghavendra dev_dbg(cdns->dev, "Controller strapped to HOST\n"); 283*f6ce6072SVignesh Raghavendra cdns->dr_mode = USB_DR_MODE_HOST; 284*f6ce6072SVignesh Raghavendra } else if (state == OTGSTS_STRAP_GADGET) { 285*f6ce6072SVignesh Raghavendra dev_dbg(cdns->dev, "Controller strapped to PERIPHERAL\n"); 286*f6ce6072SVignesh Raghavendra cdns->dr_mode = USB_DR_MODE_PERIPHERAL; 287*f6ce6072SVignesh Raghavendra } 288*f6ce6072SVignesh Raghavendra 289*f6ce6072SVignesh Raghavendra state = readl(&cdns->otg_regs->sts); 290*f6ce6072SVignesh Raghavendra if (OTGSTS_OTG_NRDY(state) != 0) { 291*f6ce6072SVignesh Raghavendra dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n"); 292*f6ce6072SVignesh Raghavendra return -ENODEV; 293*f6ce6072SVignesh Raghavendra } 294*f6ce6072SVignesh Raghavendra 295*f6ce6072SVignesh Raghavendra return ret; 296*f6ce6072SVignesh Raghavendra } 297*f6ce6072SVignesh Raghavendra 298*f6ce6072SVignesh Raghavendra int cdns3_drd_exit(struct cdns3 *cdns) 299*f6ce6072SVignesh Raghavendra { 300*f6ce6072SVignesh Raghavendra return 0; 301*f6ce6072SVignesh Raghavendra } 302