Commit e98ba8cc authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

Merge tag 'usb-for-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next

Felipe writes:

USB: changes for v5.9 merge window

CDNS3 got several improvements, most of which are non-critical fixes.
DWC3 has a reset fix for the meson platform, while dwc2 has
improvements for role switch on STM32MP15 SoCs.

Apart from these, we have the usual set of non-critical fixes all over
the place and support for new Ingenic SoC to their PHY driver.

* tag 'usb-for-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb: (38 commits)
  usb: dwc3: gadget: when the started list is empty stop the active xfer
  usb: dwc3: gadget: make starting isoc transfers more robust
  usb: dwc3: gadget: add frame number mask
  usb: gadget: function: printer: Interface is disabled and returns error
  usb: gadget: f_uac2: fix AC Interface Header Descriptor wTotalLength
  dt-bindings: usb: ti,keystone-dwc3.yaml: Improve schema
  usb: bdc: Use devm_clk_get_optional()
  usb: bdc: Halt controller on suspend
  usb: bdc: driver runs out of buffer descriptors on large ADB transfers
  usb: bdc: Adb shows offline after resuming from S2
  bdc: Fix bug causing crash after multiple disconnects
  usb: bdc: Add compatible string for new style USB DT nodes
  dt-bindings: usb: bdc: Update compatible strings
  USB: PHY: JZ4770: Reformat the code to align it.
  USB: PHY: JZ4770: Add support for new Ingenic SoCs.
  USB: PHY: JZ4770: Unify code style and simplify code.
  dt-bindings: USB: Add bindings for new Ingenic SoCs.
  usb: gadget: net2280: fix memory leak on probe error handling paths
  usb: cdns3: drd: simplify *switch_gadet and *switch_host
  usb: cdns3: core: removed overwriting some error code
  ...
parents c9779308 f5e46aa4
......@@ -4,7 +4,7 @@ Broadcom USB Device Controller (BDC)
Required properties:
- compatible: must be one of:
"brcm,bdc-v0.16"
"brcm,bdc-udc-v2"
"brcm,bdc"
- reg: the base register address and length
- interrupts: the interrupt line for this controller
......@@ -21,7 +21,7 @@ On Broadcom STB platforms, these properties are required:
Example:
bdc@f0b02000 {
compatible = "brcm,bdc-v0.16";
compatible = "brcm,bdc-udc-v2";
reg = <0xf0b02000 0xfc4>;
interrupts = <0x0 0x60 0x0>;
phys = <&usbphy_0 0x0>;
......
......@@ -4,10 +4,11 @@
$id: http://devicetree.org/schemas/usb/ingenic,jz4770-phy.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Ingenic JZ4770 USB PHY devicetree bindings
title: Ingenic SoCs USB PHY devicetree bindings
maintainers:
- Paul Cercueil <paul@crapouillou.net>
- 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
properties:
$nodename:
......@@ -16,6 +17,9 @@ properties:
compatible:
enum:
- ingenic,jz4770-phy
- ingenic,jz4780-phy
- ingenic,x1000-phy
- ingenic,x1830-phy
reg:
maxItems: 1
......
......@@ -11,22 +11,36 @@ maintainers:
properties:
compatible:
oneOf:
- const: "ti,keystone-dwc3"
- const: "ti,am654-dwc3"
items:
- enum:
- ti,keystone-dwc3
- ti,am654-dwc3
reg:
maxItems: 1
description: Address and length of the register set for the USB subsystem on
the SOC.
'#address-cells':
const: 1
'#size-cells':
const: 1
ranges: true
interrupts:
maxItems: 1
description: The irq number of this device that is used to interrupt the MPU.
clocks:
description: Clock ID for USB functional clock.
minItems: 1
maxItems: 2
assigned-clocks:
minItems: 1
maxItems: 2
assigned-clock-parents:
minItems: 1
maxItems: 2
power-domains:
description: Should contain a phandle to a PM domain provider node
......@@ -42,33 +56,42 @@ properties:
phy-names:
items:
- const: "usb3-phy"
- const: usb3-phy
dma-coherent: true
dwc3:
dma-ranges: true
patternProperties:
"usb@[a-f0-9]+$":
type: object
description: This is the node representing the DWC3 controller instance
Documentation/devicetree/bindings/usb/dwc3.txt
required:
- compatible
- reg
- "#address-cells"
- "#size-cells"
- ranges
- interrupts
- clocks
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
usb: usb@2680000 {
dwc3@2680000 {
compatible = "ti,keystone-dwc3";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x2680000 0x10000>;
clocks = <&clkusb>;
clock-names = "usb";
interrupts = <GIC_SPI 393 IRQ_TYPE_EDGE_RISING>;
ranges;
dwc3@2690000 {
usb@2690000 {
compatible = "synopsys,dwc3";
reg = <0x2690000 0x70000>;
interrupts = <GIC_SPI 393 IRQ_TYPE_EDGE_RISING>;
......
......@@ -27,13 +27,6 @@
static int cdns3_idle_init(struct cdns3 *cdns);
static inline
struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
{
WARN_ON(!cdns->roles[cdns->role]);
return cdns->roles[cdns->role];
}
static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
{
int ret;
......@@ -93,7 +86,7 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
struct device *dev = cdns->dev;
enum usb_dr_mode best_dr_mode;
enum usb_dr_mode dr_mode;
int ret = 0;
int ret;
dr_mode = usb_get_dr_mode(dev);
cdns->role = USB_ROLE_NONE;
......@@ -184,7 +177,7 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
goto err;
}
return ret;
return 0;
err:
cdns3_exit_roles(cdns);
return ret;
......@@ -198,11 +191,17 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
*/
static enum usb_role cdns3_hw_role_state_machine(struct cdns3 *cdns)
{
enum usb_role role;
enum usb_role role = USB_ROLE_NONE;
int id, vbus;
if (cdns->dr_mode != USB_DR_MODE_OTG)
goto not_otg;
if (cdns->dr_mode != USB_DR_MODE_OTG) {
if (cdns3_is_host(cdns))
role = USB_ROLE_HOST;
if (cdns3_is_device(cdns))
role = USB_ROLE_DEVICE;
return role;
}
id = cdns3_get_id(cdns);
vbus = cdns3_get_vbus(cdns);
......@@ -239,14 +238,6 @@ static enum usb_role cdns3_hw_role_state_machine(struct cdns3 *cdns)
dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role);
return role;
not_otg:
if (cdns3_is_host(cdns))
role = USB_ROLE_HOST;
if (cdns3_is_device(cdns))
role = USB_ROLE_DEVICE;
return role;
}
static int cdns3_idle_role_start(struct cdns3 *cdns)
......@@ -356,7 +347,6 @@ static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role)
case USB_ROLE_HOST:
break;
default:
ret = -EPERM;
goto pm_put;
}
}
......@@ -367,17 +357,14 @@ static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role)
case USB_ROLE_DEVICE:
break;
default:
ret = -EPERM;
goto pm_put;
}
}
cdns3_role_stop(cdns);
ret = cdns3_role_start(cdns, role);
if (ret) {
if (ret)
dev_err(cdns->dev, "set role %d has failed\n", role);
ret = -EPERM;
}
pm_put:
pm_runtime_put_sync(cdns->dev);
......@@ -402,7 +389,7 @@ static int cdns3_probe(struct platform_device *pdev)
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev, "error setting dma mask: %d\n", ret);
return -ENODEV;
return ret;
}
cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
......
......@@ -29,7 +29,6 @@
*/
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
{
int ret = 0;
u32 reg;
switch (mode) {
......@@ -61,7 +60,7 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
return -EINVAL;
}
return ret;
return 0;
}
int cdns3_get_id(struct cdns3 *cdns)
......@@ -84,25 +83,25 @@ int cdns3_get_vbus(struct cdns3 *cdns)
return vbus;
}
int cdns3_is_host(struct cdns3 *cdns)
bool cdns3_is_host(struct cdns3 *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_HOST)
return 1;
else if (!cdns3_get_id(cdns))
return 1;
return true;
else if (cdns3_get_id(cdns) == CDNS3_ID_HOST)
return true;
return 0;
return false;
}
int cdns3_is_device(struct cdns3 *cdns)
bool cdns3_is_device(struct cdns3 *cdns)
{
if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
return 1;
return true;
else if (cdns->dr_mode == USB_DR_MODE_OTG)
if (cdns3_get_id(cdns))
return 1;
if (cdns3_get_id(cdns) == CDNS3_ID_PERIPHERAL)
return true;
return 0;
return false;
}
/**
......@@ -125,56 +124,60 @@ static void cdns3_otg_enable_irq(struct cdns3 *cdns)
}
/**
* cdns3_drd_switch_host - start/stop host
* @cdns: Pointer to controller context structure
* @on: 1 for start, 0 for stop
* cdns3_drd_host_on - start host.
* @cdns: Pointer to controller context structure.
*
* Returns 0 on success otherwise negative errno
* Returns 0 on success otherwise negative errno.
*/
int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
int cdns3_drd_host_on(struct cdns3 *cdns)
{
int ret, val;
u32 reg = OTGCMD_OTG_DIS;
u32 val;
int ret;
/* switch OTG core */
if (on) {
writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
/* Enable host mode. */
writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS,
&cdns->otg_regs->cmd);
dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
val & OTGSTS_XHCI_READY,
1, 100000);
if (ret) {
val & OTGSTS_XHCI_READY, 1, 100000);
if (ret)
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
return ret;
}
} else {
}
/**
* cdns3_drd_host_off - stop host.
* @cdns: Pointer to controller context structure.
*/
void cdns3_drd_host_off(struct cdns3 *cdns)
{
u32 val;
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
&cdns->otg_regs->cmd);
/* Waiting till H_IDLE state.*/
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_HOST_STATE_MASK),
1, 2000000);
}
return 0;
}
/**
* cdns3_drd_switch_gadget - start/stop gadget
* @cdns: Pointer to controller context structure
* @on: 1 for start, 0 for stop
* cdns3_drd_gadget_on - start gadget.
* @cdns: Pointer to controller context structure.
*
* Returns 0 on success otherwise negative errno
*/
int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
int cdns3_drd_gadget_on(struct cdns3 *cdns)
{
int ret, val;
u32 reg = OTGCMD_OTG_DIS;
/* switch OTG core */
if (on) {
writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
......@@ -186,10 +189,21 @@ int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
dev_err(cdns->dev, "timeout waiting for dev_ready\n");
return ret;
}
} else {
return 0;
}
/**
* cdns3_drd_gadget_off - stop gadget.
* @cdns: Pointer to controller context structure.
*/
void cdns3_drd_gadget_off(struct cdns3 *cdns)
{
u32 val;
/*
* driver should wait at least 10us after disabling Device
* before turning-off Device (DEV_BUS_DROP)
* Driver should wait at least 10us after disabling Device
* before turning-off Device (DEV_BUS_DROP).
*/
usleep_range(20, 30);
writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
......@@ -199,9 +213,6 @@ int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
!(val & OTGSTATE_DEV_STATE_MASK),
1, 2000000);
}
return 0;
}
/**
......@@ -212,7 +223,7 @@ int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
*/
static int cdns3_init_otg_mode(struct cdns3 *cdns)
{
int ret = 0;
int ret;
cdns3_otg_disable_irq(cdns);
/* clear all interrupts */
......@@ -223,7 +234,8 @@ static int cdns3_init_otg_mode(struct cdns3 *cdns)
return ret;
cdns3_otg_enable_irq(cdns);
return ret;
return 0;
}
/**
......@@ -234,7 +246,7 @@ static int cdns3_init_otg_mode(struct cdns3 *cdns)
*/
int cdns3_drd_update_mode(struct cdns3 *cdns)
{
int ret = 0;
int ret;
switch (cdns->dr_mode) {
case USB_DR_MODE_PERIPHERAL:
......@@ -279,12 +291,12 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data)
u32 reg;
if (cdns->dr_mode != USB_DR_MODE_OTG)
return ret;
return IRQ_NONE;
reg = readl(&cdns->otg_regs->ivect);
if (!reg)
return ret;
return IRQ_NONE;
if (reg & OTGIEN_ID_CHANGE_INT) {
dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
......@@ -307,8 +319,8 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data)
int cdns3_drd_init(struct cdns3 *cdns)
{
void __iomem *regs;
int ret = 0;
u32 state;
int ret;
regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
if (IS_ERR(regs))
......@@ -359,19 +371,18 @@ int cdns3_drd_init(struct cdns3 *cdns)
cdns3_drd_thread_irq,
IRQF_SHARED,
dev_name(cdns->dev), cdns);
if (ret) {
dev_err(cdns->dev, "couldn't get otg_irq\n");
return ret;
}
state = readl(&cdns->otg_regs->sts);
if (OTGSTS_OTG_NRDY(state) != 0) {
if (OTGSTS_OTG_NRDY(state)) {
dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
return -ENODEV;
}
return ret;
return 0;
}
int cdns3_drd_exit(struct cdns3 *cdns)
......
......@@ -153,15 +153,20 @@ struct cdns3_otg_common_regs {
/* Only for CDNS3_CONTROLLER_V0 version */
#define OVERRIDE_IDPULLUP_V0 BIT(24)
int cdns3_is_host(struct cdns3 *cdns);
int cdns3_is_device(struct cdns3 *cdns);
#define CDNS3_ID_PERIPHERAL 1
#define CDNS3_ID_HOST 0
bool cdns3_is_host(struct cdns3 *cdns);
bool cdns3_is_device(struct cdns3 *cdns);
int cdns3_get_id(struct cdns3 *cdns);
int cdns3_get_vbus(struct cdns3 *cdns);
int cdns3_drd_init(struct cdns3 *cdns);
int cdns3_drd_exit(struct cdns3 *cdns);
int cdns3_drd_update_mode(struct cdns3 *cdns);
int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
int cdns3_drd_gadget_on(struct cdns3 *cdns);
void cdns3_drd_gadget_off(struct cdns3 *cdns);
int cdns3_drd_host_on(struct cdns3 *cdns);
void cdns3_drd_host_off(struct cdns3 *cdns);
int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode);
#endif /* __LINUX_CDNS3_DRD */
......@@ -123,8 +123,6 @@ static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
&priv_dev->regs->ep_cmd);
cdns3_allow_enable_l1(priv_dev, 1);
}
/**
......@@ -161,13 +159,12 @@ static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
if (result)
return result;
if (config) {
cdns3_set_hw_configuration(priv_dev);
} else {
if (!config) {
cdns3_hw_reset_eps_config(priv_dev);
usb_gadget_set_state(&priv_dev->gadget,
USB_STATE_ADDRESS);
}
break;
case USB_STATE_CONFIGURED:
result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
......@@ -640,7 +637,6 @@ void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) {
priv_dev->wait_for_setup = 0;
cdns3_allow_enable_l1(priv_dev, 0);
cdns3_ep0_setup_phase(priv_dev);
} else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
priv_dev->ep0_data_dir = dir;
......@@ -707,7 +703,6 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
unsigned long flags;
int erdy_sent = 0;
int ret = 0;
u8 zlp = 0;
......@@ -723,15 +718,8 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
/* send STATUS stage. Should be called only for SET_CONFIGURATION */
if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
cdns3_select_ep(priv_dev, 0x00);
erdy_sent = !priv_dev->hw_configured_flag;
cdns3_set_hw_configuration(priv_dev);
if (!erdy_sent)
cdns3_ep0_complete_setup(priv_dev, 0, 1);
cdns3_allow_enable_l1(priv_dev, 1);
request->actual = 0;
priv_dev->status_completion_no_call = true;
priv_dev->pending_status_request = request;
......
......@@ -242,9 +242,10 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
return -ENOMEM;
priv_ep->alloc_ring_size = ring_size;
memset(priv_ep->trb_pool, 0, ring_size);
}
memset(priv_ep->trb_pool, 0, ring_size);
priv_ep->num_trbs = num_trbs;
if (!priv_ep->num)
......@@ -1315,7 +1316,6 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
return;
writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd);
cdns3_set_register_bit(&priv_dev->regs->usb_conf,
USB_CONF_U1EN | USB_CONF_U2EN);
......@@ -1332,6 +1332,8 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
cdns3_start_all_request(priv_dev, priv_ep);
}
}
cdns3_allow_enable_l1(priv_dev, 1);
}
/**
......@@ -3015,7 +3017,7 @@ void cdns3_gadget_exit(struct cdns3 *cdns)
kfree(priv_dev->zlp_buf);
kfree(priv_dev);
cdns->gadget_dev = NULL;
cdns3_drd_switch_gadget(cdns, 0);
cdns3_drd_gadget_off(cdns);
}
static int cdns3_gadget_start(struct cdns3 *cdns)
......@@ -3146,7 +3148,7 @@ static int __cdns3_gadget_init(struct cdns3 *cdns)
return ret;
}
cdns3_drd_switch_gadget(cdns, 1);
cdns3_drd_gadget_on(cdns);
pm_runtime_get_sync(cdns->dev);
ret = cdns3_gadget_start(cdns);
......
......@@ -19,7 +19,7 @@ static int __cdns3_host_init(struct cdns3 *cdns)
struct platform_device *xhci;
int ret;
cdns3_drd_switch_host(cdns, 1);
cdns3_drd_host_on(cdns);
xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
if (!xhci) {
......@@ -53,7 +53,7 @@ static void cdns3_host_exit(struct cdns3 *cdns)
{
platform_device_unregister(cdns->host_dev);
cdns->host_dev = NULL;
cdns3_drd_switch_host(cdns, 0);
cdns3_drd_host_off(cdns);
}
int cdns3_host_init(struct cdns3 *cdns)
......
......@@ -47,6 +47,7 @@ config USB_DWC2_PERIPHERAL
config USB_DWC2_DUAL_ROLE
bool "Dual Role mode"
depends on (USB=y && USB_GADGET=y) || (USB_DWC2=m && USB && USB_GADGET)
select USB_ROLE_SWITCH
help
Select this option if you want the driver to work in a dual-role
mode. In this mode both host and gadget features are enabled, and
......
......@@ -3,7 +3,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG
ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_DWC2) += dwc2.o
dwc2-y := core.o core_intr.o platform.o
dwc2-y := core.o core_intr.o platform.o drd.o
dwc2-y += params.o
ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
......
......@@ -860,6 +860,7 @@ struct dwc2_hregs_backup {
* - USB_DR_MODE_PERIPHERAL
* - USB_DR_MODE_HOST
* - USB_DR_MODE_OTG
* @role_sw: usb_role_switch handle
* @hcd_enabled: Host mode sub-driver initialization indicator.
* @gadget_enabled: Peripheral mode sub-driver initialization indicator.
* @ll_hw_enabled: Status of low-level hardware resources.
......@@ -1054,6 +1055,7 @@ struct dwc2_hsotg {
struct dwc2_core_params params;
enum usb_otg_state op_state;
enum usb_dr_mode dr_mode;
struct usb_role_switch *role_sw;
unsigned int hcd_enabled:1;
unsigned int gadget_enabled:1;
unsigned int ll_hw_enabled:1;
......@@ -1376,6 +1378,11 @@ static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg)
return (dwc2_readl(hsotg, GINTSTS) & GINTSTS_CURMODE_HOST) == 0;
}
int dwc2_drd_init(struct dwc2_hsotg *hsotg);
void dwc2_drd_suspend(struct dwc2_hsotg *hsotg);
void dwc2_drd_resume(struct dwc2_hsotg *hsotg);
void dwc2_drd_exit(struct dwc2_hsotg *hsotg);
/*
* Dump core registers and SPRAM
*/
......@@ -1392,6 +1399,7 @@ int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2);
int dwc2_gadget_init(struct dwc2_hsotg *hsotg);
void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
bool reset);
void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg);
void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg);
void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2);
int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
......
// SPDX-License-Identifier: GPL-2.0
/*
* drd.c - DesignWare USB2 DRD Controller Dual-role support
*
* Copyright (C) 2020 STMicroelectronics
*
* Author(s): Amelie Delaunay <amelie.delaunay@st.com>
*/
#include <linux/iopoll.h>
#include <linux/platform_device.h>
#include <linux/usb/role.h>
#include "core.h"
static void dwc2_ovr_init(struct dwc2_hsotg *hsotg)
{
unsigned long flags;
u32 gotgctl;
spin_lock_irqsave(&hsotg->lock, flags);
gotgctl = dwc2_readl(hsotg, GOTGCTL);
gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN | GOTGCTL_VBVALOEN;
gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS;
gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
dwc2_writel(hsotg, gotgctl, GOTGCTL);
dwc2_force_mode(hsotg, false);
spin_unlock_irqrestore(&hsotg->lock, flags);
}
static int dwc2_ovr_avalid(struct dwc2_hsotg *hsotg, bool valid)
{
u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
/* Check if A-Session is already in the right state */
if ((valid && (gotgctl & GOTGCTL_ASESVLD)) ||
(!valid && !(gotgctl & GOTGCTL_ASESVLD)))
return -EALREADY;
if (valid)
gotgctl |= GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL;
else
gotgctl &= ~(GOTGCTL_AVALOVAL | GOTGCTL_VBVALOVAL);
dwc2_writel(hsotg, gotgctl, GOTGCTL);
return 0;
}
static int dwc2_ovr_bvalid(struct dwc2_hsotg *hsotg, bool valid)
{
u32 gotgctl = dwc2_readl(hsotg, GOTGCTL);
/* Check if B-Session is already in the right state */
if ((valid && (gotgctl & GOTGCTL_BSESVLD)) ||
(!valid && !(gotgctl & GOTGCTL_BSESVLD)))
return -EALREADY;
if (valid)
gotgctl |= GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL;
else
gotgctl &= ~(GOTGCTL_BVALOVAL | GOTGCTL_VBVALOVAL);
dwc2_writel(hsotg, gotgctl, GOTGCTL);
return 0;
}
static int dwc2_drd_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
{
struct dwc2_hsotg *hsotg = usb_role_switch_get_drvdata(sw);
unsigned long flags;
/* Skip session not in line with dr_mode */
if ((role == USB_ROLE_DEVICE && hsotg->dr_mode == USB_DR_MODE_HOST) ||
(role == USB_ROLE_HOST && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL))
return -EINVAL;
/* Skip session if core is in test mode */
if (role == USB_ROLE_NONE && hsotg->test_mode) {
dev_dbg(hsotg->dev, "Core is in test mode\n");
return -EBUSY;
}
spin_lock_irqsave(&hsotg->lock, flags);
if (role == USB_ROLE_HOST) {
if (dwc2_ovr_avalid(hsotg, true))
goto unlock;
if (hsotg->dr_mode == USB_DR_MODE_OTG)
/*
* This will raise a Connector ID Status Change
* Interrupt - connID A
*/
dwc2_force_mode(hsotg, true);
} else if (role == USB_ROLE_DEVICE) {
if (dwc2_ovr_bvalid(hsotg, true))
goto unlock;
if (hsotg->dr_mode == USB_DR_MODE_OTG)
/*
* This will raise a Connector ID Status Change
* Interrupt - connID B
*/
dwc2_force_mode(hsotg, false);
/* This clear DCTL.SFTDISCON bit */
dwc2_hsotg_core_connect(hsotg);
} else {
if (dwc2_is_device_mode(hsotg)) {
if (!dwc2_ovr_bvalid(hsotg, false))
/* This set DCTL.SFTDISCON bit */
dwc2_hsotg_core_disconnect(hsotg);
} else {
dwc2_ovr_avalid(hsotg, false);
}
}
unlock:
spin_unlock_irqrestore(&hsotg->lock, flags);
dev_dbg(hsotg->dev, "%s-session valid\n",
role == USB_ROLE_NONE ? "No" :
role == USB_ROLE_HOST ? "A" : "B");
return 0;
}
int dwc2_drd_init(struct dwc2_hsotg *hsotg)
{
struct usb_role_switch_desc role_sw_desc = {0};
struct usb_role_switch *role_sw;
int ret;
if (!device_property_read_bool(hsotg->dev, "usb-role-switch"))
return 0;
role_sw_desc.driver_data = hsotg;
role_sw_desc.fwnode = dev_fwnode(hsotg->dev);
role_sw_desc.set = dwc2_drd_role_sw_set;
role_sw_desc.allow_userspace_control = true;
role_sw = usb_role_switch_register(hsotg->dev, &role_sw_desc);
if (IS_ERR(role_sw)) {
ret = PTR_ERR(role_sw);
dev_err(hsotg->dev,
"failed to register role switch: %d\n", ret);
return ret;
}
hsotg->role_sw = role_sw;
/* Enable override and initialize values */
dwc2_ovr_init(hsotg);
return 0;
}
void dwc2_drd_suspend(struct dwc2_hsotg *hsotg)
{
u32 gintsts, gintmsk;
if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk &= ~GINTSTS_CONIDSTSCHNG;
dwc2_writel(hsotg, gintmsk, GINTMSK);
gintsts = dwc2_readl(hsotg, GINTSTS);
dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
}
}
void dwc2_drd_resume(struct dwc2_hsotg *hsotg)
{
u32 gintsts, gintmsk;
if (hsotg->role_sw && !hsotg->params.external_id_pin_ctl) {
gintsts = dwc2_readl(hsotg, GINTSTS);
dwc2_writel(hsotg, gintsts | GINTSTS_CONIDSTSCHNG, GINTSTS);
gintmsk = dwc2_readl(hsotg, GINTMSK);
gintmsk |= GINTSTS_CONIDSTSCHNG;
dwc2_writel(hsotg, gintmsk, GINTMSK);
}
}
void dwc2_drd_exit(struct dwc2_hsotg *hsotg)
{
if (hsotg->role_sw)
usb_role_switch_unregister(hsotg->role_sw);
}
......@@ -3530,7 +3530,7 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
dwc2_readl(hsotg, DOEPCTL0));
}
static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
{
/* set the soft-disconnect bit */
dwc2_set_bit(hsotg, DCTL, DCTL_SFTDISCON);
......
......@@ -183,9 +183,11 @@ static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg)
static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
struct device_node *np = hsotg->dev->of_node;
p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
p->activate_stm_id_vb_detection = true;
p->activate_stm_id_vb_detection =
!of_property_read_bool(np, "usb-role-switch");
p->host_rx_fifo_size = 440;
p->host_nperio_tx_fifo_size = 256;
p->host_perio_tx_fifo_size = 256;
......
......@@ -317,6 +317,8 @@ static int dwc2_driver_remove(struct platform_device *dev)
if (hsotg->params.activate_stm_id_vb_detection)
regulator_disable(hsotg->usb33d);
dwc2_drd_exit(hsotg);
if (hsotg->ll_hw_enabled)
dwc2_lowlevel_hw_disable(hsotg);
......@@ -533,6 +535,13 @@ static int dwc2_driver_probe(struct platform_device *dev)
dwc2_writel(hsotg, ggpio, GGPIO);
}
retval = dwc2_drd_init(hsotg);
if (retval) {
if (retval != -EPROBE_DEFER)
dev_err(hsotg->dev, "failed to initialize dual-role\n");
goto error_init;
}
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
retval = dwc2_gadget_init(hsotg);
if (retval)
......@@ -582,6 +591,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (hsotg->gadget_enabled) {
retval = usb_add_gadget_udc(hsotg->dev, &hsotg->gadget);
if (retval) {
hsotg->gadget.udc = NULL;
dwc2_hsotg_remove(hsotg);
goto error_init;
}
......@@ -593,6 +603,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
if (hsotg->params.activate_stm_id_vb_detection)
regulator_disable(hsotg->usb33d);
error:
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL)
dwc2_lowlevel_hw_disable(hsotg);
return retval;
}
......@@ -606,6 +617,8 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
if (is_device_mode)
dwc2_hsotg_suspend(dwc2);
dwc2_drd_suspend(dwc2);
if (dwc2->params.activate_stm_id_vb_detection) {
unsigned long flags;
u32 ggpio, gotgctl;
......@@ -686,6 +699,8 @@ static int __maybe_unused dwc2_resume(struct device *dev)
/* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
dwc2_force_dr_mode(dwc2);
dwc2_drd_resume(dwc2);
if (dwc2_is_device_mode(dwc2))
ret = dwc2_hsotg_resume(dwc2);
......
......@@ -737,13 +737,13 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
goto err_disable_clks;
}
ret = reset_control_reset(priv->reset);
ret = reset_control_deassert(priv->reset);
if (ret)
goto err_disable_clks;
goto err_assert_reset;
ret = dwc3_meson_g12a_get_phys(priv);
if (ret)
goto err_disable_clks;
goto err_assert_reset;
ret = priv->drvdata->setup_regmaps(priv, base);
if (ret)
......@@ -752,7 +752,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
if (priv->vbus) {
ret = regulator_enable(priv->vbus);
if (ret)
goto err_disable_clks;
goto err_assert_reset;
}
/* Get dr_mode */
......@@ -765,13 +765,13 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
ret = priv->drvdata->usb_init(priv);
if (ret)
goto err_disable_clks;
goto err_assert_reset;
/* Init PHYs */
for (i = 0 ; i < PHY_COUNT ; ++i) {
ret = phy_init(priv->phys[i]);
if (ret)
goto err_disable_clks;
goto err_assert_reset;
}
/* Set PHY Power */
......@@ -809,6 +809,9 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
for (i = 0 ; i < PHY_COUNT ; ++i)
phy_exit(priv->phys[i]);
err_assert_reset:
reset_control_assert(priv->reset);
err_disable_clks:
clk_bulk_disable_unprepare(priv->drvdata->num_clks,
priv->drvdata->clks);
......
......@@ -1403,7 +1403,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
* Check if we can start isoc transfer on the next interval or
* 4 uframes in the future with BIT[15:14] as dep->combo_num
*/
test_frame_number = dep->frame_number & 0x3fff;
test_frame_number = dep->frame_number & DWC3_FRNUMBER_MASK;
test_frame_number |= dep->combo_num << 14;
test_frame_number += max_t(u32, 4, dep->interval);
......@@ -1450,7 +1450,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
else if (test0 && test1)
dep->combo_num = 0;
dep->frame_number &= 0x3fff;
dep->frame_number &= DWC3_FRNUMBER_MASK;
dep->frame_number |= dep->combo_num << 14;
dep->frame_number += max_t(u32, 4, dep->interval);
......@@ -1463,6 +1463,7 @@ static int dwc3_gadget_start_isoc_quirk(struct dwc3_ep *dep)
static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
int ret;
int i;
......@@ -1480,6 +1481,27 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
return dwc3_gadget_start_isoc_quirk(dep);
}
if (desc->bInterval <= 14 &&
dwc->gadget.speed >= USB_SPEED_HIGH) {
u32 frame = __dwc3_gadget_get_frame(dwc);
bool rollover = frame <
(dep->frame_number & DWC3_FRNUMBER_MASK);
/*
* frame_number is set from XferNotReady and may be already
* out of date. DSTS only provides the lower 14 bit of the
* current frame number. So add the upper two bits of
* frame_number and handle a possible rollover.
* This will provide the correct frame_number unless more than
* rollover has happened since XferNotReady.
*/
dep->frame_number = (dep->frame_number & ~DWC3_FRNUMBER_MASK) |
frame;
if (rollover)
dep->frame_number += BIT(14);
}
for (i = 0; i < DWC3_ISOC_MAX_RETRIES; i++) {
dep->frame_number = DWC3_ALIGN_FRAME(dep, i + 1);
......@@ -2716,7 +2738,9 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
goto out;
if (status == -EXDEV && list_empty(&dep->started_list))
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
list_empty(&dep->started_list) &&
(list_empty(&dep->pending_list) || status == -EXDEV))
dwc3_stop_active_transfer(dep, true, true);
else if (dwc3_gadget_ep_should_continue(dep))
if (__dwc3_gadget_kick_transfer(dep) == 0)
......
......@@ -54,6 +54,8 @@ struct dwc3;
/* U2 Device exit Latency */
#define DWC3_DEFAULT_U2_DEV_EXIT_LAT 0x1FF /* Less then 511 microsec */
/* Frame/Microframe Number Mask */
#define DWC3_FRNUMBER_MASK 0x3fff
/* -------------------------------------------------------------------------- */
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
......
......@@ -338,6 +338,11 @@ printer_open(struct inode *inode, struct file *fd)
spin_lock_irqsave(&dev->lock, flags);
if (dev->interface < 0) {
spin_unlock_irqrestore(&dev->lock, flags);
return -ENODEV;
}
if (!dev->printer_cdev_open) {
dev->printer_cdev_open = 1;
fd->private_data = dev;
......@@ -430,6 +435,12 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
if (dev->interface < 0) {
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -ENODEV;
}
/* We will use this flag later to check if a printer reset happened
* after we turn interrupts back on.
*/
......@@ -561,6 +572,12 @@ printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
if (dev->interface < 0) {
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return -ENODEV;
}
/* Check if a printer reset happens while we have interrupts on */
dev->reset_printer = 0;
......@@ -667,6 +684,13 @@ printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
inode_lock(inode);
spin_lock_irqsave(&dev->lock, flags);
if (dev->interface < 0) {
spin_unlock_irqrestore(&dev->lock, flags);
inode_unlock(inode);
return -ENODEV;
}
tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
spin_unlock_irqrestore(&dev->lock, flags);
......@@ -689,6 +713,13 @@ printer_poll(struct file *fd, poll_table *wait)
mutex_lock(&dev->lock_printer_io);
spin_lock_irqsave(&dev->lock, flags);
if (dev->interface < 0) {
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
return EPOLLERR | EPOLLHUP;
}
setup_rx_reqs(dev);
spin_unlock_irqrestore(&dev->lock, flags);
mutex_unlock(&dev->lock_printer_io);
......@@ -722,6 +753,11 @@ printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
spin_lock_irqsave(&dev->lock, flags);
if (dev->interface < 0) {
spin_unlock_irqrestore(&dev->lock, flags);
return -ENODEV;
}
switch (code) {
case GADGET_GET_PRINTER_STATUS:
status = (int)dev->printer_status;
......
......@@ -215,10 +215,7 @@ static struct uac2_ac_header_descriptor ac_hdr_desc = {
.bDescriptorSubtype = UAC_MS_HEADER,
.bcdADC = cpu_to_le16(0x200),
.bCategory = UAC2_FUNCTION_IO_BOX,
.wTotalLength = cpu_to_le16(sizeof in_clk_src_desc
+ sizeof out_clk_src_desc + sizeof usb_out_it_desc
+ sizeof io_in_it_desc + sizeof usb_in_ot_desc
+ sizeof io_out_ot_desc),
/* .wTotalLength = DYNAMIC */
.bmControls = 0,
};
......@@ -501,7 +498,7 @@ static void setup_descriptor(struct f_uac2_opts *opts)
as_in_hdr_desc.bTerminalLink = usb_in_ot_desc.bTerminalID;
iad_desc.bInterfaceCount = 1;
ac_hdr_desc.wTotalLength = 0;
ac_hdr_desc.wTotalLength = cpu_to_le16(sizeof(ac_hdr_desc));
if (EPIN_EN(opts)) {
u16 len = le16_to_cpu(ac_hdr_desc.wTotalLength);
......
......@@ -1028,6 +1028,7 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
return 0;
}
static int atmel_usba_pullup(struct usb_gadget *gadget, int is_on);
static int atmel_usba_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
static int atmel_usba_stop(struct usb_gadget *gadget);
......@@ -1101,6 +1102,7 @@ static const struct usb_gadget_ops usba_udc_ops = {
.get_frame = usba_udc_get_frame,
.wakeup = usba_udc_wakeup,
.set_selfpowered = usba_udc_set_selfpowered,
.pullup = atmel_usba_pullup,
.udc_start = atmel_usba_start,
.udc_stop = atmel_usba_stop,
.match_ep = atmel_usba_match_ep,
......@@ -1957,6 +1959,24 @@ static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
return IRQ_HANDLED;
}
static int atmel_usba_pullup(struct usb_gadget *gadget, int is_on)
{
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
unsigned long flags;
u32 ctrl;
spin_lock_irqsave(&udc->lock, flags);
ctrl = usba_readl(udc, CTRL);
if (is_on)
ctrl &= ~USBA_DETACH;
else
ctrl |= USBA_DETACH;
usba_writel(udc, CTRL, ctrl);
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
}
static int atmel_usba_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
......
......@@ -44,7 +44,7 @@
#define NUM_SR_ENTRIES 64
/* Num of bds per table */
#define NUM_BDS_PER_TABLE 32
#define NUM_BDS_PER_TABLE 64
/* Num of tables in bd list for control,bulk and Int ep */
#define NUM_TABLES 2
......
......@@ -12,6 +12,7 @@
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/iopoll.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/list.h>
......@@ -29,24 +30,19 @@
#include "bdc_dbg.h"
/* Poll till controller status is not OIP */
static int poll_oip(struct bdc *bdc, int usec)
static int poll_oip(struct bdc *bdc, u32 usec)
{
u32 status;
/* Poll till STS!= OIP */
while (usec) {
status = bdc_readl(bdc->regs, BDC_BDCSC);
if (BDC_CSTS(status) != BDC_OIP) {
dev_dbg(bdc->dev,
"poll_oip complete status=%d",
BDC_CSTS(status));
return 0;
}
udelay(10);
usec -= 10;
}
dev_err(bdc->dev, "Err: operation timedout BDCSC: 0x%08x\n", status);
int ret;
ret = readl_poll_timeout(bdc->regs + BDC_BDCSC, status,
(BDC_CSTS(status) != BDC_OIP), 10, usec);
if (ret)
dev_err(bdc->dev, "operation timedout BDCSC: 0x%08x\n", status);
else
dev_dbg(bdc->dev, "%s complete status=%d", __func__, BDC_CSTS(status));
return -ETIMEDOUT;
return ret;
}
/* Stop the BDC controller */
......@@ -282,6 +278,7 @@ static void bdc_mem_init(struct bdc *bdc, bool reinit)
* in that case reinit is passed as 1
*/
if (reinit) {
int i;
/* Enable interrupts */
temp = bdc_readl(bdc->regs, BDC_BDCSC);
temp |= BDC_GIE;
......@@ -291,6 +288,13 @@ static void bdc_mem_init(struct bdc *bdc, bool reinit)
/* Initialize SRR to 0 */
memset(bdc->srr.sr_bds, 0,
NUM_SR_ENTRIES * sizeof(struct bdc_bd));
/*
* clear ep flags to avoid post disconnect stops/deconfigs but
* not during S2 exit
*/
if (!bdc->gadget.speed)
for (i = 1; i < bdc->num_eps; ++i)
bdc->bdc_ep_array[i]->flags = 0;
} else {
/* One time initiaization only */
/* Enable status report function pointers */
......@@ -489,11 +493,9 @@ static int bdc_probe(struct platform_device *pdev)
dev_dbg(dev, "%s()\n", __func__);
clk = devm_clk_get(dev, "sw_usbd");
if (IS_ERR(clk)) {
dev_info(dev, "Clock not found in Device Tree\n");
clk = NULL;
}
clk = devm_clk_get_optional(dev, "sw_usbd");
if (IS_ERR(clk))
return PTR_ERR(clk);
ret = clk_prepare_enable(clk);
if (ret) {
......@@ -599,9 +601,14 @@ static int bdc_remove(struct platform_device *pdev)
static int bdc_suspend(struct device *dev)
{
struct bdc *bdc = dev_get_drvdata(dev);
int ret;
/* Halt the controller */
ret = bdc_stop(bdc);
if (!ret)
clk_disable_unprepare(bdc->clk);
return 0;
return ret;
}
static int bdc_resume(struct device *dev)
......@@ -629,7 +636,7 @@ static SIMPLE_DEV_PM_OPS(bdc_pm_ops, bdc_suspend,
bdc_resume);
static const struct of_device_id bdc_of_match[] = {
{ .compatible = "brcm,bdc-v0.16" },
{ .compatible = "brcm,bdc-udc-v2" },
{ .compatible = "brcm,bdc" },
{ /* sentinel */ }
};
......
......@@ -615,7 +615,6 @@ int bdc_ep_enable(struct bdc_ep *ep)
}
bdc_dbg_bd_list(bdc, ep);
/* only for ep0: config ep is called for ep0 from connect event */
ep->flags |= BDC_EP_ENABLED;
if (ep->ep_num == 1)
return ret;
......@@ -759,11 +758,14 @@ static int ep_dequeue(struct bdc_ep *ep, struct bdc_req *req)
__func__, ep->name, start_bdi, end_bdi);
dev_dbg(bdc->dev, "ep_dequeue ep=%p ep->desc=%p\n",
ep, (void *)ep->usb_ep.desc);
/* Stop the ep to see where the HW is ? */
/* if still connected, stop the ep to see where the HW is ? */
if (!(bdc_readl(bdc->regs, BDC_USPC) & BDC_PST_MASK)) {
ret = bdc_stop_ep(bdc, ep->ep_num);
/* if there is an issue with stopping ep, then no need to go further */
/* if there is an issue, then no need to go further */
if (ret)
return 0;
} else
return 0;
/*
* After endpoint is stopped, there can be 3 cases, the request
......@@ -1911,7 +1913,9 @@ static int bdc_gadget_ep_disable(struct usb_ep *_ep)
__func__, ep->name, ep->flags);
if (!(ep->flags & BDC_EP_ENABLED)) {
dev_warn(bdc->dev, "%s is already disabled\n", ep->name);
if (bdc->gadget.speed != USB_SPEED_UNKNOWN)
dev_warn(bdc->dev, "%s is already disabled\n",
ep->name);
return 0;
}
spin_lock_irqsave(&bdc->lock, flags);
......
......@@ -1230,6 +1230,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
return 0;
err_del_udc:
flush_work(&gadget->work);
device_del(&udc->dev);
err_unlist_udc:
......
......@@ -2370,6 +2370,8 @@ net2272_rdk1_probe(struct pci_dev *pdev, struct net2272 *dev)
err:
while (--i >= 0) {
if (i == 1)
continue; /* BAR1 unused */
iounmap(mem_mapped_addr[i]);
release_mem_region(pci_resource_start(pdev, i),
pci_resource_len(pdev, i));
......
......@@ -3781,8 +3781,10 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return 0;
done:
if (dev)
if (dev) {
net2280_remove(pdev);
kfree(dev);
}
return retval;
}
......
......@@ -185,11 +185,11 @@ config USB_ULPI_VIEWPORT
controllers with a viewport register (e.g. Chipidea/ARC controllers).
config JZ4770_PHY
tristate "Ingenic JZ4770 Transceiver Driver"
tristate "Ingenic SoCs Transceiver Driver"
depends on MIPS || COMPILE_TEST
select USB_PHY
help
This driver provides PHY support for the USB controller found
on the JZ4770 SoC from Ingenic.
on the JZ-series and X-series SoCs from Ingenic.
endmenu
// SPDX-License-Identifier: GPL-2.0
/*
* Ingenic JZ4770 USB PHY driver
* Ingenic SoCs USB PHY driver
* Copyright (c) Paul Cercueil <paul@crapouillou.net>
* Copyright (c) 漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>
* Copyright (c) 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
*/
#include <linux/clk.h>
......@@ -12,16 +14,15 @@
#include <linux/usb/otg.h>
#include <linux/usb/phy.h>
/* OTGPHY register offsets */
#define REG_USBPCR_OFFSET 0x00
#define REG_USBRDT_OFFSET 0x04
#define REG_USBVBFIL_OFFSET 0x08
#define REG_USBPCR1_OFFSET 0x0c
/* USBPCR */
/* bits within the USBPCR register */
#define USBPCR_USB_MODE BIT(31)
#define USBPCR_AVLD_REG BIT(30)
#define USBPCR_INCRM BIT(27)
#define USBPCR_CLK12_EN BIT(26)
#define USBPCR_COMMONONN BIT(25)
#define USBPCR_VBUSVLDEXT BIT(24)
#define USBPCR_VBUSVLDEXTSEL BIT(23)
......@@ -32,46 +33,76 @@
#define USBPCR_IDPULLUP_LSB 28
#define USBPCR_IDPULLUP_MASK GENMASK(29, USBPCR_IDPULLUP_LSB)
#define USBPCR_IDPULLUP_ALWAYS (3 << USBPCR_IDPULLUP_LSB)
#define USBPCR_IDPULLUP_SUSPEND (1 << USBPCR_IDPULLUP_LSB)
#define USBPCR_IDPULLUP_OTG (0 << USBPCR_IDPULLUP_LSB)
#define USBPCR_IDPULLUP_ALWAYS (0x2 << USBPCR_IDPULLUP_LSB)
#define USBPCR_IDPULLUP_SUSPEND (0x1 << USBPCR_IDPULLUP_LSB)
#define USBPCR_IDPULLUP_OTG (0x0 << USBPCR_IDPULLUP_LSB)
#define USBPCR_COMPDISTUNE_LSB 17
#define USBPCR_COMPDISTUNE_MASK GENMASK(19, USBPCR_COMPDISTUNE_LSB)
#define USBPCR_COMPDISTUNE_DFT 4
#define USBPCR_COMPDISTUNE_DFT (0x4 << USBPCR_COMPDISTUNE_LSB)
#define USBPCR_OTGTUNE_LSB 14
#define USBPCR_OTGTUNE_MASK GENMASK(16, USBPCR_OTGTUNE_LSB)
#define USBPCR_OTGTUNE_DFT 4
#define USBPCR_OTGTUNE_DFT (0x4 << USBPCR_OTGTUNE_LSB)
#define USBPCR_SQRXTUNE_LSB 11
#define USBPCR_SQRXTUNE_MASK GENMASK(13, USBPCR_SQRXTUNE_LSB)
#define USBPCR_SQRXTUNE_DFT 3
#define USBPCR_SQRXTUNE_DCR_20PCT (0x7 << USBPCR_SQRXTUNE_LSB)
#define USBPCR_SQRXTUNE_DFT (0x3 << USBPCR_SQRXTUNE_LSB)
#define USBPCR_TXFSLSTUNE_LSB 7
#define USBPCR_TXFSLSTUNE_MASK GENMASK(10, USBPCR_TXFSLSTUNE_LSB)
#define USBPCR_TXFSLSTUNE_DFT 3
#define USBPCR_TXFSLSTUNE_DCR_50PPT (0xf << USBPCR_TXFSLSTUNE_LSB)
#define USBPCR_TXFSLSTUNE_DCR_25PPT (0x7 << USBPCR_TXFSLSTUNE_LSB)
#define USBPCR_TXFSLSTUNE_DFT (0x3 << USBPCR_TXFSLSTUNE_LSB)
#define USBPCR_TXFSLSTUNE_INC_25PPT (0x1 << USBPCR_TXFSLSTUNE_LSB)
#define USBPCR_TXFSLSTUNE_INC_50PPT (0x0 << USBPCR_TXFSLSTUNE_LSB)
#define USBPCR_TXHSXVTUNE_LSB 4
#define USBPCR_TXHSXVTUNE_MASK GENMASK(5, USBPCR_TXHSXVTUNE_LSB)
#define USBPCR_TXHSXVTUNE_DFT (0x3 << USBPCR_TXHSXVTUNE_LSB)
#define USBPCR_TXHSXVTUNE_DCR_15MV (0x1 << USBPCR_TXHSXVTUNE_LSB)
#define USBPCR_TXRISETUNE_LSB 4
#define USBPCR_TXRISETUNE_MASK GENMASK(5, USBPCR_TXRISETUNE_LSB)
#define USBPCR_TXRISETUNE_DFT 3
#define USBPCR_TXRISETUNE_DFT (0x3 << USBPCR_TXRISETUNE_LSB)
#define USBPCR_TXVREFTUNE_LSB 0
#define USBPCR_TXVREFTUNE_MASK GENMASK(3, USBPCR_TXVREFTUNE_LSB)
#define USBPCR_TXVREFTUNE_DFT 5
#define USBPCR_TXVREFTUNE_INC_25PPT (0x7 << USBPCR_TXVREFTUNE_LSB)
#define USBPCR_TXVREFTUNE_DFT (0x5 << USBPCR_TXVREFTUNE_LSB)
/* USBRDT */
/* bits within the USBRDTR register */
#define USBRDT_UTMI_RST BIT(27)
#define USBRDT_HB_MASK BIT(26)
#define USBRDT_VBFIL_LD_EN BIT(25)
#define USBRDT_IDDIG_EN BIT(24)
#define USBRDT_IDDIG_REG BIT(23)
#define USBRDT_VBFIL_EN BIT(2)
/* bits within the USBPCR1 register */
#define USBPCR1_BVLD_REG BIT(31)
#define USBPCR1_DPPD BIT(29)
#define USBPCR1_DMPD BIT(28)
#define USBPCR1_USB_SEL BIT(28)
#define USBPCR1_WORD_IF_16BIT BIT(19)
enum ingenic_usb_phy_version {
ID_JZ4770,
ID_JZ4780,
ID_X1000,
ID_X1830,
};
#define USBRDT_USBRDT_LSB 0
#define USBRDT_USBRDT_MASK GENMASK(22, USBRDT_USBRDT_LSB)
struct ingenic_soc_info {
enum ingenic_usb_phy_version version;
/* USBPCR1 */
#define USBPCR1_UHC_POWON BIT(5)
void (*usb_phy_init)(struct usb_phy *phy);
};
struct jz4770_phy {
const struct ingenic_soc_info *soc_info;
struct usb_phy phy;
struct usb_otg otg;
struct device *dev;
......@@ -90,12 +121,18 @@ static inline struct jz4770_phy *phy_to_jz4770_phy(struct usb_phy *phy)
return container_of(phy, struct jz4770_phy, phy);
}
static int jz4770_phy_set_peripheral(struct usb_otg *otg,
static int ingenic_usb_phy_set_peripheral(struct usb_otg *otg,
struct usb_gadget *gadget)
{
struct jz4770_phy *priv = otg_to_jz4770_phy(otg);
u32 reg;
if (priv->soc_info->version >= ID_X1000) {
reg = readl(priv->base + REG_USBPCR1_OFFSET);
reg |= USBPCR1_BVLD_REG;
writel(reg, priv->base + REG_USBPCR1_OFFSET);
}
reg = readl(priv->base + REG_USBPCR_OFFSET);
reg &= ~USBPCR_USB_MODE;
reg |= USBPCR_VBUSVLDEXT | USBPCR_VBUSVLDEXTSEL | USBPCR_OTG_DISABLE;
......@@ -104,7 +141,7 @@ static int jz4770_phy_set_peripheral(struct usb_otg *otg,
return 0;
}
static int jz4770_phy_set_host(struct usb_otg *otg, struct usb_bus *host)
static int ingenic_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host)
{
struct jz4770_phy *priv = otg_to_jz4770_phy(otg);
u32 reg;
......@@ -117,7 +154,7 @@ static int jz4770_phy_set_host(struct usb_otg *otg, struct usb_bus *host)
return 0;
}
static int jz4770_phy_init(struct usb_phy *phy)
static int ingenic_usb_phy_init(struct usb_phy *phy)
{
struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
int err;
......@@ -135,15 +172,7 @@ static int jz4770_phy_init(struct usb_phy *phy)
return err;
}
reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_IDPULLUP_ALWAYS |
(USBPCR_COMPDISTUNE_DFT << USBPCR_COMPDISTUNE_LSB) |
(USBPCR_OTGTUNE_DFT << USBPCR_OTGTUNE_LSB) |
(USBPCR_SQRXTUNE_DFT << USBPCR_SQRXTUNE_LSB) |
(USBPCR_TXFSLSTUNE_DFT << USBPCR_TXFSLSTUNE_LSB) |
(USBPCR_TXRISETUNE_DFT << USBPCR_TXRISETUNE_LSB) |
(USBPCR_TXVREFTUNE_DFT << USBPCR_TXVREFTUNE_LSB) |
USBPCR_POR;
writel(reg, priv->base + REG_USBPCR_OFFSET);
priv->soc_info->usb_phy_init(phy);
/* Wait for PHY to reset */
usleep_range(30, 300);
......@@ -153,7 +182,7 @@ static int jz4770_phy_init(struct usb_phy *phy)
return 0;
}
static void jz4770_phy_shutdown(struct usb_phy *phy)
static void ingenic_usb_phy_shutdown(struct usb_phy *phy)
{
struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
......@@ -161,11 +190,100 @@ static void jz4770_phy_shutdown(struct usb_phy *phy)
regulator_disable(priv->vcc_supply);
}
static void jz4770_phy_remove(void *phy)
static void ingenic_usb_phy_remove(void *phy)
{
usb_remove_phy(phy);
}
static void jz4770_usb_phy_init(struct usb_phy *phy)
{
struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
u32 reg;
reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_IDPULLUP_ALWAYS |
USBPCR_COMPDISTUNE_DFT | USBPCR_OTGTUNE_DFT | USBPCR_SQRXTUNE_DFT |
USBPCR_TXFSLSTUNE_DFT | USBPCR_TXRISETUNE_DFT | USBPCR_TXVREFTUNE_DFT |
USBPCR_POR;
writel(reg, priv->base + REG_USBPCR_OFFSET);
}
static void jz4780_usb_phy_init(struct usb_phy *phy)
{
struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
u32 reg;
reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_USB_SEL |
USBPCR1_WORD_IF_16BIT;
writel(reg, priv->base + REG_USBPCR1_OFFSET);
reg = USBPCR_TXPREEMPHTUNE | USBPCR_COMMONONN | USBPCR_POR;
writel(reg, priv->base + REG_USBPCR_OFFSET);
}
static void x1000_usb_phy_init(struct usb_phy *phy)
{
struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
u32 reg;
reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT;
writel(reg, priv->base + REG_USBPCR1_OFFSET);
reg = USBPCR_SQRXTUNE_DCR_20PCT | USBPCR_TXPREEMPHTUNE |
USBPCR_TXHSXVTUNE_DCR_15MV | USBPCR_TXVREFTUNE_INC_25PPT |
USBPCR_COMMONONN | USBPCR_POR;
writel(reg, priv->base + REG_USBPCR_OFFSET);
}
static void x1830_usb_phy_init(struct usb_phy *phy)
{
struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
u32 reg;
/* rdt */
writel(USBRDT_VBFIL_EN | USBRDT_UTMI_RST, priv->base + REG_USBRDT_OFFSET);
reg = readl(priv->base + REG_USBPCR1_OFFSET) | USBPCR1_WORD_IF_16BIT |
USBPCR1_DMPD | USBPCR1_DPPD;
writel(reg, priv->base + REG_USBPCR1_OFFSET);
reg = USBPCR_IDPULLUP_OTG | USBPCR_VBUSVLDEXT | USBPCR_TXPREEMPHTUNE |
USBPCR_COMMONONN | USBPCR_POR;
writel(reg, priv->base + REG_USBPCR_OFFSET);
}
static const struct ingenic_soc_info jz4770_soc_info = {
.version = ID_JZ4770,
.usb_phy_init = jz4770_usb_phy_init,
};
static const struct ingenic_soc_info jz4780_soc_info = {
.version = ID_JZ4780,
.usb_phy_init = jz4780_usb_phy_init,
};
static const struct ingenic_soc_info x1000_soc_info = {
.version = ID_X1000,
.usb_phy_init = x1000_usb_phy_init,
};
static const struct ingenic_soc_info x1830_soc_info = {
.version = ID_X1830,
.usb_phy_init = x1830_usb_phy_init,
};
static const struct of_device_id ingenic_usb_phy_of_matches[] = {
{ .compatible = "ingenic,jz4770-phy", .data = &jz4770_soc_info },
{ .compatible = "ingenic,jz4780-phy", .data = &jz4780_soc_info },
{ .compatible = "ingenic,x1000-phy", .data = &x1000_soc_info },
{ .compatible = "ingenic,x1830-phy", .data = &x1830_soc_info },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ingenic_usb_phy_of_matches);
static int jz4770_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
......@@ -176,18 +294,24 @@ static int jz4770_phy_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
priv->soc_info = device_get_match_data(&pdev->dev);
if (!priv->soc_info) {
dev_err(&pdev->dev, "Error: No device match found\n");
return -ENODEV;
}
platform_set_drvdata(pdev, priv);
priv->dev = dev;
priv->phy.dev = dev;
priv->phy.otg = &priv->otg;
priv->phy.label = "jz4770-phy";
priv->phy.init = jz4770_phy_init;
priv->phy.shutdown = jz4770_phy_shutdown;
priv->phy.label = "ingenic-usb-phy";
priv->phy.init = ingenic_usb_phy_init;
priv->phy.shutdown = ingenic_usb_phy_shutdown;
priv->otg.state = OTG_STATE_UNDEFINED;
priv->otg.usb_phy = &priv->phy;
priv->otg.set_host = jz4770_phy_set_host;
priv->otg.set_peripheral = jz4770_phy_set_peripheral;
priv->otg.set_host = ingenic_usb_phy_set_host;
priv->otg.set_peripheral = ingenic_usb_phy_set_peripheral;
priv->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->base)) {
......@@ -218,26 +342,20 @@ static int jz4770_phy_probe(struct platform_device *pdev)
return err;
}
return devm_add_action_or_reset(dev, jz4770_phy_remove, &priv->phy);
return devm_add_action_or_reset(dev, ingenic_usb_phy_remove, &priv->phy);
}
#ifdef CONFIG_OF
static const struct of_device_id jz4770_phy_of_matches[] = {
{ .compatible = "ingenic,jz4770-phy" },
{ }
};
MODULE_DEVICE_TABLE(of, jz4770_phy_of_matches);
#endif
static struct platform_driver jz4770_phy_driver = {
static struct platform_driver ingenic_phy_driver = {
.probe = jz4770_phy_probe,
.driver = {
.name = "jz4770-phy",
.of_match_table = of_match_ptr(jz4770_phy_of_matches),
.of_match_table = of_match_ptr(ingenic_usb_phy_of_matches),
},
};
module_platform_driver(jz4770_phy_driver);
module_platform_driver(ingenic_phy_driver);
MODULE_AUTHOR("周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>");
MODULE_AUTHOR("漆鹏振 (Qi Pengzhen) <aric.pzqi@ingenic.com>");
MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
MODULE_DESCRIPTION("Ingenic JZ4770 USB PHY driver");
MODULE_DESCRIPTION("Ingenic SoCs USB PHY driver");
MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment