forked from Minki/linux
usb: patches for v3.11 merge window
All function drivers are now converted to our new configfs-based binding. Eventually this will help us getting rid of in-kernel gadget drivers and only keep function drivers in the kernel. MUSB was taught that it needs to be built for host-only and device-only modes too. We had this support long ago but it involved a ridiculous amount of ifdefs. Now we have a much cleaner approach. Samsung Exynos4 platform now implements HSIC support. We're introducing support for AB8540 and AB9540 PHYs. MUSB module reinsertion now works as expected, before we were getting -EBUSY being returned by the resource checks done on driver core. DWC3 now has minimum support for TI's AM437x series of SoCs. OMAP5 USB3 PHY learned one extra DPLL configuration values because that PHY is reused in TI's DRA7xx devices. We're introducing support for Faraday fotg210 UDCs. Last, but not least, the usual set of non-critical fixes and cleanups ranging from usage of platform_{get,set}_drvdata to lock improvements. Signed-of-by: Felipe Balbi <balbi@ti.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.12 (GNU/Linux) iQIcBAABAgAGBQJRuODuAAoJEIaOsuA1yqREEYMP/212PIcMM/niwl2T97l+Ispc EVe8ebg69/t+LjEHmipyw00HvBuGv+6ccJbuU+NBSSi229iIkxXlE+Q7MoywHOZg eSozqiIXIotkNTPg4vT6YfWspyNaoiDrl9TK3KMP9SyctlgxqMdcfke5dqpGpdUP xqYhWCAbZ6uvu6Lq6r3NwX1pMKhXxbnTDCY77YOCb/H8UPlSHSW4nwjAKYvsEWwD RLXn0UKDZF4FRm296ftIHDD8rDazCaQPkkglQejFrqheNpbR7SUkC672veca7xF5 2iaWS62p7SWDHsfzyLpeJwoglHcxRa3E8ZqdT9ALvrimMTm0jVM0pzDSCF2xBpFq UP78YX2S94o/YC8NXfp6GMf5CFSlLDxQ7oahcUpUBVtx5l2v8bfyb2/KOrB6kHBS v8RJqFbcYXHHygaYS0oXGqKg2ScwYeVIenlrk8ByPrfkJqS3v7CKLB0wNrV5ZWyC nnfyMF+bW+M00nb9jKjS+Utni8looKpWdKcmAdP/zPVKDZE5zh5WL2q/zWepWdgP 8nIslvivXmAkNs8wN5ji/E/w9qqkXiYCVkSQXfXPgBLWesaQqBR2geRWduSetKSm AHINjU4+wXkRR0V1HyKzn+b1v5yZ5ksV7n5SXltyXKNeO0IeBDHNBHRVPFqHdgau u2prz3aPvqEFENqgr7z5 =O9AH -----END PGP SIGNATURE----- Merge tag 'usb-for-v3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next Felipe writes: usb: patches for v3.11 merge window All function drivers are now converted to our new configfs-based binding. Eventually this will help us getting rid of in-kernel gadget drivers and only keep function drivers in the kernel. MUSB was taught that it needs to be built for host-only and device-only modes too. We had this support long ago but it involved a ridiculous amount of ifdefs. Now we have a much cleaner approach. Samsung Exynos4 platform now implements HSIC support. We're introducing support for AB8540 and AB9540 PHYs. MUSB module reinsertion now works as expected, before we were getting -EBUSY being returned by the resource checks done on driver core. DWC3 now has minimum support for TI's AM437x series of SoCs. OMAP5 USB3 PHY learned one extra DPLL configuration values because that PHY is reused in TI's DRA7xx devices. We're introducing support for Faraday fotg210 UDCs. Last, but not least, the usual set of non-critical fixes and cleanups ranging from usage of platform_{get,set}_drvdata to lock improvements. Signed-of-by: Felipe Balbi <balbi@ti.com>
This commit is contained in:
commit
976f8bef9c
14
Documentation/ABI/testing/configfs-usb-gadget-eem
Normal file
14
Documentation/ABI/testing/configfs-usb-gadget-eem
Normal file
@ -0,0 +1,14 @@
|
||||
What: /config/usb-gadget/gadget/functions/eem.name
|
||||
Date: May 2013
|
||||
KenelVersion: 3.11
|
||||
Description:
|
||||
The attributes:
|
||||
|
||||
ifname - network device interface name associated with
|
||||
this function instance
|
||||
qmult - queue length multiplier for high and
|
||||
super speed
|
||||
host_addr - MAC address of host's end of this
|
||||
Ethernet over USB link
|
||||
dev_addr - MAC address of device's end of this
|
||||
Ethernet over USB link
|
8
Documentation/ABI/testing/configfs-usb-gadget-phonet
Normal file
8
Documentation/ABI/testing/configfs-usb-gadget-phonet
Normal file
@ -0,0 +1,8 @@
|
||||
What: /config/usb-gadget/gadget/functions/phonet.name
|
||||
Date: May 2013
|
||||
KenelVersion: 3.11
|
||||
Description:
|
||||
|
||||
This item contains just one readonly attribute: ifname.
|
||||
It contains the network interface name assigned during
|
||||
network device registration.
|
14
Documentation/ABI/testing/configfs-usb-gadget-rndis
Normal file
14
Documentation/ABI/testing/configfs-usb-gadget-rndis
Normal file
@ -0,0 +1,14 @@
|
||||
What: /config/usb-gadget/gadget/functions/rndis.name
|
||||
Date: May 2013
|
||||
KenelVersion: 3.11
|
||||
Description:
|
||||
The attributes:
|
||||
|
||||
ifname - network device interface name associated with
|
||||
this function instance
|
||||
qmult - queue length multiplier for high and
|
||||
super speed
|
||||
host_addr - MAC address of host's end of this
|
||||
Ethernet over USB link
|
||||
dev_addr - MAC address of device's end of this
|
||||
Ethernet over USB link
|
14
Documentation/ABI/testing/configfs-usb-gadget-subset
Normal file
14
Documentation/ABI/testing/configfs-usb-gadget-subset
Normal file
@ -0,0 +1,14 @@
|
||||
What: /config/usb-gadget/gadget/functions/geth.name
|
||||
Date: May 2013
|
||||
KenelVersion: 3.11
|
||||
Description:
|
||||
The attributes:
|
||||
|
||||
ifname - network device interface name associated with
|
||||
this function instance
|
||||
qmult - queue length multiplier for high and
|
||||
super speed
|
||||
host_addr - MAC address of host's end of this
|
||||
Ethernet over USB link
|
||||
dev_addr - MAC address of device's end of this
|
||||
Ethernet over USB link
|
@ -6,27 +6,10 @@ Practice : Universal Serial Bus" with the following modifications
|
||||
and additions :
|
||||
|
||||
Required properties :
|
||||
- compatible : Should be "nvidia,tegra20-ehci" for USB controllers
|
||||
used in host mode.
|
||||
- phy_type : Should be one of "ulpi" or "utmi".
|
||||
- nvidia,vbus-gpio : If present, specifies a gpio that needs to be
|
||||
activated for the bus to be powered.
|
||||
- nvidia,phy : phandle of the PHY instance, the controller is connected to.
|
||||
|
||||
Required properties for phy_type == ulpi:
|
||||
- nvidia,phy-reset-gpio : The GPIO used to reset the PHY.
|
||||
- compatible : Should be "nvidia,tegra20-ehci".
|
||||
- nvidia,phy : phandle of the PHY that the controller is connected to.
|
||||
- clocks : Contains a single entry which defines the USB controller's clock.
|
||||
|
||||
Optional properties:
|
||||
- dr_mode : dual role mode. Indicates the working mode for
|
||||
nvidia,tegra20-ehci compatible controllers. Can be "host", "peripheral",
|
||||
or "otg". Default to "host" if not defined for backward compatibility.
|
||||
host means this is a host controller
|
||||
peripheral means it is device controller
|
||||
otg means it can operate as either ("on the go")
|
||||
- nvidia,has-legacy-mode : boolean indicates whether this controller can
|
||||
operate in legacy mode (as APX 2500 / 2600). In legacy mode some
|
||||
registers are accessed through the APB_MISC base address instead of
|
||||
the USB controller. Since this is a legacy issue it probably does not
|
||||
warrant a compatible string of its own.
|
||||
- nvidia,needs-double-reset : boolean is to be set for some of the Tegra2
|
||||
USB ports, which need reset twice due to hardware issues.
|
||||
- nvidia,needs-double-reset : boolean is to be set for some of the Tegra20
|
||||
USB ports, which need reset twice due to hardware issues.
|
||||
|
@ -4,14 +4,49 @@ The device node for Tegra SOC USB PHY:
|
||||
|
||||
Required properties :
|
||||
- compatible : Should be "nvidia,tegra20-usb-phy".
|
||||
- reg : Address and length of the register set for the USB PHY interface.
|
||||
- phy_type : Should be one of "ulpi" or "utmi".
|
||||
- reg : Defines the following set of registers, in the order listed:
|
||||
- The PHY's own register set.
|
||||
Always present.
|
||||
- The register set of the PHY containing the UTMI pad control registers.
|
||||
Present if-and-only-if phy_type == utmi.
|
||||
- phy_type : Should be one of "utmi", "ulpi" or "hsic".
|
||||
- clocks : Defines the clocks listed in the clock-names property.
|
||||
- clock-names : The following clock names must be present:
|
||||
- reg: The clock needed to access the PHY's own registers. This is the
|
||||
associated EHCI controller's clock. Always present.
|
||||
- pll_u: PLL_U. Always present.
|
||||
- timer: The timeout clock (clk_m). Present if phy_type == utmi.
|
||||
- utmi-pads: The clock needed to access the UTMI pad control registers.
|
||||
Present if phy_type == utmi.
|
||||
- ulpi-link: The clock Tegra provides to the ULPI PHY (cdev2).
|
||||
Present if phy_type == ulpi, and ULPI link mode is in use.
|
||||
|
||||
Required properties for phy_type == ulpi:
|
||||
- nvidia,phy-reset-gpio : The GPIO used to reset the PHY.
|
||||
|
||||
Required PHY timing params for utmi phy:
|
||||
- nvidia,hssync-start-delay : Number of 480 Mhz clock cycles to wait before
|
||||
start of sync launches RxActive
|
||||
- nvidia,elastic-limit : Variable FIFO Depth of elastic input store
|
||||
- nvidia,idle-wait-delay : Number of 480 Mhz clock cycles of idle to wait
|
||||
before declare IDLE.
|
||||
- nvidia,term-range-adj : Range adjusment on terminations
|
||||
- nvidia,xcvr-setup : HS driver output control
|
||||
- nvidia,xcvr-lsfslew : LS falling slew rate control.
|
||||
- nvidia,xcvr-lsrslew : LS rising slew rate control.
|
||||
|
||||
Optional properties:
|
||||
- nvidia,has-legacy-mode : boolean indicates whether this controller can
|
||||
operate in legacy mode (as APX 2500 / 2600). In legacy mode some
|
||||
registers are accessed through the APB_MISC base address instead of
|
||||
the USB controller.
|
||||
the USB controller.
|
||||
- nvidia,is-wired : boolean. Indicates whether we can do certain kind of power
|
||||
optimizations for the devices that are always connected. e.g. modem.
|
||||
- dr_mode : dual role mode. Indicates the working mode for the PHY. Can be
|
||||
"host", "peripheral", or "otg". Defaults to "host" if not defined.
|
||||
host means this is a host controller
|
||||
peripheral means it is device controller
|
||||
otg means it can operate as either ("on the go")
|
||||
|
||||
Required properties for dr_mode == otg:
|
||||
- vbus-supply: regulator for VBUS
|
||||
|
@ -449,7 +449,11 @@
|
||||
|
||||
usb@c5004000 {
|
||||
status = "okay";
|
||||
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
|
||||
nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */
|
||||
};
|
||||
|
||||
usb-phy@c5004000 {
|
||||
nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */
|
||||
};
|
||||
|
||||
sdhci@c8000600 {
|
||||
|
@ -428,17 +428,26 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5000000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb@c5004000 {
|
||||
status = "okay";
|
||||
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
|
||||
nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */
|
||||
};
|
||||
|
||||
usb-phy@c5004000 {
|
||||
status = "okay";
|
||||
nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */
|
||||
};
|
||||
|
||||
usb@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5004400 {
|
||||
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
|
||||
usb-phy@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
sdhci@c8000200 {
|
||||
|
@ -38,13 +38,20 @@
|
||||
|
||||
usb@c5000000 {
|
||||
status = "okay";
|
||||
dr_mode = "otg";
|
||||
};
|
||||
|
||||
usb-phy@c5000000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
serial@70006000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
@ -427,17 +427,26 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5000000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb@c5004000 {
|
||||
status = "okay";
|
||||
nvidia,phy-reset-gpio = <&gpio 168 0>; /* gpio PV0 */
|
||||
nvidia,phy-reset-gpio = <&gpio 168 1>; /* gpio PV0, active low */
|
||||
};
|
||||
|
||||
usb-phy@c5004000 {
|
||||
status = "okay";
|
||||
nvidia,phy-reset-gpio = <&gpio 168 1>; /* gpio PV0, active low */
|
||||
};
|
||||
|
||||
usb@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5004400 {
|
||||
nvidia,phy-reset-gpio = <&gpio 168 0>; /* gpio PV0 */
|
||||
usb-phy@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
sdhci@c8000000 {
|
||||
|
@ -569,17 +569,28 @@
|
||||
dr_mode = "otg";
|
||||
};
|
||||
|
||||
usb-phy@c5000000 {
|
||||
status = "okay";
|
||||
vbus-supply = <&vbus_reg>;
|
||||
dr_mode = "otg";
|
||||
};
|
||||
|
||||
usb@c5004000 {
|
||||
status = "okay";
|
||||
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
|
||||
nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */
|
||||
};
|
||||
|
||||
usb-phy@c5004000 {
|
||||
status = "okay";
|
||||
nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */
|
||||
};
|
||||
|
||||
usb@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5004400 {
|
||||
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
|
||||
usb-phy@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
sdhci@c8000000 {
|
||||
@ -807,6 +818,15 @@
|
||||
gpio = <&pmic 1 0>;
|
||||
enable-active-high;
|
||||
};
|
||||
|
||||
vbus_reg: regulator@3 {
|
||||
compatible = "regulator-fixed";
|
||||
reg = <3>;
|
||||
regulator-name = "vdd_vbus_wup1";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
gpio = <&gpio 24 0>; /* PD0 */
|
||||
};
|
||||
};
|
||||
|
||||
sound {
|
||||
|
@ -470,6 +470,10 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
sdhci@c8000600 {
|
||||
cd-gpios = <&gpio 58 1>; /* gpio PH2 */
|
||||
wp-gpios = <&gpio 59 0>; /* gpio PH3 */
|
||||
|
@ -314,17 +314,27 @@
|
||||
nvidia,vbus-gpio = <&gpio 170 0>; /* gpio PV2 */
|
||||
};
|
||||
|
||||
usb-phy@c5000000 {
|
||||
status = "okay";
|
||||
vbus-supply = <&vbus_reg>;
|
||||
};
|
||||
|
||||
usb@c5004000 {
|
||||
status = "okay";
|
||||
nvidia,phy-reset-gpio = <&gpio 168 0>; /* gpio PV0 */
|
||||
nvidia,phy-reset-gpio = <&gpio 168 1>; /* gpio PV0, active low */
|
||||
};
|
||||
|
||||
usb-phy@c5004000 {
|
||||
status = "okay";
|
||||
nvidia,phy-reset-gpio = <&gpio 168 1>; /* gpio PV0, active low */
|
||||
};
|
||||
|
||||
usb@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5004400 {
|
||||
nvidia,phy-reset-gpio = <&gpio 168 0>; /* gpio PV0 */
|
||||
usb-phy@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
sdhci@c8000000 {
|
||||
@ -390,6 +400,15 @@
|
||||
regulator-max-microvolt = <1800000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
vbus_reg: regulator@2 {
|
||||
compatible = "regulator-fixed";
|
||||
reg = <2>;
|
||||
regulator-name = "usb1_vbus";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
gpio = <&gpio 170 0>; /* PV2 */
|
||||
};
|
||||
};
|
||||
|
||||
sound {
|
||||
|
@ -505,17 +505,26 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5000000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb@c5004000 {
|
||||
status = "okay";
|
||||
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
|
||||
nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */
|
||||
};
|
||||
|
||||
usb-phy@c5004000 {
|
||||
status = "okay";
|
||||
nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */
|
||||
};
|
||||
|
||||
usb@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
usb-phy@c5004400 {
|
||||
nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */
|
||||
usb-phy@c5008000 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
sdhci@c8000000 {
|
||||
|
@ -511,11 +511,21 @@
|
||||
nvidia,vbus-gpio = <&tca6416 0 0>; /* GPIO_PMU0 */
|
||||
};
|
||||
|
||||
usb-phy@c5000000 {
|
||||
status = "okay";
|
||||
vbus-supply = <&vbus1_reg>;
|
||||
};
|
||||
|
||||
usb@c5008000 {
|
||||
status = "okay";
|
||||
nvidia,vbus-gpio = <&tca6416 1 0>; /* GPIO_PMU1 */
|
||||
};
|
||||
|
||||
usb-phy@c5008000 {
|
||||
status = "okay";
|
||||
vbus-supply = <&vbus3_reg>;
|
||||
};
|
||||
|
||||
sdhci@c8000400 {
|
||||
status = "okay";
|
||||
cd-gpios = <&gpio 69 1>; /* gpio PI5 */
|
||||
@ -568,6 +578,24 @@
|
||||
regulator-max-microvolt = <5000000>;
|
||||
regulator-always-on;
|
||||
};
|
||||
|
||||
vbus1_reg: regulator@2 {
|
||||
compatible = "regulator-fixed";
|
||||
reg = <2>;
|
||||
regulator-name = "vbus1";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
gpio = <&tca6416 0 0>; /* GPIO_PMU0 */
|
||||
};
|
||||
|
||||
vbus3_reg: regulator@3 {
|
||||
compatible = "regulator-fixed";
|
||||
reg = <3>;
|
||||
regulator-name = "vbus3";
|
||||
regulator-min-microvolt = <5000000>;
|
||||
regulator-max-microvolt = <5000000>;
|
||||
gpio = <&tca6416 1 0>; /* GPIO_PMU1 */
|
||||
};
|
||||
};
|
||||
|
||||
sound {
|
||||
|
@ -455,13 +455,24 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
phy1: usb-phy@c5000400 {
|
||||
phy1: usb-phy@c5000000 {
|
||||
compatible = "nvidia,tegra20-usb-phy";
|
||||
reg = <0xc5000400 0x3c00>;
|
||||
reg = <0xc5000000 0x4000 0xc5000000 0x4000>;
|
||||
phy_type = "utmi";
|
||||
clocks = <&tegra_car 22>,
|
||||
<&tegra_car 127>,
|
||||
<&tegra_car 106>,
|
||||
<&tegra_car 22>;
|
||||
clock-names = "reg", "pll_u", "timer", "utmi-pads";
|
||||
nvidia,has-legacy-mode;
|
||||
clocks = <&tegra_car 22>, <&tegra_car 127>;
|
||||
clock-names = "phy", "pll_u";
|
||||
hssync_start_delay = <9>;
|
||||
idle_wait_delay = <17>;
|
||||
elastic_limit = <16>;
|
||||
term_range_adj = <6>;
|
||||
xcvr_setup = <9>;
|
||||
xcvr_lsfslew = <1>;
|
||||
xcvr_lsrslew = <1>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
usb@c5004000 {
|
||||
@ -474,12 +485,15 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
phy2: usb-phy@c5004400 {
|
||||
phy2: usb-phy@c5004000 {
|
||||
compatible = "nvidia,tegra20-usb-phy";
|
||||
reg = <0xc5004400 0x3c00>;
|
||||
reg = <0xc5004000 0x4000>;
|
||||
phy_type = "ulpi";
|
||||
clocks = <&tegra_car 93>, <&tegra_car 127>;
|
||||
clock-names = "phy", "pll_u";
|
||||
clocks = <&tegra_car 58>,
|
||||
<&tegra_car 127>,
|
||||
<&tegra_car 93>;
|
||||
clock-names = "reg", "pll_u", "ulpi-link";
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
usb@c5008000 {
|
||||
@ -492,12 +506,23 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
phy3: usb-phy@c5008400 {
|
||||
phy3: usb-phy@c5008000 {
|
||||
compatible = "nvidia,tegra20-usb-phy";
|
||||
reg = <0xc5008400 0x3c00>;
|
||||
reg = <0xc5008000 0x4000 0xc5000000 0x4000>;
|
||||
phy_type = "utmi";
|
||||
clocks = <&tegra_car 22>, <&tegra_car 127>;
|
||||
clock-names = "phy", "pll_u";
|
||||
clocks = <&tegra_car 59>,
|
||||
<&tegra_car 127>,
|
||||
<&tegra_car 106>,
|
||||
<&tegra_car 22>;
|
||||
clock-names = "reg", "pll_u", "timer", "utmi-pads";
|
||||
hssync_start_delay = <9>;
|
||||
idle_wait_delay = <17>;
|
||||
elastic_limit = <16>;
|
||||
term_range_adj = <6>;
|
||||
xcvr_setup = <9>;
|
||||
xcvr_lsfslew = <2>;
|
||||
xcvr_lsrslew = <2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
sdhci@c8000000 {
|
||||
|
@ -61,21 +61,46 @@
|
||||
#define USBOTGSS_REVISION 0x0000
|
||||
#define USBOTGSS_SYSCONFIG 0x0010
|
||||
#define USBOTGSS_IRQ_EOI 0x0020
|
||||
#define USBOTGSS_EOI_OFFSET 0x0008
|
||||
#define USBOTGSS_IRQSTATUS_RAW_0 0x0024
|
||||
#define USBOTGSS_IRQSTATUS_0 0x0028
|
||||
#define USBOTGSS_IRQENABLE_SET_0 0x002c
|
||||
#define USBOTGSS_IRQENABLE_CLR_0 0x0030
|
||||
#define USBOTGSS_IRQSTATUS_RAW_1 0x0034
|
||||
#define USBOTGSS_IRQSTATUS_1 0x0038
|
||||
#define USBOTGSS_IRQENABLE_SET_1 0x003c
|
||||
#define USBOTGSS_IRQENABLE_CLR_1 0x0040
|
||||
#define USBOTGSS_IRQ0_OFFSET 0x0004
|
||||
#define USBOTGSS_IRQSTATUS_RAW_1 0x0030
|
||||
#define USBOTGSS_IRQSTATUS_1 0x0034
|
||||
#define USBOTGSS_IRQENABLE_SET_1 0x0038
|
||||
#define USBOTGSS_IRQENABLE_CLR_1 0x003c
|
||||
#define USBOTGSS_IRQSTATUS_RAW_2 0x0040
|
||||
#define USBOTGSS_IRQSTATUS_2 0x0044
|
||||
#define USBOTGSS_IRQENABLE_SET_2 0x0048
|
||||
#define USBOTGSS_IRQENABLE_CLR_2 0x004c
|
||||
#define USBOTGSS_IRQSTATUS_RAW_3 0x0050
|
||||
#define USBOTGSS_IRQSTATUS_3 0x0054
|
||||
#define USBOTGSS_IRQENABLE_SET_3 0x0058
|
||||
#define USBOTGSS_IRQENABLE_CLR_3 0x005c
|
||||
#define USBOTGSS_IRQSTATUS_EOI_MISC 0x0030
|
||||
#define USBOTGSS_IRQSTATUS_RAW_MISC 0x0034
|
||||
#define USBOTGSS_IRQSTATUS_MISC 0x0038
|
||||
#define USBOTGSS_IRQENABLE_SET_MISC 0x003c
|
||||
#define USBOTGSS_IRQENABLE_CLR_MISC 0x0040
|
||||
#define USBOTGSS_IRQMISC_OFFSET 0x03fc
|
||||
#define USBOTGSS_UTMI_OTG_CTRL 0x0080
|
||||
#define USBOTGSS_UTMI_OTG_STATUS 0x0084
|
||||
#define USBOTGSS_UTMI_OTG_OFFSET 0x0480
|
||||
#define USBOTGSS_TXFIFO_DEPTH 0x0508
|
||||
#define USBOTGSS_RXFIFO_DEPTH 0x050c
|
||||
#define USBOTGSS_MMRAM_OFFSET 0x0100
|
||||
#define USBOTGSS_FLADJ 0x0104
|
||||
#define USBOTGSS_DEBUG_CFG 0x0108
|
||||
#define USBOTGSS_DEBUG_DATA 0x010c
|
||||
#define USBOTGSS_DEV_EBC_EN 0x0110
|
||||
#define USBOTGSS_DEBUG_OFFSET 0x0600
|
||||
|
||||
/* REVISION REGISTER */
|
||||
#define USBOTGSS_REVISION_XMAJOR(reg) ((reg >> 8) & 0x7)
|
||||
#define USBOTGSS_REVISION_XMAJOR1 1
|
||||
#define USBOTGSS_REVISION_XMAJOR2 2
|
||||
/* SYSCONFIG REGISTER */
|
||||
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
|
||||
|
||||
@ -85,17 +110,17 @@
|
||||
/* IRQS0 BITS */
|
||||
#define USBOTGSS_IRQO_COREIRQ_ST (1 << 0)
|
||||
|
||||
/* IRQ1 BITS */
|
||||
#define USBOTGSS_IRQ1_DMADISABLECLR (1 << 17)
|
||||
#define USBOTGSS_IRQ1_OEVT (1 << 16)
|
||||
#define USBOTGSS_IRQ1_DRVVBUS_RISE (1 << 13)
|
||||
#define USBOTGSS_IRQ1_CHRGVBUS_RISE (1 << 12)
|
||||
#define USBOTGSS_IRQ1_DISCHRGVBUS_RISE (1 << 11)
|
||||
#define USBOTGSS_IRQ1_IDPULLUP_RISE (1 << 8)
|
||||
#define USBOTGSS_IRQ1_DRVVBUS_FALL (1 << 5)
|
||||
#define USBOTGSS_IRQ1_CHRGVBUS_FALL (1 << 4)
|
||||
#define USBOTGSS_IRQ1_DISCHRGVBUS_FALL (1 << 3)
|
||||
#define USBOTGSS_IRQ1_IDPULLUP_FALL (1 << 0)
|
||||
/* IRQMISC BITS */
|
||||
#define USBOTGSS_IRQMISC_DMADISABLECLR (1 << 17)
|
||||
#define USBOTGSS_IRQMISC_OEVT (1 << 16)
|
||||
#define USBOTGSS_IRQMISC_DRVVBUS_RISE (1 << 13)
|
||||
#define USBOTGSS_IRQMISC_CHRGVBUS_RISE (1 << 12)
|
||||
#define USBOTGSS_IRQMISC_DISCHRGVBUS_RISE (1 << 11)
|
||||
#define USBOTGSS_IRQMISC_IDPULLUP_RISE (1 << 8)
|
||||
#define USBOTGSS_IRQMISC_DRVVBUS_FALL (1 << 5)
|
||||
#define USBOTGSS_IRQMISC_CHRGVBUS_FALL (1 << 4)
|
||||
#define USBOTGSS_IRQMISC_DISCHRGVBUS_FALL (1 << 3)
|
||||
#define USBOTGSS_IRQMISC_IDPULLUP_FALL (1 << 0)
|
||||
|
||||
/* UTMI_OTG_CTRL REGISTER */
|
||||
#define USBOTGSS_UTMI_OTG_CTRL_DRVVBUS (1 << 5)
|
||||
@ -122,6 +147,12 @@ struct dwc3_omap {
|
||||
void __iomem *base;
|
||||
|
||||
u32 utmi_otg_status;
|
||||
u32 utmi_otg_offset;
|
||||
u32 irqmisc_offset;
|
||||
u32 irq_eoi_offset;
|
||||
u32 debug_offset;
|
||||
u32 irq0_offset;
|
||||
u32 revision;
|
||||
|
||||
u32 dma_status:1;
|
||||
};
|
||||
@ -138,6 +169,58 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
|
||||
writel(value, base + offset);
|
||||
}
|
||||
|
||||
static u32 dwc3_omap_read_utmi_status(struct dwc3_omap *omap)
|
||||
{
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS +
|
||||
omap->utmi_otg_offset);
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_utmi_status(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS +
|
||||
omap->utmi_otg_offset, value);
|
||||
|
||||
}
|
||||
|
||||
static u32 dwc3_omap_read_irq0_status(struct dwc3_omap *omap)
|
||||
{
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0 -
|
||||
omap->irq0_offset);
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_irq0_status(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0 -
|
||||
omap->irq0_offset, value);
|
||||
|
||||
}
|
||||
|
||||
static u32 dwc3_omap_read_irqmisc_status(struct dwc3_omap *omap)
|
||||
{
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_MISC +
|
||||
omap->irqmisc_offset);
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_irqmisc_status(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_MISC +
|
||||
omap->irqmisc_offset, value);
|
||||
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_irqmisc_set(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_MISC +
|
||||
omap->irqmisc_offset, value);
|
||||
|
||||
}
|
||||
|
||||
static void dwc3_omap_write_irq0_set(struct dwc3_omap *omap, u32 value)
|
||||
{
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0 -
|
||||
omap->irq0_offset, value);
|
||||
}
|
||||
|
||||
int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
|
||||
{
|
||||
u32 val;
|
||||
@ -150,38 +233,38 @@ int dwc3_omap_mailbox(enum omap_dwc3_vbus_id_status status)
|
||||
case OMAP_DWC3_ID_GROUND:
|
||||
dev_dbg(omap->dev, "ID GND\n");
|
||||
|
||||
val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
val = dwc3_omap_read_utmi_status(omap);
|
||||
val &= ~(USBOTGSS_UTMI_OTG_STATUS_IDDIG
|
||||
| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_SESSEND);
|
||||
val |= USBOTGSS_UTMI_OTG_STATUS_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
|
||||
dwc3_omap_write_utmi_status(omap, val);
|
||||
break;
|
||||
|
||||
case OMAP_DWC3_VBUS_VALID:
|
||||
dev_dbg(omap->dev, "VBUS Connect\n");
|
||||
|
||||
val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
val = dwc3_omap_read_utmi_status(omap);
|
||||
val &= ~USBOTGSS_UTMI_OTG_STATUS_SESSEND;
|
||||
val |= USBOTGSS_UTMI_OTG_STATUS_IDDIG
|
||||
| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT;
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
|
||||
dwc3_omap_write_utmi_status(omap, val);
|
||||
break;
|
||||
|
||||
case OMAP_DWC3_ID_FLOAT:
|
||||
case OMAP_DWC3_VBUS_OFF:
|
||||
dev_dbg(omap->dev, "VBUS Disconnect\n");
|
||||
|
||||
val = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
val = dwc3_omap_read_utmi_status(omap);
|
||||
val &= ~(USBOTGSS_UTMI_OTG_STATUS_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_STATUS_POWERPRESENT);
|
||||
val |= USBOTGSS_UTMI_OTG_STATUS_SESSEND
|
||||
| USBOTGSS_UTMI_OTG_STATUS_IDDIG;
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, val);
|
||||
dwc3_omap_write_utmi_status(omap, val);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -199,44 +282,45 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
|
||||
spin_lock(&omap->lock);
|
||||
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_1);
|
||||
reg = dwc3_omap_read_irqmisc_status(omap);
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_DMADISABLECLR) {
|
||||
if (reg & USBOTGSS_IRQMISC_DMADISABLECLR) {
|
||||
dev_dbg(omap->dev, "DMA Disable was Cleared\n");
|
||||
omap->dma_status = false;
|
||||
}
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_OEVT)
|
||||
if (reg & USBOTGSS_IRQMISC_OEVT)
|
||||
dev_dbg(omap->dev, "OTG Event\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_DRVVBUS_RISE)
|
||||
if (reg & USBOTGSS_IRQMISC_DRVVBUS_RISE)
|
||||
dev_dbg(omap->dev, "DRVVBUS Rise\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_CHRGVBUS_RISE)
|
||||
if (reg & USBOTGSS_IRQMISC_CHRGVBUS_RISE)
|
||||
dev_dbg(omap->dev, "CHRGVBUS Rise\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_RISE)
|
||||
if (reg & USBOTGSS_IRQMISC_DISCHRGVBUS_RISE)
|
||||
dev_dbg(omap->dev, "DISCHRGVBUS Rise\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_IDPULLUP_RISE)
|
||||
if (reg & USBOTGSS_IRQMISC_IDPULLUP_RISE)
|
||||
dev_dbg(omap->dev, "IDPULLUP Rise\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_DRVVBUS_FALL)
|
||||
if (reg & USBOTGSS_IRQMISC_DRVVBUS_FALL)
|
||||
dev_dbg(omap->dev, "DRVVBUS Fall\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_CHRGVBUS_FALL)
|
||||
if (reg & USBOTGSS_IRQMISC_CHRGVBUS_FALL)
|
||||
dev_dbg(omap->dev, "CHRGVBUS Fall\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_DISCHRGVBUS_FALL)
|
||||
if (reg & USBOTGSS_IRQMISC_DISCHRGVBUS_FALL)
|
||||
dev_dbg(omap->dev, "DISCHRGVBUS Fall\n");
|
||||
|
||||
if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL)
|
||||
if (reg & USBOTGSS_IRQMISC_IDPULLUP_FALL)
|
||||
dev_dbg(omap->dev, "IDPULLUP Fall\n");
|
||||
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
|
||||
dwc3_omap_write_irqmisc_status(omap, reg);
|
||||
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
|
||||
reg = dwc3_omap_read_irq0_status(omap);
|
||||
|
||||
dwc3_omap_write_irq0_status(omap, reg);
|
||||
|
||||
spin_unlock(&omap->lock);
|
||||
|
||||
@ -258,26 +342,26 @@ static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
|
||||
|
||||
/* enable all IRQs */
|
||||
reg = USBOTGSS_IRQO_COREIRQ_ST;
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
|
||||
dwc3_omap_write_irq0_set(omap, reg);
|
||||
|
||||
reg = (USBOTGSS_IRQ1_OEVT |
|
||||
USBOTGSS_IRQ1_DRVVBUS_RISE |
|
||||
USBOTGSS_IRQ1_CHRGVBUS_RISE |
|
||||
USBOTGSS_IRQ1_DISCHRGVBUS_RISE |
|
||||
USBOTGSS_IRQ1_IDPULLUP_RISE |
|
||||
USBOTGSS_IRQ1_DRVVBUS_FALL |
|
||||
USBOTGSS_IRQ1_CHRGVBUS_FALL |
|
||||
USBOTGSS_IRQ1_DISCHRGVBUS_FALL |
|
||||
USBOTGSS_IRQ1_IDPULLUP_FALL);
|
||||
reg = (USBOTGSS_IRQMISC_OEVT |
|
||||
USBOTGSS_IRQMISC_DRVVBUS_RISE |
|
||||
USBOTGSS_IRQMISC_CHRGVBUS_RISE |
|
||||
USBOTGSS_IRQMISC_DISCHRGVBUS_RISE |
|
||||
USBOTGSS_IRQMISC_IDPULLUP_RISE |
|
||||
USBOTGSS_IRQMISC_DRVVBUS_FALL |
|
||||
USBOTGSS_IRQMISC_CHRGVBUS_FALL |
|
||||
USBOTGSS_IRQMISC_DISCHRGVBUS_FALL |
|
||||
USBOTGSS_IRQMISC_IDPULLUP_FALL);
|
||||
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
|
||||
dwc3_omap_write_irqmisc_set(omap, reg);
|
||||
}
|
||||
|
||||
static void dwc3_omap_disable_irqs(struct dwc3_omap *omap)
|
||||
{
|
||||
/* disable all IRQs */
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, 0x00);
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, 0x00);
|
||||
dwc3_omap_write_irqmisc_set(omap, 0x00);
|
||||
dwc3_omap_write_irq0_set(omap, 0x00);
|
||||
}
|
||||
|
||||
static u64 dwc3_omap_dma_mask = DMA_BIT_MASK(32);
|
||||
@ -294,6 +378,7 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
int irq;
|
||||
|
||||
int utmi_mode = 0;
|
||||
int x_major;
|
||||
|
||||
u32 reg;
|
||||
|
||||
@ -347,10 +432,46 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "get_sync failed with err %d\n", ret);
|
||||
return ret;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_REVISION);
|
||||
omap->revision = reg;
|
||||
x_major = USBOTGSS_REVISION_XMAJOR(reg);
|
||||
|
||||
/* Differentiate between OMAP5 and AM437x */
|
||||
switch (x_major) {
|
||||
case USBOTGSS_REVISION_XMAJOR1:
|
||||
case USBOTGSS_REVISION_XMAJOR2:
|
||||
omap->irq_eoi_offset = 0;
|
||||
omap->irq0_offset = 0;
|
||||
omap->irqmisc_offset = 0;
|
||||
omap->utmi_otg_offset = 0;
|
||||
omap->debug_offset = 0;
|
||||
break;
|
||||
default:
|
||||
/* Default to the latest revision */
|
||||
omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
|
||||
omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
|
||||
omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
|
||||
omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
|
||||
omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
|
||||
break;
|
||||
}
|
||||
|
||||
/* For OMAP5(ES2.0) and AM437x x_major is 2 even though there are
|
||||
* changes in wrapper registers, Using dt compatible for aegis
|
||||
*/
|
||||
|
||||
if (of_device_is_compatible(node, "ti,am437x-dwc3")) {
|
||||
omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
|
||||
omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
|
||||
omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
|
||||
omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
|
||||
omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
|
||||
}
|
||||
|
||||
reg = dwc3_omap_read_utmi_status(omap);
|
||||
|
||||
of_property_read_u32(node, "utmi-mode", &utmi_mode);
|
||||
|
||||
@ -365,7 +486,7 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode);
|
||||
}
|
||||
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
|
||||
dwc3_omap_write_utmi_status(omap, reg);
|
||||
|
||||
/* check the DMA Status */
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
@ -376,7 +497,7 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ #%d --> %d\n",
|
||||
omap->irq, ret);
|
||||
return ret;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
dwc3_omap_enable_irqs(omap);
|
||||
@ -384,10 +505,21 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
ret = of_platform_populate(node, NULL, NULL, dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to create dwc3 core\n");
|
||||
return ret;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
dwc3_omap_disable_irqs(omap);
|
||||
|
||||
err1:
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
err0:
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwc3_omap_remove(struct platform_device *pdev)
|
||||
@ -406,6 +538,9 @@ static const struct of_device_id of_dwc3_match[] = {
|
||||
{
|
||||
.compatible = "ti,dwc3"
|
||||
},
|
||||
{
|
||||
.compatible = "ti,am437x-dwc3"
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_dwc3_match);
|
||||
@ -431,8 +566,7 @@ static int dwc3_omap_suspend(struct device *dev)
|
||||
{
|
||||
struct dwc3_omap *omap = dev_get_drvdata(dev);
|
||||
|
||||
omap->utmi_otg_status = dwc3_omap_readl(omap->base,
|
||||
USBOTGSS_UTMI_OTG_STATUS);
|
||||
omap->utmi_otg_status = dwc3_omap_read_utmi_status(omap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -441,8 +575,7 @@ static int dwc3_omap_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3_omap *omap = dev_get_drvdata(dev);
|
||||
|
||||
dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS,
|
||||
omap->utmi_otg_status);
|
||||
dwc3_omap_write_utmi_status(omap, omap->utmi_otg_status);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
|
@ -133,7 +133,6 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pci_set_power_state(pci, PCI_D0);
|
||||
pci_set_master(pci);
|
||||
|
||||
ret = dwc3_pci_register_phys(glue);
|
||||
|
@ -192,6 +192,16 @@ config USB_FUSB300
|
||||
help
|
||||
Faraday usb device controller FUSB300 driver
|
||||
|
||||
config USB_FOTG210_UDC
|
||||
tristate "Faraday FOTG210 USB Peripheral Controller"
|
||||
help
|
||||
Faraday USB2.0 OTG controller which can be configured as
|
||||
high speed or full speed USB device. This driver supppors
|
||||
Bulk Transfer so far.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "fotg210_udc".
|
||||
|
||||
config USB_OMAP
|
||||
tristate "OMAP USB Device Controller"
|
||||
depends on ARCH_OMAP1
|
||||
@ -334,14 +344,6 @@ config USB_MV_U3D
|
||||
# Controllers available in both integrated and discrete versions
|
||||
#
|
||||
|
||||
# musb builds in ../musb along with host support
|
||||
config USB_GADGET_MUSB_HDRC
|
||||
tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)"
|
||||
depends on USB_MUSB_HDRC
|
||||
help
|
||||
This OTG-capable silicon IP is used in dual designs including
|
||||
the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
|
||||
|
||||
config USB_M66592
|
||||
tristate "Renesas M66592 USB Peripheral Controller"
|
||||
help
|
||||
@ -507,12 +509,36 @@ config USB_F_SS_LB
|
||||
config USB_U_SERIAL
|
||||
tristate
|
||||
|
||||
config USB_U_ETHER
|
||||
tristate
|
||||
|
||||
config USB_U_RNDIS
|
||||
tristate
|
||||
|
||||
config USB_F_SERIAL
|
||||
tristate
|
||||
|
||||
config USB_F_OBEX
|
||||
tristate
|
||||
|
||||
config USB_F_NCM
|
||||
tristate
|
||||
|
||||
config USB_F_ECM
|
||||
tristate
|
||||
|
||||
config USB_F_PHONET
|
||||
tristate
|
||||
|
||||
config USB_F_EEM
|
||||
tristate
|
||||
|
||||
config USB_F_SUBSET
|
||||
tristate
|
||||
|
||||
config USB_F_RNDIS
|
||||
tristate
|
||||
|
||||
choice
|
||||
tristate "USB Gadget Drivers"
|
||||
default USB_ETH
|
||||
@ -534,6 +560,57 @@ choice
|
||||
|
||||
# this first set of drivers all depend on bulk-capable hardware.
|
||||
|
||||
config USB_CONFIGFS_ECM_SUBSET
|
||||
boolean "Ethernet Control Model (CDC ECM) subset"
|
||||
depends on USB_CONFIGFS
|
||||
depends on NET
|
||||
select USB_U_ETHER
|
||||
select USB_F_SUBSET
|
||||
help
|
||||
On hardware that can't implement the full protocol,
|
||||
a simple CDC subset is used, placing fewer demands on USB.
|
||||
|
||||
config USB_CONFIGFS_RNDIS
|
||||
bool "RNDIS"
|
||||
depends on USB_CONFIGFS
|
||||
depends on NET
|
||||
select USB_U_ETHER
|
||||
select USB_F_RNDIS
|
||||
help
|
||||
Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
|
||||
and Microsoft provides redistributable binary RNDIS drivers for
|
||||
older versions of Windows.
|
||||
|
||||
To make MS-Windows work with this, use Documentation/usb/linux.inf
|
||||
as the "driver info file". For versions of MS-Windows older than
|
||||
XP, you'll need to download drivers from Microsoft's website; a URL
|
||||
is given in comments found in that info file.
|
||||
|
||||
config USB_CONFIGFS_EEM
|
||||
bool "Ethernet Emulation Model (EEM)"
|
||||
depends on USB_CONFIGFS
|
||||
depends on NET
|
||||
select USB_U_ETHER
|
||||
select USB_F_EEM
|
||||
help
|
||||
CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
|
||||
and therefore can be supported by more hardware. Technically ECM and
|
||||
EEM are designed for different applications. The ECM model extends
|
||||
the network interface to the target (e.g. a USB cable modem), and the
|
||||
EEM model is for mobile devices to communicate with hosts using
|
||||
ethernet over USB. For Linux gadgets, however, the interface with
|
||||
the host is the same (a usbX device), so the differences are minimal.
|
||||
|
||||
config USB_CONFIGFS_PHONET
|
||||
boolean "Phonet protocol"
|
||||
depends on USB_CONFIGFS
|
||||
depends on NET
|
||||
depends on PHONET
|
||||
select USB_U_ETHER
|
||||
select USB_F_PHONET
|
||||
help
|
||||
The Phonet protocol implementation for USB device.
|
||||
|
||||
config USB_ZERO
|
||||
tristate "Gadget Zero (DEVELOPMENT)"
|
||||
select USB_LIBCOMPOSITE
|
||||
@ -603,6 +680,10 @@ config USB_ETH
|
||||
tristate "Ethernet Gadget (with CDC Ethernet support)"
|
||||
depends on NET
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_ETHER
|
||||
select USB_U_RNDIS
|
||||
select USB_F_ECM
|
||||
select USB_F_SUBSET
|
||||
select CRC32
|
||||
help
|
||||
This driver implements Ethernet style communication, in one of
|
||||
@ -639,6 +720,7 @@ config USB_ETH_RNDIS
|
||||
bool "RNDIS support"
|
||||
depends on USB_ETH
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_F_RNDIS
|
||||
default y
|
||||
help
|
||||
Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
|
||||
@ -658,6 +740,7 @@ config USB_ETH_EEM
|
||||
bool "Ethernet Emulation Model (EEM) support"
|
||||
depends on USB_ETH
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_F_EEM
|
||||
default n
|
||||
help
|
||||
CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
|
||||
@ -675,6 +758,8 @@ config USB_G_NCM
|
||||
tristate "Network Control Model (NCM) support"
|
||||
depends on NET
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_ETHER
|
||||
select USB_F_NCM
|
||||
select CRC32
|
||||
help
|
||||
This driver implements USB CDC NCM subclass standard. NCM is
|
||||
@ -718,6 +803,7 @@ config USB_FUNCTIONFS
|
||||
config USB_FUNCTIONFS_ETH
|
||||
bool "Include configuration with CDC ECM (Ethernet)"
|
||||
depends on USB_FUNCTIONFS && NET
|
||||
select USB_U_ETHER
|
||||
help
|
||||
Include a configuration with CDC ECM function (Ethernet) and the
|
||||
Function Filesystem.
|
||||
@ -725,6 +811,8 @@ config USB_FUNCTIONFS_ETH
|
||||
config USB_FUNCTIONFS_RNDIS
|
||||
bool "Include configuration with RNDIS (Ethernet)"
|
||||
depends on USB_FUNCTIONFS && NET
|
||||
select USB_U_ETHER
|
||||
select USB_U_RNDIS
|
||||
help
|
||||
Include a configuration with RNDIS function (Ethernet) and the Filesystem.
|
||||
|
||||
@ -825,7 +913,9 @@ config USB_CDC_COMPOSITE
|
||||
depends on NET
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_U_ETHER
|
||||
select USB_F_ACM
|
||||
select USB_F_ECM
|
||||
help
|
||||
This driver provides two functions in one configuration:
|
||||
a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
|
||||
@ -842,7 +932,11 @@ config USB_G_NOKIA
|
||||
depends on PHONET
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_U_ETHER
|
||||
select USB_F_ACM
|
||||
select USB_F_OBEX
|
||||
select USB_F_PHONET
|
||||
select USB_F_ECM
|
||||
help
|
||||
The Nokia composite gadget provides support for acm, obex
|
||||
and phonet in only one composite gadget driver.
|
||||
@ -869,6 +963,8 @@ config USB_G_MULTI
|
||||
select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
|
||||
select USB_LIBCOMPOSITE
|
||||
select USB_U_SERIAL
|
||||
select USB_U_ETHER
|
||||
select USB_U_RNDIS
|
||||
select USB_F_ACM
|
||||
help
|
||||
The Multifunction Composite Gadget provides Ethernet (RNDIS
|
||||
|
@ -33,6 +33,7 @@ obj-$(CONFIG_USB_EG20T) += pch_udc.o
|
||||
obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
|
||||
mv_udc-y := mv_udc_core.o
|
||||
obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
|
||||
obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
|
||||
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
|
||||
|
||||
# USB Functions
|
||||
@ -45,6 +46,21 @@ usb_f_serial-y := f_serial.o
|
||||
obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o
|
||||
usb_f_obex-y := f_obex.o
|
||||
obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o
|
||||
obj-$(CONFIG_USB_U_ETHER) += u_ether.o
|
||||
u_rndis-y := rndis.o
|
||||
obj-$(CONFIG_USB_U_RNDIS) += u_rndis.o
|
||||
usb_f_ncm-y := f_ncm.o
|
||||
obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o
|
||||
usb_f_ecm-y := f_ecm.o
|
||||
obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o
|
||||
usb_f_phonet-y := f_phonet.o
|
||||
obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o
|
||||
usb_f_eem-y := f_eem.o
|
||||
obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o
|
||||
usb_f_ecm_subset-y := f_subset.o
|
||||
obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o
|
||||
usb_f_rndis-y := f_rndis.o
|
||||
obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o
|
||||
|
||||
#
|
||||
# USB gadget drivers
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "u_ether.h"
|
||||
#include "u_serial.h"
|
||||
#include "u_ecm.h"
|
||||
|
||||
|
||||
#define DRIVER_DESC "CDC Composite Gadget"
|
||||
@ -32,18 +33,9 @@
|
||||
#define CDC_VENDOR_NUM 0x0525 /* NetChip */
|
||||
#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
||||
/*
|
||||
* Kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "f_ecm.c"
|
||||
#include "u_ether.c"
|
||||
USB_ETHERNET_MODULE_PARAMETERS();
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
@ -102,12 +94,13 @@ static struct usb_gadget_strings *dev_strings[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
static struct eth_dev *the_dev;
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static struct usb_function *f_acm;
|
||||
static struct usb_function_instance *fi_serial;
|
||||
|
||||
static struct usb_function *f_ecm;
|
||||
static struct usb_function_instance *fi_ecm;
|
||||
|
||||
/*
|
||||
* We _always_ have both CDC ECM and CDC ACM functions.
|
||||
*/
|
||||
@ -120,13 +113,27 @@ static int __init cdc_do_config(struct usb_configuration *c)
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
status = ecm_bind_config(c, hostaddr, the_dev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
fi_ecm = usb_get_function_instance("ecm");
|
||||
if (IS_ERR(fi_ecm)) {
|
||||
status = PTR_ERR(fi_ecm);
|
||||
goto err_func_ecm;
|
||||
}
|
||||
|
||||
f_ecm = usb_get_function(fi_ecm);
|
||||
if (IS_ERR(f_ecm)) {
|
||||
status = PTR_ERR(f_ecm);
|
||||
goto err_get_ecm;
|
||||
}
|
||||
|
||||
status = usb_add_function(c, f_ecm);
|
||||
if (status)
|
||||
goto err_add_ecm;
|
||||
|
||||
fi_serial = usb_get_function_instance("acm");
|
||||
if (IS_ERR(fi_serial))
|
||||
return PTR_ERR(fi_serial);
|
||||
if (IS_ERR(fi_serial)) {
|
||||
status = PTR_ERR(fi_serial);
|
||||
goto err_get_acm;
|
||||
}
|
||||
|
||||
f_acm = usb_get_function(fi_serial);
|
||||
if (IS_ERR(f_acm)) {
|
||||
@ -136,12 +143,21 @@ static int __init cdc_do_config(struct usb_configuration *c)
|
||||
|
||||
status = usb_add_function(c, f_acm);
|
||||
if (status)
|
||||
goto err_conf;
|
||||
goto err_add_acm;
|
||||
|
||||
return 0;
|
||||
err_conf:
|
||||
|
||||
err_add_acm:
|
||||
usb_put_function(f_acm);
|
||||
err_func_acm:
|
||||
usb_put_function_instance(fi_serial);
|
||||
err_get_acm:
|
||||
usb_remove_function(c, f_ecm);
|
||||
err_add_ecm:
|
||||
usb_put_function(f_ecm);
|
||||
err_get_ecm:
|
||||
usb_put_function_instance(fi_ecm);
|
||||
err_func_ecm:
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -157,6 +173,7 @@ static struct usb_configuration cdc_config_driver = {
|
||||
static int __init cdc_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct f_ecm_opts *ecm_opts;
|
||||
int status;
|
||||
|
||||
if (!can_support_ecm(cdev->gadget)) {
|
||||
@ -165,10 +182,23 @@ static int __init cdc_bind(struct usb_composite_dev *cdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set up network link layer */
|
||||
the_dev = gether_setup(cdev->gadget, hostaddr);
|
||||
if (IS_ERR(the_dev))
|
||||
return PTR_ERR(the_dev);
|
||||
fi_ecm = usb_get_function_instance("ecm");
|
||||
if (IS_ERR(fi_ecm))
|
||||
return PTR_ERR(fi_ecm);
|
||||
|
||||
ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
|
||||
|
||||
gether_set_qmult(ecm_opts->net, qmult);
|
||||
if (!gether_set_host_addr(ecm_opts->net, host_addr))
|
||||
pr_info("using host ethernet address: %s", host_addr);
|
||||
if (!gether_set_dev_addr(ecm_opts->net, dev_addr))
|
||||
pr_info("using self ethernet address: %s", dev_addr);
|
||||
|
||||
fi_serial = usb_get_function_instance("acm");
|
||||
if (IS_ERR(fi_serial)) {
|
||||
status = PTR_ERR(fi_serial);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
@ -192,7 +222,9 @@ static int __init cdc_bind(struct usb_composite_dev *cdev)
|
||||
return 0;
|
||||
|
||||
fail1:
|
||||
gether_cleanup(the_dev);
|
||||
usb_put_function_instance(fi_serial);
|
||||
fail:
|
||||
usb_put_function_instance(fi_ecm);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -200,7 +232,10 @@ static int __exit cdc_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
usb_put_function(f_acm);
|
||||
usb_put_function_instance(fi_serial);
|
||||
gether_cleanup(the_dev);
|
||||
if (!IS_ERR_OR_NULL(f_ecm))
|
||||
usb_put_function(f_ecm);
|
||||
if (!IS_ERR_OR_NULL(fi_ecm))
|
||||
usb_put_function_instance(fi_ecm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
/* #define VERBOSE_DEBUG */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#if defined USB_ETH_RNDIS
|
||||
# undef USB_ETH_RNDIS
|
||||
@ -91,27 +92,23 @@ static inline bool has_rndis(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
#include <linux/module.h>
|
||||
|
||||
/*
|
||||
* Kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "f_ecm.c"
|
||||
#include "f_subset.c"
|
||||
#include "u_ecm.h"
|
||||
#include "u_gether.h"
|
||||
#ifdef USB_ETH_RNDIS
|
||||
#include "f_rndis.c"
|
||||
#include "rndis.c"
|
||||
#include "u_rndis.h"
|
||||
#include "rndis.h"
|
||||
#else
|
||||
#define rndis_borrow_net(...) do {} while (0)
|
||||
#endif
|
||||
#include "f_eem.c"
|
||||
#include "u_ether.c"
|
||||
#include "u_eem.h"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
||||
USB_ETHERNET_MODULE_PARAMETERS();
|
||||
|
||||
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
|
||||
* Instead: allocate your own, using normal USB-IF procedures.
|
||||
*/
|
||||
@ -206,8 +203,18 @@ static struct usb_gadget_strings *dev_strings[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
static struct eth_dev *the_dev;
|
||||
static struct usb_function_instance *fi_ecm;
|
||||
static struct usb_function *f_ecm;
|
||||
|
||||
static struct usb_function_instance *fi_eem;
|
||||
static struct usb_function *f_eem;
|
||||
|
||||
static struct usb_function_instance *fi_geth;
|
||||
static struct usb_function *f_geth;
|
||||
|
||||
static struct usb_function_instance *fi_rndis;
|
||||
static struct usb_function *f_rndis;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
@ -217,6 +224,8 @@ static struct eth_dev *the_dev;
|
||||
*/
|
||||
static int __init rndis_do_config(struct usb_configuration *c)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* FIXME alloc iConfiguration string, set it in c->strings */
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
@ -224,7 +233,15 @@ static int __init rndis_do_config(struct usb_configuration *c)
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
return rndis_bind_config(c, hostaddr, the_dev);
|
||||
f_rndis = usb_get_function(fi_rndis);
|
||||
if (IS_ERR(f_rndis))
|
||||
return PTR_ERR(f_rndis);
|
||||
|
||||
status = usb_add_function(c, f_rndis);
|
||||
if (status < 0)
|
||||
usb_put_function(f_rndis);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static struct usb_configuration rndis_config_driver = {
|
||||
@ -249,6 +266,8 @@ MODULE_PARM_DESC(use_eem, "use CDC EEM mode");
|
||||
*/
|
||||
static int __init eth_do_config(struct usb_configuration *c)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
/* FIXME alloc iConfiguration string, set it in c->strings */
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
@ -256,12 +275,38 @@ static int __init eth_do_config(struct usb_configuration *c)
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
if (use_eem)
|
||||
return eem_bind_config(c, the_dev);
|
||||
else if (can_support_ecm(c->cdev->gadget))
|
||||
return ecm_bind_config(c, hostaddr, the_dev);
|
||||
else
|
||||
return geth_bind_config(c, hostaddr, the_dev);
|
||||
if (use_eem) {
|
||||
f_eem = usb_get_function(fi_eem);
|
||||
if (IS_ERR(f_eem))
|
||||
return PTR_ERR(f_eem);
|
||||
|
||||
status = usb_add_function(c, f_eem);
|
||||
if (status < 0)
|
||||
usb_put_function(f_eem);
|
||||
|
||||
return status;
|
||||
} else if (can_support_ecm(c->cdev->gadget)) {
|
||||
f_ecm = usb_get_function(fi_ecm);
|
||||
if (IS_ERR(f_ecm))
|
||||
return PTR_ERR(f_ecm);
|
||||
|
||||
status = usb_add_function(c, f_ecm);
|
||||
if (status < 0)
|
||||
usb_put_function(f_ecm);
|
||||
|
||||
return status;
|
||||
} else {
|
||||
f_geth = usb_get_function(fi_geth);
|
||||
if (IS_ERR(f_geth))
|
||||
return PTR_ERR(f_geth);
|
||||
|
||||
status = usb_add_function(c, f_geth);
|
||||
if (status < 0)
|
||||
usb_put_function(f_geth);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static struct usb_configuration eth_config_driver = {
|
||||
@ -276,24 +321,50 @@ static struct usb_configuration eth_config_driver = {
|
||||
static int __init eth_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct f_eem_opts *eem_opts = NULL;
|
||||
struct f_ecm_opts *ecm_opts = NULL;
|
||||
struct f_gether_opts *geth_opts = NULL;
|
||||
struct net_device *net;
|
||||
int status;
|
||||
|
||||
/* set up network link layer */
|
||||
the_dev = gether_setup(cdev->gadget, hostaddr);
|
||||
if (IS_ERR(the_dev))
|
||||
return PTR_ERR(the_dev);
|
||||
|
||||
/* set up main config label and device descriptor */
|
||||
if (use_eem) {
|
||||
/* EEM */
|
||||
fi_eem = usb_get_function_instance("eem");
|
||||
if (IS_ERR(fi_eem))
|
||||
return PTR_ERR(fi_eem);
|
||||
|
||||
eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst);
|
||||
|
||||
net = eem_opts->net;
|
||||
|
||||
eth_config_driver.label = "CDC Ethernet (EEM)";
|
||||
device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM);
|
||||
device_desc.idProduct = cpu_to_le16(EEM_PRODUCT_NUM);
|
||||
} else if (can_support_ecm(cdev->gadget)) {
|
||||
} else if (can_support_ecm(gadget)) {
|
||||
/* ECM */
|
||||
|
||||
fi_ecm = usb_get_function_instance("ecm");
|
||||
if (IS_ERR(fi_ecm))
|
||||
return PTR_ERR(fi_ecm);
|
||||
|
||||
ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
|
||||
|
||||
net = ecm_opts->net;
|
||||
|
||||
eth_config_driver.label = "CDC Ethernet (ECM)";
|
||||
} else {
|
||||
/* CDC Subset */
|
||||
|
||||
fi_geth = usb_get_function_instance("geth");
|
||||
if (IS_ERR(fi_geth))
|
||||
return PTR_ERR(fi_geth);
|
||||
|
||||
geth_opts = container_of(fi_geth, struct f_gether_opts,
|
||||
func_inst);
|
||||
|
||||
net = geth_opts->net;
|
||||
|
||||
eth_config_driver.label = "CDC Subset/SAFE";
|
||||
|
||||
device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM);
|
||||
@ -302,8 +373,34 @@ static int __init eth_bind(struct usb_composite_dev *cdev)
|
||||
device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
|
||||
}
|
||||
|
||||
gether_set_qmult(net, qmult);
|
||||
if (!gether_set_host_addr(net, host_addr))
|
||||
pr_info("using host ethernet address: %s", host_addr);
|
||||
if (!gether_set_dev_addr(net, dev_addr))
|
||||
pr_info("using self ethernet address: %s", dev_addr);
|
||||
|
||||
if (has_rndis()) {
|
||||
/* RNDIS plus ECM-or-Subset */
|
||||
gether_set_gadget(net, cdev->gadget);
|
||||
status = gether_register_netdev(net);
|
||||
if (status)
|
||||
goto fail;
|
||||
|
||||
if (use_eem)
|
||||
eem_opts->bound = true;
|
||||
else if (can_support_ecm(gadget))
|
||||
ecm_opts->bound = true;
|
||||
else
|
||||
geth_opts->bound = true;
|
||||
|
||||
fi_rndis = usb_get_function_instance("rndis");
|
||||
if (IS_ERR(fi_rndis)) {
|
||||
status = PTR_ERR(fi_rndis);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rndis_borrow_net(fi_rndis, net);
|
||||
|
||||
device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM);
|
||||
device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM);
|
||||
device_desc.bNumConfigurations = 2;
|
||||
@ -315,7 +412,7 @@ static int __init eth_bind(struct usb_composite_dev *cdev)
|
||||
|
||||
status = usb_string_ids_tab(cdev, strings_dev);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
goto fail1;
|
||||
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
|
||||
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
|
||||
|
||||
@ -324,12 +421,12 @@ static int __init eth_bind(struct usb_composite_dev *cdev)
|
||||
status = usb_add_config(cdev, &rndis_config_driver,
|
||||
rndis_do_config);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
status = usb_add_config(cdev, ð_config_driver, eth_do_config);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
goto fail1;
|
||||
|
||||
usb_composite_overwrite_options(cdev, &coverwrite);
|
||||
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
|
||||
@ -337,14 +434,29 @@ static int __init eth_bind(struct usb_composite_dev *cdev)
|
||||
|
||||
return 0;
|
||||
|
||||
fail1:
|
||||
if (has_rndis())
|
||||
usb_put_function_instance(fi_rndis);
|
||||
fail:
|
||||
gether_cleanup(the_dev);
|
||||
if (use_eem)
|
||||
usb_put_function_instance(fi_eem);
|
||||
else if (can_support_ecm(gadget))
|
||||
usb_put_function_instance(fi_ecm);
|
||||
else
|
||||
usb_put_function_instance(fi_geth);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit eth_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
gether_cleanup(the_dev);
|
||||
if (has_rndis())
|
||||
usb_put_function_instance(fi_rndis);
|
||||
if (use_eem)
|
||||
usb_put_function_instance(fi_eem);
|
||||
else if (can_support_ecm(cdev->gadget))
|
||||
usb_put_function_instance(fi_ecm);
|
||||
else
|
||||
usb_put_function_instance(fi_geth);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -14,10 +14,13 @@
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include "u_ether.h"
|
||||
#include "u_ether_configfs.h"
|
||||
#include "u_ecm.h"
|
||||
|
||||
|
||||
/*
|
||||
@ -684,9 +687,44 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_ecm *ecm = func_to_ecm(f);
|
||||
struct usb_string *us;
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
|
||||
#ifndef USBF_ECM_INCLUDED
|
||||
struct f_ecm_opts *ecm_opts;
|
||||
|
||||
if (!can_support_ecm(cdev->gadget))
|
||||
return -EINVAL;
|
||||
|
||||
ecm_opts = container_of(f->fi, struct f_ecm_opts, func_inst);
|
||||
|
||||
/*
|
||||
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
|
||||
* configurations are bound in sequence with list_for_each_entry,
|
||||
* in each configuration its functions are bound in sequence
|
||||
* with list_for_each_entry, so we assume no race condition
|
||||
* with regard to ecm_opts->bound access
|
||||
*/
|
||||
if (!ecm_opts->bound) {
|
||||
mutex_lock(&ecm_opts->lock);
|
||||
gether_set_gadget(ecm_opts->net, cdev->gadget);
|
||||
status = gether_register_netdev(ecm_opts->net);
|
||||
mutex_unlock(&ecm_opts->lock);
|
||||
if (status)
|
||||
return status;
|
||||
ecm_opts->bound = true;
|
||||
}
|
||||
#endif
|
||||
us = usb_gstrings_attach(cdev, ecm_strings,
|
||||
ARRAY_SIZE(ecm_string_defs));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
ecm_control_intf.iInterface = us[0].id;
|
||||
ecm_data_intf.iInterface = us[2].id;
|
||||
ecm_desc.iMACAddress = us[1].id;
|
||||
ecm_iad_descriptor.iFunction = us[3].id;
|
||||
|
||||
/* allocate instance-specific interface IDs */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
@ -796,14 +834,15 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef USBF_ECM_INCLUDED
|
||||
|
||||
static void
|
||||
ecm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
ecm_old_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_ecm *ecm = func_to_ecm(f);
|
||||
|
||||
DBG(c->cdev, "ecm unbind\n");
|
||||
|
||||
ecm_string_defs[0].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
kfree(ecm->notify_req->buf);
|
||||
@ -834,17 +873,6 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
if (!can_support_ecm(c->cdev->gadget) || !ethaddr)
|
||||
return -EINVAL;
|
||||
|
||||
if (ecm_string_defs[0].id == 0) {
|
||||
status = usb_string_ids_tab(c->cdev, ecm_string_defs);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
ecm_control_intf.iInterface = ecm_string_defs[0].id;
|
||||
ecm_data_intf.iInterface = ecm_string_defs[2].id;
|
||||
ecm_desc.iMACAddress = ecm_string_defs[1].id;
|
||||
ecm_iad_descriptor.iFunction = ecm_string_defs[3].id;
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
ecm = kzalloc(sizeof *ecm, GFP_KERNEL);
|
||||
if (!ecm)
|
||||
@ -858,10 +886,9 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
ecm->port.cdc_filter = DEFAULT_FILTER;
|
||||
|
||||
ecm->port.func.name = "cdc_ethernet";
|
||||
ecm->port.func.strings = ecm_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
ecm->port.func.bind = ecm_bind;
|
||||
ecm->port.func.unbind = ecm_unbind;
|
||||
ecm->port.func.unbind = ecm_old_unbind;
|
||||
ecm->port.func.set_alt = ecm_set_alt;
|
||||
ecm->port.func.get_alt = ecm_get_alt;
|
||||
ecm->port.func.setup = ecm_setup;
|
||||
@ -872,3 +899,142 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
kfree(ecm);
|
||||
return status;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct f_ecm_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
/* f_ecm_item_ops */
|
||||
USB_ETHERNET_CONFIGFS_ITEM(ecm);
|
||||
|
||||
/* f_ecm_opts_dev_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ecm);
|
||||
|
||||
/* f_ecm_opts_host_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ecm);
|
||||
|
||||
/* f_ecm_opts_qmult */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ecm);
|
||||
|
||||
/* f_ecm_opts_ifname */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ecm);
|
||||
|
||||
static struct configfs_attribute *ecm_attrs[] = {
|
||||
&f_ecm_opts_dev_addr.attr,
|
||||
&f_ecm_opts_host_addr.attr,
|
||||
&f_ecm_opts_qmult.attr,
|
||||
&f_ecm_opts_ifname.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type ecm_func_type = {
|
||||
.ct_item_ops = &ecm_item_ops,
|
||||
.ct_attrs = ecm_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void ecm_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_ecm_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_ecm_opts, func_inst);
|
||||
if (opts->bound)
|
||||
gether_cleanup(netdev_priv(opts->net));
|
||||
else
|
||||
free_netdev(opts->net);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *ecm_alloc_inst(void)
|
||||
{
|
||||
struct f_ecm_opts *opts;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = ecm_free_inst;
|
||||
opts->net = gether_setup_default();
|
||||
if (IS_ERR(opts->net))
|
||||
return ERR_PTR(PTR_ERR(opts->net));
|
||||
|
||||
config_group_init_type_name(&opts->func_inst.group, "", &ecm_func_type);
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void ecm_free(struct usb_function *f)
|
||||
{
|
||||
struct f_ecm *ecm;
|
||||
struct f_ecm_opts *opts;
|
||||
|
||||
ecm = func_to_ecm(f);
|
||||
opts = container_of(f->fi, struct f_ecm_opts, func_inst);
|
||||
kfree(ecm);
|
||||
mutex_lock(&opts->lock);
|
||||
opts->refcnt--;
|
||||
mutex_unlock(&opts->lock);
|
||||
}
|
||||
|
||||
static void ecm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_ecm *ecm = func_to_ecm(f);
|
||||
|
||||
DBG(c->cdev, "ecm unbind\n");
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
kfree(ecm->notify_req->buf);
|
||||
usb_ep_free_request(ecm->notify, ecm->notify_req);
|
||||
}
|
||||
|
||||
struct usb_function *ecm_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_ecm *ecm;
|
||||
struct f_ecm_opts *opts;
|
||||
int status;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
ecm = kzalloc(sizeof(*ecm), GFP_KERNEL);
|
||||
if (!ecm)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts = container_of(fi, struct f_ecm_opts, func_inst);
|
||||
mutex_lock(&opts->lock);
|
||||
opts->refcnt++;
|
||||
|
||||
/* export host's Ethernet address in CDC format */
|
||||
status = gether_get_host_addr_cdc(opts->net, ecm->ethaddr,
|
||||
sizeof(ecm->ethaddr));
|
||||
if (status < 12) {
|
||||
kfree(ecm);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
ecm_string_defs[1].s = ecm->ethaddr;
|
||||
|
||||
ecm->port.ioport = netdev_priv(opts->net);
|
||||
mutex_unlock(&opts->lock);
|
||||
ecm->port.cdc_filter = DEFAULT_FILTER;
|
||||
|
||||
ecm->port.func.name = "cdc_ethernet";
|
||||
/* descriptors are per-instance copies */
|
||||
ecm->port.func.bind = ecm_bind;
|
||||
ecm->port.func.unbind = ecm_unbind;
|
||||
ecm->port.func.set_alt = ecm_set_alt;
|
||||
ecm->port.func.get_alt = ecm_get_alt;
|
||||
ecm->port.func.setup = ecm_setup;
|
||||
ecm->port.func.disable = ecm_disable;
|
||||
ecm->port.func.free_func = ecm_free;
|
||||
|
||||
return &ecm->port.func;
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
|
||||
#endif
|
||||
|
@ -12,12 +12,15 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "u_ether.h"
|
||||
#include "u_ether_configfs.h"
|
||||
#include "u_eem.h"
|
||||
|
||||
#define EEM_HLEN 2
|
||||
|
||||
@ -40,7 +43,7 @@ static inline struct f_eem *func_to_eem(struct usb_function *f)
|
||||
|
||||
/* interface descriptor: */
|
||||
|
||||
static struct usb_interface_descriptor eem_intf __initdata = {
|
||||
static struct usb_interface_descriptor eem_intf = {
|
||||
.bLength = sizeof eem_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
@ -54,7 +57,7 @@ static struct usb_interface_descriptor eem_intf __initdata = {
|
||||
|
||||
/* full speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor eem_fs_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor eem_fs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -62,7 +65,7 @@ static struct usb_endpoint_descriptor eem_fs_in_desc __initdata = {
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor eem_fs_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor eem_fs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -70,7 +73,7 @@ static struct usb_endpoint_descriptor eem_fs_out_desc __initdata = {
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *eem_fs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *eem_fs_function[] = {
|
||||
/* CDC EEM control descriptors */
|
||||
(struct usb_descriptor_header *) &eem_intf,
|
||||
(struct usb_descriptor_header *) &eem_fs_in_desc,
|
||||
@ -80,7 +83,7 @@ static struct usb_descriptor_header *eem_fs_function[] __initdata = {
|
||||
|
||||
/* high speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor eem_hs_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor eem_hs_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -89,7 +92,7 @@ static struct usb_endpoint_descriptor eem_hs_in_desc __initdata = {
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor eem_hs_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor eem_hs_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -98,7 +101,7 @@ static struct usb_endpoint_descriptor eem_hs_out_desc __initdata = {
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *eem_hs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *eem_hs_function[] = {
|
||||
/* CDC EEM control descriptors */
|
||||
(struct usb_descriptor_header *) &eem_intf,
|
||||
(struct usb_descriptor_header *) &eem_hs_in_desc,
|
||||
@ -108,7 +111,7 @@ static struct usb_descriptor_header *eem_hs_function[] __initdata = {
|
||||
|
||||
/* super speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor eem_ss_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor eem_ss_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -117,7 +120,7 @@ static struct usb_endpoint_descriptor eem_ss_in_desc __initdata = {
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor eem_ss_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor eem_ss_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -126,7 +129,7 @@ static struct usb_endpoint_descriptor eem_ss_out_desc __initdata = {
|
||||
.wMaxPacketSize = cpu_to_le16(1024),
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc __initdata = {
|
||||
static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc = {
|
||||
.bLength = sizeof eem_ss_bulk_comp_desc,
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
|
||||
@ -135,7 +138,7 @@ static struct usb_ss_ep_comp_descriptor eem_ss_bulk_comp_desc __initdata = {
|
||||
/* .bmAttributes = 0, */
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *eem_ss_function[] __initdata = {
|
||||
static struct usb_descriptor_header *eem_ss_function[] = {
|
||||
/* CDC EEM control descriptors */
|
||||
(struct usb_descriptor_header *) &eem_intf,
|
||||
(struct usb_descriptor_header *) &eem_ss_in_desc,
|
||||
@ -242,14 +245,40 @@ static void eem_disable(struct usb_function *f)
|
||||
|
||||
/* EEM function driver setup/binding */
|
||||
|
||||
static int __init
|
||||
eem_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
static int eem_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_eem *eem = func_to_eem(f);
|
||||
struct usb_string *us;
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
|
||||
struct f_eem_opts *eem_opts;
|
||||
|
||||
eem_opts = container_of(f->fi, struct f_eem_opts, func_inst);
|
||||
/*
|
||||
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
|
||||
* configurations are bound in sequence with list_for_each_entry,
|
||||
* in each configuration its functions are bound in sequence
|
||||
* with list_for_each_entry, so we assume no race condition
|
||||
* with regard to eem_opts->bound access
|
||||
*/
|
||||
if (!eem_opts->bound) {
|
||||
mutex_lock(&eem_opts->lock);
|
||||
gether_set_gadget(eem_opts->net, cdev->gadget);
|
||||
status = gether_register_netdev(eem_opts->net);
|
||||
mutex_unlock(&eem_opts->lock);
|
||||
if (status)
|
||||
return status;
|
||||
eem_opts->bound = true;
|
||||
}
|
||||
|
||||
us = usb_gstrings_attach(cdev, eem_strings,
|
||||
ARRAY_SIZE(eem_string_defs));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
eem_intf.iInterface = us[0].id;
|
||||
|
||||
/* allocate instance-specific interface IDs */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
@ -307,17 +336,6 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
eem_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_eem *eem = func_to_eem(f);
|
||||
|
||||
DBG(c->cdev, "eem unbind\n");
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
kfree(eem);
|
||||
}
|
||||
|
||||
static void eem_cmd_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
{
|
||||
struct sk_buff *skb = (struct sk_buff *)req->context;
|
||||
@ -518,55 +536,124 @@ error:
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* eem_bind_config - add CDC Ethernet (EEM) network link to a configuration
|
||||
* @c: the configuration to support the network link
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* Caller must have called @gether_setup(). Caller is also responsible
|
||||
* for calling @gether_cleanup() before module unload.
|
||||
*/
|
||||
int __init eem_bind_config(struct usb_configuration *c, struct eth_dev *dev)
|
||||
static inline struct f_eem_opts *to_f_eem_opts(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct f_eem_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
/* f_eem_item_ops */
|
||||
USB_ETHERNET_CONFIGFS_ITEM(eem);
|
||||
|
||||
/* f_eem_opts_dev_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(eem);
|
||||
|
||||
/* f_eem_opts_host_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(eem);
|
||||
|
||||
/* f_eem_opts_qmult */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(eem);
|
||||
|
||||
/* f_eem_opts_ifname */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(eem);
|
||||
|
||||
static struct configfs_attribute *eem_attrs[] = {
|
||||
&f_eem_opts_dev_addr.attr,
|
||||
&f_eem_opts_host_addr.attr,
|
||||
&f_eem_opts_qmult.attr,
|
||||
&f_eem_opts_ifname.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type eem_func_type = {
|
||||
.ct_item_ops = &eem_item_ops,
|
||||
.ct_attrs = eem_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void eem_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_eem_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_eem_opts, func_inst);
|
||||
if (opts->bound)
|
||||
gether_cleanup(netdev_priv(opts->net));
|
||||
else
|
||||
free_netdev(opts->net);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *eem_alloc_inst(void)
|
||||
{
|
||||
struct f_eem_opts *opts;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = eem_free_inst;
|
||||
opts->net = gether_setup_default();
|
||||
if (IS_ERR(opts->net))
|
||||
return ERR_CAST(opts->net);
|
||||
|
||||
config_group_init_type_name(&opts->func_inst.group, "", &eem_func_type);
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void eem_free(struct usb_function *f)
|
||||
{
|
||||
struct f_eem *eem;
|
||||
struct f_eem_opts *opts;
|
||||
|
||||
eem = func_to_eem(f);
|
||||
opts = container_of(f->fi, struct f_eem_opts, func_inst);
|
||||
kfree(eem);
|
||||
mutex_lock(&opts->lock);
|
||||
opts->refcnt--;
|
||||
mutex_unlock(&opts->lock);
|
||||
}
|
||||
|
||||
static void eem_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
DBG(c->cdev, "eem unbind\n");
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
struct usb_function *eem_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_eem *eem;
|
||||
int status;
|
||||
|
||||
/* maybe allocate device-global string IDs */
|
||||
if (eem_string_defs[0].id == 0) {
|
||||
|
||||
/* control interface label */
|
||||
status = usb_string_id(c->cdev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
eem_string_defs[0].id = status;
|
||||
eem_intf.iInterface = status;
|
||||
}
|
||||
struct f_eem_opts *opts;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
eem = kzalloc(sizeof *eem, GFP_KERNEL);
|
||||
eem = kzalloc(sizeof(*eem), GFP_KERNEL);
|
||||
if (!eem)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
eem->port.ioport = dev;
|
||||
opts = container_of(fi, struct f_eem_opts, func_inst);
|
||||
mutex_lock(&opts->lock);
|
||||
opts->refcnt++;
|
||||
|
||||
eem->port.ioport = netdev_priv(opts->net);
|
||||
mutex_unlock(&opts->lock);
|
||||
eem->port.cdc_filter = DEFAULT_FILTER;
|
||||
|
||||
eem->port.func.name = "cdc_eem";
|
||||
eem->port.func.strings = eem_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
eem->port.func.bind = eem_bind;
|
||||
eem->port.func.unbind = eem_unbind;
|
||||
eem->port.func.set_alt = eem_set_alt;
|
||||
eem->port.func.setup = eem_setup;
|
||||
eem->port.func.disable = eem_disable;
|
||||
eem->port.func.free_func = eem_free;
|
||||
eem->port.wrap = eem_wrap;
|
||||
eem->port.unwrap = eem_unwrap;
|
||||
eem->port.header_len = EEM_HLEN;
|
||||
|
||||
status = usb_add_function(c, &eem->port.func);
|
||||
if (status)
|
||||
kfree(eem);
|
||||
return status;
|
||||
return &eem->port.func;
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
|
@ -413,6 +413,7 @@ static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
|
||||
/* Caller must hold fsg->lock */
|
||||
static void wakeup_thread(struct fsg_common *common)
|
||||
{
|
||||
smp_wmb(); /* ensure the write of bh->state is complete */
|
||||
/* Tell the main thread that something has happened */
|
||||
common->thread_wakeup_needed = 1;
|
||||
if (common->thread_task)
|
||||
@ -632,6 +633,7 @@ static int sleep_thread(struct fsg_common *common)
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
common->thread_wakeup_needed = 0;
|
||||
smp_rmb(); /* ensure the latest bh->state is visible */
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -2745,8 +2747,8 @@ buffhds_first_it:
|
||||
"%-8s%-16s%04x", cfg->vendor_name ?: "Linux",
|
||||
/* Assume product name dependent on the first LUN */
|
||||
cfg->product_name ?: (common->luns->cdrom
|
||||
? "File-Stor Gadget"
|
||||
: "File-CD Gadget"),
|
||||
? "File-CD Gadget"
|
||||
: "File-Stor Gadget"),
|
||||
i);
|
||||
|
||||
/*
|
||||
|
@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/crc32.h>
|
||||
@ -23,6 +24,8 @@
|
||||
#include <linux/usb/cdc.h>
|
||||
|
||||
#include "u_ether.h"
|
||||
#include "u_ether_configfs.h"
|
||||
#include "u_ncm.h"
|
||||
|
||||
/*
|
||||
* This function is a "CDC Network Control Model" (CDC NCM) Ethernet link.
|
||||
@ -125,7 +128,7 @@ static struct usb_cdc_ncm_ntb_parameters ntb_parameters = {
|
||||
#define NCM_STATUS_INTERVAL_MS 32
|
||||
#define NCM_STATUS_BYTECOUNT 16 /* 8 byte header + data */
|
||||
|
||||
static struct usb_interface_assoc_descriptor ncm_iad_desc __initdata = {
|
||||
static struct usb_interface_assoc_descriptor ncm_iad_desc = {
|
||||
.bLength = sizeof ncm_iad_desc,
|
||||
.bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
|
||||
|
||||
@ -139,7 +142,7 @@ static struct usb_interface_assoc_descriptor ncm_iad_desc __initdata = {
|
||||
|
||||
/* interface descriptor: */
|
||||
|
||||
static struct usb_interface_descriptor ncm_control_intf __initdata = {
|
||||
static struct usb_interface_descriptor ncm_control_intf = {
|
||||
.bLength = sizeof ncm_control_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
@ -151,7 +154,7 @@ static struct usb_interface_descriptor ncm_control_intf __initdata = {
|
||||
/* .iInterface = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_cdc_header_desc ncm_header_desc __initdata = {
|
||||
static struct usb_cdc_header_desc ncm_header_desc = {
|
||||
.bLength = sizeof ncm_header_desc,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
|
||||
@ -159,7 +162,7 @@ static struct usb_cdc_header_desc ncm_header_desc __initdata = {
|
||||
.bcdCDC = cpu_to_le16(0x0110),
|
||||
};
|
||||
|
||||
static struct usb_cdc_union_desc ncm_union_desc __initdata = {
|
||||
static struct usb_cdc_union_desc ncm_union_desc = {
|
||||
.bLength = sizeof(ncm_union_desc),
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_UNION_TYPE,
|
||||
@ -167,7 +170,7 @@ static struct usb_cdc_union_desc ncm_union_desc __initdata = {
|
||||
/* .bSlaveInterface0 = DYNAMIC */
|
||||
};
|
||||
|
||||
static struct usb_cdc_ether_desc ecm_desc __initdata = {
|
||||
static struct usb_cdc_ether_desc ecm_desc = {
|
||||
.bLength = sizeof ecm_desc,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
|
||||
@ -182,7 +185,7 @@ static struct usb_cdc_ether_desc ecm_desc __initdata = {
|
||||
|
||||
#define NCAPS (USB_CDC_NCM_NCAP_ETH_FILTER | USB_CDC_NCM_NCAP_CRC_MODE)
|
||||
|
||||
static struct usb_cdc_ncm_desc ncm_desc __initdata = {
|
||||
static struct usb_cdc_ncm_desc ncm_desc = {
|
||||
.bLength = sizeof ncm_desc,
|
||||
.bDescriptorType = USB_DT_CS_INTERFACE,
|
||||
.bDescriptorSubType = USB_CDC_NCM_TYPE,
|
||||
@ -194,7 +197,7 @@ static struct usb_cdc_ncm_desc ncm_desc __initdata = {
|
||||
|
||||
/* the default data interface has no endpoints ... */
|
||||
|
||||
static struct usb_interface_descriptor ncm_data_nop_intf __initdata = {
|
||||
static struct usb_interface_descriptor ncm_data_nop_intf = {
|
||||
.bLength = sizeof ncm_data_nop_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
@ -209,7 +212,7 @@ static struct usb_interface_descriptor ncm_data_nop_intf __initdata = {
|
||||
|
||||
/* ... but the "real" data interface has two bulk endpoints */
|
||||
|
||||
static struct usb_interface_descriptor ncm_data_intf __initdata = {
|
||||
static struct usb_interface_descriptor ncm_data_intf = {
|
||||
.bLength = sizeof ncm_data_intf,
|
||||
.bDescriptorType = USB_DT_INTERFACE,
|
||||
|
||||
@ -224,7 +227,7 @@ static struct usb_interface_descriptor ncm_data_intf __initdata = {
|
||||
|
||||
/* full speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor fs_ncm_notify_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor fs_ncm_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -234,7 +237,7 @@ static struct usb_endpoint_descriptor fs_ncm_notify_desc __initdata = {
|
||||
.bInterval = NCM_STATUS_INTERVAL_MS,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_ncm_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor fs_ncm_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -242,7 +245,7 @@ static struct usb_endpoint_descriptor fs_ncm_in_desc __initdata = {
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor fs_ncm_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor fs_ncm_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -250,7 +253,7 @@ static struct usb_endpoint_descriptor fs_ncm_out_desc __initdata = {
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *ncm_fs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *ncm_fs_function[] = {
|
||||
(struct usb_descriptor_header *) &ncm_iad_desc,
|
||||
/* CDC NCM control descriptors */
|
||||
(struct usb_descriptor_header *) &ncm_control_intf,
|
||||
@ -269,7 +272,7 @@ static struct usb_descriptor_header *ncm_fs_function[] __initdata = {
|
||||
|
||||
/* high speed support: */
|
||||
|
||||
static struct usb_endpoint_descriptor hs_ncm_notify_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor hs_ncm_notify_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -278,7 +281,7 @@ static struct usb_endpoint_descriptor hs_ncm_notify_desc __initdata = {
|
||||
.wMaxPacketSize = cpu_to_le16(NCM_STATUS_BYTECOUNT),
|
||||
.bInterval = USB_MS_TO_HS_INTERVAL(NCM_STATUS_INTERVAL_MS),
|
||||
};
|
||||
static struct usb_endpoint_descriptor hs_ncm_in_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor hs_ncm_in_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -287,7 +290,7 @@ static struct usb_endpoint_descriptor hs_ncm_in_desc __initdata = {
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor hs_ncm_out_desc __initdata = {
|
||||
static struct usb_endpoint_descriptor hs_ncm_out_desc = {
|
||||
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||
.bDescriptorType = USB_DT_ENDPOINT,
|
||||
|
||||
@ -296,7 +299,7 @@ static struct usb_endpoint_descriptor hs_ncm_out_desc __initdata = {
|
||||
.wMaxPacketSize = cpu_to_le16(512),
|
||||
};
|
||||
|
||||
static struct usb_descriptor_header *ncm_hs_function[] __initdata = {
|
||||
static struct usb_descriptor_header *ncm_hs_function[] = {
|
||||
(struct usb_descriptor_header *) &ncm_iad_desc,
|
||||
/* CDC NCM control descriptors */
|
||||
(struct usb_descriptor_header *) &ncm_control_intf,
|
||||
@ -1152,13 +1155,44 @@ static void ncm_close(struct gether *geth)
|
||||
|
||||
/* ethernet function driver setup/binding */
|
||||
|
||||
static int __init
|
||||
ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_ncm *ncm = func_to_ncm(f);
|
||||
struct usb_string *us;
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
struct f_ncm_opts *ncm_opts;
|
||||
|
||||
if (!can_support_ecm(cdev->gadget))
|
||||
return -EINVAL;
|
||||
|
||||
ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
|
||||
/*
|
||||
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
|
||||
* configurations are bound in sequence with list_for_each_entry,
|
||||
* in each configuration its functions are bound in sequence
|
||||
* with list_for_each_entry, so we assume no race condition
|
||||
* with regard to ncm_opts->bound access
|
||||
*/
|
||||
if (!ncm_opts->bound) {
|
||||
mutex_lock(&ncm_opts->lock);
|
||||
gether_set_gadget(ncm_opts->net, cdev->gadget);
|
||||
status = gether_register_netdev(ncm_opts->net);
|
||||
mutex_unlock(&ncm_opts->lock);
|
||||
if (status)
|
||||
return status;
|
||||
ncm_opts->bound = true;
|
||||
}
|
||||
us = usb_gstrings_attach(cdev, ncm_strings,
|
||||
ARRAY_SIZE(ncm_string_defs));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
ncm_control_intf.iInterface = us[STRING_CTRL_IDX].id;
|
||||
ncm_data_nop_intf.iInterface = us[STRING_DATA_IDX].id;
|
||||
ncm_data_intf.iInterface = us[STRING_DATA_IDX].id;
|
||||
ecm_desc.iMACAddress = us[STRING_MAC_IDX].id;
|
||||
ncm_iad_desc.iFunction = us[STRING_IAD_IDX].id;
|
||||
|
||||
/* allocate instance-specific interface IDs */
|
||||
status = usb_interface_id(c, f);
|
||||
@ -1259,74 +1293,127 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
ncm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
|
||||
{
|
||||
struct f_ncm *ncm = func_to_ncm(f);
|
||||
return container_of(to_config_group(item), struct f_ncm_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
/* f_ncm_item_ops */
|
||||
USB_ETHERNET_CONFIGFS_ITEM(ncm);
|
||||
|
||||
/* f_ncm_opts_dev_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
|
||||
|
||||
/* f_ncm_opts_host_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
|
||||
|
||||
/* f_ncm_opts_qmult */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
|
||||
|
||||
/* f_ncm_opts_ifname */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
|
||||
|
||||
static struct configfs_attribute *ncm_attrs[] = {
|
||||
&f_ncm_opts_dev_addr.attr,
|
||||
&f_ncm_opts_host_addr.attr,
|
||||
&f_ncm_opts_qmult.attr,
|
||||
&f_ncm_opts_ifname.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type ncm_func_type = {
|
||||
.ct_item_ops = &ncm_item_ops,
|
||||
.ct_attrs = ncm_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void ncm_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_ncm_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_ncm_opts, func_inst);
|
||||
if (opts->bound)
|
||||
gether_cleanup(netdev_priv(opts->net));
|
||||
else
|
||||
free_netdev(opts->net);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *ncm_alloc_inst(void)
|
||||
{
|
||||
struct f_ncm_opts *opts;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = ncm_free_inst;
|
||||
opts->net = gether_setup_default();
|
||||
if (IS_ERR(opts->net))
|
||||
return ERR_PTR(PTR_ERR(opts->net));
|
||||
|
||||
config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void ncm_free(struct usb_function *f)
|
||||
{
|
||||
struct f_ncm *ncm;
|
||||
struct f_ncm_opts *opts;
|
||||
|
||||
ncm = func_to_ncm(f);
|
||||
opts = container_of(f->fi, struct f_ncm_opts, func_inst);
|
||||
kfree(ncm);
|
||||
mutex_lock(&opts->lock);
|
||||
opts->refcnt--;
|
||||
mutex_unlock(&opts->lock);
|
||||
}
|
||||
|
||||
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_ncm *ncm = func_to_ncm(f);
|
||||
|
||||
DBG(c->cdev, "ncm unbind\n");
|
||||
|
||||
ncm_string_defs[0].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
kfree(ncm->notify_req->buf);
|
||||
usb_ep_free_request(ncm->notify, ncm->notify_req);
|
||||
|
||||
kfree(ncm);
|
||||
}
|
||||
|
||||
/**
|
||||
* ncm_bind_config - add CDC Network link to a configuration
|
||||
* @c: the configuration to support the network link
|
||||
* @ethaddr: a buffer in which the ethernet address of the host side
|
||||
* side of the link was recorded
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* Caller must have called @gether_setup(). Caller is also responsible
|
||||
* for calling @gether_cleanup() before module unload.
|
||||
*/
|
||||
int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
struct eth_dev *dev)
|
||||
struct usb_function *ncm_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_ncm *ncm;
|
||||
int status;
|
||||
|
||||
if (!can_support_ecm(c->cdev->gadget) || !ethaddr)
|
||||
return -EINVAL;
|
||||
|
||||
if (ncm_string_defs[0].id == 0) {
|
||||
status = usb_string_ids_tab(c->cdev, ncm_string_defs);
|
||||
if (status < 0)
|
||||
return status;
|
||||
ncm_control_intf.iInterface =
|
||||
ncm_string_defs[STRING_CTRL_IDX].id;
|
||||
|
||||
status = ncm_string_defs[STRING_DATA_IDX].id;
|
||||
ncm_data_nop_intf.iInterface = status;
|
||||
ncm_data_intf.iInterface = status;
|
||||
|
||||
ecm_desc.iMACAddress = ncm_string_defs[STRING_MAC_IDX].id;
|
||||
ncm_iad_desc.iFunction = ncm_string_defs[STRING_IAD_IDX].id;
|
||||
}
|
||||
struct f_ncm *ncm;
|
||||
struct f_ncm_opts *opts;
|
||||
int status;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
ncm = kzalloc(sizeof *ncm, GFP_KERNEL);
|
||||
ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
|
||||
if (!ncm)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts = container_of(fi, struct f_ncm_opts, func_inst);
|
||||
mutex_lock(&opts->lock);
|
||||
opts->refcnt++;
|
||||
|
||||
/* export host's Ethernet address in CDC format */
|
||||
snprintf(ncm->ethaddr, sizeof ncm->ethaddr, "%pm", ethaddr);
|
||||
status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
|
||||
sizeof(ncm->ethaddr));
|
||||
if (status < 12) { /* strlen("01234567890a") */
|
||||
kfree(ncm);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
|
||||
|
||||
spin_lock_init(&ncm->lock);
|
||||
ncm_reset_values(ncm);
|
||||
ncm->port.ioport = dev;
|
||||
ncm->port.ioport = netdev_priv(opts->net);
|
||||
mutex_unlock(&opts->lock);
|
||||
ncm->port.is_fixed = true;
|
||||
|
||||
ncm->port.func.name = "cdc_network";
|
||||
ncm->port.func.strings = ncm_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
ncm->port.func.bind = ncm_bind;
|
||||
ncm->port.func.unbind = ncm_unbind;
|
||||
@ -1334,12 +1421,14 @@ int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
ncm->port.func.get_alt = ncm_get_alt;
|
||||
ncm->port.func.setup = ncm_setup;
|
||||
ncm->port.func.disable = ncm_disable;
|
||||
ncm->port.func.free_func = ncm_free;
|
||||
|
||||
ncm->port.wrap = ncm_wrap_ntb;
|
||||
ncm->port.unwrap = ncm_unwrap_ntb;
|
||||
|
||||
status = usb_add_function(c, &ncm->port.func);
|
||||
if (status)
|
||||
kfree(ncm);
|
||||
return status;
|
||||
return &ncm->port.func;
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Yauheni Kaliuta");
|
||||
|
@ -309,23 +309,20 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_obex *obex = func_to_obex(f);
|
||||
struct usb_string *us;
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
|
||||
if (!can_support_obex(c))
|
||||
return -EINVAL;
|
||||
|
||||
if (obex_string_defs[OBEX_CTRL_IDX].id == 0) {
|
||||
status = usb_string_ids_tab(c->cdev, obex_string_defs);
|
||||
if (status < 0)
|
||||
return status;
|
||||
obex_control_intf.iInterface =
|
||||
obex_string_defs[OBEX_CTRL_IDX].id;
|
||||
|
||||
status = obex_string_defs[OBEX_DATA_IDX].id;
|
||||
obex_data_nop_intf.iInterface = status;
|
||||
obex_data_intf.iInterface = status;
|
||||
}
|
||||
us = usb_gstrings_attach(cdev, obex_strings,
|
||||
ARRAY_SIZE(obex_string_defs));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
obex_control_intf.iInterface = us[OBEX_CTRL_IDX].id;
|
||||
obex_data_nop_intf.iInterface = us[OBEX_DATA_IDX].id;
|
||||
obex_data_intf.iInterface = us[OBEX_DATA_IDX].id;
|
||||
|
||||
/* allocate instance-specific interface IDs, and patch descriptors */
|
||||
|
||||
@ -406,57 +403,6 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef USBF_OBEX_INCLUDED
|
||||
|
||||
static void
|
||||
obex_old_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
obex_string_defs[OBEX_CTRL_IDX].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
kfree(func_to_obex(f));
|
||||
}
|
||||
|
||||
/**
|
||||
* obex_bind_config - add a CDC OBEX function to a configuration
|
||||
* @c: the configuration to support the CDC OBEX instance
|
||||
* @port_num: /dev/ttyGS* port this interface will use
|
||||
* Context: single threaded during gadget setup
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
int __init obex_bind_config(struct usb_configuration *c, u8 port_num)
|
||||
{
|
||||
struct f_obex *obex;
|
||||
int status;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
obex = kzalloc(sizeof *obex, GFP_KERNEL);
|
||||
if (!obex)
|
||||
return -ENOMEM;
|
||||
|
||||
obex->port_num = port_num;
|
||||
|
||||
obex->port.connect = obex_connect;
|
||||
obex->port.disconnect = obex_disconnect;
|
||||
|
||||
obex->port.func.name = "obex";
|
||||
obex->port.func.strings = obex_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
obex->port.func.bind = obex_bind;
|
||||
obex->port.func.unbind = obex_old_unbind;
|
||||
obex->port.func.set_alt = obex_set_alt;
|
||||
obex->port.func.get_alt = obex_get_alt;
|
||||
obex->port.func.disable = obex_disable;
|
||||
|
||||
status = usb_add_function(c, &obex->port.func);
|
||||
if (status)
|
||||
kfree(obex);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct f_serial_opts,
|
||||
@ -550,7 +496,6 @@ static void obex_free(struct usb_function *f)
|
||||
|
||||
static void obex_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
obex_string_defs[OBEX_CTRL_IDX].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
@ -572,7 +517,6 @@ struct usb_function *obex_alloc(struct usb_function_instance *fi)
|
||||
obex->port.disconnect = obex_disconnect;
|
||||
|
||||
obex->port.func.name = "obex";
|
||||
obex->port.func.strings = obex_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
obex->port.func.bind = obex_bind;
|
||||
obex->port.func.unbind = obex_unbind;
|
||||
@ -585,8 +529,5 @@ struct usb_function *obex_alloc(struct usb_function_instance *fi)
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc);
|
||||
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Felipe Balbi");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
@ -25,6 +26,7 @@
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#include "u_phonet.h"
|
||||
#include "u_ether.h"
|
||||
|
||||
#define PN_MEDIA_USB 0x1B
|
||||
#define MAXPACKET 512
|
||||
@ -478,8 +480,7 @@ static void pn_disconnect(struct usb_function *f)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static __init
|
||||
int pn_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
static int pn_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
@ -487,6 +488,27 @@ int pn_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
struct usb_ep *ep;
|
||||
int status, i;
|
||||
|
||||
#ifndef USBF_PHONET_INCLUDED
|
||||
struct f_phonet_opts *phonet_opts;
|
||||
|
||||
phonet_opts = container_of(f->fi, struct f_phonet_opts, func_inst);
|
||||
|
||||
/*
|
||||
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
|
||||
* configurations are bound in sequence with list_for_each_entry,
|
||||
* in each configuration its functions are bound in sequence
|
||||
* with list_for_each_entry, so we assume no race condition
|
||||
* with regard to phonet_opts->bound access
|
||||
*/
|
||||
if (!phonet_opts->bound) {
|
||||
gphonet_set_gadget(phonet_opts->net, gadget);
|
||||
status = gphonet_register_netdev(phonet_opts->net);
|
||||
if (status)
|
||||
return status;
|
||||
phonet_opts->bound = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Reserve interface IDs */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
@ -560,8 +582,98 @@ err:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void
|
||||
pn_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
static inline struct f_phonet_opts *to_f_phonet_opts(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct f_phonet_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
CONFIGFS_ATTR_STRUCT(f_phonet_opts);
|
||||
static ssize_t f_phonet_attr_show(struct config_item *item,
|
||||
struct configfs_attribute *attr,
|
||||
char *page)
|
||||
{
|
||||
struct f_phonet_opts *opts = to_f_phonet_opts(item);
|
||||
struct f_phonet_opts_attribute *f_phonet_opts_attr =
|
||||
container_of(attr, struct f_phonet_opts_attribute, attr);
|
||||
ssize_t ret = 0;
|
||||
|
||||
if (f_phonet_opts_attr->show)
|
||||
ret = f_phonet_opts_attr->show(opts, page);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void phonet_attr_release(struct config_item *item)
|
||||
{
|
||||
struct f_phonet_opts *opts = to_f_phonet_opts(item);
|
||||
|
||||
usb_put_function_instance(&opts->func_inst);
|
||||
}
|
||||
|
||||
static struct configfs_item_operations phonet_item_ops = {
|
||||
.release = phonet_attr_release,
|
||||
.show_attribute = f_phonet_attr_show,
|
||||
};
|
||||
|
||||
static ssize_t f_phonet_ifname_show(struct f_phonet_opts *opts, char *page)
|
||||
{
|
||||
return gether_get_ifname(opts->net, page, PAGE_SIZE);
|
||||
}
|
||||
|
||||
static struct f_phonet_opts_attribute f_phonet_ifname =
|
||||
__CONFIGFS_ATTR_RO(ifname, f_phonet_ifname_show);
|
||||
|
||||
static struct configfs_attribute *phonet_attrs[] = {
|
||||
&f_phonet_ifname.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type phonet_func_type = {
|
||||
.ct_item_ops = &phonet_item_ops,
|
||||
.ct_attrs = phonet_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void phonet_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_phonet_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_phonet_opts, func_inst);
|
||||
if (opts->bound)
|
||||
gphonet_cleanup(opts->net);
|
||||
else
|
||||
free_netdev(opts->net);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *phonet_alloc_inst(void)
|
||||
{
|
||||
struct f_phonet_opts *opts;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts->func_inst.free_func_inst = phonet_free_inst;
|
||||
opts->net = gphonet_setup_default();
|
||||
if (IS_ERR(opts->net))
|
||||
return ERR_PTR(PTR_ERR(opts->net));
|
||||
|
||||
config_group_init_type_name(&opts->func_inst.group, "",
|
||||
&phonet_func_type);
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void phonet_free(struct usb_function *f)
|
||||
{
|
||||
struct f_phonet *phonet;
|
||||
|
||||
phonet = func_to_pn(f);
|
||||
kfree(phonet);
|
||||
}
|
||||
|
||||
static void pn_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_phonet *fp = func_to_pn(f);
|
||||
int i;
|
||||
@ -574,61 +686,72 @@ pn_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
usb_ep_free_request(fp->out_ep, fp->out_reqv[i]);
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
kfree(fp);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static struct net_device *dev;
|
||||
|
||||
int __init phonet_bind_config(struct usb_configuration *c)
|
||||
struct usb_function *phonet_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_phonet *fp;
|
||||
int err, size;
|
||||
struct f_phonet_opts *opts;
|
||||
int size;
|
||||
|
||||
size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *));
|
||||
fp = kzalloc(size, GFP_KERNEL);
|
||||
if (!fp)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fp->dev = dev;
|
||||
opts = container_of(fi, struct f_phonet_opts, func_inst);
|
||||
|
||||
fp->dev = opts->net;
|
||||
fp->function.name = "phonet";
|
||||
fp->function.bind = pn_bind;
|
||||
fp->function.unbind = pn_unbind;
|
||||
fp->function.set_alt = pn_set_alt;
|
||||
fp->function.get_alt = pn_get_alt;
|
||||
fp->function.disable = pn_disconnect;
|
||||
fp->function.free_func = phonet_free;
|
||||
spin_lock_init(&fp->rx.lock);
|
||||
|
||||
err = usb_add_function(c, &fp->function);
|
||||
if (err)
|
||||
kfree(fp);
|
||||
return err;
|
||||
return &fp->function;
|
||||
}
|
||||
|
||||
int __init gphonet_setup(struct usb_gadget *gadget)
|
||||
struct net_device *gphonet_setup_default(void)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct phonet_port *port;
|
||||
int err;
|
||||
|
||||
/* Create net device */
|
||||
BUG_ON(dev);
|
||||
dev = alloc_netdev(sizeof(*port), "upnlink%d", pn_net_setup);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
port = netdev_priv(dev);
|
||||
spin_lock_init(&port->lock);
|
||||
netif_carrier_off(dev);
|
||||
SET_NETDEV_DEV(dev, &gadget->dev);
|
||||
|
||||
err = register_netdev(dev);
|
||||
if (err)
|
||||
free_netdev(dev);
|
||||
return err;
|
||||
return dev;
|
||||
}
|
||||
|
||||
void gphonet_cleanup(void)
|
||||
void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g)
|
||||
{
|
||||
SET_NETDEV_DEV(net, &g->dev);
|
||||
}
|
||||
|
||||
int gphonet_register_netdev(struct net_device *net)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = register_netdev(net);
|
||||
if (status)
|
||||
free_netdev(net);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void gphonet_cleanup(struct net_device *dev)
|
||||
{
|
||||
unregister_netdev(dev);
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc);
|
||||
MODULE_AUTHOR("Rémi Denis-Courmont");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -17,15 +17,17 @@
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include <linux/atomic.h>
|
||||
|
||||
#include "u_ether.h"
|
||||
#include "u_ether_configfs.h"
|
||||
#include "u_rndis.h"
|
||||
#include "rndis.h"
|
||||
|
||||
|
||||
/*
|
||||
* This function is an RNDIS Ethernet port -- a Microsoft protocol that's
|
||||
* been promoted instead of the standard CDC Ethernet. The published RNDIS
|
||||
@ -655,6 +657,13 @@ static void rndis_close(struct gether *geth)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* Some controllers can't support RNDIS ... */
|
||||
static inline bool can_support_rndis(struct usb_configuration *c)
|
||||
{
|
||||
/* everything else is *presumably* fine */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ethernet function driver setup/binding */
|
||||
|
||||
static int
|
||||
@ -662,9 +671,41 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_rndis *rndis = func_to_rndis(f);
|
||||
struct usb_string *us;
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
|
||||
#ifndef USB_FRNDIS_INCLUDED
|
||||
struct f_rndis_opts *rndis_opts;
|
||||
|
||||
if (!can_support_rndis(c))
|
||||
return -EINVAL;
|
||||
|
||||
rndis_opts = container_of(f->fi, struct f_rndis_opts, func_inst);
|
||||
|
||||
/*
|
||||
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
|
||||
* configurations are bound in sequence with list_for_each_entry,
|
||||
* in each configuration its functions are bound in sequence
|
||||
* with list_for_each_entry, so we assume no race condition
|
||||
* with regard to rndis_opts->bound access
|
||||
*/
|
||||
if (!rndis_opts->bound) {
|
||||
gether_set_gadget(rndis_opts->net, cdev->gadget);
|
||||
status = gether_register_netdev(rndis_opts->net);
|
||||
if (status)
|
||||
return status;
|
||||
rndis_opts->bound = true;
|
||||
}
|
||||
#endif
|
||||
us = usb_gstrings_attach(cdev, rndis_strings,
|
||||
ARRAY_SIZE(rndis_string_defs));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
rndis_control_intf.iInterface = us[0].id;
|
||||
rndis_data_intf.iInterface = us[1].id;
|
||||
rndis_iad_descriptor.iFunction = us[2].id;
|
||||
|
||||
/* allocate instance-specific interface IDs */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
@ -741,10 +782,12 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
rndis->port.open = rndis_open;
|
||||
rndis->port.close = rndis_close;
|
||||
|
||||
#ifdef USB_FRNDIS_INCLUDED
|
||||
status = rndis_register(rndis_response_available, rndis);
|
||||
if (status < 0)
|
||||
goto fail;
|
||||
rndis->config = status;
|
||||
#endif
|
||||
|
||||
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
|
||||
rndis_set_host_mac(rndis->config, rndis->ethaddr);
|
||||
@ -787,15 +830,15 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef USB_FRNDIS_INCLUDED
|
||||
|
||||
static void
|
||||
rndis_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
rndis_old_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_rndis *rndis = func_to_rndis(f);
|
||||
|
||||
rndis_deregister(rndis->config);
|
||||
rndis_exit();
|
||||
|
||||
rndis_string_defs[0].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
kfree(rndis->notify_req->buf);
|
||||
@ -804,13 +847,6 @@ rndis_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
kfree(rndis);
|
||||
}
|
||||
|
||||
/* Some controllers can't support RNDIS ... */
|
||||
static inline bool can_support_rndis(struct usb_configuration *c)
|
||||
{
|
||||
/* everything else is *presumably* fine */
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
u32 vendorID, const char *manufacturer, struct eth_dev *dev)
|
||||
@ -818,24 +854,6 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
struct f_rndis *rndis;
|
||||
int status;
|
||||
|
||||
if (!can_support_rndis(c) || !ethaddr)
|
||||
return -EINVAL;
|
||||
|
||||
if (rndis_string_defs[0].id == 0) {
|
||||
/* ... and setup RNDIS itself */
|
||||
status = rndis_init();
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
status = usb_string_ids_tab(c->cdev, rndis_string_defs);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
rndis_control_intf.iInterface = rndis_string_defs[0].id;
|
||||
rndis_data_intf.iInterface = rndis_string_defs[1].id;
|
||||
rndis_iad_descriptor.iFunction = rndis_string_defs[2].id;
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
status = -ENOMEM;
|
||||
rndis = kzalloc(sizeof *rndis, GFP_KERNEL);
|
||||
@ -856,19 +874,178 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
rndis->port.unwrap = rndis_rm_hdr;
|
||||
|
||||
rndis->port.func.name = "rndis";
|
||||
rndis->port.func.strings = rndis_strings;
|
||||
/* descriptors are per-instance copies */
|
||||
rndis->port.func.bind = rndis_bind;
|
||||
rndis->port.func.unbind = rndis_old_unbind;
|
||||
rndis->port.func.set_alt = rndis_set_alt;
|
||||
rndis->port.func.setup = rndis_setup;
|
||||
rndis->port.func.disable = rndis_disable;
|
||||
|
||||
status = usb_add_function(c, &rndis->port.func);
|
||||
if (status)
|
||||
kfree(rndis);
|
||||
fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
|
||||
{
|
||||
struct f_rndis_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_rndis_opts, func_inst);
|
||||
if (opts->bound)
|
||||
gether_cleanup(netdev_priv(opts->net));
|
||||
else
|
||||
free_netdev(opts->net);
|
||||
opts->borrowed_net = opts->bound = true;
|
||||
opts->net = net;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_borrow_net);
|
||||
|
||||
static inline struct f_rndis_opts *to_f_rndis_opts(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct f_rndis_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
/* f_rndis_item_ops */
|
||||
USB_ETHERNET_CONFIGFS_ITEM(rndis);
|
||||
|
||||
/* f_rndis_opts_dev_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(rndis);
|
||||
|
||||
/* f_rndis_opts_host_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(rndis);
|
||||
|
||||
/* f_rndis_opts_qmult */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(rndis);
|
||||
|
||||
/* f_rndis_opts_ifname */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(rndis);
|
||||
|
||||
static struct configfs_attribute *rndis_attrs[] = {
|
||||
&f_rndis_opts_dev_addr.attr,
|
||||
&f_rndis_opts_host_addr.attr,
|
||||
&f_rndis_opts_qmult.attr,
|
||||
&f_rndis_opts_ifname.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type rndis_func_type = {
|
||||
.ct_item_ops = &rndis_item_ops,
|
||||
.ct_attrs = rndis_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void rndis_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_rndis_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_rndis_opts, func_inst);
|
||||
if (!opts->borrowed_net) {
|
||||
if (opts->bound)
|
||||
gether_cleanup(netdev_priv(opts->net));
|
||||
else
|
||||
free_netdev(opts->net);
|
||||
}
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *rndis_alloc_inst(void)
|
||||
{
|
||||
struct f_rndis_opts *opts;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = rndis_free_inst;
|
||||
opts->net = gether_setup_default();
|
||||
if (IS_ERR(opts->net))
|
||||
return ERR_CAST(opts->net);
|
||||
|
||||
config_group_init_type_name(&opts->func_inst.group, "",
|
||||
&rndis_func_type);
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void rndis_free(struct usb_function *f)
|
||||
{
|
||||
struct f_rndis *rndis;
|
||||
struct f_rndis_opts *opts;
|
||||
|
||||
rndis = func_to_rndis(f);
|
||||
rndis_deregister(rndis->config);
|
||||
opts = container_of(f->fi, struct f_rndis_opts, func_inst);
|
||||
kfree(rndis);
|
||||
mutex_lock(&opts->lock);
|
||||
opts->refcnt--;
|
||||
mutex_unlock(&opts->lock);
|
||||
}
|
||||
|
||||
static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct f_rndis *rndis = func_to_rndis(f);
|
||||
|
||||
usb_free_all_descriptors(f);
|
||||
|
||||
kfree(rndis->notify_req->buf);
|
||||
usb_ep_free_request(rndis->notify, rndis->notify_req);
|
||||
}
|
||||
|
||||
static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_rndis *rndis;
|
||||
struct f_rndis_opts *opts;
|
||||
int status;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
rndis = kzalloc(sizeof(*rndis), GFP_KERNEL);
|
||||
if (!rndis)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts = container_of(fi, struct f_rndis_opts, func_inst);
|
||||
mutex_lock(&opts->lock);
|
||||
opts->refcnt++;
|
||||
|
||||
gether_get_host_addr_u8(opts->net, rndis->ethaddr);
|
||||
rndis->vendorID = opts->vendor_id;
|
||||
rndis->manufacturer = opts->manufacturer;
|
||||
|
||||
rndis->port.ioport = netdev_priv(opts->net);
|
||||
mutex_unlock(&opts->lock);
|
||||
/* RNDIS activates when the host changes this filter */
|
||||
rndis->port.cdc_filter = 0;
|
||||
|
||||
/* RNDIS has special (and complex) framing */
|
||||
rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
|
||||
rndis->port.wrap = rndis_add_header;
|
||||
rndis->port.unwrap = rndis_rm_hdr;
|
||||
|
||||
rndis->port.func.name = "rndis";
|
||||
/* descriptors are per-instance copies */
|
||||
rndis->port.func.bind = rndis_bind;
|
||||
rndis->port.func.unbind = rndis_unbind;
|
||||
rndis->port.func.set_alt = rndis_set_alt;
|
||||
rndis->port.func.setup = rndis_setup;
|
||||
rndis->port.func.disable = rndis_disable;
|
||||
rndis->port.func.free_func = rndis_free;
|
||||
|
||||
status = usb_add_function(c, &rndis->port.func);
|
||||
if (status) {
|
||||
status = rndis_register(rndis_response_available, rndis);
|
||||
if (status < 0) {
|
||||
kfree(rndis);
|
||||
fail:
|
||||
rndis_exit();
|
||||
return ERR_PTR(status);
|
||||
}
|
||||
return status;
|
||||
rndis->config = status;
|
||||
|
||||
return &rndis->port.func;
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
|
||||
#endif
|
||||
|
@ -12,11 +12,13 @@
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include "u_ether.h"
|
||||
|
||||
#include "u_ether_configfs.h"
|
||||
#include "u_gether.h"
|
||||
|
||||
/*
|
||||
* This function packages a simple "CDC Subset" Ethernet port with no real
|
||||
@ -295,9 +297,40 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
struct usb_composite_dev *cdev = c->cdev;
|
||||
struct f_gether *geth = func_to_geth(f);
|
||||
struct usb_string *us;
|
||||
int status;
|
||||
struct usb_ep *ep;
|
||||
|
||||
#ifndef USB_FSUBSET_INCLUDED
|
||||
struct f_gether_opts *gether_opts;
|
||||
|
||||
gether_opts = container_of(f->fi, struct f_gether_opts, func_inst);
|
||||
|
||||
/*
|
||||
* in drivers/usb/gadget/configfs.c:configfs_composite_bind()
|
||||
* configurations are bound in sequence with list_for_each_entry,
|
||||
* in each configuration its functions are bound in sequence
|
||||
* with list_for_each_entry, so we assume no race condition
|
||||
* with regard to gether_opts->bound access
|
||||
*/
|
||||
if (!gether_opts->bound) {
|
||||
mutex_lock(&gether_opts->lock);
|
||||
gether_set_gadget(gether_opts->net, cdev->gadget);
|
||||
status = gether_register_netdev(gether_opts->net);
|
||||
mutex_unlock(&gether_opts->lock);
|
||||
if (status)
|
||||
return status;
|
||||
gether_opts->bound = true;
|
||||
}
|
||||
#endif
|
||||
us = usb_gstrings_attach(cdev, geth_strings,
|
||||
ARRAY_SIZE(geth_string_defs));
|
||||
if (IS_ERR(us))
|
||||
return PTR_ERR(us);
|
||||
|
||||
subset_data_intf.iInterface = us[0].id;
|
||||
ether_desc.iMACAddress = us[1].id;
|
||||
|
||||
/* allocate instance-specific interface IDs */
|
||||
status = usb_interface_id(c, f);
|
||||
if (status < 0)
|
||||
@ -360,8 +393,10 @@ fail:
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef USB_FSUBSET_INCLUDED
|
||||
|
||||
static void
|
||||
geth_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
geth_old_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
geth_string_defs[0].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
@ -387,18 +422,6 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
struct f_gether *geth;
|
||||
int status;
|
||||
|
||||
if (!ethaddr)
|
||||
return -EINVAL;
|
||||
|
||||
/* maybe allocate device-global string IDs */
|
||||
if (geth_string_defs[0].id == 0) {
|
||||
status = usb_string_ids_tab(c->cdev, geth_string_defs);
|
||||
if (status < 0)
|
||||
return status;
|
||||
subset_data_intf.iInterface = geth_string_defs[0].id;
|
||||
ether_desc.iMACAddress = geth_string_defs[1].id;
|
||||
}
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
geth = kzalloc(sizeof *geth, GFP_KERNEL);
|
||||
if (!geth)
|
||||
@ -412,9 +435,8 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
geth->port.cdc_filter = DEFAULT_FILTER;
|
||||
|
||||
geth->port.func.name = "cdc_subset";
|
||||
geth->port.func.strings = geth_strings;
|
||||
geth->port.func.bind = geth_bind;
|
||||
geth->port.func.unbind = geth_unbind;
|
||||
geth->port.func.unbind = geth_old_unbind;
|
||||
geth->port.func.set_alt = geth_set_alt;
|
||||
geth->port.func.disable = geth_disable;
|
||||
|
||||
@ -423,3 +445,129 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
kfree(geth);
|
||||
return status;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item)
|
||||
{
|
||||
return container_of(to_config_group(item), struct f_gether_opts,
|
||||
func_inst.group);
|
||||
}
|
||||
|
||||
/* f_gether_item_ops */
|
||||
USB_ETHERNET_CONFIGFS_ITEM(gether);
|
||||
|
||||
/* f_gether_opts_dev_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(gether);
|
||||
|
||||
/* f_gether_opts_host_addr */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(gether);
|
||||
|
||||
/* f_gether_opts_qmult */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(gether);
|
||||
|
||||
/* f_gether_opts_ifname */
|
||||
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(gether);
|
||||
|
||||
static struct configfs_attribute *gether_attrs[] = {
|
||||
&f_gether_opts_dev_addr.attr,
|
||||
&f_gether_opts_host_addr.attr,
|
||||
&f_gether_opts_qmult.attr,
|
||||
&f_gether_opts_ifname.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type gether_func_type = {
|
||||
.ct_item_ops = &gether_item_ops,
|
||||
.ct_attrs = gether_attrs,
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void geth_free_inst(struct usb_function_instance *f)
|
||||
{
|
||||
struct f_gether_opts *opts;
|
||||
|
||||
opts = container_of(f, struct f_gether_opts, func_inst);
|
||||
if (opts->bound)
|
||||
gether_cleanup(netdev_priv(opts->net));
|
||||
else
|
||||
free_netdev(opts->net);
|
||||
kfree(opts);
|
||||
}
|
||||
|
||||
static struct usb_function_instance *geth_alloc_inst(void)
|
||||
{
|
||||
struct f_gether_opts *opts;
|
||||
|
||||
opts = kzalloc(sizeof(*opts), GFP_KERNEL);
|
||||
if (!opts)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
mutex_init(&opts->lock);
|
||||
opts->func_inst.free_func_inst = geth_free_inst;
|
||||
opts->net = gether_setup_default();
|
||||
if (IS_ERR(opts->net))
|
||||
return ERR_CAST(opts->net);
|
||||
|
||||
config_group_init_type_name(&opts->func_inst.group, "",
|
||||
&gether_func_type);
|
||||
|
||||
return &opts->func_inst;
|
||||
}
|
||||
|
||||
static void geth_free(struct usb_function *f)
|
||||
{
|
||||
struct f_gether *eth;
|
||||
|
||||
eth = func_to_geth(f);
|
||||
kfree(eth);
|
||||
}
|
||||
|
||||
static void geth_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||
{
|
||||
geth_string_defs[0].id = 0;
|
||||
usb_free_all_descriptors(f);
|
||||
}
|
||||
|
||||
static struct usb_function *geth_alloc(struct usb_function_instance *fi)
|
||||
{
|
||||
struct f_gether *geth;
|
||||
struct f_gether_opts *opts;
|
||||
int status;
|
||||
|
||||
/* allocate and initialize one new instance */
|
||||
geth = kzalloc(sizeof(*geth), GFP_KERNEL);
|
||||
if (!geth)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
opts = container_of(fi, struct f_gether_opts, func_inst);
|
||||
|
||||
mutex_lock(&opts->lock);
|
||||
opts->refcnt++;
|
||||
/* export host's Ethernet address in CDC format */
|
||||
status = gether_get_host_addr_cdc(opts->net, geth->ethaddr,
|
||||
sizeof(geth->ethaddr));
|
||||
if (status < 12) {
|
||||
kfree(geth);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
geth_string_defs[1].s = geth->ethaddr;
|
||||
|
||||
geth->port.ioport = netdev_priv(opts->net);
|
||||
mutex_unlock(&opts->lock);
|
||||
geth->port.cdc_filter = DEFAULT_FILTER;
|
||||
|
||||
geth->port.func.name = "cdc_subset";
|
||||
geth->port.func.bind = geth_bind;
|
||||
geth->port.func.unbind = geth_unbind;
|
||||
geth->port.func.set_alt = geth_set_alt;
|
||||
geth->port.func.disable = geth_disable;
|
||||
geth->port.func.free_func = geth_free;
|
||||
|
||||
return &geth->port.func;
|
||||
}
|
||||
|
||||
DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc);
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
|
||||
#endif
|
||||
|
@ -90,6 +90,7 @@ struct uac2_req {
|
||||
};
|
||||
|
||||
struct uac2_rtd_params {
|
||||
struct snd_uac2_chip *uac2; /* parent chip */
|
||||
bool ep_enabled; /* if the ep is enabled */
|
||||
/* Size of the ring buffer */
|
||||
size_t dma_bytes;
|
||||
@ -168,18 +169,6 @@ struct snd_uac2_chip *pdev_to_uac2(struct platform_device *p)
|
||||
return container_of(p, struct snd_uac2_chip, pdev);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct snd_uac2_chip *prm_to_uac2(struct uac2_rtd_params *r)
|
||||
{
|
||||
struct snd_uac2_chip *uac2 = container_of(r,
|
||||
struct snd_uac2_chip, c_prm);
|
||||
|
||||
if (&uac2->c_prm != r)
|
||||
uac2 = container_of(r, struct snd_uac2_chip, p_prm);
|
||||
|
||||
return uac2;
|
||||
}
|
||||
|
||||
static inline
|
||||
uint num_channels(uint chanmask)
|
||||
{
|
||||
@ -204,7 +193,7 @@ agdev_iso_complete(struct usb_ep *ep, struct usb_request *req)
|
||||
struct uac2_req *ur = req->context;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct uac2_rtd_params *prm = ur->pp;
|
||||
struct snd_uac2_chip *uac2 = prm_to_uac2(prm);
|
||||
struct snd_uac2_chip *uac2 = prm->uac2;
|
||||
|
||||
/* i/f shutting down */
|
||||
if (!prm->ep_enabled)
|
||||
@ -894,7 +883,7 @@ struct cntrl_range_lay3 {
|
||||
static inline void
|
||||
free_ep(struct uac2_rtd_params *prm, struct usb_ep *ep)
|
||||
{
|
||||
struct snd_uac2_chip *uac2 = prm_to_uac2(prm);
|
||||
struct snd_uac2_chip *uac2 = prm->uac2;
|
||||
int i;
|
||||
|
||||
prm->ep_enabled = false;
|
||||
@ -970,6 +959,9 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
|
||||
}
|
||||
agdev->in_ep->driver_data = agdev;
|
||||
|
||||
uac2->p_prm.uac2 = uac2;
|
||||
uac2->c_prm.uac2 = uac2;
|
||||
|
||||
hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress;
|
||||
hs_epout_desc.wMaxPacketSize = fs_epout_desc.wMaxPacketSize;
|
||||
hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress;
|
||||
|
@ -156,8 +156,6 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep __initdata = {
|
||||
/* The wMaxPacketSize and bInterval values will be initialized from
|
||||
* module parameters.
|
||||
*/
|
||||
.wMaxPacketSize = 0,
|
||||
.bInterval = 0,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
|
||||
@ -169,8 +167,6 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep __initdata = {
|
||||
/* The wMaxPacketSize and bInterval values will be initialized from
|
||||
* module parameters.
|
||||
*/
|
||||
.wMaxPacketSize = 0,
|
||||
.bInterval = 0,
|
||||
};
|
||||
|
||||
static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
|
||||
@ -183,17 +179,14 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep __initdata = {
|
||||
/* The wMaxPacketSize and bInterval values will be initialized from
|
||||
* module parameters.
|
||||
*/
|
||||
.wMaxPacketSize = 0,
|
||||
.bInterval = 0,
|
||||
};
|
||||
|
||||
static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp __initdata = {
|
||||
.bLength = sizeof(uvc_ss_streaming_comp),
|
||||
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
|
||||
/* The following 3 values can be tweaked if necessary. */
|
||||
.bMaxBurst = 0,
|
||||
.bmAttributes = 0,
|
||||
.wBytesPerInterval = cpu_to_le16(1024),
|
||||
/* The bMaxBurst, bmAttributes and wBytesPerInterval values will be
|
||||
* initialized from module parameters.
|
||||
*/
|
||||
};
|
||||
|
||||
static const struct usb_descriptor_header * const uvc_fs_streaming[] = {
|
||||
|
1219
drivers/usb/gadget/fotg210-udc.c
Normal file
1219
drivers/usb/gadget/fotg210-udc.c
Normal file
File diff suppressed because it is too large
Load Diff
253
drivers/usb/gadget/fotg210.h
Normal file
253
drivers/usb/gadget/fotg210.h
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Faraday FOTG210 USB OTG controller
|
||||
*
|
||||
* Copyright (C) 2013 Faraday Technology Corporation
|
||||
* Author: Yuan-Hsin Chen <yhchen@faraday-tech.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define FOTG210_MAX_NUM_EP 5 /* ep0...ep4 */
|
||||
#define FOTG210_MAX_FIFO_NUM 4 /* fifo0...fifo4 */
|
||||
|
||||
/* Global Mask of HC/OTG/DEV interrupt Register(0xC4) */
|
||||
#define FOTG210_GMIR 0xC4
|
||||
#define GMIR_INT_POLARITY 0x8 /*Active High*/
|
||||
#define GMIR_MHC_INT 0x4
|
||||
#define GMIR_MOTG_INT 0x2
|
||||
#define GMIR_MDEV_INT 0x1
|
||||
|
||||
/* Device Main Control Register(0x100) */
|
||||
#define FOTG210_DMCR 0x100
|
||||
#define DMCR_HS_EN (1 << 6)
|
||||
#define DMCR_CHIP_EN (1 << 5)
|
||||
#define DMCR_SFRST (1 << 4)
|
||||
#define DMCR_GOSUSP (1 << 3)
|
||||
#define DMCR_GLINT_EN (1 << 2)
|
||||
#define DMCR_HALF_SPEED (1 << 1)
|
||||
#define DMCR_CAP_RMWAKUP (1 << 0)
|
||||
|
||||
/* Device Address Register(0x104) */
|
||||
#define FOTG210_DAR 0x104
|
||||
#define DAR_AFT_CONF (1 << 7)
|
||||
|
||||
/* Device Test Register(0x108) */
|
||||
#define FOTG210_DTR 0x108
|
||||
#define DTR_TST_CLRFF (1 << 0)
|
||||
|
||||
/* PHY Test Mode Selector register(0x114) */
|
||||
#define FOTG210_PHYTMSR 0x114
|
||||
#define PHYTMSR_TST_PKT (1 << 4)
|
||||
#define PHYTMSR_TST_SE0NAK (1 << 3)
|
||||
#define PHYTMSR_TST_KSTA (1 << 2)
|
||||
#define PHYTMSR_TST_JSTA (1 << 1)
|
||||
#define PHYTMSR_UNPLUG (1 << 0)
|
||||
|
||||
/* Cx configuration and FIFO Empty Status register(0x120) */
|
||||
#define FOTG210_DCFESR 0x120
|
||||
#define DCFESR_FIFO_EMPTY(fifo) (1 << 8 << (fifo))
|
||||
#define DCFESR_CX_EMP (1 << 5)
|
||||
#define DCFESR_CX_CLR (1 << 3)
|
||||
#define DCFESR_CX_STL (1 << 2)
|
||||
#define DCFESR_TST_PKDONE (1 << 1)
|
||||
#define DCFESR_CX_DONE (1 << 0)
|
||||
|
||||
/* Device IDLE Counter Register(0x124) */
|
||||
#define FOTG210_DICR 0x124
|
||||
|
||||
/* Device Mask of Interrupt Group Register (0x130) */
|
||||
#define FOTG210_DMIGR 0x130
|
||||
#define DMIGR_MINT_G0 (1 << 0)
|
||||
|
||||
/* Device Mask of Interrupt Source Group 0(0x134) */
|
||||
#define FOTG210_DMISGR0 0x134
|
||||
#define DMISGR0_MCX_COMEND (1 << 3)
|
||||
#define DMISGR0_MCX_OUT_INT (1 << 2)
|
||||
#define DMISGR0_MCX_IN_INT (1 << 1)
|
||||
#define DMISGR0_MCX_SETUP_INT (1 << 0)
|
||||
|
||||
/* Device Mask of Interrupt Source Group 1 Register(0x138)*/
|
||||
#define FOTG210_DMISGR1 0x138
|
||||
#define DMISGR1_MF3_IN_INT (1 << 19)
|
||||
#define DMISGR1_MF2_IN_INT (1 << 18)
|
||||
#define DMISGR1_MF1_IN_INT (1 << 17)
|
||||
#define DMISGR1_MF0_IN_INT (1 << 16)
|
||||
#define DMISGR1_MF_IN_INT(fifo) (1 << (16 + (fifo)))
|
||||
#define DMISGR1_MF3_SPK_INT (1 << 7)
|
||||
#define DMISGR1_MF3_OUT_INT (1 << 6)
|
||||
#define DMISGR1_MF2_SPK_INT (1 << 5)
|
||||
#define DMISGR1_MF2_OUT_INT (1 << 4)
|
||||
#define DMISGR1_MF1_SPK_INT (1 << 3)
|
||||
#define DMISGR1_MF1_OUT_INT (1 << 2)
|
||||
#define DMISGR1_MF0_SPK_INT (1 << 1)
|
||||
#define DMISGR1_MF0_OUT_INT (1 << 0)
|
||||
#define DMISGR1_MF_OUTSPK_INT(fifo) (0x3 << (fifo) * 2)
|
||||
|
||||
/* Device Mask of Interrupt Source Group 2 Register (0x13C) */
|
||||
#define FOTG210_DMISGR2 0x13C
|
||||
#define DMISGR2_MDMA_ERROR (1 << 8)
|
||||
#define DMISGR2_MDMA_CMPLT (1 << 7)
|
||||
|
||||
/* Device Interrupt group Register (0x140) */
|
||||
#define FOTG210_DIGR 0x140
|
||||
#define DIGR_INT_G2 (1 << 2)
|
||||
#define DIGR_INT_G1 (1 << 1)
|
||||
#define DIGR_INT_G0 (1 << 0)
|
||||
|
||||
/* Device Interrupt Source Group 0 Register (0x144) */
|
||||
#define FOTG210_DISGR0 0x144
|
||||
#define DISGR0_CX_COMABT_INT (1 << 5)
|
||||
#define DISGR0_CX_COMFAIL_INT (1 << 4)
|
||||
#define DISGR0_CX_COMEND_INT (1 << 3)
|
||||
#define DISGR0_CX_OUT_INT (1 << 2)
|
||||
#define DISGR0_CX_IN_INT (1 << 1)
|
||||
#define DISGR0_CX_SETUP_INT (1 << 0)
|
||||
|
||||
/* Device Interrupt Source Group 1 Register (0x148) */
|
||||
#define FOTG210_DISGR1 0x148
|
||||
#define DISGR1_OUT_INT(fifo) (1 << ((fifo) * 2))
|
||||
#define DISGR1_SPK_INT(fifo) (1 << 1 << ((fifo) * 2))
|
||||
#define DISGR1_IN_INT(fifo) (1 << 16 << (fifo))
|
||||
|
||||
/* Device Interrupt Source Group 2 Register (0x14C) */
|
||||
#define FOTG210_DISGR2 0x14C
|
||||
#define DISGR2_DMA_ERROR (1 << 8)
|
||||
#define DISGR2_DMA_CMPLT (1 << 7)
|
||||
#define DISGR2_RX0BYTE_INT (1 << 6)
|
||||
#define DISGR2_TX0BYTE_INT (1 << 5)
|
||||
#define DISGR2_ISO_SEQ_ABORT_INT (1 << 4)
|
||||
#define DISGR2_ISO_SEQ_ERR_INT (1 << 3)
|
||||
#define DISGR2_RESM_INT (1 << 2)
|
||||
#define DISGR2_SUSP_INT (1 << 1)
|
||||
#define DISGR2_USBRST_INT (1 << 0)
|
||||
|
||||
/* Device Receive Zero-Length Data Packet Register (0x150)*/
|
||||
#define FOTG210_RX0BYTE 0x150
|
||||
#define RX0BYTE_EP8 (1 << 7)
|
||||
#define RX0BYTE_EP7 (1 << 6)
|
||||
#define RX0BYTE_EP6 (1 << 5)
|
||||
#define RX0BYTE_EP5 (1 << 4)
|
||||
#define RX0BYTE_EP4 (1 << 3)
|
||||
#define RX0BYTE_EP3 (1 << 2)
|
||||
#define RX0BYTE_EP2 (1 << 1)
|
||||
#define RX0BYTE_EP1 (1 << 0)
|
||||
|
||||
/* Device Transfer Zero-Length Data Packet Register (0x154)*/
|
||||
#define FOTG210_TX0BYTE 0x154
|
||||
#define TX0BYTE_EP8 (1 << 7)
|
||||
#define TX0BYTE_EP7 (1 << 6)
|
||||
#define TX0BYTE_EP6 (1 << 5)
|
||||
#define TX0BYTE_EP5 (1 << 4)
|
||||
#define TX0BYTE_EP4 (1 << 3)
|
||||
#define TX0BYTE_EP3 (1 << 2)
|
||||
#define TX0BYTE_EP2 (1 << 1)
|
||||
#define TX0BYTE_EP1 (1 << 0)
|
||||
|
||||
/* Device IN Endpoint x MaxPacketSize Register(0x160+4*(x-1)) */
|
||||
#define FOTG210_INEPMPSR(ep) (0x160 + 4 * ((ep) - 1))
|
||||
#define INOUTEPMPSR_MPS(mps) ((mps) & 0x2FF)
|
||||
#define INOUTEPMPSR_STL_EP (1 << 11)
|
||||
#define INOUTEPMPSR_RESET_TSEQ (1 << 12)
|
||||
|
||||
/* Device OUT Endpoint x MaxPacketSize Register(0x180+4*(x-1)) */
|
||||
#define FOTG210_OUTEPMPSR(ep) (0x180 + 4 * ((ep) - 1))
|
||||
|
||||
/* Device Endpoint 1~4 Map Register (0x1A0) */
|
||||
#define FOTG210_EPMAP 0x1A0
|
||||
#define EPMAP_FIFONO(ep, dir) \
|
||||
((((ep) - 1) << ((ep) - 1) * 8) << ((dir) ? 0 : 4))
|
||||
#define EPMAP_FIFONOMSK(ep, dir) \
|
||||
((3 << ((ep) - 1) * 8) << ((dir) ? 0 : 4))
|
||||
|
||||
/* Device FIFO Map Register (0x1A8) */
|
||||
#define FOTG210_FIFOMAP 0x1A8
|
||||
#define FIFOMAP_DIROUT(fifo) (0x0 << 4 << (fifo) * 8)
|
||||
#define FIFOMAP_DIRIN(fifo) (0x1 << 4 << (fifo) * 8)
|
||||
#define FIFOMAP_BIDIR(fifo) (0x2 << 4 << (fifo) * 8)
|
||||
#define FIFOMAP_NA(fifo) (0x3 << 4 << (fifo) * 8)
|
||||
#define FIFOMAP_EPNO(ep) ((ep) << ((ep) - 1) * 8)
|
||||
#define FIFOMAP_EPNOMSK(ep) (0xF << ((ep) - 1) * 8)
|
||||
|
||||
/* Device FIFO Confuguration Register (0x1AC) */
|
||||
#define FOTG210_FIFOCF 0x1AC
|
||||
#define FIFOCF_TYPE(type, fifo) ((type) << (fifo) * 8)
|
||||
#define FIFOCF_BLK_SIN(fifo) (0x0 << (fifo) * 8 << 2)
|
||||
#define FIFOCF_BLK_DUB(fifo) (0x1 << (fifo) * 8 << 2)
|
||||
#define FIFOCF_BLK_TRI(fifo) (0x2 << (fifo) * 8 << 2)
|
||||
#define FIFOCF_BLKSZ_512(fifo) (0x0 << (fifo) * 8 << 4)
|
||||
#define FIFOCF_BLKSZ_1024(fifo) (0x1 << (fifo) * 8 << 4)
|
||||
#define FIFOCF_FIFO_EN(fifo) (0x1 << (fifo) * 8 << 5)
|
||||
|
||||
/* Device FIFO n Instruction and Byte Count Register (0x1B0+4*n) */
|
||||
#define FOTG210_FIBCR(fifo) (0x1B0 + (fifo) * 4)
|
||||
#define FIBCR_BCFX 0x7FF
|
||||
#define FIBCR_FFRST (1 << 12)
|
||||
|
||||
/* Device DMA Target FIFO Number Register (0x1C0) */
|
||||
#define FOTG210_DMATFNR 0x1C0
|
||||
#define DMATFNR_ACC_CXF (1 << 4)
|
||||
#define DMATFNR_ACC_F3 (1 << 3)
|
||||
#define DMATFNR_ACC_F2 (1 << 2)
|
||||
#define DMATFNR_ACC_F1 (1 << 1)
|
||||
#define DMATFNR_ACC_F0 (1 << 0)
|
||||
#define DMATFNR_ACC_FN(fifo) (1 << (fifo))
|
||||
#define DMATFNR_DISDMA 0
|
||||
|
||||
/* Device DMA Controller Parameter setting 1 Register (0x1C8) */
|
||||
#define FOTG210_DMACPSR1 0x1C8
|
||||
#define DMACPSR1_DMA_LEN(len) (((len) & 0xFFFF) << 8)
|
||||
#define DMACPSR1_DMA_ABORT (1 << 3)
|
||||
#define DMACPSR1_DMA_TYPE(dir_in) (((dir_in) ? 1 : 0) << 1)
|
||||
#define DMACPSR1_DMA_START (1 << 0)
|
||||
|
||||
/* Device DMA Controller Parameter setting 2 Register (0x1CC) */
|
||||
#define FOTG210_DMACPSR2 0x1CC
|
||||
|
||||
/* Device DMA Controller Parameter setting 3 Register (0x1CC) */
|
||||
#define FOTG210_CXPORT 0x1D0
|
||||
|
||||
struct fotg210_request {
|
||||
struct usb_request req;
|
||||
struct list_head queue;
|
||||
};
|
||||
|
||||
struct fotg210_ep {
|
||||
struct usb_ep ep;
|
||||
struct fotg210_udc *fotg210;
|
||||
|
||||
struct list_head queue;
|
||||
unsigned stall:1;
|
||||
unsigned wedged:1;
|
||||
unsigned use_dma:1;
|
||||
|
||||
unsigned char epnum;
|
||||
unsigned char type;
|
||||
unsigned char dir_in;
|
||||
unsigned int maxp;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
};
|
||||
|
||||
struct fotg210_udc {
|
||||
spinlock_t lock; /* protect the struct */
|
||||
void __iomem *reg;
|
||||
|
||||
unsigned long irq_trigger;
|
||||
|
||||
struct usb_gadget gadget;
|
||||
struct usb_gadget_driver *driver;
|
||||
|
||||
struct fotg210_ep *ep[FOTG210_MAX_NUM_EP];
|
||||
|
||||
struct usb_request *ep0_req; /* for internal request */
|
||||
__le16 ep0_data;
|
||||
u8 ep0_dir; /* 0/0x80 out/in */
|
||||
|
||||
u8 reenum; /* if re-enumeration */
|
||||
};
|
||||
|
||||
#define gadget_to_fotg210(g) container_of((g), struct fotg210_udc, gadget)
|
@ -2589,7 +2589,7 @@ static int qe_udc_probe(struct platform_device *ofdev)
|
||||
if (ret)
|
||||
goto err6;
|
||||
|
||||
dev_set_drvdata(&ofdev->dev, udc);
|
||||
platform_set_drvdata(ofdev, udc);
|
||||
dev_info(udc->dev,
|
||||
"%s USB controller initialized as device\n",
|
||||
(udc->soc_type == PORT_QE) ? "QE" : "CPM");
|
||||
@ -2640,7 +2640,7 @@ static int qe_udc_resume(struct platform_device *dev)
|
||||
|
||||
static int qe_udc_remove(struct platform_device *ofdev)
|
||||
{
|
||||
struct qe_udc *udc = dev_get_drvdata(&ofdev->dev);
|
||||
struct qe_udc *udc = platform_get_drvdata(ofdev);
|
||||
struct qe_ep *ep;
|
||||
unsigned int size;
|
||||
DECLARE_COMPLETION(done);
|
||||
|
@ -1347,7 +1347,7 @@ static const struct usb_gadget_ops fusb300_gadget_ops = {
|
||||
|
||||
static int __exit fusb300_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct fusb300 *fusb300 = dev_get_drvdata(&pdev->dev);
|
||||
struct fusb300 *fusb300 = platform_get_drvdata(pdev);
|
||||
|
||||
usb_del_gadget_udc(&fusb300->gadget);
|
||||
iounmap(fusb300->reg);
|
||||
@ -1416,7 +1416,7 @@ static int __init fusb300_probe(struct platform_device *pdev)
|
||||
|
||||
spin_lock_init(&fusb300->lock);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, fusb300);
|
||||
platform_set_drvdata(pdev, fusb300);
|
||||
|
||||
fusb300->gadget.ops = &fusb300_gadget_ops;
|
||||
|
||||
|
@ -28,15 +28,18 @@
|
||||
# define USB_ETH_RNDIS y
|
||||
# endif
|
||||
|
||||
#define USBF_ECM_INCLUDED
|
||||
# include "f_ecm.c"
|
||||
#define USB_FSUBSET_INCLUDED
|
||||
# include "f_subset.c"
|
||||
# ifdef USB_ETH_RNDIS
|
||||
# define USB_FRNDIS_INCLUDED
|
||||
# include "f_rndis.c"
|
||||
# include "rndis.c"
|
||||
# include "rndis.h"
|
||||
# endif
|
||||
# include "u_ether.c"
|
||||
# include "u_ether.h"
|
||||
|
||||
static u8 gfs_hostaddr[ETH_ALEN];
|
||||
static u8 gfs_host_mac[ETH_ALEN];
|
||||
static struct eth_dev *the_dev;
|
||||
# ifdef CONFIG_USB_FUNCTIONFS_ETH
|
||||
static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
@ -45,7 +48,7 @@ static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
#else
|
||||
# define the_dev NULL
|
||||
# define gether_cleanup(dev) do { } while (0)
|
||||
# define gfs_hostaddr NULL
|
||||
# define gfs_host_mac NULL
|
||||
struct eth_dev;
|
||||
#endif
|
||||
|
||||
@ -73,6 +76,8 @@ struct gfs_ffs_obj {
|
||||
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
||||
USB_ETHERNET_MODULE_PARAMETERS();
|
||||
|
||||
static struct usb_device_descriptor gfs_dev_desc = {
|
||||
.bLength = sizeof gfs_dev_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
@ -350,7 +355,8 @@ static int gfs_bind(struct usb_composite_dev *cdev)
|
||||
if (missing_funcs)
|
||||
return -ENODEV;
|
||||
#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
|
||||
the_dev = gether_setup(cdev->gadget, gfs_hostaddr);
|
||||
the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, gfs_host_mac,
|
||||
qmult);
|
||||
#endif
|
||||
if (IS_ERR(the_dev)) {
|
||||
ret = PTR_ERR(the_dev);
|
||||
@ -446,7 +452,7 @@ static int gfs_do_config(struct usb_configuration *c)
|
||||
}
|
||||
|
||||
if (gc->eth) {
|
||||
ret = gc->eth(c, gfs_hostaddr, the_dev);
|
||||
ret = gc->eth(c, gfs_host_mac, the_dev);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
}
|
||||
|
@ -1533,7 +1533,7 @@ static const struct usb_gadget_ops m66592_gadget_ops = {
|
||||
|
||||
static int __exit m66592_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct m66592 *m66592 = dev_get_drvdata(&pdev->dev);
|
||||
struct m66592 *m66592 = platform_get_drvdata(pdev);
|
||||
|
||||
usb_del_gadget_udc(&m66592->gadget);
|
||||
|
||||
@ -1602,7 +1602,7 @@ static int __init m66592_probe(struct platform_device *pdev)
|
||||
m66592->irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
|
||||
|
||||
spin_lock_init(&m66592->lock);
|
||||
dev_set_drvdata(&pdev->dev, m66592);
|
||||
platform_set_drvdata(pdev, m66592);
|
||||
|
||||
m66592->gadget.ops = &m66592_gadget_ops;
|
||||
m66592->gadget.max_speed = USB_SPEED_HIGH;
|
||||
|
@ -43,16 +43,19 @@ MODULE_LICENSE("GPL");
|
||||
*/
|
||||
#include "f_mass_storage.c"
|
||||
|
||||
#define USBF_ECM_INCLUDED
|
||||
#include "f_ecm.c"
|
||||
#include "f_subset.c"
|
||||
#ifdef USB_ETH_RNDIS
|
||||
# define USB_FRNDIS_INCLUDED
|
||||
# include "f_rndis.c"
|
||||
# include "rndis.c"
|
||||
# include "rndis.h"
|
||||
#endif
|
||||
#include "u_ether.c"
|
||||
#include "u_ether.h"
|
||||
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
||||
USB_ETHERNET_MODULE_PARAMETERS();
|
||||
|
||||
/***************************** Device Descriptor ****************************/
|
||||
|
||||
#define MULTI_VENDOR_NUM 0x1d6b /* Linux Foundation */
|
||||
@ -133,7 +136,7 @@ FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
|
||||
|
||||
static struct fsg_common fsg_common;
|
||||
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
static u8 host_mac[ETH_ALEN];
|
||||
|
||||
static struct usb_function_instance *fi_acm;
|
||||
static struct eth_dev *the_dev;
|
||||
@ -152,7 +155,7 @@ static __init int rndis_do_config(struct usb_configuration *c)
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
ret = rndis_bind_config(c, hostaddr, the_dev);
|
||||
ret = rndis_bind_config(c, host_mac, the_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -216,7 +219,7 @@ static __init int cdc_do_config(struct usb_configuration *c)
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
ret = ecm_bind_config(c, hostaddr, the_dev);
|
||||
ret = ecm_bind_config(c, host_mac, the_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -280,7 +283,8 @@ static int __ref multi_bind(struct usb_composite_dev *cdev)
|
||||
}
|
||||
|
||||
/* set up network link layer */
|
||||
the_dev = gether_setup(cdev->gadget, hostaddr);
|
||||
the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac,
|
||||
qmult);
|
||||
if (IS_ERR(the_dev))
|
||||
return PTR_ERR(the_dev);
|
||||
|
||||
|
@ -2050,7 +2050,7 @@ static SIMPLE_DEV_PM_OPS(mv_u3d_pm_ops, mv_u3d_suspend, mv_u3d_resume);
|
||||
|
||||
static void mv_u3d_shutdown(struct platform_device *dev)
|
||||
{
|
||||
struct mv_u3d *u3d = dev_get_drvdata(&dev->dev);
|
||||
struct mv_u3d *u3d = platform_get_drvdata(dev);
|
||||
u32 tmp;
|
||||
|
||||
tmp = ioread32(&u3d->op_regs->usbcmd);
|
||||
|
@ -24,23 +24,12 @@
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
#include "u_ether.h"
|
||||
#include "u_ncm.h"
|
||||
|
||||
#define DRIVER_DESC "NCM Gadget"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#include "f_ncm.c"
|
||||
#include "u_ether.c"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
|
||||
* Instead: allocate your own, using normal USB-IF procedures.
|
||||
*/
|
||||
@ -54,6 +43,8 @@
|
||||
/*-------------------------------------------------------------------------*/
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
||||
USB_ETHERNET_MODULE_PARAMETERS();
|
||||
|
||||
static struct usb_device_descriptor device_desc = {
|
||||
.bLength = sizeof device_desc,
|
||||
.bDescriptorType = USB_DT_DEVICE,
|
||||
@ -111,13 +102,15 @@ static struct usb_gadget_strings *dev_strings[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct eth_dev *the_dev;
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
static struct usb_function_instance *f_ncm_inst;
|
||||
static struct usb_function *f_ncm;
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
static int __init ncm_do_config(struct usb_configuration *c)
|
||||
{
|
||||
int status;
|
||||
|
||||
/* FIXME alloc iConfiguration string, set it in c->strings */
|
||||
|
||||
if (gadget_is_otg(c->cdev->gadget)) {
|
||||
@ -125,7 +118,19 @@ static int __init ncm_do_config(struct usb_configuration *c)
|
||||
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
|
||||
}
|
||||
|
||||
return ncm_bind_config(c, hostaddr, the_dev);
|
||||
f_ncm = usb_get_function(f_ncm_inst);
|
||||
if (IS_ERR(f_ncm)) {
|
||||
status = PTR_ERR(f_ncm);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = usb_add_function(c, f_ncm);
|
||||
if (status < 0) {
|
||||
usb_put_function(f_ncm);
|
||||
return status;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct usb_configuration ncm_config_driver = {
|
||||
@ -141,12 +146,20 @@ static struct usb_configuration ncm_config_driver = {
|
||||
static int __init gncm_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
struct f_ncm_opts *ncm_opts;
|
||||
int status;
|
||||
|
||||
/* set up network link layer */
|
||||
the_dev = gether_setup(cdev->gadget, hostaddr);
|
||||
if (IS_ERR(the_dev))
|
||||
return PTR_ERR(the_dev);
|
||||
f_ncm_inst = usb_get_function_instance("ncm");
|
||||
if (IS_ERR(f_ncm_inst))
|
||||
return PTR_ERR(f_ncm_inst);
|
||||
|
||||
ncm_opts = container_of(f_ncm_inst, struct f_ncm_opts, func_inst);
|
||||
|
||||
gether_set_qmult(ncm_opts->net, qmult);
|
||||
if (!gether_set_host_addr(ncm_opts->net, host_addr))
|
||||
pr_info("using host ethernet address: %s", host_addr);
|
||||
if (!gether_set_dev_addr(ncm_opts->net, dev_addr))
|
||||
pr_info("using self ethernet address: %s", dev_addr);
|
||||
|
||||
/* Allocate string descriptor numbers ... note that string
|
||||
* contents can be overridden by the composite_dev glue.
|
||||
@ -169,13 +182,16 @@ static int __init gncm_bind(struct usb_composite_dev *cdev)
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
gether_cleanup(the_dev);
|
||||
usb_put_function_instance(f_ncm_inst);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit gncm_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
gether_cleanup(the_dev);
|
||||
if (!IS_ERR_OR_NULL(f_ncm))
|
||||
usb_put_function(f_ncm);
|
||||
if (!IS_ERR_OR_NULL(f_ncm_inst))
|
||||
usb_put_function_instance(f_ncm_inst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -16,11 +16,13 @@
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "u_serial.h"
|
||||
#include "u_ether.h"
|
||||
#include "u_phonet.h"
|
||||
#include "u_ecm.h"
|
||||
#include "gadget_chips.h"
|
||||
|
||||
/* Defines */
|
||||
@ -28,24 +30,10 @@
|
||||
#define NOKIA_VERSION_NUM 0x0211
|
||||
#define NOKIA_LONG_NAME "N900 (PC-Suite Mode)"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Kbuild is not very cooperative with respect to linking separately
|
||||
* compiled library objects into one module. So for now we won't use
|
||||
* separate compilation ... ensuring init/exit sections work to shrink
|
||||
* the runtime footprint, and giving us at least some parts of what
|
||||
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
|
||||
*/
|
||||
#define USBF_OBEX_INCLUDED
|
||||
#include "f_ecm.c"
|
||||
#include "f_obex.c"
|
||||
#include "f_phonet.c"
|
||||
#include "u_ether.c"
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
USB_GADGET_COMPOSITE_OPTIONS();
|
||||
|
||||
USB_ETHERNET_MODULE_PARAMETERS();
|
||||
|
||||
#define NOKIA_VENDOR_ID 0x0421 /* Nokia */
|
||||
#define NOKIA_PRODUCT_ID 0x01c8 /* Nokia Gadget */
|
||||
|
||||
@ -98,16 +86,15 @@ MODULE_LICENSE("GPL");
|
||||
/*-------------------------------------------------------------------------*/
|
||||
static struct usb_function *f_acm_cfg1;
|
||||
static struct usb_function *f_acm_cfg2;
|
||||
static u8 hostaddr[ETH_ALEN];
|
||||
static struct eth_dev *the_dev;
|
||||
static struct usb_function *f_ecm_cfg1;
|
||||
static struct usb_function *f_ecm_cfg2;
|
||||
static struct usb_function *f_obex1_cfg1;
|
||||
static struct usb_function *f_obex2_cfg1;
|
||||
static struct usb_function *f_obex1_cfg2;
|
||||
static struct usb_function *f_obex2_cfg2;
|
||||
static struct usb_function *f_phonet_cfg1;
|
||||
static struct usb_function *f_phonet_cfg2;
|
||||
|
||||
enum {
|
||||
TTY_PORT_OBEX0,
|
||||
TTY_PORT_OBEX1,
|
||||
TTY_PORTS_MAX,
|
||||
};
|
||||
|
||||
static unsigned char tty_lines[TTY_PORTS_MAX];
|
||||
|
||||
static struct usb_configuration nokia_config_500ma_driver = {
|
||||
.label = "Bus Powered",
|
||||
@ -126,47 +113,114 @@ static struct usb_configuration nokia_config_100ma_driver = {
|
||||
};
|
||||
|
||||
static struct usb_function_instance *fi_acm;
|
||||
static struct usb_function_instance *fi_ecm;
|
||||
static struct usb_function_instance *fi_obex1;
|
||||
static struct usb_function_instance *fi_obex2;
|
||||
static struct usb_function_instance *fi_phonet;
|
||||
|
||||
static int __init nokia_bind_config(struct usb_configuration *c)
|
||||
{
|
||||
struct usb_function *f_acm;
|
||||
struct usb_function *f_phonet = NULL;
|
||||
struct usb_function *f_obex1 = NULL;
|
||||
struct usb_function *f_ecm;
|
||||
struct usb_function *f_obex2 = NULL;
|
||||
int status = 0;
|
||||
int obex1_stat = 0;
|
||||
int obex2_stat = 0;
|
||||
int phonet_stat = 0;
|
||||
|
||||
status = phonet_bind_config(c);
|
||||
if (status)
|
||||
printk(KERN_DEBUG "could not bind phonet config\n");
|
||||
if (!IS_ERR(fi_phonet)) {
|
||||
f_phonet = usb_get_function(fi_phonet);
|
||||
if (IS_ERR(f_phonet))
|
||||
pr_debug("could not get phonet function\n");
|
||||
}
|
||||
|
||||
status = obex_bind_config(c, tty_lines[TTY_PORT_OBEX0]);
|
||||
if (status)
|
||||
printk(KERN_DEBUG "could not bind obex config %d\n", 0);
|
||||
if (!IS_ERR(fi_obex1)) {
|
||||
f_obex1 = usb_get_function(fi_obex1);
|
||||
if (IS_ERR(f_obex1))
|
||||
pr_debug("could not get obex function 0\n");
|
||||
}
|
||||
|
||||
status = obex_bind_config(c, tty_lines[TTY_PORT_OBEX1]);
|
||||
if (status)
|
||||
printk(KERN_DEBUG "could not bind obex config %d\n", 0);
|
||||
if (!IS_ERR(fi_obex2)) {
|
||||
f_obex2 = usb_get_function(fi_obex2);
|
||||
if (IS_ERR(f_obex2))
|
||||
pr_debug("could not get obex function 1\n");
|
||||
}
|
||||
|
||||
f_acm = usb_get_function(fi_acm);
|
||||
if (IS_ERR(f_acm))
|
||||
return PTR_ERR(f_acm);
|
||||
if (IS_ERR(f_acm)) {
|
||||
status = PTR_ERR(f_acm);
|
||||
goto err_get_acm;
|
||||
}
|
||||
|
||||
f_ecm = usb_get_function(fi_ecm);
|
||||
if (IS_ERR(f_ecm)) {
|
||||
status = PTR_ERR(f_ecm);
|
||||
goto err_get_ecm;
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(f_phonet)) {
|
||||
phonet_stat = usb_add_function(c, f_phonet);
|
||||
if (phonet_stat)
|
||||
pr_debug("could not add phonet function\n");
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(f_obex1)) {
|
||||
obex1_stat = usb_add_function(c, f_obex1);
|
||||
if (obex1_stat)
|
||||
pr_debug("could not add obex function 0\n");
|
||||
}
|
||||
|
||||
if (!IS_ERR_OR_NULL(f_obex2)) {
|
||||
obex2_stat = usb_add_function(c, f_obex2);
|
||||
if (obex2_stat)
|
||||
pr_debug("could not add obex function 1\n");
|
||||
}
|
||||
|
||||
status = usb_add_function(c, f_acm);
|
||||
if (status)
|
||||
goto err_conf;
|
||||
|
||||
status = ecm_bind_config(c, hostaddr, the_dev);
|
||||
status = usb_add_function(c, f_ecm);
|
||||
if (status) {
|
||||
pr_debug("could not bind ecm config %d\n", status);
|
||||
goto err_ecm;
|
||||
}
|
||||
if (c == &nokia_config_500ma_driver)
|
||||
if (c == &nokia_config_500ma_driver) {
|
||||
f_acm_cfg1 = f_acm;
|
||||
else
|
||||
f_ecm_cfg1 = f_ecm;
|
||||
f_phonet_cfg1 = f_phonet;
|
||||
f_obex1_cfg1 = f_obex1;
|
||||
f_obex2_cfg1 = f_obex2;
|
||||
} else {
|
||||
f_acm_cfg2 = f_acm;
|
||||
f_ecm_cfg2 = f_ecm;
|
||||
f_phonet_cfg2 = f_phonet;
|
||||
f_obex1_cfg2 = f_obex1;
|
||||
f_obex2_cfg2 = f_obex2;
|
||||
}
|
||||
|
||||
return status;
|
||||
err_ecm:
|
||||
usb_remove_function(c, f_acm);
|
||||
err_conf:
|
||||
if (!obex2_stat)
|
||||
usb_remove_function(c, f_obex2);
|
||||
if (!obex1_stat)
|
||||
usb_remove_function(c, f_obex1);
|
||||
if (!phonet_stat)
|
||||
usb_remove_function(c, f_phonet);
|
||||
usb_put_function(f_ecm);
|
||||
err_get_ecm:
|
||||
usb_put_function(f_acm);
|
||||
err_get_acm:
|
||||
if (!IS_ERR_OR_NULL(f_obex2))
|
||||
usb_put_function(f_obex2);
|
||||
if (!IS_ERR_OR_NULL(f_obex1))
|
||||
usb_put_function(f_obex1);
|
||||
if (!IS_ERR_OR_NULL(f_phonet))
|
||||
usb_put_function(f_phonet);
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -174,23 +228,6 @@ static int __init nokia_bind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
struct usb_gadget *gadget = cdev->gadget;
|
||||
int status;
|
||||
int cur_line;
|
||||
|
||||
status = gphonet_setup(cdev->gadget);
|
||||
if (status < 0)
|
||||
goto err_phonet;
|
||||
|
||||
for (cur_line = 0; cur_line < TTY_PORTS_MAX; cur_line++) {
|
||||
status = gserial_alloc_line(&tty_lines[cur_line]);
|
||||
if (status)
|
||||
goto err_ether;
|
||||
}
|
||||
|
||||
the_dev = gether_setup(cdev->gadget, hostaddr);
|
||||
if (IS_ERR(the_dev)) {
|
||||
status = PTR_ERR(the_dev);
|
||||
goto err_ether;
|
||||
}
|
||||
|
||||
status = usb_string_ids_tab(cdev, strings_dev);
|
||||
if (status < 0)
|
||||
@ -201,18 +238,40 @@ static int __init nokia_bind(struct usb_composite_dev *cdev)
|
||||
nokia_config_500ma_driver.iConfiguration = status;
|
||||
nokia_config_100ma_driver.iConfiguration = status;
|
||||
|
||||
if (!gadget_supports_altsettings(gadget))
|
||||
if (!gadget_supports_altsettings(gadget)) {
|
||||
status = -ENODEV;
|
||||
goto err_usb;
|
||||
}
|
||||
|
||||
fi_phonet = usb_get_function_instance("phonet");
|
||||
if (IS_ERR(fi_phonet))
|
||||
pr_debug("could not find phonet function\n");
|
||||
|
||||
fi_obex1 = usb_get_function_instance("obex");
|
||||
if (IS_ERR(fi_obex1))
|
||||
pr_debug("could not find obex function 1\n");
|
||||
|
||||
fi_obex2 = usb_get_function_instance("obex");
|
||||
if (IS_ERR(fi_obex2))
|
||||
pr_debug("could not find obex function 2\n");
|
||||
|
||||
fi_acm = usb_get_function_instance("acm");
|
||||
if (IS_ERR(fi_acm))
|
||||
goto err_usb;
|
||||
if (IS_ERR(fi_acm)) {
|
||||
status = PTR_ERR(fi_acm);
|
||||
goto err_obex2_inst;
|
||||
}
|
||||
|
||||
fi_ecm = usb_get_function_instance("ecm");
|
||||
if (IS_ERR(fi_ecm)) {
|
||||
status = PTR_ERR(fi_ecm);
|
||||
goto err_acm_inst;
|
||||
}
|
||||
|
||||
/* finally register the configuration */
|
||||
status = usb_add_config(cdev, &nokia_config_500ma_driver,
|
||||
nokia_bind_config);
|
||||
if (status < 0)
|
||||
goto err_acm_inst;
|
||||
goto err_ecm_inst;
|
||||
|
||||
status = usb_add_config(cdev, &nokia_config_100ma_driver,
|
||||
nokia_bind_config);
|
||||
@ -226,33 +285,55 @@ static int __init nokia_bind(struct usb_composite_dev *cdev)
|
||||
|
||||
err_put_cfg1:
|
||||
usb_put_function(f_acm_cfg1);
|
||||
if (!IS_ERR_OR_NULL(f_obex1_cfg1))
|
||||
usb_put_function(f_obex1_cfg1);
|
||||
if (!IS_ERR_OR_NULL(f_obex2_cfg1))
|
||||
usb_put_function(f_obex2_cfg1);
|
||||
if (!IS_ERR_OR_NULL(f_phonet_cfg1))
|
||||
usb_put_function(f_phonet_cfg1);
|
||||
usb_put_function(f_ecm_cfg1);
|
||||
err_ecm_inst:
|
||||
usb_put_function_instance(fi_ecm);
|
||||
err_acm_inst:
|
||||
usb_put_function_instance(fi_acm);
|
||||
err_obex2_inst:
|
||||
if (!IS_ERR(fi_obex2))
|
||||
usb_put_function_instance(fi_obex2);
|
||||
if (!IS_ERR(fi_obex1))
|
||||
usb_put_function_instance(fi_obex1);
|
||||
if (!IS_ERR(fi_phonet))
|
||||
usb_put_function_instance(fi_phonet);
|
||||
err_usb:
|
||||
gether_cleanup(the_dev);
|
||||
err_ether:
|
||||
cur_line--;
|
||||
while (cur_line >= 0)
|
||||
gserial_free_line(tty_lines[cur_line--]);
|
||||
|
||||
gphonet_cleanup();
|
||||
err_phonet:
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __exit nokia_unbind(struct usb_composite_dev *cdev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!IS_ERR_OR_NULL(f_obex1_cfg2))
|
||||
usb_put_function(f_obex1_cfg2);
|
||||
if (!IS_ERR_OR_NULL(f_obex2_cfg2))
|
||||
usb_put_function(f_obex2_cfg2);
|
||||
if (!IS_ERR_OR_NULL(f_obex1_cfg1))
|
||||
usb_put_function(f_obex1_cfg1);
|
||||
if (!IS_ERR_OR_NULL(f_obex2_cfg1))
|
||||
usb_put_function(f_obex2_cfg1);
|
||||
if (!IS_ERR_OR_NULL(f_phonet_cfg1))
|
||||
usb_put_function(f_phonet_cfg1);
|
||||
if (!IS_ERR_OR_NULL(f_phonet_cfg2))
|
||||
usb_put_function(f_phonet_cfg2);
|
||||
usb_put_function(f_acm_cfg1);
|
||||
usb_put_function(f_acm_cfg2);
|
||||
usb_put_function(f_ecm_cfg1);
|
||||
usb_put_function(f_ecm_cfg2);
|
||||
|
||||
usb_put_function_instance(fi_ecm);
|
||||
if (!IS_ERR(fi_obex2))
|
||||
usb_put_function_instance(fi_obex2);
|
||||
if (!IS_ERR(fi_obex1))
|
||||
usb_put_function_instance(fi_obex1);
|
||||
if (!IS_ERR(fi_phonet))
|
||||
usb_put_function_instance(fi_phonet);
|
||||
usb_put_function_instance(fi_acm);
|
||||
gphonet_cleanup();
|
||||
|
||||
for (i = 0; i < TTY_PORTS_MAX; i++)
|
||||
gserial_free_line(tty_lines[i]);
|
||||
|
||||
gether_cleanup(the_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1469,11 +1469,11 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597)
|
||||
u16 savepipe;
|
||||
u16 mask0;
|
||||
|
||||
spin_lock(&r8a66597->lock);
|
||||
|
||||
if (r8a66597_is_sudmac(r8a66597))
|
||||
r8a66597_sudmac_irq(r8a66597);
|
||||
|
||||
spin_lock(&r8a66597->lock);
|
||||
|
||||
intsts0 = r8a66597_read(r8a66597, INTSTS0);
|
||||
intenb0 = r8a66597_read(r8a66597, INTENB0);
|
||||
|
||||
@ -1822,7 +1822,7 @@ static const struct usb_gadget_ops r8a66597_gadget_ops = {
|
||||
|
||||
static int __exit r8a66597_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev);
|
||||
struct r8a66597 *r8a66597 = platform_get_drvdata(pdev);
|
||||
|
||||
usb_del_gadget_udc(&r8a66597->gadget);
|
||||
del_timer_sync(&r8a66597->timer);
|
||||
@ -1909,7 +1909,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
spin_lock_init(&r8a66597->lock);
|
||||
dev_set_drvdata(&pdev->dev, r8a66597);
|
||||
platform_set_drvdata(pdev, r8a66597);
|
||||
r8a66597->pdata = pdev->dev.platform_data;
|
||||
r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
|
||||
|
||||
|
@ -761,6 +761,7 @@ int rndis_signal_connect(int configNr)
|
||||
return rndis_indicate_status_msg(configNr,
|
||||
RNDIS_STATUS_MEDIA_CONNECT);
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_signal_connect);
|
||||
|
||||
int rndis_signal_disconnect(int configNr)
|
||||
{
|
||||
@ -769,6 +770,7 @@ int rndis_signal_disconnect(int configNr)
|
||||
return rndis_indicate_status_msg(configNr,
|
||||
RNDIS_STATUS_MEDIA_DISCONNECT);
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_signal_disconnect);
|
||||
|
||||
void rndis_uninit(int configNr)
|
||||
{
|
||||
@ -783,11 +785,13 @@ void rndis_uninit(int configNr)
|
||||
while ((buf = rndis_get_next_response(configNr, &length)))
|
||||
rndis_free_response(configNr, buf);
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_uninit);
|
||||
|
||||
void rndis_set_host_mac(int configNr, const u8 *addr)
|
||||
{
|
||||
rndis_per_dev_params[configNr].host_mac = addr;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_set_host_mac);
|
||||
|
||||
/*
|
||||
* Message Parser
|
||||
@ -870,6 +874,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_msg_parser);
|
||||
|
||||
int rndis_register(void (*resp_avail)(void *v), void *v)
|
||||
{
|
||||
@ -891,6 +896,7 @@ int rndis_register(void (*resp_avail)(void *v), void *v)
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_register);
|
||||
|
||||
void rndis_deregister(int configNr)
|
||||
{
|
||||
@ -899,6 +905,7 @@ void rndis_deregister(int configNr)
|
||||
if (configNr >= RNDIS_MAX_CONFIGS) return;
|
||||
rndis_per_dev_params[configNr].used = 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_deregister);
|
||||
|
||||
int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
|
||||
{
|
||||
@ -912,6 +919,7 @@ int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_set_param_dev);
|
||||
|
||||
int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
|
||||
{
|
||||
@ -924,6 +932,7 @@ int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_set_param_vendor);
|
||||
|
||||
int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
|
||||
{
|
||||
@ -935,6 +944,7 @@ int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_set_param_medium);
|
||||
|
||||
void rndis_add_hdr(struct sk_buff *skb)
|
||||
{
|
||||
@ -949,6 +959,7 @@ void rndis_add_hdr(struct sk_buff *skb)
|
||||
header->DataOffset = cpu_to_le32(36);
|
||||
header->DataLength = cpu_to_le32(skb->len - sizeof(*header));
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_add_hdr);
|
||||
|
||||
void rndis_free_response(int configNr, u8 *buf)
|
||||
{
|
||||
@ -965,6 +976,7 @@ void rndis_free_response(int configNr, u8 *buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_free_response);
|
||||
|
||||
u8 *rndis_get_next_response(int configNr, u32 *length)
|
||||
{
|
||||
@ -986,6 +998,7 @@ u8 *rndis_get_next_response(int configNr, u32 *length)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_get_next_response);
|
||||
|
||||
static rndis_resp_t *rndis_add_response(int configNr, u32 length)
|
||||
{
|
||||
@ -1029,6 +1042,7 @@ int rndis_rm_hdr(struct gether *port,
|
||||
skb_queue_tail(list, skb);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(rndis_rm_hdr);
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
|
||||
|
||||
@ -1160,6 +1174,7 @@ int rndis_init(void)
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(rndis_init);
|
||||
|
||||
void rndis_exit(void)
|
||||
{
|
||||
@ -1173,3 +1188,6 @@ void rndis_exit(void)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
module_exit(rndis_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -16,6 +16,7 @@
|
||||
#define _LINUX_RNDIS_H
|
||||
|
||||
#include <linux/rndis.h>
|
||||
#include "u_ether.h"
|
||||
#include "ndis.h"
|
||||
|
||||
#define RNDIS_MAXIMUM_FRAME_SIZE 1518
|
||||
@ -216,7 +217,4 @@ int rndis_signal_disconnect (int configNr);
|
||||
int rndis_state (int configNr);
|
||||
extern void rndis_set_host_mac (int configNr, const u8 *addr);
|
||||
|
||||
int rndis_init(void);
|
||||
void rndis_exit (void);
|
||||
|
||||
#endif /* _LINUX_RNDIS_H */
|
||||
|
36
drivers/usb/gadget/u_ecm.h
Normal file
36
drivers/usb/gadget/u_ecm.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* u_ecm.h
|
||||
*
|
||||
* Utility definitions for the ecm function
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Author: Andrzej Pietrasiewicz <andrzej.p@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 U_ECM_H
|
||||
#define U_ECM_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
struct f_ecm_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
struct net_device *net;
|
||||
bool bound;
|
||||
|
||||
/*
|
||||
* Read/write access to configfs attributes is handled by configfs.
|
||||
*
|
||||
* This is to protect the data from concurrent access by read/write
|
||||
* and create symlink/remove symlink.
|
||||
*/
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
#endif /* U_ECM_H */
|
36
drivers/usb/gadget/u_eem.h
Normal file
36
drivers/usb/gadget/u_eem.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* u_eem.h
|
||||
*
|
||||
* Utility definitions for the eem function
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Author: Andrzej Pietrasiewicz <andrzej.p@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 U_EEM_H
|
||||
#define U_EEM_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
struct f_eem_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
struct net_device *net;
|
||||
bool bound;
|
||||
|
||||
/*
|
||||
* Read/write access to configfs attributes is handled by configfs.
|
||||
*
|
||||
* This is to protect the data from concurrent access by read/write
|
||||
* and create symlink/remove symlink.
|
||||
*/
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
#endif /* U_EEM_H */
|
@ -63,6 +63,8 @@ struct eth_dev {
|
||||
|
||||
struct sk_buff_head rx_frames;
|
||||
|
||||
unsigned qmult;
|
||||
|
||||
unsigned header_len;
|
||||
struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb);
|
||||
int (*unwrap)(struct gether *,
|
||||
@ -76,6 +78,7 @@ struct eth_dev {
|
||||
|
||||
bool zlp;
|
||||
u8 host_mac[ETH_ALEN];
|
||||
u8 dev_mac[ETH_ALEN];
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@ -84,12 +87,8 @@ struct eth_dev {
|
||||
|
||||
#define DEFAULT_QLEN 2 /* double buffering by default */
|
||||
|
||||
static unsigned qmult = 5;
|
||||
module_param(qmult, uint, S_IRUGO|S_IWUSR);
|
||||
MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");
|
||||
|
||||
/* for dual-speed hardware, use deeper queues at high/super speed */
|
||||
static inline int qlen(struct usb_gadget *gadget)
|
||||
static inline int qlen(struct usb_gadget *gadget, unsigned qmult)
|
||||
{
|
||||
if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
|
||||
gadget->speed == USB_SPEED_SUPER))
|
||||
@ -588,7 +587,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
|
||||
if (gadget_is_dualspeed(dev->gadget))
|
||||
req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
|
||||
dev->gadget->speed == USB_SPEED_SUPER)
|
||||
? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
|
||||
? ((atomic_read(&dev->tx_qlen) % dev->qmult) != 0)
|
||||
: 0;
|
||||
|
||||
retval = usb_ep_queue(in, req, GFP_ATOMIC);
|
||||
@ -697,16 +696,6 @@ static int eth_stop(struct net_device *net)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
|
||||
static char *dev_addr;
|
||||
module_param(dev_addr, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");
|
||||
|
||||
/* this address is invisible to ifconfig */
|
||||
static char *host_addr;
|
||||
module_param(host_addr, charp, S_IRUGO);
|
||||
MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
|
||||
|
||||
static int get_ether_addr(const char *str, u8 *dev_addr)
|
||||
{
|
||||
if (str) {
|
||||
@ -728,6 +717,17 @@ static int get_ether_addr(const char *str, u8 *dev_addr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len)
|
||||
{
|
||||
if (len < 18)
|
||||
return -EINVAL;
|
||||
|
||||
snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
dev_addr[0], dev_addr[1], dev_addr[2],
|
||||
dev_addr[3], dev_addr[4], dev_addr[5]);
|
||||
return 18;
|
||||
}
|
||||
|
||||
static const struct net_device_ops eth_netdev_ops = {
|
||||
.ndo_open = eth_open,
|
||||
.ndo_stop = eth_stop,
|
||||
@ -755,8 +755,9 @@ static struct device_type gadget_type = {
|
||||
*
|
||||
* Returns negative errno, or zero on success
|
||||
*/
|
||||
struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||
const char *netname)
|
||||
struct eth_dev *gether_setup_name(struct usb_gadget *g,
|
||||
const char *dev_addr, const char *host_addr,
|
||||
u8 ethaddr[ETH_ALEN], unsigned qmult, const char *netname)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
struct net_device *net;
|
||||
@ -777,6 +778,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||
|
||||
/* network device setup */
|
||||
dev->net = net;
|
||||
dev->qmult = qmult;
|
||||
snprintf(net->name, sizeof(net->name), "%s%%d", netname);
|
||||
|
||||
if (get_ether_addr(dev_addr, net->dev_addr))
|
||||
@ -806,7 +808,8 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||
INFO(dev, "MAC %pM\n", net->dev_addr);
|
||||
INFO(dev, "HOST MAC %pM\n", dev->host_mac);
|
||||
|
||||
/* two kinds of host-initiated state changes:
|
||||
/*
|
||||
* two kinds of host-initiated state changes:
|
||||
* - iff DATA transfer is active, carrier is "on"
|
||||
* - tx queueing enabled if open *and* carrier is "on"
|
||||
*/
|
||||
@ -815,6 +818,186 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||
|
||||
return dev;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_setup_name);
|
||||
|
||||
struct net_device *gether_setup_name_default(const char *netname)
|
||||
{
|
||||
struct net_device *net;
|
||||
struct eth_dev *dev;
|
||||
|
||||
net = alloc_etherdev(sizeof(*dev));
|
||||
if (!net)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev = netdev_priv(net);
|
||||
spin_lock_init(&dev->lock);
|
||||
spin_lock_init(&dev->req_lock);
|
||||
INIT_WORK(&dev->work, eth_work);
|
||||
INIT_LIST_HEAD(&dev->tx_reqs);
|
||||
INIT_LIST_HEAD(&dev->rx_reqs);
|
||||
|
||||
skb_queue_head_init(&dev->rx_frames);
|
||||
|
||||
/* network device setup */
|
||||
dev->net = net;
|
||||
dev->qmult = QMULT_DEFAULT;
|
||||
snprintf(net->name, sizeof(net->name), "%s%%d", netname);
|
||||
|
||||
eth_random_addr(dev->dev_mac);
|
||||
pr_warn("using random %s ethernet address\n", "self");
|
||||
eth_random_addr(dev->host_mac);
|
||||
pr_warn("using random %s ethernet address\n", "host");
|
||||
|
||||
net->netdev_ops = ð_netdev_ops;
|
||||
|
||||
SET_ETHTOOL_OPS(net, &ops);
|
||||
SET_NETDEV_DEVTYPE(net, &gadget_type);
|
||||
|
||||
return net;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_setup_name_default);
|
||||
|
||||
int gether_register_netdev(struct net_device *net)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
struct usb_gadget *g;
|
||||
struct sockaddr sa;
|
||||
int status;
|
||||
|
||||
if (!net->dev.parent)
|
||||
return -EINVAL;
|
||||
dev = netdev_priv(net);
|
||||
g = dev->gadget;
|
||||
status = register_netdev(net);
|
||||
if (status < 0) {
|
||||
dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
|
||||
return status;
|
||||
} else {
|
||||
INFO(dev, "HOST MAC %pM\n", dev->host_mac);
|
||||
|
||||
/* two kinds of host-initiated state changes:
|
||||
* - iff DATA transfer is active, carrier is "on"
|
||||
* - tx queueing enabled if open *and* carrier is "on"
|
||||
*/
|
||||
netif_carrier_off(net);
|
||||
}
|
||||
sa.sa_family = net->type;
|
||||
memcpy(sa.sa_data, dev->dev_mac, ETH_ALEN);
|
||||
rtnl_lock();
|
||||
status = dev_set_mac_address(net, &sa);
|
||||
rtnl_unlock();
|
||||
if (status)
|
||||
pr_warn("cannot set self ethernet address: %d\n", status);
|
||||
else
|
||||
INFO(dev, "MAC %pM\n", dev->dev_mac);
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_register_netdev);
|
||||
|
||||
void gether_set_gadget(struct net_device *net, struct usb_gadget *g)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
|
||||
dev = netdev_priv(net);
|
||||
dev->gadget = g;
|
||||
SET_NETDEV_DEV(net, &g->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_set_gadget);
|
||||
|
||||
int gether_set_dev_addr(struct net_device *net, const char *dev_addr)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
u8 new_addr[ETH_ALEN];
|
||||
|
||||
dev = netdev_priv(net);
|
||||
if (get_ether_addr(dev_addr, new_addr))
|
||||
return -EINVAL;
|
||||
memcpy(dev->dev_mac, new_addr, ETH_ALEN);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_set_dev_addr);
|
||||
|
||||
int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
|
||||
dev = netdev_priv(net);
|
||||
return get_ether_addr_str(dev->dev_mac, dev_addr, len);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_dev_addr);
|
||||
|
||||
int gether_set_host_addr(struct net_device *net, const char *host_addr)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
u8 new_addr[ETH_ALEN];
|
||||
|
||||
dev = netdev_priv(net);
|
||||
if (get_ether_addr(host_addr, new_addr))
|
||||
return -EINVAL;
|
||||
memcpy(dev->host_mac, new_addr, ETH_ALEN);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_set_host_addr);
|
||||
|
||||
int gether_get_host_addr(struct net_device *net, char *host_addr, int len)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
|
||||
dev = netdev_priv(net);
|
||||
return get_ether_addr_str(dev->host_mac, host_addr, len);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_host_addr);
|
||||
|
||||
int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
|
||||
if (len < 13)
|
||||
return -EINVAL;
|
||||
|
||||
dev = netdev_priv(net);
|
||||
snprintf(host_addr, len, "%pm", dev->host_mac);
|
||||
|
||||
return strlen(host_addr);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_host_addr_cdc);
|
||||
|
||||
void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN])
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
|
||||
dev = netdev_priv(net);
|
||||
memcpy(host_mac, dev->host_mac, ETH_ALEN);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_host_addr_u8);
|
||||
|
||||
void gether_set_qmult(struct net_device *net, unsigned qmult)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
|
||||
dev = netdev_priv(net);
|
||||
dev->qmult = qmult;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_set_qmult);
|
||||
|
||||
unsigned gether_get_qmult(struct net_device *net)
|
||||
{
|
||||
struct eth_dev *dev;
|
||||
|
||||
dev = netdev_priv(net);
|
||||
return dev->qmult;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_qmult);
|
||||
|
||||
int gether_get_ifname(struct net_device *net, char *name, int len)
|
||||
{
|
||||
rtnl_lock();
|
||||
strlcpy(name, netdev_name(net), len);
|
||||
rtnl_unlock();
|
||||
return strlen(name);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_get_ifname);
|
||||
|
||||
/**
|
||||
* gether_cleanup - remove Ethernet-over-USB device
|
||||
@ -831,6 +1014,7 @@ void gether_cleanup(struct eth_dev *dev)
|
||||
flush_work(&dev->work);
|
||||
free_netdev(dev->net);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_cleanup);
|
||||
|
||||
/**
|
||||
* gether_connect - notify network layer that USB link is active
|
||||
@ -873,11 +1057,12 @@ struct net_device *gether_connect(struct gether *link)
|
||||
}
|
||||
|
||||
if (result == 0)
|
||||
result = alloc_requests(dev, link, qlen(dev->gadget));
|
||||
result = alloc_requests(dev, link, qlen(dev->gadget,
|
||||
dev->qmult));
|
||||
|
||||
if (result == 0) {
|
||||
dev->zlp = link->is_zlp_ok;
|
||||
DBG(dev, "qlen %d\n", qlen(dev->gadget));
|
||||
DBG(dev, "qlen %d\n", qlen(dev->gadget, dev->qmult));
|
||||
|
||||
dev->header_len = link->header_len;
|
||||
dev->unwrap = link->unwrap;
|
||||
@ -910,6 +1095,7 @@ fail0:
|
||||
return ERR_PTR(result);
|
||||
return dev->net;
|
||||
}
|
||||
EXPORT_SYMBOL(gether_connect);
|
||||
|
||||
/**
|
||||
* gether_disconnect - notify network layer that USB link is inactive
|
||||
@ -980,3 +1166,7 @@ void gether_disconnect(struct gether *link)
|
||||
dev->port_usb = NULL;
|
||||
spin_unlock(&dev->lock);
|
||||
}
|
||||
EXPORT_SYMBOL(gether_disconnect);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Brownell");
|
||||
|
@ -21,6 +21,26 @@
|
||||
|
||||
#include "gadget_chips.h"
|
||||
|
||||
#define QMULT_DEFAULT 5
|
||||
|
||||
/*
|
||||
* dev_addr: initial value
|
||||
* changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx"
|
||||
* host_addr: this address is invisible to ifconfig
|
||||
*/
|
||||
#define USB_ETHERNET_MODULE_PARAMETERS() \
|
||||
static unsigned qmult = QMULT_DEFAULT; \
|
||||
module_param(qmult, uint, S_IRUGO|S_IWUSR); \
|
||||
MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");\
|
||||
\
|
||||
static char *dev_addr; \
|
||||
module_param(dev_addr, charp, S_IRUGO); \
|
||||
MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); \
|
||||
\
|
||||
static char *host_addr; \
|
||||
module_param(host_addr, charp, S_IRUGO); \
|
||||
MODULE_PARM_DESC(host_addr, "Host Ethernet Address")
|
||||
|
||||
struct eth_dev;
|
||||
|
||||
/*
|
||||
@ -71,8 +91,9 @@ struct gether {
|
||||
|USB_CDC_PACKET_TYPE_DIRECTED)
|
||||
|
||||
/* variant of gether_setup that allows customizing network device name */
|
||||
struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||
const char *netname);
|
||||
struct eth_dev *gether_setup_name(struct usb_gadget *g,
|
||||
const char *dev_addr, const char *host_addr,
|
||||
u8 ethaddr[ETH_ALEN], unsigned qmult, const char *netname);
|
||||
|
||||
/* netdev setup/teardown as directed by the gadget driver */
|
||||
/* gether_setup - initialize one ethernet-over-usb link
|
||||
@ -88,11 +109,145 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
|
||||
* Returns negative errno, or zero on success
|
||||
*/
|
||||
static inline struct eth_dev *gether_setup(struct usb_gadget *g,
|
||||
u8 ethaddr[ETH_ALEN])
|
||||
const char *dev_addr, const char *host_addr,
|
||||
u8 ethaddr[ETH_ALEN], unsigned qmult)
|
||||
{
|
||||
return gether_setup_name(g, ethaddr, "usb");
|
||||
return gether_setup_name(g, dev_addr, host_addr, ethaddr, qmult, "usb");
|
||||
}
|
||||
|
||||
/*
|
||||
* variant of gether_setup_default that allows customizing
|
||||
* network device name
|
||||
*/
|
||||
struct net_device *gether_setup_name_default(const char *netname);
|
||||
|
||||
/*
|
||||
* gether_register_netdev - register the net device
|
||||
* @net: net device to register
|
||||
*
|
||||
* Registers the net device associated with this ethernet-over-usb link
|
||||
*
|
||||
*/
|
||||
int gether_register_netdev(struct net_device *net);
|
||||
|
||||
/* gether_setup_default - initialize one ethernet-over-usb link
|
||||
* Context: may sleep
|
||||
*
|
||||
* This sets up the single network link that may be exported by a
|
||||
* gadget driver using this framework. The link layer addresses
|
||||
* are set to random values.
|
||||
*
|
||||
* Returns negative errno, or zero on success
|
||||
*/
|
||||
static inline struct net_device *gether_setup_default(void)
|
||||
{
|
||||
return gether_setup_name_default("usb");
|
||||
}
|
||||
|
||||
/**
|
||||
* gether_set_gadget - initialize one ethernet-over-usb link with a gadget
|
||||
* @net: device representing this link
|
||||
* @g: the gadget to initialize with
|
||||
*
|
||||
* This associates one ethernet-over-usb link with a gadget.
|
||||
*/
|
||||
void gether_set_gadget(struct net_device *net, struct usb_gadget *g);
|
||||
|
||||
/**
|
||||
* gether_set_dev_addr - initialize an ethernet-over-usb link with eth address
|
||||
* @net: device representing this link
|
||||
* @dev_addr: eth address of this device
|
||||
*
|
||||
* This sets the device-side Ethernet address of this ethernet-over-usb link
|
||||
* if dev_addr is correct.
|
||||
* Returns negative errno if the new address is incorrect.
|
||||
*/
|
||||
int gether_set_dev_addr(struct net_device *net, const char *dev_addr);
|
||||
|
||||
/**
|
||||
* gether_get_dev_addr - get an ethernet-over-usb link eth address
|
||||
* @net: device representing this link
|
||||
* @dev_addr: place to store device's eth address
|
||||
* @len: length of the @dev_addr buffer
|
||||
*
|
||||
* This gets the device-side Ethernet address of this ethernet-over-usb link.
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
int gether_get_dev_addr(struct net_device *net, char *dev_addr, int len);
|
||||
|
||||
/**
|
||||
* gether_set_host_addr - initialize an ethernet-over-usb link with host address
|
||||
* @net: device representing this link
|
||||
* @host_addr: eth address of the host
|
||||
*
|
||||
* This sets the host-side Ethernet address of this ethernet-over-usb link
|
||||
* if host_addr is correct.
|
||||
* Returns negative errno if the new address is incorrect.
|
||||
*/
|
||||
int gether_set_host_addr(struct net_device *net, const char *host_addr);
|
||||
|
||||
/**
|
||||
* gether_get_host_addr - get an ethernet-over-usb link host address
|
||||
* @net: device representing this link
|
||||
* @host_addr: place to store eth address of the host
|
||||
* @len: length of the @host_addr buffer
|
||||
*
|
||||
* This gets the host-side Ethernet address of this ethernet-over-usb link.
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
int gether_get_host_addr(struct net_device *net, char *host_addr, int len);
|
||||
|
||||
/**
|
||||
* gether_get_host_addr_cdc - get an ethernet-over-usb link host address
|
||||
* @net: device representing this link
|
||||
* @host_addr: place to store eth address of the host
|
||||
* @len: length of the @host_addr buffer
|
||||
*
|
||||
* This gets the CDC formatted host-side Ethernet address of this
|
||||
* ethernet-over-usb link.
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len);
|
||||
|
||||
/**
|
||||
* gether_get_host_addr_u8 - get an ethernet-over-usb link host address
|
||||
* @net: device representing this link
|
||||
* @host_mac: place to store the eth address of the host
|
||||
*
|
||||
* This gets the binary formatted host-side Ethernet address of this
|
||||
* ethernet-over-usb link.
|
||||
*/
|
||||
void gether_get_host_addr_u8(struct net_device *net, u8 host_mac[ETH_ALEN]);
|
||||
|
||||
/**
|
||||
* gether_set_qmult - initialize an ethernet-over-usb link with a multiplier
|
||||
* @net: device representing this link
|
||||
* @qmult: queue multiplier
|
||||
*
|
||||
* This sets the queue length multiplier of this ethernet-over-usb link.
|
||||
* For higher speeds use longer queues.
|
||||
*/
|
||||
void gether_set_qmult(struct net_device *net, unsigned qmult);
|
||||
|
||||
/**
|
||||
* gether_get_qmult - get an ethernet-over-usb link multiplier
|
||||
* @net: device representing this link
|
||||
*
|
||||
* This gets the queue length multiplier of this ethernet-over-usb link.
|
||||
*/
|
||||
unsigned gether_get_qmult(struct net_device *net);
|
||||
|
||||
/**
|
||||
* gether_get_ifname - get an ethernet-over-usb link interface name
|
||||
* @net: device representing this link
|
||||
* @name: place to store the interface name
|
||||
* @len: length of the @name buffer
|
||||
*
|
||||
* This gets the interface name of this ethernet-over-usb link.
|
||||
* Returns zero on success, else negative errno.
|
||||
*/
|
||||
int gether_get_ifname(struct net_device *net, char *name, int len);
|
||||
|
||||
void gether_cleanup(struct eth_dev *dev);
|
||||
|
||||
/* connect/disconnect is handled by individual functions */
|
||||
@ -117,9 +272,6 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
struct eth_dev *dev);
|
||||
int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
struct eth_dev *dev);
|
||||
int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
|
||||
struct eth_dev *dev);
|
||||
int eem_bind_config(struct usb_configuration *c, struct eth_dev *dev);
|
||||
|
||||
#ifdef USB_ETH_RNDIS
|
||||
|
||||
|
164
drivers/usb/gadget/u_ether_configfs.h
Normal file
164
drivers/usb/gadget/u_ether_configfs.h
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* u_ether_configfs.h
|
||||
*
|
||||
* Utility definitions for configfs support in USB Ethernet functions
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Author: Andrzej Pietrasiewicz <andrzej.p@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 __U_ETHER_CONFIGFS_H
|
||||
#define __U_ETHER_CONFIGFS_H
|
||||
|
||||
#define USB_ETHERNET_CONFIGFS_ITEM(_f_) \
|
||||
CONFIGFS_ATTR_STRUCT(f_##_f_##_opts); \
|
||||
CONFIGFS_ATTR_OPS(f_##_f_##_opts); \
|
||||
\
|
||||
static void _f_##_attr_release(struct config_item *item) \
|
||||
{ \
|
||||
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
|
||||
\
|
||||
usb_put_function_instance(&opts->func_inst); \
|
||||
} \
|
||||
\
|
||||
static struct configfs_item_operations _f_##_item_ops = { \
|
||||
.release = _f_##_attr_release, \
|
||||
.show_attribute = f_##_f_##_opts_attr_show, \
|
||||
.store_attribute = f_##_f_##_opts_attr_store, \
|
||||
}
|
||||
|
||||
#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(_f_) \
|
||||
static ssize_t _f_##_opts_dev_addr_show(struct f_##_f_##_opts *opts, \
|
||||
char *page) \
|
||||
{ \
|
||||
int result; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
result = gether_get_dev_addr(opts->net, page, PAGE_SIZE); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t _f_##_opts_dev_addr_store(struct f_##_f_##_opts *opts, \
|
||||
const char *page, size_t len)\
|
||||
{ \
|
||||
int ret; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return -EBUSY; \
|
||||
} \
|
||||
\
|
||||
ret = gether_set_dev_addr(opts->net, page); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
if (!ret) \
|
||||
ret = len; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
static struct f_##_f_##_opts_attribute f_##_f_##_opts_dev_addr = \
|
||||
__CONFIGFS_ATTR(dev_addr, S_IRUGO | S_IWUSR, \
|
||||
_f_##_opts_dev_addr_show, \
|
||||
_f_##_opts_dev_addr_store)
|
||||
|
||||
#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(_f_) \
|
||||
static ssize_t _f_##_opts_host_addr_show(struct f_##_f_##_opts *opts, \
|
||||
char *page) \
|
||||
{ \
|
||||
int result; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
result = gether_get_host_addr(opts->net, page, PAGE_SIZE); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return result; \
|
||||
} \
|
||||
\
|
||||
static ssize_t _f_##_opts_host_addr_store(struct f_##_f_##_opts *opts, \
|
||||
const char *page, size_t len)\
|
||||
{ \
|
||||
int ret; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return -EBUSY; \
|
||||
} \
|
||||
\
|
||||
ret = gether_set_host_addr(opts->net, page); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
if (!ret) \
|
||||
ret = len; \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
static struct f_##_f_##_opts_attribute f_##_f_##_opts_host_addr = \
|
||||
__CONFIGFS_ATTR(host_addr, S_IRUGO | S_IWUSR, \
|
||||
_f_##_opts_host_addr_show, \
|
||||
_f_##_opts_host_addr_store)
|
||||
|
||||
#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(_f_) \
|
||||
static ssize_t _f_##_opts_qmult_show(struct f_##_f_##_opts *opts, \
|
||||
char *page) \
|
||||
{ \
|
||||
unsigned qmult; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
qmult = gether_get_qmult(opts->net); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return sprintf(page, "%d", qmult); \
|
||||
} \
|
||||
\
|
||||
static ssize_t _f_##_opts_qmult_store(struct f_##_f_##_opts *opts, \
|
||||
const char *page, size_t len)\
|
||||
{ \
|
||||
u8 val; \
|
||||
int ret; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
if (opts->refcnt) { \
|
||||
ret = -EBUSY; \
|
||||
goto out; \
|
||||
} \
|
||||
\
|
||||
ret = kstrtou8(page, 0, &val); \
|
||||
if (ret) \
|
||||
goto out; \
|
||||
\
|
||||
gether_set_qmult(opts->net, val); \
|
||||
ret = len; \
|
||||
out: \
|
||||
mutex_unlock(&opts->lock); \
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
static struct f_##_f_##_opts_attribute f_##_f_##_opts_qmult = \
|
||||
__CONFIGFS_ATTR(qmult, S_IRUGO | S_IWUSR, \
|
||||
_f_##_opts_qmult_show, \
|
||||
_f_##_opts_qmult_store)
|
||||
|
||||
#define USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(_f_) \
|
||||
static ssize_t _f_##_opts_ifname_show(struct f_##_f_##_opts *opts, \
|
||||
char *page) \
|
||||
{ \
|
||||
int ret; \
|
||||
\
|
||||
mutex_lock(&opts->lock); \
|
||||
ret = gether_get_ifname(opts->net, page, PAGE_SIZE); \
|
||||
mutex_unlock(&opts->lock); \
|
||||
\
|
||||
return ret; \
|
||||
} \
|
||||
\
|
||||
static struct f_##_f_##_opts_attribute f_##_f_##_opts_ifname = \
|
||||
__CONFIGFS_ATTR_RO(ifname, _f_##_opts_ifname_show)
|
||||
|
||||
#endif /* __U_ETHER_CONFIGFS_H */
|
36
drivers/usb/gadget/u_gether.h
Normal file
36
drivers/usb/gadget/u_gether.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* u_gether.h
|
||||
*
|
||||
* Utility definitions for the subset function
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Author: Andrzej Pietrasiewicz <andrzej.p@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 U_GETHER_H
|
||||
#define U_GETHER_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
struct f_gether_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
struct net_device *net;
|
||||
bool bound;
|
||||
|
||||
/*
|
||||
* Read/write access to configfs attributes is handled by configfs.
|
||||
*
|
||||
* This is to protect the data from concurrent access by read/write
|
||||
* and create symlink/remove symlink.
|
||||
*/
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
#endif /* U_GETHER_H */
|
36
drivers/usb/gadget/u_ncm.h
Normal file
36
drivers/usb/gadget/u_ncm.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* u_ncm.h
|
||||
*
|
||||
* Utility definitions for the ncm function
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Author: Andrzej Pietrasiewicz <andrzej.p@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 U_NCM_H
|
||||
#define U_NCM_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
struct f_ncm_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
struct net_device *net;
|
||||
bool bound;
|
||||
|
||||
/*
|
||||
* Read/write access to configfs attributes is handled by configfs.
|
||||
*
|
||||
* This is to protect the data from concurrent access by read/write
|
||||
* and create symlink/remove symlink.
|
||||
*/
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
#endif /* U_NCM_H */
|
@ -14,8 +14,16 @@
|
||||
#include <linux/usb/composite.h>
|
||||
#include <linux/usb/cdc.h>
|
||||
|
||||
int gphonet_setup(struct usb_gadget *gadget);
|
||||
int phonet_bind_config(struct usb_configuration *c);
|
||||
void gphonet_cleanup(void);
|
||||
struct f_phonet_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
bool bound;
|
||||
struct net_device *net;
|
||||
};
|
||||
|
||||
struct net_device *gphonet_setup_default(void);
|
||||
void gphonet_set_gadget(struct net_device *net, struct usb_gadget *g);
|
||||
int gphonet_register_netdev(struct net_device *net);
|
||||
int phonet_bind_config(struct usb_configuration *c, struct net_device *dev);
|
||||
void gphonet_cleanup(struct net_device *dev);
|
||||
|
||||
#endif /* __U_PHONET_H */
|
||||
|
41
drivers/usb/gadget/u_rndis.h
Normal file
41
drivers/usb/gadget/u_rndis.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* u_rndis.h
|
||||
*
|
||||
* Utility definitions for the subset function
|
||||
*
|
||||
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com
|
||||
*
|
||||
* Author: Andrzej Pietrasiewicz <andrzej.p@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 U_RNDIS_H
|
||||
#define U_RNDIS_H
|
||||
|
||||
#include <linux/usb/composite.h>
|
||||
|
||||
struct f_rndis_opts {
|
||||
struct usb_function_instance func_inst;
|
||||
u32 vendor_id;
|
||||
const char *manufacturer;
|
||||
struct net_device *net;
|
||||
bool bound;
|
||||
bool borrowed_net;
|
||||
|
||||
/*
|
||||
* Read/write access to configfs attributes is handled by configfs.
|
||||
*
|
||||
* This is to protect the data from concurrent access by read/write
|
||||
* and create symlink/remove symlink.
|
||||
*/
|
||||
struct mutex lock;
|
||||
int refcnt;
|
||||
};
|
||||
|
||||
void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net);
|
||||
|
||||
#endif /* U_RNDIS_H */
|
@ -103,10 +103,26 @@ static void uvc_buffer_queue(struct vb2_buffer *vb)
|
||||
spin_unlock_irqrestore(&queue->irqlock, flags);
|
||||
}
|
||||
|
||||
static void uvc_wait_prepare(struct vb2_queue *vq)
|
||||
{
|
||||
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
|
||||
|
||||
mutex_unlock(&queue->mutex);
|
||||
}
|
||||
|
||||
static void uvc_wait_finish(struct vb2_queue *vq)
|
||||
{
|
||||
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
|
||||
|
||||
mutex_lock(&queue->mutex);
|
||||
}
|
||||
|
||||
static struct vb2_ops uvc_queue_qops = {
|
||||
.queue_setup = uvc_queue_setup,
|
||||
.buf_prepare = uvc_buffer_prepare,
|
||||
.buf_queue = uvc_buffer_queue,
|
||||
.wait_prepare = uvc_wait_prepare,
|
||||
.wait_finish = uvc_wait_finish,
|
||||
};
|
||||
|
||||
static int uvc_queue_init(struct uvc_video_queue *queue,
|
||||
|
@ -611,7 +611,7 @@ static const struct dev_pm_ops tegra_ehci_pm_ops = {
|
||||
/* Bits of PORTSC1, which will get cleared by writing 1 into them */
|
||||
#define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
|
||||
|
||||
static void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val)
|
||||
void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val)
|
||||
{
|
||||
unsigned long val;
|
||||
struct usb_hcd *hcd = bus_to_hcd(x->otg->host);
|
||||
@ -622,8 +622,9 @@ static void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val)
|
||||
val |= TEGRA_USB_PORTSC1_PTS(pts_val & 3);
|
||||
writel(val, base + TEGRA_USB_PORTSC1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_ehci_set_pts);
|
||||
|
||||
static void tegra_ehci_set_phcd(struct usb_phy *x, bool enable)
|
||||
void tegra_ehci_set_phcd(struct usb_phy *x, bool enable)
|
||||
{
|
||||
unsigned long val;
|
||||
struct usb_hcd *hcd = bus_to_hcd(x->otg->host);
|
||||
@ -636,6 +637,7 @@ static void tegra_ehci_set_phcd(struct usb_phy *x, bool enable)
|
||||
val &= ~TEGRA_USB_PORTSC1_PHCD;
|
||||
writel(val, base + TEGRA_USB_PORTSC1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_ehci_set_phcd);
|
||||
|
||||
static int tegra_ehci_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -645,7 +647,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
||||
struct tegra_ehci_platform_data *pdata;
|
||||
int err = 0;
|
||||
int irq;
|
||||
int instance = pdev->id;
|
||||
struct device_node *np_phy;
|
||||
struct usb_phy *u_phy;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
@ -670,38 +672,49 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
||||
if (!tegra)
|
||||
return -ENOMEM;
|
||||
|
||||
hcd = usb_create_hcd(&tegra_ehci_hc_driver, &pdev->dev,
|
||||
dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
dev_err(&pdev->dev, "Unable to create HCD\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tegra);
|
||||
|
||||
tegra->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(tegra->clk)) {
|
||||
dev_err(&pdev->dev, "Can't get ehci clock\n");
|
||||
err = PTR_ERR(tegra->clk);
|
||||
goto fail_clk;
|
||||
return PTR_ERR(tegra->clk);
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(tegra->clk);
|
||||
if (err)
|
||||
goto fail_clk;
|
||||
return err;
|
||||
|
||||
tegra_periph_reset_assert(tegra->clk);
|
||||
udelay(1);
|
||||
tegra_periph_reset_deassert(tegra->clk);
|
||||
|
||||
np_phy = of_parse_phandle(pdev->dev.of_node, "nvidia,phy", 0);
|
||||
if (!np_phy) {
|
||||
err = -ENODEV;
|
||||
goto cleanup_clk;
|
||||
}
|
||||
|
||||
u_phy = tegra_usb_get_phy(np_phy);
|
||||
if (IS_ERR(u_phy)) {
|
||||
err = PTR_ERR(u_phy);
|
||||
goto cleanup_clk;
|
||||
}
|
||||
|
||||
tegra->needs_double_reset = of_property_read_bool(pdev->dev.of_node,
|
||||
"nvidia,needs-double-reset");
|
||||
|
||||
hcd = usb_create_hcd(&tegra_ehci_hc_driver, &pdev->dev,
|
||||
dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
dev_err(&pdev->dev, "Unable to create HCD\n");
|
||||
err = -ENOMEM;
|
||||
goto cleanup_clk;
|
||||
}
|
||||
hcd->phy = u_phy;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get I/O memory\n");
|
||||
err = -ENXIO;
|
||||
goto fail_io;
|
||||
goto cleanup_hcd_create;
|
||||
}
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
@ -709,58 +722,28 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
||||
if (!hcd->regs) {
|
||||
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
|
||||
err = -ENOMEM;
|
||||
goto fail_io;
|
||||
goto cleanup_hcd_create;
|
||||
}
|
||||
|
||||
/* This is pretty ugly and needs to be fixed when we do only
|
||||
* device-tree probing. Old code relies on the platform_device
|
||||
* numbering that we lack for device-tree-instantiated devices.
|
||||
*/
|
||||
if (instance < 0) {
|
||||
switch (res->start) {
|
||||
case TEGRA_USB_BASE:
|
||||
instance = 0;
|
||||
break;
|
||||
case TEGRA_USB2_BASE:
|
||||
instance = 1;
|
||||
break;
|
||||
case TEGRA_USB3_BASE:
|
||||
instance = 2;
|
||||
break;
|
||||
default:
|
||||
err = -ENODEV;
|
||||
dev_err(&pdev->dev, "unknown usb instance\n");
|
||||
goto fail_io;
|
||||
}
|
||||
err = usb_phy_init(hcd->phy);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to initialize phy\n");
|
||||
goto cleanup_hcd_create;
|
||||
}
|
||||
|
||||
tegra->phy = tegra_usb_phy_open(&pdev->dev, instance, hcd->regs,
|
||||
pdata->phy_config,
|
||||
TEGRA_USB_PHY_MODE_HOST,
|
||||
tegra_ehci_set_pts,
|
||||
tegra_ehci_set_phcd);
|
||||
if (IS_ERR(tegra->phy)) {
|
||||
dev_err(&pdev->dev, "Failed to open USB phy\n");
|
||||
err = -ENXIO;
|
||||
goto fail_io;
|
||||
}
|
||||
|
||||
hcd->phy = u_phy = &tegra->phy->u_phy;
|
||||
usb_phy_init(hcd->phy);
|
||||
|
||||
u_phy->otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
|
||||
GFP_KERNEL);
|
||||
if (!u_phy->otg) {
|
||||
dev_err(&pdev->dev, "Failed to alloc memory for otg\n");
|
||||
err = -ENOMEM;
|
||||
goto fail_io;
|
||||
goto cleanup_phy;
|
||||
}
|
||||
u_phy->otg->host = hcd_to_bus(hcd);
|
||||
|
||||
err = usb_phy_set_suspend(hcd->phy, 0);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to power on the phy\n");
|
||||
goto fail_phy;
|
||||
goto cleanup_phy;
|
||||
}
|
||||
|
||||
tegra->host_resumed = 1;
|
||||
@ -770,7 +753,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
||||
if (!irq) {
|
||||
dev_err(&pdev->dev, "Failed to get IRQ\n");
|
||||
err = -ENODEV;
|
||||
goto fail_phy;
|
||||
goto cleanup_phy;
|
||||
}
|
||||
|
||||
if (pdata->operating_mode == TEGRA_USB_OTG) {
|
||||
@ -782,10 +765,12 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
||||
tegra->transceiver = ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tegra);
|
||||
|
||||
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to add USB HCD\n");
|
||||
goto fail;
|
||||
goto cleanup_phy;
|
||||
}
|
||||
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
@ -798,15 +783,15 @@ static int tegra_ehci_probe(struct platform_device *pdev)
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
return err;
|
||||
|
||||
fail:
|
||||
cleanup_phy:
|
||||
if (!IS_ERR(tegra->transceiver))
|
||||
otg_set_host(tegra->transceiver->otg, NULL);
|
||||
fail_phy:
|
||||
|
||||
usb_phy_shutdown(hcd->phy);
|
||||
fail_io:
|
||||
clk_disable_unprepare(tegra->clk);
|
||||
fail_clk:
|
||||
cleanup_hcd_create:
|
||||
usb_put_hcd(hcd);
|
||||
cleanup_clk:
|
||||
clk_disable_unprepare(tegra->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,35 @@ config USB_MUSB_HDRC
|
||||
|
||||
if USB_MUSB_HDRC
|
||||
|
||||
choice
|
||||
bool "MUSB Mode Selection"
|
||||
default USB_MUSB_DUAL_ROLE if (USB && USB_GADGET)
|
||||
default USB_MUSB_HOST if (USB && !USB_GADGET)
|
||||
default USB_MUSB_GADGET if (!USB && USB_GADGET)
|
||||
|
||||
config USB_MUSB_HOST
|
||||
bool "Host only mode"
|
||||
depends on USB
|
||||
help
|
||||
Select this when you want to use MUSB in host mode only,
|
||||
thereby the gadget feature will be regressed.
|
||||
|
||||
config USB_MUSB_GADGET
|
||||
bool "Gadget only mode"
|
||||
depends on USB_GADGET
|
||||
help
|
||||
Select this when you want to use MUSB in gadget mode only,
|
||||
thereby the host feature will be regressed.
|
||||
|
||||
config USB_MUSB_DUAL_ROLE
|
||||
bool "Dual Role mode"
|
||||
depends on (USB && USB_GADGET)
|
||||
help
|
||||
This is the default mode of working of MUSB controller where
|
||||
both host and gadget features are enabled.
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Platform Glue Layer"
|
||||
|
||||
|
@ -6,8 +6,8 @@ obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o
|
||||
|
||||
musb_hdrc-y := musb_core.o
|
||||
|
||||
musb_hdrc-y += musb_gadget_ep0.o musb_gadget.o
|
||||
musb_hdrc-y += musb_virthub.o musb_host.o
|
||||
musb_hdrc-$(CONFIG_USB_MUSB_HOST)$(CONFIG_USB_MUSB_DUAL_ROLE) += musb_virthub.o musb_host.o
|
||||
musb_hdrc-$(CONFIG_USB_MUSB_GADGET)$(CONFIG_USB_MUSB_DUAL_ROLE) += musb_gadget_ep0.o musb_gadget.o
|
||||
musb_hdrc-$(CONFIG_DEBUG_FS) += musb_debugfs.o
|
||||
|
||||
# Hardware Glue Layer
|
||||
|
@ -450,6 +450,7 @@ static u64 bfin_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int bfin_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource musb_resources[2];
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct bfin_glue *glue;
|
||||
@ -479,8 +480,21 @@ static int bfin_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
memset(musb_resources, 0x00, sizeof(*musb_resources) *
|
||||
ARRAY_SIZE(musb_resources));
|
||||
|
||||
musb_resources[0].name = pdev->resource[0].name;
|
||||
musb_resources[0].start = pdev->resource[0].start;
|
||||
musb_resources[0].end = pdev->resource[0].end;
|
||||
musb_resources[0].flags = pdev->resource[0].flags;
|
||||
|
||||
musb_resources[1].name = pdev->resource[1].name;
|
||||
musb_resources[1].start = pdev->resource[1].start;
|
||||
musb_resources[1].end = pdev->resource[1].end;
|
||||
musb_resources[1].flags = pdev->resource[1].flags;
|
||||
|
||||
ret = platform_device_add_resources(musb, musb_resources,
|
||||
ARRAY_SIZE(musb_resources));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err3;
|
||||
|
@ -476,6 +476,7 @@ static u64 da8xx_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int da8xx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource musb_resources[2];
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct da8xx_glue *glue;
|
||||
@ -521,8 +522,21 @@ static int da8xx_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
memset(musb_resources, 0x00, sizeof(*musb_resources) *
|
||||
ARRAY_SIZE(musb_resources));
|
||||
|
||||
musb_resources[0].name = pdev->resource[0].name;
|
||||
musb_resources[0].start = pdev->resource[0].start;
|
||||
musb_resources[0].end = pdev->resource[0].end;
|
||||
musb_resources[0].flags = pdev->resource[0].flags;
|
||||
|
||||
musb_resources[1].name = pdev->resource[1].name;
|
||||
musb_resources[1].start = pdev->resource[1].start;
|
||||
musb_resources[1].end = pdev->resource[1].end;
|
||||
musb_resources[1].flags = pdev->resource[1].flags;
|
||||
|
||||
ret = platform_device_add_resources(musb, musb_resources,
|
||||
ARRAY_SIZE(musb_resources));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err5;
|
||||
|
@ -509,6 +509,7 @@ static u64 davinci_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int davinci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource musb_resources[2];
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct davinci_glue *glue;
|
||||
@ -553,8 +554,21 @@ static int davinci_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
memset(musb_resources, 0x00, sizeof(*musb_resources) *
|
||||
ARRAY_SIZE(musb_resources));
|
||||
|
||||
musb_resources[0].name = pdev->resource[0].name;
|
||||
musb_resources[0].start = pdev->resource[0].start;
|
||||
musb_resources[0].end = pdev->resource[0].end;
|
||||
musb_resources[0].flags = pdev->resource[0].flags;
|
||||
|
||||
musb_resources[1].name = pdev->resource[1].name;
|
||||
musb_resources[1].start = pdev->resource[1].start;
|
||||
musb_resources[1].end = pdev->resource[1].end;
|
||||
musb_resources[1].flags = pdev->resource[1].flags;
|
||||
|
||||
ret = platform_device_add_resources(musb, musb_resources,
|
||||
ARRAY_SIZE(musb_resources));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err5;
|
||||
|
@ -380,7 +380,6 @@ static void musb_otg_timer_func(unsigned long data)
|
||||
dev_dbg(musb->controller, "HNP: Unhandled mode %s\n",
|
||||
usb_otg_state_string(musb->xceiv->state));
|
||||
}
|
||||
musb->ignore_disconnect = 0;
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
}
|
||||
|
||||
@ -389,7 +388,7 @@ static void musb_otg_timer_func(unsigned long data)
|
||||
*/
|
||||
void musb_hnp_stop(struct musb *musb)
|
||||
{
|
||||
struct usb_hcd *hcd = musb_to_hcd(musb);
|
||||
struct usb_hcd *hcd = musb->hcd;
|
||||
void __iomem *mbase = musb->mregs;
|
||||
u8 reg;
|
||||
|
||||
@ -404,7 +403,8 @@ void musb_hnp_stop(struct musb *musb)
|
||||
break;
|
||||
case OTG_STATE_B_HOST:
|
||||
dev_dbg(musb->controller, "HNP: Disabling HR\n");
|
||||
hcd->self.is_b_host = 0;
|
||||
if (hcd)
|
||||
hcd->self.is_b_host = 0;
|
||||
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
||||
MUSB_DEV_MODE(musb);
|
||||
reg = musb_readb(mbase, MUSB_POWER);
|
||||
@ -484,7 +484,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
|
||||
musb->xceiv->state = OTG_STATE_A_HOST;
|
||||
musb->is_active = 1;
|
||||
usb_hcd_resume_root_hub(musb_to_hcd(musb));
|
||||
musb_host_resume_root_hub(musb);
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
||||
@ -501,7 +501,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
/* possibly DISCONNECT is upcoming */
|
||||
musb->xceiv->state = OTG_STATE_A_HOST;
|
||||
usb_hcd_resume_root_hub(musb_to_hcd(musb));
|
||||
musb_host_resume_root_hub(musb);
|
||||
break;
|
||||
case OTG_STATE_B_WAIT_ACON:
|
||||
case OTG_STATE_B_PERIPHERAL:
|
||||
@ -643,7 +643,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
* undesired detour through A_WAIT_BCON.
|
||||
*/
|
||||
musb_hnp_stop(musb);
|
||||
usb_hcd_resume_root_hub(musb_to_hcd(musb));
|
||||
musb_host_resume_root_hub(musb);
|
||||
musb_root_disconnect(musb);
|
||||
musb_platform_try_idle(musb, jiffies
|
||||
+ msecs_to_jiffies(musb->a_wait_bcon
|
||||
@ -685,7 +685,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
}
|
||||
|
||||
if (int_usb & MUSB_INTR_CONNECT) {
|
||||
struct usb_hcd *hcd = musb_to_hcd(musb);
|
||||
struct usb_hcd *hcd = musb->hcd;
|
||||
|
||||
handled = IRQ_HANDLED;
|
||||
musb->is_active = 1;
|
||||
@ -726,31 +726,27 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
|
||||
dev_dbg(musb->controller, "HNP: CONNECT, now b_host\n");
|
||||
b_host:
|
||||
musb->xceiv->state = OTG_STATE_B_HOST;
|
||||
hcd->self.is_b_host = 1;
|
||||
musb->ignore_disconnect = 0;
|
||||
if (musb->hcd)
|
||||
musb->hcd->self.is_b_host = 1;
|
||||
del_timer(&musb->otg_timer);
|
||||
break;
|
||||
default:
|
||||
if ((devctl & MUSB_DEVCTL_VBUS)
|
||||
== (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
|
||||
musb->xceiv->state = OTG_STATE_A_HOST;
|
||||
hcd->self.is_b_host = 0;
|
||||
if (hcd)
|
||||
hcd->self.is_b_host = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* poke the root hub */
|
||||
MUSB_HST_MODE(musb);
|
||||
if (hcd->status_urb)
|
||||
usb_hcd_poll_rh_status(hcd);
|
||||
else
|
||||
usb_hcd_resume_root_hub(hcd);
|
||||
musb_host_poke_root_hub(musb);
|
||||
|
||||
dev_dbg(musb->controller, "CONNECT (%s) devctl %02x\n",
|
||||
usb_otg_state_string(musb->xceiv->state), devctl);
|
||||
}
|
||||
|
||||
if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) {
|
||||
if (int_usb & MUSB_INTR_DISCONNECT) {
|
||||
dev_dbg(musb->controller, "DISCONNECT (%s) as %s, devctl %02x\n",
|
||||
usb_otg_state_string(musb->xceiv->state),
|
||||
MUSB_MODE(musb), devctl);
|
||||
@ -759,7 +755,7 @@ b_host:
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_HOST:
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
usb_hcd_resume_root_hub(musb_to_hcd(musb));
|
||||
musb_host_resume_root_hub(musb);
|
||||
musb_root_disconnect(musb);
|
||||
if (musb->a_wait_bcon != 0)
|
||||
musb_platform_try_idle(musb, jiffies
|
||||
@ -772,7 +768,8 @@ b_host:
|
||||
* in hnp_stop() is currently not used...
|
||||
*/
|
||||
musb_root_disconnect(musb);
|
||||
musb_to_hcd(musb)->self.is_b_host = 0;
|
||||
if (musb->hcd)
|
||||
musb->hcd->self.is_b_host = 0;
|
||||
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
|
||||
MUSB_DEV_MODE(musb);
|
||||
musb_g_disconnect(musb);
|
||||
@ -818,11 +815,6 @@ b_host:
|
||||
usb_otg_state_string(musb->xceiv->state));
|
||||
switch (musb->xceiv->state) {
|
||||
case OTG_STATE_A_SUSPEND:
|
||||
/* We need to ignore disconnect on suspend
|
||||
* otherwise tusb 2.0 won't reconnect after a
|
||||
* power cycle, which breaks otg compliance.
|
||||
*/
|
||||
musb->ignore_disconnect = 1;
|
||||
musb_g_reset(musb);
|
||||
/* FALLTHROUGH */
|
||||
case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
|
||||
@ -834,7 +826,6 @@ b_host:
|
||||
+ msecs_to_jiffies(TA_WAIT_BCON(musb)));
|
||||
break;
|
||||
case OTG_STATE_A_PERIPHERAL:
|
||||
musb->ignore_disconnect = 0;
|
||||
del_timer(&musb->otg_timer);
|
||||
musb_g_reset(musb);
|
||||
break;
|
||||
@ -909,51 +900,6 @@ b_host:
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* Program the HDRC to start (enable interrupts, dma, etc.).
|
||||
*/
|
||||
void musb_start(struct musb *musb)
|
||||
{
|
||||
void __iomem *regs = musb->mregs;
|
||||
u8 devctl = musb_readb(regs, MUSB_DEVCTL);
|
||||
|
||||
dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
|
||||
|
||||
/* Set INT enable registers, enable interrupts */
|
||||
musb->intrtxe = musb->epmask;
|
||||
musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
|
||||
musb->intrrxe = musb->epmask & 0xfffe;
|
||||
musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
|
||||
musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
|
||||
|
||||
musb_writeb(regs, MUSB_TESTMODE, 0);
|
||||
|
||||
/* put into basic highspeed mode and start session */
|
||||
musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE
|
||||
| MUSB_POWER_HSENAB
|
||||
/* ENSUSPEND wedges tusb */
|
||||
/* | MUSB_POWER_ENSUSPEND */
|
||||
);
|
||||
|
||||
musb->is_active = 0;
|
||||
devctl = musb_readb(regs, MUSB_DEVCTL);
|
||||
devctl &= ~MUSB_DEVCTL_SESSION;
|
||||
|
||||
/* session started after:
|
||||
* (a) ID-grounded irq, host mode;
|
||||
* (b) vbus present/connect IRQ, peripheral mode;
|
||||
* (c) peripheral initiates, using SRP
|
||||
*/
|
||||
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
|
||||
musb->is_active = 1;
|
||||
else
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
|
||||
musb_platform_enable(musb);
|
||||
musb_writeb(regs, MUSB_DEVCTL, devctl);
|
||||
}
|
||||
|
||||
|
||||
static void musb_generic_disable(struct musb *musb)
|
||||
{
|
||||
void __iomem *mbase = musb->mregs;
|
||||
@ -1007,6 +953,7 @@ static void musb_shutdown(struct platform_device *pdev)
|
||||
|
||||
pm_runtime_get_sync(musb->controller);
|
||||
|
||||
musb_host_cleanup(musb);
|
||||
musb_gadget_cleanup(musb);
|
||||
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
@ -1763,24 +1710,18 @@ static struct musb *allocate_instance(struct device *dev,
|
||||
struct musb *musb;
|
||||
struct musb_hw_ep *ep;
|
||||
int epnum;
|
||||
struct usb_hcd *hcd;
|
||||
int ret;
|
||||
|
||||
hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev));
|
||||
if (!hcd)
|
||||
musb = devm_kzalloc(dev, sizeof(*musb), GFP_KERNEL);
|
||||
if (!musb)
|
||||
return NULL;
|
||||
/* usbcore sets dev->driver_data to hcd, and sometimes uses that... */
|
||||
|
||||
musb = hcd_to_musb(hcd);
|
||||
INIT_LIST_HEAD(&musb->control);
|
||||
INIT_LIST_HEAD(&musb->in_bulk);
|
||||
INIT_LIST_HEAD(&musb->out_bulk);
|
||||
|
||||
hcd->uses_new_polling = 1;
|
||||
hcd->has_tt = 1;
|
||||
|
||||
musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
|
||||
musb->a_wait_bcon = OTG_TIME_A_WAIT_BCON;
|
||||
dev_set_drvdata(dev, musb);
|
||||
musb->mregs = mbase;
|
||||
musb->ctrl_base = mbase;
|
||||
musb->nIrq = -ENODEV;
|
||||
@ -1795,7 +1736,16 @@ static struct musb *allocate_instance(struct device *dev,
|
||||
|
||||
musb->controller = dev;
|
||||
|
||||
ret = musb_host_alloc(musb);
|
||||
if (ret < 0)
|
||||
goto err_free;
|
||||
|
||||
dev_set_drvdata(dev, musb);
|
||||
|
||||
return musb;
|
||||
|
||||
err_free:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void musb_free(struct musb *musb)
|
||||
@ -1821,7 +1771,7 @@ static void musb_free(struct musb *musb)
|
||||
dma_controller_destroy(c);
|
||||
}
|
||||
|
||||
usb_put_hcd(musb_to_hcd(musb));
|
||||
musb_host_free(musb);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1838,7 +1788,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
|
||||
int status;
|
||||
struct musb *musb;
|
||||
struct musb_hdrc_platform_data *plat = dev->platform_data;
|
||||
struct usb_hcd *hcd;
|
||||
|
||||
/* The driver might handle more features than the board; OK.
|
||||
* Fail when the board needs a feature that's not enabled.
|
||||
@ -1864,6 +1813,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
|
||||
musb->board_set_power = plat->set_power;
|
||||
musb->min_power = plat->min_power;
|
||||
musb->ops = plat->platform_ops;
|
||||
musb->port_mode = plat->mode;
|
||||
|
||||
/* The musb_platform_init() call:
|
||||
* - adjusts musb->mregs
|
||||
@ -1939,13 +1889,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
|
||||
musb->irq_wake = 0;
|
||||
}
|
||||
|
||||
/* host side needs more setup */
|
||||
hcd = musb_to_hcd(musb);
|
||||
otg_set_host(musb->xceiv->otg, &hcd->self);
|
||||
hcd->self.otg_port = 1;
|
||||
musb->xceiv->otg->host = &hcd->self;
|
||||
hcd->power_budget = 2 * (plat->power ? : 250);
|
||||
|
||||
/* program PHY to use external vBus if required */
|
||||
if (plat->extvbus) {
|
||||
u8 busctl = musb_read_ulpi_buscontrol(musb->mregs);
|
||||
@ -1961,7 +1904,23 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
|
||||
musb->xceiv->state = OTG_STATE_B_IDLE;
|
||||
}
|
||||
|
||||
status = musb_gadget_setup(musb);
|
||||
switch (musb->port_mode) {
|
||||
case MUSB_PORT_MODE_HOST:
|
||||
status = musb_host_setup(musb, plat->power);
|
||||
break;
|
||||
case MUSB_PORT_MODE_GADGET:
|
||||
status = musb_gadget_setup(musb);
|
||||
break;
|
||||
case MUSB_PORT_MODE_DUAL_ROLE:
|
||||
status = musb_host_setup(musb, plat->power);
|
||||
if (status < 0)
|
||||
goto fail3;
|
||||
status = musb_gadget_setup(musb);
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "unsupported port mode %d\n", musb->port_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status < 0)
|
||||
goto fail3;
|
||||
|
@ -77,28 +77,17 @@ struct musb_ep;
|
||||
#define is_peripheral_active(m) (!(m)->is_host)
|
||||
#define is_host_active(m) ((m)->is_host)
|
||||
|
||||
enum {
|
||||
MUSB_PORT_MODE_HOST = 1,
|
||||
MUSB_PORT_MODE_GADGET,
|
||||
MUSB_PORT_MODE_DUAL_ROLE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
#include <linux/fs.h>
|
||||
#define MUSB_CONFIG_PROC_FS
|
||||
#endif
|
||||
|
||||
/****************************** PERIPHERAL ROLE *****************************/
|
||||
|
||||
extern irqreturn_t musb_g_ep0_irq(struct musb *);
|
||||
extern void musb_g_tx(struct musb *, u8);
|
||||
extern void musb_g_rx(struct musb *, u8);
|
||||
extern void musb_g_reset(struct musb *);
|
||||
extern void musb_g_suspend(struct musb *);
|
||||
extern void musb_g_resume(struct musb *);
|
||||
extern void musb_g_wakeup(struct musb *);
|
||||
extern void musb_g_disconnect(struct musb *);
|
||||
|
||||
/****************************** HOST ROLE ***********************************/
|
||||
|
||||
extern irqreturn_t musb_h_ep0_irq(struct musb *);
|
||||
extern void musb_host_tx(struct musb *, u8);
|
||||
extern void musb_host_rx(struct musb *, u8);
|
||||
|
||||
/****************************** CONSTANTS ********************************/
|
||||
|
||||
#ifndef MUSB_C_NUM_EPS
|
||||
@ -373,6 +362,7 @@ struct musb {
|
||||
|
||||
u8 min_power; /* vbus for periph, in mA/2 */
|
||||
|
||||
int port_mode; /* MUSB_PORT_MODE_* */
|
||||
bool is_host;
|
||||
|
||||
int a_wait_bcon; /* VBUS timeout in msecs */
|
||||
@ -382,7 +372,6 @@ struct musb {
|
||||
unsigned is_active:1;
|
||||
|
||||
unsigned is_multipoint:1;
|
||||
unsigned ignore_disconnect:1; /* during bus resets */
|
||||
|
||||
unsigned hb_iso_rx:1; /* high bandwidth iso rx? */
|
||||
unsigned hb_iso_tx:1; /* high bandwidth iso tx? */
|
||||
@ -419,6 +408,7 @@ struct musb {
|
||||
enum musb_g_ep0_state ep0_state;
|
||||
struct usb_gadget g; /* the gadget */
|
||||
struct usb_gadget_driver *gadget_driver; /* its driver */
|
||||
struct usb_hcd *hcd; /* the usb hcd */
|
||||
|
||||
/*
|
||||
* FIXME: Remove this flag.
|
||||
@ -520,7 +510,6 @@ static inline void musb_configure_ep0(struct musb *musb)
|
||||
|
||||
extern const char musb_driver_name[];
|
||||
|
||||
extern void musb_start(struct musb *musb);
|
||||
extern void musb_stop(struct musb *musb);
|
||||
|
||||
extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src);
|
||||
|
@ -1820,7 +1820,6 @@ static int musb_gadget_start(struct usb_gadget *g,
|
||||
{
|
||||
struct musb *musb = gadget_to_musb(g);
|
||||
struct usb_otg *otg = musb->xceiv->otg;
|
||||
struct usb_hcd *hcd = musb_to_hcd(musb);
|
||||
unsigned long flags;
|
||||
int retval = 0;
|
||||
|
||||
@ -1847,17 +1846,9 @@ static int musb_gadget_start(struct usb_gadget *g,
|
||||
* handles power budgeting ... this way also
|
||||
* ensures HdrcStart is indirectly called.
|
||||
*/
|
||||
retval = usb_add_hcd(hcd, 0, 0);
|
||||
if (retval < 0) {
|
||||
dev_dbg(musb->controller, "add_hcd failed, %d\n", retval);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (musb->xceiv->last_event == USB_EVENT_ID)
|
||||
musb_platform_set_vbus(musb, 1);
|
||||
|
||||
hcd->self.uses_pio_for_control = 1;
|
||||
|
||||
if (musb->xceiv->last_event == USB_EVENT_NONE)
|
||||
pm_runtime_put(musb->controller);
|
||||
|
||||
@ -1942,7 +1933,6 @@ static int musb_gadget_stop(struct usb_gadget *g,
|
||||
musb_platform_try_idle(musb, 0);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
usb_remove_hcd(musb_to_hcd(musb));
|
||||
/*
|
||||
* FIXME we need to be able to register another
|
||||
* gadget driver here and have everything work;
|
||||
|
@ -37,6 +37,38 @@
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_MUSB_GADGET) || IS_ENABLED(CONFIG_USB_MUSB_DUAL_ROLE)
|
||||
extern irqreturn_t musb_g_ep0_irq(struct musb *);
|
||||
extern void musb_g_tx(struct musb *, u8);
|
||||
extern void musb_g_rx(struct musb *, u8);
|
||||
extern void musb_g_reset(struct musb *);
|
||||
extern void musb_g_suspend(struct musb *);
|
||||
extern void musb_g_resume(struct musb *);
|
||||
extern void musb_g_wakeup(struct musb *);
|
||||
extern void musb_g_disconnect(struct musb *);
|
||||
extern void musb_gadget_cleanup(struct musb *);
|
||||
extern int musb_gadget_setup(struct musb *);
|
||||
|
||||
#else
|
||||
static inline irqreturn_t musb_g_ep0_irq(struct musb *musb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void musb_g_tx(struct musb *musb, u8 epnum) {}
|
||||
static inline void musb_g_rx(struct musb *musb, u8 epnum) {}
|
||||
static inline void musb_g_reset(struct musb *musb) {}
|
||||
static inline void musb_g_suspend(struct musb *musb) {}
|
||||
static inline void musb_g_resume(struct musb *musb) {}
|
||||
static inline void musb_g_wakeup(struct musb *musb) {}
|
||||
static inline void musb_g_disconnect(struct musb *musb) {}
|
||||
static inline void musb_gadget_cleanup(struct musb *musb) {}
|
||||
static inline int musb_gadget_setup(struct musb *musb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
enum buffer_map_state {
|
||||
UN_MAPPED = 0,
|
||||
PRE_MAPPED,
|
||||
@ -106,14 +138,8 @@ static inline struct musb_request *next_request(struct musb_ep *ep)
|
||||
return container_of(queue->next, struct musb_request, list);
|
||||
}
|
||||
|
||||
extern void musb_g_tx(struct musb *musb, u8 epnum);
|
||||
extern void musb_g_rx(struct musb *musb, u8 epnum);
|
||||
|
||||
extern const struct usb_ep_ops musb_g_ep0_ops;
|
||||
|
||||
extern int musb_gadget_setup(struct musb *);
|
||||
extern void musb_gadget_cleanup(struct musb *);
|
||||
|
||||
extern void musb_g_giveback(struct musb_ep *, struct usb_request *, int);
|
||||
|
||||
extern void musb_ep_restart(struct musb *, struct musb_request *);
|
||||
|
@ -46,7 +46,6 @@
|
||||
#include "musb_core.h"
|
||||
#include "musb_host.h"
|
||||
|
||||
|
||||
/* MUSB HOST status 22-mar-2006
|
||||
*
|
||||
* - There's still lots of partial code duplication for fault paths, so
|
||||
@ -96,6 +95,11 @@
|
||||
* of transfers between endpoints, or anything clever.
|
||||
*/
|
||||
|
||||
struct musb *hcd_to_musb(struct usb_hcd *hcd)
|
||||
{
|
||||
return *(struct musb **) hcd->hcd_priv;
|
||||
}
|
||||
|
||||
|
||||
static void musb_ep_program(struct musb *musb, u8 epnum,
|
||||
struct urb *urb, int is_out,
|
||||
@ -310,9 +314,9 @@ __acquires(musb->lock)
|
||||
urb->actual_length, urb->transfer_buffer_length
|
||||
);
|
||||
|
||||
usb_hcd_unlink_urb_from_ep(musb_to_hcd(musb), urb);
|
||||
usb_hcd_unlink_urb_from_ep(musb->hcd, urb);
|
||||
spin_unlock(&musb->lock);
|
||||
usb_hcd_giveback_urb(musb_to_hcd(musb), urb, status);
|
||||
usb_hcd_giveback_urb(musb->hcd, urb, status);
|
||||
spin_lock(&musb->lock);
|
||||
}
|
||||
|
||||
@ -624,7 +628,7 @@ static bool musb_tx_dma_program(struct dma_controller *dma,
|
||||
u16 csr;
|
||||
u8 mode;
|
||||
|
||||
#ifdef CONFIG_USB_INVENTRA_DMA
|
||||
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
|
||||
if (length > channel->max_len)
|
||||
length = channel->max_len;
|
||||
|
||||
@ -1454,7 +1458,7 @@ done:
|
||||
if (length > qh->maxpacket)
|
||||
length = qh->maxpacket;
|
||||
/* Unmap the buffer so that CPU can use it */
|
||||
usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb);
|
||||
usb_hcd_unmap_urb_for_dma(musb->hcd, urb);
|
||||
|
||||
/*
|
||||
* We need to map sg if the transfer_buffer is
|
||||
@ -1656,7 +1660,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
|
||||
/* FIXME this is _way_ too much in-line logic for Mentor DMA */
|
||||
|
||||
#ifndef CONFIG_USB_INVENTRA_DMA
|
||||
#if !defined(CONFIG_USB_INVENTRA_DMA) && !defined(CONFIG_USB_UX500_DMA)
|
||||
if (rx_csr & MUSB_RXCSR_H_REQPKT) {
|
||||
/* REVISIT this happened for a while on some short reads...
|
||||
* the cleanup still needs investigation... looks bad...
|
||||
@ -1688,7 +1692,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
| MUSB_RXCSR_RXPKTRDY);
|
||||
musb_writew(hw_ep->regs, MUSB_RXCSR, val);
|
||||
|
||||
#ifdef CONFIG_USB_INVENTRA_DMA
|
||||
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
|
||||
if (usb_pipeisoc(pipe)) {
|
||||
struct usb_iso_packet_descriptor *d;
|
||||
|
||||
@ -1744,7 +1748,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
}
|
||||
|
||||
/* we are expecting IN packets */
|
||||
#ifdef CONFIG_USB_INVENTRA_DMA
|
||||
#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
|
||||
if (dma) {
|
||||
struct dma_controller *c;
|
||||
u16 rx_count;
|
||||
@ -1753,10 +1757,10 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
|
||||
rx_count = musb_readw(epio, MUSB_RXCOUNT);
|
||||
|
||||
dev_dbg(musb->controller, "RX%d count %d, buffer 0x%x len %d/%d\n",
|
||||
dev_dbg(musb->controller, "RX%d count %d, buffer 0x%llx len %d/%d\n",
|
||||
epnum, rx_count,
|
||||
urb->transfer_dma
|
||||
+ urb->actual_length,
|
||||
(unsigned long long) urb->transfer_dma
|
||||
+ urb->actual_length,
|
||||
qh->offset,
|
||||
urb->transfer_buffer_length);
|
||||
|
||||
@ -1868,7 +1872,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
|
||||
unsigned int received_len;
|
||||
|
||||
/* Unmap the buffer so that CPU can use it */
|
||||
usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb);
|
||||
usb_hcd_unmap_urb_for_dma(musb->hcd, urb);
|
||||
|
||||
/*
|
||||
* We need to map sg if the transfer_buffer is
|
||||
@ -2462,7 +2466,6 @@ static int musb_bus_resume(struct usb_hcd *hcd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifndef CONFIG_MUSB_PIO_ONLY
|
||||
|
||||
#define MUSB_USB_DMA_ALIGN 4
|
||||
@ -2574,10 +2577,10 @@ static void musb_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
|
||||
}
|
||||
#endif /* !CONFIG_MUSB_PIO_ONLY */
|
||||
|
||||
const struct hc_driver musb_hc_driver = {
|
||||
static const struct hc_driver musb_hc_driver = {
|
||||
.description = "musb-hcd",
|
||||
.product_desc = "MUSB HDRC host driver",
|
||||
.hcd_priv_size = sizeof(struct musb),
|
||||
.hcd_priv_size = sizeof(struct musb *),
|
||||
.flags = HCD_USB2 | HCD_MEMORY,
|
||||
|
||||
/* not using irq handler or reset hooks from usbcore, since
|
||||
@ -2605,3 +2608,66 @@ const struct hc_driver musb_hc_driver = {
|
||||
/* .start_port_reset = NULL, */
|
||||
/* .hub_irq_enable = NULL, */
|
||||
};
|
||||
|
||||
int musb_host_alloc(struct musb *musb)
|
||||
{
|
||||
struct device *dev = musb->controller;
|
||||
|
||||
/* usbcore sets dev->driver_data to hcd, and sometimes uses that... */
|
||||
musb->hcd = usb_create_hcd(&musb_hc_driver, dev, dev_name(dev));
|
||||
if (!musb->hcd)
|
||||
return -EINVAL;
|
||||
|
||||
*musb->hcd->hcd_priv = (unsigned long) musb;
|
||||
musb->hcd->self.uses_pio_for_control = 1;
|
||||
musb->hcd->uses_new_polling = 1;
|
||||
musb->hcd->has_tt = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void musb_host_cleanup(struct musb *musb)
|
||||
{
|
||||
usb_remove_hcd(musb->hcd);
|
||||
musb->hcd = NULL;
|
||||
}
|
||||
|
||||
void musb_host_free(struct musb *musb)
|
||||
{
|
||||
usb_put_hcd(musb->hcd);
|
||||
}
|
||||
|
||||
int musb_host_setup(struct musb *musb, int power_budget)
|
||||
{
|
||||
int ret;
|
||||
struct usb_hcd *hcd = musb->hcd;
|
||||
|
||||
MUSB_HST_MODE(musb);
|
||||
musb->xceiv->otg->default_a = 1;
|
||||
musb->xceiv->state = OTG_STATE_A_IDLE;
|
||||
|
||||
otg_set_host(musb->xceiv->otg, &hcd->self);
|
||||
hcd->self.otg_port = 1;
|
||||
musb->xceiv->otg->host = &hcd->self;
|
||||
hcd->power_budget = 2 * (power_budget ? : 250);
|
||||
|
||||
ret = usb_add_hcd(hcd, 0, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void musb_host_resume_root_hub(struct musb *musb)
|
||||
{
|
||||
usb_hcd_resume_root_hub(musb->hcd);
|
||||
}
|
||||
|
||||
void musb_host_poke_root_hub(struct musb *musb)
|
||||
{
|
||||
MUSB_HST_MODE(musb);
|
||||
if (musb->hcd->status_urb)
|
||||
usb_hcd_poll_rh_status(musb->hcd);
|
||||
else
|
||||
usb_hcd_resume_root_hub(musb->hcd);
|
||||
}
|
||||
|
@ -37,16 +37,6 @@
|
||||
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
static inline struct usb_hcd *musb_to_hcd(struct musb *musb)
|
||||
{
|
||||
return container_of((void *) musb, struct usb_hcd, hcd_priv);
|
||||
}
|
||||
|
||||
static inline struct musb *hcd_to_musb(struct usb_hcd *hcd)
|
||||
{
|
||||
return (struct musb *) (hcd->hcd_priv);
|
||||
}
|
||||
|
||||
/* stored in "usb_host_endpoint.hcpriv" for scheduled endpoints */
|
||||
struct musb_qh {
|
||||
struct usb_host_endpoint *hep; /* usbcore info */
|
||||
@ -86,7 +76,52 @@ static inline struct musb_qh *first_qh(struct list_head *q)
|
||||
}
|
||||
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_MUSB_HOST) || IS_ENABLED(CONFIG_USB_MUSB_DUAL_ROLE)
|
||||
extern struct musb *hcd_to_musb(struct usb_hcd *);
|
||||
extern irqreturn_t musb_h_ep0_irq(struct musb *);
|
||||
extern int musb_host_alloc(struct musb *);
|
||||
extern int musb_host_setup(struct musb *, int);
|
||||
extern void musb_host_cleanup(struct musb *);
|
||||
extern void musb_host_tx(struct musb *, u8);
|
||||
extern void musb_host_rx(struct musb *, u8);
|
||||
extern void musb_root_disconnect(struct musb *musb);
|
||||
extern void musb_host_free(struct musb *);
|
||||
extern void musb_host_cleanup(struct musb *);
|
||||
extern void musb_host_tx(struct musb *, u8);
|
||||
extern void musb_host_rx(struct musb *, u8);
|
||||
extern void musb_root_disconnect(struct musb *musb);
|
||||
extern void musb_host_resume_root_hub(struct musb *musb);
|
||||
extern void musb_host_poke_root_hub(struct musb *musb);
|
||||
#else
|
||||
static inline struct musb *hcd_to_musb(struct usb_hcd *hcd)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline irqreturn_t musb_h_ep0_irq(struct musb *musb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int musb_host_alloc(struct musb *musb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int musb_host_setup(struct musb *musb, int power_budget)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void musb_host_cleanup(struct musb *musb) {}
|
||||
static inline void musb_host_free(struct musb *musb) {}
|
||||
static inline void musb_host_tx(struct musb *musb, u8 epnum) {}
|
||||
static inline void musb_host_rx(struct musb *musb, u8 epnum) {}
|
||||
static inline void musb_root_disconnect(struct musb *musb) {}
|
||||
static inline void musb_host_resume_root_hub(struct musb *musb) {}
|
||||
static inline void musb_host_poll_rh_status(struct musb *musb) {}
|
||||
static inline void musb_host_poke_root_hub(struct musb *musb) {}
|
||||
#endif
|
||||
|
||||
struct usb_hcd;
|
||||
|
||||
@ -95,8 +130,6 @@ extern int musb_hub_control(struct usb_hcd *hcd,
|
||||
u16 typeReq, u16 wValue, u16 wIndex,
|
||||
char *buf, u16 wLength);
|
||||
|
||||
extern const struct hc_driver musb_hc_driver;
|
||||
|
||||
static inline struct urb *next_urb(struct musb_qh *qh)
|
||||
{
|
||||
struct list_head *queue;
|
||||
|
@ -44,6 +44,51 @@
|
||||
|
||||
#include "musb_core.h"
|
||||
|
||||
/*
|
||||
* Program the HDRC to start (enable interrupts, dma, etc.).
|
||||
*/
|
||||
static void musb_start(struct musb *musb)
|
||||
{
|
||||
void __iomem *regs = musb->mregs;
|
||||
u8 devctl = musb_readb(regs, MUSB_DEVCTL);
|
||||
|
||||
dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
|
||||
|
||||
/* Set INT enable registers, enable interrupts */
|
||||
musb->intrtxe = musb->epmask;
|
||||
musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
|
||||
musb->intrrxe = musb->epmask & 0xfffe;
|
||||
musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
|
||||
musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
|
||||
|
||||
musb_writeb(regs, MUSB_TESTMODE, 0);
|
||||
|
||||
/* put into basic highspeed mode and start session */
|
||||
musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE
|
||||
| MUSB_POWER_HSENAB
|
||||
/* ENSUSPEND wedges tusb */
|
||||
/* | MUSB_POWER_ENSUSPEND */
|
||||
);
|
||||
|
||||
musb->is_active = 0;
|
||||
devctl = musb_readb(regs, MUSB_DEVCTL);
|
||||
devctl &= ~MUSB_DEVCTL_SESSION;
|
||||
|
||||
/* session started after:
|
||||
* (a) ID-grounded irq, host mode;
|
||||
* (b) vbus present/connect IRQ, peripheral mode;
|
||||
* (c) peripheral initiates, using SRP
|
||||
*/
|
||||
if (musb->port_mode != MUSB_PORT_MODE_HOST &&
|
||||
(devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) {
|
||||
musb->is_active = 1;
|
||||
} else {
|
||||
devctl |= MUSB_DEVCTL_SESSION;
|
||||
}
|
||||
|
||||
musb_platform_enable(musb);
|
||||
musb_writeb(regs, MUSB_DEVCTL, devctl);
|
||||
}
|
||||
|
||||
static void musb_port_suspend(struct musb *musb, bool do_suspend)
|
||||
{
|
||||
@ -145,7 +190,6 @@ static void musb_port_reset(struct musb *musb, bool do_reset)
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
musb->ignore_disconnect = true;
|
||||
power &= 0xf0;
|
||||
musb_writeb(mbase, MUSB_POWER,
|
||||
power | MUSB_POWER_RESET);
|
||||
@ -158,8 +202,6 @@ static void musb_port_reset(struct musb *musb, bool do_reset)
|
||||
musb_writeb(mbase, MUSB_POWER,
|
||||
power & ~MUSB_POWER_RESET);
|
||||
|
||||
musb->ignore_disconnect = false;
|
||||
|
||||
power = musb_readb(mbase, MUSB_POWER);
|
||||
if (power & MUSB_POWER_HSMODE) {
|
||||
dev_dbg(musb->controller, "high-speed device connected\n");
|
||||
@ -170,7 +212,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset)
|
||||
musb->port1_status |= USB_PORT_STAT_ENABLE
|
||||
| (USB_PORT_STAT_C_RESET << 16)
|
||||
| (USB_PORT_STAT_C_ENABLE << 16);
|
||||
usb_hcd_poll_rh_status(musb_to_hcd(musb));
|
||||
usb_hcd_poll_rh_status(musb->hcd);
|
||||
|
||||
musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
|
||||
}
|
||||
@ -183,7 +225,7 @@ void musb_root_disconnect(struct musb *musb)
|
||||
musb->port1_status = USB_PORT_STAT_POWER
|
||||
| (USB_PORT_STAT_C_CONNECTION << 16);
|
||||
|
||||
usb_hcd_poll_rh_status(musb_to_hcd(musb));
|
||||
usb_hcd_poll_rh_status(musb->hcd);
|
||||
musb->is_active = 0;
|
||||
|
||||
switch (musb->xceiv->state) {
|
||||
@ -337,7 +379,7 @@ int musb_hub_control(
|
||||
musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
|
||||
| MUSB_PORT_STAT_RESUME);
|
||||
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
|
||||
usb_hcd_poll_rh_status(musb_to_hcd(musb));
|
||||
usb_hcd_poll_rh_status(musb->hcd);
|
||||
/* NOTE: it might really be A_WAIT_BCON ... */
|
||||
musb->xceiv->state = OTG_STATE_A_HOST;
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ static void musb_do_idle(unsigned long _musb)
|
||||
musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
|
||||
| MUSB_PORT_STAT_RESUME);
|
||||
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
|
||||
usb_hcd_poll_rh_status(musb_to_hcd(musb));
|
||||
usb_hcd_poll_rh_status(musb->hcd);
|
||||
/* NOTE: it might really be A_WAIT_BCON ... */
|
||||
musb->xceiv->state = OTG_STATE_A_HOST;
|
||||
}
|
||||
@ -481,6 +481,7 @@ static u64 omap2430_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int omap2430_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource musb_resouces[2];
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct omap_musb_board_data *data;
|
||||
struct platform_device *musb;
|
||||
@ -567,8 +568,21 @@ static int omap2430_probe(struct platform_device *pdev)
|
||||
|
||||
INIT_WORK(&glue->omap_musb_mailbox_work, omap_musb_mailbox_work);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
memset(musb_resouces, 0x00, sizeof(*musb_resources) *
|
||||
ARRAY_SIZE(musb_resources));
|
||||
|
||||
musb_resources[0].name = pdev->resource[0].name;
|
||||
musb_resources[0].start = pdev->resource[0].start;
|
||||
musb_resources[0].end = pdev->resource[0].end;
|
||||
musb_resources[0].flags = pdev->resource[0].flags;
|
||||
|
||||
musb_resources[1].name = pdev->resource[1].name;
|
||||
musb_resources[1].start = pdev->resource[1].start;
|
||||
musb_resources[1].end = pdev->resource[1].end;
|
||||
musb_resources[1].flags = pdev->resource[1].flags;
|
||||
|
||||
ret = platform_device_add_resources(musb, musb_resources,
|
||||
ARRAY_SIZE(musb_resources));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err2;
|
||||
|
@ -1156,6 +1156,7 @@ static u64 tusb_dmamask = DMA_BIT_MASK(32);
|
||||
|
||||
static int tusb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource musb_resources[2];
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct tusb6010_glue *glue;
|
||||
@ -1185,8 +1186,21 @@ static int tusb_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
memset(musb_resources, 0x00, sizeof(*musb_resources) *
|
||||
ARRAY_SIZE(musb_resources));
|
||||
|
||||
musb_resources[0].name = pdev->resource[0].name;
|
||||
musb_resources[0].start = pdev->resource[0].start;
|
||||
musb_resources[0].end = pdev->resource[0].end;
|
||||
musb_resources[0].flags = pdev->resource[0].flags;
|
||||
|
||||
musb_resources[1].name = pdev->resource[1].name;
|
||||
musb_resources[1].start = pdev->resource[1].start;
|
||||
musb_resources[1].end = pdev->resource[1].end;
|
||||
musb_resources[1].flags = pdev->resource[1].flags;
|
||||
|
||||
ret = platform_device_add_resources(musb, musb_resources,
|
||||
ARRAY_SIZE(musb_resources));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err3;
|
||||
|
@ -189,6 +189,7 @@ static const struct musb_platform_ops ux500_ops = {
|
||||
|
||||
static int ux500_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource musb_resources[2];
|
||||
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct platform_device *musb;
|
||||
struct ux500_glue *glue;
|
||||
@ -232,8 +233,21 @@ static int ux500_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, glue);
|
||||
|
||||
ret = platform_device_add_resources(musb, pdev->resource,
|
||||
pdev->num_resources);
|
||||
memset(musb_resources, 0x00, sizeof(*musb_resources) *
|
||||
ARRAY_SIZE(musb_resources));
|
||||
|
||||
musb_resources[0].name = pdev->resource[0].name;
|
||||
musb_resources[0].start = pdev->resource[0].start;
|
||||
musb_resources[0].end = pdev->resource[0].end;
|
||||
musb_resources[0].flags = pdev->resource[0].flags;
|
||||
|
||||
musb_resources[1].name = pdev->resource[1].name;
|
||||
musb_resources[1].start = pdev->resource[1].start;
|
||||
musb_resources[1].end = pdev->resource[1].end;
|
||||
musb_resources[1].flags = pdev->resource[1].flags;
|
||||
|
||||
ret = platform_device_add_resources(musb, musb_resources,
|
||||
ARRAY_SIZE(musb_resources));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add resources\n");
|
||||
goto err5;
|
||||
|
@ -71,8 +71,7 @@ static void ux500_dma_callback(void *private_data)
|
||||
spin_lock_irqsave(&musb->lock, flags);
|
||||
ux500_channel->channel.actual_len = ux500_channel->cur_len;
|
||||
ux500_channel->channel.status = MUSB_DMA_STATUS_FREE;
|
||||
musb_dma_completion(musb, hw_ep->epnum,
|
||||
ux500_channel->is_tx);
|
||||
musb_dma_completion(musb, hw_ep->epnum, ux500_channel->is_tx);
|
||||
spin_unlock_irqrestore(&musb->lock, flags);
|
||||
|
||||
}
|
||||
@ -366,7 +365,8 @@ void dma_controller_destroy(struct dma_controller *c)
|
||||
kfree(controller);
|
||||
}
|
||||
|
||||
struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *base)
|
||||
struct dma_controller *dma_controller_create(struct musb *musb,
|
||||
void __iomem *base)
|
||||
{
|
||||
struct ux500_dma_controller *controller;
|
||||
struct platform_device *pdev = to_platform_device(musb->controller);
|
||||
|
@ -86,7 +86,7 @@ config OMAP_USB3
|
||||
on/off the PHY.
|
||||
|
||||
config SAMSUNG_USBPHY
|
||||
tristate "Samsung USB PHY Driver"
|
||||
tristate
|
||||
help
|
||||
Enable this to support Samsung USB phy helper driver for Samsung SoCs.
|
||||
This driver provides common interface to interact, for Samsung USB 2.0 PHY
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/usb/omap_control_usb.h>
|
||||
|
||||
#define NUM_SYS_CLKS 5
|
||||
#define NUM_SYS_CLKS 6
|
||||
#define PLL_STATUS 0x00000004
|
||||
#define PLL_GO 0x00000008
|
||||
#define PLL_CONFIGURATION1 0x0000000C
|
||||
@ -62,6 +62,7 @@ enum sys_clk_rate {
|
||||
CLK_RATE_12MHZ,
|
||||
CLK_RATE_16MHZ,
|
||||
CLK_RATE_19MHZ,
|
||||
CLK_RATE_20MHZ,
|
||||
CLK_RATE_26MHZ,
|
||||
CLK_RATE_38MHZ
|
||||
};
|
||||
@ -72,6 +73,8 @@ static struct usb_dpll_params omap_usb3_dpll_params[NUM_SYS_CLKS] = {
|
||||
{1172, 8, 4, 20, 65537}, /* 19.2 MHz */
|
||||
{1250, 12, 4, 20, 0}, /* 26 MHz */
|
||||
{3125, 47, 4, 20, 92843}, /* 38.4 MHz */
|
||||
{1000, 7, 4, 10, 0}, /* 20 MHz */
|
||||
|
||||
};
|
||||
|
||||
static int omap_usb3_suspend(struct usb_phy *x, int suspend)
|
||||
@ -122,6 +125,8 @@ static inline enum sys_clk_rate __get_sys_clk_index(unsigned long rate)
|
||||
return CLK_RATE_16MHZ;
|
||||
case 19200000:
|
||||
return CLK_RATE_19MHZ;
|
||||
case 20000000:
|
||||
return CLK_RATE_20MHZ;
|
||||
case 26000000:
|
||||
return CLK_RATE_26MHZ;
|
||||
case 38400000:
|
||||
|
@ -73,7 +73,7 @@ EXPORT_SYMBOL_GPL(samsung_usbphy_parse_dt);
|
||||
* Here 'on = true' would mean USB PHY block is isolated, hence
|
||||
* de-activated and vice-versa.
|
||||
*/
|
||||
void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on)
|
||||
void samsung_usbphy_set_isolation_4210(struct samsung_usbphy *sphy, bool on)
|
||||
{
|
||||
void __iomem *reg = NULL;
|
||||
u32 reg_val;
|
||||
@ -84,32 +84,12 @@ void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on)
|
||||
return;
|
||||
}
|
||||
|
||||
switch (sphy->drv_data->cpu_type) {
|
||||
case TYPE_S3C64XX:
|
||||
/*
|
||||
* Do nothing: We will add here once S3C64xx goes for DT support
|
||||
*/
|
||||
break;
|
||||
case TYPE_EXYNOS4210:
|
||||
/*
|
||||
* Fall through since exynos4210 and exynos5250 have similar
|
||||
* register architecture: two separate registers for host and
|
||||
* device phy control with enable bit at position 0.
|
||||
*/
|
||||
case TYPE_EXYNOS5250:
|
||||
if (sphy->phy_type == USB_PHY_TYPE_DEVICE) {
|
||||
reg = sphy->pmuregs +
|
||||
sphy->drv_data->devphy_reg_offset;
|
||||
en_mask = sphy->drv_data->devphy_en_mask;
|
||||
} else if (sphy->phy_type == USB_PHY_TYPE_HOST) {
|
||||
reg = sphy->pmuregs +
|
||||
sphy->drv_data->hostphy_reg_offset;
|
||||
en_mask = sphy->drv_data->hostphy_en_mask;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(sphy->dev, "Invalid SoC type\n");
|
||||
return;
|
||||
if (sphy->phy_type == USB_PHY_TYPE_DEVICE) {
|
||||
reg = sphy->pmuregs + sphy->drv_data->devphy_reg_offset;
|
||||
en_mask = sphy->drv_data->devphy_en_mask;
|
||||
} else if (sphy->phy_type == USB_PHY_TYPE_HOST) {
|
||||
reg = sphy->pmuregs + sphy->drv_data->hostphy_reg_offset;
|
||||
en_mask = sphy->drv_data->hostphy_en_mask;
|
||||
}
|
||||
|
||||
reg_val = readl(reg);
|
||||
@ -120,8 +100,13 @@ void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on)
|
||||
reg_val |= en_mask;
|
||||
|
||||
writel(reg_val, reg);
|
||||
|
||||
if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12) {
|
||||
writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL0);
|
||||
writel(reg_val, sphy->pmuregs + EXYNOS4X12_PHY_HSIC_CTRL1);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation);
|
||||
EXPORT_SYMBOL_GPL(samsung_usbphy_set_isolation_4210);
|
||||
|
||||
/*
|
||||
* Configure the mode of working of usb-phy here: HOST/DEVICE.
|
||||
@ -162,73 +147,93 @@ int samsung_usbphy_set_type(struct usb_phy *phy,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(samsung_usbphy_set_type);
|
||||
|
||||
int samsung_usbphy_rate_to_clksel_64xx(struct samsung_usbphy *sphy,
|
||||
unsigned long rate)
|
||||
{
|
||||
unsigned int clksel;
|
||||
|
||||
switch (rate) {
|
||||
case 12 * MHZ:
|
||||
clksel = PHYCLK_CLKSEL_12M;
|
||||
break;
|
||||
case 24 * MHZ:
|
||||
clksel = PHYCLK_CLKSEL_24M;
|
||||
break;
|
||||
case 48 * MHZ:
|
||||
clksel = PHYCLK_CLKSEL_48M;
|
||||
break;
|
||||
default:
|
||||
dev_err(sphy->dev,
|
||||
"Invalid reference clock frequency: %lu\n", rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return clksel;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_64xx);
|
||||
|
||||
int samsung_usbphy_rate_to_clksel_4x12(struct samsung_usbphy *sphy,
|
||||
unsigned long rate)
|
||||
{
|
||||
unsigned int clksel;
|
||||
|
||||
switch (rate) {
|
||||
case 9600 * KHZ:
|
||||
clksel = FSEL_CLKSEL_9600K;
|
||||
break;
|
||||
case 10 * MHZ:
|
||||
clksel = FSEL_CLKSEL_10M;
|
||||
break;
|
||||
case 12 * MHZ:
|
||||
clksel = FSEL_CLKSEL_12M;
|
||||
break;
|
||||
case 19200 * KHZ:
|
||||
clksel = FSEL_CLKSEL_19200K;
|
||||
break;
|
||||
case 20 * MHZ:
|
||||
clksel = FSEL_CLKSEL_20M;
|
||||
break;
|
||||
case 24 * MHZ:
|
||||
clksel = FSEL_CLKSEL_24M;
|
||||
break;
|
||||
case 50 * MHZ:
|
||||
clksel = FSEL_CLKSEL_50M;
|
||||
break;
|
||||
default:
|
||||
dev_err(sphy->dev,
|
||||
"Invalid reference clock frequency: %lu\n", rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return clksel;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(samsung_usbphy_rate_to_clksel_4x12);
|
||||
|
||||
/*
|
||||
* Returns reference clock frequency selection value
|
||||
*/
|
||||
int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy)
|
||||
{
|
||||
struct clk *ref_clk;
|
||||
int refclk_freq = 0;
|
||||
unsigned long rate;
|
||||
int refclk_freq;
|
||||
|
||||
/*
|
||||
* In exynos5250 USB host and device PHY use
|
||||
* external crystal clock XXTI
|
||||
*/
|
||||
if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250)
|
||||
ref_clk = devm_clk_get(sphy->dev, "ext_xtal");
|
||||
ref_clk = clk_get(sphy->dev, "ext_xtal");
|
||||
else
|
||||
ref_clk = devm_clk_get(sphy->dev, "xusbxti");
|
||||
ref_clk = clk_get(sphy->dev, "xusbxti");
|
||||
if (IS_ERR(ref_clk)) {
|
||||
dev_err(sphy->dev, "Failed to get reference clock\n");
|
||||
return PTR_ERR(ref_clk);
|
||||
}
|
||||
|
||||
if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250) {
|
||||
/* set clock frequency for PLL */
|
||||
switch (clk_get_rate(ref_clk)) {
|
||||
case 9600 * KHZ:
|
||||
refclk_freq = FSEL_CLKSEL_9600K;
|
||||
break;
|
||||
case 10 * MHZ:
|
||||
refclk_freq = FSEL_CLKSEL_10M;
|
||||
break;
|
||||
case 12 * MHZ:
|
||||
refclk_freq = FSEL_CLKSEL_12M;
|
||||
break;
|
||||
case 19200 * KHZ:
|
||||
refclk_freq = FSEL_CLKSEL_19200K;
|
||||
break;
|
||||
case 20 * MHZ:
|
||||
refclk_freq = FSEL_CLKSEL_20M;
|
||||
break;
|
||||
case 50 * MHZ:
|
||||
refclk_freq = FSEL_CLKSEL_50M;
|
||||
break;
|
||||
case 24 * MHZ:
|
||||
default:
|
||||
/* default reference clock */
|
||||
refclk_freq = FSEL_CLKSEL_24M;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (clk_get_rate(ref_clk)) {
|
||||
case 12 * MHZ:
|
||||
refclk_freq = PHYCLK_CLKSEL_12M;
|
||||
break;
|
||||
case 24 * MHZ:
|
||||
refclk_freq = PHYCLK_CLKSEL_24M;
|
||||
break;
|
||||
case 48 * MHZ:
|
||||
refclk_freq = PHYCLK_CLKSEL_48M;
|
||||
break;
|
||||
default:
|
||||
if (sphy->drv_data->cpu_type == TYPE_S3C64XX)
|
||||
refclk_freq = PHYCLK_CLKSEL_48M;
|
||||
else
|
||||
refclk_freq = PHYCLK_CLKSEL_24M;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rate = clk_get_rate(ref_clk);
|
||||
refclk_freq = sphy->drv_data->rate_to_clksel(sphy, rate);
|
||||
|
||||
clk_put(ref_clk);
|
||||
|
||||
return refclk_freq;
|
||||
|
@ -47,6 +47,16 @@
|
||||
#define RSTCON_HLINK_SWRST (0x1 << 1)
|
||||
#define RSTCON_SWRST (0x1 << 0)
|
||||
|
||||
/* EXYNOS4X12 */
|
||||
#define EXYNOS4X12_PHY_HSIC_CTRL0 (0x04)
|
||||
#define EXYNOS4X12_PHY_HSIC_CTRL1 (0x08)
|
||||
|
||||
#define PHYPWR_NORMAL_MASK_HSIC1 (0x7 << 12)
|
||||
#define PHYPWR_NORMAL_MASK_HSIC0 (0x7 << 9)
|
||||
#define PHYPWR_NORMAL_MASK_PHY1 (0x7 << 6)
|
||||
|
||||
#define RSTCON_HOSTPHY_SWRST (0xf << 3)
|
||||
|
||||
/* EXYNOS5 */
|
||||
#define EXYNOS5_PHY_HOST_CTRL0 (0x00)
|
||||
|
||||
@ -241,9 +251,12 @@
|
||||
enum samsung_cpu_type {
|
||||
TYPE_S3C64XX,
|
||||
TYPE_EXYNOS4210,
|
||||
TYPE_EXYNOS4X12,
|
||||
TYPE_EXYNOS5250,
|
||||
};
|
||||
|
||||
struct samsung_usbphy;
|
||||
|
||||
/*
|
||||
* struct samsung_usbphy_drvdata - driver data for various SoC variants
|
||||
* @cpu_type: machine identifier
|
||||
@ -268,6 +281,10 @@ struct samsung_usbphy_drvdata {
|
||||
int hostphy_en_mask;
|
||||
u32 devphy_reg_offset;
|
||||
u32 hostphy_reg_offset;
|
||||
int (*rate_to_clksel)(struct samsung_usbphy *, unsigned long);
|
||||
void (*set_isolation)(struct samsung_usbphy *, bool);
|
||||
void (*phy_enable)(struct samsung_usbphy *);
|
||||
void (*phy_disable)(struct samsung_usbphy *);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -320,8 +337,13 @@ static inline const struct samsung_usbphy_drvdata
|
||||
}
|
||||
|
||||
extern int samsung_usbphy_parse_dt(struct samsung_usbphy *sphy);
|
||||
extern void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on);
|
||||
extern void samsung_usbphy_set_isolation_4210(struct samsung_usbphy *sphy,
|
||||
bool on);
|
||||
extern void samsung_usbphy_cfg_sel(struct samsung_usbphy *sphy);
|
||||
extern int samsung_usbphy_set_type(struct usb_phy *phy,
|
||||
enum samsung_usb_phy_type phy_type);
|
||||
extern int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy);
|
||||
extern int samsung_usbphy_rate_to_clksel_64xx(struct samsung_usbphy *sphy,
|
||||
unsigned long rate);
|
||||
extern int samsung_usbphy_rate_to_clksel_4x12(struct samsung_usbphy *sphy,
|
||||
unsigned long rate);
|
||||
|
@ -176,6 +176,11 @@ static void samsung_usb2phy_enable(struct samsung_usbphy *sphy)
|
||||
phypwr &= ~PHYPWR_NORMAL_MASK;
|
||||
rstcon |= RSTCON_SWRST;
|
||||
break;
|
||||
case TYPE_EXYNOS4X12:
|
||||
phypwr &= ~(PHYPWR_NORMAL_MASK_HSIC0 |
|
||||
PHYPWR_NORMAL_MASK_HSIC1 |
|
||||
PHYPWR_NORMAL_MASK_PHY1);
|
||||
rstcon |= RSTCON_HOSTPHY_SWRST;
|
||||
case TYPE_EXYNOS4210:
|
||||
phypwr &= ~PHYPWR_NORMAL_MASK_PHY0;
|
||||
rstcon |= RSTCON_SWRST;
|
||||
@ -189,6 +194,8 @@ static void samsung_usb2phy_enable(struct samsung_usbphy *sphy)
|
||||
/* reset all ports of PHY and Link */
|
||||
writel(rstcon, regs + SAMSUNG_RSTCON);
|
||||
udelay(10);
|
||||
if (sphy->drv_data->cpu_type == TYPE_EXYNOS4X12)
|
||||
rstcon &= ~RSTCON_HOSTPHY_SWRST;
|
||||
rstcon &= ~RSTCON_SWRST;
|
||||
writel(rstcon, regs + SAMSUNG_RSTCON);
|
||||
}
|
||||
@ -239,6 +246,10 @@ static void samsung_usb2phy_disable(struct samsung_usbphy *sphy)
|
||||
case TYPE_S3C64XX:
|
||||
phypwr |= PHYPWR_NORMAL_MASK;
|
||||
break;
|
||||
case TYPE_EXYNOS4X12:
|
||||
phypwr |= (PHYPWR_NORMAL_MASK_HSIC0 |
|
||||
PHYPWR_NORMAL_MASK_HSIC1 |
|
||||
PHYPWR_NORMAL_MASK_PHY1);
|
||||
case TYPE_EXYNOS4210:
|
||||
phypwr |= PHYPWR_NORMAL_MASK_PHY0;
|
||||
default:
|
||||
@ -284,17 +295,14 @@ static int samsung_usb2phy_init(struct usb_phy *phy)
|
||||
/* Disable phy isolation */
|
||||
if (sphy->plat && sphy->plat->pmu_isolation)
|
||||
sphy->plat->pmu_isolation(false);
|
||||
else
|
||||
samsung_usbphy_set_isolation(sphy, false);
|
||||
else if (sphy->drv_data->set_isolation)
|
||||
sphy->drv_data->set_isolation(sphy, false);
|
||||
|
||||
/* Selecting Host/OTG mode; After reset USB2.0PHY_CFG: HOST */
|
||||
samsung_usbphy_cfg_sel(sphy);
|
||||
|
||||
/* Initialize usb phy registers */
|
||||
if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250)
|
||||
samsung_exynos5_usb2phy_enable(sphy);
|
||||
else
|
||||
samsung_usb2phy_enable(sphy);
|
||||
sphy->drv_data->phy_enable(sphy);
|
||||
|
||||
spin_unlock_irqrestore(&sphy->lock, flags);
|
||||
|
||||
@ -334,16 +342,13 @@ static void samsung_usb2phy_shutdown(struct usb_phy *phy)
|
||||
}
|
||||
|
||||
/* De-initialize usb phy registers */
|
||||
if (sphy->drv_data->cpu_type == TYPE_EXYNOS5250)
|
||||
samsung_exynos5_usb2phy_disable(sphy);
|
||||
else
|
||||
samsung_usb2phy_disable(sphy);
|
||||
sphy->drv_data->phy_disable(sphy);
|
||||
|
||||
/* Enable phy isolation */
|
||||
if (sphy->plat && sphy->plat->pmu_isolation)
|
||||
sphy->plat->pmu_isolation(true);
|
||||
else
|
||||
samsung_usbphy_set_isolation(sphy, true);
|
||||
else if (sphy->drv_data->set_isolation)
|
||||
sphy->drv_data->set_isolation(sphy, true);
|
||||
|
||||
spin_unlock_irqrestore(&sphy->lock, flags);
|
||||
|
||||
@ -408,7 +413,10 @@ static int samsung_usb2phy_probe(struct platform_device *pdev)
|
||||
sphy->phy.label = "samsung-usb2phy";
|
||||
sphy->phy.init = samsung_usb2phy_init;
|
||||
sphy->phy.shutdown = samsung_usb2phy_shutdown;
|
||||
sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy);
|
||||
|
||||
sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy);
|
||||
if (sphy->ref_clk_freq < 0)
|
||||
return -EINVAL;
|
||||
|
||||
sphy->phy.otg = otg;
|
||||
sphy->phy.otg->phy = &sphy->phy;
|
||||
@ -438,18 +446,40 @@ static int samsung_usb2phy_remove(struct platform_device *pdev)
|
||||
static const struct samsung_usbphy_drvdata usb2phy_s3c64xx = {
|
||||
.cpu_type = TYPE_S3C64XX,
|
||||
.devphy_en_mask = S3C64XX_USBPHY_ENABLE,
|
||||
.rate_to_clksel = samsung_usbphy_rate_to_clksel_64xx,
|
||||
.set_isolation = NULL, /* TODO */
|
||||
.phy_enable = samsung_usb2phy_enable,
|
||||
.phy_disable = samsung_usb2phy_disable,
|
||||
};
|
||||
|
||||
static const struct samsung_usbphy_drvdata usb2phy_exynos4 = {
|
||||
.cpu_type = TYPE_EXYNOS4210,
|
||||
.devphy_en_mask = EXYNOS_USBPHY_ENABLE,
|
||||
.hostphy_en_mask = EXYNOS_USBPHY_ENABLE,
|
||||
.rate_to_clksel = samsung_usbphy_rate_to_clksel_64xx,
|
||||
.set_isolation = samsung_usbphy_set_isolation_4210,
|
||||
.phy_enable = samsung_usb2phy_enable,
|
||||
.phy_disable = samsung_usb2phy_disable,
|
||||
};
|
||||
|
||||
static const struct samsung_usbphy_drvdata usb2phy_exynos4x12 = {
|
||||
.cpu_type = TYPE_EXYNOS4X12,
|
||||
.devphy_en_mask = EXYNOS_USBPHY_ENABLE,
|
||||
.hostphy_en_mask = EXYNOS_USBPHY_ENABLE,
|
||||
.rate_to_clksel = samsung_usbphy_rate_to_clksel_4x12,
|
||||
.set_isolation = samsung_usbphy_set_isolation_4210,
|
||||
.phy_enable = samsung_usb2phy_enable,
|
||||
.phy_disable = samsung_usb2phy_disable,
|
||||
};
|
||||
|
||||
static struct samsung_usbphy_drvdata usb2phy_exynos5 = {
|
||||
.cpu_type = TYPE_EXYNOS5250,
|
||||
.hostphy_en_mask = EXYNOS_USBPHY_ENABLE,
|
||||
.hostphy_reg_offset = EXYNOS_USBHOST_PHY_CTRL_OFFSET,
|
||||
.rate_to_clksel = samsung_usbphy_rate_to_clksel_4x12,
|
||||
.set_isolation = samsung_usbphy_set_isolation_4210,
|
||||
.phy_enable = samsung_exynos5_usb2phy_enable,
|
||||
.phy_disable = samsung_exynos5_usb2phy_disable,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
@ -460,6 +490,9 @@ static const struct of_device_id samsung_usbphy_dt_match[] = {
|
||||
}, {
|
||||
.compatible = "samsung,exynos4210-usb2phy",
|
||||
.data = &usb2phy_exynos4,
|
||||
}, {
|
||||
.compatible = "samsung,exynos4x12-usb2phy",
|
||||
.data = &usb2phy_exynos4x12,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5250-usb2phy",
|
||||
.data = &usb2phy_exynos5
|
||||
@ -476,6 +509,9 @@ static struct platform_device_id samsung_usbphy_driver_ids[] = {
|
||||
}, {
|
||||
.name = "exynos4210-usb2phy",
|
||||
.driver_data = (unsigned long)&usb2phy_exynos4,
|
||||
}, {
|
||||
.name = "exynos4x12-usb2phy",
|
||||
.driver_data = (unsigned long)&usb2phy_exynos4x12,
|
||||
}, {
|
||||
.name = "exynos5250-usb2phy",
|
||||
.driver_data = (unsigned long)&usb2phy_exynos5,
|
||||
|
@ -65,7 +65,7 @@ static u32 samsung_usb3phy_set_refclk(struct samsung_usbphy *sphy)
|
||||
return reg;
|
||||
}
|
||||
|
||||
static int samsung_exynos5_usb3phy_enable(struct samsung_usbphy *sphy)
|
||||
static void samsung_exynos5_usb3phy_enable(struct samsung_usbphy *sphy)
|
||||
{
|
||||
void __iomem *regs = sphy->regs;
|
||||
u32 phyparam0;
|
||||
@ -133,8 +133,6 @@ static int samsung_exynos5_usb3phy_enable(struct samsung_usbphy *sphy)
|
||||
|
||||
phyclkrst &= ~(PHYCLKRST_PORTRESET);
|
||||
writel(phyclkrst, regs + EXYNOS5_DRD_PHYCLKRST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void samsung_exynos5_usb3phy_disable(struct samsung_usbphy *sphy)
|
||||
@ -184,10 +182,11 @@ static int samsung_usb3phy_init(struct usb_phy *phy)
|
||||
samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
|
||||
|
||||
/* Disable phy isolation */
|
||||
samsung_usbphy_set_isolation(sphy, false);
|
||||
if (sphy->drv_data->set_isolation)
|
||||
sphy->drv_data->set_isolation(sphy, false);
|
||||
|
||||
/* Initialize usb phy registers */
|
||||
samsung_exynos5_usb3phy_enable(sphy);
|
||||
sphy->drv_data->phy_enable(sphy);
|
||||
|
||||
spin_unlock_irqrestore(&sphy->lock, flags);
|
||||
|
||||
@ -218,10 +217,11 @@ static void samsung_usb3phy_shutdown(struct usb_phy *phy)
|
||||
samsung_usbphy_set_type(&sphy->phy, USB_PHY_TYPE_DEVICE);
|
||||
|
||||
/* De-initialize usb phy registers */
|
||||
samsung_exynos5_usb3phy_disable(sphy);
|
||||
sphy->drv_data->phy_disable(sphy);
|
||||
|
||||
/* Enable phy isolation */
|
||||
samsung_usbphy_set_isolation(sphy, true);
|
||||
if (sphy->drv_data->set_isolation)
|
||||
sphy->drv_data->set_isolation(sphy, true);
|
||||
|
||||
spin_unlock_irqrestore(&sphy->lock, flags);
|
||||
|
||||
@ -274,7 +274,10 @@ static int samsung_usb3phy_probe(struct platform_device *pdev)
|
||||
sphy->phy.init = samsung_usb3phy_init;
|
||||
sphy->phy.shutdown = samsung_usb3phy_shutdown;
|
||||
sphy->drv_data = samsung_usbphy_get_driver_data(pdev);
|
||||
sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy);
|
||||
|
||||
sphy->ref_clk_freq = samsung_usbphy_get_refclk_freq(sphy);
|
||||
if (sphy->ref_clk_freq < 0)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_init(&sphy->lock);
|
||||
|
||||
@ -300,6 +303,10 @@ static int samsung_usb3phy_remove(struct platform_device *pdev)
|
||||
static struct samsung_usbphy_drvdata usb3phy_exynos5 = {
|
||||
.cpu_type = TYPE_EXYNOS5250,
|
||||
.devphy_en_mask = EXYNOS_USBPHY_ENABLE,
|
||||
.rate_to_clksel = samsung_usbphy_rate_to_clksel_4x12,
|
||||
.set_isolation = samsung_usbphy_set_isolation_4210,
|
||||
.phy_enable = samsung_exynos5_usb3phy_enable,
|
||||
.phy_disable = samsung_exynos5_usb3phy_disable,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
@ -1,9 +1,11 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Google, Inc.
|
||||
* Copyright (C) 2013 NVIDIA Corporation
|
||||
*
|
||||
* Author:
|
||||
* Erik Gilling <konkers@google.com>
|
||||
* Benoit Goby <benoit@android.com>
|
||||
* Venu Byravarasu <vbyravarasu@nvidia.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
@ -30,9 +32,7 @@
|
||||
#include <linux/usb/ulpi.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <linux/usb/tegra_usb_phy.h>
|
||||
|
||||
#define TEGRA_USB_BASE 0xC5000000
|
||||
#define TEGRA_USB_SIZE SZ_16K
|
||||
#include <linux/module.h>
|
||||
|
||||
#define ULPI_VIEWPORT 0x170
|
||||
|
||||
@ -198,32 +198,15 @@ static struct tegra_utmip_config utmip_default[] = {
|
||||
|
||||
static int utmip_pad_open(struct tegra_usb_phy *phy)
|
||||
{
|
||||
phy->pad_clk = clk_get_sys("utmip-pad", NULL);
|
||||
phy->pad_clk = devm_clk_get(phy->dev, "utmi-pads");
|
||||
if (IS_ERR(phy->pad_clk)) {
|
||||
pr_err("%s: can't get utmip pad clock\n", __func__);
|
||||
return PTR_ERR(phy->pad_clk);
|
||||
}
|
||||
|
||||
if (phy->is_legacy_phy) {
|
||||
phy->pad_regs = phy->regs;
|
||||
} else {
|
||||
phy->pad_regs = ioremap(TEGRA_USB_BASE, TEGRA_USB_SIZE);
|
||||
if (!phy->pad_regs) {
|
||||
pr_err("%s: can't remap usb registers\n", __func__);
|
||||
clk_put(phy->pad_clk);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void utmip_pad_close(struct tegra_usb_phy *phy)
|
||||
{
|
||||
if (!phy->is_legacy_phy)
|
||||
iounmap(phy->pad_regs);
|
||||
clk_put(phy->pad_clk);
|
||||
}
|
||||
|
||||
static void utmip_pad_power_on(struct tegra_usb_phy *phy)
|
||||
{
|
||||
unsigned long val, flags;
|
||||
@ -299,7 +282,7 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
|
||||
val &= ~USB_SUSP_SET;
|
||||
writel(val, base + USB_SUSP_CTRL);
|
||||
} else
|
||||
phy->set_phcd(&phy->u_phy, true);
|
||||
tegra_ehci_set_phcd(&phy->u_phy, true);
|
||||
|
||||
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0)
|
||||
pr_err("%s: timeout waiting for phy to stabilize\n", __func__);
|
||||
@ -321,7 +304,7 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
|
||||
val &= ~USB_SUSP_CLR;
|
||||
writel(val, base + USB_SUSP_CTRL);
|
||||
} else
|
||||
phy->set_phcd(&phy->u_phy, false);
|
||||
tegra_ehci_set_phcd(&phy->u_phy, false);
|
||||
|
||||
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
|
||||
USB_PHY_CLK_VALID))
|
||||
@ -444,7 +427,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
|
||||
utmi_phy_clk_enable(phy);
|
||||
|
||||
if (!phy->is_legacy_phy)
|
||||
phy->set_pts(&phy->u_phy, 0);
|
||||
tegra_ehci_set_pts(&phy->u_phy, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -541,11 +524,18 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
|
||||
int ret;
|
||||
unsigned long val;
|
||||
void __iomem *base = phy->regs;
|
||||
struct tegra_ulpi_config *config = phy->config;
|
||||
|
||||
gpio_direction_output(config->reset_gpio, 0);
|
||||
ret = gpio_direction_output(phy->reset_gpio, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(phy->dev, "gpio %d not set to 0\n", phy->reset_gpio);
|
||||
return ret;
|
||||
}
|
||||
msleep(5);
|
||||
gpio_direction_output(config->reset_gpio, 1);
|
||||
ret = gpio_direction_output(phy->reset_gpio, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(phy->dev, "gpio %d not set to 1\n", phy->reset_gpio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
clk_prepare_enable(phy->clk);
|
||||
msleep(1);
|
||||
@ -603,63 +593,15 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
|
||||
|
||||
static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
|
||||
{
|
||||
struct tegra_ulpi_config *config = phy->config;
|
||||
|
||||
clk_disable(phy->clk);
|
||||
return gpio_direction_output(config->reset_gpio, 0);
|
||||
}
|
||||
|
||||
static int tegra_phy_init(struct usb_phy *x)
|
||||
{
|
||||
struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
|
||||
struct tegra_ulpi_config *ulpi_config;
|
||||
int err;
|
||||
|
||||
if (phy->is_ulpi_phy) {
|
||||
ulpi_config = phy->config;
|
||||
phy->clk = clk_get_sys(NULL, ulpi_config->clk);
|
||||
if (IS_ERR(phy->clk)) {
|
||||
pr_err("%s: can't get ulpi clock\n", __func__);
|
||||
err = -ENXIO;
|
||||
goto err1;
|
||||
}
|
||||
if (!gpio_is_valid(ulpi_config->reset_gpio))
|
||||
ulpi_config->reset_gpio =
|
||||
of_get_named_gpio(phy->dev->of_node,
|
||||
"nvidia,phy-reset-gpio", 0);
|
||||
if (!gpio_is_valid(ulpi_config->reset_gpio)) {
|
||||
pr_err("%s: invalid reset gpio: %d\n", __func__,
|
||||
ulpi_config->reset_gpio);
|
||||
err = -EINVAL;
|
||||
goto err1;
|
||||
}
|
||||
gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b");
|
||||
gpio_direction_output(ulpi_config->reset_gpio, 0);
|
||||
phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
|
||||
phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT;
|
||||
} else {
|
||||
err = utmip_pad_open(phy);
|
||||
if (err < 0)
|
||||
goto err1;
|
||||
}
|
||||
return 0;
|
||||
err1:
|
||||
clk_disable_unprepare(phy->pll_u);
|
||||
clk_put(phy->pll_u);
|
||||
return err;
|
||||
return gpio_direction_output(phy->reset_gpio, 0);
|
||||
}
|
||||
|
||||
static void tegra_usb_phy_close(struct usb_phy *x)
|
||||
{
|
||||
struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
|
||||
|
||||
if (phy->is_ulpi_phy)
|
||||
clk_put(phy->clk);
|
||||
else
|
||||
utmip_pad_close(phy);
|
||||
clk_disable_unprepare(phy->pll_u);
|
||||
clk_put(phy->pll_u);
|
||||
kfree(phy);
|
||||
}
|
||||
|
||||
static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
|
||||
@ -687,54 +629,63 @@ static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend)
|
||||
return tegra_usb_phy_power_on(phy);
|
||||
}
|
||||
|
||||
struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
|
||||
void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode,
|
||||
void (*set_pts)(struct usb_phy *x, u8 pts_val),
|
||||
void (*set_phcd)(struct usb_phy *x, bool enable))
|
||||
|
||||
static int ulpi_open(struct tegra_usb_phy *phy)
|
||||
{
|
||||
int err;
|
||||
|
||||
phy->clk = devm_clk_get(phy->dev, "ulpi-link");
|
||||
if (IS_ERR(phy->clk)) {
|
||||
pr_err("%s: can't get ulpi clock\n", __func__);
|
||||
return PTR_ERR(phy->clk);
|
||||
}
|
||||
|
||||
err = devm_gpio_request(phy->dev, phy->reset_gpio, "ulpi_phy_reset_b");
|
||||
if (err < 0) {
|
||||
dev_err(phy->dev, "request failed for gpio: %d\n",
|
||||
phy->reset_gpio);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = gpio_direction_output(phy->reset_gpio, 0);
|
||||
if (err < 0) {
|
||||
dev_err(phy->dev, "gpio %d direction not set to output\n",
|
||||
phy->reset_gpio);
|
||||
return err;
|
||||
}
|
||||
|
||||
phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
|
||||
if (!phy->ulpi) {
|
||||
dev_err(phy->dev, "otg_ulpi_create returned NULL\n");
|
||||
err = -ENOMEM;
|
||||
return err;
|
||||
}
|
||||
|
||||
phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_usb_phy_init(struct tegra_usb_phy *phy)
|
||||
{
|
||||
struct tegra_usb_phy *phy;
|
||||
unsigned long parent_rate;
|
||||
int i;
|
||||
int err;
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
phy = kzalloc(sizeof(struct tegra_usb_phy), GFP_KERNEL);
|
||||
if (!phy)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
phy->instance = instance;
|
||||
phy->regs = regs;
|
||||
phy->config = config;
|
||||
phy->mode = phy_mode;
|
||||
phy->dev = dev;
|
||||
phy->is_legacy_phy =
|
||||
of_property_read_bool(np, "nvidia,has-legacy-mode");
|
||||
phy->set_pts = set_pts;
|
||||
phy->set_phcd = set_phcd;
|
||||
err = of_property_match_string(np, "phy_type", "ulpi");
|
||||
if (err < 0)
|
||||
phy->is_ulpi_phy = false;
|
||||
else
|
||||
phy->is_ulpi_phy = true;
|
||||
|
||||
if (!phy->config) {
|
||||
if (phy->is_ulpi_phy) {
|
||||
pr_err("%s: ulpi phy configuration missing", __func__);
|
||||
err = -EINVAL;
|
||||
goto err0;
|
||||
} else {
|
||||
phy->config = &utmip_default[instance];
|
||||
}
|
||||
if (!phy->is_ulpi_phy) {
|
||||
if (phy->is_legacy_phy)
|
||||
phy->config = &utmip_default[0];
|
||||
else
|
||||
phy->config = &utmip_default[2];
|
||||
}
|
||||
|
||||
phy->pll_u = clk_get_sys(NULL, "pll_u");
|
||||
phy->pll_u = devm_clk_get(phy->dev, "pll_u");
|
||||
if (IS_ERR(phy->pll_u)) {
|
||||
pr_err("Can't get pll_u clock\n");
|
||||
err = PTR_ERR(phy->pll_u);
|
||||
goto err0;
|
||||
return PTR_ERR(phy->pll_u);
|
||||
}
|
||||
clk_prepare_enable(phy->pll_u);
|
||||
|
||||
err = clk_prepare_enable(phy->pll_u);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
parent_rate = clk_get_rate(clk_get_parent(phy->pll_u));
|
||||
for (i = 0; i < ARRAY_SIZE(tegra_freq_table); i++) {
|
||||
@ -746,23 +697,22 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
|
||||
if (!phy->freq) {
|
||||
pr_err("invalid pll_u parent rate %ld\n", parent_rate);
|
||||
err = -EINVAL;
|
||||
goto err1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
phy->u_phy.init = tegra_phy_init;
|
||||
phy->u_phy.shutdown = tegra_usb_phy_close;
|
||||
phy->u_phy.set_suspend = tegra_usb_phy_suspend;
|
||||
if (phy->is_ulpi_phy)
|
||||
err = ulpi_open(phy);
|
||||
else
|
||||
err = utmip_pad_open(phy);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
return phy;
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
fail:
|
||||
clk_disable_unprepare(phy->pll_u);
|
||||
clk_put(phy->pll_u);
|
||||
err0:
|
||||
kfree(phy);
|
||||
return ERR_PTR(err);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_usb_phy_open);
|
||||
|
||||
void tegra_usb_phy_preresume(struct usb_phy *x)
|
||||
{
|
||||
@ -801,3 +751,121 @@ void tegra_ehci_phy_restore_end(struct usb_phy *x)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_ehci_phy_restore_end);
|
||||
|
||||
static int tegra_usb_phy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct tegra_usb_phy *tegra_phy = NULL;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int err;
|
||||
|
||||
tegra_phy = devm_kzalloc(&pdev->dev, sizeof(*tegra_phy), GFP_KERNEL);
|
||||
if (!tegra_phy) {
|
||||
dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get I/O memory\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
tegra_phy->regs = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!tegra_phy->regs) {
|
||||
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tegra_phy->is_legacy_phy =
|
||||
of_property_read_bool(np, "nvidia,has-legacy-mode");
|
||||
|
||||
err = of_property_match_string(np, "phy_type", "ulpi");
|
||||
if (err < 0) {
|
||||
tegra_phy->is_ulpi_phy = false;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get UTMI Pad regs\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
tegra_phy->pad_regs = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!tegra_phy->regs) {
|
||||
dev_err(&pdev->dev, "Failed to remap UTMI Pad regs\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else {
|
||||
tegra_phy->is_ulpi_phy = true;
|
||||
|
||||
tegra_phy->reset_gpio =
|
||||
of_get_named_gpio(np, "nvidia,phy-reset-gpio", 0);
|
||||
if (!gpio_is_valid(tegra_phy->reset_gpio)) {
|
||||
dev_err(&pdev->dev, "invalid gpio: %d\n",
|
||||
tegra_phy->reset_gpio);
|
||||
return tegra_phy->reset_gpio;
|
||||
}
|
||||
}
|
||||
|
||||
err = of_property_match_string(np, "dr_mode", "otg");
|
||||
if (err < 0) {
|
||||
err = of_property_match_string(np, "dr_mode", "peripheral");
|
||||
if (err < 0)
|
||||
tegra_phy->mode = TEGRA_USB_PHY_MODE_HOST;
|
||||
else
|
||||
tegra_phy->mode = TEGRA_USB_PHY_MODE_DEVICE;
|
||||
} else
|
||||
tegra_phy->mode = TEGRA_USB_PHY_MODE_OTG;
|
||||
|
||||
tegra_phy->dev = &pdev->dev;
|
||||
err = tegra_usb_phy_init(tegra_phy);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
tegra_phy->u_phy.shutdown = tegra_usb_phy_close;
|
||||
tegra_phy->u_phy.set_suspend = tegra_usb_phy_suspend;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, tegra_phy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id tegra_usb_phy_id_table[] = {
|
||||
{ .compatible = "nvidia,tegra20-usb-phy", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_usb_phy_id_table);
|
||||
|
||||
static struct platform_driver tegra_usb_phy_driver = {
|
||||
.probe = tegra_usb_phy_probe,
|
||||
.driver = {
|
||||
.name = "tegra-phy",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(tegra_usb_phy_id_table),
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra_usb_phy_driver);
|
||||
|
||||
static int tegra_usb_phy_match(struct device *dev, void *data)
|
||||
{
|
||||
struct tegra_usb_phy *tegra_phy = dev_get_drvdata(dev);
|
||||
struct device_node *dn = data;
|
||||
|
||||
return (tegra_phy->dev->of_node == dn) ? 1 : 0;
|
||||
}
|
||||
|
||||
struct usb_phy *tegra_usb_get_phy(struct device_node *dn)
|
||||
{
|
||||
struct device *dev;
|
||||
struct tegra_usb_phy *tegra_phy;
|
||||
|
||||
dev = driver_find_device(&tegra_usb_phy_driver.driver, NULL, dn,
|
||||
tegra_usb_phy_match);
|
||||
if (!dev)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
|
||||
tegra_phy = dev_get_drvdata(dev);
|
||||
|
||||
return &tegra_phy->u_phy;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tegra_usb_get_phy);
|
||||
|
@ -42,6 +42,7 @@ enum tegra_usb_phy_port_speed {
|
||||
enum tegra_usb_phy_mode {
|
||||
TEGRA_USB_PHY_MODE_DEVICE,
|
||||
TEGRA_USB_PHY_MODE_HOST,
|
||||
TEGRA_USB_PHY_MODE_OTG,
|
||||
};
|
||||
|
||||
struct tegra_xtal_freq;
|
||||
@ -61,14 +62,10 @@ struct tegra_usb_phy {
|
||||
struct device *dev;
|
||||
bool is_legacy_phy;
|
||||
bool is_ulpi_phy;
|
||||
void (*set_pts)(struct usb_phy *x, u8 pts_val);
|
||||
void (*set_phcd)(struct usb_phy *x, bool enable);
|
||||
int reset_gpio;
|
||||
};
|
||||
|
||||
struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
|
||||
void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode,
|
||||
void (*set_pts)(struct usb_phy *x, u8 pts_val),
|
||||
void (*set_phcd)(struct usb_phy *x, bool enable));
|
||||
struct usb_phy *tegra_usb_get_phy(struct device_node *dn);
|
||||
|
||||
void tegra_usb_phy_preresume(struct usb_phy *phy);
|
||||
|
||||
@ -79,4 +76,8 @@ void tegra_ehci_phy_restore_start(struct usb_phy *phy,
|
||||
|
||||
void tegra_ehci_phy_restore_end(struct usb_phy *phy);
|
||||
|
||||
void tegra_ehci_set_pts(struct usb_phy *x, u8 pts_val);
|
||||
|
||||
void tegra_ehci_set_phcd(struct usb_phy *x, bool enable);
|
||||
|
||||
#endif /* __TEGRA_USB_PHY_H */
|
||||
|
Loading…
Reference in New Issue
Block a user