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. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEElLzh7wn96CXwjh2IzL64meEamQYFAl8etZ0ACgkQzL64meEa mQYbWA/+OcnH1oCqG1uD4M4NUzwAX8yM+ObEQ26e1K6r5z7G3kdqKbiOuH7Ksw35 VeHaP9XuOVtTMF+YBJptINeuwmF+KMJen+So3x5b6JOdTHdVsu/RbSBmZUPhHeUU rUJbJlDPKUcms7IsPGKtNaKkqcBDvVUgh3LvqB7UOjMRHemf7rXp3Mp6Jvx1GrhJ C5eUqpNsPWd3JykYCfsyxGN1+crkSDRZ6EEBKRw2CLq0IaRLesK0SRX91vuD/YUV XBgLauop1MQr/cJR9OWEaFhOGNTDPvP5ijKJ98JYfGDmT5fxJh+XgYHm0LOqEy9X Vjsifox2tFqIhs8a9M4Xuk5CH/wAS07VUaS7zhHvI1k+QbExJPBkT8wFqPRHUVwq sfIAy9oHA/rAOnpq4RolBZUBgypKO9qkSIbozJ0aEEMcFWqcC1zvj5+8LBZx/VG5 WiTlBqGyTaNClaT9FSJqJ3JlRdl7Sm719WpqJ4fUpHhlTXIv0PPhf5ov6a/nOYH4 qjmF5LlK9Tf96iEoVTS+5EJkos2jyWX2RHNrQBMY+mm9Du2DrmzAE4tNqz8Em/yC rUUua8L0n0popcQAQqeLw0GX7xgol+K9smAULXlWLi3+TCVOHpousB3uMCca8cIw ERxAL1mPYMUXsravwu517Ky0lDDW429O/7Gk3YyzSAjAx3zn/Sw= =xNwh -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
e98ba8cc3f
|
@ -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
|
||||
|
||||
dwc3:
|
||||
dma-coherent: true
|
||||
|
||||
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 @@ err:
|
|||
*/
|
||||
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,83 +124,95 @@ 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.
|
||||
*/
|
||||
int cdns3_drd_host_on(struct cdns3 *cdns)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
/* 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)
|
||||
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_drd_gadget_on - start gadget.
|
||||
* @cdns: Pointer to controller context structure.
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
*/
|
||||
int cdns3_drd_switch_host(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_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
|
||||
writel(OTGCMD_DEV_BUS_REQ | reg, &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) {
|
||||
dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
|
||||
|
||||
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
|
||||
val & OTGSTS_DEV_READY,
|
||||
1, 100000);
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "timeout waiting for dev_ready\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns3_drd_switch_gadget - start/stop gadget
|
||||
* @cdns: Pointer to controller context structure
|
||||
* @on: 1 for start, 0 for stop
|
||||
*
|
||||
* Returns 0 on success otherwise negative errno
|
||||
* cdns3_drd_gadget_off - stop gadget.
|
||||
* @cdns: Pointer to controller context structure.
|
||||
*/
|
||||
int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
|
||||
void cdns3_drd_gadget_off(struct cdns3 *cdns)
|
||||
{
|
||||
int ret, val;
|
||||
u32 reg = OTGCMD_OTG_DIS;
|
||||
u32 val;
|
||||
|
||||
/* 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");
|
||||
|
||||
ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
|
||||
val & OTGSTS_DEV_READY,
|
||||
1, 100000);
|
||||
if (ret) {
|
||||
dev_err(cdns->dev, "timeout waiting for dev_ready\n");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* 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 |
|
||||
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
|
||||
&cdns->otg_regs->cmd);
|
||||
/* Waiting till DEV_IDLE state.*/
|
||||
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
|
||||
!(val & OTGSTATE_DEV_STATE_MASK),
|
||||
1, 2000000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
/*
|
||||
* 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 |
|
||||
OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
|
||||
&cdns->otg_regs->cmd);
|
||||
/* Waiting till DEV_IDLE state.*/
|
||||
readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
|
||||
!(val & OTGSTATE_DEV_STATE_MASK),
|
||||
1, 2000000);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
||||
cdns3_ep0_complete_setup(priv_dev, 0, 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);
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
// 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,7 +603,8 @@ error_init:
|
|||
if (hsotg->params.activate_stm_id_vb_detection)
|
||||
regulator_disable(hsotg->usb33d);
|
||||
error:
|
||||
dwc2_lowlevel_hw_disable(hsotg);
|
||||
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 @@ err_phys_exit:
|
|||
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;
|
||||
|
||||
return -ETIMEDOUT;
|
||||
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 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;
|
||||
|
||||
clk_disable_unprepare(bdc->clk);
|
||||
return 0;
|
||||
/* Halt the controller */
|
||||
ret = bdc_stop(bdc);
|
||||
if (!ret)
|
||||
clk_disable_unprepare(bdc->clk);
|
||||
|
||||
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,10 +758,13 @@ 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 ? */
|
||||
ret = bdc_stop_ep(bdc, ep->ep_num);
|
||||
/* if there is an issue with stopping ep, then no need to go further */
|
||||
if (ret)
|
||||
/* 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, then no need to go further */
|
||||
if (ret)
|
||||
return 0;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
// 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,66 +14,95 @@
|
|||
#include <linux/usb/otg.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#define REG_USBPCR_OFFSET 0x00
|
||||
#define REG_USBRDT_OFFSET 0x04
|
||||
#define REG_USBVBFIL_OFFSET 0x08
|
||||
#define REG_USBPCR1_OFFSET 0x0c
|
||||
/* OTGPHY register offsets */
|
||||
#define REG_USBPCR_OFFSET 0x00
|
||||
#define REG_USBRDT_OFFSET 0x04
|
||||
#define REG_USBVBFIL_OFFSET 0x08
|
||||
#define REG_USBPCR1_OFFSET 0x0c
|
||||
|
||||
/* USBPCR */
|
||||
#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)
|
||||
#define USBPCR_POR BIT(22)
|
||||
#define USBPCR_SIDDQ BIT(21)
|
||||
#define USBPCR_OTG_DISABLE BIT(20)
|
||||
#define USBPCR_TXPREEMPHTUNE BIT(6)
|
||||
/* bits within the USBPCR register */
|
||||
#define USBPCR_USB_MODE BIT(31)
|
||||
#define USBPCR_AVLD_REG BIT(30)
|
||||
#define USBPCR_COMMONONN BIT(25)
|
||||
#define USBPCR_VBUSVLDEXT BIT(24)
|
||||
#define USBPCR_VBUSVLDEXTSEL BIT(23)
|
||||
#define USBPCR_POR BIT(22)
|
||||
#define USBPCR_SIDDQ BIT(21)
|
||||
#define USBPCR_OTG_DISABLE BIT(20)
|
||||
#define USBPCR_TXPREEMPHTUNE BIT(6)
|
||||
|
||||
#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_MASK GENMASK(29, 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_LSB 17
|
||||
#define USBPCR_COMPDISTUNE_MASK GENMASK(19, USBPCR_COMPDISTUNE_LSB)
|
||||
#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_LSB 14
|
||||
#define USBPCR_OTGTUNE_MASK GENMASK(16, USBPCR_OTGTUNE_LSB)
|
||||
#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_MASK GENMASK(13, USBPCR_SQRXTUNE_LSB)
|
||||
#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_LSB 7
|
||||
#define USBPCR_TXFSLSTUNE_MASK GENMASK(10, USBPCR_TXFSLSTUNE_LSB)
|
||||
#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_TXRISETUNE_LSB 4
|
||||
#define USBPCR_TXRISETUNE_MASK GENMASK(5, USBPCR_TXRISETUNE_LSB)
|
||||
#define USBPCR_TXRISETUNE_DFT 3
|
||||
#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_TXVREFTUNE_LSB 0
|
||||
#define USBPCR_TXVREFTUNE_MASK GENMASK(3, USBPCR_TXVREFTUNE_LSB)
|
||||
#define USBPCR_TXVREFTUNE_DFT 5
|
||||
#define USBPCR_TXRISETUNE_LSB 4
|
||||
#define USBPCR_TXRISETUNE_MASK GENMASK(5, USBPCR_TXRISETUNE_LSB)
|
||||
#define USBPCR_TXRISETUNE_DFT (0x3 << USBPCR_TXRISETUNE_LSB)
|
||||
|
||||
/* USBRDT */
|
||||
#define USBRDT_VBFIL_LD_EN BIT(25)
|
||||
#define USBRDT_IDDIG_EN BIT(24)
|
||||
#define USBRDT_IDDIG_REG BIT(23)
|
||||
#define USBPCR_TXVREFTUNE_LSB 0
|
||||
#define USBPCR_TXVREFTUNE_MASK GENMASK(3, USBPCR_TXVREFTUNE_LSB)
|
||||
#define USBPCR_TXVREFTUNE_INC_25PPT (0x7 << USBPCR_TXVREFTUNE_LSB)
|
||||
#define USBPCR_TXVREFTUNE_DFT (0x5 << USBPCR_TXVREFTUNE_LSB)
|
||||
|
||||
#define USBRDT_USBRDT_LSB 0
|
||||
#define USBRDT_USBRDT_MASK GENMASK(22, USBRDT_USBRDT_LSB)
|
||||
/* 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)
|
||||
|
||||
/* USBPCR1 */
|
||||
#define USBPCR1_UHC_POWON BIT(5)
|
||||
/* 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,
|
||||
};
|
||||
|
||||
struct ingenic_soc_info {
|
||||
enum ingenic_usb_phy_version version;
|
||||
|
||||
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");
|
||||
|
|
Loading…
Reference in New Issue