Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * drd.c - DesignWare USB2 DRD Controller Dual-role support
0004  *
0005  * Copyright (C) 2020 STMicroelectronics
0006  *
0007  * Author(s): Amelie Delaunay <amelie.delaunay@st.com>
0008  */
0009 
0010 #include <linux/clk.h>
0011 #include <linux/iopoll.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/usb/role.h>
0014 #include "core.h"
0015 
0016 #define dwc2_ovr_gotgctl(gotgctl) \
0017     ((gotgctl) |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN | \
0018      GOTGCTL_DBNCE_FLTR_BYPASS)
0019 
0020 static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
0021 {
0022     unsigned long flags;
0023     u32 gotgctl;
0024 
0025     spin_lock_irqsave(&hsotg->lock, flags);
0026 
0027     gotgctl = dwc2_readl(hsotg, GOTGCTL);
0028     dwc2_ovr_gotgctl(gotgctl);
0029     gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
0030     if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
0031         gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
0032     else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
0033         gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
0034     dwc2_writel(hsotg, gotgctl, GOTGCTL);
0035 
0036     spin_unlock_irqrestore(&hsotg->lock, flags);
0037 
0038     dwc2_force_mode(hsotg, (hsotg->dr_mode == USB_DR_MODE_HOST));
0039 }
0040 
0041 static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
0042 {
0043     u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
0044 
0045     /* Check if A-Session is already in the right state */
0046     if ((valid && (gotgctl & GOTGCTL_ASESVLD)) ||
0047         (!valid && !(gotgctl & GOTGCTL_ASESVLD)))
0048         return -EALREADY;
0049 
0050     /* Always enable overrides to handle the resume case */
0051     dwc2_ovr_gotgctl(gotgctl);
0052 
0053     gotgctl &= ~GOTGCTL_BVALOVAL;
0054     if (valid)
0055         gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
0056     else
0057         gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
0058     dwc2_writel(hsotg, gotgctl, GOTGCTL);
0059 
0060     return 0;
0061 }
0062 
0063 static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid)
0064 {
0065     u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
0066 
0067     /* Check if B-Session is already in the right state */
0068     if ((valid && (gotgctl & GOTGCTL_BSESVLD)) ||
0069         (!valid && !(gotgctl & GOTGCTL_BSESVLD)))
0070         return -EALREADY;
0071 
0072     /* Always enable overrides to handle the resume case */
0073     dwc2_ovr_gotgctl(gotgctl);
0074 
0075     gotgctl &= ~GOTGCTL_AVALOVAL;
0076     if (valid)
0077         gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
0078     else
0079         gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL);
0080     dwc2_writel(hsotg, gotgctl, GOTGCTL);
0081 
0082     return 0;
0083 }
0084 
0085 static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
0086 {
0087     struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw);
0088     unsigned long flags;
0089     int already = 0;
0090 
0091     /* Skip session not in line with dr_mode */
0092     if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) ||
0093         (role == USB_ROLE_HOST && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
0094         return -EINVAL;
0095 
0096 #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
0097     IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
0098     /* Skip session if core is in test mode */
0099     if (role == USB_ROLE_NONE && hsotg->test_mode) {
0100         dev_dbg(hsotg->dev, "Core is in test mode\n");
0101         return -EBUSY;
0102     }
0103 #endif
0104 
0105     /*
0106      * In case of USB_DR_MODE_PERIPHERAL, clock is disabled at the end of
0107      * the probe and enabled on udc_start.
0108      * If role-switch set is called before the udc_start, we need to enable
0109      * the clock to read/write GOTGCTL and GUSBCFG registers to override
0110      * mode and sessions. It is the case if cable is plugged at boot.
0111      */
0112     if (!hsotg->ll_hw_enabled && hsotg->clk) {
0113         int ret = clk_prepare_enable(hsotg->clk);
0114 
0115         if (ret)
0116             return ret;
0117     }
0118 
0119     spin_lock_irqsave(&hsotg->lock, flags);
0120 
0121     if (role == USB_ROLE_NONE) {
0122         /* default operation mode when usb role is USB_ROLE_NONE */
0123         if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
0124             role = USB_ROLE_HOST;
0125         else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
0126             role = USB_ROLE_DEVICE;
0127     }
0128 
0129     if (role == USB_ROLE_HOST) {
0130         already = dwc2_ovr_avalid(hsotg, true);
0131     } else if (role == USB_ROLE_DEVICE) {
0132         already = dwc2_ovr_bvalid(hsotg, true);
0133         if (dwc2_is_device_enabled(hsotg)) {
0134             /* This clear DCTL.SFTDISCON bit */
0135             dwc2_hsotg_core_connect(hsotg);
0136         }
0137     } else {
0138         if (dwc2_is_device_mode(hsotg)) {
0139             if (!dwc2_ovr_bvalid(hsotg, false))
0140                 /* This set DCTL.SFTDISCON bit */
0141                 dwc2_hsotg_core_disconnect(hsotg);
0142         } else {
0143             dwc2_ovr_avalid(hsotg, false);
0144         }
0145     }
0146 
0147     spin_unlock_irqrestore(&hsotg->lock, flags);
0148 
0149     if (!already && hsotg->dr_mode == USB_DR_MODE_OTG)
0150         /* This will raise a Connector ID Status Change Interrupt */
0151         dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
0152 
0153     if (!hsotg->ll_hw_enabled && hsotg->clk)
0154         clk_disable_unprepare(hsotg->clk);
0155 
0156     dev_dbg(hsotg->dev, "%s-session valid\n",
0157         role == USB_ROLE_NONE ? "No" :
0158         role == USB_ROLE_HOST ? "A" : "B");
0159 
0160     return 0;
0161 }
0162 
0163 int dwc2_drd_init(struct dwc2_hsotg *hsotg)
0164 {
0165     struct usb_role_switch_desc role_sw_desc = {0};
0166     struct usb_role_switch *role_sw;
0167     int ret;
0168 
0169     if (!device_property_read_bool(hsotg->dev, "usb-role-switch"))
0170         return 0;
0171 
0172     hsotg->role_sw_default_mode = usb_get_role_switch_default_mode(hsotg->dev);
0173     role_sw_desc.driver_data = hsotg;
0174     role_sw_desc.fwnode = dev_fwnode(hsotg->dev);
0175     role_sw_desc.set = dwc2_drd_role_sw_set;
0176     role_sw_desc.allow_userspace_control = true;
0177 
0178     role_sw = usb_role_switch_register(hsotg->dev, &role_sw_desc);
0179     if (IS_ERR(role_sw)) {
0180         ret = PTR_ERR(role_sw);
0181         dev_err(hsotg->dev,
0182             "failed to register role switch: %d\n", ret);
0183         return ret;
0184     }
0185 
0186     hsotg->role_sw = role_sw;
0187 
0188     /* Enable override and initialize values */
0189     dwc2_ovr_init(hsotg);
0190 
0191     return 0;
0192 }
0193 
0194 void dwc2_drd_suspend(struct dwc2_hsotg *hsotg)
0195 {
0196     u32 gintsts, gintmsk;
0197 
0198     if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
0199         gintmsk = dwc2_readl(hsotg, GINTMSK);
0200         gintmsk &= ~GINTSTS_CONIDSTSCHNG;
0201         dwc2_writel(hsotg, gintmsk, GINTMSK);
0202         gintsts = dwc2_readl(hsotg, GINTSTS);
0203         dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
0204     }
0205 }
0206 
0207 void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
0208 {
0209     u32 gintsts, gintmsk;
0210     enum usb_role role;
0211 
0212     if (hsotg->role_sw) {
0213         /* get last known role (as the get ops isn't implemented by this driver) */
0214         role = usb_role_switch_get_role(hsotg->role_sw);
0215 
0216         if (role == USB_ROLE_NONE) {
0217             if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
0218                 role = USB_ROLE_HOST;
0219             else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
0220                 role = USB_ROLE_DEVICE;
0221         }
0222 
0223         /* restore last role that may have been lost */
0224         if (role == USB_ROLE_HOST)
0225             dwc2_ovr_avalid(hsotg, true);
0226         else if (role == USB_ROLE_DEVICE)
0227             dwc2_ovr_bvalid(hsotg, true);
0228 
0229         dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
0230 
0231         dev_dbg(hsotg->dev, "resuming %s-session valid\n",
0232             role == USB_ROLE_NONE ? "No" :
0233             role == USB_ROLE_HOST ? "A" : "B");
0234     }
0235 
0236     if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
0237         gintsts = dwc2_readl(hsotg, GINTSTS);
0238         dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
0239         gintmsk = dwc2_readl(hsotg, GINTMSK);
0240         gintmsk |= GINTSTS_CONIDSTSCHNG;
0241         dwc2_writel(hsotg, gintmsk, GINTMSK);
0242     }
0243 }
0244 
0245 void dwc2_drd_exit(struct dwc2_hsotg *hsotg)
0246 {
0247     if (hsotg->role_sw)
0248         usb_role_switch_unregister(hsotg->role_sw);
0249 }