USB driver update for 3.13-rc1

Here's the big USB driver update for 3.13-rc1.
 
 It includes the usual xhci changes, EHCI updates to get the scheduling
 of USB transactions working better, and a raft of gadget and musb
 updates as well.
 
 All of this has been in linux-next for a while with no reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.19 (GNU/Linux)
 
 iEYEABECAAYFAlJ6xycACgkQMUfUDdst+ymO+gCgxXdQXSU23i9ykc2CKBemdEBH
 w6IAoKcokITcdN1IxxkfiMxOEld2hgZm
 =3kbb
 -----END PGP SIGNATURE-----

Merge tag 'usb-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB driver update from Greg KH:
 "Here's the big USB driver update for 3.13-rc1.

  It includes the usual xhci changes, EHCI updates to get the scheduling
  of USB transactions working better, and a raft of gadget and musb
  updates as well.

  All of this has been in linux-next for a while with no reported
  issues"

* tag 'usb-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (305 commits)
  USB: Maintainers change for usb serial drivers
  usb: usbtest: support container id descriptor test
  usb: usbtest: support superspeed device capbility descriptor test
  usb: usbtest: support usb2 extension descriptor test
  usb: chipidea: only get vbus regulator for non-peripheral mode
  USB: ehci-atmel: add usb_clk for transition to CCF
  usb: cdc-wdm: ignore speed change notifications
  USB: cdc-wdm: support back-to-back USB_CDC_NOTIFY_RESPONSE_AVAILABLE notifications
  usbatm: Fix dynamic_debug / ratelimited atm_dbg and atm_rldbg macros
  printk: pr_debug_ratelimited: check state first to reduce "callbacks suppressed" messages
  usb: usbtest: support bos descriptor test for usb 3.0
  USB: phy: samsung: Support multiple PHYs of same type
  usb: wusbcore: change WA_SEGS_MAX to a legal value
  usb: wusbcore: add a quirk for Alereon HWA device isoc behavior
  usb: wusbcore: combine multiple isoc frames in a single transfer request.
  usb: wusbcore: set the RPIPE wMaxPacketSize value correctly
  usb: chipidea: host: more enhancement when ci->hcd is NULL
  usb: ohci: remove ep93xx bus glue platform driver
  usb: usbtest: fix checkpatch warning as sizeof code style
  UWB: clean up attribute use by using ATTRIBUTE_GROUPS()
  ...
This commit is contained in:
Linus Torvalds 2013-11-07 09:19:48 +09:00
commit c287322c3a
217 changed files with 9056 additions and 4974 deletions

View File

@ -0,0 +1,31 @@
What: /config/usb-gadget/gadget/functions/mass_storage.name
Date: Oct 2013
KenelVersion: 3.13
Description:
The attributes:
stall - Set to permit function to halt bulk endpoints.
Disabled on some USB devices known not to work
correctly. You should set it to true.
num_buffers - Number of pipeline buffers. Valid numbers
are 2..4. Available only if
CONFIG_USB_GADGET_DEBUG_FILES is set.
What: /config/usb-gadget/gadget/functions/mass_storage.name/lun.name
Date: Oct 2013
KenelVersion: 3.13
Description:
The attributes:
file - The path to the backing file for the LUN.
Required if LUN is not marked as removable.
ro - Flag specifying access to the LUN shall be
read-only. This is implied if CD-ROM emulation
is enabled as well as when it was impossible
to open "filename" in R/W mode.
removable - Flag specifying that LUN shall be indicated as
being removable.
cdrom - Flag specifying that LUN shall be reported as
being a CD-ROM.
nofua - Flag specifying that FUA flag
in SCSI WRITE(10,12)

View File

@ -0,0 +1,66 @@
This document explains only the device tree data binding. For general
information about PHY subsystem refer to Documentation/phy.txt
PHY device node
===============
Required Properties:
#phy-cells: Number of cells in a PHY specifier; The meaning of all those
cells is defined by the binding for the phy node. The PHY
provider can use the values in cells to find the appropriate
PHY.
For example:
phys: phy {
compatible = "xxx";
reg = <...>;
.
.
#phy-cells = <1>;
.
.
};
That node describes an IP block (PHY provider) that implements 2 different PHYs.
In order to differentiate between these 2 PHYs, an additonal specifier should be
given while trying to get a reference to it.
PHY user node
=============
Required Properties:
phys : the phandle for the PHY device (used by the PHY subsystem)
phy-names : the names of the PHY corresponding to the PHYs present in the
*phys* phandle
Example 1:
usb1: usb_otg_ss@xxx {
compatible = "xxx";
reg = <xxx>;
.
.
phys = <&usb2_phy>, <&usb3_phy>;
phy-names = "usb2phy", "usb3phy";
.
.
};
This node represents a controller that uses two PHYs, one for usb2 and one for
usb3.
Example 2:
usb2: usb_otg_ss@xxx {
compatible = "xxx";
reg = <xxx>;
.
.
phys = <&phys 1>;
phy-names = "usbphy";
.
.
};
This node represents a controller that uses one of the PHYs of the PHY provider
device defined previously. Note that the phy handle has an additional specifier
"1" to differentiate between the two PHYs.

View File

@ -0,0 +1,22 @@
Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY
-------------------------------------------------
Required properties:
- compatible : should be "samsung,s5pv210-mipi-video-phy";
- reg : offset and length of the MIPI DPHY register set;
- #phy-cells : from the generic phy bindings, must be 1;
For "samsung,s5pv210-mipi-video-phy" compatible PHYs the second cell in
the PHY specifier identifies the PHY and its meaning is as follows:
0 - MIPI CSIS 0,
1 - MIPI DSIM 0,
2 - MIPI CSIS 1,
3 - MIPI DSIM 1.
Samsung EXYNOS SoC series Display Port PHY
-------------------------------------------------
Required properties:
- compatible : should be "samsung,exynos5250-dp-video-phy";
- reg : offset and length of the Display Port PHY register set;
- #phy-cells : from the generic PHY bindings, must be 0;

View File

@ -0,0 +1,17 @@
MSM SoC HSUSB controllers
EHCI
Required properties:
- compatible: Should contain "qcom,ehci-host"
- regs: offset and length of the register set in the memory map
- usb-phy: phandle for the PHY device
Example EHCI controller device node:
ehci: ehci@f9a55000 {
compatible = "qcom,ehci-host";
reg = <0xf9a55000 0x400>;
usb-phy = <&usb_otg>;
};

View File

@ -3,9 +3,6 @@ OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS
OMAP MUSB GLUE
- compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
- ti,hwmods : must be "usb_otg_hs"
- ti,has-mailbox : to specify that omap uses an external mailbox
(in control module) to communicate with the musb core during device connect
and disconnect.
- multipoint : Should be "1" indicating the musb controller supports
multipoint. This is a MUSB configuration-specific setting.
- num-eps : Specifies the number of endpoints. This is also a
@ -19,6 +16,9 @@ OMAP MUSB GLUE
- power : Should be "50". This signifies the controller can supply up to
100mA when operating in host mode.
- usb-phy : the phandle for the PHY device
- phys : the phandle for the PHY device (used by generic PHY framework)
- phy-names : the names of the PHY corresponding to the PHYs present in the
*phy* phandle.
Optional properties:
- ctrl-module : phandle of the control module this glue uses to write to
@ -28,11 +28,12 @@ SOC specific device node entry
usb_otg_hs: usb_otg_hs@4a0ab000 {
compatible = "ti,omap4-musb";
ti,hwmods = "usb_otg_hs";
ti,has-mailbox;
multipoint = <1>;
num-eps = <16>;
ram-bits = <12>;
ctrl-module = <&omap_control_usb>;
phys = <&usb2_phy>;
phy-names = "usb2-phy";
};
Board specific device node entry
@ -78,22 +79,22 @@ omap_dwc3 {
OMAP CONTROL USB
Required properties:
- compatible: Should be "ti,omap-control-usb"
- compatible: Should be one of
"ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4.
"ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register
e.g. USB2_PHY on OMAP5.
"ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
e.g. USB3 PHY and SATA PHY on OMAP5.
"ti,control-phy-dra7usb2" - if it has power down register like USB2 PHY on
DRA7 platform.
- reg : Address and length of the register set for the device. It contains
the address of "control_dev_conf" and "otghs_control" or "phy_power_usb"
depending upon omap4 or omap5.
- reg-names: The names of the register addresses corresponding to the registers
filled in "reg".
- ti,type: This is used to differentiate whether the control module has
usb mailbox or usb3 phy power. omap4 has usb mailbox in control module to
notify events to the musb core and omap5 has usb3 phy power register to
power on usb3 phy. Should be "1" if it has mailbox and "2" if it has usb3
phy power.
the address of "otghs_control" for control-phy-otghs or "power" register
for other types.
- reg-names: should be "otghs_control" control-phy-otghs and "power" for
other types.
omap_control_usb: omap-control-usb@4a002300 {
compatible = "ti,omap-control-usb";
reg = <0x4a002300 0x4>,
<0x4a00233c 0x4>;
reg-names = "control_dev_conf", "otghs_control";
ti,type = <1>;
compatible = "ti,control-phy-otghs";
reg = <0x4a00233c 0x4>;
reg-names = "otghs_control";
};

View File

@ -15,7 +15,7 @@ Optional properties:
- vcc-supply: phandle to the regulator that provides RESET to the PHY.
- reset-supply: phandle to the regulator that provides power to the PHY.
- reset-gpios: Should specify the GPIO for reset.
Example:
@ -25,10 +25,9 @@ Example:
clocks = <&osc 0>;
clock-names = "main_clk";
vcc-supply = <&hsusb1_vcc_regulator>;
reset-supply = <&hsusb1_reset_regulator>;
reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
};
hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator
and expects that clock to be configured to 19.2MHz by the NOP PHY driver.
hsusb1_vcc_regulator provides power to the PHY and hsusb1_reset_regulator
controls RESET.
hsusb1_vcc_regulator provides power to the PHY and GPIO 7 controls RESET.

View File

@ -5,6 +5,8 @@ OMAP USB2 PHY
Required properties:
- compatible: Should be "ti,omap-usb2"
- reg : Address and length of the register set for the device.
- #phy-cells: determine the number of cells that should be given in the
phandle while referencing this phy.
Optional properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
@ -16,6 +18,7 @@ usb2phy@4a0ad080 {
compatible = "ti,omap-usb2";
reg = <0x4a0ad080 0x58>;
ctrl-module = <&omap_control_usb>;
#phy-cells = <0>;
};
OMAP USB3 PHY
@ -25,6 +28,8 @@ Required properties:
- reg : Address and length of the register set for the device.
- reg-names: The names of the register addresses corresponding to the registers
filled in "reg".
- #phy-cells: determine the number of cells that should be given in the
phandle while referencing this phy.
Optional properties:
- ctrl-module : phandle of the control module used by PHY driver to power on
@ -39,4 +44,5 @@ usb3phy@4a084400 {
<0x4a084c00 0x40>;
reg-names = "phy_rx", "phy_tx", "pll_ctrl";
ctrl-module = <&omap_control_usb>;
#phy-cells = <0>;
};

View File

@ -6,10 +6,10 @@ We use two nodes:
-dptx-phy node(defined inside dp-controller node)
For the DP-PHY initialization, we use the dptx-phy node.
Required properties for dptx-phy:
-reg:
Required properties for dptx-phy: deprecated, use phys and phy-names
-reg: deprecated
Base address of DP PHY register.
-samsung,enable-mask:
-samsung,enable-mask: deprecated
The bit-mask used to enable/disable DP PHY.
For the Panel initialization, we read data from dp-controller node.
@ -27,6 +27,10 @@ Required properties for dp-controller:
from common clock binding: Shall be "dp".
-interrupt-parent:
phandle to Interrupt combiner node.
-phys:
from general PHY binding: the phandle for the PHY device.
-phy-names:
from general PHY binding: Should be "dp".
-samsung,color-space:
input video data format.
COLOR_RGB = 0, COLOR_YCBCR422 = 1, COLOR_YCBCR444 = 2
@ -68,11 +72,8 @@ SOC specific portion:
clocks = <&clock 342>;
clock-names = "dp";
dptx-phy {
reg = <0x10040720>;
samsung,enable-mask = <1>;
};
phys = <&dp_phy>;
phy-names = "dp";
};
Board Specific portion:

166
Documentation/phy.txt Normal file
View File

@ -0,0 +1,166 @@
PHY SUBSYSTEM
Kishon Vijay Abraham I <kishon@ti.com>
This document explains the Generic PHY Framework along with the APIs provided,
and how-to-use.
1. Introduction
*PHY* is the abbreviation for physical layer. It is used to connect a device
to the physical medium e.g., the USB controller has a PHY to provide functions
such as serialization, de-serialization, encoding, decoding and is responsible
for obtaining the required data transmission rate. Note that some USB
controllers have PHY functionality embedded into it and others use an external
PHY. Other peripherals that use PHY include Wireless LAN, Ethernet,
SATA etc.
The intention of creating this framework is to bring the PHY drivers spread
all over the Linux kernel to drivers/phy to increase code re-use and for
better code maintainability.
This framework will be of use only to devices that use external PHY (PHY
functionality is not embedded within the controller).
2. Registering/Unregistering the PHY provider
PHY provider refers to an entity that implements one or more PHY instances.
For the simple case where the PHY provider implements only a single instance of
the PHY, the framework provides its own implementation of of_xlate in
of_phy_simple_xlate. If the PHY provider implements multiple instances, it
should provide its own implementation of of_xlate. of_xlate is used only for
dt boot case.
#define of_phy_provider_register(dev, xlate) \
__of_phy_provider_register((dev), THIS_MODULE, (xlate))
#define devm_of_phy_provider_register(dev, xlate) \
__devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
of_phy_provider_register and devm_of_phy_provider_register macros can be used to
register the phy_provider and it takes device and of_xlate as
arguments. For the dt boot case, all PHY providers should use one of the above
2 macros to register the PHY provider.
void devm_of_phy_provider_unregister(struct device *dev,
struct phy_provider *phy_provider);
void of_phy_provider_unregister(struct phy_provider *phy_provider);
devm_of_phy_provider_unregister and of_phy_provider_unregister can be used to
unregister the PHY.
3. Creating the PHY
The PHY driver should create the PHY in order for other peripheral controllers
to make use of it. The PHY framework provides 2 APIs to create the PHY.
struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
struct phy_init_data *init_data);
struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
struct phy_init_data *init_data);
The PHY drivers can use one of the above 2 APIs to create the PHY by passing
the device pointer, phy ops and init_data.
phy_ops is a set of function pointers for performing PHY operations such as
init, exit, power_on and power_off. *init_data* is mandatory to get a reference
to the PHY in the case of non-dt boot. See section *Board File Initialization*
on how init_data should be used.
Inorder to dereference the private data (in phy_ops), the phy provider driver
can use phy_set_drvdata() after creating the PHY and use phy_get_drvdata() in
phy_ops to get back the private data.
4. Getting a reference to the PHY
Before the controller can make use of the PHY, it has to get a reference to
it. This framework provides the following APIs to get a reference to the PHY.
struct phy *phy_get(struct device *dev, const char *string);
struct phy *devm_phy_get(struct device *dev, const char *string);
phy_get and devm_phy_get can be used to get the PHY. In the case of dt boot,
the string arguments should contain the phy name as given in the dt data and
in the case of non-dt boot, it should contain the label of the PHY.
The only difference between the two APIs is that devm_phy_get associates the
device with the PHY using devres on successful PHY get. On driver detach,
release function is invoked on the the devres data and devres data is freed.
5. Releasing a reference to the PHY
When the controller no longer needs the PHY, it has to release the reference
to the PHY it has obtained using the APIs mentioned in the above section. The
PHY framework provides 2 APIs to release a reference to the PHY.
void phy_put(struct phy *phy);
void devm_phy_put(struct device *dev, struct phy *phy);
Both these APIs are used to release a reference to the PHY and devm_phy_put
destroys the devres associated with this PHY.
6. Destroying the PHY
When the driver that created the PHY is unloaded, it should destroy the PHY it
created using one of the following 2 APIs.
void phy_destroy(struct phy *phy);
void devm_phy_destroy(struct device *dev, struct phy *phy);
Both these APIs destroy the PHY and devm_phy_destroy destroys the devres
associated with this PHY.
7. PM Runtime
This subsystem is pm runtime enabled. So while creating the PHY,
pm_runtime_enable of the phy device created by this subsystem is called and
while destroying the PHY, pm_runtime_disable is called. Note that the phy
device created by this subsystem will be a child of the device that calls
phy_create (PHY provider device).
So pm_runtime_get_sync of the phy_device created by this subsystem will invoke
pm_runtime_get_sync of PHY provider device because of parent-child relationship.
It should also be noted that phy_power_on and phy_power_off performs
phy_pm_runtime_get_sync and phy_pm_runtime_put respectively.
There are exported APIs like phy_pm_runtime_get, phy_pm_runtime_get_sync,
phy_pm_runtime_put, phy_pm_runtime_put_sync, phy_pm_runtime_allow and
phy_pm_runtime_forbid for performing PM operations.
8. Board File Initialization
Certain board file initialization is necessary in order to get a reference
to the PHY in the case of non-dt boot.
Say we have a single device that implements 3 PHYs that of USB, SATA and PCIe,
then in the board file the following initialization should be done.
struct phy_consumer consumers[] = {
PHY_CONSUMER("dwc3.0", "usb"),
PHY_CONSUMER("pcie.0", "pcie"),
PHY_CONSUMER("sata.0", "sata"),
};
PHY_CONSUMER takes 2 parameters, first is the device name of the controller
(PHY consumer) and second is the port name.
struct phy_init_data init_data = {
.consumers = consumers,
.num_consumers = ARRAY_SIZE(consumers),
};
static const struct platform_device pipe3_phy_dev = {
.name = "pipe3-phy",
.id = -1,
.dev = {
.platform_data = {
.init_data = &init_data,
},
},
};
then, while doing phy_create, the PHY driver should pass this init_data
phy_create(dev, ops, pdata->init_data);
and the controller driver (phy consumer) should pass the port name along with
the device to get a reference to the PHY
phy_get(dev, "pcie");
9. DeviceTree Binding
The documentation for PHY dt binding can be found @
Documentation/devicetree/bindings/phy/phy-bindings.txt

View File

@ -66,6 +66,21 @@ In LinuxPPS the PPS sources are simply char devices usually mapped
into files /dev/pps0, /dev/pps1, etc..
PPS with USB to serial devices
------------------------------
It is possible to grab the PPS from an USB to serial device. However,
you should take into account the latencies and jitter introduced by
the USB stack. Users has reported clock instability around +-1ms when
synchronized with PPS through USB. This isn't suited for time server
synchronization.
If your device doesn't report PPS, you can check that the feature is
supported by its driver. Most of the time, you only need to add a call
to usb_serial_handle_dcd_change after checking the DCD status (see
ch341 and pl2303 examples).
Coding example
--------------

View File

@ -3691,6 +3691,14 @@ S: Maintained
F: include/asm-generic/
F: include/uapi/asm-generic/
GENERIC PHY FRAMEWORK
M: Kishon Vijay Abraham I <kishon@ti.com>
L: linux-kernel@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy.git
S: Supported
F: drivers/phy/
F: include/linux/phy/
GENERIC UIO DRIVER FOR PCI DEVICES
M: "Michael S. Tsirkin" <mst@redhat.com>
L: kvm@vger.kernel.org

View File

@ -144,6 +144,8 @@
&usb_otg_hs {
interface-type = <0>;
usb-phy = <&usb2_phy>;
phys = <&usb2_phy>;
phy-names = "usb2-phy";
mode = <3>;
power = <50>;
};

View File

@ -70,6 +70,8 @@
&usb_otg_hs {
interface-type = <0>;
usb-phy = <&usb2_phy>;
phys = <&usb2_phy>;
phy-names = "usb2-phy";
mode = <3>;
power = <50>;
};

View File

@ -76,6 +76,8 @@
&usb_otg_hs {
interface-type = <0>;
usb-phy = <&usb2_phy>;
phys = <&usb2_phy>;
phy-names = "usb2-phy";
mode = <3>;
power = <50>;
};

View File

@ -519,7 +519,8 @@
usb2_phy: usb2phy@4a0ad080 {
compatible = "ti,omap-usb2";
reg = <0x4a0ad080 0x58>;
ctrl-module = <&omap_control_usb>;
ctrl-module = <&omap_control_usb2phy>;
#phy-cells = <0>;
};
};
@ -643,12 +644,16 @@
};
};
omap_control_usb: omap-control-usb@4a002300 {
compatible = "ti,omap-control-usb";
reg = <0x4a002300 0x4>,
<0x4a00233c 0x4>;
reg-names = "control_dev_conf", "otghs_control";
ti,type = <1>;
omap_control_usb2phy: control-phy@4a002300 {
compatible = "ti,control-phy-usb2";
reg = <0x4a002300 0x4>;
reg-names = "power";
};
omap_control_usbotg: control-phy@4a00233c {
compatible = "ti,control-phy-otghs";
reg = <0x4a00233c 0x4>;
reg-names = "otghs_control";
};
usb_otg_hs: usb_otg_hs@4a0ab000 {
@ -658,10 +663,12 @@
interrupt-names = "mc", "dma";
ti,hwmods = "usb_otg_hs";
usb-phy = <&usb2_phy>;
phys = <&usb2_phy>;
phy-names = "usb2-phy";
multipoint = <1>;
num-eps = <16>;
ram-bits = <12>;
ti,has-mailbox;
ctrl-module = <&omap_control_usbotg>;
};
};
};

View File

@ -626,12 +626,16 @@
hw-caps-temp-alert;
};
omap_control_usb: omap-control-usb@4a002300 {
compatible = "ti,omap-control-usb";
reg = <0x4a002300 0x4>,
<0x4a002370 0x4>;
reg-names = "control_dev_conf", "phy_power_usb";
ti,type = <2>;
omap_control_usb2phy: control-phy@4a002300 {
compatible = "ti,control-phy-usb2";
reg = <0x4a002300 0x4>;
reg-names = "power";
};
omap_control_usb3phy: control-phy@4a002370 {
compatible = "ti,control-phy-pipe3";
reg = <0x4a002370 0x4>;
reg-names = "power";
};
omap_dwc3@4a020000 {
@ -662,7 +666,7 @@
usb2_phy: usb2phy@4a084000 {
compatible = "ti,omap-usb2";
reg = <0x4a084000 0x7c>;
ctrl-module = <&omap_control_usb>;
ctrl-module = <&omap_control_usb2phy>;
};
usb3_phy: usb3phy@4a084400 {
@ -671,7 +675,7 @@
<0x4a084800 0x64>,
<0x4a084c00 0x40>;
reg-names = "phy_rx", "phy_tx", "pll_ctrl";
ctrl-module = <&omap_control_usb>;
ctrl-module = <&omap_control_usb3phy>;
};
};

View File

@ -86,6 +86,7 @@
usb1v8-supply = <&vusb1v8>;
usb3v1-supply = <&vusb3v1>;
usb_mode = <1>;
#phy-cells = <0>;
};
twl_pwm: pwm {

View File

@ -1,15 +1,14 @@
CONFIG_EXPERIMENTAL=y
CONFIG_SYSVIPC=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=14
CONFIG_SYSFS_DEPRECATED_V2=y
CONFIG_EXPERT=y
CONFIG_SLAB=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
# CONFIG_BLK_DEV_BSG is not set
CONFIG_PARTITION_ADVANCED=y
# CONFIG_IOSCHED_CFQ is not set
CONFIG_ARCH_EP93XX=y
CONFIG_CRUNCH=y
@ -47,11 +46,8 @@ CONFIG_IPV6=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
# CONFIG_FW_LOADER is not set
CONFIG_MTD=y
CONFIG_MTD_CONCAT=y
CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_REDBOOT_PARTS=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_CFI=y
CONFIG_MTD_CFI_ADV_OPTIONS=y
@ -67,15 +63,14 @@ CONFIG_SCSI=y
# CONFIG_SCSI_PROC_FS is not set
CONFIG_BLK_DEV_SD=y
CONFIG_NETDEVICES=y
CONFIG_NET_ETHERNET=y
CONFIG_EP93XX_ETH=y
CONFIG_USB_RTL8150=y
# CONFIG_INPUT is not set
# CONFIG_SERIO is not set
# CONFIG_VT is not set
# CONFIG_LEGACY_PTYS is not set
CONFIG_SERIAL_AMBA_PL010=y
CONFIG_SERIAL_AMBA_PL010_CONSOLE=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
CONFIG_I2C_CHARDEV=y
@ -86,9 +81,9 @@ CONFIG_WATCHDOG=y
CONFIG_EP93XX_WATCHDOG=y
CONFIG_USB=y
CONFIG_USB_DEBUG=y
CONFIG_USB_DEVICEFS=y
CONFIG_USB_DYNAMIC_MINORS=y
CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y
CONFIG_USB_SERIAL=y
CONFIG_USB_SERIAL_CONSOLE=y
@ -100,24 +95,18 @@ CONFIG_RTC_DRV_EP93XX=y
CONFIG_EXT2_FS=y
CONFIG_EXT3_FS=y
# CONFIG_EXT3_FS_XATTR is not set
CONFIG_INOTIFY=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_JFFS2_FS=y
CONFIG_NFS_FS=y
CONFIG_NFS_V3=y
CONFIG_ROOT_NFS=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ISO8859_1=y
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_SLAB=y
CONFIG_DEBUG_SPINLOCK=y
CONFIG_DEBUG_MUTEXES=y
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_ERRORS=y
CONFIG_DEBUG_LL=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_LIBCRC32C=y

View File

@ -212,7 +212,7 @@ static struct clk_lookup clocks[] = {
INIT_CK(NULL, "hclk", &clk_h),
INIT_CK(NULL, "apb_pclk", &clk_p),
INIT_CK(NULL, "pll2", &clk_pll2),
INIT_CK("ep93xx-ohci", NULL, &clk_usb_host),
INIT_CK("ohci-platform", NULL, &clk_usb_host),
INIT_CK("ep93xx-keypad", NULL, &clk_keypad),
INIT_CK("ep93xx-fb", NULL, &clk_video),
INIT_CK("ep93xx-spi.0", NULL, &clk_spi),

View File

@ -36,6 +36,7 @@
#include <linux/export.h>
#include <linux/irqchip/arm-vic.h>
#include <linux/reboot.h>
#include <linux/usb/ohci_pdriver.h>
#include <mach/hardware.h>
#include <linux/platform_data/video-ep93xx.h>
@ -297,25 +298,53 @@ static struct platform_device ep93xx_rtc_device = {
.resource = ep93xx_rtc_resource,
};
/*************************************************************************
* EP93xx OHCI USB Host
*************************************************************************/
static struct clk *ep93xx_ohci_host_clock;
static int ep93xx_ohci_power_on(struct platform_device *pdev)
{
if (!ep93xx_ohci_host_clock) {
ep93xx_ohci_host_clock = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(ep93xx_ohci_host_clock))
return PTR_ERR(ep93xx_ohci_host_clock);
}
return clk_enable(ep93xx_ohci_host_clock);
}
static void ep93xx_ohci_power_off(struct platform_device *pdev)
{
clk_disable(ep93xx_ohci_host_clock);
}
static struct usb_ohci_pdata ep93xx_ohci_pdata = {
.power_on = ep93xx_ohci_power_on,
.power_off = ep93xx_ohci_power_off,
.power_suspend = ep93xx_ohci_power_off,
};
static struct resource ep93xx_ohci_resources[] = {
DEFINE_RES_MEM(EP93XX_USB_PHYS_BASE, 0x1000),
DEFINE_RES_IRQ(IRQ_EP93XX_USB),
};
static u64 ep93xx_ohci_dma_mask = DMA_BIT_MASK(32);
static struct platform_device ep93xx_ohci_device = {
.name = "ep93xx-ohci",
.name = "ohci-platform",
.id = -1,
.dev = {
.dma_mask = &ep93xx_ohci_device.dev.coherent_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
.num_resources = ARRAY_SIZE(ep93xx_ohci_resources),
.resource = ep93xx_ohci_resources,
.dev = {
.dma_mask = &ep93xx_ohci_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
.platform_data = &ep93xx_ohci_pdata,
},
};
/*************************************************************************
* EP93xx physmap'ed flash
*************************************************************************/

View File

@ -44,11 +44,6 @@
#define S5P_DAC_PHY_CONTROL S5P_PMUREG(0x070C)
#define S5P_DAC_PHY_ENABLE (1 << 0)
#define S5P_MIPI_DPHY_CONTROL(n) S5P_PMUREG(0x0710 + (n) * 4)
#define S5P_MIPI_DPHY_ENABLE (1 << 0)
#define S5P_MIPI_DPHY_SRESETN (1 << 1)
#define S5P_MIPI_DPHY_MRESETN (1 << 2)
#define S5P_INFORM0 S5P_PMUREG(0x0800)
#define S5P_INFORM1 S5P_PMUREG(0x0804)
#define S5P_INFORM2 S5P_PMUREG(0x0808)

View File

@ -289,18 +289,12 @@ static struct regulator_consumer_supply beagle_vsim_supply[] = {
static struct gpio_led gpio_leds[];
/* PHY's VCC regulator might be added later, so flag that we need it */
static struct usb_phy_gen_xceiv_platform_data hsusb2_phy_data = {
.needs_vcc = true,
};
static struct usbhs_phy_data phy_data[] = {
{
.port = 2,
.reset_gpio = 147,
.vcc_gpio = -1, /* updated in beagle_twl_gpio_setup */
.vcc_polarity = 1, /* updated in beagle_twl_gpio_setup */
.platform_data = &hsusb2_phy_data,
},
};

View File

@ -24,6 +24,7 @@
#include <linux/i2c/twl.h>
#include <linux/gpio.h>
#include <linux/string.h>
#include <linux/phy/phy.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/fixed.h>
@ -90,8 +91,18 @@ void __init omap_pmic_late_init(void)
}
#if defined(CONFIG_ARCH_OMAP3)
struct phy_consumer consumers[] = {
PHY_CONSUMER("musb-hdrc.0", "usb"),
};
struct phy_init_data init_data = {
.consumers = consumers,
.num_consumers = ARRAY_SIZE(consumers),
};
static struct twl4030_usb_data omap3_usb_pdata = {
.usb_mode = T2_USB_MODE_ULPI,
.init_data = &init_data,
};
static int omap3_batt_table[] = {

View File

@ -435,6 +435,7 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys)
struct platform_device *pdev;
char *phy_id;
struct platform_device_info pdevinfo;
struct usb_phy_gen_xceiv_platform_data nop_pdata;
for (i = 0; i < num_phys; i++) {
@ -455,11 +456,18 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys)
return -ENOMEM;
}
/* set platform data */
memset(&nop_pdata, 0, sizeof(nop_pdata));
if (gpio_is_valid(phy->vcc_gpio))
nop_pdata.needs_vcc = true;
nop_pdata.gpio_reset = phy->reset_gpio;
nop_pdata.type = USB_PHY_TYPE_USB2;
/* create a NOP PHY device */
memset(&pdevinfo, 0, sizeof(pdevinfo));
pdevinfo.name = nop_name;
pdevinfo.id = phy->port;
pdevinfo.data = phy->platform_data;
pdevinfo.data = &nop_pdata;
pdevinfo.size_data =
sizeof(struct usb_phy_gen_xceiv_platform_data);
scnprintf(phy_id, MAX_STR, "usb_phy_gen_xceiv.%d",
@ -474,14 +482,6 @@ int usbhs_init_phys(struct usbhs_phy_data *phy, int num_phys)
usb_bind_phy("ehci-omap.0", phy->port - 1, phy_id);
/* Do we need RESET regulator ? */
if (gpio_is_valid(phy->reset_gpio)) {
scnprintf(rail_name, MAX_STR,
"hsusb%d_reset", phy->port);
usbhs_add_regulator(rail_name, phy_id, "reset",
phy->reset_gpio, 1);
}
/* Do we need VCC regulator ? */
if (gpio_is_valid(phy->vcc_gpio)) {
scnprintf(rail_name, MAX_STR, "hsusb%d_vcc", phy->port);

View File

@ -58,7 +58,6 @@ struct usbhs_phy_data {
int reset_gpio;
int vcc_gpio;
bool vcc_polarity; /* 1 active high, 0 active low */
void *platform_data;
};
extern void usb_musb_init(struct omap_musb_board_data *board_data);

View File

@ -147,10 +147,6 @@
#define S5P_HDMI_PHY_CONTROL S5P_CLKREG(0xE804)
#define S5P_USB_PHY_CONTROL S5P_CLKREG(0xE80C)
#define S5P_DAC_PHY_CONTROL S5P_CLKREG(0xE810)
#define S5P_MIPI_DPHY_CONTROL(x) S5P_CLKREG(0xE814)
#define S5P_MIPI_DPHY_ENABLE (1 << 0)
#define S5P_MIPI_DPHY_SRESETN (1 << 1)
#define S5P_MIPI_DPHY_MRESETN (1 << 2)
#define S5P_INFORM0 S5P_CLKREG(0xF000)
#define S5P_INFORM1 S5P_CLKREG(0xF004)

View File

@ -382,11 +382,6 @@ config S5P_DEV_TV
help
Compile in platform device definition for TV interface
config S5P_DEV_USB_EHCI
bool
help
Compile in platform device definition for USB EHCI
config S3C24XX_PWM
bool "PWM device support"
select PWM
@ -395,11 +390,6 @@ config S3C24XX_PWM
Support for exporting the PWM timer blocks via the pwm device
system
config S5P_SETUP_MIPIPHY
bool
help
Compile in common setup code for MIPI-CSIS and MIPI-DSIM devices
config S3C_SETUP_CAMIF
bool
help

View File

@ -38,7 +38,6 @@ obj-$(CONFIG_S5P_DEV_UART) += s5p-dev-uart.o
obj-$(CONFIG_SAMSUNG_DEV_BACKLIGHT) += dev-backlight.o
obj-$(CONFIG_S3C_SETUP_CAMIF) += setup-camif.o
obj-$(CONFIG_S5P_SETUP_MIPIPHY) += setup-mipiphy.o
# DMA support

View File

@ -49,7 +49,6 @@
#include <plat/devs.h>
#include <plat/adc.h>
#include <linux/platform_data/ata-samsung_cf.h>
#include <linux/platform_data/usb-ehci-s5p.h>
#include <plat/fb.h>
#include <plat/fb-s3c2410.h>
#include <plat/hdmi.h>
@ -1359,39 +1358,6 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
}
#endif /* CONFIG_PLAT_S3C24XX */
/* USB EHCI Host Controller */
#ifdef CONFIG_S5P_DEV_USB_EHCI
static struct resource s5p_ehci_resource[] = {
[0] = DEFINE_RES_MEM(S5P_PA_EHCI, SZ_256),
[1] = DEFINE_RES_IRQ(IRQ_USB_HOST),
};
struct platform_device s5p_device_ehci = {
.name = "s5p-ehci",
.id = -1,
.num_resources = ARRAY_SIZE(s5p_ehci_resource),
.resource = s5p_ehci_resource,
.dev = {
.dma_mask = &samsung_device_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
}
};
void __init s5p_ehci_set_platdata(struct s5p_ehci_platdata *pd)
{
struct s5p_ehci_platdata *npd;
npd = s3c_set_platdata(pd, sizeof(struct s5p_ehci_platdata),
&s5p_device_ehci);
if (!npd->phy_init)
npd->phy_init = s5p_usb_phy_init;
if (!npd->phy_exit)
npd->phy_exit = s5p_usb_phy_exit;
}
#endif /* CONFIG_S5P_DEV_USB_EHCI */
/* USB HSOTG */
#ifdef CONFIG_S3C_DEV_USB_HSOTG

View File

@ -75,7 +75,6 @@ extern struct platform_device s3c_device_usb_hsotg;
extern struct platform_device s3c_device_usb_hsudc;
extern struct platform_device s3c_device_wdt;
extern struct platform_device s5p_device_ehci;
extern struct platform_device s5p_device_fimc0;
extern struct platform_device s5p_device_fimc1;
extern struct platform_device s5p_device_fimc2;

View File

@ -1,60 +0,0 @@
/*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
*
* S5P - Helper functions for MIPI-CSIS and MIPI-DSIM D-PHY control
*
* 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/export.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <mach/regs-clock.h>
static int __s5p_mipi_phy_control(int id, bool on, u32 reset)
{
static DEFINE_SPINLOCK(lock);
void __iomem *addr;
unsigned long flags;
u32 cfg;
id = max(0, id);
if (id > 1)
return -EINVAL;
addr = S5P_MIPI_DPHY_CONTROL(id);
spin_lock_irqsave(&lock, flags);
cfg = __raw_readl(addr);
cfg = on ? (cfg | reset) : (cfg & ~reset);
__raw_writel(cfg, addr);
if (on) {
cfg |= S5P_MIPI_DPHY_ENABLE;
} else if (!(cfg & (S5P_MIPI_DPHY_SRESETN |
S5P_MIPI_DPHY_MRESETN) & ~reset)) {
cfg &= ~S5P_MIPI_DPHY_ENABLE;
}
__raw_writel(cfg, addr);
spin_unlock_irqrestore(&lock, flags);
return 0;
}
int s5p_csis_phy_enable(int id, bool on)
{
return __s5p_mipi_phy_control(id, on, S5P_MIPI_DPHY_SRESETN);
}
EXPORT_SYMBOL(s5p_csis_phy_enable);
int s5p_dsim_phy_enable(struct platform_device *pdev, bool on)
{
return __s5p_mipi_phy_control(pdev->id, on, S5P_MIPI_DPHY_MRESETN);
}
EXPORT_SYMBOL(s5p_dsim_phy_enable);

View File

@ -166,4 +166,6 @@ source "drivers/reset/Kconfig"
source "drivers/fmc/Kconfig"
source "drivers/phy/Kconfig"
endmenu

View File

@ -8,6 +8,8 @@
obj-y += irqchip/
obj-y += bus/
obj-$(CONFIG_GENERIC_PHY) += phy/
# GPIO must come after pinctrl as gpios may need to mux pins etc
obj-y += pinctrl/
obj-y += gpio/

View File

@ -29,7 +29,7 @@ config VIDEO_S5P_FIMC
config VIDEO_S5P_MIPI_CSIS
tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver"
depends on REGULATOR
select S5P_SETUP_MIPIPHY
select GENERIC_PHY
help
This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2
receiver (MIPI-CSIS) devices.

View File

@ -20,6 +20,7 @@
#include <linux/memory.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_data/mipi-csis.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
@ -180,6 +181,7 @@ struct csis_drvdata {
* @sd: v4l2_subdev associated with CSIS device instance
* @index: the hardware instance index
* @pdev: CSIS platform device
* @phy: pointer to the CSIS generic PHY
* @regs: mmaped I/O registers memory
* @supplies: CSIS regulator supplies
* @clock: CSIS clocks
@ -203,6 +205,7 @@ struct csis_state {
struct v4l2_subdev sd;
u8 index;
struct platform_device *pdev;
struct phy *phy;
void __iomem *regs;
struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
struct clk *clock[NUM_CSIS_CLOCKS];
@ -779,8 +782,8 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
"samsung,csis-wclk");
state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
of_node_put(node);
return 0;
}
#else
@ -829,6 +832,10 @@ static int s5pcsis_probe(struct platform_device *pdev)
return -EINVAL;
}
state->phy = devm_phy_get(dev, "csis");
if (IS_ERR(state->phy))
return PTR_ERR(state->phy);
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
state->regs = devm_ioremap_resource(dev, mem_res);
if (IS_ERR(state->regs))
@ -922,7 +929,7 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
mutex_lock(&state->lock);
if (state->flags & ST_POWERED) {
s5pcsis_stop_stream(state);
ret = s5p_csis_phy_enable(state->index, false);
ret = phy_power_off(state->phy);
if (ret)
goto unlock;
ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
@ -958,7 +965,7 @@ static int s5pcsis_pm_resume(struct device *dev, bool runtime)
state->supplies);
if (ret)
goto unlock;
ret = s5p_csis_phy_enable(state->index, true);
ret = phy_power_on(state->phy);
if (!ret) {
state->flags |= ST_POWERED;
} else {

54
drivers/phy/Kconfig Normal file
View File

@ -0,0 +1,54 @@
#
# PHY
#
menu "PHY Subsystem"
config GENERIC_PHY
tristate "PHY Core"
help
Generic PHY support.
This framework is designed to provide a generic interface for PHY
devices present in the kernel. This layer will have the generic
API by which phy drivers can create PHY using the phy framework and
phy users can obtain reference to the PHY. All the users of this
framework should select this config.
config PHY_EXYNOS_MIPI_VIDEO
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
help
Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
and EXYNOS SoCs.
config OMAP_USB2
tristate "OMAP USB2 PHY Driver"
depends on ARCH_OMAP2PLUS
select GENERIC_PHY
select USB_PHY
select OMAP_CONTROL_USB
help
Enable this to support the transceiver that is part of SOC. This
driver takes care of all the PHY functionality apart from comparator.
The USB OTG controller communicates with the comparator using this
driver.
config TWL4030_USB
tristate "TWL4030 USB Transceiver Driver"
depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
select GENERIC_PHY
select USB_PHY
help
Enable this to support the USB OTG transceiver on TWL4030
family chips (including the TWL5030 and TPS659x0 devices).
This transceiver supports high and full speed devices plus,
in host mode, low speed.
config PHY_EXYNOS_DP_VIDEO
tristate "EXYNOS SoC series Display Port PHY driver"
depends on OF
select GENERIC_PHY
help
Support for Display Port PHY found on Samsung EXYNOS SoCs.
endmenu

9
drivers/phy/Makefile Normal file
View File

@ -0,0 +1,9 @@
#
# Makefile for the phy drivers.
#
obj-$(CONFIG_GENERIC_PHY) += phy-core.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_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o

698
drivers/phy/phy-core.c Normal file
View File

@ -0,0 +1,698 @@
/*
* phy-core.c -- Generic Phy framework.
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
*
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*
* 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 of the License, or (at your
* option) any later version.
*/
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/idr.h>
#include <linux/pm_runtime.h>
static struct class *phy_class;
static DEFINE_MUTEX(phy_provider_mutex);
static LIST_HEAD(phy_provider_list);
static DEFINE_IDA(phy_ida);
static void devm_phy_release(struct device *dev, void *res)
{
struct phy *phy = *(struct phy **)res;
phy_put(phy);
}
static void devm_phy_provider_release(struct device *dev, void *res)
{
struct phy_provider *phy_provider = *(struct phy_provider **)res;
of_phy_provider_unregister(phy_provider);
}
static void devm_phy_consume(struct device *dev, void *res)
{
struct phy *phy = *(struct phy **)res;
phy_destroy(phy);
}
static int devm_phy_match(struct device *dev, void *res, void *match_data)
{
return res == match_data;
}
static struct phy *phy_lookup(struct device *device, const char *port)
{
unsigned int count;
struct phy *phy;
struct device *dev;
struct phy_consumer *consumers;
struct class_dev_iter iter;
class_dev_iter_init(&iter, phy_class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) {
phy = to_phy(dev);
count = phy->init_data->num_consumers;
consumers = phy->init_data->consumers;
while (count--) {
if (!strcmp(consumers->dev_name, dev_name(device)) &&
!strcmp(consumers->port, port)) {
class_dev_iter_exit(&iter);
return phy;
}
consumers++;
}
}
class_dev_iter_exit(&iter);
return ERR_PTR(-ENODEV);
}
static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
{
struct phy_provider *phy_provider;
list_for_each_entry(phy_provider, &phy_provider_list, list) {
if (phy_provider->dev->of_node == node)
return phy_provider;
}
return ERR_PTR(-EPROBE_DEFER);
}
int phy_pm_runtime_get(struct phy *phy)
{
if (!pm_runtime_enabled(&phy->dev))
return -ENOTSUPP;
return pm_runtime_get(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_get);
int phy_pm_runtime_get_sync(struct phy *phy)
{
if (!pm_runtime_enabled(&phy->dev))
return -ENOTSUPP;
return pm_runtime_get_sync(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_get_sync);
int phy_pm_runtime_put(struct phy *phy)
{
if (!pm_runtime_enabled(&phy->dev))
return -ENOTSUPP;
return pm_runtime_put(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_put);
int phy_pm_runtime_put_sync(struct phy *phy)
{
if (!pm_runtime_enabled(&phy->dev))
return -ENOTSUPP;
return pm_runtime_put_sync(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_put_sync);
void phy_pm_runtime_allow(struct phy *phy)
{
if (!pm_runtime_enabled(&phy->dev))
return;
pm_runtime_allow(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_allow);
void phy_pm_runtime_forbid(struct phy *phy)
{
if (!pm_runtime_enabled(&phy->dev))
return;
pm_runtime_forbid(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_forbid);
int phy_init(struct phy *phy)
{
int ret;
ret = phy_pm_runtime_get_sync(phy);
if (ret < 0 && ret != -ENOTSUPP)
return ret;
mutex_lock(&phy->mutex);
if (phy->init_count++ == 0 && phy->ops->init) {
ret = phy->ops->init(phy);
if (ret < 0) {
dev_err(&phy->dev, "phy init failed --> %d\n", ret);
goto out;
}
}
out:
mutex_unlock(&phy->mutex);
phy_pm_runtime_put(phy);
return ret;
}
EXPORT_SYMBOL_GPL(phy_init);
int phy_exit(struct phy *phy)
{
int ret;
ret = phy_pm_runtime_get_sync(phy);
if (ret < 0 && ret != -ENOTSUPP)
return ret;
mutex_lock(&phy->mutex);
if (--phy->init_count == 0 && phy->ops->exit) {
ret = phy->ops->exit(phy);
if (ret < 0) {
dev_err(&phy->dev, "phy exit failed --> %d\n", ret);
goto out;
}
}
out:
mutex_unlock(&phy->mutex);
phy_pm_runtime_put(phy);
return ret;
}
EXPORT_SYMBOL_GPL(phy_exit);
int phy_power_on(struct phy *phy)
{
int ret = -ENOTSUPP;
ret = phy_pm_runtime_get_sync(phy);
if (ret < 0 && ret != -ENOTSUPP)
return ret;
mutex_lock(&phy->mutex);
if (phy->power_count++ == 0 && phy->ops->power_on) {
ret = phy->ops->power_on(phy);
if (ret < 0) {
dev_err(&phy->dev, "phy poweron failed --> %d\n", ret);
goto out;
}
}
out:
mutex_unlock(&phy->mutex);
return ret;
}
EXPORT_SYMBOL_GPL(phy_power_on);
int phy_power_off(struct phy *phy)
{
int ret = -ENOTSUPP;
mutex_lock(&phy->mutex);
if (--phy->power_count == 0 && phy->ops->power_off) {
ret = phy->ops->power_off(phy);
if (ret < 0) {
dev_err(&phy->dev, "phy poweroff failed --> %d\n", ret);
goto out;
}
}
out:
mutex_unlock(&phy->mutex);
phy_pm_runtime_put(phy);
return ret;
}
EXPORT_SYMBOL_GPL(phy_power_off);
/**
* of_phy_get() - lookup and obtain a reference to a phy by phandle
* @dev: device that requests this phy
* @index: the index of the phy
*
* Returns the phy associated with the given phandle value,
* after getting a refcount to it or -ENODEV if there is no such phy or
* -EPROBE_DEFER if there is a phandle to the phy, but the device is
* not yet loaded. This function uses of_xlate call back function provided
* while registering the phy_provider to find the phy instance.
*/
static struct phy *of_phy_get(struct device *dev, int index)
{
int ret;
struct phy_provider *phy_provider;
struct phy *phy = NULL;
struct of_phandle_args args;
ret = of_parse_phandle_with_args(dev->of_node, "phys", "#phy-cells",
index, &args);
if (ret) {
dev_dbg(dev, "failed to get phy in %s node\n",
dev->of_node->full_name);
return ERR_PTR(-ENODEV);
}
mutex_lock(&phy_provider_mutex);
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;
}
phy = phy_provider->of_xlate(phy_provider->dev, &args);
module_put(phy_provider->owner);
err0:
mutex_unlock(&phy_provider_mutex);
of_node_put(args.np);
return phy;
}
/**
* phy_put() - release the PHY
* @phy: the phy returned by phy_get()
*
* Releases a refcount the caller received from phy_get().
*/
void phy_put(struct phy *phy)
{
if (IS_ERR(phy))
return;
module_put(phy->ops->owner);
put_device(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_put);
/**
* devm_phy_put() - release the PHY
* @dev: device that wants to release this phy
* @phy: the phy returned by devm_phy_get()
*
* destroys the devres associated with this phy and invokes phy_put
* to release the phy.
*/
void devm_phy_put(struct device *dev, struct phy *phy)
{
int r;
r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy);
dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
}
EXPORT_SYMBOL_GPL(devm_phy_put);
/**
* of_phy_simple_xlate() - returns the phy instance from phy provider
* @dev: the PHY provider device
* @args: of_phandle_args (not used here)
*
* Intended to be used by phy provider for the common case where #phy-cells is
* 0. For other cases where #phy-cells is greater than '0', the phy provider
* should provide a custom of_xlate function that reads the *args* and returns
* the appropriate phy.
*/
struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
*args)
{
struct phy *phy;
struct class_dev_iter iter;
struct device_node *node = dev->of_node;
class_dev_iter_init(&iter, phy_class, NULL, NULL);
while ((dev = class_dev_iter_next(&iter))) {
phy = to_phy(dev);
if (node != phy->dev.of_node)
continue;
class_dev_iter_exit(&iter);
return phy;
}
class_dev_iter_exit(&iter);
return ERR_PTR(-ENODEV);
}
EXPORT_SYMBOL_GPL(of_phy_simple_xlate);
/**
* phy_get() - lookup and obtain a reference to a phy.
* @dev: device that requests this phy
* @string: the phy name as given in the dt data or the name of the controller
* port for non-dt case
*
* Returns the phy driver, after getting a refcount to it; or
* -ENODEV if there is no such phy. The caller is responsible for
* calling phy_put() to release that count.
*/
struct phy *phy_get(struct device *dev, const char *string)
{
int index = 0;
struct phy *phy = NULL;
if (string == NULL) {
dev_WARN(dev, "missing string\n");
return ERR_PTR(-EINVAL);
}
if (dev->of_node) {
index = of_property_match_string(dev->of_node, "phy-names",
string);
phy = of_phy_get(dev, index);
if (IS_ERR(phy)) {
dev_err(dev, "unable to find phy\n");
return phy;
}
} else {
phy = phy_lookup(dev, string);
if (IS_ERR(phy)) {
dev_err(dev, "unable to find phy\n");
return phy;
}
}
if (!try_module_get(phy->ops->owner))
return ERR_PTR(-EPROBE_DEFER);
get_device(&phy->dev);
return phy;
}
EXPORT_SYMBOL_GPL(phy_get);
/**
* devm_phy_get() - lookup and obtain a reference to a phy.
* @dev: device that requests this phy
* @string: the phy name as given in the dt data or phy device name
* for non-dt case
*
* Gets the phy using 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_phy_get(struct device *dev, const char *string)
{
struct phy **ptr, *phy;
ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
phy = phy_get(dev, string);
if (!IS_ERR(phy)) {
*ptr = phy;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return phy;
}
EXPORT_SYMBOL_GPL(devm_phy_get);
/**
* phy_create() - create a new phy
* @dev: device that is creating the new phy
* @ops: function pointers for performing phy operations
* @init_data: contains the list of PHY consumers or NULL
*
* Called to create a phy using phy framework.
*/
struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
struct phy_init_data *init_data)
{
int ret;
int id;
struct phy *phy;
if (!dev) {
dev_WARN(dev, "no device provided for PHY\n");
ret = -EINVAL;
goto err0;
}
phy = kzalloc(sizeof(*phy), GFP_KERNEL);
if (!phy) {
ret = -ENOMEM;
goto err0;
}
id = ida_simple_get(&phy_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
dev_err(dev, "unable to get id\n");
ret = id;
goto err0;
}
device_initialize(&phy->dev);
mutex_init(&phy->mutex);
phy->dev.class = phy_class;
phy->dev.parent = dev;
phy->dev.of_node = dev->of_node;
phy->id = id;
phy->ops = ops;
phy->init_data = init_data;
ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
if (ret)
goto err1;
ret = device_add(&phy->dev);
if (ret)
goto err1;
if (pm_runtime_enabled(dev)) {
pm_runtime_enable(&phy->dev);
pm_runtime_no_callbacks(&phy->dev);
}
return phy;
err1:
ida_remove(&phy_ida, phy->id);
put_device(&phy->dev);
kfree(phy);
err0:
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(phy_create);
/**
* devm_phy_create() - create a new phy
* @dev: device that is creating the new phy
* @ops: function pointers for performing phy operations
* @init_data: contains the list of PHY consumers or NULL
*
* Creates a new PHY device adding it to the PHY class.
* While at that, it also associates the device with the phy using devres.
* On driver detach, release function is invoked on the devres data,
* then, devres data is freed.
*/
struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
struct phy_init_data *init_data)
{
struct phy **ptr, *phy;
ptr = devres_alloc(devm_phy_consume, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
phy = phy_create(dev, ops, init_data);
if (!IS_ERR(phy)) {
*ptr = phy;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return phy;
}
EXPORT_SYMBOL_GPL(devm_phy_create);
/**
* phy_destroy() - destroy the phy
* @phy: the phy to be destroyed
*
* Called to destroy the phy.
*/
void phy_destroy(struct phy *phy)
{
pm_runtime_disable(&phy->dev);
device_unregister(&phy->dev);
}
EXPORT_SYMBOL_GPL(phy_destroy);
/**
* devm_phy_destroy() - destroy the PHY
* @dev: device that wants to release this phy
* @phy: the phy returned by devm_phy_get()
*
* destroys the devres associated with this phy and invokes phy_destroy
* to destroy the phy.
*/
void devm_phy_destroy(struct device *dev, struct phy *phy)
{
int r;
r = devres_destroy(dev, devm_phy_consume, devm_phy_match, phy);
dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
}
EXPORT_SYMBOL_GPL(devm_phy_destroy);
/**
* __of_phy_provider_register() - create/register phy provider with the framework
* @dev: struct device of the phy provider
* @owner: the module owner containing of_xlate
* @of_xlate: function pointer to obtain phy instance from phy provider
*
* Creates struct phy_provider from dev and of_xlate function pointer.
* This is used in the case of dt boot for finding the phy instance from
* phy provider.
*/
struct phy_provider *__of_phy_provider_register(struct device *dev,
struct module *owner, struct phy * (*of_xlate)(struct device *dev,
struct of_phandle_args *args))
{
struct phy_provider *phy_provider;
phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);
if (!phy_provider)
return ERR_PTR(-ENOMEM);
phy_provider->dev = dev;
phy_provider->owner = owner;
phy_provider->of_xlate = of_xlate;
mutex_lock(&phy_provider_mutex);
list_add_tail(&phy_provider->list, &phy_provider_list);
mutex_unlock(&phy_provider_mutex);
return phy_provider;
}
EXPORT_SYMBOL_GPL(__of_phy_provider_register);
/**
* __devm_of_phy_provider_register() - create/register phy provider with the
* framework
* @dev: struct device of the phy provider
* @owner: the module owner containing of_xlate
* @of_xlate: function pointer to obtain phy instance from phy provider
*
* Creates struct phy_provider from dev and of_xlate function pointer.
* This is used in the case of dt boot for finding the phy instance from
* phy provider. While at that, it also associates the device with the
* phy provider using devres. On driver detach, release function is invoked
* on the devres data, then, devres data is freed.
*/
struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
struct module *owner, struct phy * (*of_xlate)(struct device *dev,
struct of_phandle_args *args))
{
struct phy_provider **ptr, *phy_provider;
ptr = devres_alloc(devm_phy_provider_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
phy_provider = __of_phy_provider_register(dev, owner, of_xlate);
if (!IS_ERR(phy_provider)) {
*ptr = phy_provider;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return phy_provider;
}
EXPORT_SYMBOL_GPL(__devm_of_phy_provider_register);
/**
* of_phy_provider_unregister() - unregister phy provider from the framework
* @phy_provider: phy provider returned by of_phy_provider_register()
*
* Removes the phy_provider created using of_phy_provider_register().
*/
void of_phy_provider_unregister(struct phy_provider *phy_provider)
{
if (IS_ERR(phy_provider))
return;
mutex_lock(&phy_provider_mutex);
list_del(&phy_provider->list);
kfree(phy_provider);
mutex_unlock(&phy_provider_mutex);
}
EXPORT_SYMBOL_GPL(of_phy_provider_unregister);
/**
* devm_of_phy_provider_unregister() - remove phy provider from the framework
* @dev: struct device of the phy provider
*
* destroys the devres associated with this phy provider and invokes
* of_phy_provider_unregister to unregister the phy provider.
*/
void devm_of_phy_provider_unregister(struct device *dev,
struct phy_provider *phy_provider) {
int r;
r = devres_destroy(dev, devm_phy_provider_release, devm_phy_match,
phy_provider);
dev_WARN_ONCE(dev, r, "couldn't find PHY provider device resource\n");
}
EXPORT_SYMBOL_GPL(devm_of_phy_provider_unregister);
/**
* phy_release() - release the phy
* @dev: the dev member within phy
*
* When the last reference to the device is removed, it is called
* from the embedded kobject as release method.
*/
static void phy_release(struct device *dev)
{
struct phy *phy;
phy = to_phy(dev);
dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
ida_remove(&phy_ida, phy->id);
kfree(phy);
}
static int __init phy_core_init(void)
{
phy_class = class_create(THIS_MODULE, "phy");
if (IS_ERR(phy_class)) {
pr_err("failed to create phy class --> %ld\n",
PTR_ERR(phy_class));
return PTR_ERR(phy_class);
}
phy_class->dev_release = phy_release;
return 0;
}
module_init(phy_core_init);
static void __exit phy_core_exit(void)
{
class_destroy(phy_class);
}
module_exit(phy_core_exit);
MODULE_DESCRIPTION("Generic PHY Framework");
MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,111 @@
/*
* Samsung EXYNOS SoC series Display Port PHY driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Jingoo Han <jg1.han@samsung.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/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
/* DPTX_PHY_CONTROL register */
#define EXYNOS_DPTX_PHY_ENABLE (1 << 0)
struct exynos_dp_video_phy {
void __iomem *regs;
};
static int __set_phy_state(struct exynos_dp_video_phy *state, unsigned int on)
{
u32 reg;
reg = readl(state->regs);
if (on)
reg |= EXYNOS_DPTX_PHY_ENABLE;
else
reg &= ~EXYNOS_DPTX_PHY_ENABLE;
writel(reg, state->regs);
return 0;
}
static int exynos_dp_video_phy_power_on(struct phy *phy)
{
struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
return __set_phy_state(state, 1);
}
static int exynos_dp_video_phy_power_off(struct phy *phy)
{
struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
return __set_phy_state(state, 0);
}
static struct phy_ops exynos_dp_video_phy_ops = {
.power_on = exynos_dp_video_phy_power_on,
.power_off = exynos_dp_video_phy_power_off,
.owner = THIS_MODULE,
};
static int exynos_dp_video_phy_probe(struct platform_device *pdev)
{
struct exynos_dp_video_phy *state;
struct device *dev = &pdev->dev;
struct resource *res;
struct phy_provider *phy_provider;
struct phy *phy;
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
state->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(state->regs))
return PTR_ERR(state->regs);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
phy = devm_phy_create(dev, &exynos_dp_video_phy_ops, NULL);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create Display Port PHY\n");
return PTR_ERR(phy);
}
phy_set_drvdata(phy, state);
return 0;
}
static const struct of_device_id exynos_dp_video_phy_of_match[] = {
{ .compatible = "samsung,exynos5250-dp-video-phy" },
{ },
};
MODULE_DEVICE_TABLE(of, exynos_dp_video_phy_of_match);
static struct platform_driver exynos_dp_video_phy_driver = {
.probe = exynos_dp_video_phy_probe,
.driver = {
.name = "exynos-dp-video-phy",
.owner = THIS_MODULE,
.of_match_table = exynos_dp_video_phy_of_match,
}
};
module_platform_driver(exynos_dp_video_phy_driver);
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,176 @@
/*
* Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Sylwester Nawrocki <s.nawrocki@samsung.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/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
/* MIPI_PHYn_CONTROL register offset: n = 0..1 */
#define EXYNOS_MIPI_PHY_CONTROL(n) ((n) * 4)
#define EXYNOS_MIPI_PHY_ENABLE (1 << 0)
#define EXYNOS_MIPI_PHY_SRESETN (1 << 1)
#define EXYNOS_MIPI_PHY_MRESETN (1 << 2)
#define EXYNOS_MIPI_PHY_RESET_MASK (3 << 1)
enum exynos_mipi_phy_id {
EXYNOS_MIPI_PHY_ID_CSIS0,
EXYNOS_MIPI_PHY_ID_DSIM0,
EXYNOS_MIPI_PHY_ID_CSIS1,
EXYNOS_MIPI_PHY_ID_DSIM1,
EXYNOS_MIPI_PHYS_NUM
};
#define is_mipi_dsim_phy_id(id) \
((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
struct exynos_mipi_video_phy {
spinlock_t slock;
struct video_phy_desc {
struct phy *phy;
unsigned int index;
} phys[EXYNOS_MIPI_PHYS_NUM];
void __iomem *regs;
};
static int __set_phy_state(struct exynos_mipi_video_phy *state,
enum exynos_mipi_phy_id id, unsigned int on)
{
void __iomem *addr;
u32 reg, reset;
addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
if (is_mipi_dsim_phy_id(id))
reset = EXYNOS_MIPI_PHY_MRESETN;
else
reset = EXYNOS_MIPI_PHY_SRESETN;
spin_lock(&state->slock);
reg = readl(addr);
if (on)
reg |= reset;
else
reg &= ~reset;
writel(reg, addr);
/* Clear ENABLE bit only if MRESETN, SRESETN bits are not set. */
if (on)
reg |= EXYNOS_MIPI_PHY_ENABLE;
else if (!(reg & EXYNOS_MIPI_PHY_RESET_MASK))
reg &= ~EXYNOS_MIPI_PHY_ENABLE;
writel(reg, addr);
spin_unlock(&state->slock);
return 0;
}
#define to_mipi_video_phy(desc) \
container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]);
static int exynos_mipi_video_phy_power_on(struct phy *phy)
{
struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
return __set_phy_state(state, phy_desc->index, 1);
}
static int exynos_mipi_video_phy_power_off(struct phy *phy)
{
struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
return __set_phy_state(state, phy_desc->index, 0);
}
static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct exynos_mipi_video_phy *state = dev_get_drvdata(dev);
if (WARN_ON(args->args[0] > EXYNOS_MIPI_PHYS_NUM))
return ERR_PTR(-ENODEV);
return state->phys[args->args[0]].phy;
}
static struct phy_ops exynos_mipi_video_phy_ops = {
.power_on = exynos_mipi_video_phy_power_on,
.power_off = exynos_mipi_video_phy_power_off,
.owner = THIS_MODULE,
};
static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
{
struct exynos_mipi_video_phy *state;
struct device *dev = &pdev->dev;
struct resource *res;
struct phy_provider *phy_provider;
unsigned int i;
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
state->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(state->regs))
return PTR_ERR(state->regs);
dev_set_drvdata(dev, state);
spin_lock_init(&state->slock);
phy_provider = devm_of_phy_provider_register(dev,
exynos_mipi_video_phy_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
struct phy *phy = devm_phy_create(dev,
&exynos_mipi_video_phy_ops, NULL);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY %d\n", i);
return PTR_ERR(phy);
}
state->phys[i].phy = phy;
state->phys[i].index = i;
phy_set_drvdata(phy, &state->phys[i]);
}
return 0;
}
static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
{ .compatible = "samsung,s5pv210-mipi-video-phy" },
{ },
};
MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match);
static struct platform_driver exynos_mipi_video_phy_driver = {
.probe = exynos_mipi_video_phy_probe,
.driver = {
.of_match_table = exynos_mipi_video_phy_of_match,
.name = "exynos-mipi-video-phy",
.owner = THIS_MODULE,
}
};
module_platform_driver(exynos_mipi_video_phy_driver);
MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
MODULE_LICENSE("GPL v2");

View File

@ -28,6 +28,8 @@
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/usb/omap_control_usb.h>
#include <linux/phy/phy.h>
#include <linux/of_platform.h>
/**
* omap_usb2_set_comparator - links the comparator present in the sytem with
@ -118,10 +120,42 @@ static int omap_usb2_suspend(struct usb_phy *x, int suspend)
return 0;
}
static int omap_usb_power_off(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
omap_control_usb_phy_power(phy->control_dev, 0);
return 0;
}
static int omap_usb_power_on(struct phy *x)
{
struct omap_usb *phy = phy_get_drvdata(x);
omap_control_usb_phy_power(phy->control_dev, 1);
return 0;
}
static struct phy_ops ops = {
.power_on = omap_usb_power_on,
.power_off = omap_usb_power_off,
.owner = THIS_MODULE,
};
static int omap_usb2_probe(struct platform_device *pdev)
{
struct omap_usb *phy;
struct usb_otg *otg;
struct omap_usb *phy;
struct phy *generic_phy;
struct phy_provider *phy_provider;
struct usb_otg *otg;
struct device_node *node = pdev->dev.of_node;
struct device_node *control_node;
struct platform_device *control_pdev;
if (!node)
return -EINVAL;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy) {
@ -143,12 +177,25 @@ static int omap_usb2_probe(struct platform_device *pdev)
phy->phy.otg = otg;
phy->phy.type = USB_PHY_TYPE_USB2;
phy->control_dev = omap_get_control_dev();
if (IS_ERR(phy->control_dev)) {
dev_dbg(&pdev->dev, "Failed to get control device\n");
return -ENODEV;
phy_provider = devm_of_phy_provider_register(phy->dev,
of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
control_node = of_parse_phandle(node, "ctrl-module", 0);
if (!control_node) {
dev_err(&pdev->dev, "Failed to get control device phandle\n");
return -EINVAL;
}
control_pdev = of_find_device_by_node(control_node);
if (!control_pdev) {
dev_err(&pdev->dev, "Failed to get control device\n");
return -EINVAL;
}
phy->control_dev = &control_pdev->dev;
phy->is_suspended = 1;
omap_control_usb_phy_power(phy->control_dev, 0);
@ -158,6 +205,15 @@ static int omap_usb2_probe(struct platform_device *pdev)
otg->start_srp = omap_usb_start_srp;
otg->phy = &phy->phy;
platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev);
generic_phy = devm_phy_create(phy->dev, &ops, NULL);
if (IS_ERR(generic_phy))
return PTR_ERR(generic_phy);
phy_set_drvdata(generic_phy, phy);
phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
if (IS_ERR(phy->wkupclk)) {
dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
@ -173,10 +229,6 @@ static int omap_usb2_probe(struct platform_device *pdev)
usb_add_phy_dev(&phy->phy);
platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev);
return 0;
}

View File

@ -33,6 +33,7 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/usb/otg.h>
#include <linux/phy/phy.h>
#include <linux/usb/musb-omap.h>
#include <linux/usb/ulpi.h>
#include <linux/i2c/twl.h>
@ -421,17 +422,20 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
}
}
static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off)
static int twl4030_phy_power_off(struct phy *phy)
{
struct twl4030_usb *twl = phy_get_drvdata(phy);
if (twl->asleep)
return;
return 0;
twl4030_phy_power(twl, 0);
twl->asleep = 1;
dev_dbg(twl->dev, "%s\n", __func__);
return 0;
}
static void __twl4030_phy_resume(struct twl4030_usb *twl)
static void __twl4030_phy_power_on(struct twl4030_usb *twl)
{
twl4030_phy_power(twl, 1);
twl4030_i2c_access(twl, 1);
@ -440,11 +444,13 @@ static void __twl4030_phy_resume(struct twl4030_usb *twl)
twl4030_i2c_access(twl, 0);
}
static void twl4030_phy_resume(struct twl4030_usb *twl)
static int twl4030_phy_power_on(struct phy *phy)
{
struct twl4030_usb *twl = phy_get_drvdata(phy);
if (!twl->asleep)
return;
__twl4030_phy_resume(twl);
return 0;
__twl4030_phy_power_on(twl);
twl->asleep = 0;
dev_dbg(twl->dev, "%s\n", __func__);
@ -457,6 +463,7 @@ static void twl4030_phy_resume(struct twl4030_usb *twl)
cancel_delayed_work(&twl->id_workaround_work);
schedule_delayed_work(&twl->id_workaround_work, HZ);
}
return 0;
}
static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
@ -587,9 +594,9 @@ static void twl4030_id_workaround_work(struct work_struct *work)
}
}
static int twl4030_usb_phy_init(struct usb_phy *phy)
static int twl4030_phy_init(struct phy *phy)
{
struct twl4030_usb *twl = phy_to_twl(phy);
struct twl4030_usb *twl = phy_get_drvdata(phy);
enum omap_musb_vbus_id_status status;
/*
@ -602,25 +609,15 @@ static int twl4030_usb_phy_init(struct usb_phy *phy)
status = twl4030_usb_linkstat(twl);
twl->linkstat = status;
if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID)
if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID) {
omap_musb_mailbox(twl->linkstat);
twl4030_phy_power_on(phy);
}
sysfs_notify(&twl->dev->kobj, NULL, "vbus");
return 0;
}
static int twl4030_set_suspend(struct usb_phy *x, int suspend)
{
struct twl4030_usb *twl = phy_to_twl(x);
if (suspend)
twl4030_phy_suspend(twl, 1);
else
twl4030_phy_resume(twl);
return 0;
}
static int twl4030_set_peripheral(struct usb_otg *otg,
struct usb_gadget *gadget)
{
@ -646,13 +643,23 @@ static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host)
return 0;
}
static const struct phy_ops ops = {
.init = twl4030_phy_init,
.power_on = twl4030_phy_power_on,
.power_off = twl4030_phy_power_off,
.owner = THIS_MODULE,
};
static int twl4030_usb_probe(struct platform_device *pdev)
{
struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev);
struct twl4030_usb *twl;
struct phy *phy;
int status, err;
struct usb_otg *otg;
struct device_node *np = pdev->dev.of_node;
struct phy_provider *phy_provider;
struct phy_init_data *init_data = NULL;
twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
if (!twl)
@ -661,9 +668,10 @@ static int twl4030_usb_probe(struct platform_device *pdev)
if (np)
of_property_read_u32(np, "usb_mode",
(enum twl4030_usb_mode *)&twl->usb_mode);
else if (pdata)
else if (pdata) {
twl->usb_mode = pdata->usb_mode;
else {
init_data = pdata->init_data;
} else {
dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
return -EINVAL;
}
@ -682,13 +690,24 @@ static int twl4030_usb_probe(struct platform_device *pdev)
twl->phy.label = "twl4030";
twl->phy.otg = otg;
twl->phy.type = USB_PHY_TYPE_USB2;
twl->phy.set_suspend = twl4030_set_suspend;
twl->phy.init = twl4030_usb_phy_init;
otg->phy = &twl->phy;
otg->set_host = twl4030_set_host;
otg->set_peripheral = twl4030_set_peripheral;
phy_provider = devm_of_phy_provider_register(twl->dev,
of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
phy = devm_phy_create(twl->dev, &ops, init_data);
if (IS_ERR(phy)) {
dev_dbg(&pdev->dev, "Failed to create PHY\n");
return PTR_ERR(phy);
}
phy_set_drvdata(phy, twl);
/* init spinlock for workqueue */
spin_lock_init(&twl->lock);
@ -705,6 +724,8 @@ static int twl4030_usb_probe(struct platform_device *pdev)
if (device_create_file(&pdev->dev, &dev_attr_vbus))
dev_warn(&pdev->dev, "could not create sysfs file\n");
ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
/* Our job is to use irqs and status from the power module
* to keep the transceiver disabled when nothing's connected.
*

View File

@ -34,6 +34,7 @@
#include <linux/stringify.h>
#include <linux/usb.h>
#include <linux/mutex.h>
#include <linux/ratelimit.h>
/*
#define VERBOSE_DEBUG
@ -59,13 +60,12 @@
atm_printk(KERN_INFO, instance , format , ## arg)
#define atm_warn(instance, format, arg...) \
atm_printk(KERN_WARNING, instance , format , ## arg)
#define atm_dbg(instance, format, arg...) \
dynamic_pr_debug("ATM dev %d: " format , \
(instance)->atm_dev->number , ## arg)
#define atm_rldbg(instance, format, arg...) \
if (printk_ratelimit()) \
atm_dbg(instance , format , ## arg)
#define atm_dbg(instance, format, ...) \
pr_debug("ATM dev %d: " format, \
(instance)->atm_dev->number, ##__VA_ARGS__)
#define atm_rldbg(instance, format, ...) \
pr_debug_ratelimited("ATM dev %d: " format, \
(instance)->atm_dev->number, ##__VA_ARGS__)
/* flags, set by mini-driver in bind() */

View File

@ -48,6 +48,7 @@
#define PORTSC_SUSP BIT(7)
#define PORTSC_HSP BIT(9)
#define PORTSC_PTC (0x0FUL << 16)
#define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23))
/* PTS and PTW for non lpm version only */
#define PORTSC_PTS(d) \
(u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0))

View File

@ -108,14 +108,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
}
data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
if (!IS_ERR(data->phy)) {
ret = usb_phy_init(data->phy);
if (ret) {
dev_err(&pdev->dev, "unable to init phy: %d\n", ret);
goto err_clk;
}
} else if (PTR_ERR(data->phy) == -EPROBE_DEFER) {
ret = -EPROBE_DEFER;
if (IS_ERR(data->phy)) {
ret = PTR_ERR(data->phy);
goto err_clk;
}
@ -131,7 +125,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n",
ret);
goto err_phy;
goto err_clk;
}
}
@ -143,7 +137,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Can't register ci_hdrc platform device, err=%d\n",
ret);
goto err_phy;
goto err_clk;
}
if (data->usbmisc_data) {
@ -164,9 +158,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
disable_device:
ci_hdrc_remove_device(data->ci_pdev);
err_phy:
if (data->phy)
usb_phy_shutdown(data->phy);
err_clk:
clk_disable_unprepare(data->clk);
return ret;
@ -178,10 +169,6 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
ci_hdrc_remove_device(data->ci_pdev);
if (data->phy)
usb_phy_shutdown(data->phy);
clk_disable_unprepare(data->clk);
return 0;

View File

@ -172,6 +172,27 @@ u8 hw_port_test_get(struct ci_hdrc *ci)
return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC);
}
/* The PHY enters/leaves low power mode */
static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
{
enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
if (enable && !lpm) {
hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
PORTSC_PHCD(ci->hw_bank.lpm));
} else if (!enable && lpm) {
hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
0);
/*
* The controller needs at least 1ms to reflect
* PHY's status, the PHY also needs some time (less
* than 1ms) to leave low power mode.
*/
usleep_range(1500, 2000);
}
}
static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
{
u32 reg;
@ -199,6 +220,8 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
if (ci->hw_ep_max > ENDPT_MAX)
return -ENODEV;
ci_hdrc_enter_lpm(ci, false);
/* Disable all interrupts bits */
hw_write(ci, OP_USBINTR, 0xffffffff, 0);
@ -369,16 +392,28 @@ static irqreturn_t ci_irq(int irq, void *data)
static int ci_get_platdata(struct device *dev,
struct ci_hdrc_platform_data *platdata)
{
/* Get the vbus regulator */
platdata->reg_vbus = devm_regulator_get(dev, "vbus");
if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) {
return -EPROBE_DEFER;
} else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) {
platdata->reg_vbus = NULL; /* no vbus regualator is needed */
} else if (IS_ERR(platdata->reg_vbus)) {
dev_err(dev, "Getting regulator error: %ld\n",
PTR_ERR(platdata->reg_vbus));
return PTR_ERR(platdata->reg_vbus);
if (!platdata->phy_mode)
platdata->phy_mode = of_usb_get_phy_mode(dev->of_node);
if (!platdata->dr_mode)
platdata->dr_mode = of_usb_get_dr_mode(dev->of_node);
if (platdata->dr_mode == USB_DR_MODE_UNKNOWN)
platdata->dr_mode = USB_DR_MODE_OTG;
if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) {
/* Get the vbus regulator */
platdata->reg_vbus = devm_regulator_get(dev, "vbus");
if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) {
return -EPROBE_DEFER;
} else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) {
/* no vbus regualator is needed */
platdata->reg_vbus = NULL;
} else if (IS_ERR(platdata->reg_vbus)) {
dev_err(dev, "Getting regulator error: %ld\n",
PTR_ERR(platdata->reg_vbus));
return PTR_ERR(platdata->reg_vbus);
}
}
return 0;
@ -465,6 +500,33 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
}
}
static int ci_usb_phy_init(struct ci_hdrc *ci)
{
if (ci->platdata->phy) {
ci->transceiver = ci->platdata->phy;
return usb_phy_init(ci->transceiver);
} else {
ci->global_phy = true;
ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
if (IS_ERR(ci->transceiver))
ci->transceiver = NULL;
return 0;
}
}
static void ci_usb_phy_destroy(struct ci_hdrc *ci)
{
if (!ci->transceiver)
return;
otg_set_peripheral(ci->transceiver->otg, NULL);
if (ci->global_phy)
usb_put_phy(ci->transceiver);
else
usb_phy_shutdown(ci->transceiver);
}
static int ci_hdrc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -473,7 +535,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
void __iomem *base;
int ret;
enum usb_dr_mode dr_mode;
struct device_node *of_node = dev->of_node ?: dev->parent->of_node;
if (!dev->platform_data) {
dev_err(dev, "platform data missing\n");
@ -493,10 +554,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ci->dev = dev;
ci->platdata = dev->platform_data;
if (ci->platdata->phy)
ci->transceiver = ci->platdata->phy;
else
ci->global_phy = true;
ret = hw_device_init(ci, base);
if (ret < 0) {
@ -504,27 +561,25 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
ret = ci_usb_phy_init(ci);
if (ret) {
dev_err(dev, "unable to init phy: %d\n", ret);
return ret;
}
ci->hw_bank.phys = res->start;
ci->irq = platform_get_irq(pdev, 0);
if (ci->irq < 0) {
dev_err(dev, "missing IRQ\n");
return -ENODEV;
ret = -ENODEV;
goto destroy_phy;
}
ci_get_otg_capable(ci);
if (!ci->platdata->phy_mode)
ci->platdata->phy_mode = of_usb_get_phy_mode(of_node);
hw_phymode_configure(ci);
if (!ci->platdata->dr_mode)
ci->platdata->dr_mode = of_usb_get_dr_mode(of_node);
if (ci->platdata->dr_mode == USB_DR_MODE_UNKNOWN)
ci->platdata->dr_mode = USB_DR_MODE_OTG;
dr_mode = ci->platdata->dr_mode;
/* initialize role(s) before the interrupt is requested */
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
@ -537,11 +592,23 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ret = ci_hdrc_gadget_init(ci);
if (ret)
dev_info(dev, "doesn't support gadget\n");
if (!ret && ci->transceiver) {
ret = otg_set_peripheral(ci->transceiver->otg,
&ci->gadget);
/*
* If we implement all USB functions using chipidea drivers,
* it doesn't need to call above API, meanwhile, if we only
* use gadget function, calling above API is useless.
*/
if (ret && ret != -ENOTSUPP)
goto destroy_phy;
}
}
if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
dev_err(dev, "no supported roles\n");
return -ENODEV;
ret = -ENODEV;
goto destroy_phy;
}
if (ci->is_otg) {
@ -594,6 +661,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
free_irq(ci->irq, ci);
stop:
ci_role_destroy(ci);
destroy_phy:
ci_usb_phy_destroy(ci);
return ret;
}
@ -605,6 +674,8 @@ static int ci_hdrc_remove(struct platform_device *pdev)
dbg_remove_files(ci);
free_irq(ci->irq, ci);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
ci_usb_phy_destroy(ci);
kfree(ci->hw_bank.regmap);
return 0;

View File

@ -103,15 +103,15 @@ static void host_stop(struct ci_hdrc *ci)
if (hcd) {
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
if (ci->platdata->reg_vbus)
regulator_disable(ci->platdata->reg_vbus);
}
if (ci->platdata->reg_vbus)
regulator_disable(ci->platdata->reg_vbus);
}
void ci_hdrc_host_destroy(struct ci_hdrc *ci)
{
if (ci->role == CI_ROLE_HOST)
if (ci->role == CI_ROLE_HOST && ci->hcd)
host_stop(ci);
}

View File

@ -20,7 +20,6 @@
#include <linux/pm_runtime.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/usb/chipidea.h>
#include "ci.h"
@ -686,9 +685,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget)
usb_ep_fifo_flush(&ci->ep0out->ep);
usb_ep_fifo_flush(&ci->ep0in->ep);
if (ci->driver)
ci->driver->disconnect(gadget);
/* make sure to disable all endpoints */
gadget_for_each_ep(ep, gadget) {
usb_ep_disable(ep);
@ -718,6 +714,11 @@ __acquires(ci->lock)
int retval;
spin_unlock(&ci->lock);
if (ci->gadget.speed != USB_SPEED_UNKNOWN) {
if (ci->driver)
ci->driver->disconnect(&ci->gadget);
}
retval = _gadget_stop_activity(&ci->gadget);
if (retval)
goto done;
@ -1461,6 +1462,8 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
hw_device_state(ci, ci->ep0out->qh.dma);
dev_dbg(ci->dev, "Connected to host\n");
} else {
if (ci->driver)
ci->driver->disconnect(&ci->gadget);
hw_device_state(ci, 0);
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
@ -1633,23 +1636,22 @@ static int ci_udc_start(struct usb_gadget *gadget,
retval = usb_ep_enable(&ci->ep0in->ep);
if (retval)
return retval;
spin_lock_irqsave(&ci->lock, flags);
ci->driver = driver;
pm_runtime_get_sync(&ci->gadget.dev);
if (ci->vbus_active) {
spin_lock_irqsave(&ci->lock, flags);
hw_device_reset(ci, USBMODE_CM_DC);
} else {
pm_runtime_put_sync(&ci->gadget.dev);
goto done;
return retval;
}
retval = hw_device_state(ci, ci->ep0out->qh.dma);
spin_unlock_irqrestore(&ci->lock, flags);
if (retval)
pm_runtime_put_sync(&ci->gadget.dev);
done:
spin_unlock_irqrestore(&ci->lock, flags);
return retval;
}
@ -1786,34 +1788,9 @@ static int udc_start(struct ci_hdrc *ci)
ci->gadget.ep0 = &ci->ep0in->ep;
if (ci->global_phy) {
ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
if (IS_ERR(ci->transceiver))
ci->transceiver = NULL;
}
if (ci->platdata->flags & CI_HDRC_REQUIRE_TRANSCEIVER) {
if (ci->transceiver == NULL) {
retval = -ENODEV;
goto destroy_eps;
}
}
if (ci->transceiver) {
retval = otg_set_peripheral(ci->transceiver->otg,
&ci->gadget);
/*
* If we implement all USB functions using chipidea drivers,
* it doesn't need to call above API, meanwhile, if we only
* use gadget function, calling above API is useless.
*/
if (retval && retval != -ENOTSUPP)
goto put_transceiver;
}
retval = usb_add_gadget_udc(dev, &ci->gadget);
if (retval)
goto remove_trans;
goto destroy_eps;
pm_runtime_no_callbacks(&ci->gadget.dev);
pm_runtime_enable(&ci->gadget.dev);
@ -1823,17 +1800,6 @@ static int udc_start(struct ci_hdrc *ci)
return retval;
remove_trans:
if (ci->transceiver) {
otg_set_peripheral(ci->transceiver->otg, NULL);
if (ci->global_phy)
usb_put_phy(ci->transceiver);
}
dev_err(dev, "error = %i\n", retval);
put_transceiver:
if (ci->transceiver && ci->global_phy)
usb_put_phy(ci->transceiver);
destroy_eps:
destroy_eps(ci);
free_pools:

View File

@ -101,6 +101,7 @@ struct wdm_device {
struct work_struct rxwork;
int werr;
int rerr;
int resp_count;
struct list_head device_list;
int (*manage_power)(struct usb_interface *, int);
@ -253,6 +254,10 @@ static void wdm_int_callback(struct urb *urb)
"NOTIFY_NETWORK_CONNECTION %s network",
dr->wValue ? "connected to" : "disconnected from");
goto exit;
case USB_CDC_NOTIFY_SPEED_CHANGE:
dev_dbg(&desc->intf->dev, "SPEED_CHANGE received (len %u)",
urb->actual_length);
goto exit;
default:
clear_bit(WDM_POLL_RUNNING, &desc->flags);
dev_err(&desc->intf->dev,
@ -262,9 +267,9 @@ static void wdm_int_callback(struct urb *urb)
}
spin_lock(&desc->iuspin);
clear_bit(WDM_READ, &desc->flags);
responding = test_and_set_bit(WDM_RESPONDING, &desc->flags);
if (!responding && !test_bit(WDM_DISCONNECTING, &desc->flags)
if (!desc->resp_count++ && !responding
&& !test_bit(WDM_DISCONNECTING, &desc->flags)
&& !test_bit(WDM_SUSPENDING, &desc->flags)) {
rv = usb_submit_urb(desc->response, GFP_ATOMIC);
dev_dbg(&desc->intf->dev, "%s: usb_submit_urb %d",
@ -521,10 +526,36 @@ retry:
desc->length -= cntr;
/* in case we had outstanding data */
if (!desc->length)
if (!desc->length) {
clear_bit(WDM_READ, &desc->flags);
spin_unlock_irq(&desc->iuspin);
if (--desc->resp_count) {
set_bit(WDM_RESPONDING, &desc->flags);
spin_unlock_irq(&desc->iuspin);
rv = usb_submit_urb(desc->response, GFP_KERNEL);
if (rv) {
dev_err(&desc->intf->dev,
"%s: usb_submit_urb failed with result %d\n",
__func__, rv);
spin_lock_irq(&desc->iuspin);
clear_bit(WDM_RESPONDING, &desc->flags);
spin_unlock_irq(&desc->iuspin);
if (rv == -ENOMEM) {
rv = schedule_work(&desc->rxwork);
if (rv)
dev_err(&desc->intf->dev, "Cannot schedule work\n");
} else {
spin_lock_irq(&desc->iuspin);
desc->resp_count = 0;
spin_unlock_irq(&desc->iuspin);
}
}
} else
spin_unlock_irq(&desc->iuspin);
} else
spin_unlock_irq(&desc->iuspin);
rv = cntr;
@ -635,6 +666,9 @@ static int wdm_release(struct inode *inode, struct file *file)
if (!test_bit(WDM_DISCONNECTING, &desc->flags)) {
dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
kill_urbs(desc);
spin_lock_irq(&desc->iuspin);
desc->resp_count = 0;
spin_unlock_irq(&desc->iuspin);
desc->manage_power(desc->intf, 0);
} else {
/* must avoid dev_printk here as desc->intf is invalid */

View File

@ -914,10 +914,8 @@ static int proc_control(struct dev_state *ps, void __user *arg)
snoop(&dev->dev, "control urb: bRequestType=%02x "
"bRequest=%02x wValue=%04x "
"wIndex=%04x wLength=%04x\n",
ctrl.bRequestType, ctrl.bRequest,
__le16_to_cpup(&ctrl.wValue),
__le16_to_cpup(&ctrl.wIndex),
__le16_to_cpup(&ctrl.wLength));
ctrl.bRequestType, ctrl.bRequest, ctrl.wValue,
ctrl.wIndex, ctrl.wLength);
if (ctrl.bRequestType & 0x80) {
if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data,
ctrl.wLength)) {
@ -1636,32 +1634,32 @@ static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
static int proc_control_compat(struct dev_state *ps,
struct usbdevfs_ctrltransfer32 __user *p32)
{
struct usbdevfs_ctrltransfer __user *p;
__u32 udata;
p = compat_alloc_user_space(sizeof(*p));
if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
get_user(udata, &p32->data) ||
struct usbdevfs_ctrltransfer __user *p;
__u32 udata;
p = compat_alloc_user_space(sizeof(*p));
if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
get_user(udata, &p32->data) ||
put_user(compat_ptr(udata), &p->data))
return -EFAULT;
return proc_control(ps, p);
return proc_control(ps, p);
}
static int proc_bulk_compat(struct dev_state *ps,
struct usbdevfs_bulktransfer32 __user *p32)
{
struct usbdevfs_bulktransfer __user *p;
compat_uint_t n;
compat_caddr_t addr;
struct usbdevfs_bulktransfer __user *p;
compat_uint_t n;
compat_caddr_t addr;
p = compat_alloc_user_space(sizeof(*p));
p = compat_alloc_user_space(sizeof(*p));
if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
get_user(n, &p32->len) || put_user(n, &p->len) ||
get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
return -EFAULT;
if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
get_user(n, &p32->len) || put_user(n, &p->len) ||
get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
return -EFAULT;
return proc_bulk(ps, p);
return proc_bulk(ps, p);
}
static int proc_disconnectsignal_compat(struct dev_state *ps, void __user *arg)
{

View File

@ -1179,8 +1179,8 @@ static int usb_resume_interface(struct usb_device *udev,
"reset_resume", status);
} else {
intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?\n",
"reset_resume", driver->name);
dev_dbg(&intf->dev, "no reset_resume for driver %s?\n",
driver->name);
}
} else {
status = driver->resume(intf);
@ -1790,6 +1790,9 @@ int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
int ret = -EPERM;
if (enable && !udev->usb2_hw_lpm_allowed)
return 0;
if (hcd->driver->set_usb2_hw_lpm) {
ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable);
if (!ret)

View File

@ -8,7 +8,7 @@
* (C) Copyright Deti Fliegl 1999 (new USB architecture)
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
more docs, etc)
* more docs, etc)
* (C) Copyright Yggdrasil Computing, Inc. 2000
* (usb_device_id matching changes by Adam J. Richter)
* (C) Copyright Greg Kroah-Hartman 2002-2003
@ -27,7 +27,7 @@
static const struct file_operations *usb_minors[MAX_USB_MINORS];
static DECLARE_RWSEM(minor_rwsem);
static int usb_open(struct inode * inode, struct file * file)
static int usb_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
const struct file_operations *c;
@ -44,7 +44,7 @@ static int usb_open(struct inode * inode, struct file * file)
file->f_op = new_fops;
/* Curiouser and curiouser... NULL ->open() as "no device" ? */
if (file->f_op->open)
err = file->f_op->open(inode,file);
err = file->f_op->open(inode, file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
@ -166,7 +166,7 @@ int usb_register_dev(struct usb_interface *intf,
char *temp;
#ifdef CONFIG_USB_DYNAMIC_MINORS
/*
/*
* We don't care what the device tries to start at, we want to start
* at zero to pack the devices into the smallest available space with
* no holes in the minor range.

View File

@ -215,6 +215,9 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
goto disable_pci;
}
hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) &&
driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0;
if (driver->flags & HCD_MEMORY) {
/* EHCI, OHCI */
hcd->rsrc_start = pci_resource_start(dev, 0);

View File

@ -6,7 +6,7 @@
* (C) Copyright Deti Fliegl 1999
* (C) Copyright Randy Dunlap 2000
* (C) Copyright David Brownell 2000-2002
*
*
* 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 of the License, or (at your
@ -40,6 +40,7 @@
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
@ -92,10 +93,7 @@ EXPORT_SYMBOL_GPL (usb_bus_list);
/* used when allocating bus numbers */
#define USB_MAXBUS 64
struct usb_busmap {
unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];
};
static struct usb_busmap busmap;
static DECLARE_BITMAP(busmap, USB_MAXBUS);
/* used when updating list of hcds */
DEFINE_MUTEX(usb_bus_list_lock); /* exported only for usbfs */
@ -171,7 +169,7 @@ static const u8 usb25_rh_dev_descriptor[18] = {
};
/* usb 2.0 root hub device descriptor */
static const u8 usb2_rh_dev_descriptor [18] = {
static const u8 usb2_rh_dev_descriptor[18] = {
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x00, 0x02, /* __le16 bcdUSB; v2.0 */
@ -194,7 +192,7 @@ static const u8 usb2_rh_dev_descriptor [18] = {
/* no usb 2.0 root hub "device qualifier" descriptor: one speed only */
/* usb 1.1 root hub device descriptor */
static const u8 usb11_rh_dev_descriptor [18] = {
static const u8 usb11_rh_dev_descriptor[18] = {
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x10, 0x01, /* __le16 bcdUSB; v1.1 */
@ -219,7 +217,7 @@ static const u8 usb11_rh_dev_descriptor [18] = {
/* Configuration descriptors for our root hubs */
static const u8 fs_rh_config_descriptor [] = {
static const u8 fs_rh_config_descriptor[] = {
/* one configuration */
0x09, /* __u8 bLength; */
@ -228,13 +226,13 @@ static const u8 fs_rh_config_descriptor [] = {
0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
0xc0, /* __u8 bmAttributes;
0xc0, /* __u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x00, /* __u8 MaxPower; */
/* USB 1.1:
* USB 2.0, single TT organization (mandatory):
* one interface, protocol 0
@ -256,17 +254,17 @@ static const u8 fs_rh_config_descriptor [] = {
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
0x00, /* __u8 if_iInterface; */
/* one endpoint (status change endpoint) */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
0x03, /* __u8 ep_bmAttributes; Interrupt */
0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
0xff /* __u8 ep_bInterval; (255ms -- usb 2.0 spec) */
};
static const u8 hs_rh_config_descriptor [] = {
static const u8 hs_rh_config_descriptor[] = {
/* one configuration */
0x09, /* __u8 bLength; */
@ -275,13 +273,13 @@ static const u8 hs_rh_config_descriptor [] = {
0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
0xc0, /* __u8 bmAttributes;
0xc0, /* __u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x00, /* __u8 MaxPower; */
/* USB 1.1:
* USB 2.0, single TT organization (mandatory):
* one interface, protocol 0
@ -303,12 +301,12 @@ static const u8 hs_rh_config_descriptor [] = {
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
0x00, /* __u8 if_iInterface; */
/* one endpoint (status change endpoint) */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
0x03, /* __u8 ep_bmAttributes; Interrupt */
/* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
* see hub.c:hub_configure() for details. */
(USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
@ -428,7 +426,7 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
char const *s;
static char const langids[4] = {4, USB_DT_STRING, 0x09, 0x04};
// language ids
/* language ids */
switch (id) {
case 0:
/* Array of LANGID codes (0x0409 is MSFT-speak for "en-us") */
@ -464,7 +462,7 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
{
struct usb_ctrlrequest *cmd;
u16 typeReq, wValue, wIndex, wLength;
u16 typeReq, wValue, wIndex, wLength;
u8 *ubuf = urb->transfer_buffer;
unsigned len = 0;
int status;
@ -526,10 +524,10 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
*/
case DeviceRequest | USB_REQ_GET_STATUS:
tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)
tbuf[0] = (device_may_wakeup(&hcd->self.root_hub->dev)
<< USB_DEVICE_REMOTE_WAKEUP)
| (1 << USB_DEVICE_SELF_POWERED);
tbuf [1] = 0;
tbuf[1] = 0;
len = 2;
break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
@ -546,7 +544,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
goto error;
break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
tbuf [0] = 1;
tbuf[0] = 1;
len = 1;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
@ -609,13 +607,13 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
}
break;
case DeviceRequest | USB_REQ_GET_INTERFACE:
tbuf [0] = 0;
tbuf[0] = 0;
len = 1;
/* FALLTHROUGH */
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
break;
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
// wValue == urb->dev->devaddr
/* wValue == urb->dev->devaddr */
dev_dbg (hcd->self.controller, "root hub device address %d\n",
wValue);
break;
@ -625,9 +623,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
/* ENDPOINT REQUESTS */
case EndpointRequest | USB_REQ_GET_STATUS:
// ENDPOINT_HALT flag
tbuf [0] = 0;
tbuf [1] = 0;
/* ENDPOINT_HALT flag */
tbuf[0] = 0;
tbuf[1] = 0;
len = 2;
/* FALLTHROUGH */
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
@ -683,7 +681,7 @@ error:
if (urb->transfer_buffer_length < len)
len = urb->transfer_buffer_length;
urb->actual_length = len;
// always USB_DIR_IN, toward host
/* always USB_DIR_IN, toward host */
memcpy (ubuf, bufp, len);
/* report whether RH hardware supports remote wakeup */
@ -877,11 +875,11 @@ static ssize_t authorized_default_store(struct device *dev,
usb_hcd = bus_to_hcd(usb_bus);
result = sscanf(buf, "%u\n", &val);
if (result == 1) {
usb_hcd->authorized_default = val? 1 : 0;
usb_hcd->authorized_default = val ? 1 : 0;
result = size;
}
else
} else {
result = -EINVAL;
}
return result;
}
static DEVICE_ATTR_RW(authorized_default);
@ -941,12 +939,12 @@ static int usb_register_bus(struct usb_bus *bus)
int busnum;
mutex_lock(&usb_bus_list_lock);
busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
busnum = find_next_zero_bit(busmap, USB_MAXBUS, 1);
if (busnum >= USB_MAXBUS) {
printk (KERN_ERR "%s: too many buses\n", usbcore_name);
goto error_find_busnum;
}
set_bit (busnum, busmap.busmap);
set_bit(busnum, busmap);
bus->busnum = busnum;
/* Add it to the local list of buses */
@ -987,7 +985,7 @@ static void usb_deregister_bus (struct usb_bus *bus)
usb_notify_remove_bus(bus);
clear_bit (bus->busnum, busmap.busmap);
clear_bit(bus->busnum, busmap);
}
/**
@ -1033,6 +1031,7 @@ static int register_root_hub(struct usb_hcd *hcd)
dev_name(&usb_dev->dev), retval);
return retval;
}
usb_dev->lpm_capable = usb_device_supports_lpm(usb_dev);
}
retval = usb_new_device (usb_dev);
@ -1120,21 +1119,21 @@ long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
case USB_SPEED_LOW: /* INTR only */
if (is_input) {
tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
return 64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
} else {
tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
return 64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
}
case USB_SPEED_FULL: /* ISOC or INTR */
if (isoc) {
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
return ((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp;
} else {
tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
return (9107L + BW_HOST_DELAY + tmp);
return 9107L + BW_HOST_DELAY + tmp;
}
case USB_SPEED_HIGH: /* ISOC or INTR */
// FIXME adjust for input vs output
/* FIXME adjust for input vs output */
if (isoc)
tmp = HS_NSECS_ISO (bytecount);
else
@ -1651,6 +1650,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
static void __usb_hcd_giveback_urb(struct urb *urb)
{
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
struct usb_anchor *anchor = urb->anchor;
int status = urb->unlinked;
unsigned long flags;
@ -1662,6 +1662,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
unmap_urb_for_dma(hcd, urb);
usbmon_urb_complete(&hcd->self, urb, status);
usb_anchor_suspend_wakeups(anchor);
usb_unanchor_urb(urb);
/* pass ownership to the completion handler */
@ -1681,6 +1682,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
urb->complete(urb);
local_irq_restore(flags);
usb_anchor_resume_wakeups(anchor);
atomic_dec(&urb->use_count);
if (unlikely(atomic_read(&urb->reject)))
wake_up(&usb_kill_urb_queue);
@ -1703,7 +1705,9 @@ static void usb_giveback_urb_bh(unsigned long param)
urb = list_entry(local_list.next, struct urb, urb_list);
list_del_init(&urb->urb_list);
bh->completing_ep = urb->ep;
__usb_hcd_giveback_urb(urb);
bh->completing_ep = NULL;
}
/* check if there are new URBs to giveback */
@ -1812,7 +1816,7 @@ rescan:
case USB_ENDPOINT_XFER_INT:
s = "-intr"; break;
default:
s = "-iso"; break;
s = "-iso"; break;
};
s;
}));
@ -2073,8 +2077,11 @@ EXPORT_SYMBOL_GPL(usb_alloc_streams);
*
* Reverts a group of bulk endpoints back to not using stream IDs.
* Can fail if we are given bad arguments, or HCD is broken.
*
* Return: On success, the number of allocated streams. On failure, a negative
* error code.
*/
void usb_free_streams(struct usb_interface *interface,
int usb_free_streams(struct usb_interface *interface,
struct usb_host_endpoint **eps, unsigned int num_eps,
gfp_t mem_flags)
{
@ -2085,14 +2092,14 @@ void usb_free_streams(struct usb_interface *interface,
dev = interface_to_usbdev(interface);
hcd = bus_to_hcd(dev->bus);
if (dev->speed != USB_SPEED_SUPER)
return;
return -EINVAL;
/* Streams only apply to bulk endpoints. */
for (i = 0; i < num_eps; i++)
if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc))
return;
return -EINVAL;
hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
return hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
}
EXPORT_SYMBOL_GPL(usb_free_streams);
@ -2245,7 +2252,7 @@ static void hcd_resume_work(struct work_struct *work)
}
/**
* usb_hcd_resume_root_hub - called by HCD to resume its root hub
* usb_hcd_resume_root_hub - called by HCD to resume its root hub
* @hcd: host controller for this root hub
*
* The USB host controller calls this function when its root hub is
@ -2324,15 +2331,8 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
unsigned long flags;
irqreturn_t rc;
/* IRQF_DISABLED doesn't work correctly with shared IRQs
* when the first handler doesn't use it. So let's just
* assume it's never used.
*/
local_irq_save(flags);
if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
rc = IRQ_NONE;
else if (hcd->driver->irq(hcd) == IRQ_NONE)
@ -2340,7 +2340,6 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
else
rc = IRQ_HANDLED;
local_irq_restore(flags);
return rc;
}
EXPORT_SYMBOL_GPL(usb_hcd_irq);
@ -2547,13 +2546,6 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
if (hcd->driver->irq) {
/* IRQF_DISABLED doesn't work as advertised when used together
* with IRQF_SHARED. As usb_hcd_irq() will always disable
* interrupts we can remove it here.
*/
if (irqflags & IRQF_SHARED)
irqflags &= ~IRQF_DISABLED;
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
hcd->driver->description, hcd->self.busnum);
retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
@ -2600,7 +2592,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
/* Keep old behaviour if authorized_default is not in [0, 1]. */
if (authorized_default < 0 || authorized_default > 1)
hcd->authorized_default = hcd->wireless? 0 : 1;
hcd->authorized_default = hcd->wireless ? 0 : 1;
else
hcd->authorized_default = authorized_default;
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
@ -2743,7 +2735,7 @@ err_allocate_root_hub:
err_register_bus:
hcd_buffer_destroy(hcd);
return retval;
}
}
EXPORT_SYMBOL_GPL(usb_add_hcd);
/**
@ -2818,7 +2810,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
EXPORT_SYMBOL_GPL(usb_remove_hcd);
void
usb_hcd_platform_shutdown(struct platform_device* dev)
usb_hcd_platform_shutdown(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
@ -2840,7 +2832,7 @@ struct usb_mon_operations *mon_ops;
* Notice that the code is minimally error-proof. Because usbmon needs
* symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
*/
int usb_mon_register (struct usb_mon_operations *ops)
{

View File

@ -120,7 +120,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus)
if (hub_is_superspeed(hub->hdev))
return "5.0 Gb/s";
if (portstatus & USB_PORT_STAT_HIGH_SPEED)
return "480 Mb/s";
return "480 Mb/s";
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
return "1.5 Mb/s";
else
@ -135,7 +135,7 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)
return usb_get_intfdata(hdev->actconfig->interface[0]);
}
static int usb_device_supports_lpm(struct usb_device *udev)
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.
@ -156,6 +156,11 @@ static int usb_device_supports_lpm(struct usb_device *udev)
"Power management will be impacted.\n");
return 0;
}
/* udev is root hub */
if (!udev->parent)
return 1;
if (udev->parent->lpm_capable)
return 1;
@ -310,9 +315,9 @@ static void usb_set_lpm_parameters(struct usb_device *udev)
return;
udev_u1_del = udev->bos->ss_cap->bU1devExitLat;
udev_u2_del = udev->bos->ss_cap->bU2DevExitLat;
udev_u2_del = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat);
hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat;
hub_u2_del = udev->parent->bos->ss_cap->bU2DevExitLat;
hub_u2_del = le16_to_cpu(udev->parent->bos->ss_cap->bU2DevExitLat);
usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del,
hub, &udev->parent->u1_params, hub_u1_del);
@ -433,7 +438,7 @@ static void set_port_led(
case HUB_LED_OFF: s = "off"; break;
case HUB_LED_AUTO: s = "auto"; break;
default: s = "??"; break;
}; s; }),
} s; }),
status);
}
@ -857,7 +862,7 @@ static int hub_hub_status(struct usb_hub *hub,
"%s failed (err = %d)\n", __func__, ret);
} else {
*status = le16_to_cpu(hub->status->hub.wHubStatus);
*change = le16_to_cpu(hub->status->hub.wHubChange);
*change = le16_to_cpu(hub->status->hub.wHubChange);
ret = 0;
}
mutex_unlock(&hub->status_mutex);
@ -956,7 +961,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
*/
set_bit(port1, hub->change_bits);
kick_khubd(hub);
kick_khubd(hub);
}
/**
@ -1107,16 +1112,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
/*
* USB3 protocol ports will automatically transition
* to Enabled state when detect an USB3.0 device attach.
* Do not disable USB3 protocol ports.
* Do not disable USB3 protocol ports, just pretend
* power was lost
*/
if (!hub_is_superspeed(hdev)) {
portstatus &= ~USB_PORT_STAT_ENABLE;
if (!hub_is_superspeed(hdev))
usb_clear_port_feature(hdev, port1,
USB_PORT_FEAT_ENABLE);
portstatus &= ~USB_PORT_STAT_ENABLE;
} else {
/* Pretend that power was lost for USB3 devs */
portstatus &= ~USB_PORT_STAT_ENABLE;
}
}
/* Clear status-change flags; we'll debounce later */
@ -1130,6 +1132,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE);
}
if (portchange & USB_PORT_STAT_C_RESET) {
need_debounce_delay = true;
usb_clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_RESET);
}
if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
hub_is_superspeed(hub->hdev)) {
need_debounce_delay = true;
@ -1361,7 +1368,7 @@ static int hub_configure(struct usb_hub *hub,
if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
!(hub_is_superspeed(hdev))) {
int i;
char portstr [USB_MAXCHILDREN + 1];
char portstr[USB_MAXCHILDREN + 1];
for (i = 0; i < hdev->maxchild; i++)
portstr[i] = hub->descriptor->u.hs.DeviceRemovable
@ -1429,32 +1436,32 @@ static int hub_configure(struct usb_hub *hub,
/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
switch (wHubCharacteristics & HUB_CHAR_TTTT) {
case HUB_TTTT_8_BITS:
if (hdev->descriptor.bDeviceProtocol != 0) {
hub->tt.think_time = 666;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
8, hub->tt.think_time);
}
break;
case HUB_TTTT_16_BITS:
hub->tt.think_time = 666 * 2;
case HUB_TTTT_8_BITS:
if (hdev->descriptor.bDeviceProtocol != 0) {
hub->tt.think_time = 666;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
16, hub->tt.think_time);
break;
case HUB_TTTT_24_BITS:
hub->tt.think_time = 666 * 3;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
24, hub->tt.think_time);
break;
case HUB_TTTT_32_BITS:
hub->tt.think_time = 666 * 4;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
32, hub->tt.think_time);
break;
8, hub->tt.think_time);
}
break;
case HUB_TTTT_16_BITS:
hub->tt.think_time = 666 * 2;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
16, hub->tt.think_time);
break;
case HUB_TTTT_24_BITS:
hub->tt.think_time = 666 * 3;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
24, hub->tt.think_time);
break;
case HUB_TTTT_32_BITS:
hub->tt.think_time = 666 * 4;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
32, hub->tt.think_time);
break;
}
/* probe() zeroes hub->indicator[] */
@ -1560,7 +1567,7 @@ static int hub_configure(struct usb_hub *hub,
/* maybe cycle the hub leds */
if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
hub->indicator[0] = INDICATOR_CYCLE;
for (i = 0; i < hdev->maxchild; i++) {
ret = usb_hub_create_port_device(hub, i + 1);
@ -1978,7 +1985,7 @@ static void choose_devnum(struct usb_device *udev)
if (devnum >= 128)
devnum = find_next_zero_bit(bus->devmap.devicemap,
128, 1);
bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
}
if (devnum < 128) {
set_bit(devnum, bus->devmap.devicemap);
@ -2018,8 +2025,8 @@ static void hub_free_dev(struct usb_device *udev)
* Something got disconnected. Get rid of it and all of its children.
*
* If *pdev is a normal device then the parent hub must already be locked.
* If *pdev is a root hub then this routine will acquire the
* usb_bus_list_lock on behalf of the caller.
* If *pdev is a root hub then the caller must hold the usb_bus_list_lock,
* which protects the set of root hubs as well as the list of buses.
*
* Only hub drivers (including virtual root hub drivers for host
* controllers) should ever call this.
@ -2232,8 +2239,7 @@ static int usb_enumerate_device(struct usb_device *udev)
udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
}
else {
} else {
/* read the standard strings and cache them if present */
udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
udev->manufacturer = usb_cache_string(udev,
@ -2489,7 +2495,7 @@ error_device_descriptor:
usb_autosuspend_device(usb_dev);
error_autoresume:
out_authorized:
usb_unlock_device(usb_dev); // complements locktree
usb_unlock_device(usb_dev); /* complements locktree */
return result;
}
@ -3108,8 +3114,8 @@ static int finish_port_resume(struct usb_device *udev)
retry_reset_resume:
status = usb_reset_and_verify_device(udev);
/* 10.5.4.5 says be sure devices in the tree are still there.
* For now let's assume the device didn't go crazy on resume,
/* 10.5.4.5 says be sure devices in the tree are still there.
* For now let's assume the device didn't go crazy on resume,
* and device drivers will know about any resume quirks.
*/
if (status == 0) {
@ -3211,7 +3217,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
if (status == 0 && !port_is_suspended(hub, portstatus))
goto SuspendCleared;
// dev_dbg(hub->intfdev, "resume port %d\n", port1);
/* dev_dbg(hub->intfdev, "resume port %d\n", port1); */
set_bit(port1, hub->busy_bits);
@ -3855,7 +3861,7 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
* Between connect detection and reset signaling there must be a delay
* of 100ms at least for debounce and power-settling. The corresponding
* timer shall restart whenever the downstream port detects a disconnect.
*
*
* Apparently there are some bluetooth and irda-dongles and a number of
* low-speed devices for which this debounce period may last over a second.
* Not covered by the spec - but easy to deal with.
@ -3949,6 +3955,32 @@ static int hub_set_address(struct usb_device *udev, int devnum)
return retval;
}
/*
* There are reports of USB 3.0 devices that say they support USB 2.0 Link PM
* when they're plugged into a USB 2.0 port, but they don't work when LPM is
* enabled.
*
* Only enable USB 2.0 Link PM if the port is internal (hardwired), or the
* device says it supports the new USB 2.0 Link PM errata by setting the BESL
* support bit in the BOS descriptor.
*/
static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
{
int connect_type;
if (!udev->usb2_hw_lpm_capable)
return;
connect_type = usb_get_hub_port_connect_type(udev->parent,
udev->portnum);
if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) ||
connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
udev->usb2_hw_lpm_allowed = 1;
usb_set_usb2_hardware_lpm(udev, 1);
}
}
/* Reset device, (re)assign address, get device descriptor.
* Device connection must be stable, no more debouncing needed.
* Returns device in USB_STATE_ADDRESS, except on error.
@ -4055,7 +4087,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->tt = &hub->tt;
udev->ttport = port1;
}
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
* Because device hardware and firmware is sometimes buggy in
* this area, and this is how Linux has done it for ages.
@ -4130,11 +4162,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
#undef GET_DESCRIPTOR_BUFSIZE
}
/*
* If device is WUSB, we already assigned an
* unauthorized address in the Connect Ack sequence;
* authorization will assign the final address.
*/
/*
* If device is WUSB, we already assigned an
* unauthorized address in the Connect Ack sequence;
* authorization will assign the final address.
*/
if (udev->wusb == 0) {
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
retval = hub_set_address(udev, devnum);
@ -4163,7 +4195,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
msleep(10);
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
break;
}
}
retval = usb_get_device_descriptor(udev, 8);
if (retval < 8) {
@ -4219,7 +4251,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
usb_ep0_reinit(udev);
}
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
if (retval != -ENODEV)
@ -4242,6 +4274,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
/* notify HCD that we have a device connected and addressed */
if (hcd->driver->update_device)
hcd->driver->update_device(hcd, udev);
hub_set_initial_usb2_lpm_policy(udev);
fail:
if (retval) {
hub_port_disable(hub, port1, 0);
@ -4316,7 +4349,7 @@ hub_power_remaining (struct usb_hub *hub)
}
if (remaining < 0) {
dev_warn(hub->intfdev, "%dmA over power budget!\n",
- remaining);
-remaining);
remaining = 0;
}
return remaining;
@ -4427,7 +4460,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
if (portstatus & USB_PORT_STAT_ENABLE)
goto done;
goto done;
return;
}
if (hub_is_superspeed(hub->hdev))
@ -4450,7 +4483,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
usb_set_device_state(udev, USB_STATE_POWERED);
udev->bus_mA = hub->mA_per_port;
udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + 1;
udev->wusb = hub_is_wusb(hub);
@ -4504,7 +4537,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
goto loop_disable;
}
}
/* check for devices running slower than they could */
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
&& udev->speed == USB_SPEED_FULL
@ -4564,7 +4597,7 @@ loop:
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
port1);
}
done:
hub_port_disable(hub, port1, 1);
if (hcd->driver->relinquish_port && !hub->hdev->parent)
@ -4729,7 +4762,7 @@ static void hub_events(void)
* EM interference sometimes causes badly
* shielded USB devices to be shutdown by
* the hub, this hack enables them again.
* Works at least with mouse driver.
* Works at least with mouse driver.
*/
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
@ -4841,7 +4874,7 @@ static void hub_events(void)
dev_dbg(hub_dev, "over-current change\n");
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
msleep(500); /* Cool down */
hub_power_on(hub, true);
hub_power_on(hub, true);
hub_hub_status(hub, &status, &unused);
if (status & HUB_STATUS_OVERCURRENT)
dev_err(hub_dev, "over-current "
@ -4861,7 +4894,7 @@ static void hub_events(void)
usb_unlock_device(hdev);
kref_put(&hub->kref, hub_release);
} /* end while (1) */
} /* end while (1) */
}
static int hub_thread(void *__unused)
@ -4886,7 +4919,7 @@ static int hub_thread(void *__unused)
static const struct usb_device_id hub_id_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
| USB_DEVICE_ID_MATCH_INT_CLASS,
| USB_DEVICE_ID_MATCH_INT_CLASS,
.idVendor = USB_VENDOR_GENESYS_LOGIC,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
@ -5086,6 +5119,12 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
}
parent_hub = usb_hub_to_struct_hub(parent_hdev);
/* Disable USB2 hardware LPM.
* It will be re-enabled by the enumeration process.
*/
if (udev->usb2_hw_lpm_enabled == 1)
usb_set_usb2_hardware_lpm(udev, 0);
bos = udev->bos;
udev->bos = NULL;
@ -5120,13 +5159,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (ret < 0)
goto re_enumerate;
/* Device might have changed firmware (DFU or similar) */
if (descriptors_changed(udev, &descriptor, bos)) {
dev_info(&udev->dev, "device firmware changed\n");
udev->descriptor = descriptor; /* for disconnect() calls */
goto re_enumerate;
}
}
/* Restore the device's previous configuration */
if (!udev->actconfig)
@ -5151,7 +5190,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
udev->actconfig->desc.bConfigurationValue, ret);
mutex_unlock(hcd->bandwidth_mutex);
goto re_enumerate;
}
}
mutex_unlock(hcd->bandwidth_mutex);
usb_set_device_state(udev, USB_STATE_CONFIGURED);
@ -5193,12 +5232,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
done:
/* Now that the alt settings are re-installed, enable LTM and LPM. */
usb_set_usb2_hardware_lpm(udev, 1);
usb_unlocked_enable_lpm(udev);
usb_enable_ltm(udev);
usb_release_bos_descriptor(udev);
udev->bos = bos;
return 0;
re_enumerate:
/* LPM state doesn't matter when we're about to destroy the device. */
hub_port_logical_disconnect(parent_hub, port1);

View File

@ -1182,8 +1182,12 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
put_device(&dev->actconfig->interface[i]->dev);
dev->actconfig->interface[i] = NULL;
}
if (dev->usb2_hw_lpm_enabled == 1)
usb_set_usb2_hardware_lpm(dev, 0);
usb_unlocked_disable_lpm(dev);
usb_disable_ltm(dev);
dev->actconfig = NULL;
if (dev->state == USB_STATE_CONFIGURED)
usb_set_device_state(dev, USB_STATE_ADDRESS);

View File

@ -13,6 +13,7 @@
#include <linux/usb.h>
#include <linux/usb/quirks.h>
#include <linux/usb/hcd.h>
#include "usb.h"
/* Lists of quirky USB devices, split in device quirks and interface quirks.
@ -161,6 +162,21 @@ static const struct usb_device_id usb_interface_quirk_list[] = {
{ } /* terminating entry must be last */
};
static const struct usb_device_id usb_amd_resume_quirk_list[] = {
/* Lenovo Mouse with Pixart controller */
{ USB_DEVICE(0x17ef, 0x602e), .driver_info = USB_QUIRK_RESET_RESUME },
/* Pixart Mouse */
{ USB_DEVICE(0x093a, 0x2500), .driver_info = USB_QUIRK_RESET_RESUME },
{ USB_DEVICE(0x093a, 0x2510), .driver_info = USB_QUIRK_RESET_RESUME },
{ USB_DEVICE(0x093a, 0x2521), .driver_info = USB_QUIRK_RESET_RESUME },
/* Logitech Optical Mouse M90/M100 */
{ USB_DEVICE(0x046d, 0xc05a), .driver_info = USB_QUIRK_RESET_RESUME },
{ } /* terminating entry must be last */
};
static bool usb_match_any_interface(struct usb_device *udev,
const struct usb_device_id *id)
{
@ -187,6 +203,18 @@ static bool usb_match_any_interface(struct usb_device *udev,
return false;
}
static int usb_amd_resume_quirk(struct usb_device *udev)
{
struct usb_hcd *hcd;
hcd = bus_to_hcd(udev->bus);
/* The device should be attached directly to root hub */
if (udev->level == 1 && hcd->amd_resume_bug == 1)
return 1;
return 0;
}
static u32 __usb_detect_quirks(struct usb_device *udev,
const struct usb_device_id *id)
{
@ -212,6 +240,15 @@ static u32 __usb_detect_quirks(struct usb_device *udev,
void usb_detect_quirks(struct usb_device *udev)
{
udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
/*
* Pixart-based mice would trigger remote wakeup issue on AMD
* Yangtze chipset, so set them as RESET_RESUME flag.
*/
if (usb_amd_resume_quirk(udev))
udev->quirks |= __usb_detect_quirks(udev,
usb_amd_resume_quirk_list);
if (udev->quirks)
dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
udev->quirks);

View File

@ -23,14 +23,16 @@ static ssize_t field##_show(struct device *dev, \
{ \
struct usb_device *udev; \
struct usb_host_config *actconfig; \
ssize_t rc = 0; \
\
udev = to_usb_device(dev); \
usb_lock_device(udev); \
actconfig = udev->actconfig; \
if (actconfig) \
return sprintf(buf, format_string, \
rc = sprintf(buf, format_string, \
actconfig->desc.field); \
else \
return 0; \
usb_unlock_device(udev); \
return rc; \
} \
#define usb_actconfig_attr(field, format_string) \
@ -45,12 +47,15 @@ static ssize_t bMaxPower_show(struct device *dev,
{
struct usb_device *udev;
struct usb_host_config *actconfig;
ssize_t rc = 0;
udev = to_usb_device(dev);
usb_lock_device(udev);
actconfig = udev->actconfig;
if (!actconfig)
return 0;
return sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
if (actconfig)
rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
usb_unlock_device(udev);
return rc;
}
static DEVICE_ATTR_RO(bMaxPower);
@ -59,12 +64,15 @@ static ssize_t configuration_show(struct device *dev,
{
struct usb_device *udev;
struct usb_host_config *actconfig;
ssize_t rc = 0;
udev = to_usb_device(dev);
usb_lock_device(udev);
actconfig = udev->actconfig;
if ((!actconfig) || (!actconfig->string))
return 0;
return sprintf(buf, "%s\n", actconfig->string);
if (actconfig && actconfig->string)
rc = sprintf(buf, "%s\n", actconfig->string);
usb_unlock_device(udev);
return rc;
}
static DEVICE_ATTR_RO(configuration);
@ -390,7 +398,8 @@ static DEVICE_ATTR_RW(autosuspend);
static const char on_string[] = "on";
static const char auto_string[] = "auto";
static void warn_level(void) {
static void warn_level(void)
{
static int level_warned;
if (!level_warned) {
@ -449,7 +458,7 @@ static ssize_t usb2_hardware_lpm_show(struct device *dev,
struct usb_device *udev = to_usb_device(dev);
const char *p;
if (udev->usb2_hw_lpm_enabled == 1)
if (udev->usb2_hw_lpm_allowed == 1)
p = "enabled";
else
p = "disabled";
@ -469,8 +478,10 @@ static ssize_t usb2_hardware_lpm_store(struct device *dev,
ret = strtobool(buf, &value);
if (!ret)
if (!ret) {
udev->usb2_hw_lpm_allowed = value;
ret = usb_set_usb2_hardware_lpm(udev, value);
}
usb_unlock_device(udev);
@ -644,7 +655,7 @@ static ssize_t authorized_store(struct device *dev,
result = usb_deauthorize_device(usb_dev);
else
result = usb_authorize_device(usb_dev);
return result < 0? result : size;
return result < 0 ? result : size;
}
static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR,
authorized_show, authorized_store);
@ -764,6 +775,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
* Following that are the raw descriptor entries for all the
* configurations (config plus subsidiary descriptors).
*/
usb_lock_device(udev);
for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations &&
nleft > 0; ++cfgno) {
if (cfgno < 0) {
@ -784,6 +796,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
off -= srclen;
}
}
usb_unlock_device(udev);
return count - nleft;
}
@ -870,9 +883,7 @@ static ssize_t interface_show(struct device *dev, struct device_attribute *attr,
char *string;
intf = to_usb_interface(dev);
string = intf->cur_altsetting->string;
barrier(); /* The altsetting might change! */
string = ACCESS_ONCE(intf->cur_altsetting->string);
if (!string)
return 0;
return sprintf(buf, "%s\n", string);
@ -888,7 +899,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
intf = to_usb_interface(dev);
udev = interface_to_usbdev(intf);
alt = intf->cur_altsetting;
alt = ACCESS_ONCE(intf->cur_altsetting);
return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
"ic%02Xisc%02Xip%02Xin%02X\n",
@ -909,23 +920,14 @@ static ssize_t supports_autosuspend_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct usb_interface *intf;
struct usb_device *udev;
int ret;
int s;
intf = to_usb_interface(dev);
udev = interface_to_usbdev(intf);
usb_lock_device(udev);
device_lock(dev);
/* Devices will be autosuspended even when an interface isn't claimed */
if (!intf->dev.driver ||
to_usb_driver(intf->dev.driver)->supports_autosuspend)
ret = sprintf(buf, "%u\n", 1);
else
ret = sprintf(buf, "%u\n", 0);
usb_unlock_device(udev);
s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend);
device_unlock(dev);
return ret;
return sprintf(buf, "%u\n", s);
}
static DEVICE_ATTR_RO(supports_autosuspend);

View File

@ -138,13 +138,19 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
}
EXPORT_SYMBOL_GPL(usb_anchor_urb);
static int usb_anchor_check_wakeup(struct usb_anchor *anchor)
{
return atomic_read(&anchor->suspend_wakeups) == 0 &&
list_empty(&anchor->urb_list);
}
/* Callers must hold anchor->lock */
static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
{
urb->anchor = NULL;
list_del(&urb->anchor_list);
usb_put_urb(urb);
if (list_empty(&anchor->urb_list))
if (usb_anchor_check_wakeup(anchor))
wake_up(&anchor->wait);
}
@ -845,6 +851,39 @@ void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
}
EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
/**
* usb_anchor_suspend_wakeups
* @anchor: the anchor you want to suspend wakeups on
*
* Call this to stop the last urb being unanchored from waking up any
* usb_wait_anchor_empty_timeout waiters. This is used in the hcd urb give-
* back path to delay waking up until after the completion handler has run.
*/
void usb_anchor_suspend_wakeups(struct usb_anchor *anchor)
{
if (anchor)
atomic_inc(&anchor->suspend_wakeups);
}
EXPORT_SYMBOL_GPL(usb_anchor_suspend_wakeups);
/**
* usb_anchor_resume_wakeups
* @anchor: the anchor you want to resume wakeups on
*
* Allow usb_wait_anchor_empty_timeout waiters to be woken up again, and
* wake up any current waiters if the anchor is empty.
*/
void usb_anchor_resume_wakeups(struct usb_anchor *anchor)
{
if (!anchor)
return;
atomic_dec(&anchor->suspend_wakeups);
if (usb_anchor_check_wakeup(anchor))
wake_up(&anchor->wait);
}
EXPORT_SYMBOL_GPL(usb_anchor_resume_wakeups);
/**
* usb_wait_anchor_empty_timeout - wait for an anchor to be unused
* @anchor: the anchor you want to become unused
@ -858,7 +897,8 @@ EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
unsigned int timeout)
{
return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list),
return wait_event_timeout(anchor->wait,
usb_anchor_check_wakeup(anchor),
msecs_to_jiffies(timeout));
}
EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);

View File

@ -497,7 +497,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->authorized = 1;
else {
dev->authorized = usb_hcd->authorized_default;
dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;
dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0;
}
return dev;
}

View File

@ -35,6 +35,7 @@ extern int usb_get_device_descriptor(struct usb_device *dev,
unsigned int size);
extern int usb_get_bos_descriptor(struct usb_device *dev);
extern void usb_release_bos_descriptor(struct usb_device *dev);
extern int usb_device_supports_lpm(struct usb_device *udev);
extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern int usb_choose_configuration(struct usb_device *udev);

View File

@ -584,7 +584,7 @@ static int dwc3_remove(struct platform_device *pdev)
usb_phy_set_suspend(dwc->usb2_phy, 1);
usb_phy_set_suspend(dwc->usb3_phy, 1);
pm_runtime_put(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
dwc3_debugfs_exit(dwc);
@ -691,7 +691,6 @@ static int dwc3_resume(struct device *dev)
usb_phy_init(dwc->usb3_phy);
usb_phy_init(dwc->usb2_phy);
msleep(100);
spin_lock_irqsave(&dwc->lock, flags);

View File

@ -165,7 +165,6 @@ static int dwc3_pci_probe(struct pci_dev *pci,
return 0;
err3:
pci_set_drvdata(pci, NULL);
platform_device_put(dwc3);
err1:
pci_disable_device(pci);
@ -180,7 +179,6 @@ static void dwc3_pci_remove(struct pci_dev *pci)
platform_device_unregister(glue->dwc3);
platform_device_unregister(glue->usb2_phy);
platform_device_unregister(glue->usb3_phy);
pci_set_drvdata(pci, NULL);
pci_disable_device(pci);
}

View File

@ -352,7 +352,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
break;
default:
return -EINVAL;
};
}
response_pkt = (__le16 *) dwc->setup_buf;
*response_pkt = cpu_to_le16(usb_status);
@ -470,7 +470,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
default:
return -EINVAL;
};
}
return 0;
}
@ -709,7 +709,7 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
break;
};
}
return ret;
}

View File

@ -568,10 +568,6 @@ try_again:
dbgp_printk("Could not find attached debug device\n");
goto err;
}
if (ret < 0) {
dbgp_printk("Attached device is not a debug device\n");
goto err;
}
dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint;
dbgp_endpoint_in = dbgp_desc.bDebugInEndpoint;

View File

@ -58,6 +58,20 @@ config USB_GADGET_DEBUG
trying to track down. Never enable these messages for a
production build.
config USB_GADGET_VERBOSE
bool "Verbose debugging Messages (DEVELOPMENT)"
depends on USB_GADGET_DEBUG
help
Many controller and gadget drivers will print verbose debugging
messages if you use this option to ask for those messages.
Avoid enabling these messages, even if you're actively
debugging such a driver. Many drivers will emit so many
messages that the driver timings are affected, which will
either create new failure modes or remove the one you're
trying to track down. Never enable these messages for a
production build.
config USB_GADGET_DEBUG_FILES
boolean "Debugging information files (DEVELOPMENT)"
depends on PROC_FS
@ -525,6 +539,9 @@ config USB_F_SUBSET
config USB_F_RNDIS
tristate
config USB_F_MASS_STORAGE
tristate
choice
tristate "USB Gadget Drivers"
default USB_ETH
@ -662,6 +679,16 @@ config USB_CONFIGFS_PHONET
help
The Phonet protocol implementation for USB device.
config USB_CONFIGFS_MASS_STORAGE
boolean "Mass storage"
depends on USB_CONFIGFS
select USB_F_MASS_STORAGE
help
The Mass Storage Gadget acts as a USB Mass Storage disk drive.
As its storage repository it can use a regular file or a block
device (in much the same way as the "loop" device driver),
specified as a module parameter or sysfs option.
config USB_ZERO
tristate "Gadget Zero (DEVELOPMENT)"
select USB_LIBCOMPOSITE
@ -878,6 +905,7 @@ config USB_MASS_STORAGE
tristate "Mass Storage Gadget"
depends on BLOCK
select USB_LIBCOMPOSITE
select USB_F_MASS_STORAGE
help
The Mass Storage Gadget acts as a USB Mass Storage disk drive.
As its storage repository it can use a regular file or a block
@ -1001,6 +1029,7 @@ config USB_G_ACM_MS
select USB_LIBCOMPOSITE
select USB_U_SERIAL
select USB_F_ACM
select USB_F_MASS_STORAGE
help
This driver provides two functions in one configuration:
a mass storage, and a CDC ACM (serial port) link.
@ -1015,8 +1044,8 @@ config USB_G_MULTI
select USB_LIBCOMPOSITE
select USB_U_SERIAL
select USB_U_ETHER
select USB_U_RNDIS
select USB_F_ACM
select USB_F_MASS_STORAGE
help
The Multifunction Composite Gadget provides Ethernet (RNDIS
and/or CDC Ethernet), mass storage and ACM serial link
@ -1035,6 +1064,8 @@ config USB_G_MULTI
config USB_G_MULTI_RNDIS
bool "RNDIS + CDC Serial + Storage configuration"
depends on USB_G_MULTI
select USB_U_RNDIS
select USB_F_RNDIS
default y
help
This option enables a configuration with RNDIS, CDC Serial and
@ -1048,6 +1079,7 @@ config USB_G_MULTI_CDC
bool "CDC Ethernet + CDC Serial + Storage configuration"
depends on USB_G_MULTI
default n
select USB_F_ECM
help
This option enables a configuration with CDC Ethernet (ECM), CDC
Serial and Mass Storage functions available in the Multifunction

View File

@ -1,7 +1,8 @@
#
# USB peripheral controller drivers
#
ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
obj-$(CONFIG_USB_GADGET) += udc-core.o
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
@ -60,6 +61,8 @@ usb_f_ecm_subset-y := f_subset.o
obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o
usb_f_rndis-y := f_rndis.o
obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o
usb_f_mass_storage-y := f_mass_storage.o storage_common.o
obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
#
# USB gadget drivers

View File

@ -31,16 +31,7 @@
#define ACM_MS_VENDOR_NUM 0x1d6b /* Linux Foundation */
#define ACM_MS_PRODUCT_NUM 0x0106 /* Composite Gadget: ACM + MS*/
/*-------------------------------------------------------------------------*/
/*
* Kbuild is not very cooperative with respect to linking separately
* compiled library objects into one module. So for now we won't use
* separate compilation ... ensuring init/exit sections work to shrink
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
#include "f_mass_storage.c"
#include "f_mass_storage.h"
/*-------------------------------------------------------------------------*/
USB_GADGET_COMPOSITE_OPTIONS();
@ -104,18 +95,35 @@ static struct usb_gadget_strings *dev_strings[] = {
/****************************** Configurations ******************************/
static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static struct fsg_common fsg_common;
static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
#else
/*
* Number of buffers we will use.
* 2 is usually enough for good buffering pipeline
*/
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
#endif /* CONFIG_USB_DEBUG */
FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
/*-------------------------------------------------------------------------*/
static struct usb_function *f_acm;
static struct usb_function_instance *f_acm_inst;
static struct usb_function_instance *fi_msg;
static struct usb_function *f_msg;
/*
* We _always_ have both ACM and mass storage functions.
*/
static int __init acm_ms_do_config(struct usb_configuration *c)
{
struct fsg_opts *opts;
int status;
if (gadget_is_otg(c->cdev->gadget)) {
@ -123,31 +131,37 @@ static int __init acm_ms_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
f_acm_inst = usb_get_function_instance("acm");
if (IS_ERR(f_acm_inst))
return PTR_ERR(f_acm_inst);
opts = fsg_opts_from_func_inst(fi_msg);
f_acm = usb_get_function(f_acm_inst);
if (IS_ERR(f_acm)) {
status = PTR_ERR(f_acm);
goto err_func;
if (IS_ERR(f_acm))
return PTR_ERR(f_acm);
f_msg = usb_get_function(fi_msg);
if (IS_ERR(f_msg)) {
status = PTR_ERR(f_msg);
goto put_acm;
}
status = usb_add_function(c, f_acm);
if (status < 0)
goto err_conf;
goto put_msg;
status = fsg_bind_config(c->cdev, c, &fsg_common);
if (status < 0)
goto err_fsg;
status = fsg_common_run_thread(opts->common);
if (status)
goto remove_acm;
status = usb_add_function(c, f_msg);
if (status)
goto remove_acm;
return 0;
err_fsg:
remove_acm:
usb_remove_function(c, f_acm);
err_conf:
put_msg:
usb_put_function(f_msg);
put_acm:
usb_put_function(f_acm);
err_func:
usb_put_function_instance(f_acm_inst);
return status;
}
@ -163,45 +177,82 @@ static struct usb_configuration acm_ms_config_driver = {
static int __init acm_ms_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct fsg_opts *opts;
struct fsg_config config;
int status;
void *retp;
/* set up mass storage function */
retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
if (IS_ERR(retp)) {
status = PTR_ERR(retp);
return PTR_ERR(retp);
f_acm_inst = usb_get_function_instance("acm");
if (IS_ERR(f_acm_inst))
return PTR_ERR(f_acm_inst);
fi_msg = usb_get_function_instance("mass_storage");
if (IS_ERR(fi_msg)) {
status = PTR_ERR(fi_msg);
goto fail_get_msg;
}
/* set up mass storage function */
fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
opts = fsg_opts_from_func_inst(fi_msg);
opts->no_configfs = true;
status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
if (status)
goto fail;
status = fsg_common_set_nluns(opts->common, config.nluns);
if (status)
goto fail_set_nluns;
status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
if (status)
goto fail_set_cdev;
fsg_common_set_sysfs(opts->common, true);
status = fsg_common_create_luns(opts->common, &config);
if (status)
goto fail_set_cdev;
fsg_common_set_inquiry_string(opts->common, config.vendor_name,
config.product_name);
/*
* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail1;
goto fail_string_ids;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
if (status < 0)
goto fail1;
goto fail_string_ids;
usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
DRIVER_DESC);
fsg_common_put(&fsg_common);
return 0;
/* error recovery */
fail1:
fsg_common_put(&fsg_common);
fail_string_ids:
fsg_common_remove_luns(opts->common);
fail_set_cdev:
fsg_common_free_luns(opts->common);
fail_set_nluns:
fsg_common_free_buffers(opts->common);
fail:
usb_put_function_instance(fi_msg);
fail_get_msg:
usb_put_function_instance(f_acm_inst);
return status;
}
static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
{
usb_put_function(f_msg);
usb_put_function_instance(fi_msg);
usb_put_function(f_acm);
usb_put_function_instance(f_acm_inst);
return 0;

View File

@ -3078,8 +3078,6 @@ static void udc_pci_remove(struct pci_dev *pdev)
if (dev->active)
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
udc_remove(dev);
}

View File

@ -354,7 +354,7 @@ static u8 encode_bMaxPower(enum usb_device_speed speed,
return DIV_ROUND_UP(val, 8);
default:
return DIV_ROUND_UP(val, 2);
};
}
}
static int config_buf(struct usb_configuration *config,

View File

@ -557,7 +557,7 @@ static struct config_group *function_make(
fi = usb_get_function_instance(func_name);
if (IS_ERR(fi))
return ERR_PTR(PTR_ERR(fi));
return ERR_CAST(fi);
ret = config_item_set_name(&fi->group.cg_item, name);
if (ret) {
@ -991,6 +991,14 @@ static struct configfs_subsystem gadget_subsys = {
.su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex),
};
void unregister_gadget_item(struct config_item *item)
{
struct gadget_info *gi = to_gadget_info(item);
unregister_gadget(gi);
}
EXPORT_SYMBOL(unregister_gadget_item);
static int __init gadget_cfs_init(void)
{
int ret;

View File

@ -0,0 +1,6 @@
#ifndef USB__GADGET__CONFIGFS__H
#define USB__GADGET__CONFIGFS__H
void unregister_gadget_item(struct config_item *item);
#endif /* USB__GADGET__CONFIGFS__H */

View File

@ -544,7 +544,7 @@ static int dummy_enable(struct usb_ep *_ep,
default:
val = "ctrl";
break;
}; val; }),
} val; }),
max, ep->stream_en ? "enabled" : "disabled");
/* at this point real hardware should be NAKing transfers
@ -2271,7 +2271,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
default:
s = "?";
break;
}; s; }),
} s; }),
ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "",
({ char *s; \
switch (usb_pipetype(urb->pipe)) { \
@ -2287,7 +2287,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
default: \
s = "-iso"; \
break; \
}; s; }),
} s; }),
urb->actual_length, urb->transfer_buffer_length);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,166 @@
#ifndef USB_F_MASS_STORAGE_H
#define USB_F_MASS_STORAGE_H
#include <linux/usb/composite.h>
#include "storage_common.h"
struct fsg_module_parameters {
char *file[FSG_MAX_LUNS];
bool ro[FSG_MAX_LUNS];
bool removable[FSG_MAX_LUNS];
bool cdrom[FSG_MAX_LUNS];
bool nofua[FSG_MAX_LUNS];
unsigned int file_count, ro_count, removable_count, cdrom_count;
unsigned int nofua_count;
unsigned int luns; /* nluns */
bool stall; /* can_stall */
};
#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \
module_param_array_named(prefix ## name, params.name, type, \
&prefix ## params.name ## _count, \
S_IRUGO); \
MODULE_PARM_DESC(prefix ## name, desc)
#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \
module_param_named(prefix ## name, params.name, type, \
S_IRUGO); \
MODULE_PARM_DESC(prefix ## name, desc)
#define __FSG_MODULE_PARAMETERS(prefix, params) \
_FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \
"names of backing files or devices"); \
_FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \
"true to force read-only"); \
_FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \
"true to simulate removable media"); \
_FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \
"true to simulate CD-ROM instead of disk"); \
_FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool, \
"true to ignore SCSI WRITE(10,12) FUA bit"); \
_FSG_MODULE_PARAM(prefix, params, luns, uint, \
"number of LUNs"); \
_FSG_MODULE_PARAM(prefix, params, stall, bool, \
"false to prevent bulk stalls")
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
#define FSG_MODULE_PARAMETERS(prefix, params) \
__FSG_MODULE_PARAMETERS(prefix, params); \
module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);\
MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers")
#else
#define FSG_MODULE_PARAMETERS(prefix, params) \
__FSG_MODULE_PARAMETERS(prefix, params)
#endif
struct fsg_common;
/* FSF callback functions */
struct fsg_operations {
/*
* Callback function to call when thread exits. If no
* callback is set or it returns value lower then zero MSF
* will force eject all LUNs it operates on (including those
* marked as non-removable or with prevent_medium_removal flag
* set).
*/
int (*thread_exits)(struct fsg_common *common);
};
struct fsg_lun_opts {
struct config_group group;
struct fsg_lun *lun;
int lun_id;
};
struct fsg_opts {
struct fsg_common *common;
struct usb_function_instance func_inst;
struct fsg_lun_opts lun0;
struct config_group *default_groups[2];
bool no_configfs; /* for legacy gadgets */
/*
* Read/write access to configfs attributes is handled by configfs.
*
* This is to protect the data from concurrent access by read/write
* and create symlink/remove symlink.
*/
struct mutex lock;
int refcnt;
};
struct fsg_lun_config {
const char *filename;
char ro;
char removable;
char cdrom;
char nofua;
};
struct fsg_config {
unsigned nluns;
struct fsg_lun_config luns[FSG_MAX_LUNS];
/* Callback functions. */
const struct fsg_operations *ops;
/* Gadget's private data. */
void *private_data;
const char *vendor_name; /* 8 characters or less */
const char *product_name; /* 16 characters or less */
char can_stall;
unsigned int fsg_num_buffers;
};
static inline struct fsg_opts *
fsg_opts_from_func_inst(const struct usb_function_instance *fi)
{
return container_of(fi, struct fsg_opts, func_inst);
}
void fsg_common_get(struct fsg_common *common);
void fsg_common_put(struct fsg_common *common);
void fsg_common_set_sysfs(struct fsg_common *common, bool sysfs);
int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n);
void fsg_common_free_buffers(struct fsg_common *common);
int fsg_common_set_cdev(struct fsg_common *common,
struct usb_composite_dev *cdev, bool can_stall);
void fsg_common_remove_lun(struct fsg_lun *lun, bool sysfs);
void fsg_common_remove_luns(struct fsg_common *common);
void fsg_common_free_luns(struct fsg_common *common);
int fsg_common_set_nluns(struct fsg_common *common, int nluns);
void fsg_common_set_ops(struct fsg_common *common,
const struct fsg_operations *ops);
int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
unsigned int id, const char *name,
const char **name_pfx);
int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg);
void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn,
const char *pn);
int fsg_common_run_thread(struct fsg_common *common);
void fsg_config_from_params(struct fsg_config *cfg,
const struct fsg_module_parameters *params,
unsigned int fsg_num_buffers);
#endif /* USB_F_MASS_STORAGE_H */

View File

@ -76,7 +76,9 @@ struct gfs_ffs_obj {
USB_GADGET_COMPOSITE_OPTIONS();
#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
USB_ETHERNET_MODULE_PARAMETERS();
#endif
static struct usb_device_descriptor gfs_dev_desc = {
.bLength = sizeof gfs_dev_desc,

View File

@ -1165,7 +1165,7 @@ static int udc_proc_read(struct seq_file *m, void *v)
s = "invalid"; break;
default:
s = "?"; break;
}; s; }),
} s; }),
(tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
(tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
(tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
@ -1701,7 +1701,6 @@ static void goku_remove(struct pci_dev *pdev)
if (dev->enabled)
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
dev->regs = NULL;
INFO(dev, "unbind\n");

View File

@ -37,16 +37,16 @@
#define DRIVER_DESC "Mass Storage Gadget"
#define DRIVER_VERSION "2009/09/11"
/*-------------------------------------------------------------------------*/
/*
* kbuild is not very cooperative with respect to linking separately
* compiled library objects into one module. So for now we won't use
* separate compilation ... ensuring init/exit sections work to shrink
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
* Thanks to NetChip Technologies for donating this product ID.
*
* DO NOT REUSE THESE IDs with any other driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
#include "f_mass_storage.c"
#define FSG_VENDOR_ID 0x0525 /* NetChip */
#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
#include "f_mass_storage.h"
/*-------------------------------------------------------------------------*/
USB_GADGET_COMPOSITE_OPTIONS();
@ -97,11 +97,28 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL,
};
static struct usb_function_instance *fi_msg;
static struct usb_function *f_msg;
/****************************** Configurations ******************************/
static struct fsg_module_parameters mod_data = {
.stall = 1
};
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
#else
/*
* Number of buffers we will use.
* 2 is usually enough for good buffering pipeline
*/
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
static unsigned long msg_registered;
@ -115,13 +132,7 @@ static int msg_thread_exits(struct fsg_common *common)
static int __init msg_do_config(struct usb_configuration *c)
{
static const struct fsg_operations ops = {
.thread_exits = msg_thread_exits,
};
static struct fsg_common common;
struct fsg_common *retp;
struct fsg_config config;
struct fsg_opts *opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
@ -129,15 +140,24 @@ static int __init msg_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
fsg_config_from_params(&config, &mod_data);
config.ops = &ops;
opts = fsg_opts_from_func_inst(fi_msg);
retp = fsg_common_init(&common, c->cdev, &config);
if (IS_ERR(retp))
return PTR_ERR(retp);
f_msg = usb_get_function(fi_msg);
if (IS_ERR(f_msg))
return PTR_ERR(f_msg);
ret = fsg_bind_config(c->cdev, c, &common);
fsg_common_put(&common);
ret = fsg_common_run_thread(opts->common);
if (ret)
goto put_func;
ret = usb_add_function(c, f_msg);
if (ret)
goto put_func;
return 0;
put_func:
usb_put_function(f_msg);
return ret;
}
@ -152,23 +172,79 @@ static struct usb_configuration msg_config_driver = {
static int __init msg_bind(struct usb_composite_dev *cdev)
{
static const struct fsg_operations ops = {
.thread_exits = msg_thread_exits,
};
struct fsg_opts *opts;
struct fsg_config config;
int status;
fi_msg = usb_get_function_instance("mass_storage");
if (IS_ERR(fi_msg))
return PTR_ERR(fi_msg);
fsg_config_from_params(&config, &mod_data, fsg_num_buffers);
opts = fsg_opts_from_func_inst(fi_msg);
opts->no_configfs = true;
status = fsg_common_set_num_buffers(opts->common, fsg_num_buffers);
if (status)
goto fail;
status = fsg_common_set_nluns(opts->common, config.nluns);
if (status)
goto fail_set_nluns;
fsg_common_set_ops(opts->common, &ops);
status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
if (status)
goto fail_set_cdev;
fsg_common_set_sysfs(opts->common, true);
status = fsg_common_create_luns(opts->common, &config);
if (status)
goto fail_set_cdev;
fsg_common_set_inquiry_string(opts->common, config.vendor_name,
config.product_name);
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
return status;
goto fail_string_ids;
msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
if (status < 0)
return status;
goto fail_string_ids;
usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&cdev->gadget->dev,
DRIVER_DESC ", version: " DRIVER_VERSION "\n");
set_bit(0, &msg_registered);
return 0;
fail_string_ids:
fsg_common_remove_luns(opts->common);
fail_set_cdev:
fsg_common_free_luns(opts->common);
fail_set_nluns:
fsg_common_free_buffers(opts->common);
fail:
usb_put_function_instance(fi_msg);
return status;
}
static int msg_unbind(struct usb_composite_dev *cdev)
{
if (!IS_ERR(f_msg))
usb_put_function(f_msg);
if (!IS_ERR(fi_msg))
usb_put_function_instance(fi_msg);
return 0;
}
/****************************** Some noise ******************************/
@ -179,6 +255,7 @@ static __refdata struct usb_composite_driver msg_driver = {
.needs_serial = 1,
.strings = dev_strings,
.bind = msg_bind,
.unbind = msg_unbind,
};
MODULE_DESCRIPTION(DRIVER_DESC);

View File

@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include "u_serial.h"
#if defined USB_ETH_RNDIS
@ -32,22 +33,11 @@ MODULE_AUTHOR("Michal Nazarewicz");
MODULE_LICENSE("GPL");
/***************************** All the files... *****************************/
#include "f_mass_storage.h"
/*
* kbuild is not very cooperative with respect to linking separately
* compiled library objects into one module. So for now we won't use
* separate compilation ... ensuring init/exit sections work to shrink
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
#include "f_mass_storage.c"
#define USBF_ECM_INCLUDED
#include "f_ecm.c"
#include "u_ecm.h"
#ifdef USB_ETH_RNDIS
# define USB_FRNDIS_INCLUDED
# include "f_rndis.c"
# include "u_rndis.h"
# include "rndis.h"
#endif
#include "u_ether.h"
@ -132,22 +122,36 @@ static struct usb_gadget_strings *dev_strings[] = {
/****************************** Configurations ******************************/
static struct fsg_module_parameters fsg_mod_data = { .stall = 1 };
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
#else
/*
* Number of buffers we will use.
* 2 is usually enough for good buffering pipeline
*/
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
#endif /* CONFIG_USB_DEBUG */
FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
static struct fsg_common fsg_common;
static u8 host_mac[ETH_ALEN];
static struct usb_function_instance *fi_acm;
static struct eth_dev *the_dev;
static struct usb_function_instance *fi_msg;
/********** RNDIS **********/
#ifdef USB_ETH_RNDIS
static struct usb_function_instance *fi_rndis;
static struct usb_function *f_acm_rndis;
static struct usb_function *f_rndis;
static struct usb_function *f_msg_rndis;
static __init int rndis_do_config(struct usb_configuration *c)
{
struct fsg_opts *fsg_opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
@ -155,27 +159,50 @@ static __init int rndis_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
ret = rndis_bind_config(c, host_mac, the_dev);
f_rndis = usb_get_function(fi_rndis);
if (IS_ERR(f_rndis))
return PTR_ERR(f_rndis);
ret = usb_add_function(c, f_rndis);
if (ret < 0)
return ret;
goto err_func_rndis;
f_acm_rndis = usb_get_function(fi_acm);
if (IS_ERR(f_acm_rndis))
return PTR_ERR(f_acm_rndis);
if (IS_ERR(f_acm_rndis)) {
ret = PTR_ERR(f_acm_rndis);
goto err_func_acm;
}
ret = usb_add_function(c, f_acm_rndis);
if (ret)
goto err_conf;
ret = fsg_bind_config(c->cdev, c, &fsg_common);
if (ret < 0)
f_msg_rndis = usb_get_function(fi_msg);
if (IS_ERR(f_msg_rndis)) {
ret = PTR_ERR(f_msg_rndis);
goto err_fsg;
}
fsg_opts = fsg_opts_from_func_inst(fi_msg);
ret = fsg_common_run_thread(fsg_opts->common);
if (ret)
goto err_run;
ret = usb_add_function(c, f_msg_rndis);
if (ret)
goto err_run;
return 0;
err_run:
usb_put_function(f_msg_rndis);
err_fsg:
usb_remove_function(c, f_acm_rndis);
err_conf:
usb_put_function(f_acm_rndis);
err_func_acm:
usb_remove_function(c, f_rndis);
err_func_rndis:
usb_put_function(f_rndis);
return ret;
}
@ -205,10 +232,14 @@ static __ref int rndis_config_register(struct usb_composite_dev *cdev)
/********** CDC ECM **********/
#ifdef CONFIG_USB_G_MULTI_CDC
static struct usb_function_instance *fi_ecm;
static struct usb_function *f_acm_multi;
static struct usb_function *f_ecm;
static struct usb_function *f_msg_multi;
static __init int cdc_do_config(struct usb_configuration *c)
{
struct fsg_opts *fsg_opts;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
@ -216,28 +247,51 @@ static __init int cdc_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
ret = ecm_bind_config(c, host_mac, the_dev);
f_ecm = usb_get_function(fi_ecm);
if (IS_ERR(f_ecm))
return PTR_ERR(f_ecm);
ret = usb_add_function(c, f_ecm);
if (ret < 0)
return ret;
goto err_func_ecm;
/* implicit port_num is zero */
f_acm_multi = usb_get_function(fi_acm);
if (IS_ERR(f_acm_multi))
return PTR_ERR(f_acm_multi);
if (IS_ERR(f_acm_multi)) {
ret = PTR_ERR(f_acm_multi);
goto err_func_acm;
}
ret = usb_add_function(c, f_acm_multi);
if (ret)
goto err_conf;
ret = fsg_bind_config(c->cdev, c, &fsg_common);
if (ret < 0)
f_msg_multi = usb_get_function(fi_msg);
if (IS_ERR(f_msg_multi)) {
ret = PTR_ERR(f_msg_multi);
goto err_fsg;
}
fsg_opts = fsg_opts_from_func_inst(fi_msg);
ret = fsg_common_run_thread(fsg_opts->common);
if (ret)
goto err_run;
ret = usb_add_function(c, f_msg_multi);
if (ret)
goto err_run;
return 0;
err_run:
usb_put_function(f_msg_multi);
err_fsg:
usb_remove_function(c, f_acm_multi);
err_conf:
usb_put_function(f_acm_multi);
err_func_acm:
usb_remove_function(c, f_ecm);
err_func_ecm:
usb_put_function(f_ecm);
return ret;
}
@ -270,19 +324,67 @@ static __ref int cdc_config_register(struct usb_composite_dev *cdev)
static int __ref multi_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
#ifdef CONFIG_USB_G_MULTI_CDC
struct f_ecm_opts *ecm_opts;
#endif
#ifdef USB_ETH_RNDIS
struct f_rndis_opts *rndis_opts;
#endif
struct fsg_opts *fsg_opts;
struct fsg_config config;
int status;
if (!can_support_ecm(cdev->gadget)) {
dev_err(&gadget->dev, "controller '%s' not usable\n",
gadget->name);
gadget->name);
return -EINVAL;
}
/* set up network link layer */
the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac,
qmult);
if (IS_ERR(the_dev))
return PTR_ERR(the_dev);
#ifdef CONFIG_USB_G_MULTI_CDC
fi_ecm = usb_get_function_instance("ecm");
if (IS_ERR(fi_ecm))
return PTR_ERR(fi_ecm);
ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
gether_set_qmult(ecm_opts->net, qmult);
if (!gether_set_host_addr(ecm_opts->net, host_addr))
pr_info("using host ethernet address: %s", host_addr);
if (!gether_set_dev_addr(ecm_opts->net, dev_addr))
pr_info("using self ethernet address: %s", dev_addr);
#endif
#ifdef USB_ETH_RNDIS
fi_rndis = usb_get_function_instance("rndis");
if (IS_ERR(fi_rndis)) {
status = PTR_ERR(fi_rndis);
goto fail;
}
rndis_opts = container_of(fi_rndis, struct f_rndis_opts, func_inst);
gether_set_qmult(rndis_opts->net, qmult);
if (!gether_set_host_addr(rndis_opts->net, host_addr))
pr_info("using host ethernet address: %s", host_addr);
if (!gether_set_dev_addr(rndis_opts->net, dev_addr))
pr_info("using self ethernet address: %s", dev_addr);
#endif
#if (defined CONFIG_USB_G_MULTI_CDC && defined USB_ETH_RNDIS)
/*
* If both ecm and rndis are selected then:
* 1) rndis borrows the net interface from ecm
* 2) since the interface is shared it must not be bound
* twice - in ecm's _and_ rndis' binds, so do it here.
*/
gether_set_gadget(ecm_opts->net, cdev->gadget);
status = gether_register_netdev(ecm_opts->net);
if (status)
goto fail0;
rndis_borrow_net(fi_rndis, ecm_opts->net);
ecm_opts->bound = true;
#endif
/* set up serial link layer */
fi_acm = usb_get_function_instance("acm");
@ -292,49 +394,87 @@ static int __ref multi_bind(struct usb_composite_dev *cdev)
}
/* set up mass storage function */
{
void *retp;
retp = fsg_common_from_params(&fsg_common, cdev, &fsg_mod_data);
if (IS_ERR(retp)) {
status = PTR_ERR(retp);
goto fail1;
}
fi_msg = usb_get_function_instance("mass_storage");
if (IS_ERR(fi_msg)) {
status = PTR_ERR(fi_msg);
goto fail1;
}
fsg_config_from_params(&config, &fsg_mod_data, fsg_num_buffers);
fsg_opts = fsg_opts_from_func_inst(fi_msg);
fsg_opts->no_configfs = true;
status = fsg_common_set_num_buffers(fsg_opts->common, fsg_num_buffers);
if (status)
goto fail2;
status = fsg_common_set_nluns(fsg_opts->common, config.nluns);
if (status)
goto fail_set_nluns;
status = fsg_common_set_cdev(fsg_opts->common, cdev, config.can_stall);
if (status)
goto fail_set_cdev;
fsg_common_set_sysfs(fsg_opts->common, true);
status = fsg_common_create_luns(fsg_opts->common, &config);
if (status)
goto fail_set_cdev;
fsg_common_set_inquiry_string(fsg_opts->common, config.vendor_name,
config.product_name);
/* allocate string IDs */
status = usb_string_ids_tab(cdev, strings_dev);
if (unlikely(status < 0))
goto fail2;
goto fail_string_ids;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register configurations */
status = rndis_config_register(cdev);
if (unlikely(status < 0))
goto fail2;
goto fail_string_ids;
status = cdc_config_register(cdev);
if (unlikely(status < 0))
goto fail2;
goto fail_string_ids;
usb_composite_overwrite_options(cdev, &coverwrite);
/* we're done */
dev_info(&gadget->dev, DRIVER_DESC "\n");
fsg_common_put(&fsg_common);
return 0;
/* error recovery */
fail_string_ids:
fsg_common_remove_luns(fsg_opts->common);
fail_set_cdev:
fsg_common_free_luns(fsg_opts->common);
fail_set_nluns:
fsg_common_free_buffers(fsg_opts->common);
fail2:
fsg_common_put(&fsg_common);
usb_put_function_instance(fi_msg);
fail1:
usb_put_function_instance(fi_acm);
fail0:
gether_cleanup(the_dev);
#ifdef USB_ETH_RNDIS
usb_put_function_instance(fi_rndis);
fail:
#endif
#ifdef CONFIG_USB_G_MULTI_CDC
usb_put_function_instance(fi_ecm);
#endif
return status;
}
static int __exit multi_unbind(struct usb_composite_dev *cdev)
{
#ifdef CONFIG_USB_G_MULTI_CDC
usb_put_function(f_msg_multi);
#endif
#ifdef USB_ETH_RNDIS
usb_put_function(f_msg_rndis);
#endif
usb_put_function_instance(fi_msg);
#ifdef CONFIG_USB_G_MULTI_CDC
usb_put_function(f_acm_multi);
#endif
@ -342,7 +482,14 @@ static int __exit multi_unbind(struct usb_composite_dev *cdev)
usb_put_function(f_acm_rndis);
#endif
usb_put_function_instance(fi_acm);
gether_cleanup(the_dev);
#ifdef USB_ETH_RNDIS
usb_put_function(f_rndis);
usb_put_function_instance(fi_rndis);
#endif
#ifdef CONFIG_USB_G_MULTI_CDC
usb_put_function(f_ecm);
usb_put_function_instance(fi_ecm);
#endif
return 0;
}

View File

@ -310,6 +310,7 @@ static struct mv_u3d_trb *mv_u3d_build_trb_one(struct mv_u3d_req *req,
*/
trb_hw = dma_pool_alloc(u3d->trb_pool, GFP_ATOMIC, dma);
if (!trb_hw) {
kfree(trb);
dev_err(u3d->dev,
"%s, dma_pool_alloc fail\n", __func__);
return NULL;
@ -454,6 +455,7 @@ static int mv_u3d_req_to_trb(struct mv_u3d_req *req)
trb_hw = kcalloc(trb_num, sizeof(*trb_hw), GFP_ATOMIC);
if (!trb_hw) {
kfree(trb);
dev_err(u3d->dev,
"%s, trb_hw alloc fail\n", __func__);
return -ENOMEM;
@ -1936,7 +1938,7 @@ static int mv_u3d_probe(struct platform_device *dev)
}
u3d->irq = r->start;
if (request_irq(u3d->irq, mv_u3d_irq,
IRQF_DISABLED | IRQF_SHARED, driver_name, u3d)) {
IRQF_SHARED, driver_name, u3d)) {
u3d->irq = 0;
dev_err(&dev->dev, "Request irq %d for u3d failed\n",
u3d->irq);

View File

@ -129,7 +129,7 @@ static char *type_string (u8 bmAttributes)
case USB_ENDPOINT_XFER_BULK: return "bulk";
case USB_ENDPOINT_XFER_ISOC: return "iso";
case USB_ENDPOINT_XFER_INT: return "intr";
};
}
return "control";
}
#endif
@ -1630,7 +1630,7 @@ static ssize_t queues_show(struct device *_dev, struct device_attribute *attr,
val = "intr"; break;
default:
val = "iso"; break;
}; val; }),
} val; }),
usb_endpoint_maxp (d) & 0x1fff,
ep->dma ? "dma" : "pio", ep->fifo_size
);
@ -2680,7 +2680,6 @@ static void net2280_remove (struct pci_dev *pdev)
if (dev->enabled)
pci_disable_device (pdev);
device_remove_file (&pdev->dev, &dev_attr_registers);
pci_set_drvdata (pdev, NULL);
INFO (dev, "unbind\n");
}

View File

@ -3080,7 +3080,6 @@ static void pch_udc_remove(struct pci_dev *pdev)
if (dev->active)
pci_disable_device(pdev);
kfree(dev);
pci_set_drvdata(pdev, NULL);
}
#ifdef CONFIG_PM

View File

@ -1068,7 +1068,7 @@ static int rndis_proc_show(struct seq_file *m, void *v)
s = "RNDIS_INITIALIZED"; break;
case RNDIS_DATA_INITIALIZED:
s = "RNDIS_DATA_INITIALIZED"; break;
}; s; }),
} s; }),
param->medium,
(param->media_state) ? 0 : param->speed*100,
(param->media_state) ? "disconnected" : "connected",

View File

@ -83,9 +83,12 @@ struct s3c_hsotg_req;
* @dir_in: Set to true if this endpoint is of the IN direction, which
* means that it is sending data to the Host.
* @index: The index for the endpoint registers.
* @mc: Multi Count - number of transactions per microframe
* @interval - Interval for periodic endpoints
* @name: The name array passed to the USB core.
* @halted: Set if the endpoint has been halted.
* @periodic: Set if this is a periodic ep, such as Interrupt
* @isochronous: Set if this is a isochronous ep
* @sent_zlp: Set if we've sent a zero-length packet.
* @total_data: The total number of data bytes done.
* @fifo_size: The size of the FIFO (for periodic IN endpoints)
@ -121,9 +124,12 @@ struct s3c_hsotg_ep {
unsigned char dir_in;
unsigned char index;
unsigned char mc;
unsigned char interval;
unsigned int halted:1;
unsigned int periodic:1;
unsigned int isochronous:1;
unsigned int sent_zlp:1;
char name[10];
@ -468,6 +474,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
void *data;
int can_write;
int pkt_round;
int max_transfer;
to_write -= (buf_pos - hs_ep->last_load);
@ -535,8 +542,10 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
can_write *= 4; /* fifo size is in 32bit quantities. */
}
dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, mps %d\n",
__func__, gnptxsts, can_write, to_write, hs_ep->ep.maxpacket);
max_transfer = hs_ep->ep.maxpacket * hs_ep->mc;
dev_dbg(hsotg->dev, "%s: GNPTXSTS=%08x, can=%d, to=%d, max_transfer %d\n",
__func__, gnptxsts, can_write, to_write, max_transfer);
/*
* limit to 512 bytes of data, it seems at least on the non-periodic
@ -551,19 +560,21 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
* the transfer to return that it did not run out of fifo space
* doing it.
*/
if (to_write > hs_ep->ep.maxpacket) {
to_write = hs_ep->ep.maxpacket;
if (to_write > max_transfer) {
to_write = max_transfer;
s3c_hsotg_en_gsint(hsotg,
periodic ? GINTSTS_PTxFEmp :
GINTSTS_NPTxFEmp);
/* it's needed only when we do not use dedicated fifos */
if (!hsotg->dedicated_fifos)
s3c_hsotg_en_gsint(hsotg,
periodic ? GINTSTS_PTxFEmp :
GINTSTS_NPTxFEmp);
}
/* see if we can write data */
if (to_write > can_write) {
to_write = can_write;
pkt_round = to_write % hs_ep->ep.maxpacket;
pkt_round = to_write % max_transfer;
/*
* Round the write down to an
@ -581,9 +592,11 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
* is more room left.
*/
s3c_hsotg_en_gsint(hsotg,
periodic ? GINTSTS_PTxFEmp :
GINTSTS_NPTxFEmp);
/* it's needed only when we do not use dedicated fifos */
if (!hsotg->dedicated_fifos)
s3c_hsotg_en_gsint(hsotg,
periodic ? GINTSTS_PTxFEmp :
GINTSTS_NPTxFEmp);
}
dev_dbg(hsotg->dev, "write %d/%d, can_write %d, done %d\n",
@ -727,8 +740,16 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
else
packets = 1; /* send one packet if length is zero. */
if (hs_ep->isochronous && length > (hs_ep->mc * hs_ep->ep.maxpacket)) {
dev_err(hsotg->dev, "req length > maxpacket*mc\n");
return;
}
if (dir_in && index != 0)
epsize = DxEPTSIZ_MC(1);
if (hs_ep->isochronous)
epsize = DxEPTSIZ_MC(packets);
else
epsize = DxEPTSIZ_MC(1);
else
epsize = 0;
@ -820,6 +841,9 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n",
__func__, readl(hsotg->regs + epctrl_reg));
/* enable ep interrupts */
s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1);
}
/**
@ -1091,6 +1115,7 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
struct s3c_hsotg_ep *ep;
int ret;
bool halted;
dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
__func__, set ? "SET" : "CLEAR");
@ -1105,6 +1130,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
switch (le16_to_cpu(ctrl->wValue)) {
case USB_ENDPOINT_HALT:
halted = ep->halted;
s3c_hsotg_ep_sethalt(&ep->ep, set);
ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0);
@ -1114,7 +1141,12 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
return ret;
}
if (!set) {
/*
* we have to complete all requests for ep if it was
* halted, and the halt was cleared by CLEAR_FEATURE
*/
if (!set && halted) {
/*
* If we have request in progress,
* then complete it
@ -1147,6 +1179,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
return 1;
}
static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
/**
* s3c_hsotg_process_control - process a control request
* @hsotg: The device state
@ -1246,11 +1280,15 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
* don't believe we need to anything more to get the EP
* to reply with a STALL packet
*/
/*
* complete won't be called, so we enqueue
* setup request here
*/
s3c_hsotg_enqueue_setup(hsotg);
}
}
static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
/**
* s3c_hsotg_complete_setup - completion of a setup transfer
* @ep: The endpoint the request was on.
@ -1698,6 +1736,7 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep];
void __iomem *regs = hsotg->regs;
u32 mpsval;
u32 mcval;
u32 reg;
if (ep == 0) {
@ -1705,15 +1744,19 @@ static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg,
mpsval = s3c_hsotg_ep0_mps(mps);
if (mpsval > 3)
goto bad_mps;
hs_ep->ep.maxpacket = mps;
hs_ep->mc = 1;
} else {
if (mps >= DxEPCTL_MPS_LIMIT+1)
mpsval = mps & DxEPCTL_MPS_MASK;
if (mpsval > 1024)
goto bad_mps;
mpsval = mps;
mcval = ((mps >> 11) & 0x3) + 1;
hs_ep->mc = mcval;
if (mcval > 3)
goto bad_mps;
hs_ep->ep.maxpacket = mpsval;
}
hs_ep->ep.maxpacket = mps;
/*
* update both the in and out endpoint controldir_ registers, even
* if one of the directions may not be in use.
@ -1782,8 +1825,16 @@ static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
{
struct s3c_hsotg_req *hs_req = hs_ep->req;
if (!hs_ep->dir_in || !hs_req)
if (!hs_ep->dir_in || !hs_req) {
/**
* if request is not enqueued, we disable interrupts
* for endpoints, excepting ep0
*/
if (hs_ep->index != 0)
s3c_hsotg_ctrl_epint(hsotg, hs_ep->index,
hs_ep->dir_in, 0);
return 0;
}
if (hs_req->req.actual < hs_req->req.length) {
dev_dbg(hsotg->dev, "trying to write more for ep%d\n",
@ -1887,8 +1938,10 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx);
u32 ints;
u32 ctrl;
ints = readl(hsotg->regs + epint_reg);
ctrl = readl(hsotg->regs + epctl_reg);
/* Clear endpoint interrupts */
writel(ints, hsotg->regs + epint_reg);
@ -1897,6 +1950,14 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
__func__, idx, dir_in ? "in" : "out", ints);
if (ints & DxEPINT_XferCompl) {
if (hs_ep->isochronous && hs_ep->interval == 1) {
if (ctrl & DxEPCTL_EOFrNum)
ctrl |= DxEPCTL_SetEvenFr;
else
ctrl |= DxEPCTL_SetOddFr;
writel(ctrl, hsotg->regs + epctl_reg);
}
dev_dbg(hsotg->dev,
"%s: XferCompl: DxEPCTL=0x%08x, DxEPTSIZ=%08x\n",
__func__, readl(hsotg->regs + epctl_reg),
@ -1963,7 +2024,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
if (ints & DxEPINT_Back2BackSetup)
dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__);
if (dir_in) {
if (dir_in && !hs_ep->isochronous) {
/* not sure if this is important, but we'll clear it anyway */
if (ints & DIEPMSK_INTknTXFEmpMsk) {
dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n",
@ -2092,12 +2153,14 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
}
#define call_gadget(_hs, _entry) \
do { \
if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
(_hs)->driver && (_hs)->driver->_entry) { \
spin_unlock(&_hs->lock); \
(_hs)->driver->_entry(&(_hs)->gadget); \
spin_lock(&_hs->lock); \
}
} \
} while (0)
/**
* s3c_hsotg_disconnect - disconnect service
@ -2241,15 +2304,19 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
GAHBCFG_HBstLen_Incr4,
hsotg->regs + GAHBCFG);
else
writel(GAHBCFG_GlblIntrEn, hsotg->regs + GAHBCFG);
writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NPTxFEmpLvl |
GAHBCFG_PTxFEmpLvl) : 0) |
GAHBCFG_GlblIntrEn,
hsotg->regs + GAHBCFG);
/*
* Enabling INTknTXFEmpMsk here seems to be a big mistake, we end
* up being flooded with interrupts if the host is polling the
* endpoint to try and read data.
* If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts
* when we have no data to transfer. Otherwise we get being flooded by
* interrupts.
*/
writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty : 0) |
writel(((hsotg->dedicated_fifos) ? DIEPMSK_TxFIFOEmpty |
DIEPMSK_INTknTXFEmpMsk : 0) |
DIEPMSK_EPDisbldMsk | DIEPMSK_XferComplMsk |
DIEPMSK_TimeOUTMsk | DIEPMSK_AHBErrMsk |
DIEPMSK_INTknEPMisMsk,
@ -2378,10 +2445,14 @@ irq_retry:
if (gintsts & (GINTSTS_OEPInt | GINTSTS_IEPInt)) {
u32 daint = readl(hsotg->regs + DAINT);
u32 daint_out = daint >> DAINT_OutEP_SHIFT;
u32 daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT);
u32 daintmsk = readl(hsotg->regs + DAINTMSK);
u32 daint_out, daint_in;
int ep;
daint &= daintmsk;
daint_out = daint >> DAINT_OutEP_SHIFT;
daint_in = daint & ~(daint_out << DAINT_OutEP_SHIFT);
dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint);
for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) {
@ -2577,16 +2648,25 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
epctrl |= DxEPCTL_SNAK;
/* update the endpoint state */
hs_ep->ep.maxpacket = mps;
s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps);
/* default, set to non-periodic */
hs_ep->isochronous = 0;
hs_ep->periodic = 0;
hs_ep->halted = 0;
hs_ep->interval = desc->bInterval;
if (hs_ep->interval > 1 && hs_ep->mc > 1)
dev_err(hsotg->dev, "MC > 1 when interval is not 1\n");
switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
case USB_ENDPOINT_XFER_ISOC:
dev_err(hsotg->dev, "no current ISOC support\n");
ret = -EINVAL;
goto out;
epctrl |= DxEPCTL_EPType_Iso;
epctrl |= DxEPCTL_SetEvenFr;
hs_ep->isochronous = 1;
if (dir_in)
hs_ep->periodic = 1;
break;
case USB_ENDPOINT_XFER_BULK:
epctrl |= DxEPCTL_EPType_Bulk;
@ -2634,7 +2714,6 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
/* enable the endpoint interrupt */
s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1);
out:
spin_unlock_irqrestore(&hsotg->lock, flags);
return ret;
}
@ -2776,6 +2855,8 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
writel(epctl, hs->regs + epreg);
hs_ep->halted = value;
return 0;
}
@ -2903,7 +2984,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
int ret;
if (!hsotg) {
printk(KERN_ERR "%s: called with no device\n", __func__);
pr_err("%s: called with no device\n", __func__);
return -ENODEV;
}
@ -3066,7 +3147,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
hs_ep->parent = hsotg;
hs_ep->ep.name = hs_ep->name;
hs_ep->ep.maxpacket = epnum ? 512 : EP0_MPS_LIMIT;
hs_ep->ep.maxpacket = epnum ? 1024 : EP0_MPS_LIMIT;
hs_ep->ep.ops = &s3c_hsotg_ep_ops;
/*
@ -3200,7 +3281,7 @@ static int state_show(struct seq_file *seq, void *v)
readl(regs + GNPTXSTS),
readl(regs + GRXSTSR));
seq_printf(seq, "\nEndpoint status:\n");
seq_puts(seq, "\nEndpoint status:\n");
for (idx = 0; idx < 15; idx++) {
u32 in, out;
@ -3217,7 +3298,7 @@ static int state_show(struct seq_file *seq, void *v)
seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x",
in, out);
seq_printf(seq, "\n");
seq_puts(seq, "\n");
}
return 0;
@ -3251,7 +3332,7 @@ static int fifo_show(struct seq_file *seq, void *v)
u32 val;
int idx;
seq_printf(seq, "Non-periodic FIFOs:\n");
seq_puts(seq, "Non-periodic FIFOs:\n");
seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ));
val = readl(regs + GNPTXFSIZ);
@ -3259,7 +3340,7 @@ static int fifo_show(struct seq_file *seq, void *v)
val >> GNPTXFSIZ_NPTxFDep_SHIFT,
val & GNPTXFSIZ_NPTxFStAddr_MASK);
seq_printf(seq, "\nPeriodic TXFIFOs:\n");
seq_puts(seq, "\nPeriodic TXFIFOs:\n");
for (idx = 1; idx <= 15; idx++) {
val = readl(regs + DPTXFSIZn(idx));
@ -3330,7 +3411,7 @@ static int ep_show(struct seq_file *seq, void *v)
readl(regs + DIEPTSIZ(index)),
readl(regs + DOEPTSIZ(index)));
seq_printf(seq, "\n");
seq_puts(seq, "\n");
seq_printf(seq, "mps %d\n", ep->ep.maxpacket);
seq_printf(seq, "total_data=%ld\n", ep->total_data);
@ -3341,7 +3422,7 @@ static int ep_show(struct seq_file *seq, void *v)
list_for_each_entry(req, &ep->queue, queue) {
if (--show_limit < 0) {
seq_printf(seq, "not showing more requests...\n");
seq_puts(seq, "not showing more requests...\n");
break;
}

View File

@ -23,242 +23,17 @@
* The valid range of num_buffers is: num >= 2 && num <= 4.
*/
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/usb/composite.h>
#include <linux/usb/storage.h>
#include <scsi/scsi.h>
#include <asm/unaligned.h>
/*
* Thanks to NetChip Technologies for donating this product ID.
*
* DO NOT REUSE THESE IDs with any other driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
#define FSG_VENDOR_ID 0x0525 /* NetChip */
#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
/*-------------------------------------------------------------------------*/
#ifndef DEBUG
#undef VERBOSE_DEBUG
#undef DUMP_MSGS
#endif /* !DEBUG */
#ifdef VERBOSE_DEBUG
#define VLDBG LDBG
#else
#define VLDBG(lun, fmt, args...) do { } while (0)
#endif /* VERBOSE_DEBUG */
#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args)
#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args)
#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args)
#ifdef DUMP_MSGS
# define dump_msg(fsg, /* const char * */ label, \
/* const u8 * */ buf, /* unsigned */ length) do { \
if (length < 512) { \
DBG(fsg, "%s, length %u:\n", label, length); \
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \
16, 1, buf, length, 0); \
} \
} while (0)
# define dump_cdb(fsg) do { } while (0)
#else
# define dump_msg(fsg, /* const char * */ label, \
/* const u8 * */ buf, /* unsigned */ length) do { } while (0)
# ifdef VERBOSE_DEBUG
# define dump_cdb(fsg) \
print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \
16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \
# else
# define dump_cdb(fsg) do { } while (0)
# endif /* VERBOSE_DEBUG */
#endif /* DUMP_MSGS */
/*-------------------------------------------------------------------------*/
/* Length of a SCSI Command Data Block */
#define MAX_COMMAND_SIZE 16
/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
#define SS_NO_SENSE 0
#define SS_COMMUNICATION_FAILURE 0x040800
#define SS_INVALID_COMMAND 0x052000
#define SS_INVALID_FIELD_IN_CDB 0x052400
#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
#define SS_MEDIUM_NOT_PRESENT 0x023a00
#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
#define SS_RESET_OCCURRED 0x062900
#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
#define SS_UNRECOVERED_READ_ERROR 0x031100
#define SS_WRITE_ERROR 0x030c02
#define SS_WRITE_PROTECTED 0x072700
#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */
#define ASC(x) ((u8) ((x) >> 8))
#define ASCQ(x) ((u8) (x))
/*-------------------------------------------------------------------------*/
struct fsg_lun {
struct file *filp;
loff_t file_length;
loff_t num_sectors;
unsigned int initially_ro:1;
unsigned int ro:1;
unsigned int removable:1;
unsigned int cdrom:1;
unsigned int prevent_medium_removal:1;
unsigned int registered:1;
unsigned int info_valid:1;
unsigned int nofua:1;
u32 sense_data;
u32 sense_data_info;
u32 unit_attention_data;
unsigned int blkbits; /* Bits of logical block size of bound block device */
unsigned int blksize; /* logical block size of bound block device */
struct device dev;
};
static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
{
return curlun->filp != NULL;
}
static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev)
{
return container_of(dev, struct fsg_lun, dev);
}
/* Big enough to hold our biggest descriptor */
#define EP0_BUFSIZE 256
#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);
MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers");
#else
/*
* Number of buffers we will use.
* 2 is usually enough for good buffering pipeline
*/
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
/* check if fsg_num_buffers is within a valid range */
static inline int fsg_num_buffers_validate(void)
{
if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
return 0;
pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
fsg_num_buffers, 2 ,4);
return -EINVAL;
}
/* Default size of buffer length. */
#define FSG_BUFLEN ((u32)16384)
/* Maximal number of LUNs supported in mass storage function */
#define FSG_MAX_LUNS 8
enum fsg_buffer_state {
BUF_STATE_EMPTY = 0,
BUF_STATE_FULL,
BUF_STATE_BUSY
};
struct fsg_buffhd {
void *buf;
enum fsg_buffer_state state;
struct fsg_buffhd *next;
/*
* The NetChip 2280 is faster, and handles some protocol faults
* better, if we don't submit any short bulk-out read requests.
* So we will record the intended request length here.
*/
unsigned int bulk_out_intended_length;
struct usb_request *inreq;
int inreq_busy;
struct usb_request *outreq;
int outreq_busy;
};
enum fsg_state {
/* This one isn't used anywhere */
FSG_STATE_COMMAND_PHASE = -10,
FSG_STATE_DATA_PHASE,
FSG_STATE_STATUS_PHASE,
FSG_STATE_IDLE = 0,
FSG_STATE_ABORT_BULK_OUT,
FSG_STATE_RESET,
FSG_STATE_INTERFACE_CHANGE,
FSG_STATE_CONFIG_CHANGE,
FSG_STATE_DISCONNECT,
FSG_STATE_EXIT,
FSG_STATE_TERMINATED
};
enum data_direction {
DATA_DIR_UNKNOWN = 0,
DATA_DIR_FROM_HOST,
DATA_DIR_TO_HOST,
DATA_DIR_NONE
};
/*-------------------------------------------------------------------------*/
static inline u32 get_unaligned_be24(u8 *buf)
{
return 0xffffff & (u32) get_unaligned_be32(buf - 1);
}
/*-------------------------------------------------------------------------*/
enum {
FSG_STRING_INTERFACE
};
#include "storage_common.h"
/* There is only one interface. */
static struct usb_interface_descriptor
fsg_intf_desc = {
struct usb_interface_descriptor fsg_intf_desc = {
.bLength = sizeof fsg_intf_desc,
.bDescriptorType = USB_DT_INTERFACE,
@ -268,14 +43,14 @@ fsg_intf_desc = {
.bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */
.iInterface = FSG_STRING_INTERFACE,
};
EXPORT_SYMBOL(fsg_intf_desc);
/*
* Three full-speed endpoint descriptors: bulk-in, bulk-out, and
* interrupt-in.
*/
static struct usb_endpoint_descriptor
fsg_fs_bulk_in_desc = {
struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@ -283,9 +58,9 @@ fsg_fs_bulk_in_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
/* wMaxPacketSize set by autoconfiguration */
};
EXPORT_SYMBOL(fsg_fs_bulk_in_desc);
static struct usb_endpoint_descriptor
fsg_fs_bulk_out_desc = {
struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@ -293,13 +68,15 @@ fsg_fs_bulk_out_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
/* wMaxPacketSize set by autoconfiguration */
};
EXPORT_SYMBOL(fsg_fs_bulk_out_desc);
static struct usb_descriptor_header *fsg_fs_function[] = {
struct usb_descriptor_header *fsg_fs_function[] = {
(struct usb_descriptor_header *) &fsg_intf_desc,
(struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
(struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
NULL,
};
EXPORT_SYMBOL(fsg_fs_function);
/*
@ -310,8 +87,7 @@ static struct usb_descriptor_header *fsg_fs_function[] = {
* and a "device qualifier" ... plus more construction options
* for the configuration descriptor.
*/
static struct usb_endpoint_descriptor
fsg_hs_bulk_in_desc = {
struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@ -319,9 +95,9 @@ fsg_hs_bulk_in_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(512),
};
EXPORT_SYMBOL(fsg_hs_bulk_in_desc);
static struct usb_endpoint_descriptor
fsg_hs_bulk_out_desc = {
struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@ -330,17 +106,18 @@ fsg_hs_bulk_out_desc = {
.wMaxPacketSize = cpu_to_le16(512),
.bInterval = 1, /* NAK every 1 uframe */
};
EXPORT_SYMBOL(fsg_hs_bulk_out_desc);
static struct usb_descriptor_header *fsg_hs_function[] = {
struct usb_descriptor_header *fsg_hs_function[] = {
(struct usb_descriptor_header *) &fsg_intf_desc,
(struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
(struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
NULL,
};
EXPORT_SYMBOL(fsg_hs_function);
static struct usb_endpoint_descriptor
fsg_ss_bulk_in_desc = {
struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@ -348,16 +125,17 @@ fsg_ss_bulk_in_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
EXPORT_SYMBOL(fsg_ss_bulk_in_desc);
static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
.bLength = sizeof(fsg_ss_bulk_in_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/*.bMaxBurst = DYNAMIC, */
};
EXPORT_SYMBOL(fsg_ss_bulk_in_comp_desc);
static struct usb_endpoint_descriptor
fsg_ss_bulk_out_desc = {
struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
@ -365,15 +143,17 @@ fsg_ss_bulk_out_desc = {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = cpu_to_le16(1024),
};
EXPORT_SYMBOL(fsg_ss_bulk_out_desc);
static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
.bLength = sizeof(fsg_ss_bulk_in_comp_desc),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
/*.bMaxBurst = DYNAMIC, */
};
EXPORT_SYMBOL(fsg_ss_bulk_out_comp_desc);
static struct usb_descriptor_header *fsg_ss_function[] = {
struct usb_descriptor_header *fsg_ss_function[] = {
(struct usb_descriptor_header *) &fsg_intf_desc,
(struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
(struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
@ -381,17 +161,7 @@ static struct usb_descriptor_header *fsg_ss_function[] = {
(struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
NULL,
};
/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
static struct usb_string fsg_strings[] = {
{FSG_STRING_INTERFACE, fsg_string_interface},
{}
};
static struct usb_gadget_strings fsg_stringtab = {
.language = 0x0409, /* en-us */
.strings = fsg_strings,
};
EXPORT_SYMBOL(fsg_ss_function);
/*-------------------------------------------------------------------------*/
@ -401,7 +171,7 @@ static struct usb_gadget_strings fsg_stringtab = {
* the caller must own fsg->filesem for writing.
*/
static void fsg_lun_close(struct fsg_lun *curlun)
void fsg_lun_close(struct fsg_lun *curlun)
{
if (curlun->filp) {
LDBG(curlun, "close backing file\n");
@ -409,9 +179,9 @@ static void fsg_lun_close(struct fsg_lun *curlun)
curlun->filp = NULL;
}
}
EXPORT_SYMBOL(fsg_lun_close);
static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
{
int ro;
struct file *filp = NULL;
@ -508,6 +278,7 @@ out:
fput(filp);
return rc;
}
EXPORT_SYMBOL(fsg_lun_open);
/*-------------------------------------------------------------------------*/
@ -516,7 +287,7 @@ out:
* Sync the file data, don't bother with the metadata.
* This code was copied from fs/buffer.c:sys_fdatasync().
*/
static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
int fsg_lun_fsync_sub(struct fsg_lun *curlun)
{
struct file *filp = curlun->filp;
@ -524,8 +295,9 @@ static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
return 0;
return vfs_fsync(filp, 1);
}
EXPORT_SYMBOL(fsg_lun_fsync_sub);
static void store_cdrom_address(u8 *dest, int msf, u32 addr)
void store_cdrom_address(u8 *dest, int msf, u32 addr)
{
if (msf) {
/* Convert to Minutes-Seconds-Frames */
@ -542,34 +314,28 @@ static void store_cdrom_address(u8 *dest, int msf, u32 addr)
put_unaligned_be32(addr, dest);
}
}
EXPORT_SYMBOL(store_cdrom_address);
/*-------------------------------------------------------------------------*/
static ssize_t ro_show(struct device *dev, struct device_attribute *attr,
char *buf)
ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf)
{
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
? curlun->ro
: curlun->initially_ro);
}
EXPORT_SYMBOL(fsg_show_ro);
static ssize_t nofua_show(struct device *dev, struct device_attribute *attr,
char *buf)
ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf)
{
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
return sprintf(buf, "%u\n", curlun->nofua);
}
EXPORT_SYMBOL(fsg_show_nofua);
static ssize_t file_show(struct device *dev, struct device_attribute *attr,
char *buf)
ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
char *buf)
{
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
struct rw_semaphore *filesem = dev_get_drvdata(dev);
char *p;
ssize_t rc;
@ -591,17 +357,44 @@ static ssize_t file_show(struct device *dev, struct device_attribute *attr,
up_read(filesem);
return rc;
}
EXPORT_SYMBOL(fsg_show_file);
ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf)
{
return sprintf(buf, "%u\n", curlun->cdrom);
}
EXPORT_SYMBOL(fsg_show_cdrom);
static ssize_t ro_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf)
{
return sprintf(buf, "%u\n", curlun->removable);
}
EXPORT_SYMBOL(fsg_show_removable);
/*
* The caller must hold fsg->filesem for reading when calling this function.
*/
static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro)
{
if (fsg_lun_is_open(curlun)) {
LDBG(curlun, "read-only status change prevented\n");
return -EBUSY;
}
curlun->ro = ro;
curlun->initially_ro = ro;
LDBG(curlun, "read-only status set to %d\n", curlun->ro);
return 0;
}
ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count)
{
ssize_t rc;
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
struct rw_semaphore *filesem = dev_get_drvdata(dev);
unsigned ro;
bool ro;
rc = kstrtouint(buf, 2, &ro);
rc = strtobool(buf, &ro);
if (rc)
return rc;
@ -610,27 +403,21 @@ static ssize_t ro_store(struct device *dev, struct device_attribute *attr,
* backing file is closed.
*/
down_read(filesem);
if (fsg_lun_is_open(curlun)) {
LDBG(curlun, "read-only status change prevented\n");
rc = -EBUSY;
} else {
curlun->ro = ro;
curlun->initially_ro = ro;
LDBG(curlun, "read-only status set to %d\n", curlun->ro);
rc = _fsg_store_ro(curlun, ro);
if (!rc)
rc = count;
}
up_read(filesem);
return rc;
}
EXPORT_SYMBOL(fsg_store_ro);
static ssize_t nofua_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count)
{
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
unsigned nofua;
bool nofua;
int ret;
ret = kstrtouint(buf, 2, &nofua);
ret = strtobool(buf, &nofua);
if (ret)
return ret;
@ -642,12 +429,11 @@ static ssize_t nofua_store(struct device *dev, struct device_attribute *attr,
return count;
}
EXPORT_SYMBOL(fsg_store_nofua);
static ssize_t file_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count)
{
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
struct rw_semaphore *filesem = dev_get_drvdata(dev);
int rc = 0;
if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
@ -674,3 +460,45 @@ static ssize_t file_store(struct device *dev, struct device_attribute *attr,
up_write(filesem);
return (rc < 0 ? rc : count);
}
EXPORT_SYMBOL(fsg_store_file);
ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count)
{
bool cdrom;
int ret;
ret = strtobool(buf, &cdrom);
if (ret)
return ret;
down_read(filesem);
ret = cdrom ? _fsg_store_ro(curlun, true) : 0;
if (!ret) {
curlun->cdrom = cdrom;
ret = count;
}
up_read(filesem);
return ret;
}
EXPORT_SYMBOL(fsg_store_cdrom);
ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
size_t count)
{
bool removable;
int ret;
ret = strtobool(buf, &removable);
if (ret)
return ret;
curlun->removable = removable;
return count;
}
EXPORT_SYMBOL(fsg_store_removable);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,229 @@
#ifndef USB_STORAGE_COMMON_H
#define USB_STORAGE_COMMON_H
#include <linux/device.h>
#include <linux/usb/storage.h>
#include <scsi/scsi.h>
#include <asm/unaligned.h>
#ifndef DEBUG
#undef VERBOSE_DEBUG
#undef DUMP_MSGS
#endif /* !DEBUG */
#ifdef VERBOSE_DEBUG
#define VLDBG LDBG
#else
#define VLDBG(lun, fmt, args...) do { } while (0)
#endif /* VERBOSE_DEBUG */
#define _LMSG(func, lun, fmt, args...) \
do { \
if ((lun)->name_pfx && *(lun)->name_pfx) \
func("%s/%s: " fmt, *(lun)->name_pfx, \
(lun)->name, ## args); \
else \
func("%s: " fmt, (lun)->name, ## args); \
} while (0)
#define LDBG(lun, fmt, args...) _LMSG(pr_debug, lun, fmt, ## args)
#define LERROR(lun, fmt, args...) _LMSG(pr_err, lun, fmt, ## args)
#define LWARN(lun, fmt, args...) _LMSG(pr_warn, lun, fmt, ## args)
#define LINFO(lun, fmt, args...) _LMSG(pr_info, lun, fmt, ## args)
#ifdef DUMP_MSGS
# define dump_msg(fsg, /* const char * */ label, \
/* const u8 * */ buf, /* unsigned */ length) \
do { \
if (length < 512) { \
DBG(fsg, "%s, length %u:\n", label, length); \
print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \
16, 1, buf, length, 0); \
} \
} while (0)
# define dump_cdb(fsg) do { } while (0)
#else
# define dump_msg(fsg, /* const char * */ label, \
/* const u8 * */ buf, /* unsigned */ length) do { } while (0)
# ifdef VERBOSE_DEBUG
# define dump_cdb(fsg) \
print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \
16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \
# else
# define dump_cdb(fsg) do { } while (0)
# endif /* VERBOSE_DEBUG */
#endif /* DUMP_MSGS */
/* Length of a SCSI Command Data Block */
#define MAX_COMMAND_SIZE 16
/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
#define SS_NO_SENSE 0
#define SS_COMMUNICATION_FAILURE 0x040800
#define SS_INVALID_COMMAND 0x052000
#define SS_INVALID_FIELD_IN_CDB 0x052400
#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
#define SS_MEDIUM_NOT_PRESENT 0x023a00
#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
#define SS_RESET_OCCURRED 0x062900
#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
#define SS_UNRECOVERED_READ_ERROR 0x031100
#define SS_WRITE_ERROR 0x030c02
#define SS_WRITE_PROTECTED 0x072700
#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */
#define ASC(x) ((u8) ((x) >> 8))
#define ASCQ(x) ((u8) (x))
struct fsg_lun {
struct file *filp;
loff_t file_length;
loff_t num_sectors;
unsigned int initially_ro:1;
unsigned int ro:1;
unsigned int removable:1;
unsigned int cdrom:1;
unsigned int prevent_medium_removal:1;
unsigned int registered:1;
unsigned int info_valid:1;
unsigned int nofua:1;
u32 sense_data;
u32 sense_data_info;
u32 unit_attention_data;
unsigned int blkbits; /* Bits of logical block size
of bound block device */
unsigned int blksize; /* logical block size of bound block device */
struct device dev;
const char *name; /* "lun.name" */
const char **name_pfx; /* "function.name" */
};
static inline bool fsg_lun_is_open(struct fsg_lun *curlun)
{
return curlun->filp != NULL;
}
/* Big enough to hold our biggest descriptor */
#define EP0_BUFSIZE 256
#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
/* Default size of buffer length. */
#define FSG_BUFLEN ((u32)16384)
/* Maximal number of LUNs supported in mass storage function */
#define FSG_MAX_LUNS 8
enum fsg_buffer_state {
BUF_STATE_EMPTY = 0,
BUF_STATE_FULL,
BUF_STATE_BUSY
};
struct fsg_buffhd {
void *buf;
enum fsg_buffer_state state;
struct fsg_buffhd *next;
/*
* The NetChip 2280 is faster, and handles some protocol faults
* better, if we don't submit any short bulk-out read requests.
* So we will record the intended request length here.
*/
unsigned int bulk_out_intended_length;
struct usb_request *inreq;
int inreq_busy;
struct usb_request *outreq;
int outreq_busy;
};
enum fsg_state {
/* This one isn't used anywhere */
FSG_STATE_COMMAND_PHASE = -10,
FSG_STATE_DATA_PHASE,
FSG_STATE_STATUS_PHASE,
FSG_STATE_IDLE = 0,
FSG_STATE_ABORT_BULK_OUT,
FSG_STATE_RESET,
FSG_STATE_INTERFACE_CHANGE,
FSG_STATE_CONFIG_CHANGE,
FSG_STATE_DISCONNECT,
FSG_STATE_EXIT,
FSG_STATE_TERMINATED
};
enum data_direction {
DATA_DIR_UNKNOWN = 0,
DATA_DIR_FROM_HOST,
DATA_DIR_TO_HOST,
DATA_DIR_NONE
};
static inline u32 get_unaligned_be24(u8 *buf)
{
return 0xffffff & (u32) get_unaligned_be32(buf - 1);
}
static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev)
{
return container_of(dev, struct fsg_lun, dev);
}
enum {
FSG_STRING_INTERFACE
};
extern struct usb_interface_descriptor fsg_intf_desc;
extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc;
extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc;
extern struct usb_descriptor_header *fsg_fs_function[];
extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc;
extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc;
extern struct usb_descriptor_header *fsg_hs_function[];
extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc;
extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc;
extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc;
extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc;
extern struct usb_descriptor_header *fsg_ss_function[];
void fsg_lun_close(struct fsg_lun *curlun);
int fsg_lun_open(struct fsg_lun *curlun, const char *filename);
int fsg_lun_fsync_sub(struct fsg_lun *curlun);
void store_cdrom_address(u8 *dest, int msf, u32 addr);
ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
char *buf);
ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf);
ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf);
ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count);
ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count);
ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count);
ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem,
const char *buf, size_t count);
ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf,
size_t count);
#endif /* USB_STORAGE_COMMON_H */

View File

@ -472,7 +472,7 @@ static int usbg_bot_setup(struct usb_function *f,
bot_enqueue_cmd_cbw(fu);
return 0;
break;
};
}
return -ENOTSUPP;
}
@ -617,7 +617,7 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
default:
BUG();
};
}
return;
cleanup:

View File

@ -356,7 +356,8 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
return 0;
err1:
dev_err(&udc->dev, "failed to start %s: %d\n",
if (ret != -EISNAM)
dev_err(&udc->dev, "failed to start %s: %d\n",
udc->driver->function, ret);
udc->driver = NULL;
udc->dev.driver = NULL;

View File

@ -95,6 +95,18 @@ unsigned autoresume = DEFAULT_AUTORESUME;
module_param(autoresume, uint, S_IRUGO);
MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup");
/* Maximum Autoresume time */
unsigned max_autoresume;
module_param(max_autoresume, uint, S_IRUGO);
MODULE_PARM_DESC(max_autoresume, "maximum seconds before remote wakeup");
/* Interval between two remote wakeups */
unsigned autoresume_interval_ms;
module_param(autoresume_interval_ms, uint, S_IRUGO);
MODULE_PARM_DESC(autoresume_interval_ms,
"milliseconds to increase successive wakeup delays");
static unsigned autoresume_step_ms;
/*-------------------------------------------------------------------------*/
static struct usb_device_descriptor device_desc = {
@ -183,8 +195,16 @@ static void zero_suspend(struct usb_composite_dev *cdev)
return;
if (autoresume) {
mod_timer(&autoresume_timer, jiffies + (HZ * autoresume));
DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume);
if (max_autoresume &&
(autoresume_step_ms > max_autoresume * 1000))
autoresume_step_ms = autoresume * 1000;
mod_timer(&autoresume_timer, jiffies +
msecs_to_jiffies(autoresume_step_ms));
DBG(cdev, "suspend, wakeup in %d milliseconds\n",
autoresume_step_ms);
autoresume_step_ms += autoresume_interval_ms;
} else
DBG(cdev, "%s\n", __func__);
}
@ -316,6 +336,7 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
if (autoresume) {
sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
autoresume_step_ms = autoresume * 1000;
}
/* support OTG systems */

View File

@ -54,7 +54,7 @@ config USB_EHCI_HCD
config USB_EHCI_ROOT_HUB_TT
bool "Root Hub Transaction Translators"
depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST
depends on USB_EHCI_HCD
---help---
Some EHCI chips have vendor-specific extensions to integrate
transaction translators, so that no OHCI or UHCI companion
@ -66,7 +66,7 @@ config USB_EHCI_ROOT_HUB_TT
config USB_EHCI_TT_NEWSCHED
bool "Improved Transaction Translator scheduling"
depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST
depends on USB_EHCI_HCD
default y
---help---
This changes the periodic scheduling code to fill more of the low
@ -203,12 +203,11 @@ config USB_EHCI_SH
Enables support for the on-chip EHCI controller on the SuperH.
If you use the PCI EHCI controller, this option is not necessary.
config USB_EHCI_S5P
config USB_EHCI_EXYNOS
tristate "EHCI support for Samsung S5P/EXYNOS SoC Series"
depends on PLAT_S5P || ARCH_EXYNOS
help
Enable support for the Samsung S5Pxxxx and Exynos3/4/5 SOC's
on-chip EHCI controller.
Enable support for the Samsung Exynos SOC's on-chip EHCI controller.
config USB_EHCI_MV
bool "EHCI support for Marvell PXA/MMP USB controller"
@ -224,7 +223,7 @@ config USB_EHCI_MV
on-chip EHCI USB controller" for those.
config USB_W90X900_EHCI
bool "W90X900(W90P910) EHCI support"
tristate "W90X900(W90P910) EHCI support"
depends on ARCH_W90X900
---help---
Enables support for the W90X900 USB controller
@ -367,14 +366,54 @@ config USB_OHCI_HCD
if USB_OHCI_HCD
config USB_OHCI_HCD_OMAP1
bool "OHCI support for OMAP1/2 chips"
tristate "OHCI support for OMAP1/2 chips"
depends on ARCH_OMAP1
default y
---help---
Enables support for the OHCI controller on OMAP1/2 chips.
config USB_OHCI_HCD_SPEAR
tristate "Support for ST SPEAr on-chip OHCI USB controller"
depends on USB_OHCI_HCD && PLAT_SPEAR
default y
---help---
Enables support for the on-chip OHCI controller on
ST SPEAr chips.
config USB_OHCI_HCD_S3C2410
tristate "OHCI support for Samsung S3C24xx/S3C64xx SoC series"
depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX)
default y
---help---
Enables support for the on-chip OHCI controller on
S3C24xx/S3C64xx chips.
config USB_OHCI_HCD_LPC32XX
tristate "Support for LPC on-chip OHCI USB controller"
depends on USB_OHCI_HCD && ARCH_LPC32XX
default y
---help---
Enables support for the on-chip OHCI controller on
NXP chips.
config USB_OHCI_HCD_PXA27X
tristate "Support for PXA27X/PXA3XX on-chip OHCI USB controller"
depends on USB_OHCI_HCD && (PXA27x || PXA3xx)
default y
---help---
Enables support for the on-chip OHCI controller on
PXA27x/PXA3xx chips.
config USB_OHCI_HCD_AT91
tristate "Support for Atmel on-chip OHCI USB controller"
depends on USB_OHCI_HCD && ARCH_AT91
default y
---help---
Enables support for the on-chip OHCI controller on
Atmel chips.
config USB_OHCI_HCD_OMAP3
bool "OHCI support for OMAP3 and later chips"
tristate "OHCI support for OMAP3 and later chips"
depends on (ARCH_OMAP3 || ARCH_OMAP4)
default y
---help---
@ -454,8 +493,8 @@ config USB_OHCI_SH
If you use the PCI OHCI controller, this option is not necessary.
config USB_OHCI_EXYNOS
boolean "OHCI support for Samsung EXYNOS SoC Series"
depends on ARCH_EXYNOS
tristate "OHCI support for Samsung S5P/EXYNOS SoC Series"
depends on PLAT_S5P || ARCH_EXYNOS
help
Enable support for the Samsung Exynos SOC's on-chip OHCI controller.

View File

@ -34,10 +34,11 @@ obj-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o
obj-$(CONFIG_USB_EHCI_HCD_OMAP) += ehci-omap.o
obj-$(CONFIG_USB_EHCI_HCD_ORION) += ehci-orion.o
obj-$(CONFIG_USB_EHCI_HCD_SPEAR) += ehci-spear.o
obj-$(CONFIG_USB_EHCI_S5P) += ehci-s5p.o
obj-$(CONFIG_USB_EHCI_EXYNOS) += ehci-exynos.o
obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o
obj-$(CONFIG_USB_EHCI_MSM) += ehci-msm.o
obj-$(CONFIG_USB_EHCI_TEGRA) += ehci-tegra.o
obj-$(CONFIG_USB_W90X900_EHCI) += ehci-w90x900.o
obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
@ -46,6 +47,14 @@ obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o
obj-$(CONFIG_USB_OHCI_HCD_PLATFORM) += ohci-platform.o
obj-$(CONFIG_USB_OHCI_EXYNOS) += ohci-exynos.o
obj-$(CONFIG_USB_OHCI_HCD_OMAP1) += ohci-omap.o
obj-$(CONFIG_USB_OHCI_HCD_OMAP3) += ohci-omap3.o
obj-$(CONFIG_USB_OHCI_HCD_SPEAR) += ohci-spear.o
obj-$(CONFIG_USB_OHCI_HCD_AT91) += ohci-at91.o
obj-$(CONFIG_USB_OHCI_HCD_S3C2410) += ohci-s3c2410.o
obj-$(CONFIG_USB_OHCI_HCD_LPC32XX) += ohci-nxp.o
obj-$(CONFIG_USB_OHCI_HCD_PXA27X) += ohci-pxa27x.o
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_FHCI_HCD) += fhci.o

View File

@ -30,13 +30,17 @@ static const char hcd_name[] = "ehci-atmel";
static struct hc_driver __read_mostly ehci_atmel_hc_driver;
/* interface and function clocks */
static struct clk *iclk, *fclk;
static struct clk *iclk, *fclk, *uclk;
static int clocked;
/*-------------------------------------------------------------------------*/
static void atmel_start_clock(void)
{
if (IS_ENABLED(CONFIG_COMMON_CLK)) {
clk_set_rate(uclk, 48000000);
clk_prepare_enable(uclk);
}
clk_prepare_enable(iclk);
clk_prepare_enable(fclk);
clocked = 1;
@ -46,6 +50,8 @@ static void atmel_stop_clock(void)
{
clk_disable_unprepare(fclk);
clk_disable_unprepare(iclk);
if (IS_ENABLED(CONFIG_COMMON_CLK))
clk_disable_unprepare(uclk);
clocked = 0;
}
@ -130,6 +136,14 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
retval = -ENOENT;
goto fail_request_resource;
}
if (IS_ENABLED(CONFIG_COMMON_CLK)) {
uclk = devm_clk_get(&pdev->dev, "usb_clk");
if (IS_ERR(uclk)) {
dev_err(&pdev->dev, "failed to get uclk\n");
retval = PTR_ERR(uclk);
goto fail_request_resource;
}
}
ehci = hcd_to_ehci(hcd);
/* registers start at offset 0x0 */

View File

@ -334,6 +334,7 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { }
/* troubleshooting help: expose state in debugfs */
static int debug_async_open(struct inode *, struct file *);
static int debug_bandwidth_open(struct inode *, struct file *);
static int debug_periodic_open(struct inode *, struct file *);
static int debug_registers_open(struct inode *, struct file *);
@ -347,6 +348,13 @@ static const struct file_operations debug_async_fops = {
.release = debug_close,
.llseek = default_llseek,
};
static const struct file_operations debug_bandwidth_fops = {
.owner = THIS_MODULE,
.open = debug_bandwidth_open,
.read = debug_output,
.release = debug_close,
.llseek = default_llseek,
};
static const struct file_operations debug_periodic_fops = {
.owner = THIS_MODULE,
.open = debug_periodic_open,
@ -379,7 +387,7 @@ struct debug_buffer {
case QH_LOW_SPEED: tmp = 'l'; break; \
case QH_HIGH_SPEED: tmp = 'h'; break; \
default: tmp = '?'; break; \
}; tmp; })
} tmp; })
static inline char token_mark(struct ehci_hcd *ehci, __hc32 token)
{
@ -525,6 +533,89 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
return strlen(buf->output_buf);
}
static ssize_t fill_bandwidth_buffer(struct debug_buffer *buf)
{
struct ehci_hcd *ehci;
struct ehci_tt *tt;
struct ehci_per_sched *ps;
unsigned temp, size;
char *next;
unsigned i;
u8 *bw;
u16 *bf;
u8 budget[EHCI_BANDWIDTH_SIZE];
ehci = hcd_to_ehci(bus_to_hcd(buf->bus));
next = buf->output_buf;
size = buf->alloc_size;
*next = 0;
spin_lock_irq(&ehci->lock);
/* Dump the HS bandwidth table */
temp = scnprintf(next, size,
"HS bandwidth allocation (us per microframe)\n");
size -= temp;
next += temp;
for (i = 0; i < EHCI_BANDWIDTH_SIZE; i += 8) {
bw = &ehci->bandwidth[i];
temp = scnprintf(next, size,
"%2u: %4u%4u%4u%4u%4u%4u%4u%4u\n",
i, bw[0], bw[1], bw[2], bw[3],
bw[4], bw[5], bw[6], bw[7]);
size -= temp;
next += temp;
}
/* Dump all the FS/LS tables */
list_for_each_entry(tt, &ehci->tt_list, tt_list) {
temp = scnprintf(next, size,
"\nTT %s port %d FS/LS bandwidth allocation (us per frame)\n",
dev_name(&tt->usb_tt->hub->dev),
tt->tt_port + !!tt->usb_tt->multi);
size -= temp;
next += temp;
bf = tt->bandwidth;
temp = scnprintf(next, size,
" %5u%5u%5u%5u%5u%5u%5u%5u\n",
bf[0], bf[1], bf[2], bf[3],
bf[4], bf[5], bf[6], bf[7]);
size -= temp;
next += temp;
temp = scnprintf(next, size,
"FS/LS budget (us per microframe)\n");
size -= temp;
next += temp;
compute_tt_budget(budget, tt);
for (i = 0; i < EHCI_BANDWIDTH_SIZE; i += 8) {
bw = &budget[i];
temp = scnprintf(next, size,
"%2u: %4u%4u%4u%4u%4u%4u%4u%4u\n",
i, bw[0], bw[1], bw[2], bw[3],
bw[4], bw[5], bw[6], bw[7]);
size -= temp;
next += temp;
}
list_for_each_entry(ps, &tt->ps_list, ps_list) {
temp = scnprintf(next, size,
"%s ep %02x: %4u @ %2u.%u+%u mask %04x\n",
dev_name(&ps->udev->dev),
ps->ep->desc.bEndpointAddress,
ps->tt_usecs,
ps->bw_phase, ps->phase_uf,
ps->bw_period, ps->cs_mask);
size -= temp;
next += temp;
}
}
spin_unlock_irq(&ehci->lock);
return next - buf->output_buf;
}
#define DBG_SCHED_LIMIT 64
static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
{
@ -571,7 +662,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
case Q_TYPE_QH:
hw = p.qh->hw;
temp = scnprintf (next, size, " qh%d-%04x/%p",
p.qh->period,
p.qh->ps.period,
hc32_to_cpup(ehci,
&hw->hw_info2)
/* uframe masks */
@ -618,7 +709,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
speed_char (scratch),
scratch & 0x007f,
(scratch >> 8) & 0x000f, type,
p.qh->usecs, p.qh->c_usecs,
p.qh->ps.usecs,
p.qh->ps.c_usecs,
temp,
0x7ff & (scratch >> 16));
@ -645,7 +737,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
case Q_TYPE_SITD:
temp = scnprintf (next, size,
" sitd%d-%04x/%p",
p.sitd->stream->interval,
p.sitd->stream->ps.period,
hc32_to_cpup(ehci, &p.sitd->hw_uframe)
& 0x0000ffff,
p.sitd);
@ -918,6 +1010,7 @@ static int debug_close(struct inode *inode, struct file *file)
return 0;
}
static int debug_async_open(struct inode *inode, struct file *file)
{
file->private_data = alloc_buffer(inode->i_private, fill_async_buffer);
@ -925,6 +1018,14 @@ static int debug_async_open(struct inode *inode, struct file *file)
return file->private_data ? 0 : -ENOMEM;
}
static int debug_bandwidth_open(struct inode *inode, struct file *file)
{
file->private_data = alloc_buffer(inode->i_private,
fill_bandwidth_buffer);
return file->private_data ? 0 : -ENOMEM;
}
static int debug_periodic_open(struct inode *inode, struct file *file)
{
struct debug_buffer *buf;
@ -957,6 +1058,10 @@ static inline void create_debug_files (struct ehci_hcd *ehci)
&debug_async_fops))
goto file_error;
if (!debugfs_create_file("bandwidth", S_IRUGO, ehci->debug_dir, bus,
&debug_bandwidth_fops))
goto file_error;
if (!debugfs_create_file("periodic", S_IRUGO, ehci->debug_dir, bus,
&debug_periodic_fops))
goto file_error;

View File

@ -1,5 +1,5 @@
/*
* SAMSUNG S5P USB HOST EHCI Controller
* SAMSUNG EXYNOS USB HOST EHCI Controller
*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Author: Jingoo Han <jg1.han@samsung.com>
@ -20,7 +20,6 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/platform_data/usb-ehci-s5p.h>
#include <linux/usb/phy.h>
#include <linux/usb/samsung_usb_phy.h>
#include <linux/usb.h>
@ -29,7 +28,7 @@
#include "ehci.h"
#define DRIVER_DESC "EHCI s5p driver"
#define DRIVER_DESC "EHCI EXYNOS driver"
#define EHCI_INSNREG00(base) (base + 0x90)
#define EHCI_INSNREG00_ENA_INCR16 (0x1 << 25)
@ -40,21 +39,18 @@
(EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \
EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN)
static const char hcd_name[] = "ehci-s5p";
static struct hc_driver __read_mostly s5p_ehci_hc_driver;
static const char hcd_name[] = "ehci-exynos";
static struct hc_driver __read_mostly exynos_ehci_hc_driver;
struct s5p_ehci_hcd {
struct exynos_ehci_hcd {
struct clk *clk;
struct usb_phy *phy;
struct usb_otg *otg;
struct s5p_ehci_platdata *pdata;
};
static struct s5p_ehci_platdata empty_platdata;
#define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
#define to_s5p_ehci(hcd) (struct s5p_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
static void s5p_setup_vbus_gpio(struct platform_device *pdev)
static void exynos_setup_vbus_gpio(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int err;
@ -73,10 +69,9 @@ static void s5p_setup_vbus_gpio(struct platform_device *pdev)
dev_err(dev, "can't request ehci vbus gpio %d", gpio);
}
static int s5p_ehci_probe(struct platform_device *pdev)
static int exynos_ehci_probe(struct platform_device *pdev)
{
struct s5p_ehci_platdata *pdata = dev_get_platdata(&pdev->dev);
struct s5p_ehci_hcd *s5p_ehci;
struct exynos_ehci_hcd *exynos_ehci;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
struct resource *res;
@ -94,48 +89,41 @@ static int s5p_ehci_probe(struct platform_device *pdev)
if (!pdev->dev.coherent_dma_mask)
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
s5p_setup_vbus_gpio(pdev);
exynos_setup_vbus_gpio(pdev);
hcd = usb_create_hcd(&s5p_ehci_hc_driver,
hcd = usb_create_hcd(&exynos_ehci_hc_driver,
&pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
dev_err(&pdev->dev, "Unable to create HCD\n");
return -ENOMEM;
}
s5p_ehci = to_s5p_ehci(hcd);
exynos_ehci = to_exynos_ehci(hcd);
if (of_device_is_compatible(pdev->dev.of_node,
"samsung,exynos5440-ehci")) {
s5p_ehci->pdata = &empty_platdata;
"samsung,exynos5440-ehci"))
goto skip_phy;
}
phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (IS_ERR(phy)) {
/* Fallback to pdata */
if (!pdata) {
usb_put_hcd(hcd);
dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
return -EPROBE_DEFER;
} else {
s5p_ehci->pdata = pdata;
}
usb_put_hcd(hcd);
dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
return -EPROBE_DEFER;
} else {
s5p_ehci->phy = phy;
s5p_ehci->otg = phy->otg;
exynos_ehci->phy = phy;
exynos_ehci->otg = phy->otg;
}
skip_phy:
s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
exynos_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
if (IS_ERR(s5p_ehci->clk)) {
if (IS_ERR(exynos_ehci->clk)) {
dev_err(&pdev->dev, "Failed to get usbhost clock\n");
err = PTR_ERR(s5p_ehci->clk);
err = PTR_ERR(exynos_ehci->clk);
goto fail_clk;
}
err = clk_prepare_enable(s5p_ehci->clk);
err = clk_prepare_enable(exynos_ehci->clk);
if (err)
goto fail_clk;
@ -162,13 +150,11 @@ skip_phy:
goto fail_io;
}
if (s5p_ehci->otg)
s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
if (exynos_ehci->otg)
exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
if (s5p_ehci->phy)
usb_phy_init(s5p_ehci->phy);
else if (s5p_ehci->pdata->phy_init)
s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
if (exynos_ehci->phy)
usb_phy_init(exynos_ehci->phy);
ehci = hcd_to_ehci(hcd);
ehci->caps = hcd->regs;
@ -187,33 +173,29 @@ skip_phy:
return 0;
fail_add_hcd:
if (s5p_ehci->phy)
usb_phy_shutdown(s5p_ehci->phy);
else if (s5p_ehci->pdata->phy_exit)
s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
if (exynos_ehci->phy)
usb_phy_shutdown(exynos_ehci->phy);
fail_io:
clk_disable_unprepare(s5p_ehci->clk);
clk_disable_unprepare(exynos_ehci->clk);
fail_clk:
usb_put_hcd(hcd);
return err;
}
static int s5p_ehci_remove(struct platform_device *pdev)
static int exynos_ehci_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
usb_remove_hcd(hcd);
if (s5p_ehci->otg)
s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
if (exynos_ehci->otg)
exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
if (s5p_ehci->phy)
usb_phy_shutdown(s5p_ehci->phy);
else if (s5p_ehci->pdata->phy_exit)
s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
if (exynos_ehci->phy)
usb_phy_shutdown(exynos_ehci->phy);
clk_disable_unprepare(s5p_ehci->clk);
clk_disable_unprepare(exynos_ehci->clk);
usb_put_hcd(hcd);
@ -221,45 +203,39 @@ static int s5p_ehci_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
static int s5p_ehci_suspend(struct device *dev)
static int exynos_ehci_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
struct platform_device *pdev = to_platform_device(dev);
struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
bool do_wakeup = device_may_wakeup(dev);
int rc;
rc = ehci_suspend(hcd, do_wakeup);
if (s5p_ehci->otg)
s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
if (exynos_ehci->otg)
exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
if (s5p_ehci->phy)
usb_phy_shutdown(s5p_ehci->phy);
else if (s5p_ehci->pdata->phy_exit)
s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
if (exynos_ehci->phy)
usb_phy_shutdown(exynos_ehci->phy);
clk_disable_unprepare(s5p_ehci->clk);
clk_disable_unprepare(exynos_ehci->clk);
return rc;
}
static int s5p_ehci_resume(struct device *dev)
static int exynos_ehci_resume(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
struct platform_device *pdev = to_platform_device(dev);
struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
clk_prepare_enable(s5p_ehci->clk);
clk_prepare_enable(exynos_ehci->clk);
if (s5p_ehci->otg)
s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
if (exynos_ehci->otg)
exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
if (s5p_ehci->phy)
usb_phy_init(s5p_ehci->phy);
else if (s5p_ehci->pdata->phy_init)
s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
if (exynos_ehci->phy)
usb_phy_init(exynos_ehci->phy);
/* DMA burst Enable */
writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
@ -268,13 +244,13 @@ static int s5p_ehci_resume(struct device *dev)
return 0;
}
#else
#define s5p_ehci_suspend NULL
#define s5p_ehci_resume NULL
#define exynos_ehci_suspend NULL
#define exynos_ehci_resume NULL
#endif
static const struct dev_pm_ops s5p_ehci_pm_ops = {
.suspend = s5p_ehci_suspend,
.resume = s5p_ehci_resume,
static const struct dev_pm_ops exynos_ehci_pm_ops = {
.suspend = exynos_ehci_suspend,
.resume = exynos_ehci_resume,
};
#ifdef CONFIG_OF
@ -286,40 +262,40 @@ static const struct of_device_id exynos_ehci_match[] = {
MODULE_DEVICE_TABLE(of, exynos_ehci_match);
#endif
static struct platform_driver s5p_ehci_driver = {
.probe = s5p_ehci_probe,
.remove = s5p_ehci_remove,
static struct platform_driver exynos_ehci_driver = {
.probe = exynos_ehci_probe,
.remove = exynos_ehci_remove,
.shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "s5p-ehci",
.name = "exynos-ehci",
.owner = THIS_MODULE,
.pm = &s5p_ehci_pm_ops,
.pm = &exynos_ehci_pm_ops,
.of_match_table = of_match_ptr(exynos_ehci_match),
}
};
static const struct ehci_driver_overrides s5p_overrides __initdata = {
.extra_priv_size = sizeof(struct s5p_ehci_hcd),
static const struct ehci_driver_overrides exynos_overrides __initdata = {
.extra_priv_size = sizeof(struct exynos_ehci_hcd),
};
static int __init ehci_s5p_init(void)
static int __init ehci_exynos_init(void)
{
if (usb_disabled())
return -ENODEV;
pr_info("%s: " DRIVER_DESC "\n", hcd_name);
ehci_init_driver(&s5p_ehci_hc_driver, &s5p_overrides);
return platform_driver_register(&s5p_ehci_driver);
ehci_init_driver(&exynos_ehci_hc_driver, &exynos_overrides);
return platform_driver_register(&exynos_ehci_driver);
}
module_init(ehci_s5p_init);
module_init(ehci_exynos_init);
static void __exit ehci_s5p_cleanup(void)
static void __exit ehci_exynos_cleanup(void)
{
platform_driver_unregister(&s5p_ehci_driver);
platform_driver_unregister(&exynos_ehci_driver);
}
module_exit(ehci_s5p_cleanup);
module_exit(ehci_exynos_cleanup);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_ALIAS("platform:s5p-ehci");
MODULE_ALIAS("platform:exynos-ehci");
MODULE_AUTHOR("Jingoo Han");
MODULE_AUTHOR("Joonyoung Shim");
MODULE_LICENSE("GPL v2");

View File

@ -57,7 +57,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
pr_debug("initializing FSL-SOC USB Controller\n");
/* Need platform data for setup */
pdata = (struct fsl_usb2_platform_data *)dev_get_platdata(&pdev->dev);
pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
dev_err(&pdev->dev,
"No platform data for %s.\n", dev_name(&pdev->dev));
@ -664,7 +664,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_USB2 | HCD_MEMORY,
.flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
/*
* basic lifecycle operations

View File

@ -43,7 +43,7 @@ static const struct hc_driver ehci_grlib_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2,
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations

View File

@ -110,6 +110,9 @@ MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
#include "ehci.h"
#include "pci-quirks.h"
static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE],
struct ehci_tt *tt);
/*
* The MosChip MCS9990 controller updates its microframe counter
* a little before the frame counter, and occasionally we will read
@ -484,6 +487,7 @@ static int ehci_init(struct usb_hcd *hcd)
INIT_LIST_HEAD(&ehci->intr_qh_list);
INIT_LIST_HEAD(&ehci->cached_itd_list);
INIT_LIST_HEAD(&ehci->cached_sitd_list);
INIT_LIST_HEAD(&ehci->tt_list);
if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
/* periodic schedule size can be smaller than default */
@ -956,6 +960,7 @@ rescan:
goto idle_timeout;
/* BUG_ON(!list_empty(&stream->free_list)); */
reserve_release_iso_bandwidth(ehci, stream, -1);
kfree(stream);
goto done;
}
@ -982,6 +987,8 @@ idle_timeout:
if (qh->clearing_tt)
goto idle_timeout;
if (list_empty (&qh->qtd_list)) {
if (qh->ps.bw_uperiod)
reserve_release_intr_bandwidth(ehci, qh, -1);
qh_destroy(ehci, qh);
break;
}
@ -1022,7 +1029,6 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
* the toggle bit in the QH.
*/
if (qh) {
usb_settoggle(qh->dev, epnum, is_out, 0);
if (!list_empty(&qh->qtd_list)) {
WARN_ONCE(1, "clear_halt for a busy endpoint\n");
} else {
@ -1030,6 +1036,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
* while the QH is active. Unlink it now;
* re-linking will call qh_refresh().
*/
usb_settoggle(qh->ps.udev, epnum, is_out, 0);
qh->exception = 1;
if (eptype == USB_ENDPOINT_XFER_BULK)
start_unlink_async(ehci, qh);
@ -1048,6 +1055,19 @@ static int ehci_get_frame (struct usb_hcd *hcd)
/*-------------------------------------------------------------------------*/
/* Device addition and removal */
static void ehci_remove_device(struct usb_hcd *hcd, struct usb_device *udev)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
spin_lock_irq(&ehci->lock);
drop_tt(udev);
spin_unlock_irq(&ehci->lock);
}
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
/* suspend/resume, section 4.3 */
@ -1075,6 +1095,14 @@ int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
spin_unlock_irq(&ehci->lock);
synchronize_irq(hcd->irq);
/* Check for race with a wakeup request */
if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
ehci_resume(hcd, false);
return -EBUSY;
}
return 0;
}
EXPORT_SYMBOL_GPL(ehci_suspend);
@ -1158,7 +1186,7 @@ static const struct hc_driver ehci_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2,
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations
@ -1191,6 +1219,11 @@ static const struct hc_driver ehci_hc_driver = {
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
/*
* device support
*/
.free_dev = ehci_remove_device,
};
void ehci_init_driver(struct hc_driver *drv,
@ -1238,11 +1271,6 @@ MODULE_LICENSE ("GPL");
#define XILINX_OF_PLATFORM_DRIVER ehci_hcd_xilinx_of_driver
#endif
#ifdef CONFIG_USB_W90X900_EHCI
#include "ehci-w90x900.c"
#define PLATFORM_DRIVER ehci_hcd_w90x900_driver
#endif
#ifdef CONFIG_USB_OCTEON_EHCI
#include "ehci-octeon.c"
#define PLATFORM_DRIVER ehci_octeon_driver

View File

@ -224,11 +224,11 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
hw->hw_next = EHCI_LIST_END(ehci);
hw->hw_qtd_next = EHCI_LIST_END(ehci);
hw->hw_alt_next = EHCI_LIST_END(ehci);
hw->hw_token &= ~QTD_STS_ACTIVE;
ehci->dummy->hw = hw;
for (i = 0; i < ehci->periodic_size; i++)
ehci->periodic[i] = ehci->dummy->qh_dma;
ehci->periodic[i] = cpu_to_hc32(ehci,
ehci->dummy->qh_dma);
} else {
for (i = 0; i < ehci->periodic_size; i++)
ehci->periodic[i] = EHCI_LIST_END(ehci);

View File

@ -42,7 +42,6 @@
static const char hcd_name[] = "ehci-msm";
static struct hc_driver __read_mostly msm_hc_driver;
static struct usb_phy *phy;
static int ehci_msm_reset(struct usb_hcd *hcd)
{
@ -70,6 +69,7 @@ static int ehci_msm_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd;
struct resource *res;
struct usb_phy *phy;
int ret;
dev_dbg(&pdev->dev, "ehci_msm proble\n");
@ -108,10 +108,14 @@ static int ehci_msm_probe(struct platform_device *pdev)
* powering up VBUS, mapping of registers address space and power
* management.
*/
phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (pdev->dev.of_node)
phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
else
phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (IS_ERR(phy)) {
dev_err(&pdev->dev, "unable to find transceiver\n");
ret = -ENODEV;
ret = -EPROBE_DEFER;
goto put_hcd;
}
@ -121,6 +125,7 @@ static int ehci_msm_probe(struct platform_device *pdev)
goto put_hcd;
}
hcd->phy = phy;
device_init_wakeup(&pdev->dev, 1);
/*
* OTG device parent of HCD takes care of putting
@ -147,7 +152,7 @@ static int ehci_msm_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
otg_set_host(phy->otg, NULL);
otg_set_host(hcd->phy->otg, NULL);
/* FIXME: need to call usb_remove_hcd() here? */
@ -186,12 +191,19 @@ static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
.resume = ehci_msm_pm_resume,
};
static struct of_device_id msm_ehci_dt_match[] = {
{ .compatible = "qcom,ehci-host", },
{}
};
MODULE_DEVICE_TABLE(of, msm_ehci_dt_match);
static struct platform_driver ehci_msm_driver = {
.probe = ehci_msm_probe,
.remove = ehci_msm_remove,
.driver = {
.name = "msm_hsusb_host",
.pm = &ehci_msm_dev_pm_ops,
.of_match_table = msm_ehci_dt_match,
},
};

View File

@ -96,7 +96,7 @@ static const struct hc_driver mv_ehci_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2,
.flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
/*
* basic lifecycle operations

Some files were not shown because too many files have changed in this diff Show More