Commit 3e75c6de authored by Linus Torvalds's avatar Linus Torvalds

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

Pull USB patches from Greg KH:
 "Here's the big USB pull request for 3.15-rc1.

  The normal set of patches, lots of controller driver updates, and a
  smattering of individual USB driver updates as well.

  All have been in linux-next for a while"

* tag 'usb-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (249 commits)
  xhci: Transition maintainership to Mathias Nyman.
  USB: disable reset-resume when USB_QUIRK_RESET is set
  USB: unbind all interfaces before rebinding any
  usb: phy: Add ulpi IDs for SMSC USB3320 and TI TUSB1210
  usb: gadget: tcm_usb_gadget: stop format strings
  usb: gadget: f_fs: add missing spinlock and mutex unlock
  usb: gadget: composite: switch over to ERR_CAST()
  usb: gadget: inode: switch over to memdup_user()
  usb: gadget: f_subset: switch over to PTR_RET
  usb: gadget: lpc32xx_udc: fix wrong clk_put() sequence
  USB: keyspan: remove dead debugging code
  USB: serial: add missing newlines to dev_<level> messages.
  USB: serial: add missing braces
  USB: serial: continue to write on errors
  USB: serial: continue to read on errors
  USB: serial: make bulk_out_size a lower limit
  USB: cypress_m8: fix potential scheduling while atomic
  devicetree: bindings: document lsi,zevio-usb
  usb: chipidea: add support for USB OTG controller on LSI Zevio SoCs
  usb: chipidea: imx: Use dev_name() for ci_hdrc name to distinguish USBs
  ...
parents cb159556 940ab8f1
* APM X-Gene 15Gbps Multi-purpose PHY nodes
PHY nodes are defined to describe on-chip 15Gbps Multi-purpose PHY. Each
PHY (pair of lanes) has its own node.
Required properties:
- compatible : Shall be "apm,xgene-phy".
- reg : PHY memory resource is the SDS PHY access resource.
- #phy-cells : Shall be 1 as it expects one argument for setting
the mode of the PHY. Possible values are 0 (SATA),
1 (SGMII), 2 (PCIe), 3 (USB), and 4 (XFI).
Optional properties:
- status : Shall be "ok" if enabled or "disabled" if disabled.
Default is "ok".
- clocks : Reference to the clock entry.
- apm,tx-eye-tuning : Manual control to fine tune the capture of the serial
bit lines from the automatic calibrated position.
Two set of 3-tuple setting for each (up to 3)
supported link speed on the host. Range from 0 to
127 in unit of one bit period. Default is 10.
- apm,tx-eye-direction : Eye tuning manual control direction. 0 means sample
data earlier than the nominal sampling point. 1 means
sample data later than the nominal sampling point.
Two set of 3-tuple setting for each (up to 3)
supported link speed on the host. Default is 0.
- apm,tx-boost-gain : Frequency boost AC (LSB 3-bit) and DC (2-bit)
gain control. Two set of 3-tuple setting for each
(up to 3) supported link speed on the host. Range is
between 0 to 31 in unit of dB. Default is 3.
- apm,tx-amplitude : Amplitude control. Two set of 3-tuple setting for
each (up to 3) supported link speed on the host.
Range is between 0 to 199500 in unit of uV.
Default is 199500 uV.
- apm,tx-pre-cursor1 : 1st pre-cursor emphasis taps control. Two set of
3-tuple setting for each (up to 3) supported link
speed on the host. Range is 0 to 273000 in unit of
uV. Default is 0.
- apm,tx-pre-cursor2 : 2st pre-cursor emphasis taps control. Two set of
3-tuple setting for each (up to 3) supported link
speed on the host. Range is 0 to 127400 in unit uV.
Default is 0x0.
- apm,tx-post-cursor : Post-cursor emphasis taps control. Two set of
3-tuple setting for Gen1, Gen2, and Gen3. Range is
between 0 to 0x1f in unit of 18.2mV. Default is 0xf.
- apm,tx-speed : Tx operating speed. One set of 3-tuple for each
supported link speed on the host.
0 = 1-2Gbps
1 = 2-4Gbps (1st tuple default)
2 = 4-8Gbps
3 = 8-15Gbps (2nd tuple default)
4 = 2.5-4Gbps
5 = 4-5Gbps
6 = 5-6Gbps
7 = 6-16Gbps (3rd tuple default)
NOTE: PHY override parameters are board specific setting.
Example:
phy1: phy@1f21a000 {
compatible = "apm,xgene-phy";
reg = <0x0 0x1f21a000 0x0 0x100>;
#phy-cells = <1>;
status = "disabled";
};
phy2: phy@1f22a000 {
compatible = "apm,xgene-phy";
reg = <0x0 0x1f22a000 0x0 0x100>;
#phy-cells = <1>;
status = "ok";
};
phy3: phy@1f23a000 {
compatible = "apm,xgene-phy";
reg = <0x0 0x1f23a000 0x0 0x100>;
#phy-cells = <1>;
status = "ok";
};
...@@ -20,3 +20,57 @@ Required properties: ...@@ -20,3 +20,57 @@ Required properties:
- compatible : should be "samsung,exynos5250-dp-video-phy"; - compatible : should be "samsung,exynos5250-dp-video-phy";
- reg : offset and length of the Display Port PHY register set; - reg : offset and length of the Display Port PHY register set;
- #phy-cells : from the generic PHY bindings, must be 0; - #phy-cells : from the generic PHY bindings, must be 0;
Samsung S5P/EXYNOS SoC series USB PHY
-------------------------------------------------
Required properties:
- compatible : should be one of the listed compatibles:
- "samsung,exynos4210-usb2-phy"
- "samsung,exynos4x12-usb2-phy"
- "samsung,exynos5250-usb2-phy"
- reg : a list of registers used by phy driver
- first and obligatory is the location of phy modules registers
- samsung,sysreg-phandle - handle to syscon used to control the system registers
- samsung,pmureg-phandle - handle to syscon used to control PMU registers
- #phy-cells : from the generic phy bindings, must be 1;
- clocks and clock-names:
- the "phy" clock is required by the phy module, used as a gate
- the "ref" clock is used to get the rate of the clock provided to the
PHY module
The first phandle argument in the PHY specifier identifies the PHY, its
meaning is compatible dependent. For the currently supported SoCs (Exynos 4210
and Exynos 4212) it is as follows:
0 - USB device ("device"),
1 - USB host ("host"),
2 - HSIC0 ("hsic0"),
3 - HSIC1 ("hsic1"),
Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
register is supplied.
Example:
For Exynos 4412 (compatible with Exynos 4212):
usbphy: phy@125b0000 {
compatible = "samsung,exynos4x12-usb2-phy";
reg = <0x125b0000 0x100>;
clocks = <&clock 305>, <&clock 2>;
clock-names = "phy", "ref";
status = "okay";
#phy-cells = <1>;
samsung,sysreg-phandle = <&sys_reg>;
samsung,pmureg-phandle = <&pmu_reg>;
};
Then the PHY can be used in other nodes such as:
phy-consumer@12340000 {
phys = <&usbphy 2>;
phy-names = "phy";
};
Refer to DT bindings documentation of particular PHY consumer devices for more
information about required PHYs and the way of specification.
Allwinner sun4i USB PHY
-----------------------
Required properties:
- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
"allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
- reg : a list of offset + length pairs
- reg-names : "phy_ctrl", "pmu1" and for sun4i or sun7i "pmu2"
- #phy-cells : from the generic phy bindings, must be 1
- clocks : phandle + clock specifier for the phy clock
- clock-names : "usb_phy"
- resets : a list of phandle + reset specifier pairs
- reset-names : "usb0_reset", "usb1_reset" and for sun4i or sun7i "usb2_reset"
Example:
usbphy: phy@0x01c13400 {
#phy-cells = <1>;
compatible = "allwinner,sun4i-a10-usb-phy";
/* phy base regs, phy1 pmu reg, phy2 pmu reg */
reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
reg-names = "phy_ctrl", "pmu1", "pmu2";
clocks = <&usb_clk 8>;
clock-names = "usb_phy";
resets = <&usb_clk 1>, <&usb_clk 2>;
reset-names = "usb1_reset", "usb2_reset";
};
USB PHY TI PHY: DT DOCUMENTATION FOR PHYs in TI PLATFORMs
OMAP CONTROL PHY
Required properties:
- 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-usb2-dra7" - if it has power down register like USB2 PHY on
DRA7 platform.
"ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on
AM437 platform.
- reg : Address and length of the register set for the device. It contains
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,control-phy-otghs";
reg = <0x4a00233c 0x4>;
reg-names = "otghs_control";
};
OMAP USB2 PHY OMAP USB2 PHY
...@@ -21,15 +46,22 @@ usb2phy@4a0ad080 { ...@@ -21,15 +46,22 @@ usb2phy@4a0ad080 {
#phy-cells = <0>; #phy-cells = <0>;
}; };
OMAP USB3 PHY TI PIPE3 PHY
Required properties: Required properties:
- compatible: Should be "ti,omap-usb3" - compatible: Should be "ti,phy-usb3" or "ti,phy-pipe3-sata".
"ti,omap-usb3" is deprecated.
- reg : Address and length of the register set for the device. - reg : Address and length of the register set for the device.
- reg-names: The names of the register addresses corresponding to the registers - reg-names: The names of the register addresses corresponding to the registers
filled in "reg". filled in "reg".
- #phy-cells: determine the number of cells that should be given in the - #phy-cells: determine the number of cells that should be given in the
phandle while referencing this phy. phandle while referencing this phy.
- clocks: a list of phandles and clock-specifier pairs, one for each entry in
clock-names.
- clock-names: should include:
* "wkupclk" - wakeup clock.
* "sysclk" - system clock.
* "refclk" - reference clock.
Optional properties: Optional properties:
- ctrl-module : phandle of the control module used by PHY driver to power on - ctrl-module : phandle of the control module used by PHY driver to power on
...@@ -38,11 +70,17 @@ Optional properties: ...@@ -38,11 +70,17 @@ Optional properties:
This is usually a subnode of ocp2scp to which it is connected. This is usually a subnode of ocp2scp to which it is connected.
usb3phy@4a084400 { usb3phy@4a084400 {
compatible = "ti,omap-usb3"; compatible = "ti,phy-usb3";
reg = <0x4a084400 0x80>, reg = <0x4a084400 0x80>,
<0x4a084800 0x64>, <0x4a084800 0x64>,
<0x4a084c00 0x40>; <0x4a084c00 0x40>;
reg-names = "phy_rx", "phy_tx", "pll_ctrl"; reg-names = "phy_rx", "phy_tx", "pll_ctrl";
ctrl-module = <&omap_control_usb>; ctrl-module = <&omap_control_usb>;
#phy-cells = <0>; #phy-cells = <0>;
clocks = <&usb_phy_cm_clk32k>,
<&sys_clkin>,
<&usb_otg_ss_refclk960m>;
clock-names = "wkupclk",
"sysclk",
"refclk";
}; };
...@@ -18,6 +18,7 @@ Optional properties: ...@@ -18,6 +18,7 @@ Optional properties:
- vbus-supply: regulator for vbus - vbus-supply: regulator for vbus
- disable-over-current: disable over current detect - disable-over-current: disable over current detect
- external-vbus-divider: enables off-chip resistor divider for Vbus - external-vbus-divider: enables off-chip resistor divider for Vbus
- maximum-speed: limit the maximum connection speed to "full-speed".
Examples: Examples:
usb@02184000 { /* USB OTG */ usb@02184000 { /* USB OTG */
...@@ -28,4 +29,5 @@ usb@02184000 { /* USB OTG */ ...@@ -28,4 +29,5 @@ usb@02184000 { /* USB OTG */
fsl,usbmisc = <&usbmisc 0>; fsl,usbmisc = <&usbmisc 0>;
disable-over-current; disable-over-current;
external-vbus-divider; external-vbus-divider;
maximum-speed = "full-speed";
}; };
* LSI Zevio USB OTG Controller
Required properties:
- compatible: Should be "lsi,zevio-usb"
- reg: Should contain registers location and length
- interrupts: Should contain controller interrupt
Optional properties:
- vbus-supply: regulator for vbus
Examples:
usb0: usb@b0000000 {
reg = <0xb0000000 0x1000>;
compatible = "lsi,zevio-usb";
interrupts = <8>;
vbus-supply = <&vbus_reg>;
};
...@@ -6,11 +6,13 @@ Required properties: ...@@ -6,11 +6,13 @@ Required properties:
- compatible: must be "snps,dwc3" - compatible: must be "snps,dwc3"
- reg : Address and length of the register set for the device - reg : Address and length of the register set for the device
- interrupts: Interrupts used by the dwc3 controller. - interrupts: Interrupts used by the dwc3 controller.
Optional properties:
- usb-phy : array of phandle for the PHY device. The first element - usb-phy : array of phandle for the PHY device. The first element
in the array is expected to be a handle to the USB2/HS PHY and in the array is expected to be a handle to the USB2/HS PHY and
the second element is expected to be a handle to the USB3/SS PHY the second element is expected to be a handle to the USB3/SS PHY
- phys: from the *Generic PHY* bindings
Optional properties: - phy-names: from the *Generic PHY* bindings
- tx-fifo-resize: determines if the FIFO *has* to be reallocated. - tx-fifo-resize: determines if the FIFO *has* to be reallocated.
This is usually a subnode to DWC3 glue to which it is connected. This is usually a subnode to DWC3 glue to which it is connected.
......
* Freescale MXS USB Phy Device * Freescale MXS USB Phy Device
Required properties: Required properties:
- compatible: Should be "fsl,imx23-usbphy" - compatible: should contain:
* "fsl,imx23-usbphy" for imx23 and imx28
* "fsl,imx6q-usbphy" for imx6dq and imx6dl
* "fsl,imx6sl-usbphy" for imx6sl
"fsl,imx23-usbphy" is still a fallback for other strings
- reg: Should contain registers location and length - reg: Should contain registers location and length
- interrupts: Should contain phy interrupt - interrupts: Should contain phy interrupt
- fsl,anatop: phandle for anatop register, it is only for imx6 SoC series
Example: Example:
usbphy1: usbphy@020c9000 { usbphy1: usbphy@020c9000 {
compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy"; compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy";
reg = <0x020c9000 0x1000>; reg = <0x020c9000 0x1000>;
interrupts = <0 44 0x04>; interrupts = <0 44 0x04>;
fsl,anatop = <&anatop>;
}; };
...@@ -76,27 +76,3 @@ omap_dwc3 { ...@@ -76,27 +76,3 @@ omap_dwc3 {
ranges; ranges;
}; };
OMAP CONTROL USB
Required properties:
- 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.
"ti,control-phy-am437usb2" - if it has power down register like USB2 PHY on
AM437 platform.
- reg : Address and length of the register set for the device. It contains
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,control-phy-otghs";
reg = <0x4a00233c 0x4>;
reg-names = "otghs_control";
};
USB EHCI controllers USB EHCI controllers
Required properties: Required properties:
- compatible : should be "usb-ehci". - compatible : should be "generic-ehci".
- reg : should contain at least address and length of the standard EHCI - reg : should contain at least address and length of the standard EHCI
register set for the device. Optional platform-dependent registers register set for the device. Optional platform-dependent registers
(debug-port or other) can be also specified here, but only after (debug-port or other) can be also specified here, but only after
definition of standard EHCI registers. definition of standard EHCI registers.
- interrupts : one EHCI interrupt should be described here. - interrupts : one EHCI interrupt should be described here.
If device registers are implemented in big endian mode, the device
node should have "big-endian-regs" property. Optional properties:
If controller implementation operates with big endian descriptors, - big-endian-regs : boolean, set this for hcds with big-endian registers
"big-endian-desc" property should be specified. - big-endian-desc : boolean, set this for hcds with big-endian descriptors
If both big endian registers and descriptors are used by the controller - big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
implementation, "big-endian" property can be specified instead of having - clocks : a list of phandle + clock specifier pairs
both "big-endian-regs" and "big-endian-desc". - phys : phandle + phy specifier pair
- phy-names : "usb"
Example (Sequoia 440EPx): Example (Sequoia 440EPx):
ehci@e0000300 { ehci@e0000300 {
...@@ -23,3 +24,13 @@ Example (Sequoia 440EPx): ...@@ -23,3 +24,13 @@ Example (Sequoia 440EPx):
reg = <0 e0000300 90 0 e0000390 70>; reg = <0 e0000300 90 0 e0000390 70>;
big-endian; big-endian;
}; };
Example (Allwinner sun4i A10 SoC):
ehci0: usb@01c14000 {
compatible = "allwinner,sun4i-a10-ehci", "generic-ehci";
reg = <0x01c14000 0x100>;
interrupts = <39>;
clocks = <&ahb_gates 1>;
phys = <&usbphy 1>;
phy-names = "usb";
};
USB OHCI controllers
Required properties:
- compatible : "generic-ohci"
- reg : ohci controller register range (address and length)
- interrupts : ohci controller interrupt
Optional properties:
- big-endian-regs : boolean, set this for hcds with big-endian registers
- big-endian-desc : boolean, set this for hcds with big-endian descriptors
- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
- clocks : a list of phandle + clock specifier pairs
- phys : phandle + phy specifier pair
- phy-names : "usb"
Example:
ohci0: usb@01c14400 {
compatible = "allwinner,sun4i-a10-ohci", "generic-ohci";
reg = <0x01c14400 0x100>;
interrupts = <64>;
clocks = <&usb_clk 6>, <&ahb_gates 2>;
phys = <&usbphy 1>;
phy-names = "usb";
};
...@@ -2,14 +2,14 @@ Generic Platform UHCI Controller ...@@ -2,14 +2,14 @@ Generic Platform UHCI Controller
----------------------------------------------------- -----------------------------------------------------
Required properties: Required properties:
- compatible : "platform-uhci" - compatible : "generic-uhci" (deprecated: "platform-uhci")
- reg : Should contain 1 register ranges(address and length) - reg : Should contain 1 register ranges(address and length)
- interrupts : UHCI controller interrupt - interrupts : UHCI controller interrupt
Example: Example:
uhci@d8007b00 { uhci@d8007b00 {
compatible = "platform-uhci"; compatible = "generic-uhci";
reg = <0xd8007b00 0x200>; reg = <0xd8007b00 0x200>;
interrupts = <43>; interrupts = <43>;
}; };
USB xHCI controllers USB xHCI controllers
Required properties: Required properties:
- compatible: should be "xhci-platform". - compatible: should be "generic-xhci" (deprecated: "xhci-platform").
- reg: should contain address and length of the standard XHCI - reg: should contain address and length of the standard XHCI
register set for the device. register set for the device.
- interrupts: one XHCI interrupt should be described here. - interrupts: one XHCI interrupt should be described here.
Example: Example:
usb@f0931000 { usb@f0931000 {
compatible = "xhci-platform"; compatible = "generic-xhci";
reg = <0xf0931000 0x8c8>; reg = <0xf0931000 0x8c8>;
interrupts = <0x0 0x4e 0x0>; interrupts = <0x0 0x4e 0x0>;
}; };
VIA/Wondermedia VT8500 EHCI Controller
-----------------------------------------------------
Required properties:
- compatible : "via,vt8500-ehci"
- reg : Should contain 1 register ranges(address and length)
- interrupts : ehci controller interrupt
Example:
ehci@d8007900 {
compatible = "via,vt8500-ehci";
reg = <0xd8007900 0x200>;
interrupts = <43>;
};
VIA VT8500 and Wondermedia WM8xxx SoC USB controllers.
Required properties:
- compatible: Should be "via,vt8500-ehci" or "wm,prizm-ehci".
- reg: Address range of the ehci registers. size should be 0x200
- interrupts: Should contain the ehci interrupt.
usb: ehci@D8007100 {
compatible = "wm,prizm-ehci", "usb-ehci";
reg = <0xD8007100 0x200>;
interrupts = <1>;
};
.------------------------------------------------------------------------------+
| Samsung USB 2.0 PHY adaptation layer |
+-----------------------------------------------------------------------------+'
| 1. Description
+----------------
The architecture of the USB 2.0 PHY module in Samsung SoCs is similar
among many SoCs. In spite of the similarities it proved difficult to
create a one driver that would fit all these PHY controllers. Often
the differences were minor and were found in particular bits of the
registers of the PHY. In some rare cases the order of register writes or
the PHY powering up process had to be altered. This adaptation layer is
a compromise between having separate drivers and having a single driver
with added support for many special cases.
| 2. Files description
+----------------------
- phy-samsung-usb2.c
This is the main file of the adaptation layer. This file contains
the probe function and provides two callbacks to the Generic PHY
Framework. This two callbacks are used to power on and power off the
phy. They carry out the common work that has to be done on all version
of the PHY module. Depending on which SoC was chosen they execute SoC
specific callbacks. The specific SoC version is selected by choosing
the appropriate compatible string. In addition, this file contains
struct of_device_id definitions for particular SoCs.
- phy-samsung-usb2.h
This is the include file. It declares the structures used by this
driver. In addition it should contain extern declarations for
structures that describe particular SoCs.
| 3. Supporting SoCs
+--------------------
To support a new SoC a new file should be added to the drivers/phy
directory. Each SoC's configuration is stored in an instance of the
struct samsung_usb2_phy_config.
struct samsung_usb2_phy_config {
const struct samsung_usb2_common_phy *phys;
int (*rate_to_clk)(unsigned long, u32 *);
unsigned int num_phys;
bool has_mode_switch;
};
The num_phys is the number of phys handled by the driver. *phys is an
array that contains the configuration for each phy. The has_mode_switch
property is a boolean flag that determines whether the SoC has USB host
and device on a single pair of pins. If so, a special register has to
be modified to change the internal routing of these pins between a USB
device or host module.
For example the configuration for Exynos 4210 is following:
const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
.has_mode_switch = 0,
.num_phys = EXYNOS4210_NUM_PHYS,
.phys = exynos4210_phys,
.rate_to_clk = exynos4210_rate_to_clk,
}
- int (*rate_to_clk)(unsigned long, u32 *)
The rate_to_clk callback is to convert the rate of the clock
used as the reference clock for the PHY module to the value
that should be written in the hardware register.
The exynos4210_phys configuration array is as follows:
static const struct samsung_usb2_common_phy exynos4210_phys[] = {
{
.label = "device",
.id = EXYNOS4210_DEVICE,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "host",
.id = EXYNOS4210_HOST,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "hsic0",
.id = EXYNOS4210_HSIC0,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "hsic1",
.id = EXYNOS4210_HSIC1,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{},
};
- int (*power_on)(struct samsung_usb2_phy_instance *);
- int (*power_off)(struct samsung_usb2_phy_instance *);
These two callbacks are used to power on and power off the phy
by modifying appropriate registers.
Final change to the driver is adding appropriate compatible value to the
phy-samsung-usb2.c file. In case of Exynos 4210 the following lines were
added to the struct of_device_id samsung_usb2_phy_of_match[] array:
#ifdef CONFIG_PHY_EXYNOS4210_USB2
{
.compatible = "samsung,exynos4210-usb2-phy",
.data = &exynos4210_usb2_phy_config,
},
#endif
To add further flexibility to the driver the Kconfig file enables to
include support for selected SoCs in the compiled driver. The Kconfig
entry for Exynos 4210 is following:
config PHY_EXYNOS4210_USB2
bool "Support for Exynos 4210"
depends on PHY_SAMSUNG_USB2
depends on CPU_EXYNOS4210
help
Enable USB PHY support for Exynos 4210. This option requires that
Samsung USB 2.0 PHY driver is enabled and means that support for this
particular SoC is compiled in the driver. In case of Exynos 4210 four
phys are available - device, host, HSCI0 and HSCI1.
The newly created file that supports the new SoC has to be also added to the
Makefile. In case of Exynos 4210 the added line is following:
obj-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o
After completing these steps the support for the new SoC should be ready.
...@@ -9130,8 +9130,7 @@ S: Maintained ...@@ -9130,8 +9130,7 @@ S: Maintained
F: drivers/net/wireless/ath/ar5523/ F: drivers/net/wireless/ath/ar5523/
USB ATTACHED SCSI USB ATTACHED SCSI
M: Matthew Wilcox <willy@linux.intel.com> M: Hans de Goede <hdegoede@redhat.com>
M: Sarah Sharp <sarah.a.sharp@linux.intel.com>
M: Gerd Hoffmann <kraxel@redhat.com> M: Gerd Hoffmann <kraxel@redhat.com>
L: linux-usb@vger.kernel.org L: linux-usb@vger.kernel.org
L: linux-scsi@vger.kernel.org L: linux-scsi@vger.kernel.org
...@@ -9357,7 +9356,7 @@ S: Maintained ...@@ -9357,7 +9356,7 @@ S: Maintained
F: drivers/net/wireless/rndis_wlan.c F: drivers/net/wireless/rndis_wlan.c
USB XHCI DRIVER USB XHCI DRIVER
M: Sarah Sharp <sarah.a.sharp@linux.intel.com> M: Mathias Nyman <mathias.nyman@intel.com>
L: linux-usb@vger.kernel.org L: linux-usb@vger.kernel.org
S: Supported S: Supported
F: drivers/usb/host/xhci* F: drivers/usb/host/xhci*
......
...@@ -534,7 +534,6 @@ config ARCH_DOVE ...@@ -534,7 +534,6 @@ config ARCH_DOVE
select PINCTRL select PINCTRL
select PINCTRL_DOVE select PINCTRL_DOVE
select PLAT_ORION_LEGACY select PLAT_ORION_LEGACY
select USB_ARCH_HAS_EHCI
help help
Support for the Marvell Dove SoC 88AP510 Support for the Marvell Dove SoC 88AP510
...@@ -633,7 +632,6 @@ config ARCH_LPC32XX ...@@ -633,7 +632,6 @@ config ARCH_LPC32XX
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select HAVE_IDE select HAVE_IDE
select HAVE_PWM select HAVE_PWM
select USB_ARCH_HAS_OHCI
select USE_OF select USE_OF
help help
Support for the NXP LPC32XX family of processors Support for the NXP LPC32XX family of processors
...@@ -770,7 +768,6 @@ config ARCH_S3C64XX ...@@ -770,7 +768,6 @@ config ARCH_S3C64XX
select SAMSUNG_ATAGS select SAMSUNG_ATAGS
select SAMSUNG_WAKEMASK select SAMSUNG_WAKEMASK
select SAMSUNG_WDT_RESET select SAMSUNG_WDT_RESET
select USB_ARCH_HAS_OHCI
help help
Samsung S3C64XX series based systems Samsung S3C64XX series based systems
......
...@@ -36,7 +36,6 @@ config ARCH_EXYNOS5 ...@@ -36,7 +36,6 @@ config ARCH_EXYNOS5
select HAVE_ARM_SCU if SMP select HAVE_ARM_SCU if SMP
select HAVE_SMP select HAVE_SMP
select PINCTRL select PINCTRL
select USB_ARCH_HAS_XHCI
help help
Samsung EXYNOS5 (Cortex-A15) SoC based systems Samsung EXYNOS5 (Cortex-A15) SoC based systems
......
...@@ -21,7 +21,6 @@ config ARCH_OMAP3 ...@@ -21,7 +21,6 @@ config ARCH_OMAP3
select PM_OPP if PM select PM_OPP if PM
select PM_RUNTIME if CPU_IDLE select PM_RUNTIME if CPU_IDLE
select SOC_HAS_OMAP2_SDRC select SOC_HAS_OMAP2_SDRC
select USB_ARCH_HAS_EHCI if USB_SUPPORT
config ARCH_OMAP4 config ARCH_OMAP4
bool "TI OMAP4" bool "TI OMAP4"
...@@ -42,7 +41,6 @@ config ARCH_OMAP4 ...@@ -42,7 +41,6 @@ config ARCH_OMAP4
select PL310_ERRATA_727915 select PL310_ERRATA_727915
select PM_OPP if PM select PM_OPP if PM
select PM_RUNTIME if CPU_IDLE select PM_RUNTIME if CPU_IDLE
select USB_ARCH_HAS_EHCI if USB_SUPPORT
select ARM_ERRATA_754322 select ARM_ERRATA_754322
select ARM_ERRATA_775420 select ARM_ERRATA_775420
......
...@@ -114,8 +114,6 @@ config ARCH_R8A7778 ...@@ -114,8 +114,6 @@ config ARCH_R8A7778
select CPU_V7 select CPU_V7
select SH_CLK_CPG select SH_CLK_CPG
select ARM_GIC select ARM_GIC
select USB_ARCH_HAS_EHCI
select USB_ARCH_HAS_OHCI
select SYS_SUPPORTS_SH_TMU select SYS_SUPPORTS_SH_TMU
config ARCH_R8A7779 config ARCH_R8A7779
...@@ -124,8 +122,6 @@ config ARCH_R8A7779 ...@@ -124,8 +122,6 @@ config ARCH_R8A7779
select ARM_GIC select ARM_GIC
select CPU_V7 select CPU_V7
select SH_CLK_CPG select SH_CLK_CPG
select USB_ARCH_HAS_EHCI
select USB_ARCH_HAS_OHCI
select RENESAS_INTC_IRQPIN select RENESAS_INTC_IRQPIN
select SYS_SUPPORTS_SH_TMU select SYS_SUPPORTS_SH_TMU
......
...@@ -19,7 +19,6 @@ config ARCH_TEGRA ...@@ -19,7 +19,6 @@ config ARCH_TEGRA
select RESET_CONTROLLER select RESET_CONTROLLER
select SOC_BUS select SOC_BUS
select SPARSE_IRQ select SPARSE_IRQ
select USB_ARCH_HAS_EHCI if USB_SUPPORT
select USB_ULPI if USB_PHY select USB_ULPI if USB_PHY
select USB_ULPI_VIEWPORT if USB_PHY select USB_ULPI_VIEWPORT if USB_PHY
select USE_OF select USE_OF
......
...@@ -67,8 +67,6 @@ config MIPS_ALCHEMY ...@@ -67,8 +67,6 @@ config MIPS_ALCHEMY
select SYS_SUPPORTS_APM_EMULATION select SYS_SUPPORTS_APM_EMULATION
select ARCH_REQUIRE_GPIOLIB select ARCH_REQUIRE_GPIOLIB
select SYS_SUPPORTS_ZBOOT select SYS_SUPPORTS_ZBOOT
select USB_ARCH_HAS_OHCI
select USB_ARCH_HAS_EHCI
config AR7 config AR7
bool "Texas Instruments AR7" bool "Texas Instruments AR7"
...@@ -360,7 +358,6 @@ config MIPS_SEAD3 ...@@ -360,7 +358,6 @@ config MIPS_SEAD3
select SYS_SUPPORTS_LITTLE_ENDIAN select SYS_SUPPORTS_LITTLE_ENDIAN
select SYS_SUPPORTS_SMARTMIPS select SYS_SUPPORTS_SMARTMIPS
select SYS_SUPPORTS_MICROMIPS select SYS_SUPPORTS_MICROMIPS
select USB_ARCH_HAS_EHCI
select USB_EHCI_BIG_ENDIAN_DESC select USB_EHCI_BIG_ENDIAN_DESC
select USB_EHCI_BIG_ENDIAN_MMIO select USB_EHCI_BIG_ENDIAN_MMIO
select USE_OF select USE_OF
...@@ -718,8 +715,6 @@ config CAVIUM_OCTEON_SOC ...@@ -718,8 +715,6 @@ config CAVIUM_OCTEON_SOC
select SWAP_IO_SPACE select SWAP_IO_SPACE
select HW_HAS_PCI select HW_HAS_PCI
select ZONE_DMA32 select ZONE_DMA32
select USB_ARCH_HAS_OHCI
select USB_ARCH_HAS_EHCI
select HOLES_IN_ZONE select HOLES_IN_ZONE
select ARCH_REQUIRE_GPIOLIB select ARCH_REQUIRE_GPIOLIB
help help
...@@ -756,8 +751,6 @@ config NLM_XLR_BOARD ...@@ -756,8 +751,6 @@ config NLM_XLR_BOARD
select ZONE_DMA32 if 64BIT select ZONE_DMA32 if 64BIT
select SYNC_R4K select SYNC_R4K
select SYS_HAS_EARLY_PRINTK select SYS_HAS_EARLY_PRINTK
select USB_ARCH_HAS_OHCI if USB_SUPPORT
select USB_ARCH_HAS_EHCI if USB_SUPPORT
select SYS_SUPPORTS_ZBOOT select SYS_SUPPORTS_ZBOOT
select SYS_SUPPORTS_ZBOOT_UART16550 select SYS_SUPPORTS_ZBOOT_UART16550
help help
......
...@@ -74,34 +74,26 @@ config ATH79_MACH_UBNT_XM ...@@ -74,34 +74,26 @@ config ATH79_MACH_UBNT_XM
endmenu endmenu
config SOC_AR71XX config SOC_AR71XX
select USB_ARCH_HAS_EHCI
select USB_ARCH_HAS_OHCI
select HW_HAS_PCI select HW_HAS_PCI
def_bool n def_bool n
config SOC_AR724X config SOC_AR724X
select USB_ARCH_HAS_EHCI
select USB_ARCH_HAS_OHCI
select HW_HAS_PCI select HW_HAS_PCI
select PCI_AR724X if PCI select PCI_AR724X if PCI
def_bool n def_bool n
config SOC_AR913X config SOC_AR913X
select USB_ARCH_HAS_EHCI
def_bool n def_bool n
config SOC_AR933X config SOC_AR933X
select USB_ARCH_HAS_EHCI
def_bool n def_bool n
config SOC_AR934X config SOC_AR934X
select USB_ARCH_HAS_EHCI
select HW_HAS_PCI select HW_HAS_PCI
select PCI_AR724X if PCI select PCI_AR724X if PCI
def_bool n def_bool n
config SOC_QCA955X config SOC_QCA955X
select USB_ARCH_HAS_EHCI
select HW_HAS_PCI select HW_HAS_PCI
select PCI_AR724X if PCI select PCI_AR724X if PCI
def_bool n def_bool n
......
...@@ -20,19 +20,13 @@ choice ...@@ -20,19 +20,13 @@ choice
config SOC_RT305X config SOC_RT305X
bool "RT305x" bool "RT305x"
select USB_ARCH_HAS_HCD select USB_ARCH_HAS_HCD
select USB_ARCH_HAS_OHCI
select USB_ARCH_HAS_EHCI
config SOC_RT3883 config SOC_RT3883
bool "RT3883" bool "RT3883"
select USB_ARCH_HAS_OHCI
select USB_ARCH_HAS_EHCI
select HW_HAS_PCI select HW_HAS_PCI
config SOC_MT7620 config SOC_MT7620
bool "MT7620" bool "MT7620"
select USB_ARCH_HAS_OHCI
select USB_ARCH_HAS_EHCI
endchoice endchoice
......
...@@ -265,7 +265,6 @@ config 440EP ...@@ -265,7 +265,6 @@ config 440EP
select PPC_FPU select PPC_FPU
select IBM440EP_ERR42 select IBM440EP_ERR42
select IBM_EMAC_ZMII select IBM_EMAC_ZMII
select USB_ARCH_HAS_OHCI
config 440EPX config 440EPX
bool bool
......
...@@ -2,10 +2,8 @@ config PPC_PS3 ...@@ -2,10 +2,8 @@ config PPC_PS3
bool "Sony PS3" bool "Sony PS3"
depends on PPC64 && PPC_BOOK3S depends on PPC64 && PPC_BOOK3S
select PPC_CELL select PPC_CELL
select USB_ARCH_HAS_OHCI
select USB_OHCI_LITTLE_ENDIAN select USB_OHCI_LITTLE_ENDIAN
select USB_OHCI_BIG_ENDIAN_MMIO select USB_OHCI_BIG_ENDIAN_MMIO
select USB_ARCH_HAS_EHCI
select USB_EHCI_BIG_ENDIAN_MMIO select USB_EHCI_BIG_ENDIAN_MMIO
select PPC_PCI_CHOICE select PPC_PCI_CHOICE
help help
......
...@@ -347,7 +347,6 @@ config CPU_SUBTYPE_SH7720 ...@@ -347,7 +347,6 @@ config CPU_SUBTYPE_SH7720
select CPU_HAS_DSP select CPU_HAS_DSP
select SYS_SUPPORTS_SH_CMT select SYS_SUPPORTS_SH_CMT
select ARCH_WANT_OPTIONAL_GPIOLIB select ARCH_WANT_OPTIONAL_GPIOLIB
select USB_ARCH_HAS_OHCI
select USB_OHCI_SH if USB_OHCI_HCD select USB_OHCI_SH if USB_OHCI_HCD
select PINCTRL select PINCTRL
help help
...@@ -358,7 +357,6 @@ config CPU_SUBTYPE_SH7721 ...@@ -358,7 +357,6 @@ config CPU_SUBTYPE_SH7721
select CPU_SH3 select CPU_SH3
select CPU_HAS_DSP select CPU_HAS_DSP
select SYS_SUPPORTS_SH_CMT select SYS_SUPPORTS_SH_CMT
select USB_ARCH_HAS_OHCI
select USB_OHCI_SH if USB_OHCI_HCD select USB_OHCI_SH if USB_OHCI_HCD
help help
Select SH7721 if you have a SH3-DSP SH7721 CPU. Select SH7721 if you have a SH3-DSP SH7721 CPU.
...@@ -436,8 +434,6 @@ config CPU_SUBTYPE_SH7734 ...@@ -436,8 +434,6 @@ config CPU_SUBTYPE_SH7734
select CPU_SH4A select CPU_SH4A
select CPU_SHX2 select CPU_SHX2
select ARCH_WANT_OPTIONAL_GPIOLIB select ARCH_WANT_OPTIONAL_GPIOLIB
select USB_ARCH_HAS_OHCI
select USB_ARCH_HAS_EHCI
select PINCTRL select PINCTRL
help help
Select SH7734 if you have a SH4A SH7734 CPU. Select SH7734 if you have a SH4A SH7734 CPU.
...@@ -447,8 +443,6 @@ config CPU_SUBTYPE_SH7757 ...@@ -447,8 +443,6 @@ config CPU_SUBTYPE_SH7757
select CPU_SH4A select CPU_SH4A
select CPU_SHX2 select CPU_SHX2
select ARCH_WANT_OPTIONAL_GPIOLIB select ARCH_WANT_OPTIONAL_GPIOLIB
select USB_ARCH_HAS_OHCI
select USB_ARCH_HAS_EHCI
select PINCTRL select PINCTRL
help help
Select SH7757 if you have a SH4A SH7757 CPU. Select SH7757 if you have a SH4A SH7757 CPU.
...@@ -456,7 +450,6 @@ config CPU_SUBTYPE_SH7757 ...@@ -456,7 +450,6 @@ config CPU_SUBTYPE_SH7757
config CPU_SUBTYPE_SH7763 config CPU_SUBTYPE_SH7763
bool "Support SH7763 processor" bool "Support SH7763 processor"
select CPU_SH4A select CPU_SH4A
select USB_ARCH_HAS_OHCI
select USB_OHCI_SH if USB_OHCI_HCD select USB_OHCI_SH if USB_OHCI_HCD
help help
Select SH7763 if you have a SH4A SH7763(R5S77631) CPU. Select SH7763 if you have a SH4A SH7763(R5S77631) CPU.
...@@ -485,9 +478,7 @@ config CPU_SUBTYPE_SH7786 ...@@ -485,9 +478,7 @@ config CPU_SUBTYPE_SH7786
select CPU_HAS_PTEAEX select CPU_HAS_PTEAEX
select GENERIC_CLOCKEVENTS_BROADCAST if SMP select GENERIC_CLOCKEVENTS_BROADCAST if SMP
select ARCH_WANT_OPTIONAL_GPIOLIB select ARCH_WANT_OPTIONAL_GPIOLIB
select USB_ARCH_HAS_OHCI
select USB_OHCI_SH if USB_OHCI_HCD select USB_OHCI_SH if USB_OHCI_HCD
select USB_ARCH_HAS_EHCI
select USB_EHCI_SH if USB_EHCI_HCD select USB_EHCI_SH if USB_EHCI_HCD
select PINCTRL select PINCTRL
......
...@@ -2278,6 +2278,7 @@ static const struct hid_device_id hid_ignore_list[] = { ...@@ -2278,6 +2278,7 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) }, { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
{ HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) }, { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
{ HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
{ } { }
}; };
......
...@@ -961,4 +961,7 @@ ...@@ -961,4 +961,7 @@
#define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05 #define USB_DEVICE_ID_PRIMAX_KEYBOARD 0x4e05
#define USB_VENDOR_ID_RISO_KAGAKU 0x1294 /* Riso Kagaku Corp. */
#define USB_DEVICE_ID_RI_KA_WEBMAIL 0x1320 /* Webmail Notifier */
#endif #endif
...@@ -16,30 +16,56 @@ config GENERIC_PHY ...@@ -16,30 +16,56 @@ config GENERIC_PHY
framework should select this config. framework should select this config.
config PHY_EXYNOS_MIPI_VIDEO config PHY_EXYNOS_MIPI_VIDEO
depends on HAS_IOMEM
tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver" tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
depends on HAS_IOMEM
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
select GENERIC_PHY
default y if ARCH_S5PV210 || ARCH_EXYNOS
help help
Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
and EXYNOS SoCs. and EXYNOS SoCs.
config PHY_MVEBU_SATA config PHY_MVEBU_SATA
def_bool y def_bool y
depends on ARCH_KIRKWOOD || ARCH_DOVE depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE
depends on OF depends on OF
select GENERIC_PHY select GENERIC_PHY
config OMAP_CONTROL_PHY
tristate "OMAP CONTROL PHY Driver"
help
Enable this to add support for the PHY part present in the control
module. This driver has API to power on the USB2 PHY and to write to
the mailbox. The mailbox is present only in omap4 and the register to
power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
additional register to power on USB3 PHY/SATA PHY/PCIE PHY
(PIPE3 PHY).
config OMAP_USB2 config OMAP_USB2
tristate "OMAP USB2 PHY Driver" tristate "OMAP USB2 PHY Driver"
depends on ARCH_OMAP2PLUS depends on ARCH_OMAP2PLUS
depends on USB_PHY depends on USB_PHY
select GENERIC_PHY select GENERIC_PHY
select OMAP_CONTROL_USB select OMAP_CONTROL_PHY
depends on OMAP_OCP2SCP
help help
Enable this to support the transceiver that is part of SOC. This Enable this to support the transceiver that is part of SOC. This
driver takes care of all the PHY functionality apart from comparator. driver takes care of all the PHY functionality apart from comparator.
The USB OTG controller communicates with the comparator using this The USB OTG controller communicates with the comparator using this
driver. driver.
config TI_PIPE3
tristate "TI PIPE3 PHY Driver"
depends on ARCH_OMAP2PLUS || COMPILE_TEST
select GENERIC_PHY
select OMAP_CONTROL_PHY
depends on OMAP_OCP2SCP
help
Enable this to support the PIPE3 PHY that is part of TI SOCs. This
driver takes care of all the PHY functionality apart from comparator.
This driver interacts with the "OMAP Control PHY Driver" to power
on/off the PHY.
config TWL4030_USB config TWL4030_USB
tristate "TWL4030 USB Transceiver Driver" tristate "TWL4030 USB Transceiver Driver"
depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
...@@ -54,6 +80,8 @@ config TWL4030_USB ...@@ -54,6 +80,8 @@ config TWL4030_USB
config PHY_EXYNOS_DP_VIDEO config PHY_EXYNOS_DP_VIDEO
tristate "EXYNOS SoC series Display Port PHY driver" tristate "EXYNOS SoC series Display Port PHY driver"
depends on OF depends on OF
depends on ARCH_EXYNOS || COMPILE_TEST
default ARCH_EXYNOS
select GENERIC_PHY select GENERIC_PHY
help help
Support for Display Port PHY found on Samsung EXYNOS SoCs. Support for Display Port PHY found on Samsung EXYNOS SoCs.
...@@ -65,4 +93,77 @@ config BCM_KONA_USB2_PHY ...@@ -65,4 +93,77 @@ config BCM_KONA_USB2_PHY
help help
Enable this to support the Broadcom Kona USB 2.0 PHY. Enable this to support the Broadcom Kona USB 2.0 PHY.
config PHY_EXYNOS5250_SATA
tristate "Exynos5250 Sata SerDes/PHY driver"
depends on SOC_EXYNOS5250
depends on HAS_IOMEM
depends on OF
select GENERIC_PHY
select I2C
select I2C_S3C2410
select MFD_SYSCON
help
Enable this to support SATA SerDes/Phy found on Samsung's
Exynos5250 based SoCs.This SerDes/Phy supports SATA 1.5 Gb/s,
SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host
port to accept one SATA device.
config PHY_SUN4I_USB
tristate "Allwinner sunxi SoC USB PHY driver"
depends on ARCH_SUNXI && HAS_IOMEM && OF
select GENERIC_PHY
help
Enable this to support the transceiver that is part of Allwinner
sunxi SoCs.
This driver controls the entire USB PHY block, both the USB OTG
parts, as well as the 2 regular USB 2 host PHYs.
config PHY_SAMSUNG_USB2
tristate "Samsung USB 2.0 PHY driver"
select GENERIC_PHY
select MFD_SYSCON
help
Enable this to support the Samsung USB 2.0 PHY driver for Samsung
SoCs. This driver provides the interface for USB 2.0 PHY. Support for
particular SoCs has to be enabled in addition to this driver. Number
and type of supported phys depends on the SoC.
config PHY_EXYNOS4210_USB2
bool "Support for Exynos 4210"
depends on PHY_SAMSUNG_USB2
depends on CPU_EXYNOS4210
help
Enable USB PHY support for Exynos 4210. This option requires that
Samsung USB 2.0 PHY driver is enabled and means that support for this
particular SoC is compiled in the driver. In case of Exynos 4210 four
phys are available - device, host, HSIC0 and HSIC1.
config PHY_EXYNOS4X12_USB2
bool "Support for Exynos 4x12"
depends on PHY_SAMSUNG_USB2
depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
help
Enable USB PHY support for Exynos 4x12. This option requires that
Samsung USB 2.0 PHY driver is enabled and means that support for this
particular SoC is compiled in the driver. In case of Exynos 4x12 four
phys are available - device, host, HSIC0 and HSIC1.
config PHY_EXYNOS5250_USB2
bool "Support for Exynos 5250"
depends on PHY_SAMSUNG_USB2
depends on SOC_EXYNOS5250
help
Enable USB PHY support for Exynos 5250. This option requires that
Samsung USB 2.0 PHY driver is enabled and means that support for this
particular SoC is compiled in the driver. In case of Exynos 5250 four
phys are available - device, host, HSIC0 and HSIC.
config PHY_XGENE
tristate "APM X-Gene 15Gbps PHY support"
depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST)
select GENERIC_PHY
help
This option enables support for APM X-Gene SoC multi-purpose PHY.
endmenu endmenu
...@@ -7,5 +7,14 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o ...@@ -7,5 +7,14 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-samsung-usb2.o
obj-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o
obj-$(CONFIG_PHY_EXYNOS4X12_USB2) += phy-exynos4x12-usb2.o
obj-$(CONFIG_PHY_EXYNOS5250_USB2) += phy-exynos5250-usb2.o
obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
...@@ -128,10 +128,8 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev) ...@@ -128,10 +128,8 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
phy_provider = devm_of_phy_provider_register(dev, phy_provider = devm_of_phy_provider_register(dev,
of_phy_simple_xlate); of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0; return PTR_ERR_OR_ZERO(phy_provider);
} }
static const struct of_device_id bcm_kona_usb2_dt_ids[] = { static const struct of_device_id bcm_kona_usb2_dt_ids[] = {
......
...@@ -274,8 +274,8 @@ int phy_power_off(struct phy *phy) ...@@ -274,8 +274,8 @@ int phy_power_off(struct phy *phy)
EXPORT_SYMBOL_GPL(phy_power_off); EXPORT_SYMBOL_GPL(phy_power_off);
/** /**
* of_phy_get() - lookup and obtain a reference to a phy by phandle * _of_phy_get() - lookup and obtain a reference to a phy by phandle
* @dev: device that requests this phy * @np: device_node for which to get the phy
* @index: the index of the phy * @index: the index of the phy
* *
* Returns the phy associated with the given phandle value, * Returns the phy associated with the given phandle value,
...@@ -284,20 +284,17 @@ EXPORT_SYMBOL_GPL(phy_power_off); ...@@ -284,20 +284,17 @@ EXPORT_SYMBOL_GPL(phy_power_off);
* not yet loaded. This function uses of_xlate call back function provided * not yet loaded. This function uses of_xlate call back function provided
* while registering the phy_provider to find the phy instance. * while registering the phy_provider to find the phy instance.
*/ */
static struct phy *of_phy_get(struct device *dev, int index) static struct phy *_of_phy_get(struct device_node *np, int index)
{ {
int ret; int ret;
struct phy_provider *phy_provider; struct phy_provider *phy_provider;
struct phy *phy = NULL; struct phy *phy = NULL;
struct of_phandle_args args; struct of_phandle_args args;
ret = of_parse_phandle_with_args(dev->of_node, "phys", "#phy-cells", ret = of_parse_phandle_with_args(np, "phys", "#phy-cells",
index, &args); index, &args);
if (ret) { if (ret)
dev_dbg(dev, "failed to get phy in %s node\n",
dev->of_node->full_name);
return ERR_PTR(-ENODEV); return ERR_PTR(-ENODEV);
}
mutex_lock(&phy_provider_mutex); mutex_lock(&phy_provider_mutex);
phy_provider = of_phy_provider_lookup(args.np); phy_provider = of_phy_provider_lookup(args.np);
...@@ -316,6 +313,36 @@ static struct phy *of_phy_get(struct device *dev, int index) ...@@ -316,6 +313,36 @@ static struct phy *of_phy_get(struct device *dev, int index)
return phy; return phy;
} }
/**
* of_phy_get() - lookup and obtain a reference to a phy using a device_node.
* @np: device_node for which to get the phy
* @con_id: name of the phy from device's point of view
*
* 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 *of_phy_get(struct device_node *np, const char *con_id)
{
struct phy *phy = NULL;
int index = 0;
if (con_id)
index = of_property_match_string(np, "phy-names", con_id);
phy = _of_phy_get(np, index);
if (IS_ERR(phy))
return phy;
if (!try_module_get(phy->ops->owner))
return ERR_PTR(-EPROBE_DEFER);
get_device(&phy->dev);
return phy;
}
EXPORT_SYMBOL_GPL(of_phy_get);
/** /**
* phy_put() - release the PHY * phy_put() - release the PHY
* @phy: the phy returned by phy_get() * @phy: the phy returned by phy_get()
...@@ -407,7 +434,7 @@ struct phy *phy_get(struct device *dev, const char *string) ...@@ -407,7 +434,7 @@ struct phy *phy_get(struct device *dev, const char *string)
if (dev->of_node) { if (dev->of_node) {
index = of_property_match_string(dev->of_node, "phy-names", index = of_property_match_string(dev->of_node, "phy-names",
string); string);
phy = of_phy_get(dev, index); phy = _of_phy_get(dev->of_node, index);
} else { } else {
phy = phy_lookup(dev, string); phy = phy_lookup(dev, string);
} }
...@@ -498,6 +525,37 @@ struct phy *devm_phy_optional_get(struct device *dev, const char *string) ...@@ -498,6 +525,37 @@ struct phy *devm_phy_optional_get(struct device *dev, const char *string)
} }
EXPORT_SYMBOL_GPL(devm_phy_optional_get); EXPORT_SYMBOL_GPL(devm_phy_optional_get);
/**
* devm_of_phy_get() - lookup and obtain a reference to a phy.
* @dev: device that requests this phy
* @np: node containing the phy
* @con_id: name of the phy from device's point of view
*
* Gets the phy using of_phy_get(), and associates a device with it using
* devres. On driver detach, release function is invoked on the devres data,
* then, devres data is freed.
*/
struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
const char *con_id)
{
struct phy **ptr, *phy;
ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
phy = of_phy_get(np, con_id);
if (!IS_ERR(phy)) {
*ptr = phy;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return phy;
}
EXPORT_SYMBOL_GPL(devm_of_phy_get);
/** /**
* phy_create() - create a new phy * phy_create() - create a new phy
* @dev: device that is creating the new phy * @dev: device that is creating the new phy
......
/*
* Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Kamil Debski <k.debski@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/delay.h>
#include <linux/io.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#include "phy-samsung-usb2.h"
/* Exynos USB PHY registers */
/* PHY power control */
#define EXYNOS_4210_UPHYPWR 0x0
#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND BIT(0)
#define EXYNOS_4210_UPHYPWR_PHY0_PWR BIT(3)
#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR BIT(4)
#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP BIT(5)
#define EXYNOS_4210_UPHYPWR_PHY0 ( \
EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
EXYNOS_4210_UPHYPWR_PHY0_PWR | \
EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND BIT(6)
#define EXYNOS_4210_UPHYPWR_PHY1_PWR BIT(7)
#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP BIT(8)
#define EXYNOS_4210_UPHYPWR_PHY1 ( \
EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
EXYNOS_4210_UPHYPWR_PHY1_PWR | \
EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND BIT(9)
#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP BIT(10)
#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND BIT(11)
#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP BIT(12)
#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
/* PHY clock control */
#define EXYNOS_4210_UPHYCLK 0x4
#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK (0x3 << 0)
#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET 0
#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ (0x0 << 0)
#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ (0x3 << 0)
#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP BIT(2)
#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON BIT(4)
#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON BIT(7)
/* PHY reset control */
#define EXYNOS_4210_UPHYRST 0x8
#define EXYNOS_4210_URSTCON_PHY0 BIT(0)
#define EXYNOS_4210_URSTCON_OTG_HLINK BIT(1)
#define EXYNOS_4210_URSTCON_OTG_PHYLINK BIT(2)
#define EXYNOS_4210_URSTCON_PHY1_ALL BIT(3)
#define EXYNOS_4210_URSTCON_PHY1_P0 BIT(4)
#define EXYNOS_4210_URSTCON_PHY1_P1P2 BIT(5)
#define EXYNOS_4210_URSTCON_HOST_LINK_ALL BIT(6)
#define EXYNOS_4210_URSTCON_HOST_LINK_P0 BIT(7)
#define EXYNOS_4210_URSTCON_HOST_LINK_P1 BIT(8)
#define EXYNOS_4210_URSTCON_HOST_LINK_P2 BIT(9)
/* Isolation, configured in the power management unit */
#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET 0x704
#define EXYNOS_4210_USB_ISOL_DEVICE BIT(0)
#define EXYNOS_4210_USB_ISOL_HOST_OFFSET 0x708
#define EXYNOS_4210_USB_ISOL_HOST BIT(0)
/* USBYPHY1 Floating prevention */
#define EXYNOS_4210_UPHY1CON 0x34
#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION 0x1
/* Mode switching SUB Device <-> Host */
#define EXYNOS_4210_MODE_SWITCH_OFFSET 0x21c
#define EXYNOS_4210_MODE_SWITCH_MASK 1
#define EXYNOS_4210_MODE_SWITCH_DEVICE 0
#define EXYNOS_4210_MODE_SWITCH_HOST 1
enum exynos4210_phy_id {
EXYNOS4210_DEVICE,
EXYNOS4210_HOST,
EXYNOS4210_HSIC0,
EXYNOS4210_HSIC1,
EXYNOS4210_NUM_PHYS,
};
/*
* exynos4210_rate_to_clk() converts the supplied clock rate to the value that
* can be written to the phy register.
*/
static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
{
switch (rate) {
case 12 * MHZ:
*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
break;
case 24 * MHZ:
*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
break;
case 48 * MHZ:
*reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
break;
default:
return -EINVAL;
}
return 0;
}
static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 offset;
u32 mask;
switch (inst->cfg->id) {
case EXYNOS4210_DEVICE:
offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
mask = EXYNOS_4210_USB_ISOL_DEVICE;
break;
case EXYNOS4210_HOST:
offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
mask = EXYNOS_4210_USB_ISOL_HOST;
break;
default:
return;
};
regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
}
static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 rstbits = 0;
u32 phypwr = 0;
u32 rst;
u32 pwr;
u32 clk;
switch (inst->cfg->id) {
case EXYNOS4210_DEVICE:
phypwr = EXYNOS_4210_UPHYPWR_PHY0;
rstbits = EXYNOS_4210_URSTCON_PHY0;
break;
case EXYNOS4210_HOST:
phypwr = EXYNOS_4210_UPHYPWR_PHY1;
rstbits = EXYNOS_4210_URSTCON_PHY1_ALL |
EXYNOS_4210_URSTCON_PHY1_P0 |
EXYNOS_4210_URSTCON_PHY1_P1P2 |
EXYNOS_4210_URSTCON_HOST_LINK_ALL |
EXYNOS_4210_URSTCON_HOST_LINK_P0;
writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
break;
case EXYNOS4210_HSIC0:
phypwr = EXYNOS_4210_UPHYPWR_HSIC0;
rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 |
EXYNOS_4210_URSTCON_HOST_LINK_P1;
break;
case EXYNOS4210_HSIC1:
phypwr = EXYNOS_4210_UPHYPWR_HSIC1;
rstbits = EXYNOS_4210_URSTCON_PHY1_P1P2 |
EXYNOS_4210_URSTCON_HOST_LINK_P2;
break;
};
if (on) {
clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK);
clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK;
clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET;
writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK);
pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
pwr &= ~phypwr;
writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
rst |= rstbits;
writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
udelay(10);
rst &= ~rstbits;
writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
/* The following delay is necessary for the reset sequence to be
* completed */
udelay(80);
} else {
pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
pwr |= phypwr;
writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
}
}
static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
{
/* Order of initialisation is important - first power then isolation */
exynos4210_phy_pwr(inst, 1);
exynos4210_isol(inst, 0);
return 0;
}
static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
{
exynos4210_isol(inst, 1);
exynos4210_phy_pwr(inst, 0);
return 0;
}
static const struct samsung_usb2_common_phy exynos4210_phys[] = {
{
.label = "device",
.id = EXYNOS4210_DEVICE,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "host",
.id = EXYNOS4210_HOST,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "hsic0",
.id = EXYNOS4210_HSIC0,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{
.label = "hsic1",
.id = EXYNOS4210_HSIC1,
.power_on = exynos4210_power_on,
.power_off = exynos4210_power_off,
},
{},
};
const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
.has_mode_switch = 0,
.num_phys = EXYNOS4210_NUM_PHYS,
.phys = exynos4210_phys,
.rate_to_clk = exynos4210_rate_to_clk,
};
/*
* Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Kamil Debski <k.debski@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/delay.h>
#include <linux/io.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#include "phy-samsung-usb2.h"
/* Exynos USB PHY registers */
/* PHY power control */
#define EXYNOS_4x12_UPHYPWR 0x0
#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND BIT(0)
#define EXYNOS_4x12_UPHYPWR_PHY0_PWR BIT(3)
#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR BIT(4)
#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP BIT(5)
#define EXYNOS_4x12_UPHYPWR_PHY0 ( \
EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \
EXYNOS_4x12_UPHYPWR_PHY0_PWR | \
EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \
EXYNOS_4x12_UPHYPWR_PHY0_SLEEP)
#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND BIT(6)
#define EXYNOS_4x12_UPHYPWR_PHY1_PWR BIT(7)
#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP BIT(8)
#define EXYNOS_4x12_UPHYPWR_PHY1 ( \
EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \
EXYNOS_4x12_UPHYPWR_PHY1_PWR | \
EXYNOS_4x12_UPHYPWR_PHY1_SLEEP)
#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND BIT(9)
#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR BIT(10)
#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP BIT(11)
#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \
EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \
EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \
EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP)
#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND BIT(12)
#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR BIT(13)
#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP BIT(14)
#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \
EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \
EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \
EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP)
/* PHY clock control */
#define EXYNOS_4x12_UPHYCLK 0x4
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK (0x7 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET 0
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6 (0x0 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ (0x1 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ (0x2 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2 (0x3 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ (0x4 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ (0x5 << 0)
#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ (0x7 << 0)
#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP BIT(3)
#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON BIT(4)
#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON BIT(7)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK (0x7f << 10)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET 10
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ (0x24 << 10)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ (0x1c << 10)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ (0x1a << 10)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10)
#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ (0x14 << 10)
/* PHY reset control */
#define EXYNOS_4x12_UPHYRST 0x8
#define EXYNOS_4x12_URSTCON_PHY0 BIT(0)
#define EXYNOS_4x12_URSTCON_OTG_HLINK BIT(1)
#define EXYNOS_4x12_URSTCON_OTG_PHYLINK BIT(2)
#define EXYNOS_4x12_URSTCON_HOST_PHY BIT(3)
#define EXYNOS_4x12_URSTCON_PHY1 BIT(4)
#define EXYNOS_4x12_URSTCON_HSIC0 BIT(5)
#define EXYNOS_4x12_URSTCON_HSIC1 BIT(6)
#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL BIT(7)
#define EXYNOS_4x12_URSTCON_HOST_LINK_P0 BIT(8)
#define EXYNOS_4x12_URSTCON_HOST_LINK_P1 BIT(9)
#define EXYNOS_4x12_URSTCON_HOST_LINK_P2 BIT(10)
/* Isolation, configured in the power management unit */
#define EXYNOS_4x12_USB_ISOL_OFFSET 0x704
#define EXYNOS_4x12_USB_ISOL_OTG BIT(0)
#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET 0x708
#define EXYNOS_4x12_USB_ISOL_HSIC0 BIT(0)
#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET 0x70c
#define EXYNOS_4x12_USB_ISOL_HSIC1 BIT(0)
/* Mode switching SUB Device <-> Host */
#define EXYNOS_4x12_MODE_SWITCH_OFFSET 0x21c
#define EXYNOS_4x12_MODE_SWITCH_MASK 1
#define EXYNOS_4x12_MODE_SWITCH_DEVICE 0
#define EXYNOS_4x12_MODE_SWITCH_HOST 1
enum exynos4x12_phy_id {
EXYNOS4x12_DEVICE,
EXYNOS4x12_HOST,
EXYNOS4x12_HSIC0,
EXYNOS4x12_HSIC1,
EXYNOS4x12_NUM_PHYS,
};
/*
* exynos4x12_rate_to_clk() converts the supplied clock rate to the value that
* can be written to the phy register.
*/
static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg)
{
/* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */
switch (rate) {
case 9600 * KHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6;
break;
case 10 * MHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ;
break;
case 12 * MHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ;
break;
case 19200 * KHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2;
break;
case 20 * MHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ;
break;
case 24 * MHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ;
break;
case 50 * MHZ:
*reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ;
break;
default:
return -EINVAL;
}
return 0;
}
static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 offset;
u32 mask;
switch (inst->cfg->id) {
case EXYNOS4x12_DEVICE:
case EXYNOS4x12_HOST:
offset = EXYNOS_4x12_USB_ISOL_OFFSET;
mask = EXYNOS_4x12_USB_ISOL_OTG;
break;
case EXYNOS4x12_HSIC0:
offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET;
mask = EXYNOS_4x12_USB_ISOL_HSIC0;
break;
case EXYNOS4x12_HSIC1:
offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET;
mask = EXYNOS_4x12_USB_ISOL_HSIC1;
break;
default:
return;
};
regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
}
static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 clk;
clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK;
clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
}
static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
u32 rstbits = 0;
u32 phypwr = 0;
u32 rst;
u32 pwr;
u32 mode = 0;
u32 switch_mode = 0;
switch (inst->cfg->id) {
case EXYNOS4x12_DEVICE:
phypwr = EXYNOS_4x12_UPHYPWR_PHY0;
rstbits = EXYNOS_4x12_URSTCON_PHY0;
mode = EXYNOS_4x12_MODE_SWITCH_DEVICE;
switch_mode = 1;
break;
case EXYNOS4x12_HOST:
phypwr = EXYNOS_4x12_UPHYPWR_PHY1;
rstbits = EXYNOS_4x12_URSTCON_HOST_PHY;
mode = EXYNOS_4x12_MODE_SWITCH_HOST;
switch_mode = 1;
break;
case EXYNOS4x12_HSIC0:
phypwr = EXYNOS_4x12_UPHYPWR_HSIC0;
rstbits = EXYNOS_4x12_URSTCON_HSIC1 |
EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
EXYNOS_4x12_URSTCON_HOST_PHY;
break;
case EXYNOS4x12_HSIC1:
phypwr = EXYNOS_4x12_UPHYPWR_HSIC1;
rstbits = EXYNOS_4x12_URSTCON_HSIC1 |
EXYNOS_4x12_URSTCON_HOST_LINK_P1;
break;
};
if (on) {
if (switch_mode)
regmap_update_bits(drv->reg_sys,
EXYNOS_4x12_MODE_SWITCH_OFFSET,
EXYNOS_4x12_MODE_SWITCH_MASK, mode);
pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
pwr &= ~phypwr;
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST);
rst |= rstbits;
writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
udelay(10);
rst &= ~rstbits;
writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
/* The following delay is necessary for the reset sequence to be
* completed */
udelay(80);
} else {
pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
pwr |= phypwr;
writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
}
}
static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
inst->enabled = 1;
exynos4x12_setup_clk(inst);
exynos4x12_phy_pwr(inst, 1);
exynos4x12_isol(inst, 0);
/* Power on the device, as it is necessary for HSIC to work */
if (inst->cfg->id == EXYNOS4x12_HSIC0) {
struct samsung_usb2_phy_instance *device =
&drv->instances[EXYNOS4x12_DEVICE];
exynos4x12_phy_pwr(device, 1);
exynos4x12_isol(device, 0);
}
return 0;
}
static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
{
struct samsung_usb2_phy_driver *drv = inst->drv;
struct samsung_usb2_phy_instance *device =
&drv->instances[EXYNOS4x12_DEVICE];
inst->enabled = 0;
exynos4x12_isol(inst, 1);
exynos4x12_phy_pwr(inst, 0);
if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
exynos4x12_isol(device, 1);
exynos4x12_phy_pwr(device, 0);
}
return 0;
}
static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
{
.label = "device",
.id = EXYNOS4x12_DEVICE,
.power_on = exynos4x12_power_on,
.power_off = exynos4x12_power_off,
},
{
.label = "host",
.id = EXYNOS4x12_HOST,
.power_on = exynos4x12_power_on,
.power_off = exynos4x12_power_off,
},
{
.label = "hsic0",
.id = EXYNOS4x12_HSIC0,
.power_on = exynos4x12_power_on,
.power_off = exynos4x12_power_off,
},
{
.label = "hsic1",
.id = EXYNOS4x12_HSIC1,
.power_on = exynos4x12_power_on,
.power_off = exynos4x12_power_off,
},
{},
};
const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
.has_mode_switch = 1,
.num_phys = EXYNOS4x12_NUM_PHYS,
.phys = exynos4x12_phys,
.rate_to_clk = exynos4x12_rate_to_clk,
};
/*
* Samsung SATA SerDes(PHY) driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Authors: Girish K S <ks.giri@samsung.com>
* Yuvaraj Kumar C D <yuvaraj.cd@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/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/i2c.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/regmap.h>
#include <linux/spinlock.h>
#include <linux/mfd/syscon.h>
#define SATAPHY_CONTROL_OFFSET 0x0724
#define EXYNOS5_SATAPHY_PMU_ENABLE BIT(0)
#define EXYNOS5_SATA_RESET 0x4
#define RESET_GLOBAL_RST_N BIT(0)
#define RESET_CMN_RST_N BIT(1)
#define RESET_CMN_BLOCK_RST_N BIT(2)
#define RESET_CMN_I2C_RST_N BIT(3)
#define RESET_TX_RX_PIPE_RST_N BIT(4)
#define RESET_TX_RX_BLOCK_RST_N BIT(5)
#define RESET_TX_RX_I2C_RST_N (BIT(6) | BIT(7))
#define LINK_RESET 0xf0000
#define EXYNOS5_SATA_MODE0 0x10
#define SATA_SPD_GEN3 BIT(1)
#define EXYNOS5_SATA_CTRL0 0x14
#define CTRL0_P0_PHY_CALIBRATED_SEL BIT(9)
#define CTRL0_P0_PHY_CALIBRATED BIT(8)
#define EXYNOS5_SATA_PHSATA_CTRLM 0xe0
#define PHCTRLM_REF_RATE BIT(1)
#define PHCTRLM_HIGH_SPEED BIT(0)
#define EXYNOS5_SATA_PHSATA_STATM 0xf0
#define PHSTATM_PLL_LOCKED BIT(0)
#define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000))
struct exynos_sata_phy {
struct phy *phy;
struct clk *phyclk;
void __iomem *regs;
struct regmap *pmureg;
struct i2c_client *client;
};
static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit,
u32 status)
{
unsigned long timeout = jiffies + PHY_PLL_TIMEOUT;
while (time_before(jiffies, timeout)) {
if ((readl(base + reg) & checkbit) == status)
return 0;
}
return -EFAULT;
}
static int exynos_sata_phy_power_on(struct phy *phy)
{
struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
EXYNOS5_SATAPHY_PMU_ENABLE, true);
}
static int exynos_sata_phy_power_off(struct phy *phy)
{
struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
EXYNOS5_SATAPHY_PMU_ENABLE, false);
}
static int exynos_sata_phy_init(struct phy *phy)
{
u32 val = 0;
int ret = 0;
u8 buf[] = { 0x3a, 0x0b };
struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
EXYNOS5_SATAPHY_PMU_ENABLE, true);
if (ret != 0)
dev_err(&sata_phy->phy->dev, "phy init failed\n");
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N
| RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N
| RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N;
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
val |= LINK_RESET;
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
val |= RESET_CMN_RST_N;
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
val &= ~PHCTRLM_REF_RATE;
writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
/* High speed enable for Gen3 */
val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
val |= PHCTRLM_HIGH_SPEED;
writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0);
val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED;
writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0);
val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0);
val |= SATA_SPD_GEN3;
writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0);
ret = i2c_master_send(sata_phy->client, buf, sizeof(buf));
if (ret < 0)
return ret;
/* release cmu reset */
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
val &= ~RESET_CMN_RST_N;
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
val |= RESET_CMN_RST_N;
writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
ret = wait_for_reg_status(sata_phy->regs,
EXYNOS5_SATA_PHSATA_STATM,
PHSTATM_PLL_LOCKED, 1);
if (ret < 0)
dev_err(&sata_phy->phy->dev,
"PHY PLL locking failed\n");
return ret;
}
static struct phy_ops exynos_sata_phy_ops = {
.init = exynos_sata_phy_init,
.power_on = exynos_sata_phy_power_on,
.power_off = exynos_sata_phy_power_off,
.owner = THIS_MODULE,
};
static int exynos_sata_phy_probe(struct platform_device *pdev)
{
struct exynos_sata_phy *sata_phy;
struct device *dev = &pdev->dev;
struct resource *res;
struct phy_provider *phy_provider;
struct device_node *node;
int ret = 0;
sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL);
if (!sata_phy)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
sata_phy->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(sata_phy->regs))
return PTR_ERR(sata_phy->regs);
sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
"samsung,syscon-phandle");
if (IS_ERR(sata_phy->pmureg)) {
dev_err(dev, "syscon regmap lookup failed.\n");
return PTR_ERR(sata_phy->pmureg);
}
node = of_parse_phandle(dev->of_node,
"samsung,exynos-sataphy-i2c-phandle", 0);
if (!node)
return -EINVAL;
sata_phy->client = of_find_i2c_device_by_node(node);
if (!sata_phy->client)
return -EPROBE_DEFER;
dev_set_drvdata(dev, sata_phy);
sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl");
if (IS_ERR(sata_phy->phyclk)) {
dev_err(dev, "failed to get clk for PHY\n");
return PTR_ERR(sata_phy->phyclk);
}
ret = clk_prepare_enable(sata_phy->phyclk);
if (ret < 0) {
dev_err(dev, "failed to enable source clk\n");
return ret;
}
sata_phy->phy = devm_phy_create(dev, &exynos_sata_phy_ops, NULL);
if (IS_ERR(sata_phy->phy)) {
clk_disable_unprepare(sata_phy->phyclk);
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(sata_phy->phy);
}
phy_set_drvdata(sata_phy->phy, sata_phy);
phy_provider = devm_of_phy_provider_register(dev,
of_phy_simple_xlate);
if (IS_ERR(phy_provider)) {
clk_disable_unprepare(sata_phy->phyclk);
return PTR_ERR(phy_provider);
}
return 0;
}
static const struct of_device_id exynos_sata_phy_of_match[] = {
{ .compatible = "samsung,exynos5250-sata-phy" },
{ },
};
MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match);
static struct platform_driver exynos_sata_phy_driver = {
.probe = exynos_sata_phy_probe,
.driver = {
.of_match_table = exynos_sata_phy_of_match,
.name = "samsung,sata-phy",
.owner = THIS_MODULE,
}
};
module_platform_driver(exynos_sata_phy_driver);
MODULE_DESCRIPTION("Samsung SerDes PHY driver");
MODULE_LICENSE("GPL V2");
MODULE_AUTHOR("Girish K S <ks.giri@samsung.com>");
MODULE_AUTHOR("Yuvaraj C D <yuvaraj.cd@samsung.com>");
This diff is collapsed.
...@@ -21,16 +21,19 @@ ...@@ -21,16 +21,19 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/usb/omap_usb.h> #include <linux/phy/omap_usb.h>
#include <linux/usb/phy_companion.h> #include <linux/usb/phy_companion.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/usb/omap_control_usb.h> #include <linux/phy/omap_control_phy.h>
#include <linux/phy/phy.h> #include <linux/phy/phy.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
#define USB2PHY_ANA_CONFIG1 0x4c
/** /**
* omap_usb2_set_comparator - links the comparator present in the sytem with * omap_usb2_set_comparator - links the comparator present in the sytem with
* this phy * this phy
...@@ -98,65 +101,116 @@ static int omap_usb_set_peripheral(struct usb_otg *otg, ...@@ -98,65 +101,116 @@ static int omap_usb_set_peripheral(struct usb_otg *otg,
return 0; return 0;
} }
static int omap_usb2_suspend(struct usb_phy *x, int suspend) static int omap_usb_power_off(struct phy *x)
{ {
struct omap_usb *phy = phy_to_omapusb(x); struct omap_usb *phy = phy_get_drvdata(x);
int ret;
if (suspend && !phy->is_suspended) { omap_control_phy_power(phy->control_dev, 0);
omap_control_usb_phy_power(phy->control_dev, 0);
pm_runtime_put_sync(phy->dev);
phy->is_suspended = 1;
} else if (!suspend && phy->is_suspended) {
ret = pm_runtime_get_sync(phy->dev);
if (ret < 0) {
dev_err(phy->dev, "get_sync failed with err %d\n", ret);
return ret;
}
omap_control_usb_phy_power(phy->control_dev, 1);
phy->is_suspended = 0;
}
return 0; return 0;
} }
static int omap_usb_power_off(struct phy *x) static int omap_usb_power_on(struct phy *x)
{ {
struct omap_usb *phy = phy_get_drvdata(x); struct omap_usb *phy = phy_get_drvdata(x);
omap_control_usb_phy_power(phy->control_dev, 0); omap_control_phy_power(phy->control_dev, 1);
return 0; return 0;
} }
static int omap_usb_power_on(struct phy *x) static int omap_usb_init(struct phy *x)
{ {
struct omap_usb *phy = phy_get_drvdata(x); struct omap_usb *phy = phy_get_drvdata(x);
u32 val;
omap_control_usb_phy_power(phy->control_dev, 1);
if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
/*
*
* Reduce the sensitivity of internal PHY by enabling the
* DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This
* resolves issues with certain devices which can otherwise
* be prone to false disconnects.
*
*/
val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1);
val |= USB2PHY_DISCON_BYP_LATCH;
omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val);
}
return 0; return 0;
} }
static struct phy_ops ops = { static struct phy_ops ops = {
.init = omap_usb_init,
.power_on = omap_usb_power_on, .power_on = omap_usb_power_on,
.power_off = omap_usb_power_off, .power_off = omap_usb_power_off,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
#ifdef CONFIG_OF
static const struct usb_phy_data omap_usb2_data = {
.label = "omap_usb2",
.flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
};
static const struct usb_phy_data omap5_usb2_data = {
.label = "omap5_usb2",
.flags = 0,
};
static const struct usb_phy_data dra7x_usb2_data = {
.label = "dra7x_usb2",
.flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
};
static const struct usb_phy_data am437x_usb2_data = {
.label = "am437x_usb2",
.flags = 0,
};
static const struct of_device_id omap_usb2_id_table[] = {
{
.compatible = "ti,omap-usb2",
.data = &omap_usb2_data,
},
{
.compatible = "ti,omap5-usb2",
.data = &omap5_usb2_data,
},
{
.compatible = "ti,dra7x-usb2",
.data = &dra7x_usb2_data,
},
{
.compatible = "ti,am437x-usb2",
.data = &am437x_usb2_data,
},
{},
};
MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
#endif
static int omap_usb2_probe(struct platform_device *pdev) static int omap_usb2_probe(struct platform_device *pdev)
{ {
struct omap_usb *phy; struct omap_usb *phy;
struct phy *generic_phy; struct phy *generic_phy;
struct resource *res;
struct phy_provider *phy_provider; struct phy_provider *phy_provider;
struct usb_otg *otg; struct usb_otg *otg;
struct device_node *node = pdev->dev.of_node; struct device_node *node = pdev->dev.of_node;
struct device_node *control_node; struct device_node *control_node;
struct platform_device *control_pdev; struct platform_device *control_pdev;
const struct of_device_id *of_id;
struct usb_phy_data *phy_data;
of_id = of_match_device(of_match_ptr(omap_usb2_id_table), &pdev->dev);
if (!node) if (!of_id)
return -EINVAL; return -EINVAL;
phy_data = (struct usb_phy_data *)of_id->data;
phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
if (!phy) { if (!phy) {
dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n"); dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
...@@ -172,11 +226,18 @@ static int omap_usb2_probe(struct platform_device *pdev) ...@@ -172,11 +226,18 @@ static int omap_usb2_probe(struct platform_device *pdev)
phy->dev = &pdev->dev; phy->dev = &pdev->dev;
phy->phy.dev = phy->dev; phy->phy.dev = phy->dev;
phy->phy.label = "omap-usb2"; phy->phy.label = phy_data->label;
phy->phy.set_suspend = omap_usb2_suspend;
phy->phy.otg = otg; phy->phy.otg = otg;
phy->phy.type = USB_PHY_TYPE_USB2; phy->phy.type = USB_PHY_TYPE_USB2;
if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
phy->phy_base = devm_ioremap_resource(&pdev->dev, res);
if (!phy->phy_base)
return -ENOMEM;
phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
}
control_node = of_parse_phandle(node, "ctrl-module", 0); control_node = of_parse_phandle(node, "ctrl-module", 0);
if (!control_node) { if (!control_node) {
dev_err(&pdev->dev, "Failed to get control device phandle\n"); dev_err(&pdev->dev, "Failed to get control device phandle\n");
...@@ -190,14 +251,14 @@ static int omap_usb2_probe(struct platform_device *pdev) ...@@ -190,14 +251,14 @@ static int omap_usb2_probe(struct platform_device *pdev)
} }
phy->control_dev = &control_pdev->dev; phy->control_dev = &control_pdev->dev;
omap_control_phy_power(phy->control_dev, 0);
phy->is_suspended = 1;
omap_control_usb_phy_power(phy->control_dev, 0);
otg->set_host = omap_usb_set_host; otg->set_host = omap_usb_set_host;
otg->set_peripheral = omap_usb_set_peripheral; otg->set_peripheral = omap_usb_set_peripheral;
otg->set_vbus = omap_usb_set_vbus; if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
otg->start_srp = omap_usb_start_srp; otg->set_vbus = omap_usb_set_vbus;
if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
otg->start_srp = omap_usb_start_srp;
otg->phy = &phy->phy; otg->phy = &phy->phy;
platform_set_drvdata(pdev, phy); platform_set_drvdata(pdev, phy);
...@@ -297,14 +358,6 @@ static const struct dev_pm_ops omap_usb2_pm_ops = { ...@@ -297,14 +358,6 @@ static const struct dev_pm_ops omap_usb2_pm_ops = {
#define DEV_PM_OPS NULL #define DEV_PM_OPS NULL
#endif #endif
#ifdef CONFIG_OF
static const struct of_device_id omap_usb2_id_table[] = {
{ .compatible = "ti,omap-usb2" },
{}
};
MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
#endif
static struct platform_driver omap_usb2_driver = { static struct platform_driver omap_usb2_driver = {
.probe = omap_usb2_probe, .probe = omap_usb2_probe,
.remove = omap_usb2_remove, .remove = omap_usb2_remove,
......
/*
* Samsung SoC USB 1.1/2.0 PHY driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Kamil Debski <k.debski@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/clk.h>
#include <linux/mfd/syscon.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>
#include "phy-samsung-usb2.h"
static int samsung_usb2_phy_power_on(struct phy *phy)
{
struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
struct samsung_usb2_phy_driver *drv = inst->drv;
int ret;
dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
inst->cfg->label);
ret = clk_prepare_enable(drv->clk);
if (ret)
goto err_main_clk;
ret = clk_prepare_enable(drv->ref_clk);
if (ret)
goto err_instance_clk;
if (inst->cfg->power_on) {
spin_lock(&drv->lock);
ret = inst->cfg->power_on(inst);
spin_unlock(&drv->lock);
}
return 0;
err_instance_clk:
clk_disable_unprepare(drv->clk);
err_main_clk:
return ret;
}
static int samsung_usb2_phy_power_off(struct phy *phy)
{
struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
struct samsung_usb2_phy_driver *drv = inst->drv;
int ret = 0;
dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
inst->cfg->label);
if (inst->cfg->power_off) {
spin_lock(&drv->lock);
ret = inst->cfg->power_off(inst);
spin_unlock(&drv->lock);
}
clk_disable_unprepare(drv->ref_clk);
clk_disable_unprepare(drv->clk);
return ret;
}
static struct phy_ops samsung_usb2_phy_ops = {
.power_on = samsung_usb2_phy_power_on,
.power_off = samsung_usb2_phy_power_off,
.owner = THIS_MODULE,
};
static struct phy *samsung_usb2_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct samsung_usb2_phy_driver *drv;
drv = dev_get_drvdata(dev);
if (!drv)
return ERR_PTR(-EINVAL);
if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
return ERR_PTR(-ENODEV);
return drv->instances[args->args[0]].phy;
}
static const struct of_device_id samsung_usb2_phy_of_match[] = {
#ifdef CONFIG_PHY_EXYNOS4210_USB2
{
.compatible = "samsung,exynos4210-usb2-phy",
.data = &exynos4210_usb2_phy_config,
},
#endif
#ifdef CONFIG_PHY_EXYNOS4X12_USB2
{
.compatible = "samsung,exynos4x12-usb2-phy",
.data = &exynos4x12_usb2_phy_config,
},
#endif
#ifdef CONFIG_PHY_EXYNOS5250_USB2
{
.compatible = "samsung,exynos5250-usb2-phy",
.data = &exynos5250_usb2_phy_config,
},
#endif
{ },
};
static int samsung_usb2_phy_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
const struct samsung_usb2_phy_config *cfg;
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
struct resource *mem;
struct samsung_usb2_phy_driver *drv;
int i, ret;
if (!pdev->dev.of_node) {
dev_err(dev, "This driver is required to be instantiated from device tree\n");
return -EINVAL;
}
match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node);
if (!match) {
dev_err(dev, "of_match_node() failed\n");
return -EINVAL;
}
cfg = match->data;
drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) +
cfg->num_phys * sizeof(struct samsung_usb2_phy_instance),
GFP_KERNEL);
if (!drv)
return -ENOMEM;
dev_set_drvdata(dev, drv);
spin_lock_init(&drv->lock);
drv->cfg = cfg;
drv->dev = dev;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
drv->reg_phy = devm_ioremap_resource(dev, mem);
if (IS_ERR(drv->reg_phy)) {
dev_err(dev, "Failed to map register memory (phy)\n");
return PTR_ERR(drv->reg_phy);
}
drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"samsung,pmureg-phandle");
if (IS_ERR(drv->reg_pmu)) {
dev_err(dev, "Failed to map PMU registers (via syscon)\n");
return PTR_ERR(drv->reg_pmu);
}
if (drv->cfg->has_mode_switch) {
drv->reg_sys = syscon_regmap_lookup_by_phandle(
pdev->dev.of_node, "samsung,sysreg-phandle");
if (IS_ERR(drv->reg_sys)) {
dev_err(dev, "Failed to map system registers (via syscon)\n");
return PTR_ERR(drv->reg_sys);
}
}
drv->clk = devm_clk_get(dev, "phy");
if (IS_ERR(drv->clk)) {
dev_err(dev, "Failed to get clock of phy controller\n");
return PTR_ERR(drv->clk);
}
drv->ref_clk = devm_clk_get(dev, "ref");
if (IS_ERR(drv->ref_clk)) {
dev_err(dev, "Failed to get reference clock for the phy controller\n");
return PTR_ERR(drv->ref_clk);
}
drv->ref_rate = clk_get_rate(drv->ref_clk);
if (drv->cfg->rate_to_clk) {
ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val);
if (ret)
return ret;
}
for (i = 0; i < drv->cfg->num_phys; i++) {
char *label = drv->cfg->phys[i].label;
struct samsung_usb2_phy_instance *p = &drv->instances[i];
dev_dbg(dev, "Creating phy \"%s\"\n", label);
p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
if (IS_ERR(p->phy)) {
dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
label);
return PTR_ERR(p->phy);
}
p->cfg = &drv->cfg->phys[i];
p->drv = drv;
phy_set_bus_width(p->phy, 8);
phy_set_drvdata(p->phy, p);
}
phy_provider = devm_of_phy_provider_register(dev,
samsung_usb2_phy_xlate);
if (IS_ERR(phy_provider)) {
dev_err(drv->dev, "Failed to register phy provider\n");
return PTR_ERR(phy_provider);
}
return 0;
}
static struct platform_driver samsung_usb2_phy_driver = {
.probe = samsung_usb2_phy_probe,
.driver = {
.of_match_table = samsung_usb2_phy_of_match,
.name = "samsung-usb2-phy",
.owner = THIS_MODULE,
}
};
module_platform_driver(samsung_usb2_phy_driver);
MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:samsung-usb2-phy");
/*
* Samsung SoC USB 1.1/2.0 PHY driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Kamil Debski <k.debski@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.
*/
#ifndef _PHY_EXYNOS_USB2_H
#define _PHY_EXYNOS_USB2_H
#include <linux/clk.h>
#include <linux/phy/phy.h>
#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#define KHZ 1000
#define MHZ (KHZ * KHZ)
struct samsung_usb2_phy_driver;
struct samsung_usb2_phy_instance;
struct samsung_usb2_phy_config;
struct samsung_usb2_phy_instance {
const struct samsung_usb2_common_phy *cfg;
struct phy *phy;
struct samsung_usb2_phy_driver *drv;
bool enabled;
};
struct samsung_usb2_phy_driver {
const struct samsung_usb2_phy_config *cfg;
struct clk *clk;
struct clk *ref_clk;
unsigned long ref_rate;
u32 ref_reg_val;
struct device *dev;
void __iomem *reg_phy;
struct regmap *reg_pmu;
struct regmap *reg_sys;
spinlock_t lock;
struct samsung_usb2_phy_instance instances[0];
};
struct samsung_usb2_common_phy {
int (*power_on)(struct samsung_usb2_phy_instance *);
int (*power_off)(struct samsung_usb2_phy_instance *);
unsigned int id;
char *label;
};
struct samsung_usb2_phy_config {
const struct samsung_usb2_common_phy *phys;
int (*rate_to_clk)(unsigned long, u32 *);
unsigned int num_phys;
bool has_mode_switch;
};
extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config;
#endif
/*
* Allwinner sun4i USB phy driver
*
* Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
*
* Based on code from
* Allwinner Technology Co., Ltd. <www.allwinnertech.com>
*
* Modelled after: 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#define REG_ISCR 0x00
#define REG_PHYCTL 0x04
#define REG_PHYBIST 0x08
#define REG_PHYTUNE 0x0c
#define PHYCTL_DATA BIT(7)
#define SUNXI_AHB_ICHR8_EN BIT(10)
#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
#define SUNXI_ULPI_BYPASS_EN BIT(0)
/* Common Control Bits for Both PHYs */
#define PHY_PLL_BW 0x03
#define PHY_RES45_CAL_EN 0x0c
/* Private Control Bits for Each PHY */
#define PHY_TX_AMPLITUDE_TUNE 0x20
#define PHY_TX_SLEWRATE_TUNE 0x22
#define PHY_VBUSVALID_TH_SEL 0x25
#define PHY_PULLUP_RES_SEL 0x27
#define PHY_OTG_FUNC_EN 0x28
#define PHY_VBUS_DET_EN 0x29
#define PHY_DISCON_TH_SEL 0x2a
#define MAX_PHYS 3
struct sun4i_usb_phy_data {
struct clk *clk;
void __iomem *base;
struct mutex mutex;
int num_phys;
u32 disc_thresh;
struct sun4i_usb_phy {
struct phy *phy;
void __iomem *pmu;
struct regulator *vbus;
struct reset_control *reset;
int index;
} phys[MAX_PHYS];
};
#define to_sun4i_usb_phy_data(phy) \
container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
int len)
{
struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
u32 temp, usbc_bit = BIT(phy->index * 2);
int i;
mutex_lock(&phy_data->mutex);
for (i = 0; i < len; i++) {
temp = readl(phy_data->base + REG_PHYCTL);
/* clear the address portion */
temp &= ~(0xff << 8);
/* set the address */
temp |= ((addr + i) << 8);
writel(temp, phy_data->base + REG_PHYCTL);
/* set the data bit and clear usbc bit*/
temp = readb(phy_data->base + REG_PHYCTL);
if (data & 0x1)
temp |= PHYCTL_DATA;
else
temp &= ~PHYCTL_DATA;
temp &= ~usbc_bit;
writeb(temp, phy_data->base + REG_PHYCTL);
/* pulse usbc_bit */
temp = readb(phy_data->base + REG_PHYCTL);
temp |= usbc_bit;
writeb(temp, phy_data->base + REG_PHYCTL);
temp = readb(phy_data->base + REG_PHYCTL);
temp &= ~usbc_bit;
writeb(temp, phy_data->base + REG_PHYCTL);
data >>= 1;
}
mutex_unlock(&phy_data->mutex);
}
static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
{
u32 bits, reg_value;
if (!phy->pmu)
return;
bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
reg_value = readl(phy->pmu);
if (enable)
reg_value |= bits;
else
reg_value &= ~bits;
writel(reg_value, phy->pmu);
}
static int sun4i_usb_phy_init(struct phy *_phy)
{
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
int ret;
ret = clk_prepare_enable(data->clk);
if (ret)
return ret;
ret = reset_control_deassert(phy->reset);
if (ret) {
clk_disable_unprepare(data->clk);
return ret;
}
/* Adjust PHY's magnitude and rate */
sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
/* Disconnect threshold adjustment */
sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
sun4i_usb_phy_passby(phy, 1);
return 0;
}
static int sun4i_usb_phy_exit(struct phy *_phy)
{
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
sun4i_usb_phy_passby(phy, 0);
reset_control_assert(phy->reset);
clk_disable_unprepare(data->clk);
return 0;
}
static int sun4i_usb_phy_power_on(struct phy *_phy)
{
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
int ret = 0;
if (phy->vbus)
ret = regulator_enable(phy->vbus);
return ret;
}
static int sun4i_usb_phy_power_off(struct phy *_phy)
{
struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
if (phy->vbus)
regulator_disable(phy->vbus);
return 0;
}
static struct phy_ops sun4i_usb_phy_ops = {
.init = sun4i_usb_phy_init,
.exit = sun4i_usb_phy_exit,
.power_on = sun4i_usb_phy_power_on,
.power_off = sun4i_usb_phy_power_off,
.owner = THIS_MODULE,
};
static struct phy *sun4i_usb_phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
return ERR_PTR(-ENODEV);
return data->phys[args->args[0]].phy;
}
static int sun4i_usb_phy_probe(struct platform_device *pdev)
{
struct sun4i_usb_phy_data *data;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
void __iomem *pmu = NULL;
struct phy_provider *phy_provider;
struct reset_control *reset;
struct regulator *vbus;
struct resource *res;
struct phy *phy;
char name[16];
int i;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
mutex_init(&data->mutex);
if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
data->num_phys = 2;
else
data->num_phys = 3;
if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy"))
data->disc_thresh = 3;
else
data->disc_thresh = 2;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
data->base = devm_ioremap_resource(dev, res);
if (IS_ERR(data->base))
return PTR_ERR(data->base);
data->clk = devm_clk_get(dev, "usb_phy");
if (IS_ERR(data->clk)) {
dev_err(dev, "could not get usb_phy clock\n");
return PTR_ERR(data->clk);
}
/* Skip 0, 0 is the phy for otg which is not yet supported. */
for (i = 1; i < data->num_phys; i++) {
snprintf(name, sizeof(name), "usb%d_vbus", i);
vbus = devm_regulator_get_optional(dev, name);
if (IS_ERR(vbus)) {
if (PTR_ERR(vbus) == -EPROBE_DEFER)
return -EPROBE_DEFER;
vbus = NULL;
}
snprintf(name, sizeof(name), "usb%d_reset", i);
reset = devm_reset_control_get(dev, name);
if (IS_ERR(reset)) {
dev_err(dev, "failed to get reset %s\n", name);
return PTR_ERR(reset);
}
if (i) { /* No pmu for usbc0 */
snprintf(name, sizeof(name), "pmu%d", i);
res = platform_get_resource_byname(pdev,
IORESOURCE_MEM, name);
pmu = devm_ioremap_resource(dev, res);
if (IS_ERR(pmu))
return PTR_ERR(pmu);
}
phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create PHY %d\n", i);
return PTR_ERR(phy);
}
data->phys[i].phy = phy;
data->phys[i].pmu = pmu;
data->phys[i].vbus = vbus;
data->phys[i].reset = reset;
data->phys[i].index = i;
phy_set_drvdata(phy, &data->phys[i]);
}
dev_set_drvdata(dev, data);
phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
}
static const struct of_device_id sun4i_usb_phy_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-usb-phy" },
{ .compatible = "allwinner,sun5i-a13-usb-phy" },
{ .compatible = "allwinner,sun7i-a20-usb-phy" },
{ },
};
MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
static struct platform_driver sun4i_usb_phy_driver = {
.probe = sun4i_usb_phy_probe,
.driver = {
.of_match_table = sun4i_usb_phy_of_match,
.name = "sun4i-usb-phy",
.owner = THIS_MODULE,
}
};
module_platform_driver(sun4i_usb_phy_driver);
MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_LICENSE("GPL v2");
...@@ -338,7 +338,7 @@ static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode) ...@@ -338,7 +338,7 @@ static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode)
dev_err(twl->dev, "unsupported T2 transceiver mode %d\n", dev_err(twl->dev, "unsupported T2 transceiver mode %d\n",
mode); mode);
break; break;
}; }
} }
static void twl4030_i2c_access(struct twl4030_usb *twl, int on) static void twl4030_i2c_access(struct twl4030_usb *twl, int on)
...@@ -661,7 +661,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) ...@@ -661,7 +661,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
struct phy_provider *phy_provider; struct phy_provider *phy_provider;
struct phy_init_data *init_data = NULL; struct phy_init_data *init_data = NULL;
twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL); twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
if (!twl) if (!twl)
return -ENOMEM; return -ENOMEM;
...@@ -676,7 +676,7 @@ static int twl4030_usb_probe(struct platform_device *pdev) ...@@ -676,7 +676,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
} }
otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL); otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
if (!otg) if (!otg)
return -ENOMEM; return -ENOMEM;
......
This diff is collapsed.
...@@ -2,10 +2,6 @@ ...@@ -2,10 +2,6 @@
# USB device configuration # USB device configuration
# #
# These are unused now, remove them once they are no longer selected
config USB_ARCH_HAS_OHCI
bool
config USB_OHCI_BIG_ENDIAN_DESC config USB_OHCI_BIG_ENDIAN_DESC
bool bool
...@@ -17,18 +13,12 @@ config USB_OHCI_LITTLE_ENDIAN ...@@ -17,18 +13,12 @@ config USB_OHCI_LITTLE_ENDIAN
default n if STB03xxx || PPC_MPC52xx default n if STB03xxx || PPC_MPC52xx
default y default y
config USB_ARCH_HAS_EHCI
bool
config USB_EHCI_BIG_ENDIAN_MMIO config USB_EHCI_BIG_ENDIAN_MMIO
bool bool
config USB_EHCI_BIG_ENDIAN_DESC config USB_EHCI_BIG_ENDIAN_DESC
bool bool
config USB_ARCH_HAS_XHCI
bool
menuconfig USB_SUPPORT menuconfig USB_SUPPORT
bool "USB support" bool "USB support"
depends on HAS_IOMEM depends on HAS_IOMEM
......
...@@ -10,6 +10,7 @@ ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o ...@@ -10,6 +10,7 @@ ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG) += debug.o
# Glue/Bridge layers go here # Glue/Bridge layers go here
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o
# PCI doesn't provide stubs, need to check # PCI doesn't provide stubs, need to check
ifneq ($(CONFIG_PCI),) ifneq ($(CONFIG_PCI),)
......
...@@ -50,12 +50,14 @@ ...@@ -50,12 +50,14 @@
#define PORTSC_PTC (0x0FUL << 16) #define PORTSC_PTC (0x0FUL << 16)
#define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23)) #define PORTSC_PHCD(d) ((d) ? BIT(22) : BIT(23))
/* PTS and PTW for non lpm version only */ /* PTS and PTW for non lpm version only */
#define PORTSC_PFSC BIT(24)
#define PORTSC_PTS(d) \ #define PORTSC_PTS(d) \
(u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0)) (u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0))
#define PORTSC_PTW BIT(28) #define PORTSC_PTW BIT(28)
#define PORTSC_STS BIT(29) #define PORTSC_STS BIT(29)
/* DEVLC */ /* DEVLC */
#define DEVLC_PFSC BIT(23)
#define DEVLC_PSPD (0x03UL << 25) #define DEVLC_PSPD (0x03UL << 25)
#define DEVLC_PSPD_HS (0x02UL << 25) #define DEVLC_PSPD_HS (0x02UL << 25)
#define DEVLC_PTW BIT(27) #define DEVLC_PTW BIT(27)
......
...@@ -196,8 +196,6 @@ struct ci_hdrc { ...@@ -196,8 +196,6 @@ struct ci_hdrc {
struct ci_hdrc_platform_data *platdata; struct ci_hdrc_platform_data *platdata;
int vbus_active; int vbus_active;
/* FIXME: some day, we'll not use global phy */
bool global_phy;
struct usb_phy *transceiver; struct usb_phy *transceiver;
struct usb_hcd *hcd; struct usb_hcd *hcd;
struct dentry *debugfs; struct dentry *debugfs;
......
...@@ -96,7 +96,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) ...@@ -96,7 +96,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
{ {
struct ci_hdrc_imx_data *data; struct ci_hdrc_imx_data *data;
struct ci_hdrc_platform_data pdata = { struct ci_hdrc_platform_data pdata = {
.name = "ci_hdrc_imx", .name = dev_name(&pdev->dev),
.capoffset = DEF_CAPOFFSET, .capoffset = DEF_CAPOFFSET,
.flags = CI_HDRC_REQUIRE_TRANSCEIVER | .flags = CI_HDRC_REQUIRE_TRANSCEIVER |
CI_HDRC_DISABLE_STREAMING, CI_HDRC_DISABLE_STREAMING,
......
/*
* Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
*
* 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.
*
* Based off drivers/usb/chipidea/ci_hdrc_msm.c
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/usb/gadget.h>
#include <linux/usb/chipidea.h>
#include "ci.h"
static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = {
.name = "ci_hdrc_zevio",
.flags = CI_HDRC_REGS_SHARED,
.capoffset = DEF_CAPOFFSET,
};
static int ci_hdrc_zevio_probe(struct platform_device *pdev)
{
struct platform_device *ci_pdev;
dev_dbg(&pdev->dev, "ci_hdrc_zevio_probe\n");
ci_pdev = ci_hdrc_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
&ci_hdrc_zevio_platdata);
if (IS_ERR(ci_pdev)) {
dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
return PTR_ERR(ci_pdev);
}
platform_set_drvdata(pdev, ci_pdev);
return 0;
}
static int ci_hdrc_zevio_remove(struct platform_device *pdev)
{
struct platform_device *ci_pdev = platform_get_drvdata(pdev);
ci_hdrc_remove_device(ci_pdev);
return 0;
}
static const struct of_device_id ci_hdrc_zevio_dt_ids[] = {
{ .compatible = "lsi,zevio-usb", },
{ /* sentinel */ }
};
static struct platform_driver ci_hdrc_zevio_driver = {
.probe = ci_hdrc_zevio_probe,
.remove = ci_hdrc_zevio_remove,
.driver = {
.name = "zevio_usb",
.owner = THIS_MODULE,
.of_match_table = ci_hdrc_zevio_dt_ids,
},
};
MODULE_DEVICE_TABLE(of, ci_hdrc_zevio_dt_ids);
module_platform_driver(ci_hdrc_zevio_driver);
MODULE_LICENSE("GPL v2");
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
#include <linux/usb/otg.h> #include <linux/usb/otg.h>
#include <linux/usb/chipidea.h> #include <linux/usb/chipidea.h>
#include <linux/usb/of.h> #include <linux/usb/of.h>
#include <linux/of.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
...@@ -298,6 +299,13 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode) ...@@ -298,6 +299,13 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode)
if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING) if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING)
hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS); hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) {
if (ci->hw_bank.lpm)
hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC);
else
hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC);
}
/* USBMODE should be configured step by step */ /* USBMODE should be configured step by step */
hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE); hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
hw_write(ci, OP_USBMODE, USBMODE_CM, mode); hw_write(ci, OP_USBMODE, USBMODE_CM, mode);
...@@ -412,6 +420,9 @@ static int ci_get_platdata(struct device *dev, ...@@ -412,6 +420,9 @@ static int ci_get_platdata(struct device *dev,
} }
} }
if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL)
platdata->flags |= CI_HDRC_FORCE_FULLSPEED;
return 0; return 0;
} }
...@@ -496,33 +507,6 @@ static void ci_get_otg_capable(struct ci_hdrc *ci) ...@@ -496,33 +507,6 @@ 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) static int ci_hdrc_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
...@@ -532,7 +516,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -532,7 +516,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
int ret; int ret;
enum usb_dr_mode dr_mode; enum usb_dr_mode dr_mode;
if (!dev->platform_data) { if (!dev_get_platdata(dev)) {
dev_err(dev, "platform data missing\n"); dev_err(dev, "platform data missing\n");
return -ENODEV; return -ENODEV;
} }
...@@ -549,7 +533,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -549,7 +533,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
} }
ci->dev = dev; ci->dev = dev;
ci->platdata = dev->platform_data; ci->platdata = dev_get_platdata(dev);
ci->imx28_write_fix = !!(ci->platdata->flags & ci->imx28_write_fix = !!(ci->platdata->flags &
CI_HDRC_IMX28_WRITE_FIX); CI_HDRC_IMX28_WRITE_FIX);
...@@ -561,7 +545,26 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -561,7 +545,26 @@ static int ci_hdrc_probe(struct platform_device *pdev)
hw_phymode_configure(ci); hw_phymode_configure(ci);
ret = ci_usb_phy_init(ci); if (ci->platdata->phy)
ci->transceiver = ci->platdata->phy;
else
ci->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
if (IS_ERR(ci->transceiver)) {
ret = PTR_ERR(ci->transceiver);
/*
* if -ENXIO is returned, it means PHY layer wasn't
* enabled, so it makes no sense to return -EPROBE_DEFER
* in that case, since no PHY driver will ever probe.
*/
if (ret == -ENXIO)
return ret;
dev_err(dev, "no usb2 phy configured\n");
return -EPROBE_DEFER;
}
ret = usb_phy_init(ci->transceiver);
if (ret) { if (ret) {
dev_err(dev, "unable to init phy: %d\n", ret); dev_err(dev, "unable to init phy: %d\n", ret);
return ret; return ret;
...@@ -572,8 +575,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -572,8 +575,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ci->irq = platform_get_irq(pdev, 0); ci->irq = platform_get_irq(pdev, 0);
if (ci->irq < 0) { if (ci->irq < 0) {
dev_err(dev, "missing IRQ\n"); dev_err(dev, "missing IRQ\n");
ret = -ENODEV; ret = ci->irq;
goto destroy_phy; goto deinit_phy;
} }
ci_get_otg_capable(ci); ci_get_otg_capable(ci);
...@@ -590,23 +593,12 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -590,23 +593,12 @@ static int ci_hdrc_probe(struct platform_device *pdev)
ret = ci_hdrc_gadget_init(ci); ret = ci_hdrc_gadget_init(ci);
if (ret) if (ret)
dev_info(dev, "doesn't support gadget\n"); 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]) { if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
dev_err(dev, "no supported roles\n"); dev_err(dev, "no supported roles\n");
ret = -ENODEV; ret = -ENODEV;
goto destroy_phy; goto deinit_phy;
} }
if (ci->is_otg) { if (ci->is_otg) {
...@@ -663,8 +655,8 @@ static int ci_hdrc_probe(struct platform_device *pdev) ...@@ -663,8 +655,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
free_irq(ci->irq, ci); free_irq(ci->irq, ci);
stop: stop:
ci_role_destroy(ci); ci_role_destroy(ci);
destroy_phy: deinit_phy:
ci_usb_phy_destroy(ci); usb_phy_shutdown(ci->transceiver);
return ret; return ret;
} }
...@@ -677,7 +669,8 @@ static int ci_hdrc_remove(struct platform_device *pdev) ...@@ -677,7 +669,8 @@ static int ci_hdrc_remove(struct platform_device *pdev)
free_irq(ci->irq, ci); free_irq(ci->irq, ci);
ci_role_destroy(ci); ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true); ci_hdrc_enter_lpm(ci, true);
ci_usb_phy_destroy(ci); usb_phy_shutdown(ci->transceiver);
kfree(ci->hw_bank.regmap);
return 0; return 0;
} }
......
...@@ -177,19 +177,6 @@ static int hw_ep_get_halt(struct ci_hdrc *ci, int num, int dir) ...@@ -177,19 +177,6 @@ static int hw_ep_get_halt(struct ci_hdrc *ci, int num, int dir)
return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0; return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0;
} }
/**
* hw_test_and_clear_setup_status: test & clear setup status (execute without
* interruption)
* @n: endpoint number
*
* This function returns setup status
*/
static int hw_test_and_clear_setup_status(struct ci_hdrc *ci, int n)
{
n = ep_to_bit(ci, n);
return hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(n));
}
/** /**
* hw_ep_prime: primes endpoint (execute without interruption) * hw_ep_prime: primes endpoint (execute without interruption)
* @num: endpoint number * @num: endpoint number
...@@ -961,6 +948,156 @@ __acquires(hwep->lock) ...@@ -961,6 +948,156 @@ __acquires(hwep->lock)
return retval; return retval;
} }
/**
* isr_setup_packet_handler: setup packet handler
* @ci: UDC descriptor
*
* This function handles setup packet
*/
static void isr_setup_packet_handler(struct ci_hdrc *ci)
__releases(ci->lock)
__acquires(ci->lock)
{
struct ci_hw_ep *hwep = &ci->ci_hw_ep[0];
struct usb_ctrlrequest req;
int type, num, dir, err = -EINVAL;
u8 tmode = 0;
/*
* Flush data and handshake transactions of previous
* setup packet.
*/
_ep_nuke(ci->ep0out);
_ep_nuke(ci->ep0in);
/* read_setup_packet */
do {
hw_test_and_set_setup_guard(ci);
memcpy(&req, &hwep->qh.ptr->setup, sizeof(req));
} while (!hw_test_and_clear_setup_guard(ci));
type = req.bRequestType;
ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
switch (req.bRequest) {
case USB_REQ_CLEAR_FEATURE:
if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
le16_to_cpu(req.wValue) ==
USB_ENDPOINT_HALT) {
if (req.wLength != 0)
break;
num = le16_to_cpu(req.wIndex);
dir = num & USB_ENDPOINT_DIR_MASK;
num &= USB_ENDPOINT_NUMBER_MASK;
if (dir) /* TX */
num += ci->hw_ep_max / 2;
if (!ci->ci_hw_ep[num].wedge) {
spin_unlock(&ci->lock);
err = usb_ep_clear_halt(
&ci->ci_hw_ep[num].ep);
spin_lock(&ci->lock);
if (err)
break;
}
err = isr_setup_status_phase(ci);
} else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
le16_to_cpu(req.wValue) ==
USB_DEVICE_REMOTE_WAKEUP) {
if (req.wLength != 0)
break;
ci->remote_wakeup = 0;
err = isr_setup_status_phase(ci);
} else {
goto delegate;
}
break;
case USB_REQ_GET_STATUS:
if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
type != (USB_DIR_IN|USB_RECIP_INTERFACE))
goto delegate;
if (le16_to_cpu(req.wLength) != 2 ||
le16_to_cpu(req.wValue) != 0)
break;
err = isr_get_status_response(ci, &req);
break;
case USB_REQ_SET_ADDRESS:
if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
goto delegate;
if (le16_to_cpu(req.wLength) != 0 ||
le16_to_cpu(req.wIndex) != 0)
break;
ci->address = (u8)le16_to_cpu(req.wValue);
ci->setaddr = true;
err = isr_setup_status_phase(ci);
break;
case USB_REQ_SET_FEATURE:
if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
le16_to_cpu(req.wValue) ==
USB_ENDPOINT_HALT) {
if (req.wLength != 0)
break;
num = le16_to_cpu(req.wIndex);
dir = num & USB_ENDPOINT_DIR_MASK;
num &= USB_ENDPOINT_NUMBER_MASK;
if (dir) /* TX */
num += ci->hw_ep_max / 2;
spin_unlock(&ci->lock);
err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep);
spin_lock(&ci->lock);
if (!err)
isr_setup_status_phase(ci);
} else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
if (req.wLength != 0)
break;
switch (le16_to_cpu(req.wValue)) {
case USB_DEVICE_REMOTE_WAKEUP:
ci->remote_wakeup = 1;
err = isr_setup_status_phase(ci);
break;
case USB_DEVICE_TEST_MODE:
tmode = le16_to_cpu(req.wIndex) >> 8;
switch (tmode) {
case TEST_J:
case TEST_K:
case TEST_SE0_NAK:
case TEST_PACKET:
case TEST_FORCE_EN:
ci->test_mode = tmode;
err = isr_setup_status_phase(
ci);
break;
default:
break;
}
default:
goto delegate;
}
} else {
goto delegate;
}
break;
default:
delegate:
if (req.wLength == 0) /* no data phase */
ci->ep0_dir = TX;
spin_unlock(&ci->lock);
err = ci->driver->setup(&ci->gadget, &req);
spin_lock(&ci->lock);
break;
}
if (err < 0) {
spin_unlock(&ci->lock);
if (usb_ep_set_halt(&hwep->ep))
dev_err(ci->dev, "error: ep_set_halt\n");
spin_lock(&ci->lock);
}
}
/** /**
* isr_tr_complete_handler: transaction complete interrupt handler * isr_tr_complete_handler: transaction complete interrupt handler
* @ci: UDC descriptor * @ci: UDC descriptor
...@@ -972,12 +1109,10 @@ __releases(ci->lock) ...@@ -972,12 +1109,10 @@ __releases(ci->lock)
__acquires(ci->lock) __acquires(ci->lock)
{ {
unsigned i; unsigned i;
u8 tmode = 0; int err;
for (i = 0; i < ci->hw_ep_max; i++) { for (i = 0; i < ci->hw_ep_max; i++) {
struct ci_hw_ep *hwep = &ci->ci_hw_ep[i]; struct ci_hw_ep *hwep = &ci->ci_hw_ep[i];
int type, num, dir, err = -EINVAL;
struct usb_ctrlrequest req;
if (hwep->ep.desc == NULL) if (hwep->ep.desc == NULL)
continue; /* not configured */ continue; /* not configured */
...@@ -997,148 +1132,10 @@ __acquires(ci->lock) ...@@ -997,148 +1132,10 @@ __acquires(ci->lock)
} }
} }
if (hwep->type != USB_ENDPOINT_XFER_CONTROL || /* Only handle setup packet below */
!hw_test_and_clear_setup_status(ci, i)) if (i == 0 &&
continue; hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0)))
isr_setup_packet_handler(ci);
if (i != 0) {
dev_warn(ci->dev, "ctrl traffic at endpoint %d\n", i);
continue;
}
/*
* Flush data and handshake transactions of previous
* setup packet.
*/
_ep_nuke(ci->ep0out);
_ep_nuke(ci->ep0in);
/* read_setup_packet */
do {
hw_test_and_set_setup_guard(ci);
memcpy(&req, &hwep->qh.ptr->setup, sizeof(req));
} while (!hw_test_and_clear_setup_guard(ci));
type = req.bRequestType;
ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
switch (req.bRequest) {
case USB_REQ_CLEAR_FEATURE:
if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
le16_to_cpu(req.wValue) ==
USB_ENDPOINT_HALT) {
if (req.wLength != 0)
break;
num = le16_to_cpu(req.wIndex);
dir = num & USB_ENDPOINT_DIR_MASK;
num &= USB_ENDPOINT_NUMBER_MASK;
if (dir) /* TX */
num += ci->hw_ep_max/2;
if (!ci->ci_hw_ep[num].wedge) {
spin_unlock(&ci->lock);
err = usb_ep_clear_halt(
&ci->ci_hw_ep[num].ep);
spin_lock(&ci->lock);
if (err)
break;
}
err = isr_setup_status_phase(ci);
} else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
le16_to_cpu(req.wValue) ==
USB_DEVICE_REMOTE_WAKEUP) {
if (req.wLength != 0)
break;
ci->remote_wakeup = 0;
err = isr_setup_status_phase(ci);
} else {
goto delegate;
}
break;
case USB_REQ_GET_STATUS:
if (type != (USB_DIR_IN|USB_RECIP_DEVICE) &&
type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
type != (USB_DIR_IN|USB_RECIP_INTERFACE))
goto delegate;
if (le16_to_cpu(req.wLength) != 2 ||
le16_to_cpu(req.wValue) != 0)
break;
err = isr_get_status_response(ci, &req);
break;
case USB_REQ_SET_ADDRESS:
if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
goto delegate;
if (le16_to_cpu(req.wLength) != 0 ||
le16_to_cpu(req.wIndex) != 0)
break;
ci->address = (u8)le16_to_cpu(req.wValue);
ci->setaddr = true;
err = isr_setup_status_phase(ci);
break;
case USB_REQ_SET_FEATURE:
if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
le16_to_cpu(req.wValue) ==
USB_ENDPOINT_HALT) {
if (req.wLength != 0)
break;
num = le16_to_cpu(req.wIndex);
dir = num & USB_ENDPOINT_DIR_MASK;
num &= USB_ENDPOINT_NUMBER_MASK;
if (dir) /* TX */
num += ci->hw_ep_max/2;
spin_unlock(&ci->lock);
err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep);
spin_lock(&ci->lock);
if (!err)
isr_setup_status_phase(ci);
} else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
if (req.wLength != 0)
break;
switch (le16_to_cpu(req.wValue)) {
case USB_DEVICE_REMOTE_WAKEUP:
ci->remote_wakeup = 1;
err = isr_setup_status_phase(ci);
break;
case USB_DEVICE_TEST_MODE:
tmode = le16_to_cpu(req.wIndex) >> 8;
switch (tmode) {
case TEST_J:
case TEST_K:
case TEST_SE0_NAK:
case TEST_PACKET:
case TEST_FORCE_EN:
ci->test_mode = tmode;
err = isr_setup_status_phase(
ci);
break;
default:
break;
}
default:
goto delegate;
}
} else {
goto delegate;
}
break;
default:
delegate:
if (req.wLength == 0) /* no data phase */
ci->ep0_dir = TX;
spin_unlock(&ci->lock);
err = ci->driver->setup(&ci->gadget, &req);
spin_lock(&ci->lock);
break;
}
if (err < 0) {
spin_unlock(&ci->lock);
if (usb_ep_set_halt(&hwep->ep))
dev_err(ci->dev, "error: ep_set_halt\n");
spin_lock(&ci->lock);
}
} }
} }
...@@ -1193,6 +1190,11 @@ static int ep_enable(struct usb_ep *ep, ...@@ -1193,6 +1190,11 @@ static int ep_enable(struct usb_ep *ep,
hwep->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE); /* needed? */ hwep->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE); /* needed? */
if (hwep->num != 0 && hwep->type == USB_ENDPOINT_XFER_CONTROL) {
dev_err(hwep->ci->dev, "Set control xfer at non-ep0\n");
retval = -EINVAL;
}
/* /*
* Enable endpoints in the HW other than ep0 as ep0 * Enable endpoints in the HW other than ep0 as ep0
* is always enabled * is always enabled
...@@ -1837,12 +1839,6 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci) ...@@ -1837,12 +1839,6 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
dma_pool_destroy(ci->td_pool); dma_pool_destroy(ci->td_pool);
dma_pool_destroy(ci->qh_pool); dma_pool_destroy(ci->qh_pool);
if (ci->transceiver) {
otg_set_peripheral(ci->transceiver->otg, NULL);
if (ci->global_phy)
usb_put_phy(ci->transceiver);
}
} }
static int udc_id_switch_for_device(struct ci_hdrc *ci) static int udc_id_switch_for_device(struct ci_hdrc *ci)
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#define USB_MAXALTSETTING 128 /* Hard limit */ #define USB_MAXALTSETTING 128 /* Hard limit */
#define USB_MAXENDPOINTS 30 /* Hard limit */
#define USB_MAXCONFIG 8 /* Arbitrary limit */ #define USB_MAXCONFIG 8 /* Arbitrary limit */
......
This diff is collapsed.
This diff is collapsed.
...@@ -2049,7 +2049,7 @@ int usb_alloc_streams(struct usb_interface *interface, ...@@ -2049,7 +2049,7 @@ int usb_alloc_streams(struct usb_interface *interface,
{ {
struct usb_hcd *hcd; struct usb_hcd *hcd;
struct usb_device *dev; struct usb_device *dev;
int i; int i, ret;
dev = interface_to_usbdev(interface); dev = interface_to_usbdev(interface);
hcd = bus_to_hcd(dev->bus); hcd = bus_to_hcd(dev->bus);
...@@ -2058,13 +2058,24 @@ int usb_alloc_streams(struct usb_interface *interface, ...@@ -2058,13 +2058,24 @@ int usb_alloc_streams(struct usb_interface *interface,
if (dev->speed != USB_SPEED_SUPER) if (dev->speed != USB_SPEED_SUPER)
return -EINVAL; return -EINVAL;
/* Streams only apply to bulk endpoints. */ for (i = 0; i < num_eps; i++) {
for (i = 0; i < num_eps; i++) /* Streams only apply to bulk endpoints. */
if (!usb_endpoint_xfer_bulk(&eps[i]->desc)) if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
return -EINVAL; return -EINVAL;
/* Re-alloc is not allowed */
if (eps[i]->streams)
return -EINVAL;
}
return hcd->driver->alloc_streams(hcd, dev, eps, num_eps, ret = hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
num_streams, mem_flags); num_streams, mem_flags);
if (ret < 0)
return ret;
for (i = 0; i < num_eps; i++)
eps[i]->streams = ret;
return ret;
} }
EXPORT_SYMBOL_GPL(usb_alloc_streams); EXPORT_SYMBOL_GPL(usb_alloc_streams);
...@@ -2078,8 +2089,7 @@ EXPORT_SYMBOL_GPL(usb_alloc_streams); ...@@ -2078,8 +2089,7 @@ EXPORT_SYMBOL_GPL(usb_alloc_streams);
* Reverts a group of bulk endpoints back to not using stream IDs. * Reverts a group of bulk endpoints back to not using stream IDs.
* Can fail if we are given bad arguments, or HCD is broken. * Can fail if we are given bad arguments, or HCD is broken.
* *
* Return: On success, the number of allocated streams. On failure, a negative * Return: 0 on success. On failure, a negative error code.
* error code.
*/ */
int usb_free_streams(struct usb_interface *interface, int usb_free_streams(struct usb_interface *interface,
struct usb_host_endpoint **eps, unsigned int num_eps, struct usb_host_endpoint **eps, unsigned int num_eps,
...@@ -2087,19 +2097,26 @@ int usb_free_streams(struct usb_interface *interface, ...@@ -2087,19 +2097,26 @@ int usb_free_streams(struct usb_interface *interface,
{ {
struct usb_hcd *hcd; struct usb_hcd *hcd;
struct usb_device *dev; struct usb_device *dev;
int i; int i, ret;
dev = interface_to_usbdev(interface); dev = interface_to_usbdev(interface);
hcd = bus_to_hcd(dev->bus); hcd = bus_to_hcd(dev->bus);
if (dev->speed != USB_SPEED_SUPER) if (dev->speed != USB_SPEED_SUPER)
return -EINVAL; return -EINVAL;
/* Streams only apply to bulk endpoints. */ /* Double-free is not allowed */
for (i = 0; i < num_eps; i++) for (i = 0; i < num_eps; i++)
if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc)) if (!eps[i] || !eps[i]->streams)
return -EINVAL; return -EINVAL;
return hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags); ret = hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
if (ret < 0)
return ret;
for (i = 0; i < num_eps; i++)
eps[i]->streams = 0;
return ret;
} }
EXPORT_SYMBOL_GPL(usb_free_streams); EXPORT_SYMBOL_GPL(usb_free_streams);
......
This diff is collapsed.
...@@ -1293,8 +1293,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) ...@@ -1293,8 +1293,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
struct usb_interface *iface; struct usb_interface *iface;
struct usb_host_interface *alt; struct usb_host_interface *alt;
struct usb_hcd *hcd = bus_to_hcd(dev->bus); struct usb_hcd *hcd = bus_to_hcd(dev->bus);
int ret; int i, ret, manual = 0;
int manual = 0;
unsigned int epaddr; unsigned int epaddr;
unsigned int pipe; unsigned int pipe;
...@@ -1329,6 +1328,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) ...@@ -1329,6 +1328,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
mutex_unlock(hcd->bandwidth_mutex); mutex_unlock(hcd->bandwidth_mutex);
return -ENOMEM; return -ENOMEM;
} }
/* Changing alt-setting also frees any allocated streams */
for (i = 0; i < iface->cur_altsetting->desc.bNumEndpoints; i++)
iface->cur_altsetting->endpoint[i].streams = 0;
ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt); ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
if (ret < 0) { if (ret < 0) {
dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n", dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
......
...@@ -55,7 +55,7 @@ extern int usb_match_one_id_intf(struct usb_device *dev, ...@@ -55,7 +55,7 @@ extern int usb_match_one_id_intf(struct usb_device *dev,
extern int usb_match_device(struct usb_device *dev, extern int usb_match_device(struct usb_device *dev,
const struct usb_device_id *id); const struct usb_device_id *id);
extern void usb_forced_unbind_intf(struct usb_interface *intf); extern void usb_forced_unbind_intf(struct usb_interface *intf);
extern void usb_rebind_intf(struct usb_interface *intf); extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
extern void usb_hub_release_all_ports(struct usb_device *hdev, extern void usb_hub_release_all_ports(struct usb_device *hdev,
struct usb_dev_state *owner); struct usb_dev_state *owner);
......
...@@ -71,6 +71,26 @@ static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg) ...@@ -71,6 +71,26 @@ static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg)
} }
} }
/**
* dwc2_handle_usb_port_intr - handles OTG PRTINT interrupts.
* When the PRTINT interrupt fires, there are certain status bits in the Host
* Port that needs to get cleared.
*
* @hsotg: Programming view of DWC_otg controller
*/
static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
{
u32 hprt0 = readl(hsotg->regs + HPRT0);
if (hprt0 & HPRT0_ENACHG) {
hprt0 &= ~HPRT0_ENA;
writel(hprt0, hsotg->regs + HPRT0);
}
/* Clear interrupt */
writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS);
}
/** /**
* dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message * dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message
* *
...@@ -479,9 +499,8 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) ...@@ -479,9 +499,8 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
if (dwc2_is_device_mode(hsotg)) { if (dwc2_is_device_mode(hsotg)) {
dev_dbg(hsotg->dev, dev_dbg(hsotg->dev,
" --Port interrupt received in Device mode--\n"); " --Port interrupt received in Device mode--\n");
gintsts = GINTSTS_PRTINT; dwc2_handle_usb_port_intr(hsotg);
writel(gintsts, hsotg->regs + GINTSTS); retval = IRQ_HANDLED;
retval = 1;
} }
} }
......
...@@ -975,8 +975,8 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg, ...@@ -975,8 +975,8 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
struct dwc2_qtd *qtd) struct dwc2_qtd *qtd)
{ {
struct dwc2_hcd_urb *urb = qtd->urb; struct dwc2_hcd_urb *urb = qtd->urb;
int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
enum dwc2_halt_status halt_status = DWC2_HC_XFER_COMPLETE; enum dwc2_halt_status halt_status = DWC2_HC_XFER_COMPLETE;
int pipe_type;
int urb_xfer_done; int urb_xfer_done;
if (dbg_hc(chan)) if (dbg_hc(chan))
...@@ -984,6 +984,11 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg, ...@@ -984,6 +984,11 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
"--Host Channel %d Interrupt: Transfer Complete--\n", "--Host Channel %d Interrupt: Transfer Complete--\n",
chnum); chnum);
if (!urb)
goto handle_xfercomp_done;
pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
if (hsotg->core_params->dma_desc_enable > 0) { if (hsotg->core_params->dma_desc_enable > 0) {
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, halt_status); dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, halt_status);
if (pipe_type == USB_ENDPOINT_XFER_ISOC) if (pipe_type == USB_ENDPOINT_XFER_ISOC)
...@@ -1005,9 +1010,6 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg, ...@@ -1005,9 +1010,6 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
} }
} }
if (!urb)
goto handle_xfercomp_done;
/* Update the QTD and URB states */ /* Update the QTD and URB states */
switch (pipe_type) { switch (pipe_type) {
case USB_ENDPOINT_XFER_CONTROL: case USB_ENDPOINT_XFER_CONTROL:
...@@ -1105,7 +1107,7 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg, ...@@ -1105,7 +1107,7 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg,
struct dwc2_qtd *qtd) struct dwc2_qtd *qtd)
{ {
struct dwc2_hcd_urb *urb = qtd->urb; struct dwc2_hcd_urb *urb = qtd->urb;
int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info); int pipe_type;
dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: STALL Received--\n", dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: STALL Received--\n",
chnum); chnum);
...@@ -1119,6 +1121,8 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg, ...@@ -1119,6 +1121,8 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg,
if (!urb) if (!urb)
goto handle_stall_halt; goto handle_stall_halt;
pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
if (pipe_type == USB_ENDPOINT_XFER_CONTROL) if (pipe_type == USB_ENDPOINT_XFER_CONTROL)
dwc2_host_complete(hsotg, qtd, -EPIPE); dwc2_host_complete(hsotg, qtd, -EPIPE);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment