mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-22 13:54:57 +08:00
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:
commit
c287322c3a
31
Documentation/ABI/testing/configfs-usb-gadget-mass-storage
Normal file
31
Documentation/ABI/testing/configfs-usb-gadget-mass-storage
Normal 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)
|
66
Documentation/devicetree/bindings/phy/phy-bindings.txt
Normal file
66
Documentation/devicetree/bindings/phy/phy-bindings.txt
Normal 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.
|
22
Documentation/devicetree/bindings/phy/samsung-phy.txt
Normal file
22
Documentation/devicetree/bindings/phy/samsung-phy.txt
Normal 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;
|
17
Documentation/devicetree/bindings/usb/msm-hsusb.txt
Normal file
17
Documentation/devicetree/bindings/usb/msm-hsusb.txt
Normal 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>;
|
||||
};
|
||||
|
@ -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";
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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>;
|
||||
};
|
||||
|
@ -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
166
Documentation/phy.txt
Normal 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
|
@ -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
|
||||
--------------
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>;
|
||||
};
|
||||
|
@ -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>;
|
||||
};
|
||||
|
@ -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>;
|
||||
};
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -86,6 +86,7 @@
|
||||
usb1v8-supply = <&vusb1v8>;
|
||||
usb3v1-supply = <&vusb3v1>;
|
||||
usb_mode = <1>;
|
||||
#phy-cells = <0>;
|
||||
};
|
||||
|
||||
twl_pwm: pwm {
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
*************************************************************************/
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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[] = {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
@ -166,4 +166,6 @@ source "drivers/reset/Kconfig"
|
||||
|
||||
source "drivers/fmc/Kconfig"
|
||||
|
||||
source "drivers/phy/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
@ -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/
|
||||
|
@ -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.
|
||||
|
@ -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
54
drivers/phy/Kconfig
Normal 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
9
drivers/phy/Makefile
Normal 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
698
drivers/phy/phy-core.c
Normal 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");
|
111
drivers/phy/phy-exynos-dp-video.c
Normal file
111
drivers/phy/phy-exynos-dp-video.c
Normal 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");
|
176
drivers/phy/phy-exynos-mipi-video.c
Normal file
176
drivers/phy/phy-exynos-mipi-video.c
Normal 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");
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
@ -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() */
|
||||
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
6
drivers/usb/gadget/configfs.h
Normal file
6
drivers/usb/gadget/configfs.h
Normal 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 */
|
@ -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
166
drivers/usb/gadget/f_mass_storage.h
Normal file
166
drivers/usb/gadget/f_mass_storage.h
Normal 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 */
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
229
drivers/usb/gadget/storage_common.h
Normal file
229
drivers/usb/gadget/storage_common.h
Normal 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 */
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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");
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user