USB patches for 4.2-rc1
Here's the big USB patchset for 4.2-rc1. As is normal these days, the majority of changes are in the gadget drivers, with a bunch of other small driver changes. All of these have been in linux-next with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iEYEABECAAYFAlWNobIACgkQMUfUDdst+ylXtQCgwTnzFBzly+3h1Npa2CWkr/Lw TWAAn31qEP28MLjm8iVJLNPwdVd2kt1w =hrdz -----END PGP SIGNATURE----- Merge tag 'usb-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb Pull USB updates from Greg KH: "Here's the big USB patchset for 4.2-rc1. As is normal these days, the majority of changes are in the gadget drivers, with a bunch of other small driver changes. All of these have been in linux-next with no reported issues" * tag 'usb-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (175 commits) usb: dwc3: Use ASCII space in Kconfig usb: chipidea: add work-around for Marvell HSIC PHY startup usb: chipidea: allow multiple instances to use default ci_default_pdata dt-bindings: Consolidate ChipIdea USB ci13xxx bindings phy: add Marvell HSIC 28nm PHY phy: Add Marvell USB 2.0 OTG 28nm PHY dt-bindings: Add Marvell PXA1928 USB and HSIC PHY bindings USB: ssb: use devm_kzalloc USB: ssb: fix error handling in ssb_hcd_create_pdev() usb: isp1760: check for null return from kzalloc cdc-acm: Add support of ATOL FPrint fiscal printers usb: chipidea: usbmisc_imx: Remove unneeded semicolon USB: usbtmc: add device quirk for Rigol DS6104 USB: serial: mos7840: Use setup_timer phy: twl4030-usb: add ABI documentation phy: twl4030-usb: remove incorrect pm_runtime_get_sync() in probe function. phy: twl4030-usb: remove pointless 'suspended' test in 'suspend' callback. phy: twl4030-usb: make runtime pm more reliable. drivers:usb:fsl: Fix compilation error for fsl ehci drv usb: renesas_usbhs: Don't disable the pipe if Control write status stage ...
This commit is contained in:
commit
2a298679b4
8
Documentation/ABI/testing/sysfs-platform-twl4030-usb
Normal file
8
Documentation/ABI/testing/sysfs-platform-twl4030-usb
Normal file
@ -0,0 +1,8 @@
|
||||
What: /sys/bus/platform/devices/*twl4030-usb/vbus
|
||||
Description:
|
||||
Read-only status reporting if VBUS (approx 5V)
|
||||
is being supplied by the USB bus.
|
||||
|
||||
Possible values: "on", "off".
|
||||
|
||||
Changes are notified via select/poll.
|
@ -0,0 +1,40 @@
|
||||
* Broadcom SATA3 PHY for STB
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one or more of
|
||||
"brcm,bcm7445-sata-phy"
|
||||
"brcm,phy-sata3"
|
||||
- address-cells: should be 1
|
||||
- size-cells: should be 0
|
||||
- reg: register range for the PHY PCB interface
|
||||
- reg-names: should be "phy"
|
||||
|
||||
Sub-nodes:
|
||||
Each port's PHY should be represented as a sub-node.
|
||||
|
||||
Sub-nodes required properties:
|
||||
- reg: the PHY number
|
||||
- phy-cells: generic PHY binding; must be 0
|
||||
Optional:
|
||||
- brcm,enable-ssc: use spread spectrum clocking (SSC) on this port
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
sata-phy@f0458100 {
|
||||
compatible = "brcm,bcm7445-sata-phy", "brcm,phy-sata3";
|
||||
reg = <0xf0458100 0x1e00>, <0xf045804c 0x10>;
|
||||
reg-names = "phy";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
sata-phy@0 {
|
||||
reg = <0>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
sata-phy@1 {
|
||||
reg = <1>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
};
|
18
Documentation/devicetree/bindings/phy/pxa1928-usb-phy.txt
Normal file
18
Documentation/devicetree/bindings/phy/pxa1928-usb-phy.txt
Normal file
@ -0,0 +1,18 @@
|
||||
* Marvell PXA1928 USB and HSIC PHYs
|
||||
|
||||
Required properties:
|
||||
- compatible: "marvell,pxa1928-usb-phy" or "marvell,pxa1928-hsic-phy"
|
||||
- reg: base address and length of the registers
|
||||
- clocks - A single clock. From common clock binding.
|
||||
- #phys-cells: should be 0. From commmon phy binding.
|
||||
- resets: reference to the reset controller
|
||||
|
||||
Example:
|
||||
|
||||
usbphy: phy@7000 {
|
||||
compatible = "marvell,pxa1928-usb-phy";
|
||||
reg = <0x7000 0xe0>;
|
||||
clocks = <&apmu_clocks PXA1928_CLK_USB>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ This file provides information on what the device node for the R-Car generation
|
||||
Required properties:
|
||||
- compatible: "renesas,usb-phy-r8a7790" if the device is a part of R8A7790 SoC.
|
||||
"renesas,usb-phy-r8a7791" if the device is a part of R8A7791 SoC.
|
||||
"renesas,usb-phy-r8a7794" if the device is a part of R8A7794 SoC.
|
||||
- reg: offset and length of the register block.
|
||||
- #address-cells: number of address cells for the USB channel subnodes, must
|
||||
be <1>.
|
||||
|
@ -1,5 +1,15 @@
|
||||
TWL BCI (Battery Charger Interface)
|
||||
|
||||
The battery charger needs to interact with the USB phy in order
|
||||
to know when charging is permissible, and when there is a connection
|
||||
or disconnection.
|
||||
|
||||
The choice of phy cannot be configured at a hardware level, so there
|
||||
is no value in explicit configuration in device-tree. Rather
|
||||
if there is a sibling of the BCI node which is compatible with
|
||||
"ti,twl4030-usb", then that is used to determine when and how
|
||||
use USB power for charging.
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
- "ti,twl4030-bci"
|
||||
|
@ -1,35 +0,0 @@
|
||||
* Freescale i.MX ci13xxx usb controllers
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "fsl,imx27-usb"
|
||||
- reg: Should contain registers location and length
|
||||
- interrupts: Should contain controller interrupt
|
||||
- fsl,usbphy: phandle of usb phy that connects to the port
|
||||
|
||||
Recommended properies:
|
||||
- phy_type: the type of the phy connected to the core. Should be one
|
||||
of "utmi", "utmi_wide", "ulpi", "serial" or "hsic". Without this
|
||||
property the PORTSC register won't be touched
|
||||
- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
|
||||
|
||||
Optional properties:
|
||||
- fsl,usbmisc: phandler of non-core register device, with one argument
|
||||
that indicate usb controller index
|
||||
- vbus-supply: regulator for vbus
|
||||
- disable-over-current: disable over current detect
|
||||
- external-vbus-divider: enables off-chip resistor divider for Vbus
|
||||
- maximum-speed: limit the maximum connection speed to "full-speed".
|
||||
- tpl-support: TPL (Targeted Peripheral List) feature for targeted hosts
|
||||
|
||||
Examples:
|
||||
usb@02184000 { /* USB OTG */
|
||||
compatible = "fsl,imx6q-usb", "fsl,imx27-usb";
|
||||
reg = <0x02184000 0x200>;
|
||||
interrupts = <0 43 0x04>;
|
||||
fsl,usbphy = <&usbphy1>;
|
||||
fsl,usbmisc = <&usbmisc 0>;
|
||||
disable-over-current;
|
||||
external-vbus-divider;
|
||||
maximum-speed = "full-speed";
|
||||
tpl-support;
|
||||
};
|
@ -1,17 +0,0 @@
|
||||
Qualcomm CI13xxx (Chipidea) USB controllers
|
||||
|
||||
Required properties:
|
||||
- compatible: should contain "qcom,ci-hdrc"
|
||||
- reg: offset and length of the register set in the memory map
|
||||
- interrupts: interrupt-specifier for the controller interrupt.
|
||||
- usb-phy: phandle for the PHY device
|
||||
- dr_mode: Should be "peripheral"
|
||||
|
||||
Examples:
|
||||
gadget@f9a55000 {
|
||||
compatible = "qcom,ci-hdrc";
|
||||
reg = <0xf9a55000 0x400>;
|
||||
dr_mode = "peripheral";
|
||||
interrupts = <0 134 0>;
|
||||
usb-phy = <&usbphy0>;
|
||||
};
|
@ -1,15 +1,35 @@
|
||||
* USB2 ChipIdea USB controller for ci13xxx
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "chipidea,usb2"
|
||||
- compatible: should be one of:
|
||||
"fsl,imx27-usb"
|
||||
"lsi,zevio-usb"
|
||||
"qcom,ci-hdrc"
|
||||
"chipidea,usb2"
|
||||
- reg: base address and length of the registers
|
||||
- interrupts: interrupt for the USB controller
|
||||
|
||||
Recommended properies:
|
||||
- phy_type: the type of the phy connected to the core. Should be one
|
||||
of "utmi", "utmi_wide", "ulpi", "serial" or "hsic". Without this
|
||||
property the PORTSC register won't be touched.
|
||||
- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
|
||||
|
||||
Deprecated properties:
|
||||
- usb-phy: phandle for the PHY device. Use "phys" instead.
|
||||
- fsl,usbphy: phandle of usb phy that connects to the port. Use "phys" instead.
|
||||
|
||||
Optional properties:
|
||||
- clocks: reference to the USB clock
|
||||
- phys: reference to the USB PHY
|
||||
- phy-names: should be "usb-phy"
|
||||
- vbus-supply: reference to the VBUS regulator
|
||||
- maximum-speed: limit the maximum connection speed to "full-speed".
|
||||
- tpl-support: TPL (Targeted Peripheral List) feature for targeted hosts
|
||||
- fsl,usbmisc: (FSL only) phandler of non-core register device, with one
|
||||
argument that indicate usb controller index
|
||||
- disable-over-current: (FSL only) disable over current detect
|
||||
- external-vbus-divider: (FSL only) enables off-chip resistor divider for Vbus
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
* LSI Zevio USB OTG Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "lsi,zevio-usb"
|
||||
- reg: Should contain registers location and length
|
||||
- interrupts: Should contain controller interrupt
|
||||
|
||||
Optional properties:
|
||||
- vbus-supply: regulator for vbus
|
||||
|
||||
Examples:
|
||||
usb0: usb@b0000000 {
|
||||
reg = <0xb0000000 0x1000>;
|
||||
compatible = "lsi,zevio-usb";
|
||||
interrupts = <8>;
|
||||
vbus-supply = <&vbus_reg>;
|
||||
};
|
@ -49,8 +49,7 @@ st_dwc3: dwc3@8f94000 {
|
||||
st,syscfg = <&syscfg_core>;
|
||||
resets = <&powerdown STIH407_USB3_POWERDOWN>,
|
||||
<&softreset STIH407_MIPHY2_SOFTRESET>;
|
||||
reset-names = "powerdown",
|
||||
"softreset";
|
||||
reset-names = "powerdown", "softreset";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
pinctrl-names = "default";
|
||||
@ -62,7 +61,7 @@ st_dwc3: dwc3@8f94000 {
|
||||
reg = <0x09900000 0x100000>;
|
||||
interrupts = <GIC_SPI 155 IRQ_TYPE_NONE>;
|
||||
dr_mode = "host";
|
||||
phys-names = "usb2-phy", "usb3-phy";
|
||||
phys = <&usb2_picophy2>, <&phy_port2 MIPHY_TYPE_USB>;
|
||||
phy-names = "usb2-phy", "usb3-phy";
|
||||
phys = <&usb2_picophy2>, <&phy_port2 PHY_TYPE_USB3>;
|
||||
};
|
||||
};
|
||||
|
@ -38,6 +38,8 @@ Optional properties:
|
||||
- snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
|
||||
utmi_l1_suspend_n, false when asserts utmi_sleep_n
|
||||
- snps,hird-threshold: HIRD threshold
|
||||
- snps,hsphy_interface: High-Speed PHY interface selection between "utmi" for
|
||||
UTMI+ and "ulpi" for ULPI when the DWC_USB3_HSPHY_INTERFACE has value 3.
|
||||
|
||||
This is usually a subnode to DWC3 glue to which it is connected.
|
||||
|
||||
|
@ -69,6 +69,17 @@ Optional properties:
|
||||
(no, min, max) where each value represents either a voltage
|
||||
in microvolts or a value corresponding to voltage corner.
|
||||
|
||||
- qcom,manual-pullup: If present, vbus is not routed to USB controller/phy
|
||||
and controller driver therefore enables pull-up explicitly
|
||||
before starting controller using usbcmd run/stop bit.
|
||||
|
||||
- extcon: phandles to external connector devices. First phandle
|
||||
should point to external connector, which provide "USB"
|
||||
cable events, the second should point to external connector
|
||||
device, which provide "USB-HOST" cable events. If one of
|
||||
the external connector devices is not required empty <0>
|
||||
phandle should be specified.
|
||||
|
||||
Example HSUSB OTG controller device node:
|
||||
|
||||
usb@f9a55000 {
|
||||
|
@ -4,6 +4,7 @@ Required properties:
|
||||
- compatible: Must contain one of the following:
|
||||
- "renesas,usbhs-r8a7790"
|
||||
- "renesas,usbhs-r8a7791"
|
||||
- "renesas,usbhs-r8a7794"
|
||||
- reg: Base address and length of the register for the USBHS
|
||||
- interrupts: Interrupt specifier for the USBHS
|
||||
- clocks: A list of phandle + clock specifier pairs
|
||||
|
@ -30,6 +30,9 @@ TWL4030 USB PHY AND COMPARATOR
|
||||
- usb_mode : The mode used by the phy to connect to the controller. "1"
|
||||
specifies "ULPI" mode and "2" specifies "CEA2011_3PIN" mode.
|
||||
|
||||
If a sibling node is compatible "ti,twl4030-bci", then it will find
|
||||
this device and query it for USB power status.
|
||||
|
||||
twl4030-usb {
|
||||
compatible = "ti,twl4030-usb";
|
||||
interrupts = < 10 4 >;
|
||||
|
@ -13,6 +13,8 @@ Optional properties:
|
||||
- big-endian-desc : boolean, set this for hcds with big-endian descriptors
|
||||
- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
|
||||
- needs-reset-on-resume : boolean, set this to force EHCI reset after resume
|
||||
- has-transaction-translator : boolean, set this if EHCI have a Transaction
|
||||
Translator built into the root hub.
|
||||
- clocks : a list of phandle + clock specifier pairs
|
||||
- phys : phandle + phy specifier pair
|
||||
- phy-names : "usb"
|
||||
|
@ -76,6 +76,8 @@ struct phy *phy_get(struct device *dev, const char *string);
|
||||
struct phy *phy_optional_get(struct device *dev, const char *string);
|
||||
struct phy *devm_phy_get(struct device *dev, const char *string);
|
||||
struct phy *devm_phy_optional_get(struct device *dev, const char *string);
|
||||
struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
|
||||
int index);
|
||||
|
||||
phy_get, phy_optional_get, devm_phy_get and devm_phy_optional_get can
|
||||
be used to get the PHY. In the case of dt boot, the string arguments
|
||||
@ -86,7 +88,10 @@ successful PHY get. On driver detach, release function is invoked on
|
||||
the the devres data and devres data is freed. phy_optional_get and
|
||||
devm_phy_optional_get should be used when the phy is optional. These
|
||||
two functions will never return -ENODEV, but instead returns NULL when
|
||||
the phy cannot be found.
|
||||
the phy cannot be found.Some generic drivers, such as ehci, may use multiple
|
||||
phys and for such drivers referencing phy(s) by name(s) does not make sense. In
|
||||
this case, devm_of_phy_get_by_index can be used to get a phy reference based on
|
||||
the index.
|
||||
|
||||
It should be noted that NULL is a valid phy reference. All phy
|
||||
consumer calls on the NULL phy become NOPs. That is the release calls,
|
||||
|
@ -526,8 +526,6 @@ Except for ifname they can be written to until the function is linked to a
|
||||
configuration. The ifname is read-only and contains the name of the interface
|
||||
which was assigned by the net core, e. g. usb0.
|
||||
|
||||
By default there can be only 1 RNDIS interface in the system.
|
||||
|
||||
Testing the RNDIS function
|
||||
--------------------------
|
||||
|
||||
@ -629,7 +627,7 @@ Function-specific configfs interface
|
||||
The function name to use when creating the function directory is "uac2".
|
||||
The uac2 function provides these attributes in its function directory:
|
||||
|
||||
chmask - capture channel mask
|
||||
c_chmask - capture channel mask
|
||||
c_srate - capture sampling rate
|
||||
c_ssize - capture sample size (bytes)
|
||||
p_chmask - playback channel mask
|
||||
|
@ -10712,6 +10712,13 @@ S: Maintained
|
||||
F: Documentation/video4linux/zr364xx.txt
|
||||
F: drivers/media/usb/zr364xx/
|
||||
|
||||
ULPI BUS
|
||||
M: Heikki Krogerus <heikki.krogerus@linux.intel.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/usb/common/ulpi.c
|
||||
F: include/linux/ulpi/
|
||||
|
||||
USER-MODE LINUX (UML)
|
||||
M: Jeff Dike <jdike@addtoit.com>
|
||||
M: Richard Weinberger <richard@nod.at>
|
||||
|
@ -54,6 +54,26 @@ config PHY_EXYNOS_MIPI_VIDEO
|
||||
Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
|
||||
and EXYNOS SoCs.
|
||||
|
||||
config PHY_PXA_28NM_HSIC
|
||||
tristate "Marvell USB HSIC 28nm PHY Driver"
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support Marvell USB HSIC PHY driver for Marvell
|
||||
SoC. This driver will do the PHY initialization and shutdown.
|
||||
The PHY driver will be used by Marvell ehci driver.
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config PHY_PXA_28NM_USB2
|
||||
tristate "Marvell USB 2.0 28nm PHY Driver"
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support Marvell USB 2.0 PHY driver for Marvell
|
||||
SoC. This driver will do the PHY initialization and shutdown.
|
||||
The PHY driver will be used by Marvell udc/ehci/otg driver.
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config PHY_MVEBU_SATA
|
||||
def_bool y
|
||||
depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
|
||||
@ -313,4 +333,20 @@ config PHY_QCOM_UFS
|
||||
help
|
||||
Support for UFS PHY on QCOM chipsets.
|
||||
|
||||
config PHY_TUSB1210
|
||||
tristate "TI TUSB1210 ULPI PHY module"
|
||||
depends on USB_ULPI_BUS
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Support for TI TUSB1210 USB ULPI PHY.
|
||||
|
||||
config PHY_BRCMSTB_SATA
|
||||
tristate "Broadcom STB SATA PHY driver"
|
||||
depends on ARCH_BRCMSTB
|
||||
depends on OF
|
||||
select GENERIC_PHY
|
||||
help
|
||||
Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs.
|
||||
Likely useful only with CONFIG_SATA_BRCMSTB enabled.
|
||||
|
||||
endmenu
|
||||
|
@ -10,6 +10,8 @@ obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
|
||||
obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
|
||||
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
|
||||
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
|
||||
obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o
|
||||
obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o
|
||||
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
|
||||
obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o
|
||||
obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o
|
||||
@ -40,3 +42,5 @@ obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o
|
||||
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
|
||||
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
|
||||
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
|
||||
obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
|
||||
obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o
|
||||
|
216
drivers/phy/phy-brcmstb-sata.c
Normal file
216
drivers/phy/phy-brcmstb-sata.c
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Broadcom SATA3 AHCI Controller PHY Driver
|
||||
*
|
||||
* Copyright © 2009-2015 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define SATA_MDIO_BANK_OFFSET 0x23c
|
||||
#define SATA_MDIO_REG_OFFSET(ofs) ((ofs) * 4)
|
||||
#define SATA_MDIO_REG_SPACE_SIZE 0x1000
|
||||
#define SATA_MDIO_REG_LENGTH 0x1f00
|
||||
|
||||
#define MAX_PORTS 2
|
||||
|
||||
/* Register offset between PHYs in PCB space */
|
||||
#define SATA_MDIO_REG_SPACE_SIZE 0x1000
|
||||
|
||||
struct brcm_sata_port {
|
||||
int portnum;
|
||||
struct phy *phy;
|
||||
struct brcm_sata_phy *phy_priv;
|
||||
bool ssc_en;
|
||||
};
|
||||
|
||||
struct brcm_sata_phy {
|
||||
struct device *dev;
|
||||
void __iomem *phy_base;
|
||||
|
||||
struct brcm_sata_port phys[MAX_PORTS];
|
||||
};
|
||||
|
||||
enum sata_mdio_phy_regs_28nm {
|
||||
PLL_REG_BANK_0 = 0x50,
|
||||
PLL_REG_BANK_0_PLLCONTROL_0 = 0x81,
|
||||
|
||||
TXPMD_REG_BANK = 0x1a0,
|
||||
TXPMD_CONTROL1 = 0x81,
|
||||
TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0),
|
||||
TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1),
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84,
|
||||
TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff,
|
||||
};
|
||||
|
||||
static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port)
|
||||
{
|
||||
struct brcm_sata_phy *priv = port->phy_priv;
|
||||
|
||||
return priv->phy_base + (port->portnum * SATA_MDIO_REG_SPACE_SIZE);
|
||||
}
|
||||
|
||||
static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs,
|
||||
u32 msk, u32 value)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
writel(bank, addr + SATA_MDIO_BANK_OFFSET);
|
||||
tmp = readl(addr + SATA_MDIO_REG_OFFSET(ofs));
|
||||
tmp = (tmp & msk) | value;
|
||||
writel(tmp, addr + SATA_MDIO_REG_OFFSET(ofs));
|
||||
}
|
||||
|
||||
/* These defaults were characterized by H/W group */
|
||||
#define FMIN_VAL_DEFAULT 0x3df
|
||||
#define FMAX_VAL_DEFAULT 0x3df
|
||||
#define FMAX_VAL_SSC 0x83
|
||||
|
||||
static void brcm_sata_cfg_ssc_28nm(struct brcm_sata_port *port)
|
||||
{
|
||||
void __iomem *base = brcm_sata_phy_base(port);
|
||||
struct brcm_sata_phy *priv = port->phy_priv;
|
||||
u32 tmp;
|
||||
|
||||
/* override the TX spread spectrum setting */
|
||||
tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC;
|
||||
brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp);
|
||||
|
||||
/* set fixed min freq */
|
||||
brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2,
|
||||
~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK,
|
||||
FMIN_VAL_DEFAULT);
|
||||
|
||||
/* set fixed max freq depending on SSC config */
|
||||
if (port->ssc_en) {
|
||||
dev_info(priv->dev, "enabling SSC on port %d\n", port->portnum);
|
||||
tmp = FMAX_VAL_SSC;
|
||||
} else {
|
||||
tmp = FMAX_VAL_DEFAULT;
|
||||
}
|
||||
|
||||
brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3,
|
||||
~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp);
|
||||
}
|
||||
|
||||
static int brcm_sata_phy_init(struct phy *phy)
|
||||
{
|
||||
struct brcm_sata_port *port = phy_get_drvdata(phy);
|
||||
|
||||
brcm_sata_cfg_ssc_28nm(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops phy_ops_28nm = {
|
||||
.init = brcm_sata_phy_init,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct of_device_id brcm_sata_phy_of_match[] = {
|
||||
{ .compatible = "brcm,bcm7445-sata-phy" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
|
||||
|
||||
static int brcm_sata_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *dn = dev->of_node, *child;
|
||||
struct brcm_sata_phy *priv;
|
||||
struct resource *res;
|
||||
struct phy_provider *provider;
|
||||
int count = 0;
|
||||
|
||||
if (of_get_child_count(dn) == 0)
|
||||
return -ENODEV;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(dev, priv);
|
||||
priv->dev = dev;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
|
||||
priv->phy_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(priv->phy_base))
|
||||
return PTR_ERR(priv->phy_base);
|
||||
|
||||
for_each_available_child_of_node(dn, child) {
|
||||
unsigned int id;
|
||||
struct brcm_sata_port *port;
|
||||
|
||||
if (of_property_read_u32(child, "reg", &id)) {
|
||||
dev_err(dev, "missing reg property in node %s\n",
|
||||
child->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (id >= MAX_PORTS) {
|
||||
dev_err(dev, "invalid reg: %u\n", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (priv->phys[id].phy) {
|
||||
dev_err(dev, "already registered port %u\n", id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
port = &priv->phys[id];
|
||||
port->portnum = id;
|
||||
port->phy_priv = priv;
|
||||
port->phy = devm_phy_create(dev, child, &phy_ops_28nm);
|
||||
port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc");
|
||||
if (IS_ERR(port->phy)) {
|
||||
dev_err(dev, "failed to create PHY\n");
|
||||
return PTR_ERR(port->phy);
|
||||
}
|
||||
|
||||
phy_set_drvdata(port->phy, port);
|
||||
count++;
|
||||
}
|
||||
|
||||
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
|
||||
if (IS_ERR(provider)) {
|
||||
dev_err(dev, "could not register PHY provider\n");
|
||||
return PTR_ERR(provider);
|
||||
}
|
||||
|
||||
dev_info(dev, "registered %d port(s)\n", count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver brcm_sata_phy_driver = {
|
||||
.probe = brcm_sata_phy_probe,
|
||||
.driver = {
|
||||
.of_match_table = brcm_sata_phy_of_match,
|
||||
.name = "brcmstb-sata-phy",
|
||||
}
|
||||
};
|
||||
module_platform_driver(brcm_sata_phy_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Broadcom STB SATA PHY driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Marc Carino");
|
||||
MODULE_AUTHOR("Brian Norris");
|
||||
MODULE_ALIAS("platform:phy-brcmstb-sata");
|
@ -367,13 +367,21 @@ static struct phy *_of_phy_get(struct device_node *np, int index)
|
||||
phy_provider = of_phy_provider_lookup(args.np);
|
||||
if (IS_ERR(phy_provider) || !try_module_get(phy_provider->owner)) {
|
||||
phy = ERR_PTR(-EPROBE_DEFER);
|
||||
goto err0;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (!of_device_is_available(args.np)) {
|
||||
dev_warn(phy_provider->dev, "Requested PHY is disabled\n");
|
||||
phy = ERR_PTR(-ENODEV);
|
||||
goto out_put_module;
|
||||
}
|
||||
|
||||
phy = phy_provider->of_xlate(phy_provider->dev, &args);
|
||||
|
||||
out_put_module:
|
||||
module_put(phy_provider->owner);
|
||||
|
||||
err0:
|
||||
out_unlock:
|
||||
mutex_unlock(&phy_provider_mutex);
|
||||
of_node_put(args.np);
|
||||
|
||||
@ -622,6 +630,38 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_of_phy_get);
|
||||
|
||||
/**
|
||||
* devm_of_phy_get_by_index() - lookup and obtain a reference to a phy by index.
|
||||
* @dev: device that requests this phy
|
||||
* @np: node containing the phy
|
||||
* @index: index of the phy
|
||||
*
|
||||
* Gets the phy using _of_phy_get(), and associates a device with it using
|
||||
* devres. On driver detach, release function is invoked on the devres data,
|
||||
* then, devres data is freed.
|
||||
*
|
||||
*/
|
||||
struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
|
||||
int index)
|
||||
{
|
||||
struct phy **ptr, *phy;
|
||||
|
||||
ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
phy = _of_phy_get(np, index);
|
||||
if (!IS_ERR(phy)) {
|
||||
*ptr = phy;
|
||||
devres_add(dev, ptr);
|
||||
} else {
|
||||
devres_free(ptr);
|
||||
}
|
||||
|
||||
return phy;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_of_phy_get_by_index);
|
||||
|
||||
/**
|
||||
* phy_create() - create a new phy
|
||||
* @dev: device that is creating the new phy
|
||||
@ -651,16 +691,6 @@ struct phy *phy_create(struct device *dev, struct device_node *node,
|
||||
goto free_phy;
|
||||
}
|
||||
|
||||
/* phy-supply */
|
||||
phy->pwr = regulator_get_optional(dev, "phy");
|
||||
if (IS_ERR(phy->pwr)) {
|
||||
if (PTR_ERR(phy->pwr) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto free_ida;
|
||||
}
|
||||
phy->pwr = NULL;
|
||||
}
|
||||
|
||||
device_initialize(&phy->dev);
|
||||
mutex_init(&phy->mutex);
|
||||
|
||||
@ -674,6 +704,16 @@ struct phy *phy_create(struct device *dev, struct device_node *node,
|
||||
if (ret)
|
||||
goto put_dev;
|
||||
|
||||
/* phy-supply */
|
||||
phy->pwr = regulator_get_optional(&phy->dev, "phy");
|
||||
if (IS_ERR(phy->pwr)) {
|
||||
ret = PTR_ERR(phy->pwr);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto put_dev;
|
||||
|
||||
phy->pwr = NULL;
|
||||
}
|
||||
|
||||
ret = device_add(&phy->dev);
|
||||
if (ret)
|
||||
goto put_dev;
|
||||
@ -689,9 +729,6 @@ put_dev:
|
||||
put_device(&phy->dev); /* calls phy_release() which frees resources */
|
||||
return ERR_PTR(ret);
|
||||
|
||||
free_ida:
|
||||
ida_simple_remove(&phy_ida, phy->id);
|
||||
|
||||
free_phy:
|
||||
kfree(phy);
|
||||
return ERR_PTR(ret);
|
||||
|
@ -367,7 +367,7 @@ static struct miphy28lp_pll_gen pcie_pll_gen[] = {
|
||||
|
||||
static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy)
|
||||
{
|
||||
void *base = miphy_phy->base;
|
||||
void __iomem *base = miphy_phy->base;
|
||||
u8 val;
|
||||
|
||||
/* Putting Macro in reset */
|
||||
@ -391,7 +391,7 @@ static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy)
|
||||
static inline void miphy28lp_pll_calibration(struct miphy28lp_phy *miphy_phy,
|
||||
struct pll_ratio *pll_ratio)
|
||||
{
|
||||
void *base = miphy_phy->base;
|
||||
void __iomem *base = miphy_phy->base;
|
||||
u8 val;
|
||||
|
||||
/* Applying PLL Settings */
|
||||
@ -1107,11 +1107,6 @@ static struct phy *miphy28lp_xlate(struct device *dev,
|
||||
struct device_node *phynode = args->np;
|
||||
int ret, index = 0;
|
||||
|
||||
if (!of_device_is_available(phynode)) {
|
||||
dev_warn(dev, "Requested PHY is disabled\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
if (args->args_count != 1) {
|
||||
dev_err(dev, "Invalid number of cells in 'phy' property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -441,8 +441,8 @@ static int miphy365x_init(struct phy *phy)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy,
|
||||
int index)
|
||||
static int miphy365x_get_addr(struct device *dev,
|
||||
struct miphy365x_phy *miphy_phy, int index)
|
||||
{
|
||||
struct device_node *phynode = miphy_phy->phy->dev.of_node;
|
||||
const char *name;
|
||||
@ -476,11 +476,6 @@ static struct phy *miphy365x_xlate(struct device *dev,
|
||||
struct device_node *phynode = args->np;
|
||||
int ret, index;
|
||||
|
||||
if (!of_device_is_available(phynode)) {
|
||||
dev_warn(dev, "Requested PHY is disabled\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
if (args->args_count != 1) {
|
||||
dev_err(dev, "Invalid number of cells in 'phy' property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
220
drivers/phy/phy-pxa-28nm-hsic.c
Normal file
220
drivers/phy/phy-pxa-28nm-hsic.c
Normal file
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Linaro, Ltd.
|
||||
* Rob Herring <robh@kernel.org>
|
||||
*
|
||||
* Based on vendor driver:
|
||||
* Copyright (C) 2013 Marvell Inc.
|
||||
* Author: Chao Xie <xiechao.mail@gmail.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
#define PHY_28NM_HSIC_CTRL 0x08
|
||||
#define PHY_28NM_HSIC_IMPCAL_CAL 0x18
|
||||
#define PHY_28NM_HSIC_PLL_CTRL01 0x1c
|
||||
#define PHY_28NM_HSIC_PLL_CTRL2 0x20
|
||||
#define PHY_28NM_HSIC_INT 0x28
|
||||
|
||||
#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26
|
||||
#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0
|
||||
#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9
|
||||
|
||||
#define PHY_28NM_HSIC_S2H_PU_PLL BIT(10)
|
||||
#define PHY_28NM_HSIC_H2S_PLL_LOCK BIT(15)
|
||||
#define PHY_28NM_HSIC_S2H_HSIC_EN BIT(7)
|
||||
#define S2H_DRV_SE0_4RESUME BIT(14)
|
||||
#define PHY_28NM_HSIC_H2S_IMPCAL_DONE BIT(27)
|
||||
|
||||
#define PHY_28NM_HSIC_CONNECT_INT BIT(1)
|
||||
#define PHY_28NM_HSIC_HS_READY_INT BIT(2)
|
||||
|
||||
struct mv_hsic_phy {
|
||||
struct phy *phy;
|
||||
struct platform_device *pdev;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout)
|
||||
{
|
||||
timeout += jiffies;
|
||||
while (time_is_after_eq_jiffies(timeout)) {
|
||||
if ((readl(reg) & mask) == mask)
|
||||
return true;
|
||||
msleep(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int mv_hsic_phy_init(struct phy *phy)
|
||||
{
|
||||
struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
|
||||
struct platform_device *pdev = mv_phy->pdev;
|
||||
void __iomem *base = mv_phy->base;
|
||||
|
||||
clk_prepare_enable(mv_phy->clk);
|
||||
|
||||
/* Set reference clock */
|
||||
writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT |
|
||||
0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT |
|
||||
0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT,
|
||||
base + PHY_28NM_HSIC_PLL_CTRL01);
|
||||
|
||||
/* Turn on PLL */
|
||||
writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) |
|
||||
PHY_28NM_HSIC_S2H_PU_PLL,
|
||||
base + PHY_28NM_HSIC_PLL_CTRL2);
|
||||
|
||||
/* Make sure PHY PLL is locked */
|
||||
if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2,
|
||||
PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) {
|
||||
dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS.");
|
||||
clk_disable_unprepare(mv_phy->clk);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_hsic_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
|
||||
struct platform_device *pdev = mv_phy->pdev;
|
||||
void __iomem *base = mv_phy->base;
|
||||
u32 reg;
|
||||
|
||||
reg = readl(base + PHY_28NM_HSIC_CTRL);
|
||||
/* Avoid SE0 state when resume for some device will take it as reset */
|
||||
reg &= ~S2H_DRV_SE0_4RESUME;
|
||||
reg |= PHY_28NM_HSIC_S2H_HSIC_EN; /* Enable HSIC PHY */
|
||||
writel(reg, base + PHY_28NM_HSIC_CTRL);
|
||||
|
||||
/*
|
||||
* Calibration Timing
|
||||
* ____________________________
|
||||
* CAL START ___|
|
||||
* ____________________
|
||||
* CAL_DONE ___________|
|
||||
* | 400us |
|
||||
*/
|
||||
|
||||
/* Make sure PHY Calibration is ready */
|
||||
if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL,
|
||||
PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) {
|
||||
dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS.");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Waiting for HSIC connect int*/
|
||||
if (!wait_for_reg(base + PHY_28NM_HSIC_INT,
|
||||
PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) {
|
||||
dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout.");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_hsic_phy_power_off(struct phy *phy)
|
||||
{
|
||||
struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
|
||||
void __iomem *base = mv_phy->base;
|
||||
|
||||
writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN,
|
||||
base + PHY_28NM_HSIC_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_hsic_phy_exit(struct phy *phy)
|
||||
{
|
||||
struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy);
|
||||
void __iomem *base = mv_phy->base;
|
||||
|
||||
/* Turn off PLL */
|
||||
writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) &
|
||||
~PHY_28NM_HSIC_S2H_PU_PLL,
|
||||
base + PHY_28NM_HSIC_PLL_CTRL2);
|
||||
|
||||
clk_disable_unprepare(mv_phy->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct phy_ops hsic_ops = {
|
||||
.init = mv_hsic_phy_init,
|
||||
.power_on = mv_hsic_phy_power_on,
|
||||
.power_off = mv_hsic_phy_power_off,
|
||||
.exit = mv_hsic_phy_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mv_hsic_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct mv_hsic_phy *mv_phy;
|
||||
struct resource *r;
|
||||
|
||||
mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL);
|
||||
if (!mv_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
mv_phy->pdev = pdev;
|
||||
|
||||
mv_phy->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(mv_phy->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock.\n");
|
||||
return PTR_ERR(mv_phy->clk);
|
||||
}
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mv_phy->base = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(mv_phy->base))
|
||||
return PTR_ERR(mv_phy->base);
|
||||
|
||||
mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops);
|
||||
if (IS_ERR(mv_phy->phy))
|
||||
return PTR_ERR(mv_phy->phy);
|
||||
|
||||
phy_set_drvdata(mv_phy->phy, mv_phy);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id mv_hsic_phy_dt_match[] = {
|
||||
{ .compatible = "marvell,pxa1928-hsic-phy", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match);
|
||||
|
||||
static struct platform_driver mv_hsic_phy_driver = {
|
||||
.probe = mv_hsic_phy_probe,
|
||||
.driver = {
|
||||
.name = "mv-hsic-phy",
|
||||
.of_match_table = of_match_ptr(mv_hsic_phy_dt_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(mv_hsic_phy_driver);
|
||||
|
||||
MODULE_AUTHOR("Rob Herring <robh@kernel.org>");
|
||||
MODULE_DESCRIPTION("Marvell HSIC phy driver");
|
||||
MODULE_LICENSE("GPL v2");
|
355
drivers/phy/phy-pxa-28nm-usb2.c
Normal file
355
drivers/phy/phy-pxa-28nm-usb2.c
Normal file
@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Linaro, Ltd.
|
||||
* Rob Herring <robh@kernel.org>
|
||||
*
|
||||
* Based on vendor driver:
|
||||
* Copyright (C) 2013 Marvell Inc.
|
||||
* Author: Chao Xie <xiechao.mail@gmail.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
/* USB PXA1928 PHY mapping */
|
||||
#define PHY_28NM_PLL_REG0 0x0
|
||||
#define PHY_28NM_PLL_REG1 0x4
|
||||
#define PHY_28NM_CAL_REG 0x8
|
||||
#define PHY_28NM_TX_REG0 0x0c
|
||||
#define PHY_28NM_TX_REG1 0x10
|
||||
#define PHY_28NM_RX_REG0 0x14
|
||||
#define PHY_28NM_RX_REG1 0x18
|
||||
#define PHY_28NM_DIG_REG0 0x1c
|
||||
#define PHY_28NM_DIG_REG1 0x20
|
||||
#define PHY_28NM_TEST_REG0 0x24
|
||||
#define PHY_28NM_TEST_REG1 0x28
|
||||
#define PHY_28NM_MOC_REG 0x2c
|
||||
#define PHY_28NM_PHY_RESERVE 0x30
|
||||
#define PHY_28NM_OTG_REG 0x34
|
||||
#define PHY_28NM_CHRG_DET 0x38
|
||||
#define PHY_28NM_CTRL_REG0 0xc4
|
||||
#define PHY_28NM_CTRL_REG1 0xc8
|
||||
#define PHY_28NM_CTRL_REG2 0xd4
|
||||
#define PHY_28NM_CTRL_REG3 0xdc
|
||||
|
||||
/* PHY_28NM_PLL_REG0 */
|
||||
#define PHY_28NM_PLL_READY BIT(31)
|
||||
|
||||
#define PHY_28NM_PLL_SELLPFR_SHIFT 28
|
||||
#define PHY_28NM_PLL_SELLPFR_MASK (0x3 << 28)
|
||||
|
||||
#define PHY_28NM_PLL_FBDIV_SHIFT 16
|
||||
#define PHY_28NM_PLL_FBDIV_MASK (0x1ff << 16)
|
||||
|
||||
#define PHY_28NM_PLL_ICP_SHIFT 8
|
||||
#define PHY_28NM_PLL_ICP_MASK (0x7 << 8)
|
||||
|
||||
#define PHY_28NM_PLL_REFDIV_SHIFT 0
|
||||
#define PHY_28NM_PLL_REFDIV_MASK 0x7f
|
||||
|
||||
/* PHY_28NM_PLL_REG1 */
|
||||
#define PHY_28NM_PLL_PU_BY_REG BIT(1)
|
||||
|
||||
#define PHY_28NM_PLL_PU_PLL BIT(0)
|
||||
|
||||
/* PHY_28NM_CAL_REG */
|
||||
#define PHY_28NM_PLL_PLLCAL_DONE BIT(31)
|
||||
|
||||
#define PHY_28NM_PLL_IMPCAL_DONE BIT(23)
|
||||
|
||||
#define PHY_28NM_PLL_KVCO_SHIFT 16
|
||||
#define PHY_28NM_PLL_KVCO_MASK (0x7 << 16)
|
||||
|
||||
#define PHY_28NM_PLL_CAL12_SHIFT 20
|
||||
#define PHY_28NM_PLL_CAL12_MASK (0x3 << 20)
|
||||
|
||||
#define PHY_28NM_IMPCAL_VTH_SHIFT 8
|
||||
#define PHY_28NM_IMPCAL_VTH_MASK (0x7 << 8)
|
||||
|
||||
#define PHY_28NM_PLLCAL_START_SHIFT 22
|
||||
#define PHY_28NM_IMPCAL_START_SHIFT 13
|
||||
|
||||
/* PHY_28NM_TX_REG0 */
|
||||
#define PHY_28NM_TX_PU_BY_REG BIT(25)
|
||||
|
||||
#define PHY_28NM_TX_PU_ANA BIT(24)
|
||||
|
||||
#define PHY_28NM_TX_AMP_SHIFT 20
|
||||
#define PHY_28NM_TX_AMP_MASK (0x7 << 20)
|
||||
|
||||
/* PHY_28NM_RX_REG0 */
|
||||
#define PHY_28NM_RX_SQ_THRESH_SHIFT 0
|
||||
#define PHY_28NM_RX_SQ_THRESH_MASK (0xf << 0)
|
||||
|
||||
/* PHY_28NM_RX_REG1 */
|
||||
#define PHY_28NM_RX_SQCAL_DONE BIT(31)
|
||||
|
||||
/* PHY_28NM_DIG_REG0 */
|
||||
#define PHY_28NM_DIG_BITSTAFFING_ERR BIT(31)
|
||||
#define PHY_28NM_DIG_SYNC_ERR BIT(30)
|
||||
|
||||
#define PHY_28NM_DIG_SQ_FILT_SHIFT 16
|
||||
#define PHY_28NM_DIG_SQ_FILT_MASK (0x7 << 16)
|
||||
|
||||
#define PHY_28NM_DIG_SQ_BLK_SHIFT 12
|
||||
#define PHY_28NM_DIG_SQ_BLK_MASK (0x7 << 12)
|
||||
|
||||
#define PHY_28NM_DIG_SYNC_NUM_SHIFT 0
|
||||
#define PHY_28NM_DIG_SYNC_NUM_MASK (0x3 << 0)
|
||||
|
||||
#define PHY_28NM_PLL_LOCK_BYPASS BIT(7)
|
||||
|
||||
/* PHY_28NM_OTG_REG */
|
||||
#define PHY_28NM_OTG_CONTROL_BY_PIN BIT(5)
|
||||
#define PHY_28NM_OTG_PU_OTG BIT(4)
|
||||
|
||||
#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 13
|
||||
#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28 12
|
||||
#define PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 10
|
||||
#define PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 8
|
||||
#define PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 7
|
||||
#define PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28 6
|
||||
#define PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28 5
|
||||
#define PHY_28NM_CHGDTC_PD_EN_SHIFT_28 4
|
||||
#define PHY_28NM_CHGDTC_DCP_EN_SHIFT_28 3
|
||||
#define PHY_28NM_CHGDTC_CDP_EN_SHIFT_28 2
|
||||
#define PHY_28NM_CHGDTC_TESTMON_CHRGDTC_SHIFT_28 0
|
||||
|
||||
#define PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28 4
|
||||
#define PHY_28NM_CTRL1_VBUSDTC_OUT_SHIFT_28 2
|
||||
|
||||
#define PHY_28NM_CTRL3_OVERWRITE BIT(0)
|
||||
#define PHY_28NM_CTRL3_VBUS_VALID BIT(4)
|
||||
#define PHY_28NM_CTRL3_AVALID BIT(5)
|
||||
#define PHY_28NM_CTRL3_BVALID BIT(6)
|
||||
|
||||
struct mv_usb2_phy {
|
||||
struct phy *phy;
|
||||
struct platform_device *pdev;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout)
|
||||
{
|
||||
timeout += jiffies;
|
||||
while (time_is_after_eq_jiffies(timeout)) {
|
||||
if ((readl(reg) & mask) == mask)
|
||||
return true;
|
||||
msleep(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int mv_usb2_phy_28nm_init(struct phy *phy)
|
||||
{
|
||||
struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy);
|
||||
struct platform_device *pdev = mv_phy->pdev;
|
||||
void __iomem *base = mv_phy->base;
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
clk_prepare_enable(mv_phy->clk);
|
||||
|
||||
/* PHY_28NM_PLL_REG0 */
|
||||
reg = readl(base + PHY_28NM_PLL_REG0) &
|
||||
~(PHY_28NM_PLL_SELLPFR_MASK | PHY_28NM_PLL_FBDIV_MASK
|
||||
| PHY_28NM_PLL_ICP_MASK | PHY_28NM_PLL_REFDIV_MASK);
|
||||
writel(reg | (0x1 << PHY_28NM_PLL_SELLPFR_SHIFT
|
||||
| 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT
|
||||
| 0x3 << PHY_28NM_PLL_ICP_SHIFT
|
||||
| 0xd << PHY_28NM_PLL_REFDIV_SHIFT),
|
||||
base + PHY_28NM_PLL_REG0);
|
||||
|
||||
/* PHY_28NM_PLL_REG1 */
|
||||
reg = readl(base + PHY_28NM_PLL_REG1);
|
||||
writel(reg | PHY_28NM_PLL_PU_PLL | PHY_28NM_PLL_PU_BY_REG,
|
||||
base + PHY_28NM_PLL_REG1);
|
||||
|
||||
/* PHY_28NM_TX_REG0 */
|
||||
reg = readl(base + PHY_28NM_TX_REG0) & ~PHY_28NM_TX_AMP_MASK;
|
||||
writel(reg | PHY_28NM_TX_PU_BY_REG | 0x3 << PHY_28NM_TX_AMP_SHIFT |
|
||||
PHY_28NM_TX_PU_ANA,
|
||||
base + PHY_28NM_TX_REG0);
|
||||
|
||||
/* PHY_28NM_RX_REG0 */
|
||||
reg = readl(base + PHY_28NM_RX_REG0) & ~PHY_28NM_RX_SQ_THRESH_MASK;
|
||||
writel(reg | 0xa << PHY_28NM_RX_SQ_THRESH_SHIFT,
|
||||
base + PHY_28NM_RX_REG0);
|
||||
|
||||
/* PHY_28NM_DIG_REG0 */
|
||||
reg = readl(base + PHY_28NM_DIG_REG0) &
|
||||
~(PHY_28NM_DIG_BITSTAFFING_ERR | PHY_28NM_DIG_SYNC_ERR |
|
||||
PHY_28NM_DIG_SQ_FILT_MASK | PHY_28NM_DIG_SQ_BLK_MASK |
|
||||
PHY_28NM_DIG_SYNC_NUM_MASK);
|
||||
writel(reg | (0x1 << PHY_28NM_DIG_SYNC_NUM_SHIFT |
|
||||
PHY_28NM_PLL_LOCK_BYPASS),
|
||||
base + PHY_28NM_DIG_REG0);
|
||||
|
||||
/* PHY_28NM_OTG_REG */
|
||||
reg = readl(base + PHY_28NM_OTG_REG) | PHY_28NM_OTG_PU_OTG;
|
||||
writel(reg & ~PHY_28NM_OTG_CONTROL_BY_PIN, base + PHY_28NM_OTG_REG);
|
||||
|
||||
/*
|
||||
* Calibration Timing
|
||||
* ____________________________
|
||||
* CAL START ___|
|
||||
* ____________________
|
||||
* CAL_DONE ___________|
|
||||
* | 400us |
|
||||
*/
|
||||
|
||||
/* Make sure PHY Calibration is ready */
|
||||
if (!wait_for_reg(base + PHY_28NM_CAL_REG,
|
||||
PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE,
|
||||
HZ / 10)) {
|
||||
dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS.");
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_clk;
|
||||
}
|
||||
if (!wait_for_reg(base + PHY_28NM_RX_REG1,
|
||||
PHY_28NM_RX_SQCAL_DONE, HZ / 10)) {
|
||||
dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS.");
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_clk;
|
||||
}
|
||||
/* Make sure PHY PLL is ready */
|
||||
if (!wait_for_reg(base + PHY_28NM_PLL_REG0,
|
||||
PHY_28NM_PLL_READY, HZ / 10)) {
|
||||
dev_warn(&pdev->dev, "PLL_READY not set after 100mS.");
|
||||
ret = -ETIMEDOUT;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_clk:
|
||||
clk_disable_unprepare(mv_phy->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mv_usb2_phy_28nm_power_on(struct phy *phy)
|
||||
{
|
||||
struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy);
|
||||
void __iomem *base = mv_phy->base;
|
||||
|
||||
writel(readl(base + PHY_28NM_CTRL_REG3) |
|
||||
(PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID |
|
||||
PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID),
|
||||
base + PHY_28NM_CTRL_REG3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_usb2_phy_28nm_power_off(struct phy *phy)
|
||||
{
|
||||
struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy);
|
||||
void __iomem *base = mv_phy->base;
|
||||
|
||||
writel(readl(base + PHY_28NM_CTRL_REG3) |
|
||||
~(PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID
|
||||
| PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID),
|
||||
base + PHY_28NM_CTRL_REG3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mv_usb2_phy_28nm_exit(struct phy *phy)
|
||||
{
|
||||
struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy);
|
||||
void __iomem *base = mv_phy->base;
|
||||
unsigned int val;
|
||||
|
||||
val = readw(base + PHY_28NM_PLL_REG1);
|
||||
val &= ~PHY_28NM_PLL_PU_PLL;
|
||||
writew(val, base + PHY_28NM_PLL_REG1);
|
||||
|
||||
/* power down PHY Analog part */
|
||||
val = readw(base + PHY_28NM_TX_REG0);
|
||||
val &= ~PHY_28NM_TX_PU_ANA;
|
||||
writew(val, base + PHY_28NM_TX_REG0);
|
||||
|
||||
/* power down PHY OTG part */
|
||||
val = readw(base + PHY_28NM_OTG_REG);
|
||||
val &= ~PHY_28NM_OTG_PU_OTG;
|
||||
writew(val, base + PHY_28NM_OTG_REG);
|
||||
|
||||
clk_disable_unprepare(mv_phy->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct phy_ops usb_ops = {
|
||||
.init = mv_usb2_phy_28nm_init,
|
||||
.power_on = mv_usb2_phy_28nm_power_on,
|
||||
.power_off = mv_usb2_phy_28nm_power_off,
|
||||
.exit = mv_usb2_phy_28nm_exit,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mv_usb2_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct phy_provider *phy_provider;
|
||||
struct mv_usb2_phy *mv_phy;
|
||||
struct resource *r;
|
||||
|
||||
mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL);
|
||||
if (!mv_phy)
|
||||
return -ENOMEM;
|
||||
|
||||
mv_phy->pdev = pdev;
|
||||
|
||||
mv_phy->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(mv_phy->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clock.\n");
|
||||
return PTR_ERR(mv_phy->clk);
|
||||
}
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
mv_phy->base = devm_ioremap_resource(&pdev->dev, r);
|
||||
if (IS_ERR(mv_phy->base))
|
||||
return PTR_ERR(mv_phy->base);
|
||||
|
||||
mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &usb_ops);
|
||||
if (IS_ERR(mv_phy->phy))
|
||||
return PTR_ERR(mv_phy->phy);
|
||||
|
||||
phy_set_drvdata(mv_phy->phy, mv_phy);
|
||||
|
||||
phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
|
||||
return PTR_ERR_OR_ZERO(phy_provider);
|
||||
}
|
||||
|
||||
static const struct of_device_id mv_usbphy_dt_match[] = {
|
||||
{ .compatible = "marvell,pxa1928-usb-phy", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mv_usbphy_dt_match);
|
||||
|
||||
static struct platform_driver mv_usb2_phy_driver = {
|
||||
.probe = mv_usb2_phy_probe,
|
||||
.driver = {
|
||||
.name = "mv-usb2-phy",
|
||||
.of_match_table = of_match_ptr(mv_usbphy_dt_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(mv_usb2_phy_driver);
|
||||
|
||||
MODULE_AUTHOR("Rob Herring <robh@kernel.org>");
|
||||
MODULE_DESCRIPTION("Marvell USB2 phy driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -195,6 +195,7 @@ static struct phy_ops rcar_gen2_phy_ops = {
|
||||
static const struct of_device_id rcar_gen2_phy_match_table[] = {
|
||||
{ .compatible = "renesas,usb-phy-r8a7790" },
|
||||
{ .compatible = "renesas,usb-phy-r8a7791" },
|
||||
{ .compatible = "renesas,usb-phy-r8a7794" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table);
|
||||
@ -206,11 +207,6 @@ static struct phy *rcar_gen2_phy_xlate(struct device *dev,
|
||||
struct device_node *np = args->np;
|
||||
int i;
|
||||
|
||||
if (!of_device_is_available(np)) {
|
||||
dev_warn(dev, "Requested PHY is disabled\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
drv = dev_get_drvdata(dev);
|
||||
if (!drv)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/phy/phy-sun4i-usb.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
@ -58,6 +59,7 @@
|
||||
#define PHY_OTG_FUNC_EN 0x28
|
||||
#define PHY_VBUS_DET_EN 0x29
|
||||
#define PHY_DISCON_TH_SEL 0x2a
|
||||
#define PHY_SQUELCH_DETECT 0x3c
|
||||
|
||||
#define MAX_PHYS 3
|
||||
|
||||
@ -204,6 +206,13 @@ static int sun4i_usb_phy_power_off(struct phy *_phy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled)
|
||||
{
|
||||
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
|
||||
|
||||
sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2);
|
||||
}
|
||||
|
||||
static struct phy_ops sun4i_usb_phy_ops = {
|
||||
.init = sun4i_usb_phy_init,
|
||||
.exit = sun4i_usb_phy_exit,
|
||||
|
153
drivers/phy/phy-tusb1210.c
Normal file
153
drivers/phy/phy-tusb1210.c
Normal file
@ -0,0 +1,153 @@
|
||||
/**
|
||||
* tusb1210.c - TUSB1210 USB ULPI PHY driver
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
*
|
||||
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/ulpi/driver.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include "ulpi_phy.h"
|
||||
|
||||
#define TUSB1210_VENDOR_SPECIFIC2 0x80
|
||||
#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0
|
||||
#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4
|
||||
#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6
|
||||
|
||||
struct tusb1210 {
|
||||
struct ulpi *ulpi;
|
||||
struct phy *phy;
|
||||
struct gpio_desc *gpio_reset;
|
||||
struct gpio_desc *gpio_cs;
|
||||
u8 vendor_specific2;
|
||||
};
|
||||
|
||||
static int tusb1210_power_on(struct phy *phy)
|
||||
{
|
||||
struct tusb1210 *tusb = phy_get_drvdata(phy);
|
||||
|
||||
gpiod_set_value_cansleep(tusb->gpio_reset, 1);
|
||||
gpiod_set_value_cansleep(tusb->gpio_cs, 1);
|
||||
|
||||
/* Restore the optional eye diagram optimization value */
|
||||
if (tusb->vendor_specific2)
|
||||
ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2,
|
||||
tusb->vendor_specific2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tusb1210_power_off(struct phy *phy)
|
||||
{
|
||||
struct tusb1210 *tusb = phy_get_drvdata(phy);
|
||||
|
||||
gpiod_set_value_cansleep(tusb->gpio_reset, 0);
|
||||
gpiod_set_value_cansleep(tusb->gpio_cs, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct phy_ops phy_ops = {
|
||||
.power_on = tusb1210_power_on,
|
||||
.power_off = tusb1210_power_off,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int tusb1210_probe(struct ulpi *ulpi)
|
||||
{
|
||||
struct gpio_desc *gpio;
|
||||
struct tusb1210 *tusb;
|
||||
u8 val, reg;
|
||||
int ret;
|
||||
|
||||
tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL);
|
||||
if (!tusb)
|
||||
return -ENOMEM;
|
||||
|
||||
gpio = devm_gpiod_get(&ulpi->dev, "reset");
|
||||
if (!IS_ERR(gpio)) {
|
||||
ret = gpiod_direction_output(gpio, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
tusb->gpio_reset = gpio;
|
||||
}
|
||||
|
||||
gpio = devm_gpiod_get(&ulpi->dev, "cs");
|
||||
if (!IS_ERR(gpio)) {
|
||||
ret = gpiod_direction_output(gpio, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
tusb->gpio_cs = gpio;
|
||||
}
|
||||
|
||||
/*
|
||||
* VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye
|
||||
* diagram optimization and DP/DM swap.
|
||||
*/
|
||||
|
||||
/* High speed output drive strength configuration */
|
||||
device_property_read_u8(&ulpi->dev, "ihstx", &val);
|
||||
reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT;
|
||||
|
||||
/* High speed output impedance configuration */
|
||||
device_property_read_u8(&ulpi->dev, "zhsdrv", &val);
|
||||
reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT;
|
||||
|
||||
/* DP/DM swap control */
|
||||
device_property_read_u8(&ulpi->dev, "datapolarity", &val);
|
||||
reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT;
|
||||
|
||||
if (reg) {
|
||||
ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg);
|
||||
tusb->vendor_specific2 = reg;
|
||||
}
|
||||
|
||||
tusb->phy = ulpi_phy_create(ulpi, &phy_ops);
|
||||
if (IS_ERR(tusb->phy))
|
||||
return PTR_ERR(tusb->phy);
|
||||
|
||||
tusb->ulpi = ulpi;
|
||||
|
||||
phy_set_drvdata(tusb->phy, tusb);
|
||||
ulpi_set_drvdata(ulpi, tusb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tusb1210_remove(struct ulpi *ulpi)
|
||||
{
|
||||
struct tusb1210 *tusb = ulpi_get_drvdata(ulpi);
|
||||
|
||||
ulpi_phy_destroy(ulpi, tusb->phy);
|
||||
}
|
||||
|
||||
#define TI_VENDOR_ID 0x0451
|
||||
|
||||
static const struct ulpi_device_id tusb1210_ulpi_id[] = {
|
||||
{ TI_VENDOR_ID, 0x1507, },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id);
|
||||
|
||||
static struct ulpi_driver tusb1210_driver = {
|
||||
.id_table = tusb1210_ulpi_id,
|
||||
.probe = tusb1210_probe,
|
||||
.remove = tusb1210_remove,
|
||||
.driver = {
|
||||
.name = "tusb1210",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_ulpi_driver(tusb1210_driver);
|
||||
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver");
|
@ -144,6 +144,16 @@
|
||||
#define PMBR1 0x0D
|
||||
#define GPIO_USB_4PIN_ULPI_2430C (3 << 0)
|
||||
|
||||
/*
|
||||
* If VBUS is valid or ID is ground, then we know a
|
||||
* cable is present and we need to be runtime-enabled
|
||||
*/
|
||||
static inline bool cable_present(enum omap_musb_vbus_id_status stat)
|
||||
{
|
||||
return stat == OMAP_MUSB_VBUS_VALID ||
|
||||
stat == OMAP_MUSB_ID_GROUND;
|
||||
}
|
||||
|
||||
struct twl4030_usb {
|
||||
struct usb_phy phy;
|
||||
struct device *dev;
|
||||
@ -386,8 +396,6 @@ static int twl4030_usb_runtime_suspend(struct device *dev)
|
||||
struct twl4030_usb *twl = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(twl->dev, "%s\n", __func__);
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
|
||||
__twl4030_phy_power(twl, 0);
|
||||
regulator_disable(twl->usb1v5);
|
||||
@ -403,8 +411,6 @@ static int twl4030_usb_runtime_resume(struct device *dev)
|
||||
int res;
|
||||
|
||||
dev_dbg(twl->dev, "%s\n", __func__);
|
||||
if (pm_runtime_active(dev))
|
||||
return 0;
|
||||
|
||||
res = regulator_enable(twl->usb3v1);
|
||||
if (res)
|
||||
@ -536,8 +542,10 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
|
||||
|
||||
mutex_lock(&twl->lock);
|
||||
if (status >= 0 && status != twl->linkstat) {
|
||||
status_changed =
|
||||
cable_present(twl->linkstat) !=
|
||||
cable_present(status);
|
||||
twl->linkstat = status;
|
||||
status_changed = true;
|
||||
}
|
||||
mutex_unlock(&twl->lock);
|
||||
|
||||
@ -553,15 +561,11 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
|
||||
* USB_LINK_VBUS state. musb_hdrc won't care until it
|
||||
* starts to handle softconnect right.
|
||||
*/
|
||||
if ((status == OMAP_MUSB_VBUS_VALID) ||
|
||||
(status == OMAP_MUSB_ID_GROUND)) {
|
||||
if (pm_runtime_suspended(twl->dev))
|
||||
pm_runtime_get_sync(twl->dev);
|
||||
if (cable_present(status)) {
|
||||
pm_runtime_get_sync(twl->dev);
|
||||
} else {
|
||||
if (pm_runtime_active(twl->dev)) {
|
||||
pm_runtime_mark_last_busy(twl->dev);
|
||||
pm_runtime_put_autosuspend(twl->dev);
|
||||
}
|
||||
pm_runtime_mark_last_busy(twl->dev);
|
||||
pm_runtime_put_autosuspend(twl->dev);
|
||||
}
|
||||
omap_musb_mailbox(status);
|
||||
}
|
||||
@ -711,7 +715,6 @@ static int twl4030_usb_probe(struct platform_device *pdev)
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
/* Our job is to use irqs and status from the power module
|
||||
* to keep the transceiver disabled when nothing's connected.
|
||||
@ -767,6 +770,9 @@ static int twl4030_usb_remove(struct platform_device *pdev)
|
||||
|
||||
/* disable complete OTG block */
|
||||
twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
|
||||
|
||||
if (cable_present(twl->linkstat))
|
||||
pm_runtime_put_noidle(twl->dev);
|
||||
pm_runtime_mark_last_busy(twl->dev);
|
||||
pm_runtime_put(twl->dev);
|
||||
|
||||
|
31
drivers/phy/ulpi_phy.h
Normal file
31
drivers/phy/ulpi_phy.h
Normal file
@ -0,0 +1,31 @@
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
/**
|
||||
* Helper that registers PHY for a ULPI device and adds a lookup for binding it
|
||||
* and it's controller, which is always the parent.
|
||||
*/
|
||||
static inline struct phy
|
||||
*ulpi_phy_create(struct ulpi *ulpi, struct phy_ops *ops)
|
||||
{
|
||||
struct phy *phy;
|
||||
int ret;
|
||||
|
||||
phy = phy_create(&ulpi->dev, NULL, ops);
|
||||
if (IS_ERR(phy))
|
||||
return phy;
|
||||
|
||||
ret = phy_create_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent));
|
||||
if (ret) {
|
||||
phy_destroy(phy);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return phy;
|
||||
}
|
||||
|
||||
/* Remove a PHY that was created with ulpi_phy_create() and it's lookup. */
|
||||
static inline void ulpi_phy_destroy(struct ulpi *ulpi, struct phy *phy)
|
||||
{
|
||||
phy_remove_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent));
|
||||
phy_destroy(phy);
|
||||
}
|
@ -638,10 +638,15 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
|
||||
|
||||
INIT_WORK(&bci->work, twl4030_bci_usb_work);
|
||||
|
||||
bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
|
||||
if (!IS_ERR_OR_NULL(bci->transceiver)) {
|
||||
bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
|
||||
usb_register_notifier(bci->transceiver, &bci->usb_nb);
|
||||
bci->usb_nb.notifier_call = twl4030_bci_usb_ncb;
|
||||
if (bci->dev->of_node) {
|
||||
struct device_node *phynode;
|
||||
|
||||
phynode = of_find_compatible_node(bci->dev->of_node->parent,
|
||||
NULL, "ti,twl4030-usb");
|
||||
if (phynode)
|
||||
bci->transceiver = devm_usb_get_phy_by_node(
|
||||
bci->dev, phynode, &bci->usb_nb);
|
||||
}
|
||||
|
||||
/* Enable interrupts now. */
|
||||
@ -671,10 +676,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
|
||||
fail_unmask_interrupts:
|
||||
if (!IS_ERR_OR_NULL(bci->transceiver)) {
|
||||
usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
|
||||
usb_put_phy(bci->transceiver);
|
||||
}
|
||||
free_irq(bci->irq_bci, bci);
|
||||
fail_bci_irq:
|
||||
free_irq(bci->irq_chg, bci);
|
||||
@ -703,10 +704,6 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev)
|
||||
twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff,
|
||||
TWL4030_INTERRUPTS_BCIIMR2A);
|
||||
|
||||
if (!IS_ERR_OR_NULL(bci->transceiver)) {
|
||||
usb_unregister_notifier(bci->transceiver, &bci->usb_nb);
|
||||
usb_put_phy(bci->transceiver);
|
||||
}
|
||||
free_irq(bci->irq_bci, bci);
|
||||
free_irq(bci->irq_chg, bci);
|
||||
power_supply_unregister(bci->usb);
|
||||
|
@ -255,7 +255,8 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance,
|
||||
|
||||
usb_dbg(usbatm, "%s entered\n", __func__);
|
||||
|
||||
if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) {
|
||||
buffer = (unsigned char *)__get_free_page(GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
ret = -ENOMEM;
|
||||
usb_dbg(usbatm, "%s: no memory for buffer!\n", __func__);
|
||||
goto out;
|
||||
@ -638,7 +639,8 @@ static void speedtch_handle_int(struct urb *int_urb)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((int_urb = instance->int_urb)) {
|
||||
int_urb = instance->int_urb;
|
||||
if (int_urb) {
|
||||
ret = usb_submit_urb(int_urb, GFP_ATOMIC);
|
||||
schedule_work(&instance->status_check_work);
|
||||
if (ret < 0) {
|
||||
@ -650,7 +652,8 @@ static void speedtch_handle_int(struct urb *int_urb)
|
||||
return;
|
||||
|
||||
fail:
|
||||
if ((int_urb = instance->int_urb))
|
||||
int_urb = instance->int_urb;
|
||||
if (int_urb)
|
||||
mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY));
|
||||
}
|
||||
|
||||
@ -759,11 +762,13 @@ static void speedtch_release_interfaces(struct usb_device *usb_dev,
|
||||
struct usb_interface *cur_intf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_interfaces; i++)
|
||||
if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) {
|
||||
for (i = 0; i < num_interfaces; i++) {
|
||||
cur_intf = usb_ifnum_to_if(usb_dev, i);
|
||||
if (cur_intf) {
|
||||
usb_set_intfdata(cur_intf, NULL);
|
||||
usb_driver_release_interface(&speedtch_usb_driver, cur_intf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int speedtch_bind(struct usbatm_data *usbatm,
|
||||
@ -787,7 +792,8 @@ static int speedtch_bind(struct usbatm_data *usbatm,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!(data_intf = usb_ifnum_to_if(usb_dev, INTERFACE_DATA))) {
|
||||
data_intf = usb_ifnum_to_if(usb_dev, INTERFACE_DATA);
|
||||
if (!data_intf) {
|
||||
usb_err(usbatm, "%s: data interface not found!\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -382,7 +382,8 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
|
||||
"%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)",
|
||||
__func__, length, pdu_length, vcc);
|
||||
|
||||
if (!(skb = dev_alloc_skb(length))) {
|
||||
skb = dev_alloc_skb(length);
|
||||
if (!skb) {
|
||||
if (printk_ratelimit())
|
||||
atm_err(instance, "%s: no memory for skb (length: %u)!\n",
|
||||
__func__, length);
|
||||
@ -816,7 +817,8 @@ static int usbatm_atm_open(struct atm_vcc *vcc)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(new = kzalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL))) {
|
||||
new = kzalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL);
|
||||
if (!new) {
|
||||
atm_err(instance, "%s: no memory for vcc_data!\n", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
|
@ -73,7 +73,8 @@ static int xusbatm_capture_intf(struct usbatm_data *usbatm, struct usb_device *u
|
||||
usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, ifnum, ret);
|
||||
return ret;
|
||||
}
|
||||
if ((ret = usb_set_interface(usb_dev, ifnum, altsetting))) {
|
||||
ret = usb_set_interface(usb_dev, ifnum, altsetting);
|
||||
if (ret) {
|
||||
usb_err(usbatm, "%s: altsetting %2d for interface %2d failed (%d)!\n", __func__, altsetting, ifnum, ret);
|
||||
return ret;
|
||||
}
|
||||
@ -128,7 +129,8 @@ static int xusbatm_bind(struct usbatm_data *usbatm,
|
||||
rx_intf->altsetting->desc.bInterfaceNumber,
|
||||
tx_intf->altsetting->desc.bInterfaceNumber);
|
||||
|
||||
if ((ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf)))
|
||||
ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((tx_intf != rx_intf) && (ret = xusbatm_capture_intf(usbatm, usb_dev, tx_intf, tx_alt, tx_intf != intf))) {
|
||||
|
@ -25,7 +25,7 @@ struct ci_hdrc_usb2_priv {
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static struct ci_hdrc_platform_data ci_default_pdata = {
|
||||
static const struct ci_hdrc_platform_data ci_default_pdata = {
|
||||
.capoffset = DEF_CAPOFFSET,
|
||||
.flags = CI_HDRC_DISABLE_STREAMING,
|
||||
};
|
||||
@ -37,8 +37,10 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
|
||||
struct ci_hdrc_platform_data *ci_pdata = dev_get_platdata(dev);
|
||||
int ret;
|
||||
|
||||
if (!ci_pdata)
|
||||
ci_pdata = &ci_default_pdata;
|
||||
if (!ci_pdata) {
|
||||
ci_pdata = devm_kmalloc(dev, sizeof(*ci_pdata), GFP_KERNEL);
|
||||
*ci_pdata = ci_default_pdata; /* struct copy */
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
|
@ -37,12 +37,14 @@ static int (*orig_bus_suspend)(struct usb_hcd *hcd);
|
||||
|
||||
struct ehci_ci_priv {
|
||||
struct regulator *reg_vbus;
|
||||
struct ci_hdrc *ci;
|
||||
};
|
||||
|
||||
static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv;
|
||||
struct ci_hdrc *ci = priv->ci;
|
||||
struct device *dev = hcd->self.controller;
|
||||
int ret = 0;
|
||||
int port = HCS_N_PORTS(ehci->hcs_params);
|
||||
@ -64,6 +66,15 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) {
|
||||
/*
|
||||
* Marvell 28nm HSIC PHY requires forcing the port to HS mode.
|
||||
* As HSIC is always HS, this should be safe for others.
|
||||
*/
|
||||
hw_port_test_set(ci, 5);
|
||||
hw_port_test_set(ci, 0);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
@ -112,6 +123,7 @@ static int host_start(struct ci_hdrc *ci)
|
||||
|
||||
priv = (struct ehci_ci_priv *)ehci->priv;
|
||||
priv->reg_vbus = NULL;
|
||||
priv->ci = ci;
|
||||
|
||||
if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
|
||||
if (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON) {
|
||||
|
@ -160,7 +160,7 @@ static int usbmisc_imx27_init(struct imx_usbmisc_data *data)
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
};
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&usbmisc->lock, flags);
|
||||
if (data->disable_oc)
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include <linux/usb/cdc.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "cdc-acm.h"
|
||||
@ -56,27 +57,27 @@
|
||||
|
||||
static struct usb_driver acm_driver;
|
||||
static struct tty_driver *acm_tty_driver;
|
||||
static struct acm *acm_table[ACM_TTY_MINORS];
|
||||
|
||||
static DEFINE_MUTEX(acm_table_lock);
|
||||
static DEFINE_IDR(acm_minors);
|
||||
static DEFINE_MUTEX(acm_minors_lock);
|
||||
|
||||
static void acm_tty_set_termios(struct tty_struct *tty,
|
||||
struct ktermios *termios_old);
|
||||
|
||||
/*
|
||||
* acm_table accessors
|
||||
* acm_minors accessors
|
||||
*/
|
||||
|
||||
/*
|
||||
* Look up an ACM structure by index. If found and not disconnected, increment
|
||||
* Look up an ACM structure by minor. If found and not disconnected, increment
|
||||
* its refcount and return it with its mutex held.
|
||||
*/
|
||||
static struct acm *acm_get_by_index(unsigned index)
|
||||
static struct acm *acm_get_by_minor(unsigned int minor)
|
||||
{
|
||||
struct acm *acm;
|
||||
|
||||
mutex_lock(&acm_table_lock);
|
||||
acm = acm_table[index];
|
||||
mutex_lock(&acm_minors_lock);
|
||||
acm = idr_find(&acm_minors, minor);
|
||||
if (acm) {
|
||||
mutex_lock(&acm->mutex);
|
||||
if (acm->disconnected) {
|
||||
@ -87,7 +88,7 @@ static struct acm *acm_get_by_index(unsigned index)
|
||||
mutex_unlock(&acm->mutex);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&acm_table_lock);
|
||||
mutex_unlock(&acm_minors_lock);
|
||||
return acm;
|
||||
}
|
||||
|
||||
@ -98,14 +99,9 @@ static int acm_alloc_minor(struct acm *acm)
|
||||
{
|
||||
int minor;
|
||||
|
||||
mutex_lock(&acm_table_lock);
|
||||
for (minor = 0; minor < ACM_TTY_MINORS; minor++) {
|
||||
if (!acm_table[minor]) {
|
||||
acm_table[minor] = acm;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&acm_table_lock);
|
||||
mutex_lock(&acm_minors_lock);
|
||||
minor = idr_alloc(&acm_minors, acm, 0, ACM_TTY_MINORS, GFP_KERNEL);
|
||||
mutex_unlock(&acm_minors_lock);
|
||||
|
||||
return minor;
|
||||
}
|
||||
@ -113,9 +109,9 @@ static int acm_alloc_minor(struct acm *acm)
|
||||
/* Release the minor number associated with 'acm'. */
|
||||
static void acm_release_minor(struct acm *acm)
|
||||
{
|
||||
mutex_lock(&acm_table_lock);
|
||||
acm_table[acm->minor] = NULL;
|
||||
mutex_unlock(&acm_table_lock);
|
||||
mutex_lock(&acm_minors_lock);
|
||||
idr_remove(&acm_minors, acm->minor);
|
||||
mutex_unlock(&acm_minors_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -497,7 +493,7 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
|
||||
|
||||
dev_dbg(tty->dev, "%s\n", __func__);
|
||||
|
||||
acm = acm_get_by_index(tty->index);
|
||||
acm = acm_get_by_minor(tty->index);
|
||||
if (!acm)
|
||||
return -ENODEV;
|
||||
|
||||
@ -1267,12 +1263,9 @@ skip_normal_probe:
|
||||
!= CDC_DATA_INTERFACE_TYPE) {
|
||||
if (control_interface->cur_altsetting->desc.bInterfaceClass
|
||||
== CDC_DATA_INTERFACE_TYPE) {
|
||||
struct usb_interface *t;
|
||||
dev_dbg(&intf->dev,
|
||||
"Your device has switched interfaces.\n");
|
||||
t = control_interface;
|
||||
control_interface = data_interface;
|
||||
data_interface = t;
|
||||
swap(control_interface, data_interface);
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1301,12 +1294,9 @@ skip_normal_probe:
|
||||
/* workaround for switched endpoints */
|
||||
if (!usb_endpoint_dir_in(epread)) {
|
||||
/* descriptors are swapped */
|
||||
struct usb_endpoint_descriptor *t;
|
||||
dev_dbg(&intf->dev,
|
||||
"The data interface has switched endpoints\n");
|
||||
t = epread;
|
||||
epread = epwrite;
|
||||
epwrite = t;
|
||||
swap(epread, epwrite);
|
||||
}
|
||||
made_compressed_probe:
|
||||
dev_dbg(&intf->dev, "interfaces are valid\n");
|
||||
@ -1316,7 +1306,7 @@ made_compressed_probe:
|
||||
goto alloc_fail;
|
||||
|
||||
minor = acm_alloc_minor(acm);
|
||||
if (minor == ACM_TTY_MINORS) {
|
||||
if (minor < 0) {
|
||||
dev_err(&intf->dev, "no more free acm devices\n");
|
||||
kfree(acm);
|
||||
return -ENODEV;
|
||||
@ -1477,6 +1467,11 @@ skip_countries:
|
||||
goto alloc_fail8;
|
||||
}
|
||||
|
||||
if (quirks & CLEAR_HALT_CONDITIONS) {
|
||||
usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress));
|
||||
usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress));
|
||||
}
|
||||
|
||||
return 0;
|
||||
alloc_fail8:
|
||||
if (acm->country_codes) {
|
||||
@ -1756,6 +1751,10 @@ static const struct usb_device_id acm_ids[] = {
|
||||
.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
|
||||
},
|
||||
|
||||
{ USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */
|
||||
.driver_info = CLEAR_HALT_CONDITIONS,
|
||||
},
|
||||
|
||||
/* Nokia S60 phones expose two ACM channels. The first is
|
||||
* a modem and is picked up by the standard AT-command
|
||||
* information below. The second is 'vendor-specific' but
|
||||
|
@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
#define ACM_TTY_MAJOR 166
|
||||
#define ACM_TTY_MINORS 32
|
||||
#define ACM_TTY_MINORS 256
|
||||
|
||||
/*
|
||||
* Requests.
|
||||
@ -133,3 +133,4 @@ struct acm {
|
||||
#define NO_DATA_INTERFACE BIT(4)
|
||||
#define IGNORE_DEVICE BIT(5)
|
||||
#define QUIRK_CONTROL_LINE_STATE BIT(6)
|
||||
#define CLEAR_HALT_CONDITIONS BIT(7)
|
||||
|
@ -660,7 +660,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
switch (cmd) {
|
||||
|
||||
case LPGETSTATUS:
|
||||
if ((retval = usblp_read_status(usblp, usblp->statusbuf))) {
|
||||
retval = usblp_read_status(usblp, usblp->statusbuf);
|
||||
if (retval) {
|
||||
printk_ratelimited(KERN_ERR "usblp%d:"
|
||||
"failed reading printer status (%d)\n",
|
||||
usblp->minor, retval);
|
||||
@ -693,9 +694,11 @@ static struct urb *usblp_new_writeurb(struct usblp *usblp, int transfer_length)
|
||||
struct urb *urb;
|
||||
char *writebuf;
|
||||
|
||||
if ((writebuf = kmalloc(transfer_length, GFP_KERNEL)) == NULL)
|
||||
writebuf = kmalloc(transfer_length, GFP_KERNEL);
|
||||
if (writebuf == NULL)
|
||||
return NULL;
|
||||
if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) {
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (urb == NULL) {
|
||||
kfree(writebuf);
|
||||
return NULL;
|
||||
}
|
||||
@ -732,7 +735,8 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t
|
||||
transfer_length = USBLP_BUF_SIZE;
|
||||
|
||||
rv = -ENOMEM;
|
||||
if ((writeurb = usblp_new_writeurb(usblp, transfer_length)) == NULL)
|
||||
writeurb = usblp_new_writeurb(usblp, transfer_length);
|
||||
if (writeurb == NULL)
|
||||
goto raise_urb;
|
||||
usb_anchor_urb(writeurb, &usblp->urbs);
|
||||
|
||||
@ -980,7 +984,8 @@ static int usblp_submit_read(struct usblp *usblp)
|
||||
int rc;
|
||||
|
||||
rc = -ENOMEM;
|
||||
if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL)
|
||||
urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (urb == NULL)
|
||||
goto raise_urb;
|
||||
|
||||
usb_fill_bulk_urb(urb, usblp->dev,
|
||||
|
@ -109,6 +109,7 @@ struct usbtmc_ID_rigol_quirk {
|
||||
|
||||
static const struct usbtmc_ID_rigol_quirk usbtmc_id_quirk[] = {
|
||||
{ 0x1ab1, 0x0588 },
|
||||
{ 0x1ab1, 0x04b0 },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
|
@ -7,3 +7,4 @@ usb-common-y += common.o
|
||||
usb-common-$(CONFIG_USB_LED_TRIG) += led.o
|
||||
|
||||
obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
|
||||
obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o
|
||||
|
255
drivers/usb/common/ulpi.c
Normal file
255
drivers/usb/common/ulpi.c
Normal file
@ -0,0 +1,255 @@
|
||||
/**
|
||||
* ulpi.c - USB ULPI PHY bus
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
*
|
||||
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/ulpi/interface.h>
|
||||
#include <linux/ulpi/driver.h>
|
||||
#include <linux/ulpi/regs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
int ulpi_read(struct ulpi *ulpi, u8 addr)
|
||||
{
|
||||
return ulpi->ops->read(ulpi->ops, addr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ulpi_read);
|
||||
|
||||
int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val)
|
||||
{
|
||||
return ulpi->ops->write(ulpi->ops, addr, val);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ulpi_write);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int ulpi_match(struct device *dev, struct device_driver *driver)
|
||||
{
|
||||
struct ulpi_driver *drv = to_ulpi_driver(driver);
|
||||
struct ulpi *ulpi = to_ulpi_dev(dev);
|
||||
const struct ulpi_device_id *id;
|
||||
|
||||
for (id = drv->id_table; id->vendor; id++)
|
||||
if (id->vendor == ulpi->id.vendor &&
|
||||
id->product == ulpi->id.product)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
struct ulpi *ulpi = to_ulpi_dev(dev);
|
||||
|
||||
if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x",
|
||||
ulpi->id.vendor, ulpi->id.product))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ulpi_probe(struct device *dev)
|
||||
{
|
||||
struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
|
||||
|
||||
return drv->probe(to_ulpi_dev(dev));
|
||||
}
|
||||
|
||||
static int ulpi_remove(struct device *dev)
|
||||
{
|
||||
struct ulpi_driver *drv = to_ulpi_driver(dev->driver);
|
||||
|
||||
if (drv->remove)
|
||||
drv->remove(to_ulpi_dev(dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bus_type ulpi_bus = {
|
||||
.name = "ulpi",
|
||||
.match = ulpi_match,
|
||||
.uevent = ulpi_uevent,
|
||||
.probe = ulpi_probe,
|
||||
.remove = ulpi_remove,
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct ulpi *ulpi = to_ulpi_dev(dev);
|
||||
|
||||
return sprintf(buf, "ulpi:v%04xp%04x\n",
|
||||
ulpi->id.vendor, ulpi->id.product);
|
||||
}
|
||||
static DEVICE_ATTR_RO(modalias);
|
||||
|
||||
static struct attribute *ulpi_dev_attrs[] = {
|
||||
&dev_attr_modalias.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group ulpi_dev_attr_group = {
|
||||
.attrs = ulpi_dev_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *ulpi_dev_attr_groups[] = {
|
||||
&ulpi_dev_attr_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static void ulpi_dev_release(struct device *dev)
|
||||
{
|
||||
kfree(to_ulpi_dev(dev));
|
||||
}
|
||||
|
||||
static struct device_type ulpi_dev_type = {
|
||||
.name = "ulpi_device",
|
||||
.groups = ulpi_dev_attr_groups,
|
||||
.release = ulpi_dev_release,
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* ulpi_register_driver - register a driver with the ULPI bus
|
||||
* @drv: driver being registered
|
||||
*
|
||||
* Registers a driver with the ULPI bus.
|
||||
*/
|
||||
int ulpi_register_driver(struct ulpi_driver *drv)
|
||||
{
|
||||
if (!drv->probe)
|
||||
return -EINVAL;
|
||||
|
||||
drv->driver.bus = &ulpi_bus;
|
||||
|
||||
return driver_register(&drv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ulpi_register_driver);
|
||||
|
||||
/**
|
||||
* ulpi_unregister_driver - unregister a driver with the ULPI bus
|
||||
* @drv: driver to unregister
|
||||
*
|
||||
* Unregisters a driver with the ULPI bus.
|
||||
*/
|
||||
void ulpi_unregister_driver(struct ulpi_driver *drv)
|
||||
{
|
||||
driver_unregister(&drv->driver);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ulpi_unregister_driver);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int ulpi_register(struct device *dev, struct ulpi *ulpi)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Test the interface */
|
||||
ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ulpi_read(ulpi, ULPI_SCRATCH);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != 0xaa)
|
||||
return -ENODEV;
|
||||
|
||||
ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW);
|
||||
ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8;
|
||||
|
||||
ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW);
|
||||
ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8;
|
||||
|
||||
ulpi->dev.parent = dev;
|
||||
ulpi->dev.bus = &ulpi_bus;
|
||||
ulpi->dev.type = &ulpi_dev_type;
|
||||
dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev));
|
||||
|
||||
ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev));
|
||||
|
||||
request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product);
|
||||
|
||||
ret = device_register(&ulpi->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",
|
||||
ulpi->id.vendor, ulpi->id.product);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ulpi_register_interface - instantiate new ULPI device
|
||||
* @dev: USB controller's device interface
|
||||
* @ops: ULPI register access
|
||||
*
|
||||
* Allocates and registers a ULPI device and an interface for it. Called from
|
||||
* the USB controller that provides the ULPI interface.
|
||||
*/
|
||||
struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops)
|
||||
{
|
||||
struct ulpi *ulpi;
|
||||
int ret;
|
||||
|
||||
ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
|
||||
if (!ulpi)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ulpi->ops = ops;
|
||||
ops->dev = dev;
|
||||
|
||||
ret = ulpi_register(dev, ulpi);
|
||||
if (ret) {
|
||||
kfree(ulpi);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return ulpi;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ulpi_register_interface);
|
||||
|
||||
/**
|
||||
* ulpi_unregister_interface - unregister ULPI interface
|
||||
* @intrf: struct ulpi_interface
|
||||
*
|
||||
* Unregisters a ULPI device and it's interface that was created with
|
||||
* ulpi_create_interface().
|
||||
*/
|
||||
void ulpi_unregister_interface(struct ulpi *ulpi)
|
||||
{
|
||||
device_unregister(&ulpi->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ulpi_unregister_interface);
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
static int __init ulpi_init(void)
|
||||
{
|
||||
return bus_register(&ulpi_bus);
|
||||
}
|
||||
module_init(ulpi_init);
|
||||
|
||||
static void __exit ulpi_exit(void)
|
||||
{
|
||||
bus_unregister(&ulpi_bus);
|
||||
}
|
||||
module_exit(ulpi_exit);
|
||||
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("USB ULPI PHY bus");
|
@ -84,3 +84,23 @@ config USB_OTG_FSM
|
||||
Implements OTG Finite State Machine as specified in On-The-Go
|
||||
and Embedded Host Supplement to the USB Revision 2.0 Specification.
|
||||
|
||||
config USB_ULPI_BUS
|
||||
tristate "USB ULPI PHY interface support"
|
||||
depends on USB_SUPPORT
|
||||
help
|
||||
UTMI+ Low Pin Interface (ULPI) is specification for a commonly used
|
||||
USB 2.0 PHY interface. The ULPI specification defines a standard set
|
||||
of registers that can be used to detect the vendor and product which
|
||||
allows ULPI to be handled as a bus. This module is the driver for that
|
||||
bus.
|
||||
|
||||
The ULPI interfaces (the buses) are registered by the drivers for USB
|
||||
controllers which support ULPI register access and have ULPI PHY
|
||||
attached to them. The ULPI PHY drivers themselves are normal PHY
|
||||
drivers.
|
||||
|
||||
ULPI PHYs provide often functions such as ADP sensing/probing (OTG
|
||||
protocol) and USB charger detection.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called ulpi.
|
||||
|
@ -70,7 +70,7 @@ int hcd_buffer_create(struct usb_hcd *hcd)
|
||||
size = pool_max[i];
|
||||
if (!size)
|
||||
continue;
|
||||
snprintf(name, sizeof name, "buffer-%d", size);
|
||||
snprintf(name, sizeof(name), "buffer-%d", size);
|
||||
hcd->pool[i] = dma_pool_create(name, hcd->self.controller,
|
||||
size, size, 0);
|
||||
if (!hcd->pool[i]) {
|
||||
@ -95,6 +95,7 @@ void hcd_buffer_destroy(struct usb_hcd *hcd)
|
||||
|
||||
for (i = 0; i < HCD_BUFFER_POOLS; i++) {
|
||||
struct dma_pool *pool = hcd->pool[i];
|
||||
|
||||
if (pool) {
|
||||
dma_pool_destroy(pool);
|
||||
hcd->pool[i] = NULL;
|
||||
|
@ -513,7 +513,7 @@ static void async_completed(struct urb *urb)
|
||||
snoop(&urb->dev->dev, "urb complete\n");
|
||||
snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
|
||||
as->status, COMPLETE, NULL, 0);
|
||||
if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN)
|
||||
if ((urb->transfer_flags & URB_DIR_MASK) == URB_DIR_IN)
|
||||
snoop_urb_data(urb, urb->actual_length);
|
||||
|
||||
if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
|
||||
|
@ -2691,7 +2691,8 @@ int usb_add_hcd(struct usb_hcd *hcd,
|
||||
if ((retval = usb_register_bus(&hcd->self)) < 0)
|
||||
goto err_register_bus;
|
||||
|
||||
if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {
|
||||
rhdev = usb_alloc_dev(NULL, &hcd->self, 0);
|
||||
if (rhdev == NULL) {
|
||||
dev_err(hcd->self.controller, "unable to allocate root hub\n");
|
||||
retval = -ENOMEM;
|
||||
goto err_allocate_root_hub;
|
||||
|
@ -127,7 +127,7 @@ static int usb_device_supports_lpm(struct usb_device *udev)
|
||||
/* USB 2.1 (and greater) devices indicate LPM support through
|
||||
* their USB 2.0 Extended Capabilities BOS descriptor.
|
||||
*/
|
||||
if (udev->speed == USB_SPEED_HIGH) {
|
||||
if (udev->speed == USB_SPEED_HIGH || udev->speed == USB_SPEED_FULL) {
|
||||
if (udev->bos->ext_cap &&
|
||||
(USB_LPM_SUPPORT &
|
||||
le32_to_cpu(udev->bos->ext_cap->bmAttributes)))
|
||||
@ -795,7 +795,8 @@ int usb_hub_clear_tt_buffer(struct urb *urb)
|
||||
* since each TT has "at least two" buffers that can need it (and
|
||||
* there can be many TTs per hub). even if they're uncommon.
|
||||
*/
|
||||
if ((clear = kmalloc (sizeof *clear, GFP_ATOMIC)) == NULL) {
|
||||
clear = kmalloc(sizeof *clear, GFP_ATOMIC);
|
||||
if (clear == NULL) {
|
||||
dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n");
|
||||
/* FIXME recover somehow ... RESET_TT? */
|
||||
return -ENOMEM;
|
||||
@ -2350,6 +2351,26 @@ static void set_usb_port_removable(struct usb_device *udev)
|
||||
|
||||
hub = usb_hub_to_struct_hub(udev->parent);
|
||||
|
||||
/*
|
||||
* If the platform firmware has provided information about a port,
|
||||
* use that to determine whether it's removable.
|
||||
*/
|
||||
switch (hub->ports[udev->portnum - 1]->connect_type) {
|
||||
case USB_PORT_CONNECT_TYPE_HOT_PLUG:
|
||||
udev->removable = USB_DEVICE_REMOVABLE;
|
||||
return;
|
||||
case USB_PORT_CONNECT_TYPE_HARD_WIRED:
|
||||
case USB_PORT_NOT_USED:
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise, check whether the hub knows whether a port is removable
|
||||
* or not
|
||||
*/
|
||||
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
|
||||
|
||||
if (!(wHubCharacteristics & HUB_CHAR_COMPOUND))
|
||||
@ -2369,21 +2390,6 @@ static void set_usb_port_removable(struct usb_device *udev)
|
||||
else
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
|
||||
/*
|
||||
* Platform firmware may have populated an alternative value for
|
||||
* removable. If the parent port has a known connect_type use
|
||||
* that instead.
|
||||
*/
|
||||
switch (hub->ports[udev->portnum - 1]->connect_type) {
|
||||
case USB_PORT_CONNECT_TYPE_HOT_PLUG:
|
||||
udev->removable = USB_DEVICE_REMOVABLE;
|
||||
break;
|
||||
case USB_PORT_CONNECT_TYPE_HARD_WIRED:
|
||||
udev->removable = USB_DEVICE_FIXED;
|
||||
break;
|
||||
default: /* use what was set above */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2616,9 +2622,6 @@ static bool use_new_scheme(struct usb_device *udev, int retry)
|
||||
return USE_NEW_SCHEME(retry);
|
||||
}
|
||||
|
||||
static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
struct usb_device *udev, unsigned int delay, bool warm);
|
||||
|
||||
/* Is a USB 3.0 port in the Inactive or Compliance Mode state?
|
||||
* Port worm reset is required to recover
|
||||
*/
|
||||
@ -2706,44 +2709,6 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hub_port_finish_reset(struct usb_hub *hub, int port1,
|
||||
struct usb_device *udev, int *status)
|
||||
{
|
||||
switch (*status) {
|
||||
case 0:
|
||||
/* TRSTRCY = 10 ms; plus some extra */
|
||||
msleep(10 + 40);
|
||||
if (udev) {
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
|
||||
update_devnum(udev, 0);
|
||||
/* The xHC may think the device is already reset,
|
||||
* so ignore the status.
|
||||
*/
|
||||
if (hcd->driver->reset_device)
|
||||
hcd->driver->reset_device(hcd, udev);
|
||||
}
|
||||
/* FALL THROUGH */
|
||||
case -ENOTCONN:
|
||||
case -ENODEV:
|
||||
usb_clear_port_feature(hub->hdev,
|
||||
port1, USB_PORT_FEAT_C_RESET);
|
||||
if (hub_is_superspeed(hub->hdev)) {
|
||||
usb_clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_BH_PORT_RESET);
|
||||
usb_clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_PORT_LINK_STATE);
|
||||
usb_clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_CONNECTION);
|
||||
}
|
||||
if (udev)
|
||||
usb_set_device_state(udev, *status
|
||||
? USB_STATE_NOTATTACHED
|
||||
: USB_STATE_DEFAULT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */
|
||||
static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
struct usb_device *udev, unsigned int delay, bool warm)
|
||||
@ -2767,13 +2732,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
* If the caller hasn't explicitly requested a warm reset,
|
||||
* double check and see if one is needed.
|
||||
*/
|
||||
status = hub_port_status(hub, port1,
|
||||
&portstatus, &portchange);
|
||||
if (status < 0)
|
||||
goto done;
|
||||
|
||||
if (hub_port_warm_reset_required(hub, port1, portstatus))
|
||||
warm = true;
|
||||
if (hub_port_status(hub, port1, &portstatus, &portchange) == 0)
|
||||
if (hub_port_warm_reset_required(hub, port1,
|
||||
portstatus))
|
||||
warm = true;
|
||||
}
|
||||
clear_bit(port1, hub->warm_reset_bits);
|
||||
|
||||
@ -2799,11 +2761,19 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
|
||||
/* Check for disconnect or reset */
|
||||
if (status == 0 || status == -ENOTCONN || status == -ENODEV) {
|
||||
hub_port_finish_reset(hub, port1, udev, &status);
|
||||
usb_clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_RESET);
|
||||
|
||||
if (!hub_is_superspeed(hub->hdev))
|
||||
goto done;
|
||||
|
||||
usb_clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_BH_PORT_RESET);
|
||||
usb_clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_PORT_LINK_STATE);
|
||||
usb_clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_CONNECTION);
|
||||
|
||||
/*
|
||||
* If a USB 3.0 device migrates from reset to an error
|
||||
* state, re-issue the warm reset.
|
||||
@ -2836,6 +2806,26 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
|
||||
dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n");
|
||||
|
||||
done:
|
||||
if (status == 0) {
|
||||
/* TRSTRCY = 10 ms; plus some extra */
|
||||
msleep(10 + 40);
|
||||
if (udev) {
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
|
||||
update_devnum(udev, 0);
|
||||
/* The xHC may think the device is already reset,
|
||||
* so ignore the status.
|
||||
*/
|
||||
if (hcd->driver->reset_device)
|
||||
hcd->driver->reset_device(hcd, udev);
|
||||
|
||||
usb_set_device_state(udev, USB_STATE_DEFAULT);
|
||||
}
|
||||
} else {
|
||||
if (udev)
|
||||
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
|
||||
}
|
||||
|
||||
if (!hub_is_superspeed(hub->hdev))
|
||||
up_read(&ehci_cf_port_reset_rwsem);
|
||||
|
||||
|
@ -50,18 +50,10 @@ config USB_DWC2_DUAL_ROLE
|
||||
option requires USB_GADGET to be enabled.
|
||||
endchoice
|
||||
|
||||
config USB_DWC2_PLATFORM
|
||||
tristate "DWC2 Platform"
|
||||
default USB_DWC2_HOST || USB_DWC2_PERIPHERAL
|
||||
help
|
||||
The Designware USB2.0 platform interface module for
|
||||
controllers directly connected to the CPU.
|
||||
|
||||
config USB_DWC2_PCI
|
||||
tristate "DWC2 PCI"
|
||||
depends on PCI
|
||||
default n
|
||||
select USB_DWC2_PLATFORM
|
||||
select NOP_USB_XCEIV
|
||||
help
|
||||
The Designware USB2.0 PCI interface module for controllers
|
||||
|
@ -2,7 +2,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
|
||||
dwc2-y := core.o core_intr.o platform.o
|
||||
|
||||
ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
|
||||
dwc2-y += hcd.o hcd_intr.o
|
||||
@ -13,6 +13,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC2_PERIPHERAL) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
|
||||
dwc2-y += gadget.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_DEBUG_FS),)
|
||||
dwc2-y += debugfs.o
|
||||
endif
|
||||
|
||||
# NOTE: The previous s3c-hsotg peripheral mode only driver has been moved to
|
||||
# this location and renamed gadget.c. When building for dynamically linked
|
||||
# modules, dwc2.ko will get built for host mode, peripheral mode, and dual-role
|
||||
@ -21,6 +25,3 @@ endif
|
||||
|
||||
obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o
|
||||
dwc2_pci-y := pci.o
|
||||
|
||||
obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o
|
||||
dwc2_platform-y := platform.o
|
||||
|
@ -56,6 +56,389 @@
|
||||
#include "core.h"
|
||||
#include "hcd.h"
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
/**
|
||||
* dwc2_backup_host_registers() - Backup controller host registers.
|
||||
* When suspending usb bus, registers needs to be backuped
|
||||
* if controller power is disabled once suspended.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*/
|
||||
static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_hregs_backup *hr;
|
||||
int i;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s\n", __func__);
|
||||
|
||||
/* Backup Host regs */
|
||||
hr = hsotg->hr_backup;
|
||||
if (!hr) {
|
||||
hr = devm_kzalloc(hsotg->dev, sizeof(*hr), GFP_KERNEL);
|
||||
if (!hr) {
|
||||
dev_err(hsotg->dev, "%s: can't allocate host regs\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hsotg->hr_backup = hr;
|
||||
}
|
||||
hr->hcfg = readl(hsotg->regs + HCFG);
|
||||
hr->haintmsk = readl(hsotg->regs + HAINTMSK);
|
||||
for (i = 0; i < hsotg->core_params->host_channels; ++i)
|
||||
hr->hcintmsk[i] = readl(hsotg->regs + HCINTMSK(i));
|
||||
|
||||
hr->hprt0 = readl(hsotg->regs + HPRT0);
|
||||
hr->hfir = readl(hsotg->regs + HFIR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_restore_host_registers() - Restore controller host registers.
|
||||
* When resuming usb bus, device registers needs to be restored
|
||||
* if controller power were disabled.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*/
|
||||
static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_hregs_backup *hr;
|
||||
int i;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s\n", __func__);
|
||||
|
||||
/* Restore host regs */
|
||||
hr = hsotg->hr_backup;
|
||||
if (!hr) {
|
||||
dev_err(hsotg->dev, "%s: no host registers to restore\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(hr->hcfg, hsotg->regs + HCFG);
|
||||
writel(hr->haintmsk, hsotg->regs + HAINTMSK);
|
||||
|
||||
for (i = 0; i < hsotg->core_params->host_channels; ++i)
|
||||
writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i));
|
||||
|
||||
writel(hr->hprt0, hsotg->regs + HPRT0);
|
||||
writel(hr->hfir, hsotg->regs + HFIR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
|
||||
static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
|
||||
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
/**
|
||||
* dwc2_backup_device_registers() - Backup controller device registers.
|
||||
* When suspending usb bus, registers needs to be backuped
|
||||
* if controller power is disabled once suspended.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*/
|
||||
static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_dregs_backup *dr;
|
||||
int i;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s\n", __func__);
|
||||
|
||||
/* Backup dev regs */
|
||||
dr = hsotg->dr_backup;
|
||||
if (!dr) {
|
||||
dr = devm_kzalloc(hsotg->dev, sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr) {
|
||||
dev_err(hsotg->dev, "%s: can't allocate device regs\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hsotg->dr_backup = dr;
|
||||
}
|
||||
|
||||
dr->dcfg = readl(hsotg->regs + DCFG);
|
||||
dr->dctl = readl(hsotg->regs + DCTL);
|
||||
dr->daintmsk = readl(hsotg->regs + DAINTMSK);
|
||||
dr->diepmsk = readl(hsotg->regs + DIEPMSK);
|
||||
dr->doepmsk = readl(hsotg->regs + DOEPMSK);
|
||||
|
||||
for (i = 0; i < hsotg->num_of_eps; i++) {
|
||||
/* Backup IN EPs */
|
||||
dr->diepctl[i] = readl(hsotg->regs + DIEPCTL(i));
|
||||
|
||||
/* Ensure DATA PID is correctly configured */
|
||||
if (dr->diepctl[i] & DXEPCTL_DPID)
|
||||
dr->diepctl[i] |= DXEPCTL_SETD1PID;
|
||||
else
|
||||
dr->diepctl[i] |= DXEPCTL_SETD0PID;
|
||||
|
||||
dr->dieptsiz[i] = readl(hsotg->regs + DIEPTSIZ(i));
|
||||
dr->diepdma[i] = readl(hsotg->regs + DIEPDMA(i));
|
||||
|
||||
/* Backup OUT EPs */
|
||||
dr->doepctl[i] = readl(hsotg->regs + DOEPCTL(i));
|
||||
|
||||
/* Ensure DATA PID is correctly configured */
|
||||
if (dr->doepctl[i] & DXEPCTL_DPID)
|
||||
dr->doepctl[i] |= DXEPCTL_SETD1PID;
|
||||
else
|
||||
dr->doepctl[i] |= DXEPCTL_SETD0PID;
|
||||
|
||||
dr->doeptsiz[i] = readl(hsotg->regs + DOEPTSIZ(i));
|
||||
dr->doepdma[i] = readl(hsotg->regs + DOEPDMA(i));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_restore_device_registers() - Restore controller device registers.
|
||||
* When resuming usb bus, device registers needs to be restored
|
||||
* if controller power were disabled.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*/
|
||||
static int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_dregs_backup *dr;
|
||||
u32 dctl;
|
||||
int i;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s\n", __func__);
|
||||
|
||||
/* Restore dev regs */
|
||||
dr = hsotg->dr_backup;
|
||||
if (!dr) {
|
||||
dev_err(hsotg->dev, "%s: no device registers to restore\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(dr->dcfg, hsotg->regs + DCFG);
|
||||
writel(dr->dctl, hsotg->regs + DCTL);
|
||||
writel(dr->daintmsk, hsotg->regs + DAINTMSK);
|
||||
writel(dr->diepmsk, hsotg->regs + DIEPMSK);
|
||||
writel(dr->doepmsk, hsotg->regs + DOEPMSK);
|
||||
|
||||
for (i = 0; i < hsotg->num_of_eps; i++) {
|
||||
/* Restore IN EPs */
|
||||
writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i));
|
||||
writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i));
|
||||
writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i));
|
||||
|
||||
/* Restore OUT EPs */
|
||||
writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i));
|
||||
writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i));
|
||||
writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i));
|
||||
}
|
||||
|
||||
/* Set the Power-On Programming done bit */
|
||||
dctl = readl(hsotg->regs + DCTL);
|
||||
dctl |= DCTL_PWRONPRGDONE;
|
||||
writel(dctl, hsotg->regs + DCTL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
|
||||
static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* dwc2_backup_global_registers() - Backup global controller registers.
|
||||
* When suspending usb bus, registers needs to be backuped
|
||||
* if controller power is disabled once suspended.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*/
|
||||
static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_gregs_backup *gr;
|
||||
int i;
|
||||
|
||||
/* Backup global regs */
|
||||
gr = hsotg->gr_backup;
|
||||
if (!gr) {
|
||||
gr = devm_kzalloc(hsotg->dev, sizeof(*gr), GFP_KERNEL);
|
||||
if (!gr) {
|
||||
dev_err(hsotg->dev, "%s: can't allocate global regs\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hsotg->gr_backup = gr;
|
||||
}
|
||||
|
||||
gr->gotgctl = readl(hsotg->regs + GOTGCTL);
|
||||
gr->gintmsk = readl(hsotg->regs + GINTMSK);
|
||||
gr->gahbcfg = readl(hsotg->regs + GAHBCFG);
|
||||
gr->gusbcfg = readl(hsotg->regs + GUSBCFG);
|
||||
gr->grxfsiz = readl(hsotg->regs + GRXFSIZ);
|
||||
gr->gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ);
|
||||
gr->hptxfsiz = readl(hsotg->regs + HPTXFSIZ);
|
||||
gr->gdfifocfg = readl(hsotg->regs + GDFIFOCFG);
|
||||
for (i = 0; i < MAX_EPS_CHANNELS; i++)
|
||||
gr->dtxfsiz[i] = readl(hsotg->regs + DPTXFSIZN(i));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_restore_global_registers() - Restore controller global registers.
|
||||
* When resuming usb bus, device registers needs to be restored
|
||||
* if controller power were disabled.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*/
|
||||
static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dwc2_gregs_backup *gr;
|
||||
int i;
|
||||
|
||||
dev_dbg(hsotg->dev, "%s\n", __func__);
|
||||
|
||||
/* Restore global regs */
|
||||
gr = hsotg->gr_backup;
|
||||
if (!gr) {
|
||||
dev_err(hsotg->dev, "%s: no global registers to restore\n",
|
||||
__func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(0xffffffff, hsotg->regs + GINTSTS);
|
||||
writel(gr->gotgctl, hsotg->regs + GOTGCTL);
|
||||
writel(gr->gintmsk, hsotg->regs + GINTMSK);
|
||||
writel(gr->gusbcfg, hsotg->regs + GUSBCFG);
|
||||
writel(gr->gahbcfg, hsotg->regs + GAHBCFG);
|
||||
writel(gr->grxfsiz, hsotg->regs + GRXFSIZ);
|
||||
writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ);
|
||||
writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ);
|
||||
writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG);
|
||||
for (i = 0; i < MAX_EPS_CHANNELS; i++)
|
||||
writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_exit_hibernation() - Exit controller from Partial Power Down.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
* @restore: Controller registers need to be restored
|
||||
*/
|
||||
int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore)
|
||||
{
|
||||
u32 pcgcctl;
|
||||
int ret = 0;
|
||||
|
||||
if (!hsotg->core_params->hibernation)
|
||||
return -ENOTSUPP;
|
||||
|
||||
pcgcctl = readl(hsotg->regs + PCGCTL);
|
||||
pcgcctl &= ~PCGCTL_STOPPCLK;
|
||||
writel(pcgcctl, hsotg->regs + PCGCTL);
|
||||
|
||||
pcgcctl = readl(hsotg->regs + PCGCTL);
|
||||
pcgcctl &= ~PCGCTL_PWRCLMP;
|
||||
writel(pcgcctl, hsotg->regs + PCGCTL);
|
||||
|
||||
pcgcctl = readl(hsotg->regs + PCGCTL);
|
||||
pcgcctl &= ~PCGCTL_RSTPDWNMODULE;
|
||||
writel(pcgcctl, hsotg->regs + PCGCTL);
|
||||
|
||||
udelay(100);
|
||||
if (restore) {
|
||||
ret = dwc2_restore_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
if (dwc2_is_host_mode(hsotg)) {
|
||||
ret = dwc2_restore_host_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore host registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = dwc2_restore_device_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to restore device registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_enter_hibernation() - Put controller in Partial Power Down.
|
||||
*
|
||||
* @hsotg: Programming view of the DWC_otg controller
|
||||
*/
|
||||
int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 pcgcctl;
|
||||
int ret = 0;
|
||||
|
||||
if (!hsotg->core_params->hibernation)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* Backup all registers */
|
||||
ret = dwc2_backup_global_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup global registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dwc2_is_host_mode(hsotg)) {
|
||||
ret = dwc2_backup_host_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup host registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = dwc2_backup_device_registers(hsotg);
|
||||
if (ret) {
|
||||
dev_err(hsotg->dev, "%s: failed to backup device registers\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Put the controller in low power state */
|
||||
pcgcctl = readl(hsotg->regs + PCGCTL);
|
||||
|
||||
pcgcctl |= PCGCTL_PWRCLMP;
|
||||
writel(pcgcctl, hsotg->regs + PCGCTL);
|
||||
ndelay(20);
|
||||
|
||||
pcgcctl |= PCGCTL_RSTPDWNMODULE;
|
||||
writel(pcgcctl, hsotg->regs + PCGCTL);
|
||||
ndelay(20);
|
||||
|
||||
pcgcctl |= PCGCTL_STOPPCLK;
|
||||
writel(pcgcctl, hsotg->regs + PCGCTL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc2_enable_common_interrupts() - Initializes the commmon interrupts,
|
||||
* used in both device and host modes
|
||||
@ -77,8 +460,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg)
|
||||
|
||||
if (hsotg->core_params->dma_enable <= 0)
|
||||
intmsk |= GINTSTS_RXFLVL;
|
||||
if (hsotg->core_params->external_id_pin_ctl <= 0)
|
||||
intmsk |= GINTSTS_CONIDSTSCHNG;
|
||||
|
||||
intmsk |= GINTSTS_CONIDSTSCHNG | GINTSTS_WKUPINT | GINTSTS_USBSUSP |
|
||||
intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP |
|
||||
GINTSTS_SESSREQINT;
|
||||
|
||||
writel(intmsk, hsotg->regs + GINTMSK);
|
||||
@ -2602,6 +2987,40 @@ static void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val)
|
||||
hsotg->core_params->uframe_sched = val;
|
||||
}
|
||||
|
||||
static void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg,
|
||||
int val)
|
||||
{
|
||||
if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
|
||||
if (val >= 0) {
|
||||
dev_err(hsotg->dev,
|
||||
"'%d' invalid for parameter external_id_pin_ctl\n",
|
||||
val);
|
||||
dev_err(hsotg->dev, "external_id_pin_ctl must be 0 or 1\n");
|
||||
}
|
||||
val = 0;
|
||||
dev_dbg(hsotg->dev, "Setting external_id_pin_ctl to %d\n", val);
|
||||
}
|
||||
|
||||
hsotg->core_params->external_id_pin_ctl = val;
|
||||
}
|
||||
|
||||
static void dwc2_set_param_hibernation(struct dwc2_hsotg *hsotg,
|
||||
int val)
|
||||
{
|
||||
if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) {
|
||||
if (val >= 0) {
|
||||
dev_err(hsotg->dev,
|
||||
"'%d' invalid for parameter hibernation\n",
|
||||
val);
|
||||
dev_err(hsotg->dev, "hibernation must be 0 or 1\n");
|
||||
}
|
||||
val = 0;
|
||||
dev_dbg(hsotg->dev, "Setting hibernation to %d\n", val);
|
||||
}
|
||||
|
||||
hsotg->core_params->hibernation = val;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called during module intialization to pass module parameters
|
||||
* for the DWC_otg core.
|
||||
@ -2646,6 +3065,8 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
|
||||
dwc2_set_param_ahbcfg(hsotg, params->ahbcfg);
|
||||
dwc2_set_param_otg_ver(hsotg, params->otg_ver);
|
||||
dwc2_set_param_uframe_sched(hsotg, params->uframe_sched);
|
||||
dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl);
|
||||
dwc2_set_param_hibernation(hsotg, params->hibernation);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2814,6 +3235,22 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets all parameters to the given value.
|
||||
*
|
||||
* Assumes that the dwc2_core_params struct contains only integers.
|
||||
*/
|
||||
void dwc2_set_all_params(struct dwc2_core_params *params, int value)
|
||||
{
|
||||
int *p = (int *)params;
|
||||
size_t size = sizeof(*params) / sizeof(*p);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
p[i] = value;
|
||||
}
|
||||
|
||||
|
||||
u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103;
|
||||
|
@ -331,6 +331,17 @@ enum dwc2_ep0_state {
|
||||
* by the driver and are ignored in this
|
||||
* configuration value.
|
||||
* @uframe_sched: True to enable the microframe scheduler
|
||||
* @external_id_pin_ctl: Specifies whether ID pin is handled externally.
|
||||
* Disable CONIDSTSCHNG controller interrupt in such
|
||||
* case.
|
||||
* 0 - No (default)
|
||||
* 1 - Yes
|
||||
* @hibernation: Specifies whether the controller support hibernation.
|
||||
* If hibernation is enabled, the controller will enter
|
||||
* hibernation in both peripheral and host mode when
|
||||
* needed.
|
||||
* 0 - No (default)
|
||||
* 1 - Yes
|
||||
*
|
||||
* The following parameters may be specified when starting the module. These
|
||||
* parameters define how the DWC_otg controller should be configured. A
|
||||
@ -368,6 +379,8 @@ struct dwc2_core_params {
|
||||
int reload_ctl;
|
||||
int ahbcfg;
|
||||
int uframe_sched;
|
||||
int external_id_pin_ctl;
|
||||
int hibernation;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -451,6 +464,82 @@ struct dwc2_hw_params {
|
||||
/* Size of control and EP0 buffers */
|
||||
#define DWC2_CTRL_BUFF_SIZE 8
|
||||
|
||||
/**
|
||||
* struct dwc2_gregs_backup - Holds global registers state before entering partial
|
||||
* power down
|
||||
* @gotgctl: Backup of GOTGCTL register
|
||||
* @gintmsk: Backup of GINTMSK register
|
||||
* @gahbcfg: Backup of GAHBCFG register
|
||||
* @gusbcfg: Backup of GUSBCFG register
|
||||
* @grxfsiz: Backup of GRXFSIZ register
|
||||
* @gnptxfsiz: Backup of GNPTXFSIZ register
|
||||
* @gi2cctl: Backup of GI2CCTL register
|
||||
* @hptxfsiz: Backup of HPTXFSIZ register
|
||||
* @gdfifocfg: Backup of GDFIFOCFG register
|
||||
* @dtxfsiz: Backup of DTXFSIZ registers for each endpoint
|
||||
* @gpwrdn: Backup of GPWRDN register
|
||||
*/
|
||||
struct dwc2_gregs_backup {
|
||||
u32 gotgctl;
|
||||
u32 gintmsk;
|
||||
u32 gahbcfg;
|
||||
u32 gusbcfg;
|
||||
u32 grxfsiz;
|
||||
u32 gnptxfsiz;
|
||||
u32 gi2cctl;
|
||||
u32 hptxfsiz;
|
||||
u32 pcgcctl;
|
||||
u32 gdfifocfg;
|
||||
u32 dtxfsiz[MAX_EPS_CHANNELS];
|
||||
u32 gpwrdn;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dwc2_dregs_backup - Holds device registers state before entering partial
|
||||
* power down
|
||||
* @dcfg: Backup of DCFG register
|
||||
* @dctl: Backup of DCTL register
|
||||
* @daintmsk: Backup of DAINTMSK register
|
||||
* @diepmsk: Backup of DIEPMSK register
|
||||
* @doepmsk: Backup of DOEPMSK register
|
||||
* @diepctl: Backup of DIEPCTL register
|
||||
* @dieptsiz: Backup of DIEPTSIZ register
|
||||
* @diepdma: Backup of DIEPDMA register
|
||||
* @doepctl: Backup of DOEPCTL register
|
||||
* @doeptsiz: Backup of DOEPTSIZ register
|
||||
* @doepdma: Backup of DOEPDMA register
|
||||
*/
|
||||
struct dwc2_dregs_backup {
|
||||
u32 dcfg;
|
||||
u32 dctl;
|
||||
u32 daintmsk;
|
||||
u32 diepmsk;
|
||||
u32 doepmsk;
|
||||
u32 diepctl[MAX_EPS_CHANNELS];
|
||||
u32 dieptsiz[MAX_EPS_CHANNELS];
|
||||
u32 diepdma[MAX_EPS_CHANNELS];
|
||||
u32 doepctl[MAX_EPS_CHANNELS];
|
||||
u32 doeptsiz[MAX_EPS_CHANNELS];
|
||||
u32 doepdma[MAX_EPS_CHANNELS];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dwc2_hregs_backup - Holds host registers state before entering partial
|
||||
* power down
|
||||
* @hcfg: Backup of HCFG register
|
||||
* @haintmsk: Backup of HAINTMSK register
|
||||
* @hcintmsk: Backup of HCINTMSK register
|
||||
* @hptr0: Backup of HPTR0 register
|
||||
* @hfir: Backup of HFIR register
|
||||
*/
|
||||
struct dwc2_hregs_backup {
|
||||
u32 hcfg;
|
||||
u32 haintmsk;
|
||||
u32 hcintmsk[MAX_EPS_CHANNELS];
|
||||
u32 hprt0;
|
||||
u32 hfir;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
|
||||
* and periodic schedules
|
||||
@ -481,6 +570,9 @@ struct dwc2_hw_params {
|
||||
* interrupt
|
||||
* @wkp_timer: Timer object for handling Wakeup Detected interrupt
|
||||
* @lx_state: Lx state of connected device
|
||||
* @gregs_backup: Backup of global registers during suspend
|
||||
* @dregs_backup: Backup of device registers during suspend
|
||||
* @hregs_backup: Backup of host registers during suspend
|
||||
*
|
||||
* These are for host mode:
|
||||
*
|
||||
@ -613,11 +705,12 @@ struct dwc2_hsotg {
|
||||
struct work_struct wf_otg;
|
||||
struct timer_list wkp_timer;
|
||||
enum dwc2_lx_state lx_state;
|
||||
struct dwc2_gregs_backup *gr_backup;
|
||||
struct dwc2_dregs_backup *dr_backup;
|
||||
struct dwc2_hregs_backup *hr_backup;
|
||||
|
||||
struct dentry *debug_root;
|
||||
struct dentry *debug_file;
|
||||
struct dentry *debug_testmode;
|
||||
struct dentry *debug_fifo;
|
||||
struct debugfs_regset32 *regset;
|
||||
|
||||
/* DWC OTG HW Release versions */
|
||||
#define DWC2_CORE_REV_2_71a 0x4f54271a
|
||||
@ -751,6 +844,8 @@ enum dwc2_halt_status {
|
||||
* and the DWC_otg controller
|
||||
*/
|
||||
extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg);
|
||||
extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore);
|
||||
|
||||
/*
|
||||
* Host core Functions.
|
||||
@ -983,6 +1078,15 @@ extern void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val);
|
||||
|
||||
extern void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val);
|
||||
|
||||
extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
|
||||
const struct dwc2_core_params *params);
|
||||
|
||||
extern void dwc2_set_all_params(struct dwc2_core_params *params, int value);
|
||||
|
||||
extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Dump core registers and SPRAM
|
||||
*/
|
||||
@ -1005,6 +1109,8 @@ extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
|
||||
bool reset);
|
||||
extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg);
|
||||
extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2);
|
||||
extern int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode);
|
||||
#define dwc2_is_device_connected(hsotg) (hsotg->connected)
|
||||
#else
|
||||
static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2)
|
||||
{ return 0; }
|
||||
@ -1018,6 +1124,10 @@ static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
|
||||
bool reset) {}
|
||||
static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
|
||||
static inline int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg,
|
||||
int testmode)
|
||||
{ return 0; }
|
||||
#define dwc2_is_device_connected(hsotg) (0)
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
@ -1025,14 +1135,12 @@ extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
|
||||
#else
|
||||
static inline void dwc2_set_all_params(struct dwc2_core_params *params, int value) {}
|
||||
static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
|
||||
static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
|
||||
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
|
||||
const struct dwc2_core_params *params)
|
||||
static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
{ return 0; }
|
||||
#endif
|
||||
|
||||
|
@ -334,6 +334,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
|
||||
*/
|
||||
static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int ret;
|
||||
dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n");
|
||||
dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state);
|
||||
|
||||
@ -345,6 +346,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
|
||||
/* Clear Remote Wakeup Signaling */
|
||||
dctl &= ~DCTL_RMTWKUPSIG;
|
||||
writel(dctl, hsotg->regs + DCTL);
|
||||
ret = dwc2_exit_hibernation(hsotg, true);
|
||||
if (ret && (ret != -ENOTSUPP))
|
||||
dev_err(hsotg->dev, "exit hibernation failed\n");
|
||||
|
||||
call_gadget(hsotg, resume);
|
||||
}
|
||||
/* Change to L0 state */
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
@ -397,6 +403,7 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg)
|
||||
static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
u32 dsts;
|
||||
int ret;
|
||||
|
||||
dev_dbg(hsotg->dev, "USB SUSPEND\n");
|
||||
|
||||
@ -411,10 +418,43 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
|
||||
"DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n",
|
||||
!!(dsts & DSTS_SUSPSTS),
|
||||
hsotg->hw_params.power_optimized);
|
||||
if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) {
|
||||
/* Ignore suspend request before enumeration */
|
||||
if (!dwc2_is_device_connected(hsotg)) {
|
||||
dev_dbg(hsotg->dev,
|
||||
"ignore suspend request before enumeration\n");
|
||||
goto clear_int;
|
||||
}
|
||||
|
||||
ret = dwc2_enter_hibernation(hsotg);
|
||||
if (ret) {
|
||||
if (ret != -ENOTSUPP)
|
||||
dev_err(hsotg->dev,
|
||||
"enter hibernation failed\n");
|
||||
goto skip_power_saving;
|
||||
}
|
||||
|
||||
udelay(100);
|
||||
|
||||
/* Ask phy to be suspended */
|
||||
if (!IS_ERR_OR_NULL(hsotg->uphy))
|
||||
usb_phy_set_suspend(hsotg->uphy, true);
|
||||
skip_power_saving:
|
||||
/*
|
||||
* Change to L2 (suspend) state before releasing
|
||||
* spinlock
|
||||
*/
|
||||
hsotg->lx_state = DWC2_L2;
|
||||
|
||||
/* Call gadget suspend callback */
|
||||
call_gadget(hsotg, suspend);
|
||||
}
|
||||
} else {
|
||||
if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) {
|
||||
dev_dbg(hsotg->dev, "a_peripheral->a_host\n");
|
||||
|
||||
/* Change to L2 (suspend) state */
|
||||
hsotg->lx_state = DWC2_L2;
|
||||
/* Clear the a_peripheral flag, back to a_host */
|
||||
spin_unlock(&hsotg->lock);
|
||||
dwc2_hcd_start(hsotg);
|
||||
@ -423,9 +463,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg)
|
||||
}
|
||||
}
|
||||
|
||||
/* Change to L2 (suspend) state */
|
||||
hsotg->lx_state = DWC2_L2;
|
||||
|
||||
clear_int:
|
||||
/* Clear interrupt */
|
||||
writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
|
||||
}
|
||||
@ -522,4 +560,3 @@ out:
|
||||
spin_unlock(&hsotg->lock);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc2_handle_common_intr);
|
||||
|
27
drivers/usb/dwc2/debug.h
Normal file
27
drivers/usb/dwc2/debug.h
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* debug.h - Designware USB2 DRD controller debug header
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
* Mian Yousaf Kaukab <yousaf.kaukab@intel.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
extern int dwc2_debugfs_init(struct dwc2_hsotg *);
|
||||
extern void dwc2_debugfs_exit(struct dwc2_hsotg *);
|
||||
#else
|
||||
static inline int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
|
||||
{ return 0; }
|
||||
static inline void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg)
|
||||
{ }
|
||||
#endif
|
771
drivers/usb/dwc2/debugfs.c
Normal file
771
drivers/usb/dwc2/debugfs.c
Normal file
@ -0,0 +1,771 @@
|
||||
/**
|
||||
* debugfs.c - Designware USB2 DRD controller debugfs
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
* Mian Yousaf Kaukab <yousaf.kaukab@intel.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "debug.h"
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \
|
||||
IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
|
||||
/**
|
||||
* testmode_write - debugfs: change usb test mode
|
||||
* @seq: The seq file to write to.
|
||||
* @v: Unused parameter.
|
||||
*
|
||||
* This debugfs entry modify the current usb test mode.
|
||||
*/
|
||||
static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t
|
||||
count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct dwc2_hsotg *hsotg = s->private;
|
||||
unsigned long flags;
|
||||
u32 testmode = 0;
|
||||
char buf[32];
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!strncmp(buf, "test_j", 6))
|
||||
testmode = TEST_J;
|
||||
else if (!strncmp(buf, "test_k", 6))
|
||||
testmode = TEST_K;
|
||||
else if (!strncmp(buf, "test_se0_nak", 12))
|
||||
testmode = TEST_SE0_NAK;
|
||||
else if (!strncmp(buf, "test_packet", 11))
|
||||
testmode = TEST_PACKET;
|
||||
else if (!strncmp(buf, "test_force_enable", 17))
|
||||
testmode = TEST_FORCE_EN;
|
||||
else
|
||||
testmode = 0;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
s3c_hsotg_set_test_mode(hsotg, testmode);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* testmode_show - debugfs: show usb test mode state
|
||||
* @seq: The seq file to write to.
|
||||
* @v: Unused parameter.
|
||||
*
|
||||
* This debugfs entry shows which usb test mode is currently enabled.
|
||||
*/
|
||||
static int testmode_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = s->private;
|
||||
unsigned long flags;
|
||||
int dctl;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
dctl = readl(hsotg->regs + DCTL);
|
||||
dctl &= DCTL_TSTCTL_MASK;
|
||||
dctl >>= DCTL_TSTCTL_SHIFT;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
switch (dctl) {
|
||||
case 0:
|
||||
seq_puts(s, "no test\n");
|
||||
break;
|
||||
case TEST_J:
|
||||
seq_puts(s, "test_j\n");
|
||||
break;
|
||||
case TEST_K:
|
||||
seq_puts(s, "test_k\n");
|
||||
break;
|
||||
case TEST_SE0_NAK:
|
||||
seq_puts(s, "test_se0_nak\n");
|
||||
break;
|
||||
case TEST_PACKET:
|
||||
seq_puts(s, "test_packet\n");
|
||||
break;
|
||||
case TEST_FORCE_EN:
|
||||
seq_puts(s, "test_force_enable\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "UNKNOWN %d\n", dctl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int testmode_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, testmode_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations testmode_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = testmode_open,
|
||||
.write = testmode_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* state_show - debugfs: show overall driver and device state.
|
||||
* @seq: The seq file to write to.
|
||||
* @v: Unused parameter.
|
||||
*
|
||||
* This debugfs entry shows the overall state of the hardware and
|
||||
* some general information about each of the endpoints available
|
||||
* to the system.
|
||||
*/
|
||||
static int state_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = seq->private;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
int idx;
|
||||
|
||||
seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n",
|
||||
readl(regs + DCFG),
|
||||
readl(regs + DCTL),
|
||||
readl(regs + DSTS));
|
||||
|
||||
seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n",
|
||||
readl(regs + DIEPMSK), readl(regs + DOEPMSK));
|
||||
|
||||
seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n",
|
||||
readl(regs + GINTMSK),
|
||||
readl(regs + GINTSTS));
|
||||
|
||||
seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n",
|
||||
readl(regs + DAINTMSK),
|
||||
readl(regs + DAINT));
|
||||
|
||||
seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n",
|
||||
readl(regs + GNPTXSTS),
|
||||
readl(regs + GRXSTSR));
|
||||
|
||||
seq_puts(seq, "\nEndpoint status:\n");
|
||||
|
||||
for (idx = 0; idx < hsotg->num_of_eps; idx++) {
|
||||
u32 in, out;
|
||||
|
||||
in = readl(regs + DIEPCTL(idx));
|
||||
out = readl(regs + DOEPCTL(idx));
|
||||
|
||||
seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x",
|
||||
idx, in, out);
|
||||
|
||||
in = readl(regs + DIEPTSIZ(idx));
|
||||
out = readl(regs + DOEPTSIZ(idx));
|
||||
|
||||
seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x",
|
||||
in, out);
|
||||
|
||||
seq_puts(seq, "\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int state_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, state_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations state_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = state_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* fifo_show - debugfs: show the fifo information
|
||||
* @seq: The seq_file to write data to.
|
||||
* @v: Unused parameter.
|
||||
*
|
||||
* Show the FIFO information for the overall fifo and all the
|
||||
* periodic transmission FIFOs.
|
||||
*/
|
||||
static int fifo_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = seq->private;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
u32 val;
|
||||
int idx;
|
||||
|
||||
seq_puts(seq, "Non-periodic FIFOs:\n");
|
||||
seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ));
|
||||
|
||||
val = readl(regs + GNPTXFSIZ);
|
||||
seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n",
|
||||
val >> FIFOSIZE_DEPTH_SHIFT,
|
||||
val & FIFOSIZE_DEPTH_MASK);
|
||||
|
||||
seq_puts(seq, "\nPeriodic TXFIFOs:\n");
|
||||
|
||||
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
|
||||
val = readl(regs + DPTXFSIZN(idx));
|
||||
|
||||
seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
|
||||
val >> FIFOSIZE_DEPTH_SHIFT,
|
||||
val & FIFOSIZE_STARTADDR_MASK);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fifo_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, fifo_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations fifo_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = fifo_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static const char *decode_direction(int is_in)
|
||||
{
|
||||
return is_in ? "in" : "out";
|
||||
}
|
||||
|
||||
/**
|
||||
* ep_show - debugfs: show the state of an endpoint.
|
||||
* @seq: The seq_file to write data to.
|
||||
* @v: Unused parameter.
|
||||
*
|
||||
* This debugfs entry shows the state of the given endpoint (one is
|
||||
* registered for each available).
|
||||
*/
|
||||
static int ep_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep = seq->private;
|
||||
struct dwc2_hsotg *hsotg = ep->parent;
|
||||
struct s3c_hsotg_req *req;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
int index = ep->index;
|
||||
int show_limit = 15;
|
||||
unsigned long flags;
|
||||
|
||||
seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n",
|
||||
ep->index, ep->ep.name, decode_direction(ep->dir_in));
|
||||
|
||||
/* first show the register state */
|
||||
|
||||
seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n",
|
||||
readl(regs + DIEPCTL(index)),
|
||||
readl(regs + DOEPCTL(index)));
|
||||
|
||||
seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n",
|
||||
readl(regs + DIEPDMA(index)),
|
||||
readl(regs + DOEPDMA(index)));
|
||||
|
||||
seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n",
|
||||
readl(regs + DIEPINT(index)),
|
||||
readl(regs + DOEPINT(index)));
|
||||
|
||||
seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n",
|
||||
readl(regs + DIEPTSIZ(index)),
|
||||
readl(regs + DOEPTSIZ(index)));
|
||||
|
||||
seq_puts(seq, "\n");
|
||||
seq_printf(seq, "mps %d\n", ep->ep.maxpacket);
|
||||
seq_printf(seq, "total_data=%ld\n", ep->total_data);
|
||||
|
||||
seq_printf(seq, "request list (%p,%p):\n",
|
||||
ep->queue.next, ep->queue.prev);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (--show_limit < 0) {
|
||||
seq_puts(seq, "not showing more requests...\n");
|
||||
break;
|
||||
}
|
||||
|
||||
seq_printf(seq, "%c req %p: %d bytes @%p, ",
|
||||
req == ep->req ? '*' : ' ',
|
||||
req, req->req.length, req->req.buf);
|
||||
seq_printf(seq, "%d done, res %d\n",
|
||||
req->req.actual, req->req.status);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ep_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ep_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ep_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* s3c_hsotg_create_debug - create debugfs directory and files
|
||||
* @hsotg: The driver state
|
||||
*
|
||||
* Create the debugfs files to allow the user to get information
|
||||
* about the state of the system. The directory name is created
|
||||
* with the same name as the device itself, in case we end up
|
||||
* with multiple blocks in future systems.
|
||||
*/
|
||||
static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dentry *root;
|
||||
struct dentry *file;
|
||||
unsigned epidx;
|
||||
|
||||
root = hsotg->debug_root;
|
||||
|
||||
/* create general state file */
|
||||
|
||||
file = debugfs_create_file("state", S_IRUGO, root, hsotg, &state_fops);
|
||||
if (IS_ERR(file))
|
||||
dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
|
||||
|
||||
file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, hsotg,
|
||||
&testmode_fops);
|
||||
if (IS_ERR(file))
|
||||
dev_err(hsotg->dev, "%s: failed to create testmode\n",
|
||||
__func__);
|
||||
|
||||
file = debugfs_create_file("fifo", S_IRUGO, root, hsotg, &fifo_fops);
|
||||
if (IS_ERR(file))
|
||||
dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__);
|
||||
|
||||
/* Create one file for each out endpoint */
|
||||
for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
|
||||
struct s3c_hsotg_ep *ep;
|
||||
|
||||
ep = hsotg->eps_out[epidx];
|
||||
if (ep) {
|
||||
file = debugfs_create_file(ep->name, S_IRUGO,
|
||||
root, ep, &ep_fops);
|
||||
if (IS_ERR(file))
|
||||
dev_err(hsotg->dev, "failed to create %s debug file\n",
|
||||
ep->name);
|
||||
}
|
||||
}
|
||||
/* Create one file for each in endpoint. EP0 is handled with out eps */
|
||||
for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) {
|
||||
struct s3c_hsotg_ep *ep;
|
||||
|
||||
ep = hsotg->eps_in[epidx];
|
||||
if (ep) {
|
||||
file = debugfs_create_file(ep->name, S_IRUGO,
|
||||
root, ep, &ep_fops);
|
||||
if (IS_ERR(file))
|
||||
dev_err(hsotg->dev, "failed to create %s debug file\n",
|
||||
ep->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) {}
|
||||
#endif
|
||||
|
||||
/* s3c_hsotg_delete_debug is removed as cleanup in done in dwc2_debugfs_exit */
|
||||
|
||||
#define dump_register(nm) \
|
||||
{ \
|
||||
.name = #nm, \
|
||||
.offset = nm, \
|
||||
}
|
||||
|
||||
static const struct debugfs_reg32 dwc2_regs[] = {
|
||||
/*
|
||||
* Accessing registers like this can trigger mode mismatch interrupt.
|
||||
* However, according to dwc2 databook, the register access, in this
|
||||
* case, is completed on the processor bus but is ignored by the core
|
||||
* and does not affect its operation.
|
||||
*/
|
||||
dump_register(GOTGCTL),
|
||||
dump_register(GOTGINT),
|
||||
dump_register(GAHBCFG),
|
||||
dump_register(GUSBCFG),
|
||||
dump_register(GRSTCTL),
|
||||
dump_register(GINTSTS),
|
||||
dump_register(GINTMSK),
|
||||
dump_register(GRXSTSR),
|
||||
dump_register(GRXSTSP),
|
||||
dump_register(GRXFSIZ),
|
||||
dump_register(GNPTXFSIZ),
|
||||
dump_register(GNPTXSTS),
|
||||
dump_register(GI2CCTL),
|
||||
dump_register(GPVNDCTL),
|
||||
dump_register(GGPIO),
|
||||
dump_register(GUID),
|
||||
dump_register(GSNPSID),
|
||||
dump_register(GHWCFG1),
|
||||
dump_register(GHWCFG2),
|
||||
dump_register(GHWCFG3),
|
||||
dump_register(GHWCFG4),
|
||||
dump_register(GLPMCFG),
|
||||
dump_register(GPWRDN),
|
||||
dump_register(GDFIFOCFG),
|
||||
dump_register(ADPCTL),
|
||||
dump_register(HPTXFSIZ),
|
||||
dump_register(DPTXFSIZN(1)),
|
||||
dump_register(DPTXFSIZN(2)),
|
||||
dump_register(DPTXFSIZN(3)),
|
||||
dump_register(DPTXFSIZN(4)),
|
||||
dump_register(DPTXFSIZN(5)),
|
||||
dump_register(DPTXFSIZN(6)),
|
||||
dump_register(DPTXFSIZN(7)),
|
||||
dump_register(DPTXFSIZN(8)),
|
||||
dump_register(DPTXFSIZN(9)),
|
||||
dump_register(DPTXFSIZN(10)),
|
||||
dump_register(DPTXFSIZN(11)),
|
||||
dump_register(DPTXFSIZN(12)),
|
||||
dump_register(DPTXFSIZN(13)),
|
||||
dump_register(DPTXFSIZN(14)),
|
||||
dump_register(DPTXFSIZN(15)),
|
||||
dump_register(DCFG),
|
||||
dump_register(DCTL),
|
||||
dump_register(DSTS),
|
||||
dump_register(DIEPMSK),
|
||||
dump_register(DOEPMSK),
|
||||
dump_register(DAINT),
|
||||
dump_register(DAINTMSK),
|
||||
dump_register(DTKNQR1),
|
||||
dump_register(DTKNQR2),
|
||||
dump_register(DTKNQR3),
|
||||
dump_register(DTKNQR4),
|
||||
dump_register(DVBUSDIS),
|
||||
dump_register(DVBUSPULSE),
|
||||
dump_register(DIEPCTL(0)),
|
||||
dump_register(DIEPCTL(1)),
|
||||
dump_register(DIEPCTL(2)),
|
||||
dump_register(DIEPCTL(3)),
|
||||
dump_register(DIEPCTL(4)),
|
||||
dump_register(DIEPCTL(5)),
|
||||
dump_register(DIEPCTL(6)),
|
||||
dump_register(DIEPCTL(7)),
|
||||
dump_register(DIEPCTL(8)),
|
||||
dump_register(DIEPCTL(9)),
|
||||
dump_register(DIEPCTL(10)),
|
||||
dump_register(DIEPCTL(11)),
|
||||
dump_register(DIEPCTL(12)),
|
||||
dump_register(DIEPCTL(13)),
|
||||
dump_register(DIEPCTL(14)),
|
||||
dump_register(DIEPCTL(15)),
|
||||
dump_register(DOEPCTL(0)),
|
||||
dump_register(DOEPCTL(1)),
|
||||
dump_register(DOEPCTL(2)),
|
||||
dump_register(DOEPCTL(3)),
|
||||
dump_register(DOEPCTL(4)),
|
||||
dump_register(DOEPCTL(5)),
|
||||
dump_register(DOEPCTL(6)),
|
||||
dump_register(DOEPCTL(7)),
|
||||
dump_register(DOEPCTL(8)),
|
||||
dump_register(DOEPCTL(9)),
|
||||
dump_register(DOEPCTL(10)),
|
||||
dump_register(DOEPCTL(11)),
|
||||
dump_register(DOEPCTL(12)),
|
||||
dump_register(DOEPCTL(13)),
|
||||
dump_register(DOEPCTL(14)),
|
||||
dump_register(DOEPCTL(15)),
|
||||
dump_register(DIEPINT(0)),
|
||||
dump_register(DIEPINT(1)),
|
||||
dump_register(DIEPINT(2)),
|
||||
dump_register(DIEPINT(3)),
|
||||
dump_register(DIEPINT(4)),
|
||||
dump_register(DIEPINT(5)),
|
||||
dump_register(DIEPINT(6)),
|
||||
dump_register(DIEPINT(7)),
|
||||
dump_register(DIEPINT(8)),
|
||||
dump_register(DIEPINT(9)),
|
||||
dump_register(DIEPINT(10)),
|
||||
dump_register(DIEPINT(11)),
|
||||
dump_register(DIEPINT(12)),
|
||||
dump_register(DIEPINT(13)),
|
||||
dump_register(DIEPINT(14)),
|
||||
dump_register(DIEPINT(15)),
|
||||
dump_register(DOEPINT(0)),
|
||||
dump_register(DOEPINT(1)),
|
||||
dump_register(DOEPINT(2)),
|
||||
dump_register(DOEPINT(3)),
|
||||
dump_register(DOEPINT(4)),
|
||||
dump_register(DOEPINT(5)),
|
||||
dump_register(DOEPINT(6)),
|
||||
dump_register(DOEPINT(7)),
|
||||
dump_register(DOEPINT(8)),
|
||||
dump_register(DOEPINT(9)),
|
||||
dump_register(DOEPINT(10)),
|
||||
dump_register(DOEPINT(11)),
|
||||
dump_register(DOEPINT(12)),
|
||||
dump_register(DOEPINT(13)),
|
||||
dump_register(DOEPINT(14)),
|
||||
dump_register(DOEPINT(15)),
|
||||
dump_register(DIEPTSIZ(0)),
|
||||
dump_register(DIEPTSIZ(1)),
|
||||
dump_register(DIEPTSIZ(2)),
|
||||
dump_register(DIEPTSIZ(3)),
|
||||
dump_register(DIEPTSIZ(4)),
|
||||
dump_register(DIEPTSIZ(5)),
|
||||
dump_register(DIEPTSIZ(6)),
|
||||
dump_register(DIEPTSIZ(7)),
|
||||
dump_register(DIEPTSIZ(8)),
|
||||
dump_register(DIEPTSIZ(9)),
|
||||
dump_register(DIEPTSIZ(10)),
|
||||
dump_register(DIEPTSIZ(11)),
|
||||
dump_register(DIEPTSIZ(12)),
|
||||
dump_register(DIEPTSIZ(13)),
|
||||
dump_register(DIEPTSIZ(14)),
|
||||
dump_register(DIEPTSIZ(15)),
|
||||
dump_register(DOEPTSIZ(0)),
|
||||
dump_register(DOEPTSIZ(1)),
|
||||
dump_register(DOEPTSIZ(2)),
|
||||
dump_register(DOEPTSIZ(3)),
|
||||
dump_register(DOEPTSIZ(4)),
|
||||
dump_register(DOEPTSIZ(5)),
|
||||
dump_register(DOEPTSIZ(6)),
|
||||
dump_register(DOEPTSIZ(7)),
|
||||
dump_register(DOEPTSIZ(8)),
|
||||
dump_register(DOEPTSIZ(9)),
|
||||
dump_register(DOEPTSIZ(10)),
|
||||
dump_register(DOEPTSIZ(11)),
|
||||
dump_register(DOEPTSIZ(12)),
|
||||
dump_register(DOEPTSIZ(13)),
|
||||
dump_register(DOEPTSIZ(14)),
|
||||
dump_register(DOEPTSIZ(15)),
|
||||
dump_register(DIEPDMA(0)),
|
||||
dump_register(DIEPDMA(1)),
|
||||
dump_register(DIEPDMA(2)),
|
||||
dump_register(DIEPDMA(3)),
|
||||
dump_register(DIEPDMA(4)),
|
||||
dump_register(DIEPDMA(5)),
|
||||
dump_register(DIEPDMA(6)),
|
||||
dump_register(DIEPDMA(7)),
|
||||
dump_register(DIEPDMA(8)),
|
||||
dump_register(DIEPDMA(9)),
|
||||
dump_register(DIEPDMA(10)),
|
||||
dump_register(DIEPDMA(11)),
|
||||
dump_register(DIEPDMA(12)),
|
||||
dump_register(DIEPDMA(13)),
|
||||
dump_register(DIEPDMA(14)),
|
||||
dump_register(DIEPDMA(15)),
|
||||
dump_register(DOEPDMA(0)),
|
||||
dump_register(DOEPDMA(1)),
|
||||
dump_register(DOEPDMA(2)),
|
||||
dump_register(DOEPDMA(3)),
|
||||
dump_register(DOEPDMA(4)),
|
||||
dump_register(DOEPDMA(5)),
|
||||
dump_register(DOEPDMA(6)),
|
||||
dump_register(DOEPDMA(7)),
|
||||
dump_register(DOEPDMA(8)),
|
||||
dump_register(DOEPDMA(9)),
|
||||
dump_register(DOEPDMA(10)),
|
||||
dump_register(DOEPDMA(11)),
|
||||
dump_register(DOEPDMA(12)),
|
||||
dump_register(DOEPDMA(13)),
|
||||
dump_register(DOEPDMA(14)),
|
||||
dump_register(DOEPDMA(15)),
|
||||
dump_register(DTXFSTS(0)),
|
||||
dump_register(DTXFSTS(1)),
|
||||
dump_register(DTXFSTS(2)),
|
||||
dump_register(DTXFSTS(3)),
|
||||
dump_register(DTXFSTS(4)),
|
||||
dump_register(DTXFSTS(5)),
|
||||
dump_register(DTXFSTS(6)),
|
||||
dump_register(DTXFSTS(7)),
|
||||
dump_register(DTXFSTS(8)),
|
||||
dump_register(DTXFSTS(9)),
|
||||
dump_register(DTXFSTS(10)),
|
||||
dump_register(DTXFSTS(11)),
|
||||
dump_register(DTXFSTS(12)),
|
||||
dump_register(DTXFSTS(13)),
|
||||
dump_register(DTXFSTS(14)),
|
||||
dump_register(DTXFSTS(15)),
|
||||
dump_register(PCGCTL),
|
||||
dump_register(HCFG),
|
||||
dump_register(HFIR),
|
||||
dump_register(HFNUM),
|
||||
dump_register(HPTXSTS),
|
||||
dump_register(HAINT),
|
||||
dump_register(HAINTMSK),
|
||||
dump_register(HFLBADDR),
|
||||
dump_register(HPRT0),
|
||||
dump_register(HCCHAR(0)),
|
||||
dump_register(HCCHAR(1)),
|
||||
dump_register(HCCHAR(2)),
|
||||
dump_register(HCCHAR(3)),
|
||||
dump_register(HCCHAR(4)),
|
||||
dump_register(HCCHAR(5)),
|
||||
dump_register(HCCHAR(6)),
|
||||
dump_register(HCCHAR(7)),
|
||||
dump_register(HCCHAR(8)),
|
||||
dump_register(HCCHAR(9)),
|
||||
dump_register(HCCHAR(10)),
|
||||
dump_register(HCCHAR(11)),
|
||||
dump_register(HCCHAR(12)),
|
||||
dump_register(HCCHAR(13)),
|
||||
dump_register(HCCHAR(14)),
|
||||
dump_register(HCCHAR(15)),
|
||||
dump_register(HCSPLT(0)),
|
||||
dump_register(HCSPLT(1)),
|
||||
dump_register(HCSPLT(2)),
|
||||
dump_register(HCSPLT(3)),
|
||||
dump_register(HCSPLT(4)),
|
||||
dump_register(HCSPLT(5)),
|
||||
dump_register(HCSPLT(6)),
|
||||
dump_register(HCSPLT(7)),
|
||||
dump_register(HCSPLT(8)),
|
||||
dump_register(HCSPLT(9)),
|
||||
dump_register(HCSPLT(10)),
|
||||
dump_register(HCSPLT(11)),
|
||||
dump_register(HCSPLT(12)),
|
||||
dump_register(HCSPLT(13)),
|
||||
dump_register(HCSPLT(14)),
|
||||
dump_register(HCSPLT(15)),
|
||||
dump_register(HCINT(0)),
|
||||
dump_register(HCINT(1)),
|
||||
dump_register(HCINT(2)),
|
||||
dump_register(HCINT(3)),
|
||||
dump_register(HCINT(4)),
|
||||
dump_register(HCINT(5)),
|
||||
dump_register(HCINT(6)),
|
||||
dump_register(HCINT(7)),
|
||||
dump_register(HCINT(8)),
|
||||
dump_register(HCINT(9)),
|
||||
dump_register(HCINT(10)),
|
||||
dump_register(HCINT(11)),
|
||||
dump_register(HCINT(12)),
|
||||
dump_register(HCINT(13)),
|
||||
dump_register(HCINT(14)),
|
||||
dump_register(HCINT(15)),
|
||||
dump_register(HCINTMSK(0)),
|
||||
dump_register(HCINTMSK(1)),
|
||||
dump_register(HCINTMSK(2)),
|
||||
dump_register(HCINTMSK(3)),
|
||||
dump_register(HCINTMSK(4)),
|
||||
dump_register(HCINTMSK(5)),
|
||||
dump_register(HCINTMSK(6)),
|
||||
dump_register(HCINTMSK(7)),
|
||||
dump_register(HCINTMSK(8)),
|
||||
dump_register(HCINTMSK(9)),
|
||||
dump_register(HCINTMSK(10)),
|
||||
dump_register(HCINTMSK(11)),
|
||||
dump_register(HCINTMSK(12)),
|
||||
dump_register(HCINTMSK(13)),
|
||||
dump_register(HCINTMSK(14)),
|
||||
dump_register(HCINTMSK(15)),
|
||||
dump_register(HCTSIZ(0)),
|
||||
dump_register(HCTSIZ(1)),
|
||||
dump_register(HCTSIZ(2)),
|
||||
dump_register(HCTSIZ(3)),
|
||||
dump_register(HCTSIZ(4)),
|
||||
dump_register(HCTSIZ(5)),
|
||||
dump_register(HCTSIZ(6)),
|
||||
dump_register(HCTSIZ(7)),
|
||||
dump_register(HCTSIZ(8)),
|
||||
dump_register(HCTSIZ(9)),
|
||||
dump_register(HCTSIZ(10)),
|
||||
dump_register(HCTSIZ(11)),
|
||||
dump_register(HCTSIZ(12)),
|
||||
dump_register(HCTSIZ(13)),
|
||||
dump_register(HCTSIZ(14)),
|
||||
dump_register(HCTSIZ(15)),
|
||||
dump_register(HCDMA(0)),
|
||||
dump_register(HCDMA(1)),
|
||||
dump_register(HCDMA(2)),
|
||||
dump_register(HCDMA(3)),
|
||||
dump_register(HCDMA(4)),
|
||||
dump_register(HCDMA(5)),
|
||||
dump_register(HCDMA(6)),
|
||||
dump_register(HCDMA(7)),
|
||||
dump_register(HCDMA(8)),
|
||||
dump_register(HCDMA(9)),
|
||||
dump_register(HCDMA(10)),
|
||||
dump_register(HCDMA(11)),
|
||||
dump_register(HCDMA(12)),
|
||||
dump_register(HCDMA(13)),
|
||||
dump_register(HCDMA(14)),
|
||||
dump_register(HCDMA(15)),
|
||||
dump_register(HCDMAB(0)),
|
||||
dump_register(HCDMAB(1)),
|
||||
dump_register(HCDMAB(2)),
|
||||
dump_register(HCDMAB(3)),
|
||||
dump_register(HCDMAB(4)),
|
||||
dump_register(HCDMAB(5)),
|
||||
dump_register(HCDMAB(6)),
|
||||
dump_register(HCDMAB(7)),
|
||||
dump_register(HCDMAB(8)),
|
||||
dump_register(HCDMAB(9)),
|
||||
dump_register(HCDMAB(10)),
|
||||
dump_register(HCDMAB(11)),
|
||||
dump_register(HCDMAB(12)),
|
||||
dump_register(HCDMAB(13)),
|
||||
dump_register(HCDMAB(14)),
|
||||
dump_register(HCDMAB(15)),
|
||||
};
|
||||
|
||||
int dwc2_debugfs_init(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
int ret;
|
||||
struct dentry *file;
|
||||
|
||||
hsotg->debug_root = debugfs_create_dir(dev_name(hsotg->dev), NULL);
|
||||
if (!hsotg->debug_root) {
|
||||
ret = -ENOMEM;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
/* Add gadget debugfs nodes */
|
||||
s3c_hsotg_create_debug(hsotg);
|
||||
|
||||
hsotg->regset = devm_kzalloc(hsotg->dev, sizeof(*hsotg->regset),
|
||||
GFP_KERNEL);
|
||||
if (!hsotg->regset) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
hsotg->regset->regs = dwc2_regs;
|
||||
hsotg->regset->nregs = ARRAY_SIZE(dwc2_regs);
|
||||
hsotg->regset->base = hsotg->regs;
|
||||
|
||||
file = debugfs_create_regset32("regdump", S_IRUGO, hsotg->debug_root,
|
||||
hsotg->regset);
|
||||
if (!file) {
|
||||
ret = -ENOMEM;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err1:
|
||||
debugfs_remove_recursive(hsotg->debug_root);
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
debugfs_remove_recursive(hsotg->debug_root);
|
||||
hsotg->debug_root = NULL;
|
||||
}
|
@ -20,7 +20,6 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/delay.h>
|
||||
@ -35,7 +34,6 @@
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/phy.h>
|
||||
#include <linux/platform_data/s3c-hsotg.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "hw.h"
|
||||
@ -792,6 +790,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
|
||||
ep->name, req, req->length, req->buf, req->no_interrupt,
|
||||
req->zero, req->short_not_ok);
|
||||
|
||||
/* Prevent new request submission when controller is suspended */
|
||||
if (hs->lx_state == DWC2_L2) {
|
||||
dev_dbg(hs->dev, "%s: don't submit request while suspended\n",
|
||||
__func__);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* initialise status of the request */
|
||||
INIT_LIST_HEAD(&hs_req->queue);
|
||||
req->actual = 0;
|
||||
@ -894,7 +899,7 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
|
||||
* @testmode: requested usb test mode
|
||||
* Enable usb Test Mode requested by the Host.
|
||||
*/
|
||||
static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
|
||||
int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
|
||||
{
|
||||
int dctl = readl(hsotg->regs + DCTL);
|
||||
|
||||
@ -2185,7 +2190,6 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg)
|
||||
|
||||
call_gadget(hsotg, disconnect);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_disconnect);
|
||||
|
||||
/**
|
||||
* s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler
|
||||
@ -2310,8 +2314,9 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
|
||||
writel(GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT |
|
||||
GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF |
|
||||
GINTSTS_CONIDSTSCHNG | GINTSTS_USBRST |
|
||||
GINTSTS_ENUMDONE | GINTSTS_OTGINT |
|
||||
GINTSTS_USBSUSP | GINTSTS_WKUPINT,
|
||||
GINTSTS_RESETDET | GINTSTS_ENUMDONE |
|
||||
GINTSTS_OTGINT | GINTSTS_USBSUSP |
|
||||
GINTSTS_WKUPINT,
|
||||
hsotg->regs + GINTMSK);
|
||||
|
||||
if (using_dma(hsotg))
|
||||
@ -2477,7 +2482,19 @@ irq_retry:
|
||||
}
|
||||
}
|
||||
|
||||
if (gintsts & GINTSTS_USBRST) {
|
||||
if (gintsts & GINTSTS_RESETDET) {
|
||||
dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__);
|
||||
|
||||
writel(GINTSTS_RESETDET, hsotg->regs + GINTSTS);
|
||||
|
||||
/* This event must be used only if controller is suspended */
|
||||
if (hsotg->lx_state == DWC2_L2) {
|
||||
dwc2_exit_hibernation(hsotg, true);
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
}
|
||||
}
|
||||
|
||||
if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) {
|
||||
|
||||
u32 usb_status = readl(hsotg->regs + GOTGCTL);
|
||||
|
||||
@ -2497,6 +2514,7 @@ irq_retry:
|
||||
kill_all_requests(hsotg, hsotg->eps_out[0],
|
||||
-ECONNRESET);
|
||||
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
s3c_hsotg_core_init_disconnected(hsotg, true);
|
||||
}
|
||||
}
|
||||
@ -2745,7 +2763,7 @@ error:
|
||||
* s3c_hsotg_ep_disable - disable given endpoint
|
||||
* @ep: The endpoint to disable.
|
||||
*/
|
||||
static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
|
||||
static int s3c_hsotg_ep_disable(struct usb_ep *ep)
|
||||
{
|
||||
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
|
||||
struct dwc2_hsotg *hsotg = hs_ep->parent;
|
||||
@ -2788,10 +2806,6 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c_hsotg_ep_disable(struct usb_ep *ep)
|
||||
{
|
||||
return s3c_hsotg_ep_disable_force(ep, false);
|
||||
}
|
||||
/**
|
||||
* on_list - check request is on the given endpoint
|
||||
* @ep: The endpoint to check.
|
||||
@ -3187,6 +3201,14 @@ static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
if (is_active) {
|
||||
/*
|
||||
* If controller is hibernated, it must exit from hibernation
|
||||
* before being initialized
|
||||
*/
|
||||
if (hsotg->lx_state == DWC2_L2) {
|
||||
dwc2_exit_hibernation(hsotg, false);
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
}
|
||||
/* Kill any ep0 requests as controller will be reinitialized */
|
||||
kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
|
||||
s3c_hsotg_core_init_disconnected(hsotg, false);
|
||||
@ -3391,404 +3413,6 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* testmode_write - debugfs: change usb test mode
|
||||
* @seq: The seq file to write to.
|
||||
* @v: Unused parameter.
|
||||
*
|
||||
* This debugfs entry modify the current usb test mode.
|
||||
*/
|
||||
static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t
|
||||
count, loff_t *ppos)
|
||||
{
|
||||
struct seq_file *s = file->private_data;
|
||||
struct dwc2_hsotg *hsotg = s->private;
|
||||
unsigned long flags;
|
||||
u32 testmode = 0;
|
||||
char buf[32];
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!strncmp(buf, "test_j", 6))
|
||||
testmode = TEST_J;
|
||||
else if (!strncmp(buf, "test_k", 6))
|
||||
testmode = TEST_K;
|
||||
else if (!strncmp(buf, "test_se0_nak", 12))
|
||||
testmode = TEST_SE0_NAK;
|
||||
else if (!strncmp(buf, "test_packet", 11))
|
||||
testmode = TEST_PACKET;
|
||||
else if (!strncmp(buf, "test_force_enable", 17))
|
||||
testmode = TEST_FORCE_EN;
|
||||
else
|
||||
testmode = 0;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
s3c_hsotg_set_test_mode(hsotg, testmode);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* testmode_show - debugfs: show usb test mode state
|
||||
* @seq: The seq file to write to.
|
||||
* @v: Unused parameter.
|
||||
*
|
||||
* This debugfs entry shows which usb test mode is currently enabled.
|
||||
*/
|
||||
static int testmode_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = s->private;
|
||||
unsigned long flags;
|
||||
int dctl;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
dctl = readl(hsotg->regs + DCTL);
|
||||
dctl &= DCTL_TSTCTL_MASK;
|
||||
dctl >>= DCTL_TSTCTL_SHIFT;
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
switch (dctl) {
|
||||
case 0:
|
||||
seq_puts(s, "no test\n");
|
||||
break;
|
||||
case TEST_J:
|
||||
seq_puts(s, "test_j\n");
|
||||
break;
|
||||
case TEST_K:
|
||||
seq_puts(s, "test_k\n");
|
||||
break;
|
||||
case TEST_SE0_NAK:
|
||||
seq_puts(s, "test_se0_nak\n");
|
||||
break;
|
||||
case TEST_PACKET:
|
||||
seq_puts(s, "test_packet\n");
|
||||
break;
|
||||
case TEST_FORCE_EN:
|
||||
seq_puts(s, "test_force_enable\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(s, "UNKNOWN %d\n", dctl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int testmode_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, testmode_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations testmode_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = testmode_open,
|
||||
.write = testmode_write,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* state_show - debugfs: show overall driver and device state.
|
||||
* @seq: The seq file to write to.
|
||||
* @v: Unused parameter.
|
||||
*
|
||||
* This debugfs entry shows the overall state of the hardware and
|
||||
* some general information about each of the endpoints available
|
||||
* to the system.
|
||||
*/
|
||||
static int state_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = seq->private;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
int idx;
|
||||
|
||||
seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n",
|
||||
readl(regs + DCFG),
|
||||
readl(regs + DCTL),
|
||||
readl(regs + DSTS));
|
||||
|
||||
seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n",
|
||||
readl(regs + DIEPMSK), readl(regs + DOEPMSK));
|
||||
|
||||
seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n",
|
||||
readl(regs + GINTMSK),
|
||||
readl(regs + GINTSTS));
|
||||
|
||||
seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n",
|
||||
readl(regs + DAINTMSK),
|
||||
readl(regs + DAINT));
|
||||
|
||||
seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n",
|
||||
readl(regs + GNPTXSTS),
|
||||
readl(regs + GRXSTSR));
|
||||
|
||||
seq_puts(seq, "\nEndpoint status:\n");
|
||||
|
||||
for (idx = 0; idx < hsotg->num_of_eps; idx++) {
|
||||
u32 in, out;
|
||||
|
||||
in = readl(regs + DIEPCTL(idx));
|
||||
out = readl(regs + DOEPCTL(idx));
|
||||
|
||||
seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x",
|
||||
idx, in, out);
|
||||
|
||||
in = readl(regs + DIEPTSIZ(idx));
|
||||
out = readl(regs + DOEPTSIZ(idx));
|
||||
|
||||
seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x",
|
||||
in, out);
|
||||
|
||||
seq_puts(seq, "\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int state_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, state_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations state_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = state_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* fifo_show - debugfs: show the fifo information
|
||||
* @seq: The seq_file to write data to.
|
||||
* @v: Unused parameter.
|
||||
*
|
||||
* Show the FIFO information for the overall fifo and all the
|
||||
* periodic transmission FIFOs.
|
||||
*/
|
||||
static int fifo_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = seq->private;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
u32 val;
|
||||
int idx;
|
||||
|
||||
seq_puts(seq, "Non-periodic FIFOs:\n");
|
||||
seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ));
|
||||
|
||||
val = readl(regs + GNPTXFSIZ);
|
||||
seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n",
|
||||
val >> FIFOSIZE_DEPTH_SHIFT,
|
||||
val & FIFOSIZE_DEPTH_MASK);
|
||||
|
||||
seq_puts(seq, "\nPeriodic TXFIFOs:\n");
|
||||
|
||||
for (idx = 1; idx < hsotg->num_of_eps; idx++) {
|
||||
val = readl(regs + DPTXFSIZN(idx));
|
||||
|
||||
seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
|
||||
val >> FIFOSIZE_DEPTH_SHIFT,
|
||||
val & FIFOSIZE_STARTADDR_MASK);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fifo_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, fifo_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations fifo_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = fifo_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
|
||||
static const char *decode_direction(int is_in)
|
||||
{
|
||||
return is_in ? "in" : "out";
|
||||
}
|
||||
|
||||
/**
|
||||
* ep_show - debugfs: show the state of an endpoint.
|
||||
* @seq: The seq_file to write data to.
|
||||
* @v: Unused parameter.
|
||||
*
|
||||
* This debugfs entry shows the state of the given endpoint (one is
|
||||
* registered for each available).
|
||||
*/
|
||||
static int ep_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct s3c_hsotg_ep *ep = seq->private;
|
||||
struct dwc2_hsotg *hsotg = ep->parent;
|
||||
struct s3c_hsotg_req *req;
|
||||
void __iomem *regs = hsotg->regs;
|
||||
int index = ep->index;
|
||||
int show_limit = 15;
|
||||
unsigned long flags;
|
||||
|
||||
seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n",
|
||||
ep->index, ep->ep.name, decode_direction(ep->dir_in));
|
||||
|
||||
/* first show the register state */
|
||||
|
||||
seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n",
|
||||
readl(regs + DIEPCTL(index)),
|
||||
readl(regs + DOEPCTL(index)));
|
||||
|
||||
seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n",
|
||||
readl(regs + DIEPDMA(index)),
|
||||
readl(regs + DOEPDMA(index)));
|
||||
|
||||
seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n",
|
||||
readl(regs + DIEPINT(index)),
|
||||
readl(regs + DOEPINT(index)));
|
||||
|
||||
seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n",
|
||||
readl(regs + DIEPTSIZ(index)),
|
||||
readl(regs + DOEPTSIZ(index)));
|
||||
|
||||
seq_puts(seq, "\n");
|
||||
seq_printf(seq, "mps %d\n", ep->ep.maxpacket);
|
||||
seq_printf(seq, "total_data=%ld\n", ep->total_data);
|
||||
|
||||
seq_printf(seq, "request list (%p,%p):\n",
|
||||
ep->queue.next, ep->queue.prev);
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
list_for_each_entry(req, &ep->queue, queue) {
|
||||
if (--show_limit < 0) {
|
||||
seq_puts(seq, "not showing more requests...\n");
|
||||
break;
|
||||
}
|
||||
|
||||
seq_printf(seq, "%c req %p: %d bytes @%p, ",
|
||||
req == ep->req ? '*' : ' ',
|
||||
req, req->req.length, req->req.buf);
|
||||
seq_printf(seq, "%d done, res %d\n",
|
||||
req->req.actual, req->req.status);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, ep_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations ep_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = ep_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* s3c_hsotg_create_debug - create debugfs directory and files
|
||||
* @hsotg: The driver state
|
||||
*
|
||||
* Create the debugfs files to allow the user to get information
|
||||
* about the state of the system. The directory name is created
|
||||
* with the same name as the device itself, in case we end up
|
||||
* with multiple blocks in future systems.
|
||||
*/
|
||||
static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
struct dentry *root;
|
||||
unsigned epidx;
|
||||
|
||||
root = debugfs_create_dir(dev_name(hsotg->dev), NULL);
|
||||
hsotg->debug_root = root;
|
||||
if (IS_ERR(root)) {
|
||||
dev_err(hsotg->dev, "cannot create debug root\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* create general state file */
|
||||
|
||||
hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root,
|
||||
hsotg, &state_fops);
|
||||
|
||||
if (IS_ERR(hsotg->debug_file))
|
||||
dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
|
||||
|
||||
hsotg->debug_testmode = debugfs_create_file("testmode",
|
||||
S_IRUGO | S_IWUSR, root,
|
||||
hsotg, &testmode_fops);
|
||||
|
||||
if (IS_ERR(hsotg->debug_testmode))
|
||||
dev_err(hsotg->dev, "%s: failed to create testmode\n",
|
||||
__func__);
|
||||
|
||||
hsotg->debug_fifo = debugfs_create_file("fifo", S_IRUGO, root,
|
||||
hsotg, &fifo_fops);
|
||||
|
||||
if (IS_ERR(hsotg->debug_fifo))
|
||||
dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__);
|
||||
|
||||
/* Create one file for each out endpoint */
|
||||
for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
|
||||
struct s3c_hsotg_ep *ep;
|
||||
|
||||
ep = hsotg->eps_out[epidx];
|
||||
if (ep) {
|
||||
ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
|
||||
root, ep, &ep_fops);
|
||||
|
||||
if (IS_ERR(ep->debugfs))
|
||||
dev_err(hsotg->dev, "failed to create %s debug file\n",
|
||||
ep->name);
|
||||
}
|
||||
}
|
||||
/* Create one file for each in endpoint. EP0 is handled with out eps */
|
||||
for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) {
|
||||
struct s3c_hsotg_ep *ep;
|
||||
|
||||
ep = hsotg->eps_in[epidx];
|
||||
if (ep) {
|
||||
ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
|
||||
root, ep, &ep_fops);
|
||||
|
||||
if (IS_ERR(ep->debugfs))
|
||||
dev_err(hsotg->dev, "failed to create %s debug file\n",
|
||||
ep->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* s3c_hsotg_delete_debug - cleanup debugfs entries
|
||||
* @hsotg: The driver state
|
||||
*
|
||||
* Cleanup (remove) the debugfs files for use on module exit.
|
||||
*/
|
||||
static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned epidx;
|
||||
|
||||
for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
|
||||
if (hsotg->eps_in[epidx])
|
||||
debugfs_remove(hsotg->eps_in[epidx]->debugfs);
|
||||
if (hsotg->eps_out[epidx])
|
||||
debugfs_remove(hsotg->eps_out[epidx]->debugfs);
|
||||
}
|
||||
|
||||
debugfs_remove(hsotg->debug_file);
|
||||
debugfs_remove(hsotg->debug_testmode);
|
||||
debugfs_remove(hsotg->debug_fifo);
|
||||
debugfs_remove(hsotg->debug_root);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
@ -3896,6 +3520,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
hsotg->gadget.max_speed = USB_SPEED_HIGH;
|
||||
hsotg->gadget.ops = &s3c_hsotg_gadget_ops;
|
||||
hsotg->gadget.name = dev_name(dev);
|
||||
if (hsotg->dr_mode == USB_DR_MODE_OTG)
|
||||
hsotg->gadget.is_otg = 1;
|
||||
|
||||
/* reset the system */
|
||||
|
||||
@ -4028,8 +3654,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
if (ret)
|
||||
goto err_supplies;
|
||||
|
||||
s3c_hsotg_create_debug(hsotg);
|
||||
|
||||
s3c_hsotg_dump(hsotg);
|
||||
|
||||
return 0;
|
||||
@ -4041,7 +3665,6 @@ err_clk:
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc2_gadget_init);
|
||||
|
||||
/**
|
||||
* s3c_hsotg_remove - remove function for hsotg driver
|
||||
@ -4050,18 +3673,19 @@ EXPORT_SYMBOL_GPL(dwc2_gadget_init);
|
||||
int s3c_hsotg_remove(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
usb_del_gadget_udc(&hsotg->gadget);
|
||||
s3c_hsotg_delete_debug(hsotg);
|
||||
clk_disable_unprepare(hsotg->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_remove);
|
||||
|
||||
int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (hsotg->lx_state != DWC2_L0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
|
||||
if (hsotg->driver) {
|
||||
@ -4095,13 +3719,15 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg)
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_suspend);
|
||||
|
||||
int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (hsotg->lx_state == DWC2_L2)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&hsotg->init_mutex);
|
||||
|
||||
if (hsotg->driver) {
|
||||
@ -4124,4 +3750,3 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(s3c_hsotg_resume);
|
||||
|
@ -357,12 +357,12 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg)
|
||||
writel(0, hsotg->regs + HPRT0);
|
||||
}
|
||||
|
||||
/* Caller must hold driver lock */
|
||||
static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_hcd_urb *urb, void **ep_handle,
|
||||
gfp_t mem_flags)
|
||||
{
|
||||
struct dwc2_qtd *qtd;
|
||||
unsigned long flags;
|
||||
u32 intr_mask;
|
||||
int retval;
|
||||
int dev_speed;
|
||||
@ -413,11 +413,9 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg,
|
||||
*/
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
tr_type = dwc2_hcd_select_transactions(hsotg);
|
||||
if (tr_type != DWC2_TRANSACTION_NONE)
|
||||
dwc2_hcd_queue_transactions(hsotg, tr_type);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -721,9 +719,7 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
||||
/* 3072 = 3 max-size Isoc packets */
|
||||
buf_size = 3072;
|
||||
|
||||
qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size,
|
||||
&qh->dw_align_buf_dma,
|
||||
GFP_ATOMIC);
|
||||
qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA);
|
||||
if (!qh->dw_align_buf)
|
||||
return -ENOMEM;
|
||||
qh->dw_align_buf_size = buf_size;
|
||||
@ -748,6 +744,15 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
||||
}
|
||||
}
|
||||
|
||||
qh->dw_align_buf_dma = dma_map_single(hsotg->dev,
|
||||
qh->dw_align_buf, qh->dw_align_buf_size,
|
||||
chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) {
|
||||
dev_err(hsotg->dev, "can't map align_buf\n");
|
||||
chan->align_buf = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan->align_buf = qh->dw_align_buf_dma;
|
||||
return 0;
|
||||
}
|
||||
@ -1774,6 +1779,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
|
||||
/* Not supported */
|
||||
break;
|
||||
|
||||
case USB_PORT_FEAT_TEST:
|
||||
hprt0 = dwc2_read_hprt0(hsotg);
|
||||
dev_dbg(hsotg->dev,
|
||||
"SetPortFeature - USB_PORT_FEAT_TEST\n");
|
||||
hprt0 &= ~HPRT0_TSTCTL_MASK;
|
||||
hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT;
|
||||
writel(hprt0, hsotg->regs + HPRT0);
|
||||
break;
|
||||
|
||||
default:
|
||||
retval = -EINVAL;
|
||||
dev_err(hsotg->dev,
|
||||
@ -2313,6 +2327,22 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
|
||||
usleep_range(1000, 3000);
|
||||
}
|
||||
|
||||
static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
|
||||
|
||||
hsotg->lx_state = DWC2_L2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dwc2_hcd_resume(struct usb_hcd *hcd)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
|
||||
|
||||
hsotg->lx_state = DWC2_L0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns the current frame number */
|
||||
static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd)
|
||||
{
|
||||
@ -2468,7 +2498,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
|
||||
"%s: unaligned transfer with no transfer_buffer",
|
||||
__func__);
|
||||
retval = -EINVAL;
|
||||
goto fail1;
|
||||
goto fail0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2496,7 +2526,6 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
retval = usb_hcd_link_urb_to_ep(hcd, urb);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
if (retval)
|
||||
goto fail1;
|
||||
|
||||
@ -2505,22 +2534,22 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
|
||||
goto fail2;
|
||||
|
||||
if (alloc_bandwidth) {
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
dwc2_allocate_bus_bandwidth(hcd,
|
||||
dwc2_hcd_get_ep_bandwidth(hsotg, ep),
|
||||
urb);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
dwc2_urb->priv = NULL;
|
||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
fail1:
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
urb->hcpriv = NULL;
|
||||
fail0:
|
||||
kfree(dwc2_urb);
|
||||
|
||||
return retval;
|
||||
@ -2683,6 +2712,9 @@ static struct hc_driver dwc2_hc_driver = {
|
||||
.hub_status_data = _dwc2_hcd_hub_status_data,
|
||||
.hub_control = _dwc2_hcd_hub_control,
|
||||
.clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete,
|
||||
|
||||
.bus_suspend = _dwc2_hcd_suspend,
|
||||
.bus_resume = _dwc2_hcd_resume,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -2748,8 +2780,6 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
|
||||
destroy_workqueue(hsotg->wq_otg);
|
||||
}
|
||||
|
||||
kfree(hsotg->core_params);
|
||||
hsotg->core_params = NULL;
|
||||
del_timer(&hsotg->wkp_timer);
|
||||
}
|
||||
|
||||
@ -2761,30 +2791,13 @@ static void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
|
||||
dwc2_hcd_free(hsotg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets all parameters to the given value.
|
||||
*
|
||||
* Assumes that the dwc2_core_params struct contains only integers.
|
||||
*/
|
||||
void dwc2_set_all_params(struct dwc2_core_params *params, int value)
|
||||
{
|
||||
int *p = (int *)params;
|
||||
size_t size = sizeof(*params) / sizeof(*p);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
p[i] = value;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc2_set_all_params);
|
||||
|
||||
/*
|
||||
* Initializes the HCD. This function allocates memory for and initializes the
|
||||
* static parts of the usb_hcd and dwc2_hsotg structures. It also registers the
|
||||
* USB bus with the core and calls the hc_driver->start() function. It returns
|
||||
* a negative error on failure.
|
||||
*/
|
||||
int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
|
||||
const struct dwc2_core_params *params)
|
||||
int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct dwc2_host_chan *channel;
|
||||
@ -2797,12 +2810,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
|
||||
|
||||
dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n");
|
||||
|
||||
/* Detect config values from hardware */
|
||||
retval = dwc2_get_hwparams(hsotg);
|
||||
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = -ENOMEM;
|
||||
|
||||
hcfg = readl(hsotg->regs + HCFG);
|
||||
@ -2821,15 +2828,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
|
||||
hsotg->last_frame_num = HFNUM_MAX_FRNUM;
|
||||
#endif
|
||||
|
||||
hsotg->core_params = kzalloc(sizeof(*hsotg->core_params), GFP_KERNEL);
|
||||
if (!hsotg->core_params)
|
||||
goto error1;
|
||||
|
||||
dwc2_set_all_params(hsotg->core_params, -1);
|
||||
|
||||
/* Validate parameter values */
|
||||
dwc2_set_parameters(hsotg, params);
|
||||
|
||||
/* Check if the bus driver or platform code has setup a dma_mask */
|
||||
if (hsotg->core_params->dma_enable > 0 &&
|
||||
hsotg->dev->dma_mask == NULL) {
|
||||
@ -2947,6 +2945,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
|
||||
/* Don't support SG list at this point */
|
||||
hcd->self.sg_tablesize = 0;
|
||||
|
||||
if (!IS_ERR_OR_NULL(hsotg->uphy))
|
||||
otg_set_host(hsotg->uphy->otg, &hcd->self);
|
||||
|
||||
/*
|
||||
* Finish generic HCD initialization and start the HCD. This function
|
||||
* allocates the DMA buffer pool, registers the USB bus, requests the
|
||||
@ -2979,7 +2980,6 @@ error1:
|
||||
dev_err(hsotg->dev, "%s() FAILED, returning %d\n", __func__, retval);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc2_hcd_init);
|
||||
|
||||
/*
|
||||
* Removes the HCD.
|
||||
@ -3000,6 +3000,9 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(hsotg->uphy))
|
||||
otg_set_host(hsotg->uphy->otg, NULL);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
hsotg->priv = NULL;
|
||||
dwc2_hcd_release(hsotg);
|
||||
@ -3010,4 +3013,3 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg)
|
||||
kfree(hsotg->frame_num_array);
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dwc2_hcd_remove);
|
||||
|
@ -451,13 +451,8 @@ static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe)
|
||||
return !dwc2_hcd_is_pipe_in(pipe);
|
||||
}
|
||||
|
||||
extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
|
||||
const struct dwc2_core_params *params);
|
||||
extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq);
|
||||
extern void dwc2_hcd_remove(struct dwc2_hsotg *hsotg);
|
||||
extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg,
|
||||
const struct dwc2_core_params *params);
|
||||
extern void dwc2_set_all_params(struct dwc2_core_params *params, int value);
|
||||
extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg);
|
||||
|
||||
/* Transaction Execution Functions */
|
||||
extern enum dwc2_transaction_type dwc2_hcd_select_transactions(
|
||||
|
@ -350,6 +350,9 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
|
||||
dev_vdbg(hsotg->dev,
|
||||
"--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
|
||||
hprt0);
|
||||
if (hsotg->lx_state != DWC2_L0)
|
||||
usb_hcd_resume_root_hub(hsotg->priv);
|
||||
|
||||
hsotg->flags.b.port_connect_status_change = 1;
|
||||
hsotg->flags.b.port_connect_status = 1;
|
||||
hprt0_modify |= HPRT0_CONNDET;
|
||||
@ -463,10 +466,15 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
|
||||
}
|
||||
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && xfer_length && chan->ep_is_in) {
|
||||
if (chan->align_buf && xfer_length) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
|
||||
xfer_length);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + urb->actual_length,
|
||||
chan->qh->dw_align_buf, xfer_length);
|
||||
}
|
||||
|
||||
dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
|
||||
@ -552,13 +560,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
|
||||
chan, chnum, qtd, halt_status, NULL);
|
||||
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && frame_desc->actual_length &&
|
||||
chan->ep_is_in) {
|
||||
if (chan->align_buf && frame_desc->actual_length) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
|
||||
__func__);
|
||||
memcpy(urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset, chan->qh->dw_align_buf,
|
||||
frame_desc->actual_length);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset,
|
||||
chan->qh->dw_align_buf,
|
||||
frame_desc->actual_length);
|
||||
}
|
||||
break;
|
||||
case DWC2_HC_XFER_FRAME_OVERRUN:
|
||||
@ -581,13 +594,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state(
|
||||
chan, chnum, qtd, halt_status, NULL);
|
||||
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && frame_desc->actual_length &&
|
||||
chan->ep_is_in) {
|
||||
if (chan->align_buf && frame_desc->actual_length) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n",
|
||||
__func__);
|
||||
memcpy(urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset, chan->qh->dw_align_buf,
|
||||
frame_desc->actual_length);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset,
|
||||
chan->qh->dw_align_buf,
|
||||
frame_desc->actual_length);
|
||||
}
|
||||
|
||||
/* Skip whole frame */
|
||||
@ -923,6 +941,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
|
||||
|
||||
if (chan->align_buf) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size, DMA_FROM_DEVICE);
|
||||
memcpy(qtd->urb->buf + frame_desc->offset +
|
||||
qtd->isoc_split_offset, chan->qh->dw_align_buf, len);
|
||||
}
|
||||
@ -1152,8 +1172,14 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
|
||||
/* Non DWORD-aligned buffer case handling */
|
||||
if (chan->align_buf && xfer_length && chan->ep_is_in) {
|
||||
dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__);
|
||||
memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf,
|
||||
xfer_length);
|
||||
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
||||
chan->qh->dw_align_buf_size,
|
||||
chan->ep_is_in ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
if (chan->ep_is_in)
|
||||
memcpy(urb->buf + urb->actual_length,
|
||||
chan->qh->dw_align_buf,
|
||||
xfer_length);
|
||||
}
|
||||
|
||||
urb->actual_length += xfer_length;
|
||||
@ -1182,6 +1208,16 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
|
||||
struct dwc2_host_chan *chan, int chnum,
|
||||
struct dwc2_qtd *qtd)
|
||||
{
|
||||
if (!qtd) {
|
||||
dev_dbg(hsotg->dev, "%s: qtd is NULL\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!qtd->urb) {
|
||||
dev_dbg(hsotg->dev, "%s: qtd->urb is NULL\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dbg_hc(chan))
|
||||
dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NAK Received--\n",
|
||||
chnum);
|
||||
|
@ -229,11 +229,13 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg,
|
||||
*/
|
||||
void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh)
|
||||
{
|
||||
if (hsotg->core_params->dma_desc_enable > 0)
|
||||
if (hsotg->core_params->dma_desc_enable > 0) {
|
||||
dwc2_hcd_qh_free_ddma(hsotg, qh);
|
||||
else if (qh->dw_align_buf)
|
||||
dma_free_coherent(hsotg->dev, qh->dw_align_buf_size,
|
||||
qh->dw_align_buf, qh->dw_align_buf_dma);
|
||||
} else {
|
||||
/* kfree(NULL) is safe */
|
||||
kfree(qh->dw_align_buf);
|
||||
qh->dw_align_buf_dma = (dma_addr_t)0;
|
||||
}
|
||||
kfree(qh);
|
||||
}
|
||||
|
||||
@ -761,6 +763,7 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb)
|
||||
|
||||
/**
|
||||
* dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH
|
||||
* Caller must hold driver lock.
|
||||
*
|
||||
* @hsotg: The DWC HCD structure
|
||||
* @qtd: The QTD to add
|
||||
@ -777,7 +780,6 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
|
||||
struct dwc2_qh **qh, gfp_t mem_flags)
|
||||
{
|
||||
struct dwc2_hcd_urb *urb = qtd->urb;
|
||||
unsigned long flags;
|
||||
int allocated = 0;
|
||||
int retval;
|
||||
|
||||
@ -792,15 +794,12 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd,
|
||||
allocated = 1;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&hsotg->lock, flags);
|
||||
|
||||
retval = dwc2_hcd_qh_add(hsotg, *qh);
|
||||
if (retval)
|
||||
goto fail;
|
||||
|
||||
qtd->qh = *qh;
|
||||
list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list);
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -817,10 +816,7 @@ fail:
|
||||
qtd_list_entry)
|
||||
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp);
|
||||
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
dwc2_hcd_qh_free(hsotg, qh_tmp);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&hsotg->lock, flags);
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
@ -47,6 +47,7 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "hcd.h"
|
||||
#include "debug.h"
|
||||
|
||||
static const char dwc2_driver_name[] = "dwc2";
|
||||
|
||||
@ -76,6 +77,8 @@ static const struct dwc2_core_params params_bcm2835 = {
|
||||
.reload_ctl = 0,
|
||||
.ahbcfg = 0x10,
|
||||
.uframe_sched = 0,
|
||||
.external_id_pin_ctl = -1,
|
||||
.hibernation = -1,
|
||||
};
|
||||
|
||||
static const struct dwc2_core_params params_rk3066 = {
|
||||
@ -104,6 +107,8 @@ static const struct dwc2_core_params params_rk3066 = {
|
||||
.reload_ctl = -1,
|
||||
.ahbcfg = 0x7, /* INCR16 */
|
||||
.uframe_sched = -1,
|
||||
.external_id_pin_ctl = -1,
|
||||
.hibernation = -1,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -121,6 +126,7 @@ static int dwc2_driver_remove(struct platform_device *dev)
|
||||
{
|
||||
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
|
||||
|
||||
dwc2_debugfs_exit(hsotg);
|
||||
if (hsotg->hcd_enabled)
|
||||
dwc2_hcd_remove(hsotg);
|
||||
if (hsotg->gadget_enabled)
|
||||
@ -237,6 +243,21 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
spin_lock_init(&hsotg->lock);
|
||||
mutex_init(&hsotg->init_mutex);
|
||||
|
||||
/* Detect config values from hardware */
|
||||
retval = dwc2_get_hwparams(hsotg);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
hsotg->core_params = devm_kzalloc(&dev->dev,
|
||||
sizeof(*hsotg->core_params), GFP_KERNEL);
|
||||
if (!hsotg->core_params)
|
||||
return -ENOMEM;
|
||||
|
||||
dwc2_set_all_params(hsotg->core_params, -1);
|
||||
|
||||
/* Validate parameter values */
|
||||
dwc2_set_parameters(hsotg, params);
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_HOST) {
|
||||
retval = dwc2_gadget_init(hsotg, irq);
|
||||
if (retval)
|
||||
@ -245,7 +266,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
}
|
||||
|
||||
if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
|
||||
retval = dwc2_hcd_init(hsotg, irq, params);
|
||||
retval = dwc2_hcd_init(hsotg, irq);
|
||||
if (retval) {
|
||||
if (hsotg->gadget_enabled)
|
||||
s3c_hsotg_remove(hsotg);
|
||||
@ -256,6 +277,8 @@ static int dwc2_driver_probe(struct platform_device *dev)
|
||||
|
||||
platform_set_drvdata(dev, hsotg);
|
||||
|
||||
dwc2_debugfs_init(hsotg);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,13 @@ config USB_DWC3
|
||||
|
||||
if USB_DWC3
|
||||
|
||||
config USB_DWC3_ULPI
|
||||
bool "Register ULPI PHY Interface"
|
||||
depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_DWC3
|
||||
help
|
||||
Select this if you have ULPI type PHY attached to your DWC3
|
||||
controller.
|
||||
|
||||
choice
|
||||
bool "DWC3 Mode Selection"
|
||||
default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET)
|
||||
|
@ -15,6 +15,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
|
||||
dwc3-y += gadget.o ep0.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_USB_DWC3_ULPI),)
|
||||
dwc3-y += ulpi.o
|
||||
endif
|
||||
|
||||
ifneq ($(CONFIG_DEBUG_FS),)
|
||||
dwc3-y += debugfs.o
|
||||
endif
|
||||
|
@ -116,6 +116,33 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_soft_reset - Issue soft reset
|
||||
* @dwc: Pointer to our controller context structure
|
||||
*/
|
||||
static int dwc3_soft_reset(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned long timeout;
|
||||
u32 reg;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(500);
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
|
||||
do {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (!(reg & DWC3_DCTL_CSFTRST))
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(dwc->dev, "Reset Timed Out\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
cpu_relax();
|
||||
} while (true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_free_one_event_buffer - Frees one event buffer
|
||||
* @dwc: Pointer to our controller context structure
|
||||
@ -367,10 +394,15 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
|
||||
/**
|
||||
* dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
|
||||
* @dwc: Pointer to our controller context structure
|
||||
*
|
||||
* Returns 0 on success. The USB PHY interfaces are configured but not
|
||||
* initialized. The PHY interfaces and the PHYs get initialized together with
|
||||
* the core in dwc3_core_init.
|
||||
*/
|
||||
static void dwc3_phy_setup(struct dwc3 *dwc)
|
||||
static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
|
||||
|
||||
@ -409,10 +441,41 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
|
||||
|
||||
mdelay(100);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
|
||||
|
||||
/* Select the HS PHY interface */
|
||||
switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) {
|
||||
case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI:
|
||||
if (!strncmp(dwc->hsphy_interface, "utmi", 4)) {
|
||||
reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI;
|
||||
break;
|
||||
} else if (!strncmp(dwc->hsphy_interface, "ulpi", 4)) {
|
||||
reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
} else {
|
||||
dev_warn(dwc->dev, "HSPHY Interface not defined\n");
|
||||
|
||||
/* Relying on default value. */
|
||||
if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
|
||||
/* Making sure the interface and PHY are operational */
|
||||
ret = dwc3_soft_reset(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
udelay(1);
|
||||
|
||||
ret = dwc3_ulpi_init(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
|
||||
* '0' during coreConsultant configuration. So default value will
|
||||
@ -427,7 +490,7 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
|
||||
|
||||
mdelay(100);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -438,7 +501,6 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
|
||||
*/
|
||||
static int dwc3_core_init(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned long timeout;
|
||||
u32 hwparams4 = dwc->hwparams.hwparams4;
|
||||
u32 reg;
|
||||
int ret;
|
||||
@ -466,21 +528,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
}
|
||||
|
||||
/* issue device SoftReset too */
|
||||
timeout = jiffies + msecs_to_jiffies(500);
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
|
||||
do {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (!(reg & DWC3_DCTL_CSFTRST))
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(dwc->dev, "Reset Timed Out\n");
|
||||
ret = -ETIMEDOUT;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
cpu_relax();
|
||||
} while (true);
|
||||
ret = dwc3_soft_reset(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
ret = dwc3_core_soft_reset(dwc);
|
||||
if (ret)
|
||||
@ -555,8 +605,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
|
||||
|
||||
dwc3_phy_setup(dwc);
|
||||
|
||||
ret = dwc3_alloc_scratch_buffers(dwc);
|
||||
if (ret)
|
||||
goto err1;
|
||||
@ -836,6 +884,8 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
"snps,tx_de_emphasis_quirk");
|
||||
of_property_read_u8(node, "snps,tx_de_emphasis",
|
||||
&tx_de_emphasis);
|
||||
of_property_read_string(node, "snps,hsphy_interface",
|
||||
&dwc->hsphy_interface);
|
||||
} else if (pdata) {
|
||||
dwc->maximum_speed = pdata->maximum_speed;
|
||||
dwc->has_lpm_erratum = pdata->has_lpm_erratum;
|
||||
@ -863,6 +913,8 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
|
||||
if (pdata->tx_de_emphasis)
|
||||
tx_de_emphasis = pdata->tx_de_emphasis;
|
||||
|
||||
dwc->hsphy_interface = pdata->hsphy_interface;
|
||||
}
|
||||
|
||||
/* default to superspeed if no maximum_speed passed */
|
||||
@ -875,12 +927,18 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
dwc->hird_threshold = hird_threshold
|
||||
| (dwc->is_utmi_l1_suspend << 4);
|
||||
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
ret = dwc3_phy_setup(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
ret = dwc3_core_get_phy(dwc);
|
||||
if (ret)
|
||||
goto err0;
|
||||
|
||||
spin_lock_init(&dwc->lock);
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
|
||||
if (!dev->dma_mask) {
|
||||
dev->dma_mask = dev->parent->dma_mask;
|
||||
@ -892,8 +950,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_forbid(dev);
|
||||
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to allocate event buffers\n");
|
||||
@ -964,6 +1020,7 @@ err2:
|
||||
|
||||
err1:
|
||||
dwc3_free_event_buffers(dwc);
|
||||
dwc3_ulpi_exit(dwc);
|
||||
|
||||
err0:
|
||||
/*
|
||||
@ -999,6 +1056,7 @@ static int dwc3_remove(struct platform_device *pdev)
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
dwc3_core_exit(dwc);
|
||||
dwc3_ulpi_exit(dwc);
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/ulpi/interface.h>
|
||||
|
||||
#include <linux/phy/phy.h>
|
||||
|
||||
@ -173,6 +174,15 @@
|
||||
/* Global USB2 PHY Configuration Register */
|
||||
#define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31)
|
||||
#define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6)
|
||||
#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4)
|
||||
|
||||
/* Global USB2 PHY Vendor Control Register */
|
||||
#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25)
|
||||
#define DWC3_GUSB2PHYACC_BUSY (1 << 23)
|
||||
#define DWC3_GUSB2PHYACC_WRITE (1 << 22)
|
||||
#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16)
|
||||
#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8)
|
||||
#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff)
|
||||
|
||||
/* Global USB3 PIPE Control Register */
|
||||
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
|
||||
@ -652,6 +662,7 @@ struct dwc3_scratchpad_array {
|
||||
* @usb3_phy: pointer to USB3 PHY
|
||||
* @usb2_generic_phy: pointer to USB2 PHY
|
||||
* @usb3_generic_phy: pointer to USB3 PHY
|
||||
* @ulpi: pointer to ulpi interface
|
||||
* @dcfg: saved contents of DCFG register
|
||||
* @gctl: saved contents of GCTL register
|
||||
* @isoch_delay: wValue from Set Isochronous Delay request;
|
||||
@ -673,6 +684,7 @@ struct dwc3_scratchpad_array {
|
||||
* @test_mode_nr: test feature selector
|
||||
* @lpm_nyet_threshold: LPM NYET response threshold
|
||||
* @hird_threshold: HIRD threshold
|
||||
* @hsphy_interface: "utmi" or "ulpi"
|
||||
* @delayed_status: true when gadget driver asks for delayed status
|
||||
* @ep0_bounced: true when we used bounce buffer
|
||||
* @ep0_expect_in: true when we expect a DATA IN transfer
|
||||
@ -739,6 +751,8 @@ struct dwc3 {
|
||||
struct phy *usb2_generic_phy;
|
||||
struct phy *usb3_generic_phy;
|
||||
|
||||
struct ulpi *ulpi;
|
||||
|
||||
void __iomem *regs;
|
||||
size_t regs_size;
|
||||
|
||||
@ -800,6 +814,8 @@ struct dwc3 {
|
||||
u8 lpm_nyet_threshold;
|
||||
u8 hird_threshold;
|
||||
|
||||
const char *hsphy_interface;
|
||||
|
||||
unsigned delayed_status:1;
|
||||
unsigned ep0_bounced:1;
|
||||
unsigned ep0_expect_in:1;
|
||||
@ -1035,4 +1051,14 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc)
|
||||
}
|
||||
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_ULPI)
|
||||
int dwc3_ulpi_init(struct dwc3 *dwc);
|
||||
void dwc3_ulpi_exit(struct dwc3 *dwc);
|
||||
#else
|
||||
static inline int dwc3_ulpi_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
static inline void dwc3_ulpi_exit(struct dwc3 *dwc)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
#endif /* __DRIVERS_USB_DWC3_CORE_H */
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "platform_data.h"
|
||||
|
||||
@ -31,6 +33,15 @@
|
||||
#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30
|
||||
#define PCI_DEVICE_ID_INTEL_SPTH 0xa130
|
||||
|
||||
static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
|
||||
static const struct acpi_gpio_params cs_gpios = { 1, 0, false };
|
||||
|
||||
static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = {
|
||||
{ "reset-gpios", &reset_gpios, 1 },
|
||||
{ "cs-gpios", &cs_gpios, 1 },
|
||||
{ },
|
||||
};
|
||||
|
||||
static int dwc3_pci_quirks(struct pci_dev *pdev)
|
||||
{
|
||||
if (pdev->vendor == PCI_VENDOR_ID_AMD &&
|
||||
@ -65,6 +76,30 @@ static int dwc3_pci_quirks(struct pci_dev *pdev)
|
||||
sizeof(pdata));
|
||||
}
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
|
||||
acpi_dwc3_byt_gpios);
|
||||
|
||||
/* These GPIOs will turn on the USB2 PHY */
|
||||
gpio = gpiod_get(&pdev->dev, "cs");
|
||||
if (!IS_ERR(gpio)) {
|
||||
gpiod_direction_output(gpio, 0);
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
}
|
||||
|
||||
gpio = gpiod_get(&pdev->dev, "reset");
|
||||
if (!IS_ERR(gpio)) {
|
||||
gpiod_direction_output(gpio, 0);
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -128,6 +163,7 @@ err:
|
||||
|
||||
static void dwc3_pci_remove(struct pci_dev *pci)
|
||||
{
|
||||
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pci->dev));
|
||||
platform_device_unregister(pci_get_drvdata(pci));
|
||||
}
|
||||
|
||||
|
@ -291,6 +291,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"Command Complete --> %d",
|
||||
DWC3_DGCMD_STATUS(reg));
|
||||
if (DWC3_DGCMD_STATUS(reg))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -328,6 +330,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
dwc3_trace(trace_dwc3_gadget,
|
||||
"Command Complete --> %d",
|
||||
DWC3_DEPCMD_STATUS(reg));
|
||||
if (DWC3_DEPCMD_STATUS(reg))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1902,12 +1906,16 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
|
||||
{
|
||||
unsigned status = 0;
|
||||
int clean_busy;
|
||||
u32 is_xfer_complete;
|
||||
|
||||
is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE);
|
||||
|
||||
if (event->status & DEPEVT_STATUS_BUSERR)
|
||||
status = -ECONNRESET;
|
||||
|
||||
clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status);
|
||||
if (clean_busy)
|
||||
if (clean_busy && (is_xfer_complete ||
|
||||
usb_endpoint_xfer_isoc(dep->endpoint.desc)))
|
||||
dep->flags &= ~DWC3_EP_BUSY;
|
||||
|
||||
/*
|
||||
|
@ -45,4 +45,6 @@ struct dwc3_platform_data {
|
||||
|
||||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
|
||||
const char *hsphy_interface;
|
||||
};
|
||||
|
91
drivers/usb/dwc3/ulpi.c
Normal file
91
drivers/usb/dwc3/ulpi.c
Normal file
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* ulpi.c - DesignWare USB3 Controller's ULPI PHY interface
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corporation
|
||||
*
|
||||
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/ulpi/regs.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "io.h"
|
||||
|
||||
#define DWC3_ULPI_ADDR(a) \
|
||||
((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \
|
||||
DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \
|
||||
DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a))
|
||||
|
||||
static int dwc3_ulpi_busyloop(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned count = 1000;
|
||||
u32 reg;
|
||||
|
||||
while (count--) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
|
||||
if (!(reg & DWC3_GUSB2PHYACC_BUSY))
|
||||
return 0;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(ops->dev);
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
|
||||
|
||||
ret = dwc3_ulpi_busyloop(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
|
||||
|
||||
return DWC3_GUSB2PHYACC_DATA(reg);
|
||||
}
|
||||
|
||||
static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(ops->dev);
|
||||
u32 reg;
|
||||
|
||||
reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
|
||||
reg |= DWC3_GUSB2PHYACC_WRITE | val;
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);
|
||||
|
||||
return dwc3_ulpi_busyloop(dwc);
|
||||
}
|
||||
|
||||
static struct ulpi_ops dwc3_ulpi_ops = {
|
||||
.read = dwc3_ulpi_read,
|
||||
.write = dwc3_ulpi_write,
|
||||
};
|
||||
|
||||
int dwc3_ulpi_init(struct dwc3 *dwc)
|
||||
{
|
||||
/* Register the interface */
|
||||
dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops);
|
||||
if (IS_ERR(dwc->ulpi)) {
|
||||
dev_err(dwc->dev, "failed to register ULPI interface");
|
||||
return PTR_ERR(dwc->ulpi);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dwc3_ulpi_exit(struct dwc3 *dwc)
|
||||
{
|
||||
if (dwc->ulpi) {
|
||||
ulpi_unregister_interface(dwc->ulpi);
|
||||
dwc->ulpi = NULL;
|
||||
}
|
||||
}
|
@ -258,15 +258,25 @@ struct usb_ep *usb_ep_autoconfig_ss(
|
||||
/* First, apply chip-specific "best usage" knowledge.
|
||||
* This might make a good usb_gadget_ops hook ...
|
||||
*/
|
||||
if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
|
||||
/* ep-e, ep-f are PIO with only 64 byte fifos */
|
||||
ep = find_ep (gadget, "ep-e");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
ep = find_ep (gadget, "ep-f");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
if (gadget_is_net2280(gadget)) {
|
||||
char name[8];
|
||||
|
||||
if (type == USB_ENDPOINT_XFER_INT) {
|
||||
/* ep-e, ep-f are PIO with only 64 byte fifos */
|
||||
ep = find_ep(gadget, "ep-e");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
ep = find_ep(gadget, "ep-f");
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
}
|
||||
|
||||
/* USB3380: use same address for usb and hardware endpoints */
|
||||
snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc),
|
||||
usb_endpoint_dir_in(desc) ? "in" : "out");
|
||||
ep = find_ep(gadget, name);
|
||||
if (ep && ep_matches(gadget, ep, desc, ep_comp))
|
||||
goto found_ep;
|
||||
} else if (gadget_is_goku (gadget)) {
|
||||
if (USB_ENDPOINT_XFER_INT == type) {
|
||||
/* single buffering is enough */
|
||||
|
@ -3435,6 +3435,7 @@ done:
|
||||
static void ffs_closed(struct ffs_data *ffs)
|
||||
{
|
||||
struct ffs_dev *ffs_obj;
|
||||
struct f_fs_opts *opts;
|
||||
|
||||
ENTER();
|
||||
ffs_dev_lock();
|
||||
@ -3449,8 +3450,13 @@ static void ffs_closed(struct ffs_data *ffs)
|
||||
ffs_obj->ffs_closed_callback)
|
||||
ffs_obj->ffs_closed_callback(ffs);
|
||||
|
||||
if (!ffs_obj->opts || ffs_obj->opts->no_configfs
|
||||
|| !ffs_obj->opts->func_inst.group.cg_item.ci_parent)
|
||||
if (ffs_obj->opts)
|
||||
opts = ffs_obj->opts;
|
||||
else
|
||||
goto done;
|
||||
|
||||
if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent
|
||||
|| !atomic_read(&opts->func_inst.group.cg_item.ci_kref.refcount))
|
||||
goto done;
|
||||
|
||||
unregister_gadget_item(ffs_obj->opts->
|
||||
|
@ -76,7 +76,7 @@ struct f_rndis {
|
||||
u8 ethaddr[ETH_ALEN];
|
||||
u32 vendorID;
|
||||
const char *manufacturer;
|
||||
int config;
|
||||
struct rndis_params *params;
|
||||
|
||||
struct usb_ep *notify;
|
||||
struct usb_request *notify_req;
|
||||
@ -453,7 +453,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
|
||||
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
|
||||
// spin_lock(&dev->lock);
|
||||
status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
|
||||
status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
|
||||
if (status < 0)
|
||||
pr_err("RNDIS command error %d, %d/%d\n",
|
||||
status, req->actual, req->length);
|
||||
@ -499,12 +499,12 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
|
||||
u32 n;
|
||||
|
||||
/* return the result */
|
||||
buf = rndis_get_next_response(rndis->config, &n);
|
||||
buf = rndis_get_next_response(rndis->params, &n);
|
||||
if (buf) {
|
||||
memcpy(req->buf, buf, n);
|
||||
req->complete = rndis_response_complete;
|
||||
req->context = rndis;
|
||||
rndis_free_response(rndis->config, buf);
|
||||
rndis_free_response(rndis->params, buf);
|
||||
value = n;
|
||||
}
|
||||
/* else stalls ... spec says to avoid that */
|
||||
@ -597,7 +597,7 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
if (IS_ERR(net))
|
||||
return PTR_ERR(net);
|
||||
|
||||
rndis_set_param_dev(rndis->config, net,
|
||||
rndis_set_param_dev(rndis->params, net,
|
||||
&rndis->port.cdc_filter);
|
||||
} else
|
||||
goto fail;
|
||||
@ -617,7 +617,7 @@ static void rndis_disable(struct usb_function *f)
|
||||
|
||||
DBG(cdev, "rndis deactivated\n");
|
||||
|
||||
rndis_uninit(rndis->config);
|
||||
rndis_uninit(rndis->params);
|
||||
gether_disconnect(&rndis->port);
|
||||
|
||||
usb_ep_disable(rndis->notify);
|
||||
@ -640,9 +640,9 @@ static void rndis_open(struct gether *geth)
|
||||
|
||||
DBG(cdev, "%s\n", __func__);
|
||||
|
||||
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3,
|
||||
rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3,
|
||||
bitrate(cdev->gadget) / 100);
|
||||
rndis_signal_connect(rndis->config);
|
||||
rndis_signal_connect(rndis->params);
|
||||
}
|
||||
|
||||
static void rndis_close(struct gether *geth)
|
||||
@ -651,8 +651,8 @@ static void rndis_close(struct gether *geth)
|
||||
|
||||
DBG(geth->func.config->cdev, "%s\n", __func__);
|
||||
|
||||
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
|
||||
rndis_signal_disconnect(rndis->config);
|
||||
rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
|
||||
rndis_signal_disconnect(rndis->params);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -796,11 +796,11 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
rndis->port.open = rndis_open;
|
||||
rndis->port.close = rndis_close;
|
||||
|
||||
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
|
||||
rndis_set_host_mac(rndis->config, rndis->ethaddr);
|
||||
rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0);
|
||||
rndis_set_host_mac(rndis->params, rndis->ethaddr);
|
||||
|
||||
if (rndis->manufacturer && rndis->vendorID &&
|
||||
rndis_set_param_vendor(rndis->config, rndis->vendorID,
|
||||
rndis_set_param_vendor(rndis->params, rndis->vendorID,
|
||||
rndis->manufacturer)) {
|
||||
status = -EINVAL;
|
||||
goto fail_free_descs;
|
||||
@ -944,7 +944,7 @@ static void rndis_free(struct usb_function *f)
|
||||
struct f_rndis_opts *opts;
|
||||
|
||||
rndis = func_to_rndis(f);
|
||||
rndis_deregister(rndis->config);
|
||||
rndis_deregister(rndis->params);
|
||||
opts = container_of(f->fi, struct f_rndis_opts, func_inst);
|
||||
kfree(rndis);
|
||||
mutex_lock(&opts->lock);
|
||||
@ -968,7 +968,7 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_rndis *rndis;
|
||||
struct f_rndis_opts *opts;
|
||||
int status;
|
||||
struct rndis_params *params;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
rndis = kzalloc(sizeof(*rndis), GFP_KERNEL);
|
||||
@ -1002,36 +1002,16 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
|
||||
rndis->port.func.disable = rndis_disable;
|
||||
rndis->port.func.free_func = rndis_free;
|
||||
|
||||
status = rndis_register(rndis_response_available, rndis);
|
||||
if (status < 0) {
|
||||
params = rndis_register(rndis_response_available, rndis);
|
||||
if (IS_ERR(params)) {
|
||||
kfree(rndis);
|
||||
return ERR_PTR(status);
|
||||
return ERR_CAST(params);
|
||||
}
|
||||
rndis->config = status;
|
||||
rndis->params = params;
|
||||
|
||||
return &rndis->port.func;
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc);
|
||||
|
||||
static int __init rndis_mod_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rndis_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return usb_function_register(&rndisusb_func);
|
||||
}
|
||||
module_init(rndis_mod_init);
|
||||
|
||||
static void __exit rndis_mod_exit(void)
|
||||
{
|
||||
usb_function_unregister(&rndisusb_func);
|
||||
rndis_exit();
|
||||
}
|
||||
module_exit(rndis_mod_exit);
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/slab.h>
|
||||
@ -57,17 +58,26 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging");
|
||||
#define rndis_debug 0
|
||||
#endif
|
||||
|
||||
#define RNDIS_MAX_CONFIGS 1
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
|
||||
#define NAME_TEMPLATE "driver/rndis-%03d"
|
||||
|
||||
static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS];
|
||||
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
|
||||
|
||||
static DEFINE_IDA(rndis_ida);
|
||||
|
||||
/* Driver Version */
|
||||
static const __le32 rndis_driver_version = cpu_to_le32(1);
|
||||
|
||||
/* Function Prototypes */
|
||||
static rndis_resp_t *rndis_add_response(int configNr, u32 length);
|
||||
static rndis_resp_t *rndis_add_response(struct rndis_params *params,
|
||||
u32 length);
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
|
||||
static const struct file_operations rndis_proc_fops;
|
||||
|
||||
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
|
||||
|
||||
/* supported OIDs */
|
||||
static const u32 oid_supported_list[] =
|
||||
@ -160,7 +170,7 @@ static const u32 oid_supported_list[] =
|
||||
|
||||
|
||||
/* NDIS Functions */
|
||||
static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf,
|
||||
unsigned buf_len, rndis_resp_t *r)
|
||||
{
|
||||
int retval = -ENOTSUPP;
|
||||
@ -192,7 +202,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
outbuf = (__le32 *)&resp[1];
|
||||
resp->InformationBufferOffset = cpu_to_le32(16);
|
||||
|
||||
net = rndis_per_dev_params[configNr].dev;
|
||||
net = params->dev;
|
||||
stats = dev_get_stats(net, &temp);
|
||||
|
||||
switch (OID) {
|
||||
@ -225,7 +235,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
/* mandatory */
|
||||
case RNDIS_OID_GEN_MEDIA_SUPPORTED:
|
||||
pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__);
|
||||
*outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
|
||||
*outbuf = cpu_to_le32(params->medium);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
@ -233,16 +243,15 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
case RNDIS_OID_GEN_MEDIA_IN_USE:
|
||||
pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__);
|
||||
/* one medium, one transport... (maybe you do it better) */
|
||||
*outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
|
||||
*outbuf = cpu_to_le32(params->medium);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
/* mandatory */
|
||||
case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE:
|
||||
pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
|
||||
if (rndis_per_dev_params[configNr].dev) {
|
||||
*outbuf = cpu_to_le32(
|
||||
rndis_per_dev_params[configNr].dev->mtu);
|
||||
if (params->dev) {
|
||||
*outbuf = cpu_to_le32(params->dev->mtu);
|
||||
retval = 0;
|
||||
}
|
||||
break;
|
||||
@ -251,21 +260,18 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
case RNDIS_OID_GEN_LINK_SPEED:
|
||||
if (rndis_debug > 1)
|
||||
pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__);
|
||||
if (rndis_per_dev_params[configNr].media_state
|
||||
== RNDIS_MEDIA_STATE_DISCONNECTED)
|
||||
if (params->media_state == RNDIS_MEDIA_STATE_DISCONNECTED)
|
||||
*outbuf = cpu_to_le32(0);
|
||||
else
|
||||
*outbuf = cpu_to_le32(
|
||||
rndis_per_dev_params[configNr].speed);
|
||||
*outbuf = cpu_to_le32(params->speed);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
/* mandatory */
|
||||
case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE:
|
||||
pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
|
||||
if (rndis_per_dev_params[configNr].dev) {
|
||||
*outbuf = cpu_to_le32(
|
||||
rndis_per_dev_params[configNr].dev->mtu);
|
||||
if (params->dev) {
|
||||
*outbuf = cpu_to_le32(params->dev->mtu);
|
||||
retval = 0;
|
||||
}
|
||||
break;
|
||||
@ -273,9 +279,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
/* mandatory */
|
||||
case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE:
|
||||
pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
|
||||
if (rndis_per_dev_params[configNr].dev) {
|
||||
*outbuf = cpu_to_le32(
|
||||
rndis_per_dev_params[configNr].dev->mtu);
|
||||
if (params->dev) {
|
||||
*outbuf = cpu_to_le32(params->dev->mtu);
|
||||
retval = 0;
|
||||
}
|
||||
break;
|
||||
@ -283,20 +288,16 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
/* mandatory */
|
||||
case RNDIS_OID_GEN_VENDOR_ID:
|
||||
pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__);
|
||||
*outbuf = cpu_to_le32(
|
||||
rndis_per_dev_params[configNr].vendorID);
|
||||
*outbuf = cpu_to_le32(params->vendorID);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
/* mandatory */
|
||||
case RNDIS_OID_GEN_VENDOR_DESCRIPTION:
|
||||
pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__);
|
||||
if (rndis_per_dev_params[configNr].vendorDescr) {
|
||||
length = strlen(rndis_per_dev_params[configNr].
|
||||
vendorDescr);
|
||||
memcpy(outbuf,
|
||||
rndis_per_dev_params[configNr].vendorDescr,
|
||||
length);
|
||||
if (params->vendorDescr) {
|
||||
length = strlen(params->vendorDescr);
|
||||
memcpy(outbuf, params->vendorDescr, length);
|
||||
} else {
|
||||
outbuf[0] = 0;
|
||||
}
|
||||
@ -313,7 +314,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
/* mandatory */
|
||||
case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
|
||||
pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__);
|
||||
*outbuf = cpu_to_le32(*rndis_per_dev_params[configNr].filter);
|
||||
*outbuf = cpu_to_le32(*params->filter);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
@ -328,8 +329,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS:
|
||||
if (rndis_debug > 1)
|
||||
pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);
|
||||
*outbuf = cpu_to_le32(rndis_per_dev_params[configNr]
|
||||
.media_state);
|
||||
*outbuf = cpu_to_le32(params->media_state);
|
||||
retval = 0;
|
||||
break;
|
||||
|
||||
@ -409,11 +409,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
/* mandatory */
|
||||
case RNDIS_OID_802_3_PERMANENT_ADDRESS:
|
||||
pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__);
|
||||
if (rndis_per_dev_params[configNr].dev) {
|
||||
if (params->dev) {
|
||||
length = ETH_ALEN;
|
||||
memcpy(outbuf,
|
||||
rndis_per_dev_params[configNr].host_mac,
|
||||
length);
|
||||
memcpy(outbuf, params->host_mac, length);
|
||||
retval = 0;
|
||||
}
|
||||
break;
|
||||
@ -421,11 +419,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
/* mandatory */
|
||||
case RNDIS_OID_802_3_CURRENT_ADDRESS:
|
||||
pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__);
|
||||
if (rndis_per_dev_params[configNr].dev) {
|
||||
if (params->dev) {
|
||||
length = ETH_ALEN;
|
||||
memcpy(outbuf,
|
||||
rndis_per_dev_params [configNr].host_mac,
|
||||
length);
|
||||
memcpy(outbuf, params->host_mac, length);
|
||||
retval = 0;
|
||||
}
|
||||
break;
|
||||
@ -490,12 +486,11 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
|
||||
rndis_resp_t *r)
|
||||
static int gen_ndis_set_resp(struct rndis_params *params, u32 OID,
|
||||
u8 *buf, u32 buf_len, rndis_resp_t *r)
|
||||
{
|
||||
rndis_set_cmplt_type *resp;
|
||||
int i, retval = -ENOTSUPP;
|
||||
struct rndis_params *params;
|
||||
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
@ -514,7 +509,6 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
|
||||
}
|
||||
}
|
||||
|
||||
params = &rndis_per_dev_params[configNr];
|
||||
switch (OID) {
|
||||
case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
|
||||
|
||||
@ -563,16 +557,16 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
|
||||
* Response Functions
|
||||
*/
|
||||
|
||||
static int rndis_init_response(int configNr, rndis_init_msg_type *buf)
|
||||
static int rndis_init_response(struct rndis_params *params,
|
||||
rndis_init_msg_type *buf)
|
||||
{
|
||||
rndis_init_cmplt_type *resp;
|
||||
rndis_resp_t *r;
|
||||
struct rndis_params *params = rndis_per_dev_params + configNr;
|
||||
|
||||
if (!params->dev)
|
||||
return -ENOTSUPP;
|
||||
|
||||
r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type));
|
||||
r = rndis_add_response(params, sizeof(rndis_init_cmplt_type));
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
resp = (rndis_init_cmplt_type *)r->buf;
|
||||
@ -599,11 +593,11 @@ static int rndis_init_response(int configNr, rndis_init_msg_type *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
|
||||
static int rndis_query_response(struct rndis_params *params,
|
||||
rndis_query_msg_type *buf)
|
||||
{
|
||||
rndis_query_cmplt_type *resp;
|
||||
rndis_resp_t *r;
|
||||
struct rndis_params *params = rndis_per_dev_params + configNr;
|
||||
|
||||
/* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */
|
||||
if (!params->dev)
|
||||
@ -615,7 +609,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
|
||||
* rndis_query_cmplt_type followed by data.
|
||||
* oid_supported_list is the largest data reply
|
||||
*/
|
||||
r = rndis_add_response(configNr,
|
||||
r = rndis_add_response(params,
|
||||
sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type));
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
@ -624,7 +618,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
|
||||
resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C);
|
||||
resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
|
||||
|
||||
if (gen_ndis_query_resp(configNr, le32_to_cpu(buf->OID),
|
||||
if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID),
|
||||
le32_to_cpu(buf->InformationBufferOffset)
|
||||
+ 8 + (u8 *)buf,
|
||||
le32_to_cpu(buf->InformationBufferLength),
|
||||
@ -641,14 +635,14 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rndis_set_response(int configNr, rndis_set_msg_type *buf)
|
||||
static int rndis_set_response(struct rndis_params *params,
|
||||
rndis_set_msg_type *buf)
|
||||
{
|
||||
u32 BufLength, BufOffset;
|
||||
rndis_set_cmplt_type *resp;
|
||||
rndis_resp_t *r;
|
||||
struct rndis_params *params = rndis_per_dev_params + configNr;
|
||||
|
||||
r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type));
|
||||
r = rndis_add_response(params, sizeof(rndis_set_cmplt_type));
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
resp = (rndis_set_cmplt_type *)r->buf;
|
||||
@ -671,7 +665,7 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf)
|
||||
resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C);
|
||||
resp->MessageLength = cpu_to_le32(16);
|
||||
resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
|
||||
if (gen_ndis_set_resp(configNr, le32_to_cpu(buf->OID),
|
||||
if (gen_ndis_set_resp(params, le32_to_cpu(buf->OID),
|
||||
((u8 *)buf) + 8 + BufOffset, BufLength, r))
|
||||
resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
|
||||
else
|
||||
@ -681,13 +675,13 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf)
|
||||
static int rndis_reset_response(struct rndis_params *params,
|
||||
rndis_reset_msg_type *buf)
|
||||
{
|
||||
rndis_reset_cmplt_type *resp;
|
||||
rndis_resp_t *r;
|
||||
struct rndis_params *params = rndis_per_dev_params + configNr;
|
||||
|
||||
r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type));
|
||||
r = rndis_add_response(params, sizeof(rndis_reset_cmplt_type));
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
resp = (rndis_reset_cmplt_type *)r->buf;
|
||||
@ -702,16 +696,15 @@ static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rndis_keepalive_response(int configNr,
|
||||
static int rndis_keepalive_response(struct rndis_params *params,
|
||||
rndis_keepalive_msg_type *buf)
|
||||
{
|
||||
rndis_keepalive_cmplt_type *resp;
|
||||
rndis_resp_t *r;
|
||||
struct rndis_params *params = rndis_per_dev_params + configNr;
|
||||
|
||||
/* host "should" check only in RNDIS_DATA_INITIALIZED state */
|
||||
|
||||
r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type));
|
||||
r = rndis_add_response(params, sizeof(rndis_keepalive_cmplt_type));
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
resp = (rndis_keepalive_cmplt_type *)r->buf;
|
||||
@ -729,17 +722,15 @@ static int rndis_keepalive_response(int configNr,
|
||||
/*
|
||||
* Device to Host Comunication
|
||||
*/
|
||||
static int rndis_indicate_status_msg(int configNr, u32 status)
|
||||
static int rndis_indicate_status_msg(struct rndis_params *params, u32 status)
|
||||
{
|
||||
rndis_indicate_status_msg_type *resp;
|
||||
rndis_resp_t *r;
|
||||
struct rndis_params *params = rndis_per_dev_params + configNr;
|
||||
|
||||
if (params->state == RNDIS_UNINITIALIZED)
|
||||
return -ENOTSUPP;
|
||||
|
||||
r = rndis_add_response(configNr,
|
||||
sizeof(rndis_indicate_status_msg_type));
|
||||
r = rndis_add_response(params, sizeof(rndis_indicate_status_msg_type));
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
resp = (rndis_indicate_status_msg_type *)r->buf;
|
||||
@ -754,53 +745,48 @@ static int rndis_indicate_status_msg(int configNr, u32 status)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rndis_signal_connect(int configNr)
|
||||
int rndis_signal_connect(struct rndis_params *params)
|
||||
{
|
||||
rndis_per_dev_params[configNr].media_state
|
||||
= RNDIS_MEDIA_STATE_CONNECTED;
|
||||
return rndis_indicate_status_msg(configNr,
|
||||
RNDIS_STATUS_MEDIA_CONNECT);
|
||||
params->media_state = RNDIS_MEDIA_STATE_CONNECTED;
|
||||
return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_CONNECT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_signal_connect);
|
||||
|
||||
int rndis_signal_disconnect(int configNr)
|
||||
int rndis_signal_disconnect(struct rndis_params *params)
|
||||
{
|
||||
rndis_per_dev_params[configNr].media_state
|
||||
= RNDIS_MEDIA_STATE_DISCONNECTED;
|
||||
return rndis_indicate_status_msg(configNr,
|
||||
RNDIS_STATUS_MEDIA_DISCONNECT);
|
||||
params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED;
|
||||
return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_DISCONNECT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_signal_disconnect);
|
||||
|
||||
void rndis_uninit(int configNr)
|
||||
void rndis_uninit(struct rndis_params *params)
|
||||
{
|
||||
u8 *buf;
|
||||
u32 length;
|
||||
|
||||
if (configNr >= RNDIS_MAX_CONFIGS)
|
||||
if (!params)
|
||||
return;
|
||||
rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED;
|
||||
params->state = RNDIS_UNINITIALIZED;
|
||||
|
||||
/* drain the response queue */
|
||||
while ((buf = rndis_get_next_response(configNr, &length)))
|
||||
rndis_free_response(configNr, buf);
|
||||
while ((buf = rndis_get_next_response(params, &length)))
|
||||
rndis_free_response(params, buf);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_uninit);
|
||||
|
||||
void rndis_set_host_mac(int configNr, const u8 *addr)
|
||||
void rndis_set_host_mac(struct rndis_params *params, const u8 *addr)
|
||||
{
|
||||
rndis_per_dev_params[configNr].host_mac = addr;
|
||||
params->host_mac = addr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_set_host_mac);
|
||||
|
||||
/*
|
||||
* Message Parser
|
||||
*/
|
||||
int rndis_msg_parser(u8 configNr, u8 *buf)
|
||||
int rndis_msg_parser(struct rndis_params *params, u8 *buf)
|
||||
{
|
||||
u32 MsgType, MsgLength;
|
||||
__le32 *tmp;
|
||||
struct rndis_params *params;
|
||||
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
@ -809,9 +795,8 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
|
||||
MsgType = get_unaligned_le32(tmp++);
|
||||
MsgLength = get_unaligned_le32(tmp++);
|
||||
|
||||
if (configNr >= RNDIS_MAX_CONFIGS)
|
||||
if (!params)
|
||||
return -ENOTSUPP;
|
||||
params = &rndis_per_dev_params[configNr];
|
||||
|
||||
/* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
|
||||
* rx/tx statistics and link status, in addition to KEEPALIVE traffic
|
||||
@ -824,8 +809,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
|
||||
pr_debug("%s: RNDIS_MSG_INIT\n",
|
||||
__func__);
|
||||
params->state = RNDIS_INITIALIZED;
|
||||
return rndis_init_response(configNr,
|
||||
(rndis_init_msg_type *)buf);
|
||||
return rndis_init_response(params, (rndis_init_msg_type *)buf);
|
||||
|
||||
case RNDIS_MSG_HALT:
|
||||
pr_debug("%s: RNDIS_MSG_HALT\n",
|
||||
@ -838,17 +822,16 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
|
||||
return 0;
|
||||
|
||||
case RNDIS_MSG_QUERY:
|
||||
return rndis_query_response(configNr,
|
||||
return rndis_query_response(params,
|
||||
(rndis_query_msg_type *)buf);
|
||||
|
||||
case RNDIS_MSG_SET:
|
||||
return rndis_set_response(configNr,
|
||||
(rndis_set_msg_type *)buf);
|
||||
return rndis_set_response(params, (rndis_set_msg_type *)buf);
|
||||
|
||||
case RNDIS_MSG_RESET:
|
||||
pr_debug("%s: RNDIS_MSG_RESET\n",
|
||||
__func__);
|
||||
return rndis_reset_response(configNr,
|
||||
return rndis_reset_response(params,
|
||||
(rndis_reset_msg_type *)buf);
|
||||
|
||||
case RNDIS_MSG_KEEPALIVE:
|
||||
@ -856,7 +839,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
|
||||
if (rndis_debug > 1)
|
||||
pr_debug("%s: RNDIS_MSG_KEEPALIVE\n",
|
||||
__func__);
|
||||
return rndis_keepalive_response(configNr,
|
||||
return rndis_keepalive_response(params,
|
||||
(rndis_keepalive_msg_type *)
|
||||
buf);
|
||||
|
||||
@ -876,71 +859,131 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_msg_parser);
|
||||
|
||||
int rndis_register(void (*resp_avail)(void *v), void *v)
|
||||
static inline int rndis_get_nr(void)
|
||||
{
|
||||
u8 i;
|
||||
return ida_simple_get(&rndis_ida, 0, 0, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline void rndis_put_nr(int nr)
|
||||
{
|
||||
ida_simple_remove(&rndis_ida, nr);
|
||||
}
|
||||
|
||||
struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v)
|
||||
{
|
||||
struct rndis_params *params;
|
||||
int i;
|
||||
|
||||
if (!resp_avail)
|
||||
return -EINVAL;
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
|
||||
if (!rndis_per_dev_params[i].used) {
|
||||
rndis_per_dev_params[i].used = 1;
|
||||
rndis_per_dev_params[i].resp_avail = resp_avail;
|
||||
rndis_per_dev_params[i].v = v;
|
||||
pr_debug("%s: configNr = %d\n", __func__, i);
|
||||
return i;
|
||||
i = rndis_get_nr();
|
||||
if (i < 0) {
|
||||
pr_debug("failed\n");
|
||||
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
params = kzalloc(sizeof(*params), GFP_KERNEL);
|
||||
if (!params) {
|
||||
rndis_put_nr(i);
|
||||
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
{
|
||||
struct proc_dir_entry *proc_entry;
|
||||
char name[20];
|
||||
|
||||
sprintf(name, NAME_TEMPLATE, i);
|
||||
proc_entry = proc_create_data(name, 0660, NULL,
|
||||
&rndis_proc_fops, params);
|
||||
if (!proc_entry) {
|
||||
kfree(params);
|
||||
rndis_put_nr(i);
|
||||
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
}
|
||||
pr_debug("failed\n");
|
||||
#endif
|
||||
|
||||
return -ENODEV;
|
||||
params->confignr = i;
|
||||
params->used = 1;
|
||||
params->state = RNDIS_UNINITIALIZED;
|
||||
params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED;
|
||||
params->resp_avail = resp_avail;
|
||||
params->v = v;
|
||||
INIT_LIST_HEAD(&(params->resp_queue));
|
||||
pr_debug("%s: configNr = %d\n", __func__, i);
|
||||
|
||||
return params;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_register);
|
||||
|
||||
void rndis_deregister(int configNr)
|
||||
void rndis_deregister(struct rndis_params *params)
|
||||
{
|
||||
int i;
|
||||
|
||||
pr_debug("%s:\n", __func__);
|
||||
|
||||
if (configNr >= RNDIS_MAX_CONFIGS) return;
|
||||
rndis_per_dev_params[configNr].used = 0;
|
||||
if (!params)
|
||||
return;
|
||||
|
||||
i = params->confignr;
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
{
|
||||
char name[20];
|
||||
|
||||
sprintf(name, NAME_TEMPLATE, i);
|
||||
remove_proc_entry(name, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
kfree(params);
|
||||
rndis_put_nr(i);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_deregister);
|
||||
|
||||
int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
|
||||
int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev,
|
||||
u16 *cdc_filter)
|
||||
{
|
||||
pr_debug("%s:\n", __func__);
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
|
||||
if (!params)
|
||||
return -1;
|
||||
|
||||
rndis_per_dev_params[configNr].dev = dev;
|
||||
rndis_per_dev_params[configNr].filter = cdc_filter;
|
||||
params->dev = dev;
|
||||
params->filter = cdc_filter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_set_param_dev);
|
||||
|
||||
int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
|
||||
int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID,
|
||||
const char *vendorDescr)
|
||||
{
|
||||
pr_debug("%s:\n", __func__);
|
||||
if (!vendorDescr) return -1;
|
||||
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
|
||||
if (!params)
|
||||
return -1;
|
||||
|
||||
rndis_per_dev_params[configNr].vendorID = vendorID;
|
||||
rndis_per_dev_params[configNr].vendorDescr = vendorDescr;
|
||||
params->vendorID = vendorID;
|
||||
params->vendorDescr = vendorDescr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_set_param_vendor);
|
||||
|
||||
int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
|
||||
int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed)
|
||||
{
|
||||
pr_debug("%s: %u %u\n", __func__, medium, speed);
|
||||
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
|
||||
if (!params)
|
||||
return -1;
|
||||
|
||||
rndis_per_dev_params[configNr].medium = medium;
|
||||
rndis_per_dev_params[configNr].speed = speed;
|
||||
params->medium = medium;
|
||||
params->speed = speed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -961,13 +1004,12 @@ void rndis_add_hdr(struct sk_buff *skb)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_add_hdr);
|
||||
|
||||
void rndis_free_response(int configNr, u8 *buf)
|
||||
void rndis_free_response(struct rndis_params *params, u8 *buf)
|
||||
{
|
||||
rndis_resp_t *r;
|
||||
struct list_head *act, *tmp;
|
||||
|
||||
list_for_each_safe(act, tmp,
|
||||
&(rndis_per_dev_params[configNr].resp_queue))
|
||||
list_for_each_safe(act, tmp, &(params->resp_queue))
|
||||
{
|
||||
r = list_entry(act, rndis_resp_t, list);
|
||||
if (r && r->buf == buf) {
|
||||
@ -978,15 +1020,14 @@ void rndis_free_response(int configNr, u8 *buf)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_free_response);
|
||||
|
||||
u8 *rndis_get_next_response(int configNr, u32 *length)
|
||||
u8 *rndis_get_next_response(struct rndis_params *params, u32 *length)
|
||||
{
|
||||
rndis_resp_t *r;
|
||||
struct list_head *act, *tmp;
|
||||
|
||||
if (!length) return NULL;
|
||||
|
||||
list_for_each_safe(act, tmp,
|
||||
&(rndis_per_dev_params[configNr].resp_queue))
|
||||
list_for_each_safe(act, tmp, &(params->resp_queue))
|
||||
{
|
||||
r = list_entry(act, rndis_resp_t, list);
|
||||
if (!r->send) {
|
||||
@ -1000,7 +1041,7 @@ u8 *rndis_get_next_response(int configNr, u32 *length)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rndis_get_next_response);
|
||||
|
||||
static rndis_resp_t *rndis_add_response(int configNr, u32 length)
|
||||
static rndis_resp_t *rndis_add_response(struct rndis_params *params, u32 length)
|
||||
{
|
||||
rndis_resp_t *r;
|
||||
|
||||
@ -1012,8 +1053,7 @@ static rndis_resp_t *rndis_add_response(int configNr, u32 length)
|
||||
r->length = length;
|
||||
r->send = 0;
|
||||
|
||||
list_add_tail(&r->list,
|
||||
&(rndis_per_dev_params[configNr].resp_queue));
|
||||
list_add_tail(&r->list, &(params->resp_queue));
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -1103,11 +1143,11 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
|
||||
break;
|
||||
case 'C':
|
||||
case 'c':
|
||||
rndis_signal_connect(p->confignr);
|
||||
rndis_signal_connect(p);
|
||||
break;
|
||||
case 'D':
|
||||
case 'd':
|
||||
rndis_signal_disconnect(p->confignr);
|
||||
rndis_signal_disconnect(p);
|
||||
break;
|
||||
default:
|
||||
if (fl_speed) p->speed = speed;
|
||||
@ -1137,54 +1177,4 @@ static const struct file_operations rndis_proc_fops = {
|
||||
|
||||
#define NAME_TEMPLATE "driver/rndis-%03d"
|
||||
|
||||
static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
|
||||
|
||||
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
|
||||
|
||||
|
||||
int rndis_init(void)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
char name [20];
|
||||
|
||||
sprintf(name, NAME_TEMPLATE, i);
|
||||
rndis_connect_state[i] = proc_create_data(name, 0660, NULL,
|
||||
&rndis_proc_fops,
|
||||
(void *)(rndis_per_dev_params + i));
|
||||
if (!rndis_connect_state[i]) {
|
||||
pr_debug("%s: remove entries", __func__);
|
||||
while (i) {
|
||||
sprintf(name, NAME_TEMPLATE, --i);
|
||||
remove_proc_entry(name, NULL);
|
||||
}
|
||||
pr_debug("\n");
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
rndis_per_dev_params[i].confignr = i;
|
||||
rndis_per_dev_params[i].used = 0;
|
||||
rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED;
|
||||
rndis_per_dev_params[i].media_state
|
||||
= RNDIS_MEDIA_STATE_DISCONNECTED;
|
||||
INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rndis_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
u8 i;
|
||||
char name[20];
|
||||
|
||||
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
|
||||
sprintf(name, NAME_TEMPLATE, i);
|
||||
remove_proc_entry(name, NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,7 @@ typedef struct rndis_resp_t
|
||||
|
||||
typedef struct rndis_params
|
||||
{
|
||||
u8 confignr;
|
||||
int confignr;
|
||||
u8 used;
|
||||
u16 saved_filter;
|
||||
enum rndis_state state;
|
||||
@ -197,24 +197,25 @@ typedef struct rndis_params
|
||||
} rndis_params;
|
||||
|
||||
/* RNDIS Message parser and other useless functions */
|
||||
int rndis_msg_parser (u8 configNr, u8 *buf);
|
||||
int rndis_register(void (*resp_avail)(void *v), void *v);
|
||||
void rndis_deregister (int configNr);
|
||||
int rndis_set_param_dev (u8 configNr, struct net_device *dev,
|
||||
int rndis_msg_parser(struct rndis_params *params, u8 *buf);
|
||||
struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v);
|
||||
void rndis_deregister(struct rndis_params *params);
|
||||
int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev,
|
||||
u16 *cdc_filter);
|
||||
int rndis_set_param_vendor (u8 configNr, u32 vendorID,
|
||||
int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID,
|
||||
const char *vendorDescr);
|
||||
int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
|
||||
void rndis_add_hdr (struct sk_buff *skb);
|
||||
int rndis_set_param_medium(struct rndis_params *params, u32 medium,
|
||||
u32 speed);
|
||||
void rndis_add_hdr(struct sk_buff *skb);
|
||||
int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
|
||||
struct sk_buff_head *list);
|
||||
u8 *rndis_get_next_response (int configNr, u32 *length);
|
||||
void rndis_free_response (int configNr, u8 *buf);
|
||||
u8 *rndis_get_next_response(struct rndis_params *params, u32 *length);
|
||||
void rndis_free_response(struct rndis_params *params, u8 *buf);
|
||||
|
||||
void rndis_uninit (int configNr);
|
||||
int rndis_signal_connect (int configNr);
|
||||
int rndis_signal_disconnect (int configNr);
|
||||
int rndis_state (int configNr);
|
||||
extern void rndis_set_host_mac (int configNr, const u8 *addr);
|
||||
void rndis_uninit(struct rndis_params *params);
|
||||
int rndis_signal_connect(struct rndis_params *params);
|
||||
int rndis_signal_disconnect(struct rndis_params *params);
|
||||
int rndis_state(struct rndis_params *params);
|
||||
extern void rndis_set_host_mac(struct rndis_params *params, const u8 *addr);
|
||||
|
||||
#endif /* _LINUX_RNDIS_H */
|
||||
|
@ -39,8 +39,6 @@ struct f_rndis_opts {
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
int rndis_init(void);
|
||||
void rndis_exit(void);
|
||||
void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net);
|
||||
|
||||
#endif /* U_RNDIS_H */
|
||||
|
@ -56,7 +56,6 @@ struct uvc_event
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/gadget.h>
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/version.h>
|
||||
#include <media/v4l2-fh.h>
|
||||
#include <media/v4l2-device.h>
|
||||
|
||||
|
@ -769,9 +769,12 @@ ep_config (struct ep_data *data, const char *buf, size_t len)
|
||||
if (data->dev->state == STATE_DEV_UNBOUND) {
|
||||
value = -ENOENT;
|
||||
goto gone;
|
||||
} else if ((ep = data->ep) == NULL) {
|
||||
value = -ENODEV;
|
||||
goto gone;
|
||||
} else {
|
||||
ep = data->ep;
|
||||
if (ep == NULL) {
|
||||
value = -ENODEV;
|
||||
goto gone;
|
||||
}
|
||||
}
|
||||
switch (data->dev->gadget->speed) {
|
||||
case USB_SPEED_LOW:
|
||||
|
@ -704,8 +704,8 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep,
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
DBG(DBG_DMA, "%s: req l/%u d/%08x %c%c%c\n",
|
||||
ep->ep.name, req->req.length, req->req.dma,
|
||||
DBG(DBG_DMA, "%s: req l/%u d/%pad %c%c%c\n",
|
||||
ep->ep.name, req->req.length, &req->req.dma,
|
||||
req->req.zero ? 'Z' : 'z',
|
||||
req->req.short_not_ok ? 'S' : 's',
|
||||
req->req.no_interrupt ? 'I' : 'i');
|
||||
@ -2203,7 +2203,7 @@ static int usba_udc_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int usba_udc_suspend(struct device *dev)
|
||||
{
|
||||
struct usba_udc *udc = dev_get_drvdata(dev);
|
||||
|
@ -123,6 +123,11 @@ static char *type_string(u8 bmAttributes)
|
||||
#define valid_bit cpu_to_le32(BIT(VALID_BIT))
|
||||
#define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE))
|
||||
|
||||
static void ep_clear_seqnum(struct net2280_ep *ep);
|
||||
static void stop_activity(struct net2280 *dev,
|
||||
struct usb_gadget_driver *driver);
|
||||
static void ep0_start(struct net2280 *dev);
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static inline void enable_pciirqenb(struct net2280_ep *ep)
|
||||
{
|
||||
@ -142,7 +147,9 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
struct net2280 *dev;
|
||||
struct net2280_ep *ep;
|
||||
u32 max, tmp;
|
||||
u32 max;
|
||||
u32 tmp = 0;
|
||||
u32 type;
|
||||
unsigned long flags;
|
||||
static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
|
||||
int ret = 0;
|
||||
@ -198,15 +205,29 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
|
||||
/* set type, direction, address; reset fifo counters */
|
||||
writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
|
||||
tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
|
||||
if (tmp == USB_ENDPOINT_XFER_INT) {
|
||||
|
||||
if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) {
|
||||
tmp = readl(&ep->cfg->ep_cfg);
|
||||
/* If USB ep number doesn't match hardware ep number */
|
||||
if ((tmp & 0xf) != usb_endpoint_num(desc)) {
|
||||
ret = -EINVAL;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
goto print_err;
|
||||
}
|
||||
if (ep->is_in)
|
||||
tmp &= ~USB3380_EP_CFG_MASK_IN;
|
||||
else
|
||||
tmp &= ~USB3380_EP_CFG_MASK_OUT;
|
||||
}
|
||||
type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
|
||||
if (type == USB_ENDPOINT_XFER_INT) {
|
||||
/* erratum 0105 workaround prevents hs NYET */
|
||||
if (dev->chiprev == 0100 &&
|
||||
dev->gadget.speed == USB_SPEED_HIGH &&
|
||||
!(desc->bEndpointAddress & USB_DIR_IN))
|
||||
writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE),
|
||||
&ep->regs->ep_rsp);
|
||||
} else if (tmp == USB_ENDPOINT_XFER_BULK) {
|
||||
} else if (type == USB_ENDPOINT_XFER_BULK) {
|
||||
/* catch some particularly blatant driver bugs */
|
||||
if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) ||
|
||||
(dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
|
||||
@ -216,10 +237,10 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
goto print_err;
|
||||
}
|
||||
}
|
||||
ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
|
||||
ep->is_iso = (type == USB_ENDPOINT_XFER_ISOC);
|
||||
/* Enable this endpoint */
|
||||
if (dev->quirks & PLX_LEGACY) {
|
||||
tmp <<= ENDPOINT_TYPE;
|
||||
tmp |= type << ENDPOINT_TYPE;
|
||||
tmp |= desc->bEndpointAddress;
|
||||
/* default full fifo lines */
|
||||
tmp |= (4 << ENDPOINT_BYTE_COUNT);
|
||||
@ -228,17 +249,17 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
} else {
|
||||
/* In Legacy mode, only OUT endpoints are used */
|
||||
if (dev->enhanced_mode && ep->is_in) {
|
||||
tmp <<= IN_ENDPOINT_TYPE;
|
||||
tmp |= type << IN_ENDPOINT_TYPE;
|
||||
tmp |= BIT(IN_ENDPOINT_ENABLE);
|
||||
/* Not applicable to Legacy */
|
||||
tmp |= BIT(ENDPOINT_DIRECTION);
|
||||
} else {
|
||||
tmp <<= OUT_ENDPOINT_TYPE;
|
||||
tmp |= type << OUT_ENDPOINT_TYPE;
|
||||
tmp |= BIT(OUT_ENDPOINT_ENABLE);
|
||||
tmp |= (ep->is_in << ENDPOINT_DIRECTION);
|
||||
}
|
||||
|
||||
tmp |= usb_endpoint_num(desc);
|
||||
tmp |= (4 << ENDPOINT_BYTE_COUNT);
|
||||
if (!dev->enhanced_mode)
|
||||
tmp |= usb_endpoint_num(desc);
|
||||
tmp |= (ep->ep.maxburst << MAX_BURST_SIZE);
|
||||
}
|
||||
|
||||
@ -256,6 +277,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
|
||||
BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
|
||||
}
|
||||
|
||||
if (dev->quirks & PLX_SUPERSPEED)
|
||||
ep_clear_seqnum(ep);
|
||||
writel(tmp, &ep->cfg->ep_cfg);
|
||||
|
||||
/* enable irqs */
|
||||
@ -441,6 +464,13 @@ static void ep_reset_338x(struct net2280_regs __iomem *regs,
|
||||
BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
|
||||
BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
|
||||
BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat);
|
||||
|
||||
tmp = readl(&ep->cfg->ep_cfg);
|
||||
if (ep->is_in)
|
||||
tmp &= ~USB3380_EP_CFG_MASK_IN;
|
||||
else
|
||||
tmp &= ~USB3380_EP_CFG_MASK_OUT;
|
||||
writel(tmp, &ep->cfg->ep_cfg);
|
||||
}
|
||||
|
||||
static void nuke(struct net2280_ep *);
|
||||
@ -1468,11 +1498,14 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
tmp = readl(&dev->usb->usbctl);
|
||||
dev->softconnect = (is_on != 0);
|
||||
if (is_on)
|
||||
tmp |= BIT(USB_DETECT_ENABLE);
|
||||
else
|
||||
tmp &= ~BIT(USB_DETECT_ENABLE);
|
||||
writel(tmp, &dev->usb->usbctl);
|
||||
if (is_on) {
|
||||
ep0_start(dev);
|
||||
writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
|
||||
} else {
|
||||
writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl);
|
||||
stop_activity(dev, dev->driver);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
return 0;
|
||||
@ -1860,8 +1893,8 @@ static void defect7374_enable_data_eps_zero(struct net2280 *dev)
|
||||
tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) |
|
||||
(2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
|
||||
((dev->enhanced_mode) ?
|
||||
BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) |
|
||||
BIT(IN_ENDPOINT_ENABLE));
|
||||
BIT(OUT_ENDPOINT_ENABLE) | BIT(IN_ENDPOINT_ENABLE) :
|
||||
BIT(ENDPOINT_ENABLE)));
|
||||
|
||||
for (i = 1; i < 5; i++)
|
||||
writel(tmp, &dev->ep[i].cfg->ep_cfg);
|
||||
@ -1975,9 +2008,15 @@ static void usb_reset_338x(struct net2280 *dev)
|
||||
/* clear old dma and irq state */
|
||||
for (tmp = 0; tmp < 4; tmp++) {
|
||||
struct net2280_ep *ep = &dev->ep[tmp + 1];
|
||||
struct net2280_dma_regs __iomem *dma;
|
||||
|
||||
if (ep->dma)
|
||||
if (ep->dma) {
|
||||
abort_dma(ep);
|
||||
} else {
|
||||
dma = &dev->dma[tmp];
|
||||
writel(BIT(DMA_ABORT), &dma->dmastat);
|
||||
writel(0, &dma->dmactl);
|
||||
}
|
||||
}
|
||||
|
||||
writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1);
|
||||
@ -2065,6 +2104,12 @@ static void usb_reinit_338x(struct net2280 *dev)
|
||||
|
||||
if (dev->enhanced_mode) {
|
||||
ep->cfg = &dev->epregs[ne[i]];
|
||||
/*
|
||||
* Set USB endpoint number, hardware allows same number
|
||||
* in both directions.
|
||||
*/
|
||||
if (i > 0 && i < 5)
|
||||
writel(ne[i], &ep->cfg->ep_cfg);
|
||||
ep->regs = (struct net2280_ep_regs __iomem *)
|
||||
(((void __iomem *)&dev->epregs[ne[i]]) +
|
||||
ep_reg_addr[i]);
|
||||
@ -2874,6 +2919,26 @@ next_endpoints3:
|
||||
return;
|
||||
}
|
||||
|
||||
static void usb338x_handle_ep_intr(struct net2280 *dev, u32 stat0)
|
||||
{
|
||||
u32 index;
|
||||
u32 bit;
|
||||
|
||||
for (index = 0; index < ARRAY_SIZE(ep_bit); index++) {
|
||||
bit = BIT(ep_bit[index]);
|
||||
|
||||
if (!stat0)
|
||||
break;
|
||||
|
||||
if (!(stat0 & bit))
|
||||
continue;
|
||||
|
||||
stat0 &= ~bit;
|
||||
|
||||
handle_ep_small(&dev->ep[index]);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
|
||||
{
|
||||
struct net2280_ep *ep;
|
||||
@ -3098,20 +3163,31 @@ do_stall:
|
||||
#undef w_length
|
||||
|
||||
next_endpoints:
|
||||
/* endpoint data irq ? */
|
||||
scratch = stat & 0x7f;
|
||||
stat &= ~0x7f;
|
||||
for (num = 0; scratch; num++) {
|
||||
u32 t;
|
||||
if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) {
|
||||
u32 mask = (BIT(ENDPOINT_0_INTERRUPT) |
|
||||
USB3380_IRQSTAT0_EP_INTR_MASK_IN |
|
||||
USB3380_IRQSTAT0_EP_INTR_MASK_OUT);
|
||||
|
||||
/* do this endpoint's FIFO and queue need tending? */
|
||||
t = BIT(num);
|
||||
if ((scratch & t) == 0)
|
||||
continue;
|
||||
scratch ^= t;
|
||||
if (stat & mask) {
|
||||
usb338x_handle_ep_intr(dev, stat & mask);
|
||||
stat &= ~mask;
|
||||
}
|
||||
} else {
|
||||
/* endpoint data irq ? */
|
||||
scratch = stat & 0x7f;
|
||||
stat &= ~0x7f;
|
||||
for (num = 0; scratch; num++) {
|
||||
u32 t;
|
||||
|
||||
ep = &dev->ep[num];
|
||||
handle_ep_small(ep);
|
||||
/* do this endpoint's FIFO and queue need tending? */
|
||||
t = BIT(num);
|
||||
if ((scratch & t) == 0)
|
||||
continue;
|
||||
scratch ^= t;
|
||||
|
||||
ep = &dev->ep[num];
|
||||
handle_ep_small(ep);
|
||||
}
|
||||
}
|
||||
|
||||
if (stat)
|
||||
|
@ -92,40 +92,38 @@ static struct s3c2410_udc_mach_info *udc_info;
|
||||
|
||||
static uint32_t s3c2410_ticks = 0;
|
||||
|
||||
static int dprintk(int level, const char *fmt, ...)
|
||||
__printf(2, 3)
|
||||
static void dprintk(int level, const char *fmt, ...)
|
||||
{
|
||||
static char printk_buf[1024];
|
||||
static long prevticks;
|
||||
static int invocation;
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
int len;
|
||||
|
||||
if (level > USB_S3C2410_DEBUG_LEVEL)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
va_start(args, fmt);
|
||||
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
|
||||
if (s3c2410_ticks != prevticks) {
|
||||
prevticks = s3c2410_ticks;
|
||||
invocation = 0;
|
||||
}
|
||||
|
||||
len = scnprintf(printk_buf,
|
||||
sizeof(printk_buf), "%1lu.%02d USB: ",
|
||||
prevticks, invocation++);
|
||||
pr_debug("%1lu.%02d USB: %pV", prevticks, invocation++, &vaf);
|
||||
|
||||
va_start(args, fmt);
|
||||
len = vscnprintf(printk_buf+len,
|
||||
sizeof(printk_buf)-len, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
pr_debug("%s", printk_buf);
|
||||
return len;
|
||||
}
|
||||
#else
|
||||
static int dprintk(int level, const char *fmt, ...)
|
||||
__printf(2, 3)
|
||||
static void dprintk(int level, const char *fmt, ...)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
|
||||
{
|
||||
u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg;
|
||||
|
@ -137,7 +137,7 @@ config XPS_USB_HCD_XILINX
|
||||
devices only.
|
||||
|
||||
config USB_EHCI_FSL
|
||||
bool "Support for Freescale PPC on-chip EHCI USB controller"
|
||||
tristate "Support for Freescale PPC on-chip EHCI USB controller"
|
||||
depends on FSL_SOC
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
select USB_FSL_MPH_DR_OF if OF
|
||||
|
@ -24,7 +24,9 @@ endif
|
||||
|
||||
obj-$(CONFIG_USB_WHCI_HCD) += whci/
|
||||
|
||||
obj-$(CONFIG_PCI) += pci-quirks.o
|
||||
ifneq ($(CONFIG_USB), )
|
||||
obj-$(CONFIG_PCI) += pci-quirks.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
|
||||
obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
|
||||
@ -70,6 +72,7 @@ obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
|
||||
obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
|
||||
obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o
|
||||
obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o
|
||||
obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o
|
||||
obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o
|
||||
obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o
|
||||
obj-$(CONFIG_USB_FUSBH200_HCD) += fusbh200-hcd.o
|
||||
|
@ -628,7 +628,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
||||
unsigned i;
|
||||
__hc32 tag;
|
||||
|
||||
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC)))
|
||||
seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC);
|
||||
if (!seen)
|
||||
return 0;
|
||||
seen_count = 0;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2005-2009 MontaVista Software, Inc.
|
||||
* Copyright 2008,2012 Freescale Semiconductor, Inc.
|
||||
* Copyright 2008,2012,2015 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
@ -24,29 +24,38 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/ehci_def.h>
|
||||
#include <linux/usb/hcd.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/fsl_devices.h>
|
||||
|
||||
#include "ehci.h"
|
||||
#include "ehci-fsl.h"
|
||||
|
||||
#define DRIVER_DESC "Freescale EHCI Host controller driver"
|
||||
#define DRV_NAME "ehci-fsl"
|
||||
|
||||
static struct hc_driver __read_mostly fsl_ehci_hc_driver;
|
||||
|
||||
/* configure so an HC device and id are always provided */
|
||||
/* always called with process context; sleeping is OK */
|
||||
|
||||
/**
|
||||
* usb_hcd_fsl_probe - initialize FSL-based HCDs
|
||||
* @drvier: Driver to be used for this HCD
|
||||
/*
|
||||
* fsl_ehci_drv_probe - initialize FSL-based HCDs
|
||||
* @pdev: USB Host Controller being probed
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Allocates basic resources for this USB host controller.
|
||||
*
|
||||
*/
|
||||
static int usb_hcd_fsl_probe(const struct hc_driver *driver,
|
||||
struct platform_device *pdev)
|
||||
static int fsl_ehci_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_usb2_platform_data *pdata;
|
||||
struct usb_hcd *hcd;
|
||||
@ -86,7 +95,8 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
|
||||
}
|
||||
irq = res->start;
|
||||
|
||||
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
|
||||
hcd = usb_create_hcd(&fsl_ehci_hc_driver, &pdev->dev,
|
||||
dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
retval = -ENOMEM;
|
||||
goto err1;
|
||||
@ -159,38 +169,6 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* may be called without controller electrically present */
|
||||
/* may be called with controller, bus, and devices active */
|
||||
|
||||
/**
|
||||
* usb_hcd_fsl_remove - shutdown processing for FSL-based HCDs
|
||||
* @dev: USB Host Controller being removed
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Reverses the effect of usb_hcd_fsl_probe().
|
||||
*
|
||||
*/
|
||||
static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
|
||||
otg_set_host(hcd->usb_phy->otg, NULL);
|
||||
usb_put_phy(hcd->usb_phy);
|
||||
}
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
/*
|
||||
* do platform specific un-initialization:
|
||||
* release iomux pins, disable clock, etc.
|
||||
*/
|
||||
if (pdata->exit)
|
||||
pdata->exit(pdev);
|
||||
usb_put_hcd(hcd);
|
||||
}
|
||||
|
||||
static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
|
||||
enum fsl_usb2_phy_modes phy_mode,
|
||||
unsigned int port_offset)
|
||||
@ -636,79 +614,77 @@ static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port)
|
||||
#define ehci_start_port_reset NULL
|
||||
#endif /* CONFIG_USB_OTG */
|
||||
|
||||
|
||||
static const struct hc_driver ehci_fsl_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "Freescale On-Chip EHCI Host Controller",
|
||||
.hcd_priv_size = sizeof(struct ehci_fsl),
|
||||
|
||||
/*
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
|
||||
|
||||
/*
|
||||
* basic lifecycle operations
|
||||
*/
|
||||
static struct ehci_driver_overrides ehci_fsl_overrides __initdata = {
|
||||
.extra_priv_size = sizeof(struct ehci_fsl),
|
||||
.reset = ehci_fsl_setup,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
/*
|
||||
* managing i/o requests and associated device resources
|
||||
*/
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
/*
|
||||
* scheduling support
|
||||
*/
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
/*
|
||||
* root hub support
|
||||
*/
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
.start_port_reset = ehci_start_port_reset,
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int ehci_fsl_drv_probe(struct platform_device *pdev)
|
||||
{
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
/**
|
||||
* fsl_ehci_drv_remove - shutdown processing for FSL-based HCDs
|
||||
* @dev: USB Host Controller being removed
|
||||
* Context: !in_interrupt()
|
||||
*
|
||||
* Reverses the effect of usb_hcd_fsl_probe().
|
||||
*
|
||||
*/
|
||||
|
||||
/* FIXME we only want one one probe() not two */
|
||||
return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev);
|
||||
}
|
||||
|
||||
static int ehci_fsl_drv_remove(struct platform_device *pdev)
|
||||
static int fsl_ehci_drv_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
/* FIXME we only want one one remove() not two */
|
||||
usb_hcd_fsl_remove(hcd, pdev);
|
||||
if (!IS_ERR_OR_NULL(hcd->usb_phy)) {
|
||||
otg_set_host(hcd->usb_phy->otg, NULL);
|
||||
usb_put_phy(hcd->usb_phy);
|
||||
}
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
/*
|
||||
* do platform specific un-initialization:
|
||||
* release iomux pins, disable clock, etc.
|
||||
*/
|
||||
if (pdata->exit)
|
||||
pdata->exit(pdev);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_ALIAS("platform:fsl-ehci");
|
||||
|
||||
static struct platform_driver ehci_fsl_driver = {
|
||||
.probe = ehci_fsl_drv_probe,
|
||||
.remove = ehci_fsl_drv_remove,
|
||||
.probe = fsl_ehci_drv_probe,
|
||||
.remove = fsl_ehci_drv_remove,
|
||||
.shutdown = usb_hcd_platform_shutdown,
|
||||
.driver = {
|
||||
.name = "fsl-ehci",
|
||||
.pm = EHCI_FSL_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ehci_fsl_init(void)
|
||||
{
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
pr_info(DRV_NAME ": " DRIVER_DESC "\n");
|
||||
|
||||
ehci_init_driver(&fsl_ehci_hc_driver, &ehci_fsl_overrides);
|
||||
|
||||
fsl_ehci_hc_driver.product_desc =
|
||||
"Freescale On-Chip EHCI Host Controller";
|
||||
fsl_ehci_hc_driver.start_port_reset = ehci_start_port_reset;
|
||||
|
||||
|
||||
return platform_driver_register(&ehci_fsl_driver);
|
||||
}
|
||||
module_init(ehci_fsl_init);
|
||||
|
||||
static void __exit ehci_fsl_cleanup(void)
|
||||
{
|
||||
platform_driver_unregister(&ehci_fsl_driver);
|
||||
}
|
||||
module_exit(ehci_fsl_cleanup);
|
||||
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
|
@ -239,7 +239,7 @@ static void tdi_reset (struct ehci_hcd *ehci)
|
||||
* Reset a non-running (STS_HALT == 1) controller.
|
||||
* Must be called with interrupts enabled and the lock not held.
|
||||
*/
|
||||
static int ehci_reset (struct ehci_hcd *ehci)
|
||||
int ehci_reset(struct ehci_hcd *ehci)
|
||||
{
|
||||
int retval;
|
||||
u32 command = ehci_readl(ehci, &ehci->regs->command);
|
||||
@ -275,6 +275,7 @@ static int ehci_reset (struct ehci_hcd *ehci)
|
||||
ehci->resuming_ports = 0;
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ehci_reset);
|
||||
|
||||
/*
|
||||
* Idle the controller (turn off the schedules).
|
||||
@ -1250,11 +1251,6 @@ MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_AUTHOR (DRIVER_AUTHOR);
|
||||
MODULE_LICENSE ("GPL");
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_FSL
|
||||
#include "ehci-fsl.c"
|
||||
#define PLATFORM_DRIVER ehci_fsl_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_SH
|
||||
#include "ehci-sh.c"
|
||||
#define PLATFORM_DRIVER ehci_hcd_sh_driver
|
||||
|
@ -155,7 +155,7 @@ static int ehci_port_change(struct ehci_hcd *ehci)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||
void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||
bool suspending, bool do_wakeup)
|
||||
{
|
||||
int port;
|
||||
@ -220,6 +220,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||
|
||||
spin_unlock_irq(&ehci->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ehci_adjust_port_wakeup_flags);
|
||||
|
||||
static int ehci_bus_suspend (struct usb_hcd *hcd)
|
||||
{
|
||||
|
@ -88,15 +88,13 @@ static int ehci_platform_power_on(struct platform_device *dev)
|
||||
}
|
||||
|
||||
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
|
||||
if (priv->phys[phy_num]) {
|
||||
ret = phy_init(priv->phys[phy_num]);
|
||||
if (ret)
|
||||
goto err_exit_phy;
|
||||
ret = phy_power_on(priv->phys[phy_num]);
|
||||
if (ret) {
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
goto err_exit_phy;
|
||||
}
|
||||
ret = phy_init(priv->phys[phy_num]);
|
||||
if (ret)
|
||||
goto err_exit_phy;
|
||||
ret = phy_power_on(priv->phys[phy_num]);
|
||||
if (ret) {
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
goto err_exit_phy;
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,10 +102,8 @@ static int ehci_platform_power_on(struct platform_device *dev)
|
||||
|
||||
err_exit_phy:
|
||||
while (--phy_num >= 0) {
|
||||
if (priv->phys[phy_num]) {
|
||||
phy_power_off(priv->phys[phy_num]);
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
}
|
||||
phy_power_off(priv->phys[phy_num]);
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
}
|
||||
err_disable_clks:
|
||||
while (--clk >= 0)
|
||||
@ -123,10 +119,8 @@ static void ehci_platform_power_off(struct platform_device *dev)
|
||||
int clk, phy_num;
|
||||
|
||||
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
|
||||
if (priv->phys[phy_num]) {
|
||||
phy_power_off(priv->phys[phy_num]);
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
}
|
||||
phy_power_off(priv->phys[phy_num]);
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
}
|
||||
|
||||
for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
|
||||
@ -154,7 +148,6 @@ static int ehci_platform_probe(struct platform_device *dev)
|
||||
struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
struct ehci_platform_priv *priv;
|
||||
struct ehci_hcd *ehci;
|
||||
const char *phy_name;
|
||||
int err, irq, phy_num, clk = 0;
|
||||
|
||||
if (usb_disabled())
|
||||
@ -202,38 +195,28 @@ static int ehci_platform_probe(struct platform_device *dev)
|
||||
"needs-reset-on-resume"))
|
||||
pdata->reset_on_resume = 1;
|
||||
|
||||
if (of_property_read_bool(dev->dev.of_node,
|
||||
"has-transaction-translator"))
|
||||
pdata->has_tt = 1;
|
||||
|
||||
priv->num_phys = of_count_phandle_with_args(dev->dev.of_node,
|
||||
"phys", "#phy-cells");
|
||||
priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1;
|
||||
|
||||
priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
|
||||
sizeof(struct phy *), GFP_KERNEL);
|
||||
if (!priv->phys)
|
||||
return -ENOMEM;
|
||||
if (priv->num_phys > 0) {
|
||||
priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
|
||||
sizeof(struct phy *), GFP_KERNEL);
|
||||
if (!priv->phys)
|
||||
return -ENOMEM;
|
||||
} else
|
||||
priv->num_phys = 0;
|
||||
|
||||
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
|
||||
err = of_property_read_string_index(
|
||||
dev->dev.of_node,
|
||||
"phy-names", phy_num,
|
||||
&phy_name);
|
||||
|
||||
if (err < 0) {
|
||||
if (priv->num_phys > 1) {
|
||||
dev_err(&dev->dev, "phy-names not provided");
|
||||
goto err_put_hcd;
|
||||
} else
|
||||
phy_name = "usb";
|
||||
}
|
||||
|
||||
priv->phys[phy_num] = devm_phy_get(&dev->dev,
|
||||
phy_name);
|
||||
if (IS_ERR(priv->phys[phy_num])) {
|
||||
err = PTR_ERR(priv->phys[phy_num]);
|
||||
if ((priv->num_phys > 1) ||
|
||||
(err == -EPROBE_DEFER))
|
||||
goto err_put_hcd;
|
||||
priv->phys[phy_num] = NULL;
|
||||
}
|
||||
priv->phys[phy_num] = devm_of_phy_get_by_index(
|
||||
&dev->dev, dev->dev.of_node, phy_num);
|
||||
if (IS_ERR(priv->phys[phy_num])) {
|
||||
err = PTR_ERR(priv->phys[phy_num]);
|
||||
goto err_put_hcd;
|
||||
}
|
||||
}
|
||||
|
||||
for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
|
||||
|
@ -304,6 +304,7 @@ struct dma_aligned_buffer {
|
||||
static void free_dma_aligned_buffer(struct urb *urb)
|
||||
{
|
||||
struct dma_aligned_buffer *temp;
|
||||
size_t length;
|
||||
|
||||
if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER))
|
||||
return;
|
||||
@ -311,9 +312,14 @@ static void free_dma_aligned_buffer(struct urb *urb)
|
||||
temp = container_of(urb->transfer_buffer,
|
||||
struct dma_aligned_buffer, data);
|
||||
|
||||
if (usb_urb_dir_in(urb))
|
||||
memcpy(temp->old_xfer_buffer, temp->data,
|
||||
urb->transfer_buffer_length);
|
||||
if (usb_urb_dir_in(urb)) {
|
||||
if (usb_pipeisoc(urb->pipe))
|
||||
length = urb->transfer_buffer_length;
|
||||
else
|
||||
length = urb->actual_length;
|
||||
|
||||
memcpy(temp->old_xfer_buffer, temp->data, length);
|
||||
}
|
||||
urb->transfer_buffer = temp->old_xfer_buffer;
|
||||
kfree(temp->kmalloc_ptr);
|
||||
|
||||
|
@ -868,10 +868,13 @@ extern void ehci_init_driver(struct hc_driver *drv,
|
||||
extern int ehci_setup(struct usb_hcd *hcd);
|
||||
extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr,
|
||||
u32 mask, u32 done, int usec);
|
||||
extern int ehci_reset(struct ehci_hcd *ehci);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup);
|
||||
extern int ehci_resume(struct usb_hcd *hcd, bool force_reset);
|
||||
extern void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||
bool suspending, bool do_wakeup);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||
|
@ -126,6 +126,8 @@ static int usb_get_ver_info(struct device_node *np)
|
||||
/*
|
||||
* returns 1 for usb controller version 1.6
|
||||
* returns 2 for usb controller version 2.2
|
||||
* returns 3 for usb controller version 2.4
|
||||
* returns 4 for usb controller version 2.5
|
||||
* returns 0 otherwise
|
||||
*/
|
||||
if (of_device_is_compatible(np, "fsl-usb2-dr")) {
|
||||
@ -135,6 +137,8 @@ static int usb_get_ver_info(struct device_node *np)
|
||||
ver = FSL_USB_VER_2_2;
|
||||
else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.4"))
|
||||
ver = FSL_USB_VER_2_4;
|
||||
else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.5"))
|
||||
ver = FSL_USB_VER_2_5;
|
||||
else /* for previous controller versions */
|
||||
ver = FSL_USB_VER_OLD;
|
||||
|
||||
@ -150,6 +154,10 @@ static int usb_get_ver_info(struct device_node *np)
|
||||
ver = FSL_USB_VER_1_6;
|
||||
else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.2"))
|
||||
ver = FSL_USB_VER_2_2;
|
||||
else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.4"))
|
||||
ver = FSL_USB_VER_2_4;
|
||||
else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.5"))
|
||||
ver = FSL_USB_VER_2_5;
|
||||
else /* for previous controller versions */
|
||||
ver = FSL_USB_VER_OLD;
|
||||
}
|
||||
|
@ -499,7 +499,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
||||
unsigned i;
|
||||
__hc32 tag;
|
||||
|
||||
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC)))
|
||||
seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC);
|
||||
if (!seen)
|
||||
return 0;
|
||||
seen_count = 0;
|
||||
|
||||
|
@ -500,7 +500,8 @@ static void start_atl_transfers(struct isp116x *isp116x)
|
||||
if (isp116x->periodic_count) {
|
||||
isp116x->fmindex = index =
|
||||
(isp116x->fmindex + 1) & (PERIODIC_SIZE - 1);
|
||||
if ((load = isp116x->load[index])) {
|
||||
load = isp116x->load[index];
|
||||
if (load) {
|
||||
/* Bring all int transfers for this frame
|
||||
into the active queue */
|
||||
isp116x->atl_active = last_ep =
|
||||
|
@ -491,7 +491,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
||||
char *next;
|
||||
unsigned i;
|
||||
|
||||
if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC)))
|
||||
seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC);
|
||||
if (!seen)
|
||||
return 0;
|
||||
seen_count = 0;
|
||||
|
||||
@ -506,7 +507,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
|
||||
/* dump a snapshot of the periodic schedule (and load) */
|
||||
spin_lock_irqsave (&ohci->lock, flags);
|
||||
for (i = 0; i < NUM_INTS; i++) {
|
||||
if (!(ed = ohci->periodic [i]))
|
||||
ed = ohci->periodic[i];
|
||||
if (!ed)
|
||||
continue;
|
||||
|
||||
temp = scnprintf (next, size, "%2d [%3d]:", i, ohci->load [i]);
|
||||
|
@ -155,7 +155,8 @@ static int ohci_urb_enqueue (
|
||||
int retval = 0;
|
||||
|
||||
/* every endpoint has a ed, locate and maybe (re)initialize it */
|
||||
if (! (ed = ed_get (ohci, urb->ep, urb->dev, pipe, urb->interval)))
|
||||
ed = ed_get(ohci, urb->ep, urb->dev, pipe, urb->interval);
|
||||
if (! ed)
|
||||
return -ENOMEM;
|
||||
|
||||
/* for the private part of the URB we need the number of TDs (size) */
|
||||
|
@ -57,15 +57,13 @@ static int ohci_platform_power_on(struct platform_device *dev)
|
||||
}
|
||||
|
||||
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
|
||||
if (priv->phys[phy_num]) {
|
||||
ret = phy_init(priv->phys[phy_num]);
|
||||
if (ret)
|
||||
goto err_exit_phy;
|
||||
ret = phy_power_on(priv->phys[phy_num]);
|
||||
if (ret) {
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
goto err_exit_phy;
|
||||
}
|
||||
ret = phy_init(priv->phys[phy_num]);
|
||||
if (ret)
|
||||
goto err_exit_phy;
|
||||
ret = phy_power_on(priv->phys[phy_num]);
|
||||
if (ret) {
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
goto err_exit_phy;
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,10 +71,8 @@ static int ohci_platform_power_on(struct platform_device *dev)
|
||||
|
||||
err_exit_phy:
|
||||
while (--phy_num >= 0) {
|
||||
if (priv->phys[phy_num]) {
|
||||
phy_power_off(priv->phys[phy_num]);
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
}
|
||||
phy_power_off(priv->phys[phy_num]);
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
}
|
||||
err_disable_clks:
|
||||
while (--clk >= 0)
|
||||
@ -92,10 +88,8 @@ static void ohci_platform_power_off(struct platform_device *dev)
|
||||
int clk, phy_num;
|
||||
|
||||
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
|
||||
if (priv->phys[phy_num]) {
|
||||
phy_power_off(priv->phys[phy_num]);
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
}
|
||||
phy_power_off(priv->phys[phy_num]);
|
||||
phy_exit(priv->phys[phy_num]);
|
||||
}
|
||||
|
||||
for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--)
|
||||
@ -123,7 +117,6 @@ static int ohci_platform_probe(struct platform_device *dev)
|
||||
struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
|
||||
struct ohci_platform_priv *priv;
|
||||
struct ohci_hcd *ohci;
|
||||
const char *phy_name;
|
||||
int err, irq, phy_num, clk = 0;
|
||||
|
||||
if (usb_disabled())
|
||||
@ -174,36 +167,22 @@ static int ohci_platform_probe(struct platform_device *dev)
|
||||
|
||||
priv->num_phys = of_count_phandle_with_args(dev->dev.of_node,
|
||||
"phys", "#phy-cells");
|
||||
priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1;
|
||||
|
||||
priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
|
||||
sizeof(struct phy *), GFP_KERNEL);
|
||||
if (!priv->phys)
|
||||
return -ENOMEM;
|
||||
if (priv->num_phys > 0) {
|
||||
priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
|
||||
sizeof(struct phy *), GFP_KERNEL);
|
||||
if (!priv->phys)
|
||||
return -ENOMEM;
|
||||
} else
|
||||
priv->num_phys = 0;
|
||||
|
||||
for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
|
||||
err = of_property_read_string_index(
|
||||
dev->dev.of_node,
|
||||
"phy-names", phy_num,
|
||||
&phy_name);
|
||||
|
||||
if (err < 0) {
|
||||
if (priv->num_phys > 1) {
|
||||
dev_err(&dev->dev, "phy-names not provided");
|
||||
goto err_put_hcd;
|
||||
} else
|
||||
phy_name = "usb";
|
||||
}
|
||||
|
||||
priv->phys[phy_num] = devm_phy_get(&dev->dev,
|
||||
phy_name);
|
||||
if (IS_ERR(priv->phys[phy_num])) {
|
||||
err = PTR_ERR(priv->phys[phy_num]);
|
||||
if ((priv->num_phys > 1) ||
|
||||
(err == -EPROBE_DEFER))
|
||||
goto err_put_hcd;
|
||||
priv->phys[phy_num] = NULL;
|
||||
}
|
||||
priv->phys[phy_num] = devm_of_phy_get_by_index(
|
||||
&dev->dev, dev->dev.of_node, phy_num);
|
||||
if (IS_ERR(priv->phys[phy_num])) {
|
||||
err = PTR_ERR(priv->phys[phy_num]);
|
||||
goto err_put_hcd;
|
||||
}
|
||||
}
|
||||
|
||||
for (clk = 0; clk < OHCI_MAX_CLKS; clk++) {
|
||||
|
@ -407,7 +407,8 @@ static struct ed *ed_get (
|
||||
|
||||
spin_lock_irqsave (&ohci->lock, flags);
|
||||
|
||||
if (!(ed = ep->hcpriv)) {
|
||||
ed = ep->hcpriv;
|
||||
if (!ed) {
|
||||
struct td *td;
|
||||
int is_out;
|
||||
u32 info;
|
||||
|
@ -105,7 +105,7 @@ static struct platform_device *ssb_hcd_create_pdev(struct ssb_device *dev, bool
|
||||
{
|
||||
struct platform_device *hci_dev;
|
||||
struct resource hci_res[2];
|
||||
int ret = -ENOMEM;
|
||||
int ret;
|
||||
|
||||
memset(hci_res, 0, sizeof(hci_res));
|
||||
|
||||
@ -119,7 +119,7 @@ static struct platform_device *ssb_hcd_create_pdev(struct ssb_device *dev, bool
|
||||
hci_dev = platform_device_alloc(ohci ? "ohci-platform" :
|
||||
"ehci-platform" , 0);
|
||||
if (!hci_dev)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
hci_dev->dev.parent = dev->dev;
|
||||
hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask;
|
||||
@ -166,7 +166,8 @@ static int ssb_hcd_probe(struct ssb_device *dev,
|
||||
if (dma_set_mask_and_coherent(dev->dma_dev, DMA_BIT_MASK(32)))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
usb_dev = kzalloc(sizeof(struct ssb_hcd_device), GFP_KERNEL);
|
||||
usb_dev = devm_kzalloc(dev->dev, sizeof(struct ssb_hcd_device),
|
||||
GFP_KERNEL);
|
||||
if (!usb_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -181,10 +182,8 @@ static int ssb_hcd_probe(struct ssb_device *dev,
|
||||
start = ssb_admatch_base(tmp);
|
||||
len = (coreid == SSB_DEV_USB20_HOST) ? 0x800 : ssb_admatch_size(tmp);
|
||||
usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, true, start, len);
|
||||
if (IS_ERR(usb_dev->ohci_dev)) {
|
||||
err = PTR_ERR(usb_dev->ohci_dev);
|
||||
goto err_free_usb_dev;
|
||||
}
|
||||
if (IS_ERR(usb_dev->ohci_dev))
|
||||
return PTR_ERR(usb_dev->ohci_dev);
|
||||
|
||||
if (coreid == SSB_DEV_USB20_HOST) {
|
||||
start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */
|
||||
@ -200,8 +199,6 @@ static int ssb_hcd_probe(struct ssb_device *dev,
|
||||
|
||||
err_unregister_ohci_dev:
|
||||
platform_device_unregister(usb_dev->ohci_dev);
|
||||
err_free_usb_dev:
|
||||
kfree(usb_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1184,6 +1184,10 @@ int xhci_bus_resume(struct usb_hcd *hcd)
|
||||
struct xhci_bus_state *bus_state;
|
||||
u32 temp;
|
||||
unsigned long flags;
|
||||
unsigned long port_was_suspended = 0;
|
||||
bool need_usb2_u3_exit = false;
|
||||
int slot_id;
|
||||
int sret;
|
||||
|
||||
max_ports = xhci_get_ports(hcd, &port_array);
|
||||
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
||||
@ -1207,7 +1211,6 @@ int xhci_bus_resume(struct usb_hcd *hcd)
|
||||
/* Check whether need resume ports. If needed
|
||||
resume port and disable remote wakeup */
|
||||
u32 temp;
|
||||
int slot_id;
|
||||
|
||||
temp = readl(port_array[port_index]);
|
||||
if (DEV_SUPERSPEED(temp))
|
||||
@ -1216,39 +1219,47 @@ int xhci_bus_resume(struct usb_hcd *hcd)
|
||||
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
|
||||
if (test_bit(port_index, &bus_state->bus_suspended) &&
|
||||
(temp & PORT_PLS_MASK)) {
|
||||
if (DEV_SUPERSPEED(temp)) {
|
||||
xhci_set_link_state(xhci, port_array,
|
||||
port_index, XDEV_U0);
|
||||
} else {
|
||||
set_bit(port_index, &port_was_suspended);
|
||||
if (!DEV_SUPERSPEED(temp)) {
|
||||
xhci_set_link_state(xhci, port_array,
|
||||
port_index, XDEV_RESUME);
|
||||
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
msleep(20);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
xhci_set_link_state(xhci, port_array,
|
||||
port_index, XDEV_U0);
|
||||
need_usb2_u3_exit = true;
|
||||
}
|
||||
/* wait for the port to enter U0 and report port link
|
||||
* state change.
|
||||
*/
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
msleep(20);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
/* Clear PLC */
|
||||
xhci_test_and_clear_bit(xhci, port_array, port_index,
|
||||
PORT_PLC);
|
||||
|
||||
slot_id = xhci_find_slot_id_by_port(hcd,
|
||||
xhci, port_index + 1);
|
||||
if (slot_id)
|
||||
xhci_ring_device(xhci, slot_id);
|
||||
} else
|
||||
writel(temp, port_array[port_index]);
|
||||
}
|
||||
|
||||
if (need_usb2_u3_exit) {
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
msleep(20);
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
}
|
||||
|
||||
port_index = max_ports;
|
||||
while (port_index--) {
|
||||
if (!(port_was_suspended & BIT(port_index)))
|
||||
continue;
|
||||
/* Clear PLC to poll it later after XDEV_U0 */
|
||||
xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC);
|
||||
xhci_set_link_state(xhci, port_array, port_index, XDEV_U0);
|
||||
}
|
||||
|
||||
port_index = max_ports;
|
||||
while (port_index--) {
|
||||
if (!(port_was_suspended & BIT(port_index)))
|
||||
continue;
|
||||
/* Poll and Clear PLC */
|
||||
sret = xhci_handshake(port_array[port_index], PORT_PLC,
|
||||
PORT_PLC, 10 * 1000);
|
||||
if (sret)
|
||||
xhci_warn(xhci, "port %d resume PLC timeout\n",
|
||||
port_index);
|
||||
xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC);
|
||||
slot_id = xhci_find_slot_id_by_port(hcd, xhci, port_index + 1);
|
||||
if (slot_id)
|
||||
xhci_ring_device(xhci, slot_id);
|
||||
}
|
||||
|
||||
(void) readl(&xhci->op_regs->command);
|
||||
|
||||
bus_state->next_statechange = jiffies + msecs_to_jiffies(5);
|
||||
|
@ -45,6 +45,13 @@ static const char hcd_name[] = "xhci_hcd";
|
||||
|
||||
static struct hc_driver __read_mostly xhci_pci_hc_driver;
|
||||
|
||||
static int xhci_pci_setup(struct usb_hcd *hcd);
|
||||
|
||||
static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
|
||||
.extra_priv_size = sizeof(struct xhci_hcd),
|
||||
.reset = xhci_pci_setup,
|
||||
};
|
||||
|
||||
/* called after powerup, by probe or system-pm "wakeup" */
|
||||
static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
|
||||
{
|
||||
@ -206,7 +213,6 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
|
||||
if (!retval)
|
||||
return retval;
|
||||
|
||||
kfree(xhci);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -247,11 +253,6 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
goto dealloc_usb2_hcd;
|
||||
}
|
||||
|
||||
/* Set the xHCI pointer before xhci_pci_setup() (aka hcd_driver.reset)
|
||||
* is called by usb_add_hcd().
|
||||
*/
|
||||
*((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
|
||||
|
||||
retval = usb_add_hcd(xhci->shared_hcd, dev->irq,
|
||||
IRQF_SHARED);
|
||||
if (retval)
|
||||
@ -290,8 +291,6 @@ static void xhci_pci_remove(struct pci_dev *dev)
|
||||
/* Workaround for spurious wakeups at shutdown with HSW */
|
||||
if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
|
||||
pci_set_power_state(dev, PCI_D3hot);
|
||||
|
||||
kfree(xhci);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
@ -379,7 +378,7 @@ static struct pci_driver xhci_pci_driver = {
|
||||
|
||||
static int __init xhci_pci_init(void)
|
||||
{
|
||||
xhci_init_driver(&xhci_pci_hc_driver, xhci_pci_setup);
|
||||
xhci_init_driver(&xhci_pci_hc_driver, &xhci_pci_overrides);
|
||||
#ifdef CONFIG_PM
|
||||
xhci_pci_hc_driver.pci_suspend = xhci_pci_suspend;
|
||||
xhci_pci_hc_driver.pci_resume = xhci_pci_resume;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user