cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

drd.c (6953B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * drd.c - DesignWare USB2 DRD Controller Dual-role support
      4 *
      5 * Copyright (C) 2020 STMicroelectronics
      6 *
      7 * Author(s): Amelie Delaunay <amelie.delaunay@st.com>
      8 */
      9
     10#include <linux/clk.h>
     11#include <linux/iopoll.h>
     12#include <linux/platform_device.h>
     13#include <linux/usb/role.h>
     14#include "core.h"
     15
     16#define dwc2_ovr_gotgctl(gotgctl) \
     17	((gotgctl) |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN | \
     18	 GOTGCTL_DBNCE_FLTR_BYPASS)
     19
     20static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
     21{
     22	unsigned long flags;
     23	u32 gotgctl;
     24
     25	spin_lock_irqsave(&hsotg->lock, flags);
     26
     27	gotgctl = dwc2_readl(hsotg, GOTGCTL);
     28	dwc2_ovr_gotgctl(gotgctl);
     29	gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
     30	if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
     31		gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
     32	else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
     33		gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
     34	dwc2_writel(hsotg, gotgctl, GOTGCTL);
     35
     36	spin_unlock_irqrestore(&hsotg->lock, flags);
     37
     38	dwc2_force_mode(hsotg, (hsotg->dr_mode == USB_DR_MODE_HOST));
     39}
     40
     41static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
     42{
     43	u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
     44
     45	/* Check if A-Session is already in the right state */
     46	if ((valid && (gotgctl & GOTGCTL_ASESVLD)) ||
     47	    (!valid && !(gotgctl & GOTGCTL_ASESVLD)))
     48		return -EALREADY;
     49
     50	/* Always enable overrides to handle the resume case */
     51	dwc2_ovr_gotgctl(gotgctl);
     52
     53	gotgctl &= ~GOTGCTL_BVALOVAL;
     54	if (valid)
     55		gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
     56	else
     57		gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
     58	dwc2_writel(hsotg, gotgctl, GOTGCTL);
     59
     60	return 0;
     61}
     62
     63static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid)
     64{
     65	u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
     66
     67	/* Check if B-Session is already in the right state */
     68	if ((valid && (gotgctl & GOTGCTL_BSESVLD)) ||
     69	    (!valid && !(gotgctl & GOTGCTL_BSESVLD)))
     70		return -EALREADY;
     71
     72	/* Always enable overrides to handle the resume case */
     73	dwc2_ovr_gotgctl(gotgctl);
     74
     75	gotgctl &= ~GOTGCTL_AVALOVAL;
     76	if (valid)
     77		gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
     78	else
     79		gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL);
     80	dwc2_writel(hsotg, gotgctl, GOTGCTL);
     81
     82	return 0;
     83}
     84
     85static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
     86{
     87	struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw);
     88	unsigned long flags;
     89	int already = 0;
     90
     91	/* Skip session not in line with dr_mode */
     92	if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) ||
     93	    (role == USB_ROLE_HOST && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
     94		return -EINVAL;
     95
     96#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
     97	IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
     98	/* Skip session if core is in test mode */
     99	if (role == USB_ROLE_NONE && hsotg->test_mode) {
    100		dev_dbg(hsotg->dev, "Core is in test mode\n");
    101		return -EBUSY;
    102	}
    103#endif
    104
    105	/*
    106	 * In case of USB_DR_MODE_PERIPHERAL, clock is disabled at the end of
    107	 * the probe and enabled on udc_start.
    108	 * If role-switch set is called before the udc_start, we need to enable
    109	 * the clock to read/write GOTGCTL and GUSBCFG registers to override
    110	 * mode and sessions. It is the case if cable is plugged at boot.
    111	 */
    112	if (!hsotg->ll_hw_enabled && hsotg->clk) {
    113		int ret = clk_prepare_enable(hsotg->clk);
    114
    115		if (ret)
    116			return ret;
    117	}
    118
    119	spin_lock_irqsave(&hsotg->lock, flags);
    120
    121	if (role == USB_ROLE_NONE) {
    122		/* default operation mode when usb role is USB_ROLE_NONE */
    123		if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
    124			role = USB_ROLE_HOST;
    125		else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
    126			role = USB_ROLE_DEVICE;
    127	}
    128
    129	if (role == USB_ROLE_HOST) {
    130		already = dwc2_ovr_avalid(hsotg, true);
    131	} else if (role == USB_ROLE_DEVICE) {
    132		already = dwc2_ovr_bvalid(hsotg, true);
    133		if (dwc2_is_device_enabled(hsotg)) {
    134			/* This clear DCTL.SFTDISCON bit */
    135			dwc2_hsotg_core_connect(hsotg);
    136		}
    137	} else {
    138		if (dwc2_is_device_mode(hsotg)) {
    139			if (!dwc2_ovr_bvalid(hsotg, false))
    140				/* This set DCTL.SFTDISCON bit */
    141				dwc2_hsotg_core_disconnect(hsotg);
    142		} else {
    143			dwc2_ovr_avalid(hsotg, false);
    144		}
    145	}
    146
    147	spin_unlock_irqrestore(&hsotg->lock, flags);
    148
    149	if (!already && hsotg->dr_mode == USB_DR_MODE_OTG)
    150		/* This will raise a Connector ID Status Change Interrupt */
    151		dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
    152
    153	if (!hsotg->ll_hw_enabled && hsotg->clk)
    154		clk_disable_unprepare(hsotg->clk);
    155
    156	dev_dbg(hsotg->dev, "%s-session valid\n",
    157		role == USB_ROLE_NONE ? "No" :
    158		role == USB_ROLE_HOST ? "A" : "B");
    159
    160	return 0;
    161}
    162
    163int dwc2_drd_init(struct dwc2_hsotg *hsotg)
    164{
    165	struct usb_role_switch_desc role_sw_desc = {0};
    166	struct usb_role_switch *role_sw;
    167	int ret;
    168
    169	if (!device_property_read_bool(hsotg->dev, "usb-role-switch"))
    170		return 0;
    171
    172	hsotg->role_sw_default_mode = usb_get_role_switch_default_mode(hsotg->dev);
    173	role_sw_desc.driver_data = hsotg;
    174	role_sw_desc.fwnode = dev_fwnode(hsotg->dev);
    175	role_sw_desc.set = dwc2_drd_role_sw_set;
    176	role_sw_desc.allow_userspace_control = true;
    177
    178	role_sw = usb_role_switch_register(hsotg->dev, &role_sw_desc);
    179	if (IS_ERR(role_sw)) {
    180		ret = PTR_ERR(role_sw);
    181		dev_err(hsotg->dev,
    182			"failed to register role switch: %d\n", ret);
    183		return ret;
    184	}
    185
    186	hsotg->role_sw = role_sw;
    187
    188	/* Enable override and initialize values */
    189	dwc2_ovr_init(hsotg);
    190
    191	return 0;
    192}
    193
    194void dwc2_drd_suspend(struct dwc2_hsotg *hsotg)
    195{
    196	u32 gintsts, gintmsk;
    197
    198	if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
    199		gintmsk = dwc2_readl(hsotg, GINTMSK);
    200		gintmsk &= ~GINTSTS_CONIDSTSCHNG;
    201		dwc2_writel(hsotg, gintmsk, GINTMSK);
    202		gintsts = dwc2_readl(hsotg, GINTSTS);
    203		dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
    204	}
    205}
    206
    207void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
    208{
    209	u32 gintsts, gintmsk;
    210	enum usb_role role;
    211
    212	if (hsotg->role_sw) {
    213		/* get last known role (as the get ops isn't implemented by this driver) */
    214		role = usb_role_switch_get_role(hsotg->role_sw);
    215
    216		if (role == USB_ROLE_NONE) {
    217			if (hsotg->role_sw_default_mode == USB_DR_MODE_HOST)
    218				role = USB_ROLE_HOST;
    219			else if (hsotg->role_sw_default_mode == USB_DR_MODE_PERIPHERAL)
    220				role = USB_ROLE_DEVICE;
    221		}
    222
    223		/* restore last role that may have been lost */
    224		if (role == USB_ROLE_HOST)
    225			dwc2_ovr_avalid(hsotg, true);
    226		else if (role == USB_ROLE_DEVICE)
    227			dwc2_ovr_bvalid(hsotg, true);
    228
    229		dwc2_force_mode(hsotg, role == USB_ROLE_HOST);
    230
    231		dev_dbg(hsotg->dev, "resuming %s-session valid\n",
    232			role == USB_ROLE_NONE ? "No" :
    233			role == USB_ROLE_HOST ? "A" : "B");
    234	}
    235
    236	if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
    237		gintsts = dwc2_readl(hsotg, GINTSTS);
    238		dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
    239		gintmsk = dwc2_readl(hsotg, GINTMSK);
    240		gintmsk |= GINTSTS_CONIDSTSCHNG;
    241		dwc2_writel(hsotg, gintmsk, GINTMSK);
    242	}
    243}
    244
    245void dwc2_drd_exit(struct dwc2_hsotg *hsotg)
    246{
    247	if (hsotg->role_sw)
    248		usb_role_switch_unregister(hsotg->role_sw);
    249}