From d400f209b4afe4a196baac276128eccac6a11b31 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 16 May 2013 19:42:55 +0530 Subject: [PATCH 01/95] ARM: tegra: finalize USB EHCI and PHY bindings The existing Tegra USB bindings have a few issues: 1) Many properties are documented as being part of the EHCI controller node, yet they apply more to the PHY device. They should be moved. 2) Some registers in PHY1 are shared with PHY3, and hence PHY3 needs a reg entry to point at PHY1's register space. We can't assume the PHY1 driver is present, so the PHY3 driver will directly access those registers. 3) The list of clocks required by the PHY was missing some required entries. 4) UTMI PHY Timing parameters are added 5) VBUS control is now specified using a regulator rather than a plain GPIO 6) Added nvidia,is-wired property to indicate whether the device is hard wired on the board, or pluggable. This patch fixes the binding definition to resolve these issues. Signed-off-by: Venu Byravarasu Signed-off-by: Stephen Warren --- .../bindings/usb/nvidia,tegra20-ehci.txt | 27 +++--------- .../bindings/usb/nvidia,tegra20-usb-phy.txt | 41 +++++++++++++++++-- 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/nvidia,tegra20-ehci.txt b/Documentation/devicetree/bindings/usb/nvidia,tegra20-ehci.txt index 34c952883276c3..df0933043a5be4 100644 --- a/Documentation/devicetree/bindings/usb/nvidia,tegra20-ehci.txt +++ b/Documentation/devicetree/bindings/usb/nvidia,tegra20-ehci.txt @@ -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. diff --git a/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt b/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt index 6bdaba2f0aa19a..c4c9e9e664aac3 100644 --- a/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt +++ b/Documentation/devicetree/bindings/usb/nvidia,tegra20-usb-phy.txt @@ -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. \ No newline at end of file + 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 From 9dffe3be3f321581c4510f2fa2e217b18c703bcd Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 16 May 2013 19:42:56 +0530 Subject: [PATCH 02/95] ARM: tegra: modify ULPI reset GPIO properties 1. All Tegra20 ULPI reset GPIO DT properties are modified to indicate active low nature of the GPIO. 2. Placed USB PHY DT node immediately below the EHCI controller DT nodes and corrected reg value in the name of USB PHY DT node. Signed-off-by: Venu Byravarasu Signed-off-by: Stephen Warren --- arch/arm/boot/dts/tegra20-colibri-512.dtsi | 6 +++++- arch/arm/boot/dts/tegra20-harmony.dts | 10 +++++----- arch/arm/boot/dts/tegra20-paz00.dts | 10 +++++----- arch/arm/boot/dts/tegra20-seaboard.dts | 10 +++++----- arch/arm/boot/dts/tegra20-trimslice.dts | 10 +++++----- arch/arm/boot/dts/tegra20-ventana.dts | 10 +++++----- 6 files changed, 30 insertions(+), 26 deletions(-) diff --git a/arch/arm/boot/dts/tegra20-colibri-512.dtsi b/arch/arm/boot/dts/tegra20-colibri-512.dtsi index a573b94b7c93ea..c12af78e479cf3 100644 --- a/arch/arm/boot/dts/tegra20-colibri-512.dtsi +++ b/arch/arm/boot/dts/tegra20-colibri-512.dtsi @@ -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 { diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts index e7d5de4e00b99e..e84f3f6a539258 100644 --- a/arch/arm/boot/dts/tegra20-harmony.dts +++ b/arch/arm/boot/dts/tegra20-harmony.dts @@ -430,15 +430,15 @@ 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@c5008000 { - status = "okay"; + usb-phy@c5004000 { + nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */ }; - usb-phy@c5004400 { - nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */ + usb@c5008000 { + status = "okay"; }; sdhci@c8000200 { diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts index e3e0c9977df451..e9ac2a9913074f 100644 --- a/arch/arm/boot/dts/tegra20-paz00.dts +++ b/arch/arm/boot/dts/tegra20-paz00.dts @@ -429,15 +429,15 @@ 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@c5008000 { - status = "okay"; + usb-phy@c5004000 { + nvidia,phy-reset-gpio = <&gpio 168 1>; /* gpio PV0, active low */ }; - usb-phy@c5004400 { - nvidia,phy-reset-gpio = <&gpio 168 0>; /* gpio PV0 */ + usb@c5008000 { + status = "okay"; }; sdhci@c8000000 { diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts index cee4c34010fed6..9dd4f8ee4f4a0d 100644 --- a/arch/arm/boot/dts/tegra20-seaboard.dts +++ b/arch/arm/boot/dts/tegra20-seaboard.dts @@ -571,15 +571,15 @@ 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@c5008000 { - status = "okay"; + usb-phy@c5004000 { + nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */ }; - usb-phy@c5004400 { - nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */ + usb@c5008000 { + status = "okay"; }; sdhci@c8000000 { diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts index 9cc78a15d73986..fd7afd6b830626 100644 --- a/arch/arm/boot/dts/tegra20-trimslice.dts +++ b/arch/arm/boot/dts/tegra20-trimslice.dts @@ -316,15 +316,15 @@ 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@c5008000 { - status = "okay"; + usb-phy@c5004000 { + nvidia,phy-reset-gpio = <&gpio 168 1>; /* gpio PV0, active low */ }; - usb-phy@c5004400 { - nvidia,phy-reset-gpio = <&gpio 168 0>; /* gpio PV0 */ + usb@c5008000 { + status = "okay"; }; sdhci@c8000000 { diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts index dd38f1f038347e..a75a0e78523f05 100644 --- a/arch/arm/boot/dts/tegra20-ventana.dts +++ b/arch/arm/boot/dts/tegra20-ventana.dts @@ -507,15 +507,15 @@ 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@c5008000 { - status = "okay"; + usb-phy@c5004000 { + nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */ }; - usb-phy@c5004400 { - nvidia,phy-reset-gpio = <&gpio 169 0>; /* gpio PV1 */ + usb@c5008000 { + status = "okay"; }; sdhci@c8000000 { From 4c94c8b5b3cc38b0d0b1ec79642f5710e19a3e01 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 16 May 2013 19:42:57 +0530 Subject: [PATCH 03/95] ARM: tegra: update device trees for USB binding rework This patch updates all Tegra board files so that they contain all the properties required by the updated USB DT binding. Note that this patch only adds the new properties and does not yet remove the old properties, in order to maintain bisectability. The old properties will be removed once the driver has been updated to assume the new bindings. Signed-off-by: Venu Byravarasu [swarren: fixed some newly added regulator-name properties to better match schematic, avoided duplicate regulator-name on Whistler.] Signed-off-by: Stephen Warren --- arch/arm/boot/dts/tegra20-harmony.dts | 9 +++++ arch/arm/boot/dts/tegra20-iris-512.dts | 9 ++++- arch/arm/boot/dts/tegra20-paz00.dts | 9 +++++ arch/arm/boot/dts/tegra20-seaboard.dts | 20 ++++++++++ arch/arm/boot/dts/tegra20-tamonten.dtsi | 4 ++ arch/arm/boot/dts/tegra20-trimslice.dts | 19 ++++++++++ arch/arm/boot/dts/tegra20-ventana.dts | 9 +++++ arch/arm/boot/dts/tegra20-whistler.dts | 28 ++++++++++++++ arch/arm/boot/dts/tegra20.dtsi | 49 +++++++++++++++++++------ 9 files changed, 143 insertions(+), 13 deletions(-) diff --git a/arch/arm/boot/dts/tegra20-harmony.dts b/arch/arm/boot/dts/tegra20-harmony.dts index e84f3f6a539258..ec5293758753a8 100644 --- a/arch/arm/boot/dts/tegra20-harmony.dts +++ b/arch/arm/boot/dts/tegra20-harmony.dts @@ -428,12 +428,17 @@ status = "okay"; }; + usb-phy@c5000000 { + status = "okay"; + }; + usb@c5004000 { status = "okay"; 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 */ }; @@ -441,6 +446,10 @@ status = "okay"; }; + usb-phy@c5008000 { + status = "okay"; + }; + sdhci@c8000200 { status = "okay"; cd-gpios = <&gpio 69 1>; /* gpio PI5 */ diff --git a/arch/arm/boot/dts/tegra20-iris-512.dts b/arch/arm/boot/dts/tegra20-iris-512.dts index 52f1103907d786..9f64f708688197 100644 --- a/arch/arm/boot/dts/tegra20-iris-512.dts +++ b/arch/arm/boot/dts/tegra20-iris-512.dts @@ -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"; }; diff --git a/arch/arm/boot/dts/tegra20-paz00.dts b/arch/arm/boot/dts/tegra20-paz00.dts index e9ac2a9913074f..1c17ffaff1ad8a 100644 --- a/arch/arm/boot/dts/tegra20-paz00.dts +++ b/arch/arm/boot/dts/tegra20-paz00.dts @@ -427,12 +427,17 @@ status = "okay"; }; + usb-phy@c5000000 { + status = "okay"; + }; + usb@c5004000 { status = "okay"; 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 */ }; @@ -440,6 +445,10 @@ status = "okay"; }; + usb-phy@c5008000 { + status = "okay"; + }; + sdhci@c8000000 { status = "okay"; cd-gpios = <&gpio 173 1>; /* gpio PV5 */ diff --git a/arch/arm/boot/dts/tegra20-seaboard.dts b/arch/arm/boot/dts/tegra20-seaboard.dts index 9dd4f8ee4f4a0d..009dafecf88b90 100644 --- a/arch/arm/boot/dts/tegra20-seaboard.dts +++ b/arch/arm/boot/dts/tegra20-seaboard.dts @@ -569,12 +569,19 @@ 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 1>; /* gpio PV1, active low */ }; usb-phy@c5004000 { + status = "okay"; nvidia,phy-reset-gpio = <&gpio 169 1>; /* gpio PV1, active low */ }; @@ -582,6 +589,10 @@ status = "okay"; }; + usb-phy@c5008000 { + status = "okay"; + }; + sdhci@c8000000 { status = "okay"; power-gpios = <&gpio 86 0>; /* gpio PK6 */ @@ -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 { diff --git a/arch/arm/boot/dts/tegra20-tamonten.dtsi b/arch/arm/boot/dts/tegra20-tamonten.dtsi index 50b3ec16b93aa2..fc2f7d6e70b23f 100644 --- a/arch/arm/boot/dts/tegra20-tamonten.dtsi +++ b/arch/arm/boot/dts/tegra20-tamonten.dtsi @@ -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 */ diff --git a/arch/arm/boot/dts/tegra20-trimslice.dts b/arch/arm/boot/dts/tegra20-trimslice.dts index fd7afd6b830626..0e65c00ec73241 100644 --- a/arch/arm/boot/dts/tegra20-trimslice.dts +++ b/arch/arm/boot/dts/tegra20-trimslice.dts @@ -314,12 +314,18 @@ 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 1>; /* gpio PV0, active low */ }; usb-phy@c5004000 { + status = "okay"; nvidia,phy-reset-gpio = <&gpio 168 1>; /* gpio PV0, active low */ }; @@ -327,6 +333,10 @@ status = "okay"; }; + usb-phy@c5008000 { + status = "okay"; + }; + sdhci@c8000000 { status = "okay"; bus-width = <4>; @@ -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 { diff --git a/arch/arm/boot/dts/tegra20-ventana.dts b/arch/arm/boot/dts/tegra20-ventana.dts index a75a0e78523f05..e00f89e645f911 100644 --- a/arch/arm/boot/dts/tegra20-ventana.dts +++ b/arch/arm/boot/dts/tegra20-ventana.dts @@ -505,12 +505,17 @@ status = "okay"; }; + usb-phy@c5000000 { + status = "okay"; + }; + usb@c5004000 { status = "okay"; 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 */ }; @@ -518,6 +523,10 @@ status = "okay"; }; + usb-phy@c5008000 { + status = "okay"; + }; + sdhci@c8000000 { status = "okay"; power-gpios = <&gpio 86 0>; /* gpio PK6 */ diff --git a/arch/arm/boot/dts/tegra20-whistler.dts b/arch/arm/boot/dts/tegra20-whistler.dts index d2567f83aaffd1..3c24c9b92b440f 100644 --- a/arch/arm/boot/dts/tegra20-whistler.dts +++ b/arch/arm/boot/dts/tegra20-whistler.dts @@ -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 { diff --git a/arch/arm/boot/dts/tegra20.dtsi b/arch/arm/boot/dts/tegra20.dtsi index 56a91106041b31..96d6d8a3aa7208 100644 --- a/arch/arm/boot/dts/tegra20.dtsi +++ b/arch/arm/boot/dts/tegra20.dtsi @@ -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 { From 09fc7d22b024692b2fe8a943b246de1af307132b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 24 Apr 2013 17:21:42 +0300 Subject: [PATCH 04/95] usb: musb: fix incorrect usage of resource pointer We can't simply pass the resource pointer from our device down to our children, otherwise module reinsertion will not work as the resource will continue to be marked as busy. Fix it by building a proper struct resource for our child musb device. Tested-by: Dmitry Lifshitz Signed-off-by: Felipe Balbi --- drivers/usb/musb/blackfin.c | 18 ++++++++++++++++-- drivers/usb/musb/da8xx.c | 18 ++++++++++++++++-- drivers/usb/musb/davinci.c | 18 ++++++++++++++++-- drivers/usb/musb/omap2430.c | 18 ++++++++++++++++-- drivers/usb/musb/tusb6010.c | 18 ++++++++++++++++-- drivers/usb/musb/ux500.c | 18 ++++++++++++++++-- 6 files changed, 96 insertions(+), 12 deletions(-) diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 5e63b160db0c10..6ba8439bd5a6e0 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -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; diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index b903b744a2249d..0da6f648a9fe13 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -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; diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index bea6cc35471c5f..f8aeaf2e2cd1f8 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -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; diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 628b93fe5cccf5..b3e77613e0af23 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -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; diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 7369ba33c94f1e..2c06a8969a9f79 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -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; diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 2c80004e0a8302..028ff4d07dc74a 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -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; From 6d349671174c5da77835dd1b82e874508167f57b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 29 Apr 2013 12:02:24 +0300 Subject: [PATCH 05/95] usb: musb: remove 'ignore_disconnect' flag This was related to an old bug on early versions of TUSB6010 which we don't support anymore. It's known to cause issues on several other situations with more recent devices so we better remove this flag now and come up with a better workaround should one be deemed necessary. Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 10 +--------- drivers/usb/musb/musb_core.h | 1 - drivers/usb/musb/musb_virthub.c | 3 --- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 37a261a6bb6aa1..fc4d97e2bc3dc7 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -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); } @@ -727,7 +726,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, b_host: musb->xceiv->state = OTG_STATE_B_HOST; hcd->self.is_b_host = 1; - musb->ignore_disconnect = 0; del_timer(&musb->otg_timer); break; default: @@ -750,7 +748,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, 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); @@ -818,11 +816,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, 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 +827,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, + 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; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 7fb4819a6f115f..8013a5f6d3d2ff 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -382,7 +382,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? */ diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index ef7d11045f561c..17c6c0da77db69 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -145,7 +145,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 +157,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"); From b7e2e75a8c6062afe1dd88b0b299938e5d36dff8 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 10 Apr 2013 21:55:40 +0200 Subject: [PATCH 06/95] usb: gadget: drop unused USB_GADGET_MUSB_HDRC The functionality meant to be represented by this symbol will be re-added later, but for now, USB_GADGET_MUSB_HDRC is in fact unused and can be dropped. Signed-off-by: Daniel Mack Acked-by: Peter Korsgaard Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f41aa0d0c41431..f9bf7283e538de 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -334,14 +334,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 From c2a2759d33787e49b751a446d63dd6af3abe3aeb Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 10 Apr 2013 21:55:41 +0200 Subject: [PATCH 07/95] usb: musb: move function declarations to musb_{host,gadget}.h Let the function declarations live in the header files they belong to, which makes it easier to stub them out later. Signed-off-by: Daniel Mack Acked-by: Peter Korsgaard Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.h | 17 ----------------- drivers/usb/musb/musb_gadget.h | 17 +++++++++++------ drivers/usb/musb/musb_host.h | 4 +++- 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 8013a5f6d3d2ff..0193dc9f435107 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -82,23 +82,6 @@ struct musb_ep; #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 diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h index 66b7c5e0fb4454..75f821cc3d98af 100644 --- a/drivers/usb/musb/musb_gadget.h +++ b/drivers/usb/musb/musb_gadget.h @@ -37,6 +37,17 @@ #include +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 *); + enum buffer_map_state { UN_MAPPED = 0, PRE_MAPPED, @@ -106,14 +117,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 *); diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 5a9c8feec10c8b..967026986de91a 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -84,7 +84,9 @@ static inline struct musb_qh *first_qh(struct list_head *q) return list_entry(q->next, struct musb_qh, ring); } - +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); extern void musb_root_disconnect(struct musb *musb); struct usb_hcd; From 0b3eba442d4810df4bdd46d6c3e189c9e7760137 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 10 Apr 2013 21:55:42 +0200 Subject: [PATCH 08/95] usb: musb: factor some host-specific functions In particular, this introduces musb_host_resume_root_hub()and musb_host_poke_root_hub() which will be stubbed out later. Signed-off-by: Daniel Mack Acked-by: Peter Korsgaard Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 18 +++++++----------- drivers/usb/musb/musb_host.c | 15 ++++++++++++++- drivers/usb/musb/musb_host.h | 3 +++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index fc4d97e2bc3dc7..bd14fed44af160 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -483,7 +483,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; @@ -500,7 +500,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: @@ -642,7 +642,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 @@ -732,17 +732,13 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, 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); @@ -757,7 +753,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, 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 diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 8914dec49f01cf..8b977d23ddfb8f 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -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 @@ -2608,3 +2607,17 @@ const struct hc_driver musb_hc_driver = { /* .start_port_reset = NULL, */ /* .hub_irq_enable = NULL, */ }; + +void musb_host_resume_root_hub(struct musb *musb) +{ + usb_hcd_resume_root_hub(musb_to_hcd(musb)); +} + +void musb_host_poke_root_hub(struct musb *musb) +{ + MUSB_HST_MODE(musb); + if (musb_to_hcd(musb)->status_urb) + usb_hcd_poll_rh_status(musb_to_hcd(musb)); + else + usb_hcd_resume_root_hub(musb_to_hcd(musb)); +} diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 967026986de91a..c63e5853df0330 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -89,6 +89,9 @@ 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); + struct usb_hcd; extern int musb_hub_status_data(struct usb_hcd *hcd, char *buf); From fe4cb0912f8e737f8e4b8b38b9e692f8062f5423 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 10 Apr 2013 21:55:43 +0200 Subject: [PATCH 09/95] usb: musb: gadget: remove hcd initialization This will be done from a more appropriate location and as it doesn't work anyway, it can safely be removed before the other changes. Signed-off-by: Daniel Mack Acked-by: Peter Korsgaard Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_gadget.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index ba7092349fa955..0414bc19d00952 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -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; From 69ae2a70bfabbd6af6309bf723ec76493512dba1 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 10 Apr 2013 21:55:44 +0200 Subject: [PATCH 10/95] usb: musb: move musb_start to musb_virthub.c This function has its only user in musb_virthub.c, so let's move it there and make it static. Signed-off-by: Daniel Mack Acked-by: Peter Korsgaard Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 45 --------------------------------- drivers/usb/musb/musb_core.h | 1 - drivers/usb/musb/musb_virthub.c | 44 ++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 46 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index bd14fed44af160..8b50cd9b5297cd 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -897,51 +897,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, /*-------------------------------------------------------------------------*/ -/* -* 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; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 0193dc9f435107..e981b9bb373908 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -502,7 +502,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); diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 17c6c0da77db69..47ee430a13c929 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -44,6 +44,50 @@ #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 ((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) { From 74c2e93600581d80695604126a3725a157d0ab72 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 10 Apr 2013 21:55:45 +0200 Subject: [PATCH 11/95] usb: musb: factor out hcd initalization The musb struct is currently allocated along with the hcd, which makes it difficult to build a driver that only acts as gadget device. Fix this by allocating musb directly, and keep the hcd around as a pointer in the musb struct. struct hc_driver musb_hc_driver can now also be static to musb_host.c, and the macro musb_to_hcd() is just a pointer dereferencer for now, and will be eliminated later. Signed-off-by: Daniel Mack Acked-by: Peter Korsgaard Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 40 +++++++++++++++---------------- drivers/usb/musb/musb_core.h | 1 + drivers/usb/musb/musb_host.c | 46 ++++++++++++++++++++++++++++++------ drivers/usb/musb/musb_host.h | 19 +++++++-------- 4 files changed, 67 insertions(+), 39 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 8b50cd9b5297cd..727a687433d61d 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -403,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); @@ -725,7 +726,8 @@ 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; + if (musb->hcd) + musb->hcd->self.is_b_host = 1; del_timer(&musb->otg_timer); break; default: @@ -766,7 +768,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, * 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); @@ -1706,24 +1709,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; @@ -1738,7 +1735,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) @@ -1764,7 +1770,7 @@ static void musb_free(struct musb *musb) dma_controller_destroy(c); } - usb_put_hcd(musb_to_hcd(musb)); + musb_host_free(musb); } /* @@ -1781,7 +1787,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. @@ -1882,13 +1887,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); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index e981b9bb373908..e633fce787fbe7 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -401,6 +401,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. diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 8b977d23ddfb8f..e6ece8a4e8b60f 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -95,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, @@ -2464,7 +2469,6 @@ static int musb_bus_resume(struct usb_hcd *hcd) return 0; } - #ifndef CONFIG_MUSB_PIO_ONLY #define MUSB_USB_DMA_ALIGN 4 @@ -2576,10 +2580,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 @@ -2608,16 +2612,44 @@ const struct hc_driver musb_hc_driver = { /* .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); +} + void musb_host_resume_root_hub(struct musb *musb) { - usb_hcd_resume_root_hub(musb_to_hcd(musb)); + usb_hcd_resume_root_hub(musb->hcd); } void musb_host_poke_root_hub(struct musb *musb) { MUSB_HST_MODE(musb); - if (musb_to_hcd(musb)->status_urb) - usb_hcd_poll_rh_status(musb_to_hcd(musb)); + if (musb->hcd->status_urb) + usb_hcd_poll_rh_status(musb->hcd); else - usb_hcd_resume_root_hub(musb_to_hcd(musb)); + usb_hcd_resume_root_hub(musb->hcd); } diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index c63e5853df0330..e47035e1e3c9a0 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -37,15 +37,9 @@ #include -static inline struct usb_hcd *musb_to_hcd(struct musb *musb) -{ - return container_of((void *) musb, struct usb_hcd, hcd_priv); -} +#define musb_to_hcd(MUSB) ((MUSB)->hcd) -static inline struct musb *hcd_to_musb(struct usb_hcd *hcd) -{ - return (struct musb *) (hcd->hcd_priv); -} +extern struct musb *hcd_to_musb(struct usb_hcd *); /* stored in "usb_host_endpoint.hcpriv" for scheduled endpoints */ struct musb_qh { @@ -85,10 +79,15 @@ static inline struct musb_qh *first_qh(struct list_head *q) } extern irqreturn_t musb_h_ep0_irq(struct musb *); +extern int musb_host_alloc(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); @@ -99,8 +98,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; From b7b741ea38a32336b45870b76aaec1abe57badd0 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 10 Apr 2013 21:55:46 +0200 Subject: [PATCH 12/95] usb: musb: add Kconfig options for HOST, GAGDET or DUAL_ROLE modes This makes building the actual object files optional to the selected mode, which saves users who know which kind of USB mode support they need some binary size. Unimplemented functions are stubbed out with static inline functions. Signed-off-by: Daniel Mack Acked-by: Peter Korsgaard Signed-off-by: Felipe Balbi --- drivers/usb/musb/Kconfig | 29 +++++++++++++++++++++++++++++ drivers/usb/musb/Makefile | 4 ++-- drivers/usb/musb/musb_gadget.h | 21 +++++++++++++++++++++ drivers/usb/musb/musb_host.h | 29 +++++++++++++++++++++++++++-- 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 06f8d29af1ef7a..797e3fd455102b 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -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" diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 3b858715b5eaa9..2b82ed7c85ca9b 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -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 diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h index 75f821cc3d98af..0314dfc770c7e5 100644 --- a/drivers/usb/musb/musb_gadget.h +++ b/drivers/usb/musb/musb_gadget.h @@ -37,6 +37,7 @@ #include +#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); @@ -48,6 +49,26 @@ 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, diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index e47035e1e3c9a0..1ce6e4ec9021bc 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -39,8 +39,6 @@ #define musb_to_hcd(MUSB) ((MUSB)->hcd) -extern struct musb *hcd_to_musb(struct usb_hcd *); - /* stored in "usb_host_endpoint.hcpriv" for scheduled endpoints */ struct musb_qh { struct usb_host_endpoint *hep; /* usbcore info */ @@ -78,6 +76,9 @@ static inline struct musb_qh *first_qh(struct list_head *q) return list_entry(q->next, struct musb_qh, ring); } + +#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 void musb_host_tx(struct musb *, u8); @@ -90,6 +91,30 @@ 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 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; From 2cc65feab2f18dfa4297209829ce228989c7356b Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 10 Apr 2013 21:55:47 +0200 Subject: [PATCH 13/95] usb: musb: add musb_host_setup() and musb_host_cleanup() This patch re-introduces the bits that are necessary to use the musb controller in host mode. Signed-off-by: Daniel Mack Acked-by: Peter Korsgaard Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 5 +++++ drivers/usb/musb/musb_host.c | 21 +++++++++++++++++++++ drivers/usb/musb/musb_host.h | 8 ++++++++ 3 files changed, 34 insertions(+) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 727a687433d61d..18da746ae6bf6c 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -953,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); @@ -1902,6 +1903,10 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb->xceiv->state = OTG_STATE_B_IDLE; } + status = musb_host_setup(musb, plat->power); + if (status < 0) + goto fail3; + status = musb_gadget_setup(musb); if (status < 0) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index e6ece8a4e8b60f..6057c2363e86be 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2640,6 +2640,27 @@ 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); diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 1ce6e4ec9021bc..48a4bdd8b9f9cc 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -81,6 +81,8 @@ static inline struct musb_qh *first_qh(struct list_head *q) 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); @@ -107,6 +109,12 @@ 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) {} From 9ad96e694c3d33465d099d0a2db1aae8cb358d1f Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 10 Apr 2013 21:55:48 +0200 Subject: [PATCH 14/95] usb: musb: re-introduce musb->port_mode Define MUSB_PORT_MODE_{HOST,GADGET,DUAL_ROLE} and store the platform-specified value in struct musb. Note that MUSB_PORT_MODE_HOST has to be set to 1 in order to match existing device tree bindings which are already documented but in fact unusued. For information on the bindings, please refer to Documentation/devicetree/bindings/usb/omap-usb.txt Documentation/devicetree/bindings/usb/am33xx-usb.txt Signed-off-by: Daniel Mack Acked-by: Peter Korsgaard Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 1 + drivers/usb/musb/musb_core.h | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 18da746ae6bf6c..a1aa75eb51e27f 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1813,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 diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index e633fce787fbe7..7d341c387eab94 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -77,6 +77,12 @@ 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 #define MUSB_CONFIG_PROC_FS @@ -356,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 */ From 6c5f6a6f532ad3c14fbed04b6f2dd0e0cab1a143 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 10 Apr 2013 21:55:49 +0200 Subject: [PATCH 15/95] usb: musb: use musb->port_mode Initialize the host and gadget subsystems of the musb driver only when the appropriate mode is selected from platform data, or device-tree information, respectively. Signed-off-by: Daniel Mack Acked-by: Peter Korsgaard Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 22 +++++++++++++++++----- drivers/usb/musb/musb_virthub.c | 3 ++- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index a1aa75eb51e27f..a04cf8b1678bb5 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1904,11 +1904,23 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb->xceiv->state = OTG_STATE_B_IDLE; } - status = musb_host_setup(musb, plat->power); - if (status < 0) - goto fail3; - - 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; diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 47ee430a13c929..104d22f7142d2e 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -79,7 +79,8 @@ static void musb_start(struct musb *musb) * (b) vbus present/connect IRQ, peripheral mode; * (c) peripheral initiates, using SRP */ - if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) { + if (musb->port_mode != MUSB_PORT_MODE_HOST && + (devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS) { musb->is_active = 1; } else { devctl |= MUSB_DEVCTL_SESSION; From 8b125df5b24cfb0ec7fa1971e343cc0badc1827d Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Wed, 10 Apr 2013 21:55:50 +0200 Subject: [PATCH 16/95] usb: musb: eliminate musb_to_hcd With the hcd is now a direct member of struct musb, we can now simply eliminate the musb_to_hcd() macro. There aren't that many users left anyway, as some where already fixed up when parts were factored out to musb_host.c Signed-off-by: Daniel Mack Acked-by: Peter Korsgaard Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_core.c | 4 ++-- drivers/usb/musb/musb_host.c | 8 ++++---- drivers/usb/musb/musb_host.h | 2 -- drivers/usb/musb/musb_virthub.c | 6 +++--- drivers/usb/musb/omap2430.c | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index a04cf8b1678bb5..29a24ced67483d 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -388,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; @@ -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; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 6057c2363e86be..f15d114572d528 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -315,9 +315,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); } @@ -1460,7 +1460,7 @@ void musb_host_tx(struct musb *musb, u8 epnum) 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 @@ -1875,7 +1875,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 diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 48a4bdd8b9f9cc..c202d5ba684bfa 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -37,8 +37,6 @@ #include -#define musb_to_hcd(MUSB) ((MUSB)->hcd) - /* stored in "usb_host_endpoint.hcpriv" for scheduled endpoints */ struct musb_qh { struct usb_host_endpoint *hep; /* usbcore info */ diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 104d22f7142d2e..a523950c2b32e6 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -212,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; } @@ -225,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) { @@ -379,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; } diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index b3e77613e0af23..c7c1d7ab547107 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -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; } From dbbafe666c47d2e3d59cea66066f98c946a1e2de Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 29 Apr 2013 12:16:30 +0200 Subject: [PATCH 17/95] usb: gadget: uvc: Remove unneeded endpoint descriptor fields initialization The streaming endpoint bandwidth parameters are computed are runtime from module parameters. Remove the corresponding static initializers. Signed-off-by: Laurent Pinchart Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_uvc.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/f_uvc.c index 38dcedddc52c0b..5f91c7a599461b 100644 --- a/drivers/usb/gadget/f_uvc.c +++ b/drivers/usb/gadget/f_uvc.c @@ -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[] = { From aee5500f4fe5fec7ba1c371e6538d791ca294bd8 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Wed, 15 May 2013 14:03:24 +0200 Subject: [PATCH 18/95] usb: musb: enable ux500 host side dma support Host side dma support for ux500 is enabled by piggybacking on Inventra dma support. Acked-by: Linus Walleij Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_host.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index f15d114572d528..1e370eec9100f2 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -629,7 +629,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; @@ -1663,7 +1663,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... @@ -1695,7 +1695,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; @@ -1751,7 +1751,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; From 3147dad6fa457e0bb7edaab36f6d290c7048b49e Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 15 May 2013 14:03:25 +0200 Subject: [PATCH 19/95] usb: musb: various cosmetic fixes on ux500 files Various non functional coding style fixes on ux500_dma.c and phy-ab8500-usb.c drivers. Acked-by: Linus Walleij Acked-by: Maxime Coquelin Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi --- drivers/usb/musb/ux500_dma.c | 6 +++--- drivers/usb/phy/phy-ab8500-usb.c | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index 33812064114557..63e7c8a6b125df 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -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); diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index e5eb1b5a04ebb8..f760e935e059dc 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -286,7 +286,8 @@ static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) else if (pinctrl_select_state(ab->pinctrl, ab->pins_sleep)) dev_err(ab->dev, "could not set pins to sleep state\n"); - /* as USB pins are shared with idddet, release them to allow + /* + * as USB pins are shared with iddet, release them to allow * iddet to request them */ pinctrl_put(ab->pinctrl); @@ -553,7 +554,7 @@ static irqreturn_t ab8500_usb_disconnect_irq(int irq, void *data) static irqreturn_t ab8500_usb_link_status_irq(int irq, void *data) { - struct ab8500_usb *ab = (struct ab8500_usb *) data; + struct ab8500_usb *ab = (struct ab8500_usb *)data; abx500_usb_link_status_update(ab); @@ -627,7 +628,7 @@ static int ab8500_usb_set_peripheral(struct usb_otg *otg, * is fixed. */ - if ((ab->mode != USB_IDLE) && (!gadget)) { + if ((ab->mode != USB_IDLE) && !gadget) { ab->mode = USB_IDLE; schedule_work(&ab->phy_dis_work); } @@ -651,7 +652,7 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) * is fixed. */ - if ((ab->mode != USB_IDLE) && (!host)) { + if ((ab->mode != USB_IDLE) && !host) { ab->mode = USB_IDLE; schedule_work(&ab->phy_dis_work); } From d0ed0645a5cfb0cccca6baa84b459eb2078e7fb1 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Wed, 15 May 2013 14:03:26 +0200 Subject: [PATCH 20/95] usb: phy: ab8500-usb: add transceiver clock control Add common clock support code for the ab8500-usb phy driver. Acked-by: Ulf Hansson Acked-by: Linus Walleij Acked-by: Maxime Coquelin Signed-off-by: Mian Yousaf Kaukab Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-ab8500-usb.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index f760e935e059dc..bdf7f570b5e4c7 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -126,6 +128,7 @@ struct ab8500_usb { unsigned vbus_draw; struct work_struct phy_dis_work; enum ab8500_usb_mode mode; + struct clk *sysclk; struct regulator *v_ape; struct regulator *v_musb; struct regulator *v_ulpi; @@ -252,6 +255,9 @@ static void ab8500_usb_phy_enable(struct ab8500_usb *ab, bool sel_host) if (IS_ERR(ab->pinctrl)) dev_err(ab->dev, "could not get/set default pinstate\n"); + if (clk_prepare_enable(ab->sysclk)) + dev_err(ab->dev, "can't prepare/enable clock\n"); + ab8500_usb_regulator_enable(ab); abx500_mask_and_set_register_interruptible(ab->dev, @@ -274,6 +280,8 @@ static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) /* Needed to disable the phy.*/ ab8500_usb_wd_workaround(ab); + clk_disable_unprepare(ab->sysclk); + ab8500_usb_regulator_disable(ab); if (!IS_ERR(ab->pinctrl)) { @@ -784,6 +792,12 @@ static int ab8500_usb_probe(struct platform_device *pdev) if (err) return err; + ab->sysclk = devm_clk_get(ab->dev, "sysclk"); + if (IS_ERR(ab->sysclk)) { + dev_err(ab->dev, "Could not get sysclk.\n"); + return PTR_ERR(ab->sysclk); + } + err = ab8500_usb_irq_setup(pdev, ab); if (err < 0) return err; From fb21f37a5249374ed63f763455a0b07b80c1e50c Mon Sep 17 00:00:00 2001 From: Sakethram Bommisetti Date: Wed, 15 May 2013 14:03:27 +0200 Subject: [PATCH 21/95] usb: phy: ab8500-usb: restart phy during probe Add an ab8500_usb_restart_phy() function called during probe to ensure that the AB8500 USB phy is initialized properly even when a cable is connected at probe time. Without this fix subsequent host reconnections are not detected properly. Acked-by: Linus Walleij Acked-by: Maxime Coquelin Signed-off-by: Sakethram Bommisetti Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-ab8500-usb.c | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index bdf7f570b5e4c7..63bb7cc14a2a79 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -668,6 +668,33 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host) return 0; } +static void ab8500_usb_restart_phy(struct ab8500_usb *ab) +{ + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_PHY_CTRL_REG, + AB8500_BIT_PHY_CTRL_DEVICE_EN, + AB8500_BIT_PHY_CTRL_DEVICE_EN); + + udelay(100); + + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_PHY_CTRL_REG, + AB8500_BIT_PHY_CTRL_DEVICE_EN, + 0); + + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_PHY_CTRL_REG, + AB8500_BIT_PHY_CTRL_HOST_EN, + AB8500_BIT_PHY_CTRL_HOST_EN); + + udelay(100); + + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8500_USB_PHY_CTRL_REG, + AB8500_BIT_PHY_CTRL_HOST_EN, + 0); +} + static int ab8500_usb_regulator_get(struct ab8500_usb *ab) { int err; @@ -887,6 +914,12 @@ static int ab8500_usb_probe(struct platform_device *pdev) /* Needed to enable ID detection. */ ab8500_usb_wd_workaround(ab); + /* + * This is required for usb-link-status to work properly when a + * cable is connected at boot time. + */ + ab8500_usb_restart_phy(ab); + abx500_usb_link_status_update(ab); dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev); From a96afc6b75cf9e7991a9b869246c5dca2a8a4e7a Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 15 May 2013 14:03:28 +0200 Subject: [PATCH 22/95] usb: phy: ab8500-usb: fix phy tuning value select logic The driver supports both ab8500 and ab8505, but the actual phy tuning values logic sets ab8500 values: if (!is_ab8500_2p0_or_earlier(ab->ab8500)) which is supposed to set values for ab8500, but incorrectly results true for ab8505 too. Fix this by adding an additional is_ab8500(ab->ab8500) check. Acked-by: Linus Walleij Acked-by: Maxime Coquelin Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-ab8500-usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 63bb7cc14a2a79..e6d461943f8472 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -835,8 +835,8 @@ static int ab8500_usb_probe(struct platform_device *pdev) return err; } - /* Phy tuning values for AB8500 */ - if (!is_ab8500_2p0_or_earlier(ab->ab8500)) { + /* Phy tuning values for AB8500 > v2.0 */ + if (is_ab8500(ab->ab8500) && !is_ab8500_2p0_or_earlier(ab->ab8500)) { /* Enable the PBT/Bank 0x12 access */ err = abx500_set_register_interruptible(ab->dev, AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x01); From b3affc399183eb0c548626daa9daad3d0e100906 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 15 May 2013 14:03:29 +0200 Subject: [PATCH 23/95] usb: phy: ab8500-usb: add platform_device_id table Add an initial platform_device_id table to the ab8500-usb driver to allow probing additional variants of the ab8500 family chips. Acked-by: Linus Walleij Acked-by: Maxime Coquelin Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-ab8500-usb.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index e6d461943f8472..a0f7becb2e8181 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -943,11 +943,18 @@ static int ab8500_usb_remove(struct platform_device *pdev) return 0; } +static struct platform_device_id ab8500_usb_devtype[] = { + { .name = "ab8500-usb", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, ab8500_usb_devtype); + static struct platform_driver ab8500_usb_driver = { .probe = ab8500_usb_probe, .remove = ab8500_usb_remove, + .id_table = ab8500_usb_devtype, .driver = { - .name = "ab8500-usb", + .name = "abx5x0-usb", .owner = THIS_MODULE, }, }; @@ -964,7 +971,6 @@ static void __exit ab8500_usb_exit(void) } module_exit(ab8500_usb_exit); -MODULE_ALIAS("platform:ab8500_usb"); MODULE_AUTHOR("ST-Ericsson AB"); MODULE_DESCRIPTION("AB8500 usb transceiver driver"); MODULE_LICENSE("GPL"); From 16604a3c27f4a43588069ae9a77a9961a485d53f Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 15 May 2013 14:03:30 +0200 Subject: [PATCH 24/95] usb: phy: ab8500-usb: move phy tuning values on separate functions Move each chip's PHY tuning value set code to a separate function to improve code readability. Acked-by: Linus Walleij Acked-by: Maxime Coquelin Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-ab8500-usb.c | 161 +++++++++++++++++-------------- 1 file changed, 86 insertions(+), 75 deletions(-) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index a0f7becb2e8181..bbc46197dce061 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -771,6 +771,86 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev, return 0; } +static void ab8500_usb_set_ab8500_tuning_values(struct ab8500_usb *ab) +{ + int err; + + /* Enable the PBT/Bank 0x12 access */ + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x01); + if (err < 0) + dev_err(ab->dev, "Failed to enable bank12 access err=%d\n", + err); + + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE1, 0xC8); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE1 register err=%d\n", + err); + + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE2, 0x00); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE2 register err=%d\n", + err); + + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x78); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", + err); + + /* Switch to normal mode/disable Bank 0x12 access */ + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x00); + if (err < 0) + dev_err(ab->dev, "Failed to switch bank12 access err=%d\n", + err); +} + +static void ab8500_usb_set_ab8505_tuning_values(struct ab8500_usb *ab) +{ + int err; + + /* Enable the PBT/Bank 0x12 access */ + err = abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, + 0x01, 0x01); + if (err < 0) + dev_err(ab->dev, "Failed to enable bank12 access err=%d\n", + err); + + err = abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE1, + 0xC8, 0xC8); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE1 register err=%d\n", + err); + + err = abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE2, + 0x60, 0x60); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE2 register err=%d\n", + err); + + err = abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE3, + 0xFC, 0x80); + + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", + err); + + /* Switch to normal mode/disable Bank 0x12 access */ + err = abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, + 0x00, 0x00); + if (err < 0) + dev_err(ab->dev, "Failed to switch bank12 access err=%d\n", + err); +} + static int ab8500_usb_probe(struct platform_device *pdev) { struct ab8500_usb *ab; @@ -835,81 +915,12 @@ static int ab8500_usb_probe(struct platform_device *pdev) return err; } - /* Phy tuning values for AB8500 > v2.0 */ - if (is_ab8500(ab->ab8500) && !is_ab8500_2p0_or_earlier(ab->ab8500)) { - /* Enable the PBT/Bank 0x12 access */ - err = abx500_set_register_interruptible(ab->dev, - AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x01); - if (err < 0) - dev_err(ab->dev, "Failed to enable bank12 access err=%d\n", - err); - - err = abx500_set_register_interruptible(ab->dev, - AB8500_DEBUG, AB8500_USB_PHY_TUNE1, 0xC8); - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE1 register err=%d\n", - err); - - err = abx500_set_register_interruptible(ab->dev, - AB8500_DEBUG, AB8500_USB_PHY_TUNE2, 0x00); - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE2 register err=%d\n", - err); - - err = abx500_set_register_interruptible(ab->dev, - AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x78); - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", - err); - - /* Switch to normal mode/disable Bank 0x12 access */ - err = abx500_set_register_interruptible(ab->dev, - AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x00); - if (err < 0) - dev_err(ab->dev, "Failed to switch bank12 access err=%d\n", - err); - } - - /* Phy tuning values for AB8505 */ - if (is_ab8505(ab->ab8500)) { - /* Enable the PBT/Bank 0x12 access */ - err = abx500_mask_and_set_register_interruptible(ab->dev, - AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, - 0x01, 0x01); - if (err < 0) - dev_err(ab->dev, "Failed to enable bank12 access err=%d\n", - err); - - err = abx500_mask_and_set_register_interruptible(ab->dev, - AB8500_DEBUG, AB8500_USB_PHY_TUNE1, - 0xC8, 0xC8); - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE1 register err=%d\n", - err); - - err = abx500_mask_and_set_register_interruptible(ab->dev, - AB8500_DEBUG, AB8500_USB_PHY_TUNE2, - 0x60, 0x60); - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE2 register err=%d\n", - err); - - err = abx500_mask_and_set_register_interruptible(ab->dev, - AB8500_DEBUG, AB8500_USB_PHY_TUNE3, - 0xFC, 0x80); - - if (err < 0) - dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", - err); - - /* Switch to normal mode/disable Bank 0x12 access */ - err = abx500_mask_and_set_register_interruptible(ab->dev, - AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, - 0x00, 0x00); - if (err < 0) - dev_err(ab->dev, "Failed to switch bank12 access err=%d\n", - err); - } + if (is_ab8500(ab->ab8500) && !is_ab8500_2p0_or_earlier(ab->ab8500)) + /* Phy tuning values for AB8500 > v2.0 */ + ab8500_usb_set_ab8500_tuning_values(ab); + else if (is_ab8505(ab->ab8500)) + /* Phy tuning values for AB8505 */ + ab8500_usb_set_ab8505_tuning_values(ab); /* Needed to enable ID detection. */ ab8500_usb_wd_workaround(ab); From bd4c9f0278338585024db55f2ff74d6e0a6a1f6b Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 15 May 2013 14:03:31 +0200 Subject: [PATCH 25/95] usb: phy: ab8500-usb: add flag bits to control driver features Introduce a "flags" field in "struct ab8500_usb" to allow controlling driver features and quirks depending on ab8500 chip variant and revision. Acked-by: Linus Walleij Acked-by: Maxime Coquelin Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-ab8500-usb.c | 109 +++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 36 deletions(-) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index bbc46197dce061..54dd2b16c80b4a 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -121,6 +121,17 @@ enum ab8500_usb_mode { USB_DEDICATED_CHG }; +/* Register USB_LINK_STATUS interrupt */ +#define AB8500_USB_FLAG_USE_LINK_STATUS_IRQ (1 << 0) +/* Register ID_WAKEUP_F interrupt */ +#define AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ (1 << 1) +/* Register VBUS_DET_F interrupt */ +#define AB8500_USB_FLAG_USE_VBUS_DET_IRQ (1 << 2) +/* Driver is using the ab-iddet driver*/ +#define AB8500_USB_FLAG_USE_AB_IDDET (1 << 3) +/* Enable setting regulators voltage */ +#define AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE (1 << 4) + struct ab8500_usb { struct usb_phy phy; struct device *dev; @@ -136,6 +147,7 @@ struct ab8500_usb { int previous_link_status_state; struct pinctrl *pinctrl; struct pinctrl_state *pins_sleep; + unsigned int flags; }; static inline struct ab8500_usb *phy_to_ab(struct usb_phy *x) @@ -174,7 +186,7 @@ static void ab8500_usb_regulator_enable(struct ab8500_usb *ab) if (ret) dev_err(ab->dev, "Failed to enable v-ape\n"); - if (!is_ab8500_2p0_or_earlier(ab->ab8500)) { + if (ab->flags & AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE) { ab->saved_v_ulpi = regulator_get_voltage(ab->v_ulpi); if (ab->saved_v_ulpi < 0) dev_err(ab->dev, "Failed to get v_ulpi voltage\n"); @@ -194,7 +206,7 @@ static void ab8500_usb_regulator_enable(struct ab8500_usb *ab) if (ret) dev_err(ab->dev, "Failed to enable vddulpivio18\n"); - if (!is_ab8500_2p0_or_earlier(ab->ab8500)) { + if (ab->flags & AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE) { volt = regulator_get_voltage(ab->v_ulpi); if ((volt != 1300000) && (volt != 1350000)) dev_err(ab->dev, "Vintcore is not set to 1.3V volt=%d\n", @@ -215,7 +227,7 @@ static void ab8500_usb_regulator_disable(struct ab8500_usb *ab) regulator_disable(ab->v_ulpi); /* USB is not the only consumer of Vintcore, restore old settings */ - if (!is_ab8500_2p0_or_earlier(ab->ab8500)) { + if (ab->flags & AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE) { if (ab->saved_v_ulpi > 0) { ret = regulator_set_voltage(ab->v_ulpi, ab->saved_v_ulpi, ab->saved_v_ulpi); @@ -729,43 +741,52 @@ static int ab8500_usb_irq_setup(struct platform_device *pdev, int err; int irq; - irq = platform_get_irq_byname(pdev, "USB_LINK_STATUS"); - if (irq < 0) { - dev_err(&pdev->dev, "Link status irq not found\n"); - return irq; - } - err = devm_request_threaded_irq(&pdev->dev, irq, NULL, - ab8500_usb_link_status_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, "usb-link-status", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for link status irq\n"); - return err; + if (ab->flags & AB8500_USB_FLAG_USE_LINK_STATUS_IRQ) { + irq = platform_get_irq_byname(pdev, "USB_LINK_STATUS"); + if (irq < 0) { + dev_err(&pdev->dev, "Link status irq not found\n"); + return irq; + } + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + ab8500_usb_link_status_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-link-status", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for link status irq\n"); + return err; + } } - irq = platform_get_irq_byname(pdev, "ID_WAKEUP_F"); - if (irq < 0) { - dev_err(&pdev->dev, "ID fall irq not found\n"); - return irq; - } - err = devm_request_threaded_irq(&pdev->dev, irq, NULL, - ab8500_usb_disconnect_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, "usb-id-fall", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for ID fall irq\n"); - return err; + if (ab->flags & AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ) { + irq = platform_get_irq_byname(pdev, "ID_WAKEUP_F"); + if (irq < 0) { + dev_err(&pdev->dev, "ID fall irq not found\n"); + return irq; + } + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + ab8500_usb_disconnect_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-id-fall", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for ID fall irq\n"); + return err; + } } - irq = platform_get_irq_byname(pdev, "VBUS_DET_F"); - if (irq < 0) { - dev_err(&pdev->dev, "VBUS fall irq not found\n"); - return irq; - } - err = devm_request_threaded_irq(&pdev->dev, irq, NULL, - ab8500_usb_disconnect_irq, - IRQF_NO_SUSPEND | IRQF_SHARED, "usb-vbus-fall", ab); - if (err < 0) { - dev_err(ab->dev, "request_irq failed for Vbus fall irq\n"); - return err; + if (ab->flags & AB8500_USB_FLAG_USE_VBUS_DET_IRQ) { + irq = platform_get_irq_byname(pdev, "VBUS_DET_F"); + if (irq < 0) { + dev_err(&pdev->dev, "VBUS fall irq not found\n"); + return irq; + } + err = devm_request_threaded_irq(&pdev->dev, irq, NULL, + ab8500_usb_disconnect_irq, + IRQF_NO_SUSPEND | IRQF_SHARED, + "usb-vbus-fall", ab); + if (err < 0) { + dev_err(ab->dev, "request_irq failed for Vbus fall irq\n"); + return err; + } } return 0; @@ -888,6 +909,22 @@ static int ab8500_usb_probe(struct platform_device *pdev) otg->set_host = ab8500_usb_set_host; otg->set_peripheral = ab8500_usb_set_peripheral; + if (is_ab8500(ab->ab8500)) { + ab->flags |= AB8500_USB_FLAG_USE_LINK_STATUS_IRQ | + AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ | + AB8500_USB_FLAG_USE_VBUS_DET_IRQ | + AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE; + } else if (is_ab8505(ab->ab8500)) { + ab->flags |= AB8500_USB_FLAG_USE_LINK_STATUS_IRQ | + AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ | + AB8500_USB_FLAG_USE_VBUS_DET_IRQ | + AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE; + } + + /* Disable regulator voltage setting for AB8500 <= v2.0 */ + if (is_ab8500_2p0_or_earlier(ab->ab8500)) + ab->flags &= ~AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE; + platform_set_drvdata(pdev, ab); ATOMIC_INIT_NOTIFIER_HEAD(&ab->phy.notifier); From 0c380c0ee03c4465f45f19c1c3fc49edd299c922 Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 15 May 2013 14:03:32 +0200 Subject: [PATCH 26/95] usb: phy: ab8500-usb: add ab8540 support Add support for the ab8540 variant of the ab8500 family. Acked-by: Linus Walleij Acked-by: Maxime Coquelin Cc: Avinash Kumar Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-ab8500-usb.c | 297 ++++++++++++++++++++++++++++++- 1 file changed, 294 insertions(+), 3 deletions(-) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 54dd2b16c80b4a..ceab508b90e867 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -1,10 +1,11 @@ /* * drivers/usb/otg/ab8500_usb.c * - * USB transceiver driver for AB8500 chip + * USB transceiver driver for AB8500 family chips * - * Copyright (C) 2010 ST-Ericsson AB + * Copyright (C) 2010-2013 ST-Ericsson AB * Mian Yousaf Kaukab + * Avinash Kumar * * 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 @@ -43,21 +44,33 @@ /* Bank AB8500_USB */ #define AB8500_USB_LINE_STAT_REG 0x80 #define AB8505_USB_LINE_STAT_REG 0x94 +#define AB8540_USB_LINK_STAT_REG 0x94 +#define AB8540_USB_OTG_CTL_REG 0x87 #define AB8500_USB_PHY_CTRL_REG 0x8A +#define AB8540_VBUS_CTRL_REG 0x82 /* Bank AB8500_DEVELOPMENT */ #define AB8500_BANK12_ACCESS 0x00 /* Bank AB8500_DEBUG */ +#define AB8540_DEBUG 0x32 #define AB8500_USB_PHY_TUNE1 0x05 #define AB8500_USB_PHY_TUNE2 0x06 #define AB8500_USB_PHY_TUNE3 0x07 +/* Bank AB8500_INTERRUPT */ +#define AB8500_IT_SOURCE2_REG 0x01 + #define AB8500_BIT_OTG_STAT_ID (1 << 0) #define AB8500_BIT_PHY_CTRL_HOST_EN (1 << 0) #define AB8500_BIT_PHY_CTRL_DEVICE_EN (1 << 1) #define AB8500_BIT_WD_CTRL_ENABLE (1 << 0) #define AB8500_BIT_WD_CTRL_KICK (1 << 1) +#define AB8500_BIT_SOURCE2_VBUSDET (1 << 7) +#define AB8540_BIT_OTG_CTL_VBUS_VALID_ENA (1 << 0) +#define AB8540_BIT_OTG_CTL_ID_HOST_ENA (1 << 1) +#define AB8540_BIT_OTG_CTL_ID_DEV_ENA (1 << 5) +#define AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA (1 << 0) #define AB8500_WD_KICK_DELAY_US 100 /* usec */ #define AB8500_WD_V11_DISABLE_DELAY_US 100 /* usec */ @@ -114,6 +127,37 @@ enum ab8505_usb_link_status { USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8505, }; +enum ab8540_usb_link_status { + USB_LINK_NOT_CONFIGURED_8540 = 0, + USB_LINK_STD_HOST_NC_8540, + USB_LINK_STD_HOST_C_NS_8540, + USB_LINK_STD_HOST_C_S_8540, + USB_LINK_CDP_8540, + USB_LINK_RESERVED0_8540, + USB_LINK_RESERVED1_8540, + USB_LINK_DEDICATED_CHG_8540, + USB_LINK_ACA_RID_A_8540, + USB_LINK_ACA_RID_B_8540, + USB_LINK_ACA_RID_C_NM_8540, + USB_LINK_RESERVED2_8540, + USB_LINK_RESERVED3_8540, + USB_LINK_HM_IDGND_8540, + USB_LINK_CHARGERPORT_NOT_OK_8540, + USB_LINK_CHARGER_DM_HIGH_8540, + USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8540, + USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_8540, + USB_LINK_STD_UPSTREAM_8540, + USB_LINK_CHARGER_SE1_8540, + USB_LINK_CARKIT_CHGR_1_8540, + USB_LINK_CARKIT_CHGR_2_8540, + USB_LINK_ACA_DOCK_CHGR_8540, + USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_8540, + USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_8540, + USB_LINK_SAMSUNG_UART_CBL_PHY_EN_8540, + USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_8540, + USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8540 +}; + enum ab8500_usb_mode { USB_IDLE = 0, USB_PERIPHERAL, @@ -131,6 +175,10 @@ enum ab8500_usb_mode { #define AB8500_USB_FLAG_USE_AB_IDDET (1 << 3) /* Enable setting regulators voltage */ #define AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE (1 << 4) +/* Enable the check_vbus_status workaround */ +#define AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS (1 << 5) +/* Enable the vbus host workaround */ +#define AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK (1 << 6) struct ab8500_usb { struct usb_phy phy; @@ -138,6 +186,7 @@ struct ab8500_usb { struct ab8500 *ab8500; unsigned vbus_draw; struct work_struct phy_dis_work; + struct work_struct vbus_event_work; enum ab8500_usb_mode mode; struct clk *sysclk; struct regulator *v_ape; @@ -147,6 +196,7 @@ struct ab8500_usb { int previous_link_status_state; struct pinctrl *pinctrl; struct pinctrl_state *pins_sleep; + bool enabled_charging_detection; unsigned int flags; }; @@ -275,6 +325,15 @@ static void ab8500_usb_phy_enable(struct ab8500_usb *ab, bool sel_host) abx500_mask_and_set_register_interruptible(ab->dev, AB8500_USB, AB8500_USB_PHY_CTRL_REG, bit, bit); + + if (ab->flags & AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK) { + if (sel_host) + abx500_set_register_interruptible(ab->dev, + AB8500_USB, AB8540_USB_OTG_CTL_REG, + AB8540_BIT_OTG_CTL_VBUS_VALID_ENA | + AB8540_BIT_OTG_CTL_ID_HOST_ENA | + AB8540_BIT_OTG_CTL_ID_DEV_ENA); + } } static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) @@ -319,6 +378,128 @@ static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) #define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_enable(ab, false) #define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_disable(ab, false) +static int ab8540_usb_link_status_update(struct ab8500_usb *ab, + enum ab8540_usb_link_status lsts) +{ + enum ux500_musb_vbus_id_status event = 0; + + dev_dbg(ab->dev, "ab8540_usb_link_status_update %d\n", lsts); + + if (ab->enabled_charging_detection) { + /* Disable USB Charger detection */ + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8540_VBUS_CTRL_REG, + AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA, 0x00); + ab->enabled_charging_detection = false; + } + + /* + * Spurious link_status interrupts are seen in case of a + * disconnection of a device in IDGND and RIDA stage + */ + if (ab->previous_link_status_state == USB_LINK_HM_IDGND_8540 && + (lsts == USB_LINK_STD_HOST_C_NS_8540 || + lsts == USB_LINK_STD_HOST_NC_8540)) + return 0; + + if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_8540 && + (lsts == USB_LINK_STD_HOST_NC_8540)) + return 0; + + ab->previous_link_status_state = lsts; + + switch (lsts) { + case USB_LINK_ACA_RID_B_8540: + event = UX500_MUSB_RIDB; + case USB_LINK_NOT_CONFIGURED_8540: + case USB_LINK_RESERVED0_8540: + case USB_LINK_RESERVED1_8540: + case USB_LINK_RESERVED2_8540: + case USB_LINK_RESERVED3_8540: + ab->mode = USB_IDLE; + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + if (event != UX500_MUSB_RIDB) + event = UX500_MUSB_NONE; + /* + * Fallback to default B_IDLE as nothing + * is connected + */ + ab->phy.state = OTG_STATE_B_IDLE; + break; + + case USB_LINK_ACA_RID_C_NM_8540: + event = UX500_MUSB_RIDC; + case USB_LINK_STD_HOST_NC_8540: + case USB_LINK_STD_HOST_C_NS_8540: + case USB_LINK_STD_HOST_C_S_8540: + case USB_LINK_CDP_8540: + if (ab->mode == USB_IDLE) { + ab->mode = USB_PERIPHERAL; + ab8500_usb_peri_phy_en(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); + } + if (event != UX500_MUSB_RIDC) + event = UX500_MUSB_VBUS; + break; + + case USB_LINK_ACA_RID_A_8540: + case USB_LINK_ACA_DOCK_CHGR_8540: + event = UX500_MUSB_RIDA; + case USB_LINK_HM_IDGND_8540: + case USB_LINK_STD_UPSTREAM_8540: + if (ab->mode == USB_IDLE) { + ab->mode = USB_HOST; + ab8500_usb_host_phy_en(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); + } + ab->phy.otg->default_a = true; + if (event != UX500_MUSB_RIDA) + event = UX500_MUSB_ID; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + break; + + case USB_LINK_DEDICATED_CHG_8540: + ab->mode = USB_DEDICATED_CHG; + event = UX500_MUSB_CHARGER; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + break; + + case USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8540: + case USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_8540: + event = UX500_MUSB_NONE; + if (ab->mode == USB_HOST) { + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + ab8500_usb_host_phy_dis(ab); + ab->mode = USB_IDLE; + } + if (ab->mode == USB_PERIPHERAL) { + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + ab8500_usb_peri_phy_dis(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_CLEAN, &ab->vbus_draw); + ab->mode = USB_IDLE; + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + } + break; + + default: + event = UX500_MUSB_NONE; + break; + } + + return 0; +} + static int ab8505_usb_link_status_update(struct ab8500_usb *ab, enum ab8505_usb_link_status lsts) { @@ -519,6 +700,13 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab) AB8500_USB, AB8505_USB_LINE_STAT_REG, ®); lsts = (reg >> 3) & 0x1F; ret = ab8505_usb_link_status_update(ab, lsts); + } else if (is_ab8540(ab->ab8500)) { + enum ab8540_usb_link_status lsts; + + abx500_get_register_interruptible(ab->dev, + AB8500_USB, AB8540_USB_LINK_STAT_REG, ®); + lsts = (reg >> 3) & 0xFF; + ret = ab8540_usb_link_status_update(ab, lsts); } return ret; @@ -593,6 +781,69 @@ static void ab8500_usb_phy_disable_work(struct work_struct *work) ab8500_usb_peri_phy_dis(ab); } +/* Check if VBUS is set and linkstatus has not detected a cable. */ +static bool ab8500_usb_check_vbus_status(struct ab8500_usb *ab) +{ + u8 isource2; + u8 reg; + enum ab8540_usb_link_status lsts; + + abx500_get_register_interruptible(ab->dev, + AB8500_INTERRUPT, AB8500_IT_SOURCE2_REG, + &isource2); + + /* If Vbus is below 3.6V abort */ + if (!(isource2 & AB8500_BIT_SOURCE2_VBUSDET)) + return false; + + abx500_get_register_interruptible(ab->dev, + AB8500_USB, AB8540_USB_LINK_STAT_REG, + ®); + + lsts = (reg >> 3) & 0xFF; + + /* Check if linkstatus has detected a cable */ + if (lsts) + return false; + + return true; +} + +/* re-trigger charger detection again with watchdog re-kick. */ +static void ab8500_usb_vbus_turn_on_event_work(struct work_struct *work) +{ + struct ab8500_usb *ab = container_of(work, struct ab8500_usb, + vbus_event_work); + + if (ab->mode != USB_IDLE) + return; + + abx500_set_register_interruptible(ab->dev, + AB8500_SYS_CTRL2_BLOCK, AB8500_MAIN_WD_CTRL_REG, + AB8500_BIT_WD_CTRL_ENABLE); + + udelay(100); + + abx500_set_register_interruptible(ab->dev, + AB8500_SYS_CTRL2_BLOCK, AB8500_MAIN_WD_CTRL_REG, + AB8500_BIT_WD_CTRL_ENABLE | AB8500_BIT_WD_CTRL_KICK); + + udelay(100); + + /* Disable Main watchdog */ + abx500_set_register_interruptible(ab->dev, + AB8500_SYS_CTRL2_BLOCK, AB8500_MAIN_WD_CTRL_REG, + 0x0); + + /* Enable USB Charger detection */ + abx500_mask_and_set_register_interruptible(ab->dev, + AB8500_USB, AB8540_VBUS_CTRL_REG, + AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA, + AB8540_BIT_VBUS_CTRL_CHARG_DET_ENA); + + ab->enabled_charging_detection = true; +} + static unsigned ab8500_eyediagram_workaroud(struct ab8500_usb *ab, unsigned mA) { /* @@ -872,6 +1123,29 @@ static void ab8500_usb_set_ab8505_tuning_values(struct ab8500_usb *ab) err); } +static void ab8500_usb_set_ab8540_tuning_values(struct ab8500_usb *ab) +{ + int err; + + err = abx500_set_register_interruptible(ab->dev, + AB8540_DEBUG, AB8500_USB_PHY_TUNE1, 0xCC); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE1 register ret=%d\n", + err); + + err = abx500_set_register_interruptible(ab->dev, + AB8540_DEBUG, AB8500_USB_PHY_TUNE2, 0x60); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE2 register ret=%d\n", + err); + + err = abx500_set_register_interruptible(ab->dev, + AB8540_DEBUG, AB8500_USB_PHY_TUNE3, 0x90); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE3 regester ret=%d\n", + err); +} + static int ab8500_usb_probe(struct platform_device *pdev) { struct ab8500_usb *ab; @@ -919,6 +1193,11 @@ static int ab8500_usb_probe(struct platform_device *pdev) AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ | AB8500_USB_FLAG_USE_VBUS_DET_IRQ | AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE; + } else if (is_ab8540(ab->ab8500)) { + ab->flags |= AB8500_USB_FLAG_USE_LINK_STATUS_IRQ | + AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS | + AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK | + AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE; } /* Disable regulator voltage setting for AB8500 <= v2.0 */ @@ -932,6 +1211,8 @@ static int ab8500_usb_probe(struct platform_device *pdev) /* all: Disable phy when called from set_host and set_peripheral */ INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work); + INIT_WORK(&ab->vbus_event_work, ab8500_usb_vbus_turn_on_event_work); + err = ab8500_usb_regulator_get(ab); if (err) return err; @@ -958,6 +1239,9 @@ static int ab8500_usb_probe(struct platform_device *pdev) else if (is_ab8505(ab->ab8500)) /* Phy tuning values for AB8505 */ ab8500_usb_set_ab8505_tuning_values(ab); + else if (is_ab8540(ab->ab8500)) + /* Phy tuning values for AB8540 */ + ab8500_usb_set_ab8540_tuning_values(ab); /* Needed to enable ID detection. */ ab8500_usb_wd_workaround(ab); @@ -968,6 +1252,11 @@ static int ab8500_usb_probe(struct platform_device *pdev) */ ab8500_usb_restart_phy(ab); + if (ab->flags & AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS) { + if (ab8500_usb_check_vbus_status(ab)) + schedule_work(&ab->vbus_event_work); + } + abx500_usb_link_status_update(ab); dev_info(&pdev->dev, "revision 0x%2x driver initialized\n", rev); @@ -980,6 +1269,7 @@ static int ab8500_usb_remove(struct platform_device *pdev) struct ab8500_usb *ab = platform_get_drvdata(pdev); cancel_work_sync(&ab->phy_dis_work); + cancel_work_sync(&ab->vbus_event_work); usb_remove_phy(&ab->phy); @@ -993,6 +1283,7 @@ static int ab8500_usb_remove(struct platform_device *pdev) static struct platform_device_id ab8500_usb_devtype[] = { { .name = "ab8500-usb", }, + { .name = "ab8540-usb", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, ab8500_usb_devtype); @@ -1020,5 +1311,5 @@ static void __exit ab8500_usb_exit(void) module_exit(ab8500_usb_exit); MODULE_AUTHOR("ST-Ericsson AB"); -MODULE_DESCRIPTION("AB8500 usb transceiver driver"); +MODULE_DESCRIPTION("AB8500 family usb transceiver driver"); MODULE_LICENSE("GPL"); From f85bff5d1ece92017a57129877573e0fa4b1050c Mon Sep 17 00:00:00 2001 From: Fabio Baltieri Date: Wed, 15 May 2013 14:03:33 +0200 Subject: [PATCH 27/95] usb: phy: ab8500-usb: add ab9540 support Add support for the ab9540 variant of the ab8500 family. Acked-by: Linus Walleij Acked-by: Maxime Coquelin Cc: Avinash Kumar Cc: Thirupathi Chippakurthy Signed-off-by: Fabio Baltieri Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-ab8500-usb.c | 213 +++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index ceab508b90e867..087402350b6d3f 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -6,6 +6,7 @@ * Copyright (C) 2010-2013 ST-Ericsson AB * Mian Yousaf Kaukab * Avinash Kumar + * Thirupathi Chippakurthy * * 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 @@ -45,6 +46,7 @@ #define AB8500_USB_LINE_STAT_REG 0x80 #define AB8505_USB_LINE_STAT_REG 0x94 #define AB8540_USB_LINK_STAT_REG 0x94 +#define AB9540_USB_LINK_STAT_REG 0x94 #define AB8540_USB_OTG_CTL_REG 0x87 #define AB8500_USB_PHY_CTRL_REG 0x8A #define AB8540_VBUS_CTRL_REG 0x82 @@ -158,6 +160,37 @@ enum ab8540_usb_link_status { USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_8540 }; +enum ab9540_usb_link_status { + USB_LINK_NOT_CONFIGURED_9540 = 0, + USB_LINK_STD_HOST_NC_9540, + USB_LINK_STD_HOST_C_NS_9540, + USB_LINK_STD_HOST_C_S_9540, + USB_LINK_CDP_9540, + USB_LINK_RESERVED0_9540, + USB_LINK_RESERVED1_9540, + USB_LINK_DEDICATED_CHG_9540, + USB_LINK_ACA_RID_A_9540, + USB_LINK_ACA_RID_B_9540, + USB_LINK_ACA_RID_C_NM_9540, + USB_LINK_RESERVED2_9540, + USB_LINK_RESERVED3_9540, + USB_LINK_HM_IDGND_9540, + USB_LINK_CHARGERPORT_NOT_OK_9540, + USB_LINK_CHARGER_DM_HIGH_9540, + USB_LINK_PHYEN_NO_VBUS_NO_IDGND_9540, + USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_9540, + USB_LINK_STD_UPSTREAM_9540, + USB_LINK_CHARGER_SE1_9540, + USB_LINK_CARKIT_CHGR_1_9540, + USB_LINK_CARKIT_CHGR_2_9540, + USB_LINK_ACA_DOCK_CHGR_9540, + USB_LINK_SAMSUNG_BOOT_CBL_PHY_EN_9540, + USB_LINK_SAMSUNG_BOOT_CBL_PHY_DISB_9540, + USB_LINK_SAMSUNG_UART_CBL_PHY_EN_9540, + USB_LINK_SAMSUNG_UART_CBL_PHY_DISB_9540, + USB_LINK_MOTOROLA_FACTORY_CBL_PHY_EN_9540 +}; + enum ab8500_usb_mode { USB_IDLE = 0, USB_PERIPHERAL, @@ -378,6 +411,132 @@ static void ab8500_usb_phy_disable(struct ab8500_usb *ab, bool sel_host) #define ab8500_usb_peri_phy_en(ab) ab8500_usb_phy_enable(ab, false) #define ab8500_usb_peri_phy_dis(ab) ab8500_usb_phy_disable(ab, false) +static int ab9540_usb_link_status_update(struct ab8500_usb *ab, + enum ab9540_usb_link_status lsts) +{ + enum ux500_musb_vbus_id_status event = 0; + + dev_dbg(ab->dev, "ab9540_usb_link_status_update %d\n", lsts); + + if (ab->previous_link_status_state == USB_LINK_HM_IDGND_9540 && + (lsts == USB_LINK_STD_HOST_C_NS_9540 || + lsts == USB_LINK_STD_HOST_NC_9540)) + return 0; + + if (ab->previous_link_status_state == USB_LINK_ACA_RID_A_9540 && + (lsts == USB_LINK_STD_HOST_NC_9540)) + return 0; + + ab->previous_link_status_state = lsts; + + switch (lsts) { + case USB_LINK_ACA_RID_B_9540: + event = UX500_MUSB_RIDB; + case USB_LINK_NOT_CONFIGURED_9540: + case USB_LINK_RESERVED0_9540: + case USB_LINK_RESERVED1_9540: + case USB_LINK_RESERVED2_9540: + case USB_LINK_RESERVED3_9540: + if (ab->mode == USB_PERIPHERAL) + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_CLEAN, &ab->vbus_draw); + ab->mode = USB_IDLE; + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + if (event != UX500_MUSB_RIDB) + event = UX500_MUSB_NONE; + /* Fallback to default B_IDLE as nothing is connected. */ + ab->phy.state = OTG_STATE_B_IDLE; + break; + + case USB_LINK_ACA_RID_C_NM_9540: + event = UX500_MUSB_RIDC; + case USB_LINK_STD_HOST_NC_9540: + case USB_LINK_STD_HOST_C_NS_9540: + case USB_LINK_STD_HOST_C_S_9540: + case USB_LINK_CDP_9540: + if (ab->mode == USB_HOST) { + ab->mode = USB_PERIPHERAL; + ab8500_usb_host_phy_dis(ab); + ab8500_usb_peri_phy_en(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); + } + if (ab->mode == USB_IDLE) { + ab->mode = USB_PERIPHERAL; + ab8500_usb_peri_phy_en(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); + } + if (event != UX500_MUSB_RIDC) + event = UX500_MUSB_VBUS; + break; + + case USB_LINK_ACA_RID_A_9540: + event = UX500_MUSB_RIDA; + case USB_LINK_HM_IDGND_9540: + case USB_LINK_STD_UPSTREAM_9540: + if (ab->mode == USB_PERIPHERAL) { + ab->mode = USB_HOST; + ab8500_usb_peri_phy_dis(ab); + ab8500_usb_host_phy_en(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); + } + if (ab->mode == USB_IDLE) { + ab->mode = USB_HOST; + ab8500_usb_host_phy_en(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_PREPARE, &ab->vbus_draw); + } + ab->phy.otg->default_a = true; + if (event != UX500_MUSB_RIDA) + event = UX500_MUSB_ID; + + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + break; + + case USB_LINK_DEDICATED_CHG_9540: + ab->mode = USB_DEDICATED_CHG; + event = UX500_MUSB_CHARGER; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + break; + + case USB_LINK_PHYEN_NO_VBUS_NO_IDGND_9540: + case USB_LINK_STD_UPSTREAM_NO_IDGNG_VBUS_9540: + if (!(is_ab9540_2p0_or_earlier(ab->ab8500))) { + event = UX500_MUSB_NONE; + if (ab->mode == USB_HOST) { + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + ab8500_usb_host_phy_dis(ab); + ab->mode = USB_IDLE; + } + if (ab->mode == USB_PERIPHERAL) { + atomic_notifier_call_chain(&ab->phy.notifier, + event, &ab->vbus_draw); + ab8500_usb_peri_phy_dis(ab); + atomic_notifier_call_chain(&ab->phy.notifier, + UX500_MUSB_CLEAN, + &ab->vbus_draw); + ab->mode = USB_IDLE; + ab->phy.otg->default_a = false; + ab->vbus_draw = 0; + } + } + break; + + default: + break; + } + + return 0; +} + static int ab8540_usb_link_status_update(struct ab8500_usb *ab, enum ab8540_usb_link_status lsts) { @@ -707,6 +866,13 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab) AB8500_USB, AB8540_USB_LINK_STAT_REG, ®); lsts = (reg >> 3) & 0xFF; ret = ab8540_usb_link_status_update(ab, lsts); + } else if (is_ab9540(ab->ab8500)) { + enum ab9540_usb_link_status lsts; + + abx500_get_register_interruptible(ab->dev, + AB8500_USB, AB9540_USB_LINK_STAT_REG, ®); + lsts = (reg >> 3) & 0xFF; + ret = ab9540_usb_link_status_update(ab, lsts); } return ret; @@ -1146,6 +1312,43 @@ static void ab8500_usb_set_ab8540_tuning_values(struct ab8500_usb *ab) err); } +static void ab8500_usb_set_ab9540_tuning_values(struct ab8500_usb *ab) +{ + int err; + + /* Enable the PBT/Bank 0x12 access */ + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x01); + if (err < 0) + dev_err(ab->dev, "Failed to enable bank12 access err=%d\n", + err); + + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE1, 0xC8); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE1 register err=%d\n", + err); + + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE2, 0x60); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE2 register err=%d\n", + err); + + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEBUG, AB8500_USB_PHY_TUNE3, 0x80); + if (err < 0) + dev_err(ab->dev, "Failed to set PHY_TUNE3 regester err=%d\n", + err); + + /* Switch to normal mode/disable Bank 0x12 access */ + err = abx500_set_register_interruptible(ab->dev, + AB8500_DEVELOPMENT, AB8500_BANK12_ACCESS, 0x00); + if (err < 0) + dev_err(ab->dev, "Failed to switch bank12 access err=%d\n", + err); +} + static int ab8500_usb_probe(struct platform_device *pdev) { struct ab8500_usb *ab; @@ -1198,6 +1401,12 @@ static int ab8500_usb_probe(struct platform_device *pdev) AB8500_USB_FLAG_USE_CHECK_VBUS_STATUS | AB8500_USB_FLAG_USE_VBUS_HOST_QUIRK | AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE; + } else if (is_ab9540(ab->ab8500)) { + ab->flags |= AB8500_USB_FLAG_USE_LINK_STATUS_IRQ | + AB8500_USB_FLAG_REGULATOR_SET_VOLTAGE; + if (is_ab9540_2p0_or_earlier(ab->ab8500)) + ab->flags |= AB8500_USB_FLAG_USE_ID_WAKEUP_IRQ | + AB8500_USB_FLAG_USE_VBUS_DET_IRQ; } /* Disable regulator voltage setting for AB8500 <= v2.0 */ @@ -1242,6 +1451,9 @@ static int ab8500_usb_probe(struct platform_device *pdev) else if (is_ab8540(ab->ab8500)) /* Phy tuning values for AB8540 */ ab8500_usb_set_ab8540_tuning_values(ab); + else if (is_ab9540(ab->ab8500)) + /* Phy tuning values for AB9540 */ + ab8500_usb_set_ab9540_tuning_values(ab); /* Needed to enable ID detection. */ ab8500_usb_wd_workaround(ab); @@ -1284,6 +1496,7 @@ static int ab8500_usb_remove(struct platform_device *pdev) static struct platform_device_id ab8500_usb_devtype[] = { { .name = "ab8500-usb", }, { .name = "ab8540-usb", }, + { .name = "ab9540-usb", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, ab8500_usb_devtype); From 6278703bcc4fa9778898ce9a7028b0b8e99747ef Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 16 May 2013 11:57:06 +0200 Subject: [PATCH 28/95] usb: phy: samsung: Select common driver part implicitly Since phy-samsung-usb library can be used only by phy-samsung-usb2 and phy-samsung-usb3 drivers, there is no need to give explicit control over its Kconfig symbol. This patch makes CONFIG_SAMSUNG_USBPHY symbol hidden and selected implicitly by CONFIG_SAMSUNG_USB2PHY and CONFIG_SAMSUNG_USB3PHY. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 7ef3eb8617a6c7..37e747a9e27da5 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -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 From 87331b069761a292c7d832fc3bb34cde56418982 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 16 May 2013 11:57:07 +0200 Subject: [PATCH 29/95] usb: phy: samsung: Use clk_get to get reference clock There is no need to use devm_clk_get to get a clock that is being put at the end of the function. This patch changes the code getting reference clock to use clk_get instead of useless in this case devm_clk_get. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-samsung-usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c index 7b118ee5f5e4ae..62cdb7e13d977d 100644 --- a/drivers/usb/phy/phy-samsung-usb.c +++ b/drivers/usb/phy/phy-samsung-usb.c @@ -175,9 +175,9 @@ int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) * 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); From 0aa823a2ca02cd21c587713e8195a2ffb8bd7872 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 16 May 2013 11:57:08 +0200 Subject: [PATCH 30/95] usb: phy: samsung: Consolidate reference clock rate handling This patch cleans up handling of reference clock rate in Samsung USB PHY drivers. It is mostly a cosmetic change but improves error handling in case of failing to get reference clock or invalid clock rate. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-samsung-usb.c | 114 +++++++++++++++++------------ drivers/usb/phy/phy-samsung-usb.h | 7 ++ drivers/usb/phy/phy-samsung-usb2.c | 8 +- drivers/usb/phy/phy-samsung-usb3.c | 6 +- 4 files changed, 86 insertions(+), 49 deletions(-) diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c index 62cdb7e13d977d..c40ea321ae46ca 100644 --- a/drivers/usb/phy/phy-samsung-usb.c +++ b/drivers/usb/phy/phy-samsung-usb.c @@ -162,13 +162,76 @@ 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 @@ -183,52 +246,9 @@ int samsung_usbphy_get_refclk_freq(struct samsung_usbphy *sphy) 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; diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h index 70a9cae5e37fd0..0336f6b02bc436 100644 --- a/drivers/usb/phy/phy-samsung-usb.h +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -244,6 +244,8 @@ enum samsung_cpu_type { TYPE_EXYNOS5250, }; +struct samsung_usbphy; + /* * struct samsung_usbphy_drvdata - driver data for various SoC variants * @cpu_type: machine identifier @@ -268,6 +270,7 @@ 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); }; /* @@ -325,3 +328,7 @@ 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); diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c index 9d5e273abcc765..be6031d50a6d8e 100644 --- a/drivers/usb/phy/phy-samsung-usb2.c +++ b/drivers/usb/phy/phy-samsung-usb2.c @@ -408,7 +408,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 +441,21 @@ 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, }; 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, }; 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, }; #ifdef CONFIG_OF diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c index 5a9efcbcb532cc..ec44b35fb24a43 100644 --- a/drivers/usb/phy/phy-samsung-usb3.c +++ b/drivers/usb/phy/phy-samsung-usb3.c @@ -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,7 @@ 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, }; #ifdef CONFIG_OF From 3f339074edfa0d531edaa6bd095f718e58b50a7d Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 16 May 2013 11:57:09 +0200 Subject: [PATCH 31/95] usb: phy: samsung: Pass set_isolation callback through driver data This patch extends driver data structure with set_isolation callback, which allows to remove the need for checking for SoC type in a switch statement. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-samsung-usb.c | 36 +++++++----------------------- drivers/usb/phy/phy-samsung-usb.h | 4 +++- drivers/usb/phy/phy-samsung-usb2.c | 11 +++++---- drivers/usb/phy/phy-samsung-usb3.c | 7 ++++-- 4 files changed, 23 insertions(+), 35 deletions(-) diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c index c40ea321ae46ca..7a1ed90bd58e97 100644 --- a/drivers/usb/phy/phy-samsung-usb.c +++ b/drivers/usb/phy/phy-samsung-usb.c @@ -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); @@ -121,7 +101,7 @@ void samsung_usbphy_set_isolation(struct samsung_usbphy *sphy, bool on) writel(reg_val, reg); } -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. diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h index 0336f6b02bc436..52037841ff2e79 100644 --- a/drivers/usb/phy/phy-samsung-usb.h +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -271,6 +271,7 @@ struct samsung_usbphy_drvdata { u32 devphy_reg_offset; u32 hostphy_reg_offset; int (*rate_to_clksel)(struct samsung_usbphy *, unsigned long); + void (*set_isolation)(struct samsung_usbphy *, bool); }; /* @@ -323,7 +324,8 @@ 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); diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c index be6031d50a6d8e..a01247e03e5819 100644 --- a/drivers/usb/phy/phy-samsung-usb2.c +++ b/drivers/usb/phy/phy-samsung-usb2.c @@ -284,8 +284,8 @@ 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); @@ -342,8 +342,8 @@ static void samsung_usb2phy_shutdown(struct usb_phy *phy) /* 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); @@ -442,6 +442,7 @@ 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 */ }; static const struct samsung_usbphy_drvdata usb2phy_exynos4 = { @@ -449,6 +450,7 @@ static const struct samsung_usbphy_drvdata usb2phy_exynos4 = { .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, }; static struct samsung_usbphy_drvdata usb2phy_exynos5 = { @@ -456,6 +458,7 @@ static struct samsung_usbphy_drvdata usb2phy_exynos5 = { .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, }; #ifdef CONFIG_OF diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c index ec44b35fb24a43..3a30a5edde9884 100644 --- a/drivers/usb/phy/phy-samsung-usb3.c +++ b/drivers/usb/phy/phy-samsung-usb3.c @@ -184,7 +184,8 @@ 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); @@ -221,7 +222,8 @@ static void samsung_usb3phy_shutdown(struct usb_phy *phy) samsung_exynos5_usb3phy_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); @@ -304,6 +306,7 @@ 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, }; #ifdef CONFIG_OF From 84035f09adf81c2554bb39ff100651f167f7d384 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 16 May 2013 11:57:10 +0200 Subject: [PATCH 32/95] usb: phy: samsung: Pass enable/disable callbacks through driver data To remove unnecessary if statements, this patch introduces phy_enable and phy_disable callbacks in driver data structure that implement SoC-specific PHY initialization and deinitialization. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-samsung-usb.h | 2 ++ drivers/usb/phy/phy-samsung-usb2.c | 16 ++++++++-------- drivers/usb/phy/phy-samsung-usb3.c | 10 +++++----- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h index 52037841ff2e79..31e2ec3b2eb9a5 100644 --- a/drivers/usb/phy/phy-samsung-usb.h +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -272,6 +272,8 @@ struct samsung_usbphy_drvdata { 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 *); }; /* diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c index a01247e03e5819..1c3abd434b8547 100644 --- a/drivers/usb/phy/phy-samsung-usb2.c +++ b/drivers/usb/phy/phy-samsung-usb2.c @@ -291,10 +291,7 @@ static int samsung_usb2phy_init(struct usb_phy *phy) 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,10 +331,7 @@ 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) @@ -443,6 +437,8 @@ static const struct samsung_usbphy_drvdata usb2phy_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 = { @@ -451,6 +447,8 @@ static const struct samsung_usbphy_drvdata usb2phy_exynos4 = { .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 struct samsung_usbphy_drvdata usb2phy_exynos5 = { @@ -459,6 +457,8 @@ static struct samsung_usbphy_drvdata usb2phy_exynos5 = { .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 diff --git a/drivers/usb/phy/phy-samsung-usb3.c b/drivers/usb/phy/phy-samsung-usb3.c index 3a30a5edde9884..300e0cf5e31f4f 100644 --- a/drivers/usb/phy/phy-samsung-usb3.c +++ b/drivers/usb/phy/phy-samsung-usb3.c @@ -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) @@ -188,7 +186,7 @@ static int samsung_usb3phy_init(struct usb_phy *phy) 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); @@ -219,7 +217,7 @@ 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 */ if (sphy->drv_data->set_isolation) @@ -307,6 +305,8 @@ static struct samsung_usbphy_drvdata usb3phy_exynos5 = { .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 From 1b635f0ff3f7fcc15e2a5fdc430c99f88904d3c6 Mon Sep 17 00:00:00 2001 From: Tomasz Figa Date: Thu, 16 May 2013 11:57:11 +0200 Subject: [PATCH 33/95] usb: phy: samsung: Add support for USB 2.0 PHY on Exynos 4x12 This patch adds driver data for Exynos 4x12 USB 2.0 PHY. Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-samsung-usb.h | 1 + drivers/usb/phy/phy-samsung-usb2.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h index 31e2ec3b2eb9a5..585d12f5c04428 100644 --- a/drivers/usb/phy/phy-samsung-usb.h +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -241,6 +241,7 @@ enum samsung_cpu_type { TYPE_S3C64XX, TYPE_EXYNOS4210, + TYPE_EXYNOS4X12, TYPE_EXYNOS5250, }; diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c index 1c3abd434b8547..03180c06bfca08 100644 --- a/drivers/usb/phy/phy-samsung-usb2.c +++ b/drivers/usb/phy/phy-samsung-usb2.c @@ -177,6 +177,7 @@ static void samsung_usb2phy_enable(struct samsung_usbphy *sphy) rstcon |= RSTCON_SWRST; break; case TYPE_EXYNOS4210: + case TYPE_EXYNOS4X12: phypwr &= ~PHYPWR_NORMAL_MASK_PHY0; rstcon |= RSTCON_SWRST; default: @@ -240,6 +241,7 @@ static void samsung_usb2phy_disable(struct samsung_usbphy *sphy) phypwr |= PHYPWR_NORMAL_MASK; break; case TYPE_EXYNOS4210: + case TYPE_EXYNOS4X12: phypwr |= PHYPWR_NORMAL_MASK_PHY0; default: break; @@ -451,6 +453,16 @@ static const struct samsung_usbphy_drvdata usb2phy_exynos4 = { .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, @@ -469,6 +481,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 @@ -485,6 +500,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, From 2d2428c027c6b2a815f4a90aa6883c02088e1e5e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 17 May 2013 15:21:19 +0530 Subject: [PATCH 34/95] usb: gadget: mv_u3d_core: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Yu Xu Signed-off-by: Felipe Balbi --- drivers/usb/gadget/mv_u3d_core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 58288e9cf728e2..751b17af68f065 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -1786,8 +1786,6 @@ static int mv_u3d_remove(struct platform_device *dev) clk_put(u3d->clk); - platform_set_drvdata(dev, NULL); - kfree(u3d); return 0; @@ -1997,7 +1995,6 @@ static int mv_u3d_probe(struct platform_device *dev) err_get_cap_regs: err_get_clk: clk_put(u3d->clk); - platform_set_drvdata(dev, NULL); kfree(u3d); err_alloc_private: err_pdata: From e823aa7c6d36932e37370156eac143a6dde4affb Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 17 May 2013 15:21:20 +0530 Subject: [PATCH 35/95] usb: gadget: pxa27x_udc: Remove redundant platform_set_drvdata() Commit 0998d06310 (device-core: Ensure drvdata = NULL when no driver is bound) removes the need to set driver data field to NULL. Signed-off-by: Sachin Kamat Cc: Eric Miao Signed-off-by: Felipe Balbi --- drivers/usb/gadget/pxa27x_udc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index 6b4c7d95853f2e..41cea9566ac8bb 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -2505,7 +2505,6 @@ static int pxa_udc_remove(struct platform_device *_dev) usb_put_phy(udc->transceiver); udc->transceiver = NULL; - platform_set_drvdata(_dev, NULL); the_controller = NULL; clk_put(udc->clk); iounmap(udc->regs); From 7a22cc9709bb2cb0c4ffed1fa0979b8b4909f6b7 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 21 May 2013 17:17:22 +0530 Subject: [PATCH 36/95] usb: phy: phy-nop: Remove redundant use of of_match_ptr 'nop_xceiv_dt_ids' is always compiled in. Hence use of of_match_ptr is unnecessary. Acked-by: Alan Stern Signed-off-by: Sachin Kamat Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-nop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-nop.c b/drivers/usb/phy/phy-nop.c index 638cc5dade3522..55445e5d72e597 100644 --- a/drivers/usb/phy/phy-nop.c +++ b/drivers/usb/phy/phy-nop.c @@ -270,7 +270,7 @@ static struct platform_driver nop_usb_xceiv_driver = { .driver = { .name = "nop_usb_xceiv", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(nop_xceiv_dt_ids), + .of_match_table = nop_xceiv_dt_ids, }, }; From 6d3d61f8219455c047777e2f7954cd3cbac2ec4e Mon Sep 17 00:00:00 2001 From: Dongjin Kim Date: Wed, 22 May 2013 02:01:46 +0900 Subject: [PATCH 37/95] usb: phy: samsung: Add support HSIC on Exynos4X12 This patch adds to enable High Speed Inter Chip on Exynos4X12. Both channels are controlled by usbphy driver based on the patch series of usbphy driver submitted by Tomasz Figa. [1] https://patchwork.kernel.org/patch/2576121 [2] https://patchwork.kernel.org/patch/2576131 [3] https://patchwork.kernel.org/patch/2576141 [4] https://patchwork.kernel.org/patch/2576151 [5] https://patchwork.kernel.org/patch/2576161 [6] https://patchwork.kernel.org/patch/2576171 Signed-off-by: Dongjin Kim Cc: Tomasz Figa Cc: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-samsung-usb.c | 5 +++++ drivers/usb/phy/phy-samsung-usb.h | 10 ++++++++++ drivers/usb/phy/phy-samsung-usb2.c | 13 +++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-samsung-usb.c b/drivers/usb/phy/phy-samsung-usb.c index 7a1ed90bd58e97..ac025ca08425ee 100644 --- a/drivers/usb/phy/phy-samsung-usb.c +++ b/drivers/usb/phy/phy-samsung-usb.c @@ -100,6 +100,11 @@ void samsung_usbphy_set_isolation_4210(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_4210); diff --git a/drivers/usb/phy/phy-samsung-usb.h b/drivers/usb/phy/phy-samsung-usb.h index 585d12f5c04428..68771bfd18253d 100644 --- a/drivers/usb/phy/phy-samsung-usb.h +++ b/drivers/usb/phy/phy-samsung-usb.h @@ -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) diff --git a/drivers/usb/phy/phy-samsung-usb2.c b/drivers/usb/phy/phy-samsung-usb2.c index 03180c06bfca08..1011c16ade7e4b 100644 --- a/drivers/usb/phy/phy-samsung-usb2.c +++ b/drivers/usb/phy/phy-samsung-usb2.c @@ -176,8 +176,12 @@ static void samsung_usb2phy_enable(struct samsung_usbphy *sphy) phypwr &= ~PHYPWR_NORMAL_MASK; rstcon |= RSTCON_SWRST; break; - case TYPE_EXYNOS4210: 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; default: @@ -190,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); } @@ -240,8 +246,11 @@ static void samsung_usb2phy_disable(struct samsung_usbphy *sphy) case TYPE_S3C64XX: phypwr |= PHYPWR_NORMAL_MASK; break; - case TYPE_EXYNOS4210: 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: break; From dae8eadf2a8a125dbbfc067a7ff16cf600b1023b Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 23 May 2013 19:19:22 +0900 Subject: [PATCH 38/95] usb: gadget: use platform_{get,set}_drvdata() Use the wrapper functions for getting and setting the driver data using platform_device instead of using dev_{get,set}_drvdata() with &pdev->dev, so we can directly pass a struct platform_device. Signed-off-by: Jingoo Han Signed-off-by: Felipe Balbi --- drivers/usb/gadget/fsl_qe_udc.c | 4 ++-- drivers/usb/gadget/fusb300_udc.c | 4 ++-- drivers/usb/gadget/m66592-udc.c | 4 ++-- drivers/usb/gadget/mv_u3d_core.c | 2 +- drivers/usb/gadget/r8a66597-udc.c | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c index 9a7ee3347e4d92..f3bb363f1d4acf 100644 --- a/drivers/usb/gadget/fsl_qe_udc.c +++ b/drivers/usb/gadget/fsl_qe_udc.c @@ -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); diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c index b8632d40f8bffc..c83f3e16532582 100644 --- a/drivers/usb/gadget/fusb300_udc.c +++ b/drivers/usb/gadget/fusb300_udc.c @@ -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; diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index 51cfe72da5bb88..46ba9838c3a091 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -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; diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c index 751b17af68f065..07fdb3eaf48a4f 100644 --- a/drivers/usb/gadget/mv_u3d_core.c +++ b/drivers/usb/gadget/mv_u3d_core.c @@ -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); diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 7ff7d9cf2061d2..51ea1690ca5dbb 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -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; From 9e69fae1a89e543949fe81603f7229463b8061de Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 16 May 2013 19:42:58 +0530 Subject: [PATCH 39/95] usb: phy: tegra: Return correct error value provided by clk_get_sys In case if clk_get_sys fails, return correct error value provided by the API. Signed-off-by: Venu Byravarasu Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 17d811292f3a93..8bcc12f6804a03 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -620,8 +620,7 @@ static int tegra_phy_init(struct usb_phy *x) 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; + return PTR_ERR(phy->clk); } if (!gpio_is_valid(ulpi_config->reset_gpio)) ulpi_config->reset_gpio = From 9cd9384c73395f6ce78e1b9d529bc9f294fd5223 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 16 May 2013 19:42:59 +0530 Subject: [PATCH 40/95] usb: phy: tegra: Get PHY mode using DT Added a new PHY mode to support OTG. Obtained Tegra USB PHY mode using DT property. Signed-off-by: Venu Byravarasu Signed-off-by: Felipe Balbi --- drivers/usb/host/ehci-tegra.c | 1 - drivers/usb/phy/phy-tegra-usb.c | 13 +++++++++++-- include/linux/usb/tegra_usb_phy.h | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 59d111bf44a9d9..d64199044b4a88 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -736,7 +736,6 @@ static int tegra_ehci_probe(struct platform_device *pdev) 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)) { diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 8bcc12f6804a03..0ff1f3e03b1e3e 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -687,7 +687,7 @@ static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend) } 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 __iomem *regs, void *config, void (*set_pts)(struct usb_phy *x, u8 pts_val), void (*set_phcd)(struct usb_phy *x, bool enable)) @@ -705,7 +705,6 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, 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"); @@ -717,6 +716,16 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, else phy->is_ulpi_phy = true; + err = of_property_match_string(np, "dr_mode", "otg"); + if (err < 0) { + err = of_property_match_string(np, "dr_mode", "peripheral"); + if (err < 0) + phy->mode = TEGRA_USB_PHY_MODE_HOST; + else + phy->mode = TEGRA_USB_PHY_MODE_DEVICE; + } else + phy->mode = TEGRA_USB_PHY_MODE_OTG; + if (!phy->config) { if (phy->is_ulpi_phy) { pr_err("%s: ulpi phy configuration missing", __func__); diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 1b7519a8c0bf5e..ff2d95978b3855 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -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; @@ -66,7 +67,7 @@ struct tegra_usb_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 __iomem *regs, void *config, void (*set_pts)(struct usb_phy *x, u8 pts_val), void (*set_phcd)(struct usb_phy *x, bool enable)); From 12ea18e4f0bd793b7f9d7e8bf6c76815d5621ac3 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 16 May 2013 19:43:00 +0530 Subject: [PATCH 41/95] usb: phy: tegra: get ULPI reset GPIO info using DT. As GPIO information is avail through DT, used it to get Tegra ULPI reset GPIO number. Added a new member to tegra_usb_phy structure to store this number. Signed-off-by: Venu Byravarasu Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 25 +++++++++++-------------- include/linux/usb/tegra_usb_phy.h | 1 + 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 0ff1f3e03b1e3e..b8c688af3322fe 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -541,11 +541,10 @@ 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); + gpio_direction_output(phy->reset_gpio, 0); msleep(5); - gpio_direction_output(config->reset_gpio, 1); + gpio_direction_output(phy->reset_gpio, 1); clk_prepare_enable(phy->clk); msleep(1); @@ -603,10 +602,8 @@ 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); + return gpio_direction_output(phy->reset_gpio, 0); } static int tegra_phy_init(struct usb_phy *x) @@ -622,18 +619,18 @@ static int tegra_phy_init(struct usb_phy *x) pr_err("%s: can't get ulpi clock\n", __func__); return PTR_ERR(phy->clk); } - 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)) { + + phy->reset_gpio = + of_get_named_gpio(phy->dev->of_node, + "nvidia,phy-reset-gpio", 0); + if (!gpio_is_valid(phy->reset_gpio)) { pr_err("%s: invalid reset gpio: %d\n", __func__, - ulpi_config->reset_gpio); + phy->reset_gpio); err = -EINVAL; goto err1; } - gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b"); - gpio_direction_output(ulpi_config->reset_gpio, 0); + gpio_request(phy->reset_gpio, "ulpi_phy_reset_b"); + gpio_direction_output(phy->reset_gpio, 0); phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0); phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT; } else { diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index ff2d95978b3855..97d123c4d7cc14 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -62,6 +62,7 @@ struct tegra_usb_phy { struct device *dev; bool is_legacy_phy; bool is_ulpi_phy; + int reset_gpio; void (*set_pts)(struct usb_phy *x, u8 pts_val); void (*set_phcd)(struct usb_phy *x, bool enable); }; From 6829f92f6e64bfc6a553d7a2203ce1cb2e433c01 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 16 May 2013 19:43:01 +0530 Subject: [PATCH 42/95] usb: phy: tegra: Add error handling & clean up. Check return values from all GPIO APIs and handle errors accordingly. Remove the call to clk_disable_unprepare(); this function does not prepare or enable the clock, so the error path should not disable or unprepare it. Signed-off-by: Venu Byravarasu Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-tegra-usb.c | 50 +++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index b8c688af3322fe..f423ae88b741a0 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -542,9 +542,17 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy) unsigned long val; void __iomem *base = phy->regs; - gpio_direction_output(phy->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(phy->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); @@ -624,24 +632,44 @@ static int tegra_phy_init(struct usb_phy *x) of_get_named_gpio(phy->dev->of_node, "nvidia,phy-reset-gpio", 0); if (!gpio_is_valid(phy->reset_gpio)) { - pr_err("%s: invalid reset gpio: %d\n", __func__, + dev_err(phy->dev, "invalid gpio: %d\n", + phy->reset_gpio); + err = phy->reset_gpio; + goto cleanup_clk_get; + } + + err = gpio_request(phy->reset_gpio, "ulpi_phy_reset_b"); + if (err < 0) { + dev_err(phy->dev, "request failed for gpio: %d\n", phy->reset_gpio); - err = -EINVAL; - goto err1; + goto cleanup_clk_get; + } + + 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); + goto cleanup_gpio_req; } - gpio_request(phy->reset_gpio, "ulpi_phy_reset_b"); - gpio_direction_output(phy->reset_gpio, 0); + 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; + goto cleanup_gpio_req; + } + phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT; } else { err = utmip_pad_open(phy); if (err < 0) - goto err1; + return err; } return 0; -err1: - clk_disable_unprepare(phy->pll_u); - clk_put(phy->pll_u); +cleanup_gpio_req: + gpio_free(phy->reset_gpio); +cleanup_clk_get: + clk_put(phy->clk); return err; } From 2d22b42db02fdafeb7b990c2c25caabff4dd46fe Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 16 May 2013 19:43:02 +0530 Subject: [PATCH 43/95] usb: phy: registering Tegra USB PHY as platform driver Registered Tegra USB PHY as a separate platform driver. To synchronize host controller and PHY initialization, used deferred probe mechanism. As PHY should be initialized before EHCI starts running, deferred probe of Tegra EHCI driver till PHY probe gets completed. Got rid of instance number based handling in host driver. Made use of DT params to get the PHY Pad registers. Signed-off-by: Venu Byravarasu Signed-off-by: Felipe Balbi --- drivers/usb/host/ehci-tegra.c | 104 +++++----- drivers/usb/phy/phy-tegra-usb.c | 325 +++++++++++++++++------------- include/linux/usb/tegra_usb_phy.h | 11 +- 3 files changed, 230 insertions(+), 210 deletions(-) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index d64199044b4a88..8390c870299acd 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -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,57 +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; - } - - /* 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; - } + goto cleanup_hcd_create; } - tegra->phy = tegra_usb_phy_open(&pdev->dev, instance, hcd->regs, - pdata->phy_config, - 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; + err = usb_phy_init(hcd->phy); + if (err) { + dev_err(&pdev->dev, "Failed to initialize phy\n"); + goto cleanup_hcd_create; } - 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; @@ -769,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) { @@ -781,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); @@ -797,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; } diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index f423ae88b741a0..5d9af11d27319f 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -1,9 +1,11 @@ /* * Copyright (C) 2010 Google, Inc. + * Copyright (C) 2013 NVIDIA Corporation * * Author: * Erik Gilling * Benoit Goby + * Venu Byravarasu * * 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 #include #include - -#define TEGRA_USB_BASE 0xC5000000 -#define TEGRA_USB_SIZE SZ_16K +#include #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; } @@ -614,76 +597,11 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy) return gpio_direction_output(phy->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__); - return PTR_ERR(phy->clk); - } - - phy->reset_gpio = - of_get_named_gpio(phy->dev->of_node, - "nvidia,phy-reset-gpio", 0); - if (!gpio_is_valid(phy->reset_gpio)) { - dev_err(phy->dev, "invalid gpio: %d\n", - phy->reset_gpio); - err = phy->reset_gpio; - goto cleanup_clk_get; - } - - err = gpio_request(phy->reset_gpio, "ulpi_phy_reset_b"); - if (err < 0) { - dev_err(phy->dev, "request failed for gpio: %d\n", - phy->reset_gpio); - goto cleanup_clk_get; - } - - 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); - goto cleanup_gpio_req; - } - - 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; - goto cleanup_gpio_req; - } - - phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT; - } else { - err = utmip_pad_open(phy); - if (err < 0) - return err; - } - return 0; -cleanup_gpio_req: - gpio_free(phy->reset_gpio); -cleanup_clk_get: - clk_put(phy->clk); - return err; -} - 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) @@ -711,63 +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, - 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) { - 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->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); + } - phy->instance = instance; - phy->regs = regs; - phy->config = config; - 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; + 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 = of_property_match_string(np, "dr_mode", "otg"); + err = gpio_direction_output(phy->reset_gpio, 0); if (err < 0) { - err = of_property_match_string(np, "dr_mode", "peripheral"); - if (err < 0) - phy->mode = TEGRA_USB_PHY_MODE_HOST; + 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) +{ + unsigned long parent_rate; + int i; + int err; + + if (!phy->is_ulpi_phy) { + if (phy->is_legacy_phy) + phy->config = &utmip_default[0]; else - phy->mode = TEGRA_USB_PHY_MODE_DEVICE; - } else - phy->mode = TEGRA_USB_PHY_MODE_OTG; - - 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]; - } + 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++) { @@ -779,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) { @@ -834,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); diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 97d123c4d7cc14..0cd15d2df53df7 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -63,14 +63,9 @@ struct tegra_usb_phy { bool is_legacy_phy; bool is_ulpi_phy; int reset_gpio; - void (*set_pts)(struct usb_phy *x, u8 pts_val); - void (*set_phcd)(struct usb_phy *x, bool enable); }; -struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance, - void __iomem *regs, void *config, - 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); @@ -81,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 */ From 91e3af64c77f333155e253e9c399c746e087855f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 30 May 2013 03:06:27 +0300 Subject: [PATCH 44/95] usb: musb: host: fix build warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit very minor patch fixing the following build warning on a debug message: drivers/usb/musb/musb_host.c: In function ‘musb_host_rx’: drivers/usb/musb/musb_host.c:1763:4: warning: format ‘%x’ \ expects argument of type ‘unsigned int’, but argument \ 6 has type ‘dma_addr_t’ [-Wformat] Signed-off-by: Felipe Balbi --- drivers/usb/musb/musb_host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 1e370eec9100f2..47ebde88a8054b 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1760,10 +1760,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); From f016a16d8c1f6cd0567a88d0a5843aab385e969a Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Thu, 30 May 2013 18:19:40 +0800 Subject: [PATCH 45/95] usb: dwc3: remove redundant D0 power state set Pci_enable_device() will set device power state to D0, so it's no need to do it again in dwc3_pci_probe(). Signed-off-by: Yijing Wang Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 227d4a7acad759..c06860831c78df 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -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); From 81fbf101f2858e63bbb380447a76870924b84653 Mon Sep 17 00:00:00 2001 From: Ruchika Kharwar Date: Thu, 30 May 2013 14:54:09 -0500 Subject: [PATCH 46/95] usb: phy: omap-usb3: updated dpll M,N values to support DRA7xx devices Addition of the M and N recommended values for the USB3 PHY DPLL. Sysclk for DRA7xx is 20MHz. This yields: Clk = 20MHz * M/(N+1) = 20MHz * 1000 /(7+1) = 2.5 Ghz Signed-off-by: Ruchika Kharwar Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-omap-usb3.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-omap-usb3.c b/drivers/usb/phy/phy-omap-usb3.c index a6e60b1e102e84..efe6e1464f45b5 100644 --- a/drivers/usb/phy/phy-omap-usb3.c +++ b/drivers/usb/phy/phy-omap-usb3.c @@ -27,7 +27,7 @@ #include #include -#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: From a2cc81d315c45cce35c0e50716fbee5dc00e28aa Mon Sep 17 00:00:00 2001 From: Michael Grzeschik Date: Sat, 9 Feb 2013 00:54:54 +0100 Subject: [PATCH 47/95] usb: gadget: uvc: Implement videobuf2 .wait_prepare and .wait_finish operations Those optional operations are used to release and reacquire the queue lock when videobuf2 needs to perform operations that sleep for a long time, such as waiting for a buffer to be complete. Implement them to avoid blocking qbuf or streamoff calls when a dqbuf is in progress. Signed-off-by: Michael Grzeschik Signed-off-by: Felipe Balbi --- drivers/usb/gadget/uvc_queue.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index 7ce27e35550b7e..e6170478ea9f73 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -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, From f37d49ad6e3d351fcf3cd042953ff273b6cd47e0 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Thu, 30 May 2013 00:51:37 +0400 Subject: [PATCH 48/95] usb: gadget: r8a66597-udc: do not unlock unheld spinlock in r8a66597_sudmac_irq() r8a66597_irq() processes sudmac part (r8a66597_sudmac_irq()) before locking r8a66597->lock. But transfer_complete(), that is called inside (r8a66597_sudmac_irq()->sudmac_finish()->transfer_complete()), expects r8a66597->lock is locked. As a result unheld spinlock can be unlocked. The patch just moves locking before calling r8a66597_sudmac_irq(). Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Felipe Balbi --- drivers/usb/gadget/r8a66597-udc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c index 51ea1690ca5dbb..c6af649f324008 100644 --- a/drivers/usb/gadget/r8a66597-udc.c +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -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); From eb127cb519d73c0d0e8089dcbde8c94dc1fc7e20 Mon Sep 17 00:00:00 2001 From: Jassi Brar Date: Thu, 30 May 2013 18:23:33 +0530 Subject: [PATCH 49/95] USB: gadget: f_uac2: Fix broken prm to uac2 mapping prm_to_uac2() is broken because it tests against pointer it itself mapped onto, which will never be different. Fix the mapping by adding pointer to parent chip in each rtd param and removing the prm_to_uac2(). Reported-by: Julien Rouviere Signed-off-by: Jassi Brar Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_uac2.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c index 03c1fb686644e0..2f23566e53d88b 100644 --- a/drivers/usb/gadget/f_uac2.c +++ b/drivers/usb/gadget/f_uac2.c @@ -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; From d68c277b501889b3a50c179d1c3d704db7947b83 Mon Sep 17 00:00:00 2001 From: UCHINO Satoshi Date: Thu, 23 May 2013 11:10:11 +0900 Subject: [PATCH 50/95] usb: gadget: f_mass_storage: add missing memory barrier for thread_wakeup_needed Without this memory barrier, the file-storage thread may fail to escape from the following while loop, because it may observe new common->thread_wakeup_needed and old bh->state which are updated by the callback functions. /* Wait for the CBW to arrive */ while (bh->state != BUF_STATE_FULL) { rc = sleep_thread(common); if (rc) return rc; } Cc: stable@vger.kernel.org Signed-off-by: UCHINO Satoshi Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_mass_storage.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 97666e8b1b9554..c35a9ecc576bb9 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -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; } From f1a1823ff24fa4e3412b5078f20021cf40834946 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 09:22:03 +0200 Subject: [PATCH 51/95] usb: gadget: u_ether: convert into module u_ether.c has been #include'd by all gadgets which implement USB Ethernet functions. In order to add configfs support, the f_ecm.c, f_eem.c, f_ncm.c, f_subset.c, f_rndis.c need to be converted into modules and must not be #include'd. Consequently, the u_ether.c needs to be a module too, in a manner similar to u_serial.c. The resulting module should not take any parameters, so they are pushed to the current users of it, that is ether.c, g_ffs.c, multi.c, ncm.c, nokia.c. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 10 ++++++++++ drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/cdc2.c | 10 ++++++---- drivers/usb/gadget/ether.c | 14 +++++++------ drivers/usb/gadget/g_ffs.c | 13 +++++++----- drivers/usb/gadget/multi.c | 13 +++++++----- drivers/usb/gadget/ncm.c | 10 ++++++---- drivers/usb/gadget/nokia.c | 11 +++++++---- drivers/usb/gadget/u_ether.c | 38 +++++++++++++++++------------------- drivers/usb/gadget/u_ether.h | 30 ++++++++++++++++++++++++---- 10 files changed, 98 insertions(+), 52 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f9bf7283e538de..d5ae4dff3b90cf 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -499,6 +499,9 @@ config USB_F_SS_LB config USB_U_SERIAL tristate +config USB_U_ETHER + tristate + config USB_F_SERIAL tristate @@ -595,6 +598,7 @@ config USB_ETH tristate "Ethernet Gadget (with CDC Ethernet support)" depends on NET select USB_LIBCOMPOSITE + select USB_U_ETHER select CRC32 help This driver implements Ethernet style communication, in one of @@ -667,6 +671,7 @@ config USB_G_NCM tristate "Network Control Model (NCM) support" depends on NET select USB_LIBCOMPOSITE + select USB_U_ETHER select CRC32 help This driver implements USB CDC NCM subclass standard. NCM is @@ -710,6 +715,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. @@ -717,6 +723,7 @@ config USB_FUNCTIONFS_ETH config USB_FUNCTIONFS_RNDIS bool "Include configuration with RNDIS (Ethernet)" depends on USB_FUNCTIONFS && NET + select USB_U_ETHER help Include a configuration with RNDIS function (Ethernet) and the Filesystem. @@ -817,6 +824,7 @@ config USB_CDC_COMPOSITE depends on NET select USB_LIBCOMPOSITE select USB_U_SERIAL + select USB_U_ETHER select USB_F_ACM help This driver provides two functions in one configuration: @@ -834,6 +842,7 @@ config USB_G_NOKIA depends on PHONET select USB_LIBCOMPOSITE select USB_U_SERIAL + select USB_U_ETHER select USB_F_ACM help The Nokia composite gadget provides support for acm, obex @@ -861,6 +870,7 @@ 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_F_ACM help The Multifunction Composite Gadget provides Ethernet (RNDIS diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 6afd16659e78d7..b6c2bf7a3c2d98 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -45,6 +45,7 @@ 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 # # USB gadget drivers diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index 2c52551827692c..bffa997fd0406d 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -35,6 +35,8 @@ /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); +USB_ETHERNET_MODULE_PARAMETERS(); + /* * Kbuild is not very cooperative with respect to linking separately * compiled library objects into one module. So for now we won't use @@ -43,7 +45,6 @@ USB_GADGET_COMPOSITE_OPTIONS(); * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ #include "f_ecm.c" -#include "u_ether.c" /*-------------------------------------------------------------------------*/ @@ -102,7 +103,7 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -static u8 hostaddr[ETH_ALEN]; +static u8 host_mac[ETH_ALEN]; static struct eth_dev *the_dev; /*-------------------------------------------------------------------------*/ static struct usb_function *f_acm; @@ -120,7 +121,7 @@ static int __init cdc_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - status = ecm_bind_config(c, hostaddr, the_dev); + status = ecm_bind_config(c, host_mac, the_dev); if (status < 0) return status; @@ -166,7 +167,8 @@ static int __init cdc_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); diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 56c8ecae9bc321..75418c7050fb97 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -107,11 +107,12 @@ static inline bool has_rndis(void) #include "rndis.c" #endif #include "f_eem.c" -#include "u_ether.c" /*-------------------------------------------------------------------------*/ 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,7 +207,7 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -static u8 hostaddr[ETH_ALEN]; +static u8 host_mac[ETH_ALEN]; static struct eth_dev *the_dev; /*-------------------------------------------------------------------------*/ @@ -224,7 +225,7 @@ static int __init rndis_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return rndis_bind_config(c, hostaddr, the_dev); + return rndis_bind_config(c, host_mac, the_dev); } static struct usb_configuration rndis_config_driver = { @@ -259,9 +260,9 @@ static int __init eth_do_config(struct usb_configuration *c) 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); + return ecm_bind_config(c, host_mac, the_dev); else - return geth_bind_config(c, hostaddr, the_dev); + return geth_bind_config(c, host_mac, the_dev); } static struct usb_configuration eth_config_driver = { @@ -279,7 +280,8 @@ static int __init eth_bind(struct usb_composite_dev *cdev) int status; /* 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); diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 787a78e92aa2d2..45f26be640def2 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -34,9 +34,9 @@ # include "f_rndis.c" # include "rndis.c" # 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 +45,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 +73,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 +352,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 +449,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; } diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 4a45e80c6e384f..cdb8dbf34c8dc7 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -49,10 +49,12 @@ MODULE_LICENSE("GPL"); # include "f_rndis.c" # include "rndis.c" #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 +135,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 +154,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 +218,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 +282,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); diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index 3b02fd4649ce9f..e411135b21e528 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -37,7 +37,6 @@ * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ #include "f_ncm.c" -#include "u_ether.c" /*-------------------------------------------------------------------------*/ @@ -54,6 +53,8 @@ /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); +USB_ETHERNET_MODULE_PARAMETERS(); + static struct usb_device_descriptor device_desc = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -112,7 +113,7 @@ static struct usb_gadget_strings *dev_strings[] = { }; struct eth_dev *the_dev; -static u8 hostaddr[ETH_ALEN]; +static u8 host_mac[ETH_ALEN]; /*-------------------------------------------------------------------------*/ @@ -125,7 +126,7 @@ static int __init ncm_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return ncm_bind_config(c, hostaddr, the_dev); + return ncm_bind_config(c, host_mac, the_dev); } static struct usb_configuration ncm_config_driver = { @@ -144,7 +145,8 @@ static int __init gncm_bind(struct usb_composite_dev *cdev) int status; /* 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); diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 3b344b41a167e2..39f6cb5f984d53 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -41,11 +41,13 @@ #include "f_ecm.c" #include "f_obex.c" #include "f_phonet.c" -#include "u_ether.c" +#include "u_ether.h" /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); +USB_ETHERNET_MODULE_PARAMETERS(); + #define NOKIA_VENDOR_ID 0x0421 /* Nokia */ #define NOKIA_PRODUCT_ID 0x01c8 /* Nokia Gadget */ @@ -98,7 +100,7 @@ MODULE_LICENSE("GPL"); /*-------------------------------------------------------------------------*/ static struct usb_function *f_acm_cfg1; static struct usb_function *f_acm_cfg2; -static u8 hostaddr[ETH_ALEN]; +static u8 host_mac[ETH_ALEN]; static struct eth_dev *the_dev; enum { @@ -152,7 +154,7 @@ static int __init nokia_bind_config(struct usb_configuration *c) if (status) goto err_conf; - status = ecm_bind_config(c, hostaddr, the_dev); + status = ecm_bind_config(c, host_mac, the_dev); if (status) { pr_debug("could not bind ecm config %d\n", status); goto err_ecm; @@ -186,7 +188,8 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) goto err_ether; } - 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)) { status = PTR_ERR(the_dev); goto err_ether; diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 4b76124ce96b8d..5f9dacfe6be5f6 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -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 *, @@ -84,12 +86,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 +586,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 +695,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) { @@ -755,8 +743,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 +766,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)) @@ -815,6 +805,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN], return dev; } +EXPORT_SYMBOL(gether_setup_name); /** * gether_cleanup - remove Ethernet-over-USB device @@ -831,6 +822,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 +865,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 +903,7 @@ struct net_device *gether_connect(struct gether *link) return ERR_PTR(result); return dev->net; } +EXPORT_SYMBOL(gether_connect); /** * gether_disconnect - notify network layer that USB link is inactive @@ -980,3 +974,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"); diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 02522338a7081a..02f58acdd5e988 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -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,9 +109,10 @@ 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"); } void gether_cleanup(struct eth_dev *dev); From cbbd14a9021140a306a01f8fcaa645faafae18a5 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Fri, 24 May 2013 10:23:02 +0200 Subject: [PATCH 52/95] usb: gadget: rndis: convert into module In order to convert to configfs the usb functions need to be converted to a new interface and compiled as modules. This patch creates an rndis module which will be used by the new functions. After all users of f_rndis are converted to the new interface, this module can be merged with f_rndis module. Acked-by: Michal Nazarewicz Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 6 ++++++ drivers/usb/gadget/Makefile | 2 ++ drivers/usb/gadget/ether.c | 4 +++- drivers/usb/gadget/g_ffs.c | 2 +- drivers/usb/gadget/multi.c | 2 +- drivers/usb/gadget/rndis.c | 18 ++++++++++++++++++ drivers/usb/gadget/rndis.h | 1 + 7 files changed, 32 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index d5ae4dff3b90cf..5345b689522074 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -502,6 +502,9 @@ config USB_U_SERIAL config USB_U_ETHER tristate +config USB_U_RNDIS + tristate + config USB_F_SERIAL tristate @@ -599,6 +602,7 @@ config USB_ETH depends on NET select USB_LIBCOMPOSITE select USB_U_ETHER + select USB_U_RNDIS select CRC32 help This driver implements Ethernet style communication, in one of @@ -724,6 +728,7 @@ 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. @@ -871,6 +876,7 @@ config USB_G_MULTI 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 diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index b6c2bf7a3c2d98..7a0463ef3684d7 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -46,6 +46,8 @@ 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 gadget drivers diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 75418c7050fb97..6bff24f193a2a9 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -91,6 +91,8 @@ static inline bool has_rndis(void) #endif } +#include + /*-------------------------------------------------------------------------*/ /* @@ -104,7 +106,7 @@ static inline bool has_rndis(void) #include "f_subset.c" #ifdef USB_ETH_RNDIS #include "f_rndis.c" -#include "rndis.c" +#include "rndis.h" #endif #include "f_eem.c" diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 45f26be640def2..fbfdb53a2db5f8 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -32,7 +32,7 @@ # include "f_subset.c" # ifdef USB_ETH_RNDIS # include "f_rndis.c" -# include "rndis.c" +# include "rndis.h" # endif # include "u_ether.h" diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index cdb8dbf34c8dc7..ce21e9f8203ee8 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -47,7 +47,7 @@ MODULE_LICENSE("GPL"); #include "f_subset.c" #ifdef USB_ETH_RNDIS # include "f_rndis.c" -# include "rndis.c" +# include "rndis.h" #endif #include "u_ether.h" diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 1e4cfb05f70b9a..8c5e95762e59a2 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -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; } +EXPORT_SYMBOL(rndis_init); void rndis_exit(void) { @@ -1173,3 +1188,6 @@ void rndis_exit(void) } #endif } +EXPORT_SYMBOL(rndis_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 0647f2f34e8987..6e796152a7b2f6 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -16,6 +16,7 @@ #define _LINUX_RNDIS_H #include +#include "u_ether.h" #include "ndis.h" #define RNDIS_MAXIMUM_FRAME_SIZE 1518 From bcd4a1c40bee885e5266ca53de05d985a5518d7a Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 09:22:05 +0200 Subject: [PATCH 53/95] usb: gadget: u_ether: construct with default values and add setters/getters Add an interface to create a struct netdev_dev filled with default values, an interface which makes it an interface to fill the struct with useful values and an interface to read the values set. The patch also adds an interface to register the net device associated with an ethernet-over-usb link. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/u_ether.c | 185 ++++++++++++++++++++++++++++++++++- drivers/usb/gadget/u_ether.h | 123 +++++++++++++++++++++++ 2 files changed, 307 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 5f9dacfe6be5f6..6d3ccdc09e1eaa 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -78,6 +78,7 @@ struct eth_dev { bool zlp; u8 host_mac[ETH_ALEN]; + u8 dev_mac[ETH_ALEN]; }; /*-------------------------------------------------------------------------*/ @@ -716,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, @@ -796,7 +808,8 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, 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" */ @@ -807,6 +820,176 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g, } 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_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 * Context: may sleep diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 02f58acdd5e988..d74b8f7214a43c 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -115,6 +115,129 @@ static inline struct eth_dev *gether_setup(struct usb_gadget *g, 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_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 */ From 40d133d7f542616cf9538508a372306e626a16e9 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 09:22:06 +0200 Subject: [PATCH 54/95] usb: gadget: f_ncm: convert to new function interface with backward compatibility Converting ncm to the new function interface requires converting the USB ncm's function code and its users. This patch converts the f_ncm.c to the new function interface. The file is now compiled into a separate usb_f_ncm.ko module. The old function interface is provided by means of a preprocessor conditional directives. After all users are converted, the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_ncm.c | 196 +++++++++++++++++++++++++++++------- drivers/usb/gadget/ncm.c | 1 + drivers/usb/gadget/u_ncm.h | 27 +++++ 5 files changed, 194 insertions(+), 35 deletions(-) create mode 100644 drivers/usb/gadget/u_ncm.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5345b689522074..127292ffb4d995 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -511,6 +511,9 @@ config USB_F_SERIAL config USB_F_OBEX tristate +config USB_F_NCM + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 7a0463ef3684d7..34b117eec36eee 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -48,6 +48,8 @@ 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 gadget drivers diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index ee19bc8d0040c1..722ca1b0dda085 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -23,6 +24,7 @@ #include #include "u_ether.h" +#include "u_ncm.h" /* * This function is a "CDC Network Control Model" (CDC NCM) Ethernet link. @@ -125,7 +127,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 +141,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 +153,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 +161,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 +169,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 +184,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 +196,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 +211,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 +226,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 +236,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 +244,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 +252,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 +271,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 +280,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 +289,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 +298,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,14 +1154,50 @@ 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); int status; struct usb_ep *ep; +#ifndef USB_FNCM_INCLUDED + 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) { + gether_set_gadget(ncm_opts->net, cdev->gadget); + status = gether_register_netdev(ncm_opts->net); + if (status) + return status; + ncm_opts->bound = true; + } +#endif + 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; + } + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -1259,8 +1297,10 @@ ncm_bind(struct usb_configuration *c, struct usb_function *f) return status; } +#ifdef USB_FNCM_INCLUDED + static void -ncm_unbind(struct usb_configuration *c, struct usb_function *f) +ncm_old_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_ncm *ncm = func_to_ncm(f); @@ -1296,21 +1336,6 @@ int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], 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; - } - /* allocate and initialize one new instance */ ncm = kzalloc(sizeof *ncm, GFP_KERNEL); if (!ncm) @@ -1329,7 +1354,7 @@ int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], ncm->port.func.strings = ncm_strings; /* descriptors are per-instance copies */ ncm->port.func.bind = ncm_bind; - ncm->port.func.unbind = ncm_unbind; + ncm->port.func.unbind = ncm_old_unbind; ncm->port.func.set_alt = ncm_set_alt; ncm->port.func.get_alt = ncm_get_alt; ncm->port.func.setup = ncm_setup; @@ -1343,3 +1368,104 @@ int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], kfree(ncm); return status; } + +#else + +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); + 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)); + + return &opts->func_inst; +} + +static void ncm_free(struct usb_function *f) +{ + struct f_ncm *ncm; + + ncm = func_to_ncm(f); + kfree(ncm); +} + +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); +} + +struct usb_function *ncm_alloc(struct usb_function_instance *fi) +{ + struct f_ncm *ncm; + struct f_ncm_opts *opts; + int status; + + /* allocate and initialize one new instance */ + ncm = kzalloc(sizeof(*ncm), GFP_KERNEL); + if (!ncm) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_ncm_opts, func_inst); + + /* export host's Ethernet address in CDC format */ + 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 = netdev_priv(opts->net); + 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; + ncm->port.func.set_alt = ncm_set_alt; + 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; + + return &ncm->port.func; +} + +DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yauheni Kaliuta"); + +#endif + diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index e411135b21e528..e2f97eecd7cea5 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -36,6 +36,7 @@ * the runtime footprint, and giving us at least some parts of what * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ +#define USB_FNCM_INCLUDED #include "f_ncm.c" /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/u_ncm.h b/drivers/usb/gadget/u_ncm.h new file mode 100644 index 00000000000000..1e22b5fa844b34 --- /dev/null +++ b/drivers/usb/gadget/u_ncm.h @@ -0,0 +1,27 @@ +/* + * u_ncm.h + * + * Utility definitions for the ncm function + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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 + +struct f_ncm_opts { + struct usb_function_instance func_inst; + struct net_device *net; + bool bound; +}; + +#endif /* U_NCM_H */ From 9575bcf9c0c98350c61964214c0c029dfed0c32e Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 09:22:07 +0200 Subject: [PATCH 55/95] usb: gadget: ncm: convert to new function interface Utilize our new configfs-based interface. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/ncm.c | 57 +++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 127292ffb4d995..bf7ad7317b5d63 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -679,6 +679,7 @@ config USB_G_NCM 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 diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c index e2f97eecd7cea5..81956feca1bd84 100644 --- a/drivers/usb/gadget/ncm.c +++ b/drivers/usb/gadget/ncm.c @@ -24,23 +24,12 @@ #include #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. - */ -#define USB_FNCM_INCLUDED -#include "f_ncm.c" - -/*-------------------------------------------------------------------------*/ - /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! * Instead: allocate your own, using normal USB-IF procedures. */ @@ -113,13 +102,15 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -struct eth_dev *the_dev; -static u8 host_mac[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)) { @@ -127,7 +118,19 @@ static int __init ncm_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return ncm_bind_config(c, host_mac, 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 = { @@ -143,13 +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, dev_addr, host_addr, host_mac, - qmult); - 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. @@ -172,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; } From dd67a17f2b8866d165699e4581ae8847ef8c0fe6 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 09:22:08 +0200 Subject: [PATCH 56/95] usb: gadget: f_ncm: remove compatibility layer There are no old function interface users left, so the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_ncm.c | 80 ------------------------------------ drivers/usb/gadget/u_ether.h | 2 - 2 files changed, 82 deletions(-) diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index 722ca1b0dda085..d8069de4f9d8bb 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -1160,8 +1160,6 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) struct f_ncm *ncm = func_to_ncm(f); int status; struct usb_ep *ep; - -#ifndef USB_FNCM_INCLUDED struct f_ncm_opts *ncm_opts; if (!can_support_ecm(cdev->gadget)) @@ -1182,7 +1180,6 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) return status; ncm_opts->bound = true; } -#endif if (ncm_string_defs[0].id == 0) { status = usb_string_ids_tab(c->cdev, ncm_string_defs); if (status < 0) @@ -1297,80 +1294,6 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) return status; } -#ifdef USB_FNCM_INCLUDED - -static void -ncm_old_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 f_ncm *ncm; - int status; - - if (!can_support_ecm(c->cdev->gadget) || !ethaddr) - return -EINVAL; - - /* allocate and initialize one new instance */ - ncm = kzalloc(sizeof *ncm, GFP_KERNEL); - if (!ncm) - return -ENOMEM; - - /* export host's Ethernet address in CDC format */ - snprintf(ncm->ethaddr, sizeof ncm->ethaddr, "%pm", ethaddr); - ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr; - - spin_lock_init(&ncm->lock); - ncm_reset_values(ncm); - ncm->port.ioport = dev; - 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_old_unbind; - ncm->port.func.set_alt = ncm_set_alt; - ncm->port.func.get_alt = ncm_get_alt; - ncm->port.func.setup = ncm_setup; - ncm->port.func.disable = ncm_disable; - - 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; -} - -#else - static void ncm_free_inst(struct usb_function_instance *f) { struct f_ncm_opts *opts; @@ -1466,6 +1389,3 @@ struct usb_function *ncm_alloc(struct usb_function_instance *fi) DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Yauheni Kaliuta"); - -#endif - diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index d74b8f7214a43c..1671a7963d011f 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -262,8 +262,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 From 8feffd0030fef652224439ac6facbfbf6ba85139 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 09:22:09 +0200 Subject: [PATCH 57/95] usb: gadget: f_ncm: use usb_gstrings_attach Trivial patch making use of the new usb_gstrings_attach interface. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_ncm.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index d8069de4f9d8bb..effd2fada9251b 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -1158,6 +1158,7 @@ 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; @@ -1180,20 +1181,15 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) return status; ncm_opts->bound = true; } - 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; - } + 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); @@ -1335,7 +1331,6 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "ncm unbind\n"); - ncm_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(ncm->notify_req->buf); @@ -1370,7 +1365,6 @@ struct usb_function *ncm_alloc(struct usb_function_instance *fi) 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; From e730660378be92b83288b59b824ccdace5cd2652 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 09:22:10 +0200 Subject: [PATCH 58/95] usb: gadget: f_ncm: add configfs support Add configfs support to the NCM function driver so that we can, eventually, get rid of kernel-based gadget drivers. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_ncm.c | 166 +++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/u_ncm.h | 9 ++ 2 files changed, 175 insertions(+) diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index effd2fada9251b..697262e2a57531 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -1175,8 +1175,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) * 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; @@ -1290,6 +1292,159 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) return status; } +static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct f_ncm_opts, + func_inst.group); +} + +CONFIGFS_ATTR_STRUCT(f_ncm_opts); +CONFIGFS_ATTR_OPS(f_ncm_opts); + +static void ncm_attr_release(struct config_item *item) +{ + struct f_ncm_opts *opts = to_f_ncm_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations ncm_item_ops = { + .release = ncm_attr_release, + .show_attribute = f_ncm_opts_attr_show, + .store_attribute = f_ncm_opts_attr_store, +}; + +static ssize_t ncm_opts_dev_addr_show(struct f_ncm_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 ncm_opts_dev_addr_store(struct f_ncm_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_ncm_opts_attribute f_ncm_opts_dev_addr = + __CONFIGFS_ATTR(dev_addr, S_IRUGO | S_IWUSR, ncm_opts_dev_addr_show, + ncm_opts_dev_addr_store); + +static ssize_t ncm_opts_host_addr_show(struct f_ncm_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 ncm_opts_host_addr_store(struct f_ncm_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_ncm_opts_attribute f_ncm_opts_host_addr = + __CONFIGFS_ATTR(host_addr, S_IRUGO | S_IWUSR, ncm_opts_host_addr_show, + ncm_opts_host_addr_store); + +static ssize_t ncm_opts_qmult_show(struct f_ncm_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 ncm_opts_qmult_store(struct f_ncm_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_ncm_opts_attribute f_ncm_opts_qmult = + __CONFIGFS_ATTR(qmult, S_IRUGO | S_IWUSR, ncm_opts_qmult_show, + ncm_opts_qmult_store); + +static ssize_t ncm_opts_ifname_show(struct f_ncm_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_ncm_opts_attribute f_ncm_opts_ifname = + __CONFIGFS_ATTR_RO(ifname, ncm_opts_ifname_show); + +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; @@ -1309,20 +1464,28 @@ static struct usb_function_instance *ncm_alloc_inst(void) 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) @@ -1349,6 +1512,8 @@ struct usb_function *ncm_alloc(struct usb_function_instance *fi) 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 */ status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr, @@ -1362,6 +1527,7 @@ struct usb_function *ncm_alloc(struct usb_function_instance *fi) spin_lock_init(&ncm->lock); ncm_reset_values(ncm); ncm->port.ioport = netdev_priv(opts->net); + mutex_unlock(&opts->lock); ncm->port.is_fixed = true; ncm->port.func.name = "cdc_network"; diff --git a/drivers/usb/gadget/u_ncm.h b/drivers/usb/gadget/u_ncm.h index 1e22b5fa844b34..ce0f3a78ca1394 100644 --- a/drivers/usb/gadget/u_ncm.h +++ b/drivers/usb/gadget/u_ncm.h @@ -22,6 +22,15 @@ 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 */ From aa83c6adf753cc46e7898605ceb33dfa98eb63fa Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:32:02 +0200 Subject: [PATCH 59/95] usb: gadget: add helpers for configfs support for USB Ethernet All USB Ethernet functions will have very similar attributes in configfs. This patch provides helper definitions to ease writing the functions and reduce source code duplication. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_ncm.c | 139 ++-------------------- drivers/usb/gadget/u_ether_configfs.h | 164 ++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 128 deletions(-) create mode 100644 drivers/usb/gadget/u_ether_configfs.h diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index 697262e2a57531..47a5724211cd35 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c @@ -24,6 +24,7 @@ #include #include "u_ether.h" +#include "u_ether_configfs.h" #include "u_ncm.h" /* @@ -1298,138 +1299,20 @@ static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item) func_inst.group); } -CONFIGFS_ATTR_STRUCT(f_ncm_opts); -CONFIGFS_ATTR_OPS(f_ncm_opts); +/* f_ncm_item_ops */ +USB_ETHERNET_CONFIGFS_ITEM(ncm); -static void ncm_attr_release(struct config_item *item) -{ - struct f_ncm_opts *opts = to_f_ncm_opts(item); - - usb_put_function_instance(&opts->func_inst); -} - -static struct configfs_item_operations ncm_item_ops = { - .release = ncm_attr_release, - .show_attribute = f_ncm_opts_attr_show, - .store_attribute = f_ncm_opts_attr_store, -}; - -static ssize_t ncm_opts_dev_addr_show(struct f_ncm_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 ncm_opts_dev_addr_store(struct f_ncm_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_ncm_opts_attribute f_ncm_opts_dev_addr = - __CONFIGFS_ATTR(dev_addr, S_IRUGO | S_IWUSR, ncm_opts_dev_addr_show, - ncm_opts_dev_addr_store); - -static ssize_t ncm_opts_host_addr_show(struct f_ncm_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 ncm_opts_host_addr_store(struct f_ncm_opts *opts, - const char *page, size_t len) -{ - int ret; +/* f_ncm_opts_dev_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm); - 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_ncm_opts_attribute f_ncm_opts_host_addr = - __CONFIGFS_ATTR(host_addr, S_IRUGO | S_IWUSR, ncm_opts_host_addr_show, - ncm_opts_host_addr_store); - -static ssize_t ncm_opts_qmult_show(struct f_ncm_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 ncm_opts_qmult_store(struct f_ncm_opts *opts, - const char *page, size_t len) -{ - u8 val; - int ret; +/* f_ncm_opts_host_addr */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm); - 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_ncm_opts_attribute f_ncm_opts_qmult = - __CONFIGFS_ATTR(qmult, S_IRUGO | S_IWUSR, ncm_opts_qmult_show, - ncm_opts_qmult_store); - -static ssize_t ncm_opts_ifname_show(struct f_ncm_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; -} +/* f_ncm_opts_qmult */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm); -static struct f_ncm_opts_attribute f_ncm_opts_ifname = - __CONFIGFS_ATTR_RO(ifname, ncm_opts_ifname_show); +/* f_ncm_opts_ifname */ +USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm); static struct configfs_attribute *ncm_attrs[] = { &f_ncm_opts_dev_addr.attr, diff --git a/drivers/usb/gadget/u_ether_configfs.h b/drivers/usb/gadget/u_ether_configfs.h new file mode 100644 index 00000000000000..bcbd30146cfde6 --- /dev/null +++ b/drivers/usb/gadget/u_ether_configfs.h @@ -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 + * + * 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 */ From fee562a6450b7806f1fbbe1469a67b5395b5c10a Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:32:03 +0200 Subject: [PATCH 60/95] usb: gadget: f_ecm: convert to new function interface with backward compatibility Converting ecm to the new function interface requires converting the USB ecm's function code and its users. This patch converts the f_ecm.c to the new function interface. The file is now compiled into a separate usb_f_ecm.ko module. The old function interface is provided by means of a preprocessor conditional directives. After all users are converted, the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/cdc2.c | 1 + drivers/usb/gadget/ether.c | 1 + drivers/usb/gadget/f_ecm.c | 149 ++++++++++++++++++++++++++++++++---- drivers/usb/gadget/g_ffs.c | 1 + drivers/usb/gadget/multi.c | 1 + drivers/usb/gadget/nokia.c | 3 +- drivers/usb/gadget/u_ecm.h | 27 +++++++ 9 files changed, 174 insertions(+), 14 deletions(-) create mode 100644 drivers/usb/gadget/u_ecm.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index bf7ad7317b5d63..6f1afd7553e04b 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -514,6 +514,9 @@ config USB_F_OBEX config USB_F_NCM tristate +config USB_F_ECM + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 34b117eec36eee..66152f513afd30 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -50,6 +50,8 @@ 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 gadget drivers diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index bffa997fd0406d..ceedaf7cabc665 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -44,6 +44,7 @@ USB_ETHERNET_MODULE_PARAMETERS(); * 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_ECM_INCLUDED #include "f_ecm.c" /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 6bff24f193a2a9..862ef656e8a145 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -102,6 +102,7 @@ static inline bool has_rndis(void) * 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_ECM_INCLUDED #include "f_ecm.c" #include "f_subset.c" #ifdef USB_ETH_RNDIS diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index abf8a31ae14602..1b5aeb280d4b05 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -14,10 +14,12 @@ #include #include +#include #include #include #include "u_ether.h" +#include "u_ecm.h" /* @@ -687,6 +689,40 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) 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) { + gether_set_gadget(ecm_opts->net, cdev->gadget); + status = gether_register_netdev(ecm_opts->net); + if (status) + return status; + ecm_opts->bound = true; + } +#endif + 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 instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -796,8 +832,10 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) 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); @@ -834,17 +872,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) @@ -861,7 +888,7 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], 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,99 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], kfree(ecm); return status; } + +#else + +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); + + 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)); + + return &opts->func_inst; +} + +static void ecm_free(struct usb_function *f) +{ + struct f_ecm *ecm; + + ecm = func_to_ecm(f); + kfree(ecm); +} + +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"); + + ecm_string_defs[0].id = 0; + 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); + + /* 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); + 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.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 diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index fbfdb53a2db5f8..d38a073efd5d2e 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -28,6 +28,7 @@ # define USB_ETH_RNDIS y # endif +#define USBF_ECM_INCLUDED # include "f_ecm.c" # include "f_subset.c" # ifdef USB_ETH_RNDIS diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index ce21e9f8203ee8..61643938d36bd3 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -43,6 +43,7 @@ MODULE_LICENSE("GPL"); */ #include "f_mass_storage.c" +#define USBF_ECM_INCLUDED #include "f_ecm.c" #include "f_subset.c" #ifdef USB_ETH_RNDIS diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 39f6cb5f984d53..8e42c88b453b3d 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -37,8 +37,9 @@ * 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 +#define USBF_ECM_INCLUDED #include "f_ecm.c" +#define USBF_OBEX_INCLUDED #include "f_obex.c" #include "f_phonet.c" #include "u_ether.h" diff --git a/drivers/usb/gadget/u_ecm.h b/drivers/usb/gadget/u_ecm.h new file mode 100644 index 00000000000000..99b6b995988f4b --- /dev/null +++ b/drivers/usb/gadget/u_ecm.h @@ -0,0 +1,27 @@ +/* + * u_ecm.h + * + * Utility definitions for the ecm function + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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 + +struct f_ecm_opts { + struct usb_function_instance func_inst; + struct net_device *net; + bool bound; +}; + +#endif /* U_ECM_H */ From a38a275030086d95306555e544fc7c0e65ccd00e Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:32:04 +0200 Subject: [PATCH 61/95] usb: gadget: cdc2: convert to new interface of f_ecm f_ecm has been converted to new configfs infrastructure, fixing cdc2 gadget driver. [ balbi@ti.com : fixed a bunch of errors when adding ECM function ] Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/cdc2.c | 86 ++++++++++++++++++++++++++------------ 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 6f1afd7553e04b..fcc3bfb6193592 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -838,6 +838,7 @@ config USB_CDC_COMPOSITE 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. diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c index ceedaf7cabc665..5a5acf22c69451 100644 --- a/drivers/usb/gadget/cdc2.c +++ b/drivers/usb/gadget/cdc2.c @@ -15,6 +15,7 @@ #include "u_ether.h" #include "u_serial.h" +#include "u_ecm.h" #define DRIVER_DESC "CDC Composite Gadget" @@ -32,21 +33,10 @@ #define CDC_VENDOR_NUM 0x0525 /* NetChip */ #define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */ -/*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); USB_ETHERNET_MODULE_PARAMETERS(); -/* - * 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_ECM_INCLUDED -#include "f_ecm.c" - /*-------------------------------------------------------------------------*/ static struct usb_device_descriptor device_desc = { @@ -104,12 +94,13 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -static u8 host_mac[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. */ @@ -122,13 +113,27 @@ static int __init cdc_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - status = ecm_bind_config(c, host_mac, 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)) { @@ -138,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; } @@ -159,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)) { @@ -167,11 +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, dev_addr, host_addr, host_mac, - qmult); - 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. @@ -195,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; } @@ -203,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; } From 06b72598e819cf60ebf2267b94c7044ff550d076 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:32:05 +0200 Subject: [PATCH 62/95] usb: gadget: f_ecm: use usb_gstrings_attach use the new usb_gstrings_attach interface. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_ecm.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 1b5aeb280d4b05..22d19483e10135 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -686,6 +686,7 @@ 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; @@ -712,16 +713,14 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) ecm_opts->bound = true; } #endif - 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; - } + 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); @@ -841,7 +840,6 @@ ecm_old_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "ecm unbind\n"); - ecm_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(ecm->notify_req->buf); @@ -885,7 +883,6 @@ 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_old_unbind; @@ -944,7 +941,6 @@ static void ecm_unbind(struct usb_configuration *c, struct usb_function *f) DBG(c->cdev, "ecm unbind\n"); - ecm_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(ecm->notify_req->buf); @@ -977,7 +973,6 @@ struct usb_function *ecm_alloc(struct usb_function_instance *fi) 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; From da92801c647cdebfd45001fd6aaecb8f0be7f56b Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:32:06 +0200 Subject: [PATCH 63/95] usb: gadget: f_ecm: add configfs support f_ecm learns about our new configfs-based binding. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_ecm.c | 50 +++++++++++++++++++++++++++++++++++++- drivers/usb/gadget/u_ecm.h | 9 +++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c index 22d19483e10135..fcafe1af45850f 100644 --- a/drivers/usb/gadget/f_ecm.c +++ b/drivers/usb/gadget/f_ecm.c @@ -19,6 +19,7 @@ #include #include "u_ether.h" +#include "u_ether_configfs.h" #include "u_ecm.h" @@ -706,8 +707,10 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) * 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; @@ -899,6 +902,41 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], #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; @@ -918,21 +956,28 @@ static struct usb_function_instance *ecm_alloc_inst(void) 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) @@ -959,6 +1004,8 @@ struct usb_function *ecm_alloc(struct usb_function_instance *fi) 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, @@ -970,6 +1017,7 @@ struct usb_function *ecm_alloc(struct usb_function_instance *fi) 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"; diff --git a/drivers/usb/gadget/u_ecm.h b/drivers/usb/gadget/u_ecm.h index 99b6b995988f4b..262cc03cc2c01c 100644 --- a/drivers/usb/gadget/u_ecm.h +++ b/drivers/usb/gadget/u_ecm.h @@ -22,6 +22,15 @@ 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 */ From 1af877c409985fdf8f4d3cf14e8d08fb1038a0a4 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:51:07 +0200 Subject: [PATCH 64/95] usb: gadget: f_obex: use usb_gstrings_attach use the new usb_gstrings_attach interface Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_obex.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 8aa2be5329bcff..8d6073aef84e1e 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -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 */ @@ -411,7 +408,6 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f) 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)); } @@ -440,7 +436,6 @@ int __init obex_bind_config(struct usb_configuration *c, u8 port_num) 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; @@ -550,7 +545,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 +566,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; From 3a34344979617f1b0df2c4191778009dba4e5e50 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:51:08 +0200 Subject: [PATCH 65/95] usb: gadget: nokia: convert to new interface of f_obex preparation to use configfs-based approach on g_nokia.ko Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/nokia.c | 122 +++++++++++++++++++++++++------------ 2 files changed, 85 insertions(+), 38 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index fcc3bfb6193592..277eb5d1ff93b5 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -857,6 +857,7 @@ config USB_G_NOKIA select USB_U_SERIAL select USB_U_ETHER select USB_F_ACM + select USB_F_OBEX help The Nokia composite gadget provides support for acm, obex and phonet in only one composite gadget driver. diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 8e42c88b453b3d..a69e8bfb3ecb91 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -16,6 +16,7 @@ */ #include +#include #include #include "u_serial.h" @@ -39,8 +40,6 @@ */ #define USBF_ECM_INCLUDED #include "f_ecm.c" -#define USBF_OBEX_INCLUDED -#include "f_obex.c" #include "f_phonet.c" #include "u_ether.h" @@ -102,16 +101,12 @@ MODULE_LICENSE("GPL"); static struct usb_function *f_acm_cfg1; static struct usb_function *f_acm_cfg2; static u8 host_mac[ETH_ALEN]; +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 eth_dev *the_dev; -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", .bConfigurationValue = 1, @@ -129,27 +124,51 @@ static struct usb_configuration nokia_config_100ma_driver = { }; static struct usb_function_instance *fi_acm; +static struct usb_function_instance *fi_obex1; +static struct usb_function_instance *fi_obex2; static int __init nokia_bind_config(struct usb_configuration *c) { struct usb_function *f_acm; + struct usb_function *f_obex1 = NULL; + struct usb_function *f_obex2 = NULL; int status = 0; + int obex1_stat = 0; + int obex2_stat = 0; status = phonet_bind_config(c); if (status) - printk(KERN_DEBUG "could not bind phonet config\n"); + pr_debug("could not bind phonet config\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; + } + + 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) @@ -160,16 +179,30 @@ static int __init nokia_bind_config(struct usb_configuration *c) 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_obex1_cfg1 = f_obex1; + f_obex2_cfg1 = f_obex2; + } else { f_acm_cfg2 = f_acm; + 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); 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); return status; } @@ -177,18 +210,11 @@ 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, dev_addr, host_addr, host_mac, qmult); if (IS_ERR(the_dev)) { @@ -208,9 +234,17 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) if (!gadget_supports_altsettings(gadget)) goto err_usb; + 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; + goto err_obex2_inst; /* finally register the configuration */ status = usb_add_config(cdev, &nokia_config_500ma_driver, @@ -230,15 +264,20 @@ 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); 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); 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; @@ -246,16 +285,23 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) 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); usb_put_function(f_acm_cfg1); usb_put_function(f_acm_cfg2); + if (!IS_ERR(fi_obex1)) + usb_put_function_instance(fi_obex1); + if (!IS_ERR(fi_obex2)) + usb_put_function_instance(fi_obex2); 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; From 9b2252cace741e4843983d77ead80c3cf1d74e20 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:51:09 +0200 Subject: [PATCH 66/95] usb: gadget: f_obex: remove compatibility layer There are no old function interface users left, so the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_obex.c | 52 ------------------------------------- 1 file changed, 52 deletions(-) diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c index 8d6073aef84e1e..ad39f1dacba353 100644 --- a/drivers/usb/gadget/f_obex.c +++ b/drivers/usb/gadget/f_obex.c @@ -403,55 +403,6 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f) return status; } -#ifdef USBF_OBEX_INCLUDED - -static void -obex_old_unbind(struct usb_configuration *c, struct usb_function *f) -{ - 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"; - /* 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, @@ -578,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"); From 0189e63ad8a06bcd9f74ca00490cf7aa7bae0cff Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:51:10 +0200 Subject: [PATCH 67/95] usb: gadget: phonet: move global dev variable to its user cleanup patch only in preparation for configfs. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_phonet.c | 20 +++++++++++--------- drivers/usb/gadget/nokia.c | 14 +++++++++----- drivers/usb/gadget/u_phonet.h | 6 +++--- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index b21ab558b6c01a..f8df525da11873 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -579,9 +579,8 @@ pn_unbind(struct usb_configuration *c, struct usb_function *f) /*-------------------------------------------------------------------------*/ -static struct net_device *dev; - -int __init phonet_bind_config(struct usb_configuration *c) +int __init phonet_bind_config(struct usb_configuration *c, + struct net_device *dev) { struct f_phonet *fp; int err, size; @@ -606,16 +605,16 @@ int __init phonet_bind_config(struct usb_configuration *c) return err; } -int __init gphonet_setup(struct usb_gadget *gadget) +struct net_device __init *gphonet_setup(struct usb_gadget *gadget) { + 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); @@ -623,12 +622,15 @@ int __init gphonet_setup(struct usb_gadget *gadget) SET_NETDEV_DEV(dev, &gadget->dev); err = register_netdev(dev); - if (err) + if (err) { free_netdev(dev); - return err; + + return ERR_PTR(err); + } + return dev; } -void gphonet_cleanup(void) +void gphonet_cleanup(struct net_device *dev) { unregister_netdev(dev); } diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index a69e8bfb3ecb91..5650ece3e09cba 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -106,6 +106,8 @@ static struct usb_function *f_obex2_cfg1; static struct usb_function *f_obex1_cfg2; static struct usb_function *f_obex2_cfg2; static struct eth_dev *the_dev; +static struct net_device *phonet_dev; + static struct usb_configuration nokia_config_500ma_driver = { .label = "Bus Powered", @@ -136,7 +138,7 @@ static int __init nokia_bind_config(struct usb_configuration *c) int obex1_stat = 0; int obex2_stat = 0; - status = phonet_bind_config(c); + status = phonet_bind_config(c, phonet_dev); if (status) pr_debug("could not bind phonet config\n"); @@ -211,9 +213,11 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) struct usb_gadget *gadget = cdev->gadget; int status; - status = gphonet_setup(cdev->gadget); - if (status < 0) + phonet_dev = gphonet_setup(cdev->gadget); + if (IS_ERR(phonet_dev)) { + status = PTR_ERR(phonet_dev); goto err_phonet; + } the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac, qmult); @@ -278,7 +282,7 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) err_usb: gether_cleanup(the_dev); err_ether: - gphonet_cleanup(); + gphonet_cleanup(phonet_dev); err_phonet: return status; } @@ -300,7 +304,7 @@ static int __exit nokia_unbind(struct usb_composite_dev *cdev) if (!IS_ERR(fi_obex2)) usb_put_function_instance(fi_obex2); usb_put_function_instance(fi_acm); - gphonet_cleanup(); + gphonet_cleanup(phonet_dev); gether_cleanup(the_dev); diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/u_phonet.h index 09a75259b6cd4f..459ee3221db571 100644 --- a/drivers/usb/gadget/u_phonet.h +++ b/drivers/usb/gadget/u_phonet.h @@ -14,8 +14,8 @@ #include #include -int gphonet_setup(struct usb_gadget *gadget); -int phonet_bind_config(struct usb_configuration *c); -void gphonet_cleanup(void); +struct net_device *gphonet_setup(struct usb_gadget *gadget); +int phonet_bind_config(struct usb_configuration *c, struct net_device *dev); +void gphonet_cleanup(struct net_device *dev); #endif /* __U_PHONET_H */ From fcbdf12ebef73a6069e2a1aada1e546fb578a4aa Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:51:11 +0200 Subject: [PATCH 68/95] usb: gadget: f_phonet: convert to new function interface with backward compatibility Converting f_phonet to the new function interface requires converting the f_phonet's function code and its users. This patch converts the f_phonet.c to the new function interface. The file is now compiled into a separate usb_f_phonet.ko module. The old function interface is provided by means of preprocessor conditional directives. After all users are converted, the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_phonet.c | 151 ++++++++++++++++++++++++++++++++-- drivers/usb/gadget/nokia.c | 1 + drivers/usb/gadget/u_phonet.h | 9 ++ 5 files changed, 161 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 277eb5d1ff93b5..25fb902909547d 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -517,6 +517,9 @@ config USB_F_NCM config USB_F_ECM tristate +config USB_F_PHONET + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 66152f513afd30..db8ce05bbc2c1e 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -52,6 +52,8 @@ 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 gadget drivers diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index f8df525da11873..a667dfe453db79 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -478,8 +479,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 +487,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 +581,10 @@ int pn_bind(struct usb_configuration *c, struct usb_function *f) return status; } +#ifdef USBF_PHONET_INCLUDED + static void -pn_unbind(struct usb_configuration *c, struct usb_function *f) +pn_old_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_phonet *fp = func_to_pn(f); int i; @@ -593,7 +616,7 @@ int __init phonet_bind_config(struct usb_configuration *c, fp->dev = dev; fp->function.name = "phonet"; fp->function.bind = pn_bind; - fp->function.unbind = pn_unbind; + fp->function.unbind = pn_old_unbind; fp->function.set_alt = pn_set_alt; fp->function.get_alt = pn_get_alt; fp->function.disable = pn_disconnect; @@ -605,7 +628,125 @@ int __init phonet_bind_config(struct usb_configuration *c, return err; } -struct net_device __init *gphonet_setup(struct usb_gadget *gadget) +#else + +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)); + + 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; + + /* We are already disconnected */ + if (fp->in_req) + usb_ep_free_request(fp->in_ep, fp->in_req); + for (i = 0; i < phonet_rxq_size; i++) + if (fp->out_reqv[i]) + usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); + + usb_free_all_descriptors(f); +} + +struct usb_function *phonet_alloc(struct usb_function_instance *fi) +{ + struct f_phonet *fp; + 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 ERR_PTR(-ENOMEM); + + 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); + + return &fp->function; +} + +struct net_device *gphonet_setup_default(void) +{ + struct net_device *dev; + struct phonet_port *port; + + /* Create net device */ + dev = alloc_netdev(sizeof(*port), "upnlink%d", pn_net_setup); + if (!dev) + return ERR_PTR(-ENOMEM); + + port = netdev_priv(dev); + spin_lock_init(&port->lock); + netif_carrier_off(dev); + + return dev; +} + +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; +} + +DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc); +MODULE_AUTHOR("Rémi Denis-Courmont"); +MODULE_LICENSE("GPL"); + +#endif + +struct net_device *gphonet_setup(struct usb_gadget *gadget) { struct net_device *dev; struct phonet_port *port; diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 5650ece3e09cba..a20bcbfe4a6e13 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -40,6 +40,7 @@ */ #define USBF_ECM_INCLUDED #include "f_ecm.c" +#define USBF_PHONET_INCLUDED #include "f_phonet.c" #include "u_ether.h" diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/u_phonet.h index 459ee3221db571..1ff5ad5d3bf56a 100644 --- a/drivers/usb/gadget/u_phonet.h +++ b/drivers/usb/gadget/u_phonet.h @@ -14,6 +14,15 @@ #include #include +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); struct net_device *gphonet_setup(struct usb_gadget *gadget); int phonet_bind_config(struct usb_configuration *c, struct net_device *dev); void gphonet_cleanup(struct net_device *dev); From 83167f12da059799072128e91ab692613d8237ee Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:51:12 +0200 Subject: [PATCH 69/95] usb: gadget: nokia: convert to new interface of f_phonet use the new interface which will allow us to deprecate the legacy way of binding functions. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/nokia.c | 56 ++++++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 25fb902909547d..313ed5a7f6eda3 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -861,6 +861,7 @@ config USB_G_NOKIA select USB_U_ETHER select USB_F_ACM select USB_F_OBEX + select USB_F_PHONET help The Nokia composite gadget provides support for acm, obex and phonet in only one composite gadget driver. diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index a20bcbfe4a6e13..084f947ffd79ae 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -40,8 +40,6 @@ */ #define USBF_ECM_INCLUDED #include "f_ecm.c" -#define USBF_PHONET_INCLUDED -#include "f_phonet.c" #include "u_ether.h" /*-------------------------------------------------------------------------*/ @@ -106,8 +104,9 @@ 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; static struct eth_dev *the_dev; -static struct net_device *phonet_dev; static struct usb_configuration nokia_config_500ma_driver = { @@ -129,19 +128,24 @@ static struct usb_configuration nokia_config_100ma_driver = { static struct usb_function_instance *fi_acm; 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_obex2 = NULL; int status = 0; int obex1_stat = 0; int obex2_stat = 0; + int phonet_stat = 0; - status = phonet_bind_config(c, phonet_dev); - if (status) - pr_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"); + } if (!IS_ERR(fi_obex1)) { f_obex1 = usb_get_function(fi_obex1); @@ -161,6 +165,12 @@ static int __init nokia_bind_config(struct usb_configuration *c) goto err_get_acm; } + 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) @@ -184,10 +194,12 @@ static int __init nokia_bind_config(struct usb_configuration *c) } if (c == &nokia_config_500ma_driver) { f_acm_cfg1 = f_acm; + f_phonet_cfg1 = f_phonet; f_obex1_cfg1 = f_obex1; f_obex2_cfg1 = f_obex2; } else { f_acm_cfg2 = f_acm; + f_phonet_cfg2 = f_phonet; f_obex1_cfg2 = f_obex1; f_obex2_cfg2 = f_obex2; } @@ -200,12 +212,16 @@ static int __init nokia_bind_config(struct usb_configuration *c) 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_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; } @@ -214,12 +230,6 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) struct usb_gadget *gadget = cdev->gadget; int status; - phonet_dev = gphonet_setup(cdev->gadget); - if (IS_ERR(phonet_dev)) { - status = PTR_ERR(phonet_dev); - goto err_phonet; - } - the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac, qmult); if (IS_ERR(the_dev)) { @@ -239,6 +249,10 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) if (!gadget_supports_altsettings(gadget)) 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"); @@ -273,6 +287,8 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) 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); err_acm_inst: usb_put_function_instance(fi_acm); err_obex2_inst: @@ -280,11 +296,11 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) 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: - gphonet_cleanup(phonet_dev); -err_phonet: return status; } @@ -298,14 +314,20 @@ static int __exit nokia_unbind(struct usb_composite_dev *cdev) 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); - if (!IS_ERR(fi_obex1)) - usb_put_function_instance(fi_obex1); + 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(phonet_dev); gether_cleanup(the_dev); From 0383070e8d904f006b6eaffceb3fae4cdd25c01a Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:51:13 +0200 Subject: [PATCH 70/95] usb: gadget: f_phonet: remove compatibility layer There are no old function interface users left, so the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_phonet.c | 84 ++--------------------------------- drivers/usb/gadget/u_phonet.h | 1 - 2 files changed, 4 insertions(+), 81 deletions(-) diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index a667dfe453db79..5dd77487616823 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -581,55 +581,6 @@ static int pn_bind(struct usb_configuration *c, struct usb_function *f) return status; } -#ifdef USBF_PHONET_INCLUDED - -static void -pn_old_unbind(struct usb_configuration *c, struct usb_function *f) -{ - struct f_phonet *fp = func_to_pn(f); - int i; - - /* We are already disconnected */ - if (fp->in_req) - usb_ep_free_request(fp->in_ep, fp->in_req); - for (i = 0; i < phonet_rxq_size; i++) - if (fp->out_reqv[i]) - usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); - - usb_free_all_descriptors(f); - kfree(fp); -} - -/*-------------------------------------------------------------------------*/ - -int __init phonet_bind_config(struct usb_configuration *c, - struct net_device *dev) -{ - struct f_phonet *fp; - int err, size; - - size = sizeof(*fp) + (phonet_rxq_size * sizeof(struct usb_request *)); - fp = kzalloc(size, GFP_KERNEL); - if (!fp) - return -ENOMEM; - - fp->dev = dev; - fp->function.name = "phonet"; - fp->function.bind = pn_bind; - fp->function.unbind = pn_old_unbind; - fp->function.set_alt = pn_set_alt; - fp->function.get_alt = pn_get_alt; - fp->function.disable = pn_disconnect; - spin_lock_init(&fp->rx.lock); - - err = usb_add_function(c, &fp->function); - if (err) - kfree(fp); - return err; -} - -#else - static void phonet_free_inst(struct usb_function_instance *f) { struct f_phonet_opts *opts; @@ -740,38 +691,11 @@ int gphonet_register_netdev(struct net_device *net) return status; } -DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc); -MODULE_AUTHOR("Rémi Denis-Courmont"); -MODULE_LICENSE("GPL"); - -#endif - -struct net_device *gphonet_setup(struct usb_gadget *gadget) -{ - struct net_device *dev; - struct phonet_port *port; - int err; - - /* Create net device */ - dev = alloc_netdev(sizeof(*port), "upnlink%d", pn_net_setup); - if (!dev) - 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_PTR(err); - } - return dev; -} - 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"); diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/u_phonet.h index 1ff5ad5d3bf56a..98ced18779eaf4 100644 --- a/drivers/usb/gadget/u_phonet.h +++ b/drivers/usb/gadget/u_phonet.h @@ -23,7 +23,6 @@ struct f_phonet_opts { 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); -struct net_device *gphonet_setup(struct usb_gadget *gadget); int phonet_bind_config(struct usb_configuration *c, struct net_device *dev); void gphonet_cleanup(struct net_device *dev); From b904d0811dd3c878075d15b8cecbeb3bea89167d Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:51:14 +0200 Subject: [PATCH 71/95] usb: gadget: nokia: convert to new interface of f_ecm this will let us deprecate (and remove) the old interface. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/nokia.c | 66 ++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 313ed5a7f6eda3..5022c2d3e365b8 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -862,6 +862,7 @@ config USB_G_NOKIA 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. diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c index 084f947ffd79ae..0a8099a488c4b4 100644 --- a/drivers/usb/gadget/nokia.c +++ b/drivers/usb/gadget/nokia.c @@ -22,6 +22,7 @@ #include "u_serial.h" #include "u_ether.h" #include "u_phonet.h" +#include "u_ecm.h" #include "gadget_chips.h" /* Defines */ @@ -29,20 +30,6 @@ #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_ECM_INCLUDED -#include "f_ecm.c" -#include "u_ether.h" - -/*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); USB_ETHERNET_MODULE_PARAMETERS(); @@ -99,14 +86,14 @@ MODULE_LICENSE("GPL"); /*-------------------------------------------------------------------------*/ static struct usb_function *f_acm_cfg1; static struct usb_function *f_acm_cfg2; -static u8 host_mac[ETH_ALEN]; +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; -static struct eth_dev *the_dev; static struct usb_configuration nokia_config_500ma_driver = { @@ -126,6 +113,7 @@ 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; @@ -135,6 +123,7 @@ 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; @@ -165,6 +154,12 @@ static int __init nokia_bind_config(struct usb_configuration *c) 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) @@ -187,18 +182,20 @@ static int __init nokia_bind_config(struct usb_configuration *c) if (status) goto err_conf; - status = ecm_bind_config(c, host_mac, 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) { f_acm_cfg1 = f_acm; + 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; @@ -214,6 +211,8 @@ static int __init nokia_bind_config(struct usb_configuration *c) 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)) @@ -230,13 +229,6 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) struct usb_gadget *gadget = cdev->gadget; int status; - the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac, - qmult); - if (IS_ERR(the_dev)) { - status = PTR_ERR(the_dev); - goto err_ether; - } - status = usb_string_ids_tab(cdev, strings_dev); if (status < 0) goto err_usb; @@ -246,8 +238,10 @@ 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)) @@ -262,14 +256,22 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) pr_debug("could not find obex function 2\n"); fi_acm = usb_get_function_instance("acm"); - if (IS_ERR(fi_acm)) + 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); @@ -289,6 +291,9 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) 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: @@ -299,8 +304,6 @@ static int __init nokia_bind(struct usb_composite_dev *cdev) if (!IS_ERR(fi_phonet)) usb_put_function_instance(fi_phonet); err_usb: - gether_cleanup(the_dev); -err_ether: return status; } @@ -320,7 +323,10 @@ static int __exit nokia_unbind(struct usb_composite_dev *cdev) 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)) @@ -329,8 +335,6 @@ static int __exit nokia_unbind(struct usb_composite_dev *cdev) usb_put_function_instance(fi_phonet); usb_put_function_instance(fi_acm); - gether_cleanup(the_dev); - return 0; } From 83408745b202695e8911d71a9854c517e565c343 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Thu, 23 May 2013 10:51:15 +0200 Subject: [PATCH 72/95] usb: gadget: f_phonet: add configfs support f_phonet learns about configfs so we can remove in-kernel gadget drivers. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- .../ABI/testing/configfs-usb-gadget-phonet | 8 +++ drivers/usb/gadget/Kconfig | 10 ++++ drivers/usb/gadget/f_phonet.c | 56 +++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-phonet diff --git a/Documentation/ABI/testing/configfs-usb-gadget-phonet b/Documentation/ABI/testing/configfs-usb-gadget-phonet new file mode 100644 index 00000000000000..19b67d3eab9451 --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-phonet @@ -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. diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 5022c2d3e365b8..d6c4e601d7119c 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -541,6 +541,16 @@ choice # this first set of drivers all depend on bulk-capable hardware. +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 diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index 5dd77487616823..7944fb0efe3b6a 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -26,6 +26,7 @@ #include #include "u_phonet.h" +#include "u_ether.h" #define PN_MEDIA_USB 0x1B #define MAXPACKET 512 @@ -581,6 +582,58 @@ static int pn_bind(struct usb_configuration *c, struct usb_function *f) return status; } +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; @@ -606,6 +659,9 @@ static struct usb_function_instance *phonet_alloc_inst(void) 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; } From bf4277c73adc3a6b244deab34a3906b9418b9d5e Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:45 +0200 Subject: [PATCH 73/95] usb: gadget: u_ether: allow getting binary-form host address helper function to copy MAC address to proper place. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/u_ether.c | 9 +++++++++ drivers/usb/gadget/u_ether.h | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 6d3ccdc09e1eaa..2aae0d61bb1971 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -963,6 +963,15 @@ int gether_get_host_addr_cdc(struct net_device *net, char *host_addr, int len) } 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; diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 1671a7963d011f..5efa657cf469eb 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -209,6 +209,16 @@ int gether_get_host_addr(struct net_device *net, char *host_addr, int len); */ 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 From 9c62ce83e4258bacc459faf57bf2ed83cce6be08 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:46 +0200 Subject: [PATCH 74/95] usb: gadget: ether: convert to new interface of f_ecm moving to new interface so we can remove the older one. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/ether.c | 73 +++++++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index d6c4e601d7119c..a34d3da00b28ae 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -622,6 +622,7 @@ config USB_ETH select USB_LIBCOMPOSITE select USB_U_ETHER select USB_U_RNDIS + select USB_F_ECM select CRC32 help This driver implements Ethernet style communication, in one of diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 862ef656e8a145..f4d46d7cfe372a 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -102,8 +102,7 @@ static inline bool has_rndis(void) * 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_ECM_INCLUDED -#include "f_ecm.c" +#include "u_ecm.h" #include "f_subset.c" #ifdef USB_ETH_RNDIS #include "f_rndis.c" @@ -212,6 +211,10 @@ static struct usb_gadget_strings *dev_strings[] = { static u8 host_mac[ETH_ALEN]; static struct eth_dev *the_dev; + +static struct usb_function_instance *fi_ecm; +static struct usb_function *f_ecm; + /*-------------------------------------------------------------------------*/ /* @@ -253,6 +256,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)) { @@ -262,10 +267,19 @@ static int __init eth_do_config(struct usb_configuration *c) if (use_eem) return eem_bind_config(c, the_dev); - else if (can_support_ecm(c->cdev->gadget)) - return ecm_bind_config(c, host_mac, the_dev); - else + 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 return geth_bind_config(c, host_mac, the_dev); + } static struct usb_configuration eth_config_driver = { @@ -280,13 +294,16 @@ 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_ecm_opts *ecm_opts = NULL; int status; - /* set up network link layer */ - the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac, - qmult); - if (IS_ERR(the_dev)) - return PTR_ERR(the_dev); + if (use_eem || !can_support_ecm(gadget)) { + /* set up network link layer */ + the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, + host_mac, qmult); + if (IS_ERR(the_dev)) + return PTR_ERR(the_dev); + } /* set up main config label and device descriptor */ if (use_eem) { @@ -294,8 +311,23 @@ static int __init eth_bind(struct usb_composite_dev *cdev) 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); + + 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); + + the_dev = netdev_priv(ecm_opts->net); + eth_config_driver.label = "CDC Ethernet (ECM)"; } else { /* CDC Subset */ @@ -309,6 +341,15 @@ static int __init eth_bind(struct usb_composite_dev *cdev) if (has_rndis()) { /* RNDIS plus ECM-or-Subset */ + if (!use_eem && can_support_ecm(gadget)) { + gether_set_gadget(ecm_opts->net, cdev->gadget); + status = gether_register_netdev(ecm_opts->net); + if (status) + goto fail; + ecm_opts->bound = true; + gether_get_host_addr_u8(ecm_opts->net, host_mac); + } + device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM); device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM); device_desc.bNumConfigurations = 2; @@ -343,13 +384,19 @@ static int __init eth_bind(struct usb_composite_dev *cdev) return 0; fail: - gether_cleanup(the_dev); + if (use_eem || !can_support_ecm(gadget)) + gether_cleanup(the_dev); + else + usb_put_function_instance(fi_ecm); return status; } static int __exit eth_unbind(struct usb_composite_dev *cdev) { - gether_cleanup(the_dev); + if (use_eem || !can_support_ecm(cdev->gadget)) + gether_cleanup(the_dev); + else + usb_put_function_instance(fi_ecm); return 0; } From b29002a157940752dfed2c488b2011f63f007d71 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:47 +0200 Subject: [PATCH 75/95] usb: gadget: f_eem: convert to new function interface with backward compatibility Converting eem to the new function interface requires converting the USB eem's function code and its users. This patch converts the f_eem.c to the new function interface. The file is now compiled into a separate usb_f_eem.ko module. The old function interface is provided by means of a preprocessor conditional directives. After all users are converted, the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/ether.c | 2 + drivers/usb/gadget/f_eem.c | 174 ++++++++++++++++++++++++++++-------- drivers/usb/gadget/u_eem.h | 27 ++++++ 5 files changed, 172 insertions(+), 36 deletions(-) create mode 100644 drivers/usb/gadget/u_eem.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index a34d3da00b28ae..a9d3a7f676b707 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -520,6 +520,9 @@ config USB_F_ECM config USB_F_PHONET tristate +config USB_F_EEM + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index db8ce05bbc2c1e..7069f53e140dbb 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -54,6 +54,8 @@ 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 gadget drivers diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index f4d46d7cfe372a..397609da590955 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -108,6 +108,8 @@ static inline bool has_rndis(void) #include "f_rndis.c" #include "rndis.h" #endif + +#define USB_FEEM_INCLUDED #include "f_eem.c" /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index f4e0bbef602a3e..471acc824115c3 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -12,12 +12,14 @@ */ #include +#include #include #include #include #include #include "u_ether.h" +#include "u_eem.h" #define EEM_HLEN 2 @@ -40,7 +42,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 +56,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 +64,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 +72,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 +82,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 +91,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 +100,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 +110,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 +119,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 +128,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 +137,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 +244,44 @@ 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); int status; struct usb_ep *ep; +#ifndef USB_FEEM_INCLUDED + 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) { + gether_set_gadget(eem_opts->net, cdev->gadget); + status = gether_register_netdev(eem_opts->net); + if (status) + return status; + eem_opts->bound = true; + } +#endif + + /* 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; + } + /* allocate instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -307,17 +339,6 @@ eem_bind(struct usb_configuration *c, struct usb_function *f) 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,6 +539,18 @@ static int eem_unwrap(struct gether *port, return status; } +#ifdef USB_FEEM_INCLUDED + +static void eem_old_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); +} + /** * eem_bind_config - add CDC Ethernet (EEM) network link to a configuration * @c: the configuration to support the network link @@ -533,17 +566,6 @@ int __init eem_bind_config(struct usb_configuration *c, struct eth_dev *dev) 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; - } - /* allocate and initialize one new instance */ eem = kzalloc(sizeof *eem, GFP_KERNEL); if (!eem) @@ -556,7 +578,7 @@ int __init eem_bind_config(struct usb_configuration *c, struct eth_dev *dev) 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.unbind = eem_old_unbind; eem->port.func.set_alt = eem_set_alt; eem->port.func.setup = eem_setup; eem->port.func.disable = eem_disable; @@ -570,3 +592,83 @@ int __init eem_bind_config(struct usb_configuration *c, struct eth_dev *dev) return status; } +#else + +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); + opts->func_inst.free_func_inst = eem_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) + return ERR_CAST(opts->net); + + return &opts->func_inst; +} + +static void eem_free(struct usb_function *f) +{ + struct f_eem *eem; + + eem = func_to_eem(f); + kfree(eem); +} + +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; + struct f_eem_opts *opts; + + /* allocate and initialize one new instance */ + eem = kzalloc(sizeof(*eem), GFP_KERNEL); + if (!eem) + return ERR_PTR(-ENOMEM); + + opts = container_of(fi, struct f_eem_opts, func_inst); + + eem->port.ioport = netdev_priv(opts->net); + 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; + + return &eem->port.func; +} + +DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Brownell"); + +#endif diff --git a/drivers/usb/gadget/u_eem.h b/drivers/usb/gadget/u_eem.h new file mode 100644 index 00000000000000..8f432f2a5e571a --- /dev/null +++ b/drivers/usb/gadget/u_eem.h @@ -0,0 +1,27 @@ +/* + * u_eem.h + * + * Utility definitions for the eem function + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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 + +struct f_eem_opts { + struct usb_function_instance func_inst; + struct net_device *net; + bool bound; +}; + +#endif /* U_EEM_H */ From 94b5573e97729f0e1496d23b69cbe2c6b24ec0c3 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:48 +0200 Subject: [PATCH 76/95] usb: gadget: ether: convert to new interface of f_eem use new interface so old one can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/ether.c | 54 +++++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index a9d3a7f676b707..159393d58912a9 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -681,6 +681,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 diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 397609da590955..2078e6c227a19c 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -109,8 +109,7 @@ static inline bool has_rndis(void) #include "rndis.h" #endif -#define USB_FEEM_INCLUDED -#include "f_eem.c" +#include "u_eem.h" /*-------------------------------------------------------------------------*/ USB_GADGET_COMPOSITE_OPTIONS(); @@ -217,6 +216,9 @@ 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; + /*-------------------------------------------------------------------------*/ /* @@ -267,9 +269,17 @@ 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)) { + 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); @@ -296,10 +306,11 @@ 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; int status; - if (use_eem || !can_support_ecm(gadget)) { + if (!use_eem && !can_support_ecm(gadget)) { /* set up network link layer */ the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, host_mac, qmult); @@ -310,6 +321,20 @@ static int __init eth_bind(struct usb_composite_dev *cdev) /* 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); + + gether_set_qmult(eem_opts->net, qmult); + if (!gether_set_host_addr(eem_opts->net, host_addr)) + pr_info("using host ethernet address: %s", host_addr); + if (!gether_set_dev_addr(eem_opts->net, dev_addr)) + pr_info("using self ethernet address: %s", dev_addr); + + the_dev = netdev_priv(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); @@ -343,7 +368,14 @@ static int __init eth_bind(struct usb_composite_dev *cdev) if (has_rndis()) { /* RNDIS plus ECM-or-Subset */ - if (!use_eem && can_support_ecm(gadget)) { + if (use_eem) { + gether_set_gadget(eem_opts->net, cdev->gadget); + status = gether_register_netdev(eem_opts->net); + if (status) + goto fail; + eem_opts->bound = true; + gether_get_host_addr_u8(eem_opts->net, host_mac); + } else if (can_support_ecm(gadget)) { gether_set_gadget(ecm_opts->net, cdev->gadget); status = gether_register_netdev(ecm_opts->net); if (status) @@ -386,8 +418,10 @@ static int __init eth_bind(struct usb_composite_dev *cdev) return 0; fail: - if (use_eem || !can_support_ecm(gadget)) + if (!use_eem && !can_support_ecm(gadget)) gether_cleanup(the_dev); + else if (use_eem) + usb_put_function_instance(fi_eem); else usb_put_function_instance(fi_ecm); return status; @@ -395,8 +429,10 @@ static int __init eth_bind(struct usb_composite_dev *cdev) static int __exit eth_unbind(struct usb_composite_dev *cdev) { - if (use_eem || !can_support_ecm(cdev->gadget)) + if (!use_eem && !can_support_ecm(cdev->gadget)) gether_cleanup(the_dev); + else if (use_eem) + usb_put_function_instance(fi_eem); else usb_put_function_instance(fi_ecm); return 0; From c96022ded7377228cc3381c661fd07efe10ba7fb Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:49 +0200 Subject: [PATCH 77/95] usb: gadget: f_eem: remove compatibility layer There are no old function interface users left, so the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_eem.c | 59 ------------------------------------ drivers/usb/gadget/u_ether.h | 1 - 2 files changed, 60 deletions(-) diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index 471acc824115c3..9d54d68628e5b8 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -251,7 +251,6 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) int status; struct usb_ep *ep; -#ifndef USB_FEEM_INCLUDED struct f_eem_opts *eem_opts; eem_opts = container_of(f->fi, struct f_eem_opts, func_inst); @@ -269,7 +268,6 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) return status; eem_opts->bound = true; } -#endif /* maybe allocate device-global string IDs */ if (eem_string_defs[0].id == 0) { @@ -539,61 +537,6 @@ static int eem_unwrap(struct gether *port, return status; } -#ifdef USB_FEEM_INCLUDED - -static void eem_old_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); -} - -/** - * 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) -{ - struct f_eem *eem; - int status; - - /* allocate and initialize one new instance */ - eem = kzalloc(sizeof *eem, GFP_KERNEL); - if (!eem) - return -ENOMEM; - - eem->port.ioport = dev; - 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_old_unbind; - eem->port.func.set_alt = eem_set_alt; - eem->port.func.setup = eem_setup; - eem->port.func.disable = eem_disable; - 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; -} - -#else - static void eem_free_inst(struct usb_function_instance *f) { struct f_eem_opts *opts; @@ -670,5 +613,3 @@ struct usb_function *eem_alloc(struct usb_function_instance *fi) DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); - -#endif diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 5efa657cf469eb..fb23d1fde8eb52 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h @@ -272,7 +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 eem_bind_config(struct usb_configuration *c, struct eth_dev *dev); #ifdef USB_ETH_RNDIS From 998da497ec75076549469216a35305684098375f Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:50 +0200 Subject: [PATCH 78/95] usb: gadget: f_eem: use usb_gstrings_attach use the new usb_gstrings_attach interface Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_eem.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index 9d54d68628e5b8..31a2cb7ebe82a8 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -248,6 +248,7 @@ 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; @@ -269,16 +270,11 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) eem_opts->bound = true; } - /* 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; - } + 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); @@ -595,7 +591,6 @@ struct usb_function *eem_alloc(struct usb_function_instance *fi) 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; From 17b80976f0aa28842593dae8b05fae9274b51375 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:51 +0200 Subject: [PATCH 79/95] usb: gadget: f_eem: add configfs support f_eem learns about our configfs interface so we can remove in-kernel gadget drivers in future. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- .../ABI/testing/configfs-usb-gadget-eem | 14 ++++++ drivers/usb/gadget/Kconfig | 15 ++++++ drivers/usb/gadget/f_eem.c | 49 +++++++++++++++++++ drivers/usb/gadget/u_eem.h | 9 ++++ 4 files changed, 87 insertions(+) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-eem diff --git a/Documentation/ABI/testing/configfs-usb-gadget-eem b/Documentation/ABI/testing/configfs-usb-gadget-eem new file mode 100644 index 00000000000000..10e87d67fa2e9c --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-eem @@ -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 diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 159393d58912a9..d5b0ffe2611884 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -544,6 +544,21 @@ choice # this first set of drivers all depend on bulk-capable hardware. +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 diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c index 31a2cb7ebe82a8..90ee8022e8d80c 100644 --- a/drivers/usb/gadget/f_eem.c +++ b/drivers/usb/gadget/f_eem.c @@ -19,6 +19,7 @@ #include #include "u_ether.h" +#include "u_ether_configfs.h" #include "u_eem.h" #define EEM_HLEN 2 @@ -263,8 +264,10 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) * 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; @@ -533,6 +536,41 @@ static int eem_unwrap(struct gether *port, return status; } +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; @@ -552,20 +590,28 @@ static struct usb_function_instance *eem_alloc_inst(void) 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) @@ -586,8 +632,11 @@ struct usb_function *eem_alloc(struct usb_function_instance *fi) return ERR_PTR(-ENOMEM); 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"; diff --git a/drivers/usb/gadget/u_eem.h b/drivers/usb/gadget/u_eem.h index 8f432f2a5e571a..e3ae97874c4fc0 100644 --- a/drivers/usb/gadget/u_eem.h +++ b/drivers/usb/gadget/u_eem.h @@ -22,6 +22,15 @@ 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 */ From cf99e8c6898862a482f8ed65d18979fb1a51fbc1 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:52 +0200 Subject: [PATCH 80/95] usb: gadget: multi: Remove unused include cleanup only, no functional changes. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/multi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 61643938d36bd3..656c99983f62f7 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -45,7 +45,6 @@ MODULE_LICENSE("GPL"); #define USBF_ECM_INCLUDED #include "f_ecm.c" -#include "f_subset.c" #ifdef USB_ETH_RNDIS # include "f_rndis.c" # include "rndis.h" From 8cedba7c73af1369599b1111639cfeb66fe13aaa Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:53 +0200 Subject: [PATCH 81/95] usb: gadget: f_subset: convert to new function interface with backward compatibility Converting ecm subset to the new function interface requires converting the USB subset's function code and its users. This patch converts the f_subset.c to the new function interface. The file is now compiled into a separate usb_f_subset.ko module. The old function interface is provided by means of a preprocessor conditional directives. After all users are converted, the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/ether.c | 1 + drivers/usb/gadget/f_subset.c | 136 ++++++++++++++++++++++++++++++---- drivers/usb/gadget/g_ffs.c | 1 + drivers/usb/gadget/u_gether.h | 27 +++++++ 6 files changed, 155 insertions(+), 15 deletions(-) create mode 100644 drivers/usb/gadget/u_gether.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index d5b0ffe2611884..803aa93f3aa575 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -523,6 +523,9 @@ config USB_F_PHONET config USB_F_EEM tristate +config USB_F_SUBSET + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 7069f53e140dbb..1bfad55b967803 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -56,6 +56,8 @@ 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 gadget drivers diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 2078e6c227a19c..31739662ad007c 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -103,6 +103,7 @@ static inline bool has_rndis(void) * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ #include "u_ecm.h" +#define USB_FSUBSET_INCLUDED #include "f_subset.c" #ifdef USB_ETH_RNDIS #include "f_rndis.c" diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index 7be04b3424941f..5ae0bf61242884 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -12,11 +12,12 @@ #include #include +#include #include #include #include "u_ether.h" - +#include "u_gether.h" /* * This function packages a simple "CDC Subset" Ethernet port with no real @@ -298,6 +299,35 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) 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) { + gether_set_gadget(gether_opts->net, cdev->gadget); + status = gether_register_netdev(gether_opts->net); + if (status) + return status; + gether_opts->bound = true; + } +#endif + /* 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 instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -360,8 +390,10 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) 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 +419,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) @@ -414,7 +434,7 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], 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 +443,89 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], kfree(geth); return status; } + +#else + +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); + + opts->func_inst.free_func_inst = geth_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) + return ERR_CAST(opts->net); + + 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); + + /* 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); + 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.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 diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index d38a073efd5d2e..3d290e5106aff2 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -30,6 +30,7 @@ #define USBF_ECM_INCLUDED # include "f_ecm.c" +#define USB_FSUBSET_INCLUDED # include "f_subset.c" # ifdef USB_ETH_RNDIS # include "f_rndis.c" diff --git a/drivers/usb/gadget/u_gether.h b/drivers/usb/gadget/u_gether.h new file mode 100644 index 00000000000000..3a4a2bf61cdc31 --- /dev/null +++ b/drivers/usb/gadget/u_gether.h @@ -0,0 +1,27 @@ +/* + * u_gether.h + * + * Utility definitions for the subset function + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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 + +struct f_gether_opts { + struct usb_function_instance func_inst; + struct net_device *net; + bool bound; +}; + +#endif /* U_GETHER_H */ From 8af5232d6f48896b151898ccb2e9e155481bb785 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:54 +0200 Subject: [PATCH 82/95] usb: gadget: ether: convert to new interface of f_subset teach ethernet code about the new interface of f_subset so the old one can eventually be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/ether.c | 104 ++++++++++++++++++++----------------- 2 files changed, 58 insertions(+), 47 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 803aa93f3aa575..0a444f39109fa7 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -644,6 +644,7 @@ config USB_ETH 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 diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 31739662ad007c..9e96d5583e4cca 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -14,6 +14,7 @@ /* #define VERBOSE_DEBUG */ #include +#include #if defined USB_ETH_RNDIS # undef USB_ETH_RNDIS @@ -103,8 +104,7 @@ static inline bool has_rndis(void) * a "gcc --combine ... part1.c part2.c part3.c ... " build would. */ #include "u_ecm.h" -#define USB_FSUBSET_INCLUDED -#include "f_subset.c" +#include "u_gether.h" #ifdef USB_ETH_RNDIS #include "f_rndis.c" #include "rndis.h" @@ -220,6 +220,9 @@ 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; + /*-------------------------------------------------------------------------*/ /* @@ -290,8 +293,17 @@ static int __init eth_do_config(struct usb_configuration *c) usb_put_function(f_ecm); return status; - } else - return geth_bind_config(c, host_mac, the_dev); + } 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; + } } @@ -309,16 +321,10 @@ 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; - if (!use_eem && !can_support_ecm(gadget)) { - /* set up network link layer */ - the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, - host_mac, qmult); - if (IS_ERR(the_dev)) - return PTR_ERR(the_dev); - } - /* set up main config label and device descriptor */ if (use_eem) { /* EEM */ @@ -328,13 +334,8 @@ static int __init eth_bind(struct usb_composite_dev *cdev) eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst); - gether_set_qmult(eem_opts->net, qmult); - if (!gether_set_host_addr(eem_opts->net, host_addr)) - pr_info("using host ethernet address: %s", host_addr); - if (!gether_set_dev_addr(eem_opts->net, dev_addr)) - pr_info("using self ethernet address: %s", dev_addr); - - the_dev = netdev_priv(eem_opts->net); + net = eem_opts->net; + the_dev = netdev_priv(net); eth_config_driver.label = "CDC Ethernet (EEM)"; device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM); @@ -348,17 +349,23 @@ static int __init eth_bind(struct usb_composite_dev *cdev) 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); - - the_dev = netdev_priv(ecm_opts->net); + net = ecm_opts->net; + the_dev = netdev_priv(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; + the_dev = netdev_priv(net); + eth_config_driver.label = "CDC Subset/SAFE"; device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM); @@ -367,23 +374,26 @@ 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 */ - if (use_eem) { - gether_set_gadget(eem_opts->net, cdev->gadget); - status = gether_register_netdev(eem_opts->net); - if (status) - goto fail; + gether_set_gadget(net, cdev->gadget); + status = gether_register_netdev(net); + if (status) + goto fail; + gether_get_host_addr_u8(net, host_mac); + + if (use_eem) eem_opts->bound = true; - gether_get_host_addr_u8(eem_opts->net, host_mac); - } else if (can_support_ecm(gadget)) { - gether_set_gadget(ecm_opts->net, cdev->gadget); - status = gether_register_netdev(ecm_opts->net); - if (status) - goto fail; + else if (can_support_ecm(gadget)) ecm_opts->bound = true; - gether_get_host_addr_u8(ecm_opts->net, host_mac); - } + else + geth_opts->bound = true; device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM); device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM); @@ -419,23 +429,23 @@ static int __init eth_bind(struct usb_composite_dev *cdev) return 0; fail: - if (!use_eem && !can_support_ecm(gadget)) - gether_cleanup(the_dev); - else if (use_eem) + if (use_eem) usb_put_function_instance(fi_eem); - else + 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) { - if (!use_eem && !can_support_ecm(cdev->gadget)) - gether_cleanup(the_dev); - else if (use_eem) + if (use_eem) usb_put_function_instance(fi_eem); - else + else if (can_support_ecm(cdev->gadget)) usb_put_function_instance(fi_ecm); + else + usb_put_function_instance(fi_geth); return 0; } From 74bf963d1d230b0cedbcbc46f6f15f20c2e3ee55 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:55 +0200 Subject: [PATCH 83/95] usb: gadget: f_subset: use usb_gstrings_attach use the new usb_gstrings_attach interface. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_subset.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index 5ae0bf61242884..089881a530d5e9 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -296,6 +296,7 @@ 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; @@ -319,14 +320,13 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) gether_opts->bound = true; } #endif - /* 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; - } + 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); @@ -432,7 +432,6 @@ 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_old_unbind; geth->port.func.set_alt = geth_set_alt; @@ -514,7 +513,6 @@ static struct usb_function *geth_alloc(struct usb_function_instance *fi) 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.set_alt = geth_set_alt; From 02832e56f88a981474ee4c7c141f46fc1b4454f4 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:56 +0200 Subject: [PATCH 84/95] usb: gadget: f_subset: add configfs support f_subset learns about configfs so we can, eventually, remove in-kernel gadget drivers. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- .../ABI/testing/configfs-usb-gadget-subset | 14 ++++++ drivers/usb/gadget/Kconfig | 10 ++++ drivers/usb/gadget/f_subset.c | 46 ++++++++++++++++++- drivers/usb/gadget/u_gether.h | 9 ++++ 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-subset diff --git a/Documentation/ABI/testing/configfs-usb-gadget-subset b/Documentation/ABI/testing/configfs-usb-gadget-subset new file mode 100644 index 00000000000000..f47170a2f7dca0 --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-subset @@ -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 diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 0a444f39109fa7..3a72b9dbf7f01f 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -547,6 +547,16 @@ 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_EEM bool "Ethernet Emulation Model (EEM)" depends on USB_CONFIGFS diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c index 089881a530d5e9..fbc7a24942e4a2 100644 --- a/drivers/usb/gadget/f_subset.c +++ b/drivers/usb/gadget/f_subset.c @@ -17,6 +17,7 @@ #include #include "u_ether.h" +#include "u_ether_configfs.h" #include "u_gether.h" /* @@ -313,8 +314,10 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) * 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; @@ -445,6 +448,41 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], #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; @@ -464,12 +502,15 @@ static struct usb_function_instance *geth_alloc_inst(void) 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; } @@ -500,6 +541,8 @@ static struct usb_function *geth_alloc(struct usb_function_instance *fi) 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)); @@ -510,6 +553,7 @@ static struct usb_function *geth_alloc(struct usb_function_instance *fi) 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"; diff --git a/drivers/usb/gadget/u_gether.h b/drivers/usb/gadget/u_gether.h index 3a4a2bf61cdc31..d4078426ba5dfe 100644 --- a/drivers/usb/gadget/u_gether.h +++ b/drivers/usb/gadget/u_gether.h @@ -22,6 +22,15 @@ 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 */ From f466c6353819326873fa48a02c6f2d7c903240d6 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:57 +0200 Subject: [PATCH 85/95] usb: gadget: f_rndis: convert to new function interface with backward compatibility Converting rndis to the new function interface requires converting the USB rndis' function code and its users. This patch converts the f_rndis.c to the new function interface. The file is now compiled into a separate usb_f_rndis.ko module. The old function interface is provided by means of a preprocessor conditional directives. After all users are converted, the old interface can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 3 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/ether.c | 1 + drivers/usb/gadget/f_rndis.c | 203 ++++++++++++++++++++++++++++++----- drivers/usb/gadget/g_ffs.c | 1 + drivers/usb/gadget/multi.c | 1 + drivers/usb/gadget/u_rndis.h | 32 ++++++ 7 files changed, 215 insertions(+), 28 deletions(-) create mode 100644 drivers/usb/gadget/u_rndis.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 3a72b9dbf7f01f..22c86089d64717 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -526,6 +526,9 @@ config USB_F_EEM config USB_F_SUBSET tristate +config USB_F_RNDIS + tristate + choice tristate "USB Gadget Drivers" default USB_ETH diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1bfad55b967803..b41776065f277a 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -58,6 +58,8 @@ 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 diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 9e96d5583e4cca..4d7290a48fe723 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -106,6 +106,7 @@ static inline bool has_rndis(void) #include "u_ecm.h" #include "u_gether.h" #ifdef USB_ETH_RNDIS +#define USB_FRNDIS_INCLUDED #include "f_rndis.c" #include "rndis.h" #endif diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 36e8c44d8e5e2f..437198b6d8fa74 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -17,15 +17,16 @@ #include #include +#include #include #include #include #include "u_ether.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 +656,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 @@ -665,6 +673,45 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) 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 + + 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 instance-specific interface IDs */ status = usb_interface_id(c, f); if (status < 0) @@ -741,10 +788,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,8 +836,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) 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); @@ -804,13 +855,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 +862,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); @@ -859,7 +885,7 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], rndis->port.func.strings = rndis_strings; /* descriptors are per-instance copies */ rndis->port.func.bind = rndis_bind; - rndis->port.func.unbind = rndis_unbind; + 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; @@ -872,3 +898,124 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], } 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 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); + + opts->func_inst.free_func_inst = rndis_free_inst; + opts->net = gether_setup_default(); + if (IS_ERR(opts->net)) + return ERR_CAST(opts->net); + + return &opts->func_inst; +} + +static void rndis_free(struct usb_function *f) +{ + struct f_rndis *rndis; + + rndis = func_to_rndis(f); + rndis_deregister(rndis->config); + kfree(rndis); +} + +static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct f_rndis *rndis = func_to_rndis(f); + + rndis_exit(); + rndis_string_defs[0].id = 0; + 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) { + rndis_exit(); + return ERR_PTR(-ENOMEM); + } + + opts = container_of(fi, struct f_rndis_opts, func_inst); + + 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); + /* 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"; + rndis->port.func.strings = rndis_strings; + /* 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 = rndis_register(rndis_response_available, rndis); + if (status < 0) { + kfree(rndis); + return ERR_PTR(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 diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c index 3d290e5106aff2..5327c82472eda8 100644 --- a/drivers/usb/gadget/g_ffs.c +++ b/drivers/usb/gadget/g_ffs.c @@ -33,6 +33,7 @@ #define USB_FSUBSET_INCLUDED # include "f_subset.c" # ifdef USB_ETH_RNDIS +# define USB_FRNDIS_INCLUDED # include "f_rndis.c" # include "rndis.h" # endif diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c index 656c99983f62f7..032b96a51ce4e0 100644 --- a/drivers/usb/gadget/multi.c +++ b/drivers/usb/gadget/multi.c @@ -46,6 +46,7 @@ MODULE_LICENSE("GPL"); #define USBF_ECM_INCLUDED #include "f_ecm.c" #ifdef USB_ETH_RNDIS +# define USB_FRNDIS_INCLUDED # include "f_rndis.c" # include "rndis.h" #endif diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/u_rndis.h new file mode 100644 index 00000000000000..d274df56ce757a --- /dev/null +++ b/drivers/usb/gadget/u_rndis.h @@ -0,0 +1,32 @@ +/* + * u_rndis.h + * + * Utility definitions for the subset function + * + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Author: Andrzej Pietrasiewicz + * + * 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 + +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; +}; + +void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net); + +#endif /* U_RNDIS_H */ From 9bd4a10e1bf881af0b0a7c117c7092b558447047 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:58 +0200 Subject: [PATCH 86/95] usb: gadget: ether: convert to new interface of f_rndis use new interface so old one can be removed. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 1 + drivers/usb/gadget/ether.c | 56 ++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 22c86089d64717..19373a300ec4fa 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -694,6 +694,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, diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 4d7290a48fe723..f48712ffe26147 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -94,23 +94,14 @@ static inline bool has_rndis(void) #include -/*-------------------------------------------------------------------------*/ - -/* - * 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 "u_ecm.h" #include "u_gether.h" #ifdef USB_ETH_RNDIS -#define USB_FRNDIS_INCLUDED -#include "f_rndis.c" +#include "u_rndis.h" #include "rndis.h" +#else +#define rndis_borrow_net(...) do {} while (0) #endif - #include "u_eem.h" /*-------------------------------------------------------------------------*/ @@ -212,9 +203,6 @@ static struct usb_gadget_strings *dev_strings[] = { NULL, }; -static u8 host_mac[ETH_ALEN]; -static struct eth_dev *the_dev; - static struct usb_function_instance *fi_ecm; static struct usb_function *f_ecm; @@ -224,6 +212,9 @@ 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; + /*-------------------------------------------------------------------------*/ /* @@ -233,6 +224,8 @@ static struct usb_function *f_geth; */ 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)) { @@ -240,7 +233,15 @@ static int __init rndis_do_config(struct usb_configuration *c) c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; } - return rndis_bind_config(c, host_mac, the_dev); + f_rndis = usb_get_function(fi_rndis); + if (IS_ERR(f_rndis)) + return PTR_ERR(f_rndis); + + status = usb_add_function(c, f_rndis); + if (status < 0) + usb_put_function(f_rndis); + + return status; } static struct usb_configuration rndis_config_driver = { @@ -336,7 +337,6 @@ static int __init eth_bind(struct usb_composite_dev *cdev) eem_opts = container_of(fi_eem, struct f_eem_opts, func_inst); net = eem_opts->net; - the_dev = netdev_priv(net); eth_config_driver.label = "CDC Ethernet (EEM)"; device_desc.idVendor = cpu_to_le16(EEM_VENDOR_NUM); @@ -351,7 +351,6 @@ static int __init eth_bind(struct usb_composite_dev *cdev) ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst); net = ecm_opts->net; - the_dev = netdev_priv(net); eth_config_driver.label = "CDC Ethernet (ECM)"; } else { @@ -365,7 +364,6 @@ static int __init eth_bind(struct usb_composite_dev *cdev) func_inst); net = geth_opts->net; - the_dev = netdev_priv(net); eth_config_driver.label = "CDC Subset/SAFE"; @@ -387,7 +385,6 @@ static int __init eth_bind(struct usb_composite_dev *cdev) status = gether_register_netdev(net); if (status) goto fail; - gether_get_host_addr_u8(net, host_mac); if (use_eem) eem_opts->bound = true; @@ -396,6 +393,14 @@ static int __init eth_bind(struct usb_composite_dev *cdev) 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; @@ -407,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; @@ -416,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", @@ -429,6 +434,9 @@ static int __init eth_bind(struct usb_composite_dev *cdev) return 0; +fail1: + if (has_rndis()) + usb_put_function_instance(fi_rndis); fail: if (use_eem) usb_put_function_instance(fi_eem); @@ -441,6 +449,8 @@ static int __init eth_bind(struct usb_composite_dev *cdev) static int __exit eth_unbind(struct usb_composite_dev *cdev) { + 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)) From 76da66d1708d9df4cffa148546853b43249f4d76 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:15:59 +0200 Subject: [PATCH 87/95] usb: gadget: rndis: init & exit rndis at module load/unload This is required in preparation for using usb_gstrings_attach. The rndis initialization so far has been performed on the first occurence of rndis_bind(), but the condition to check it (first or not first) was "borrowed" from strings handling. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_rndis.c | 15 ++------------- drivers/usb/gadget/rndis.c | 4 ++-- drivers/usb/gadget/rndis.h | 3 --- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 437198b6d8fa74..e5c6aee02fd6de 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -698,11 +698,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) #endif 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; @@ -844,7 +839,6 @@ 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); @@ -891,11 +885,9 @@ rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN], rndis->port.func.disable = rndis_disable; status = usb_add_function(c, &rndis->port.func); - if (status) { + if (status) kfree(rndis); fail: - rndis_exit(); - } return status; } @@ -958,7 +950,6 @@ static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_rndis *rndis = func_to_rndis(f); - rndis_exit(); rndis_string_defs[0].id = 0; usb_free_all_descriptors(f); @@ -974,10 +965,8 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) /* allocate and initialize one new instance */ rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); - if (!rndis) { - rndis_exit(); + if (!rndis) return ERR_PTR(-ENOMEM); - } opts = container_of(fi, struct f_rndis_opts, func_inst); diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c index 8c5e95762e59a2..3e3ea720303043 100644 --- a/drivers/usb/gadget/rndis.c +++ b/drivers/usb/gadget/rndis.c @@ -1174,7 +1174,7 @@ int rndis_init(void) return 0; } -EXPORT_SYMBOL(rndis_init); +module_init(rndis_init); void rndis_exit(void) { @@ -1188,6 +1188,6 @@ void rndis_exit(void) } #endif } -EXPORT_SYMBOL(rndis_exit); +module_exit(rndis_exit); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 6e796152a7b2f6..0f4abb4c377502 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -217,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 */ From 4e75e7275652824395696ba1e34c10d368958caf Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:16:00 +0200 Subject: [PATCH 88/95] usb: gadget: f_rndis: use usb_gstrings_attach use new usb_gstrings_attach interface Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_rndis.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index e5c6aee02fd6de..4045ca24e7c8e6 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -670,6 +670,7 @@ 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; @@ -696,16 +697,13 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis_opts->bound = true; } #endif - - if (rndis_string_defs[0].id == 0) { - 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; - } + 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); @@ -840,7 +838,6 @@ rndis_old_unbind(struct usb_configuration *c, struct usb_function *f) rndis_deregister(rndis->config); - rndis_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(rndis->notify_req->buf); @@ -876,7 +873,6 @@ 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; @@ -950,7 +946,6 @@ static void rndis_unbind(struct usb_configuration *c, struct usb_function *f) { struct f_rndis *rndis = func_to_rndis(f); - rndis_string_defs[0].id = 0; usb_free_all_descriptors(f); kfree(rndis->notify_req->buf); @@ -984,7 +979,6 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) 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_unbind; From b3df2faacb40da7d9c4ed1a0b5304cf346e46ca0 Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 28 May 2013 09:16:01 +0200 Subject: [PATCH 89/95] usb: gadget: f_rndis: add configfs support f_rndis learns about configfs so we can, eventually, remove in-kernel gadget drivers. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- .../ABI/testing/configfs-usb-gadget-rndis | 14 ++++++ drivers/usb/gadget/Kconfig | 16 ++++++ drivers/usb/gadget/f_rndis.c | 49 ++++++++++++++++++- drivers/usb/gadget/u_rndis.h | 9 ++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 Documentation/ABI/testing/configfs-usb-gadget-rndis diff --git a/Documentation/ABI/testing/configfs-usb-gadget-rndis b/Documentation/ABI/testing/configfs-usb-gadget-rndis new file mode 100644 index 00000000000000..ff127ddb807ccd --- /dev/null +++ b/Documentation/ABI/testing/configfs-usb-gadget-rndis @@ -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 diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 19373a300ec4fa..b6cd4bd74cb234 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -560,6 +560,22 @@ config USB_CONFIGFS_ECM_SUBSET 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 diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 4045ca24e7c8e6..191df35ae69d04 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -24,6 +24,7 @@ #include #include "u_ether.h" +#include "u_ether_configfs.h" #include "u_rndis.h" #include "rndis.h" @@ -903,6 +904,41 @@ void rndis_borrow_net(struct usb_function_instance *f, struct net_device *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; @@ -924,22 +960,30 @@ static struct usb_function_instance *rndis_alloc_inst(void) 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) @@ -964,12 +1008,15 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) 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; diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/u_rndis.h index d274df56ce757a..c62ba82e960035 100644 --- a/drivers/usb/gadget/u_rndis.h +++ b/drivers/usb/gadget/u_rndis.h @@ -25,6 +25,15 @@ struct f_rndis_opts { 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); From b84a8dee23fd41600a8aebcba1410b5bb5b3bdeb Mon Sep 17 00:00:00 2001 From: Yuan-Hsin Chen Date: Thu, 30 May 2013 15:41:01 +0000 Subject: [PATCH 90/95] usb: gadget: add Faraday fotg210_udc driver Faraday fotg210 udc driver supports only Bulk transfer so far. fotg210 could be configured as an USB2.0 peripheral. This driver is tested with mass storage gadget driver on Faraday EVB a369. Signed-off-by: Yuan-Hsin Chen Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 10 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/fotg210-udc.c | 1219 ++++++++++++++++++++++++++++++ drivers/usb/gadget/fotg210.h | 253 +++++++ 4 files changed, 1483 insertions(+) create mode 100644 drivers/usb/gadget/fotg210-udc.c create mode 100644 drivers/usb/gadget/fotg210.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index b6cd4bd74cb234..2b2a11c8977a88 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -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 diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index b41776065f277a..bad08e66f3697a 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -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 diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/fotg210-udc.c new file mode 100644 index 00000000000000..cce5535b1dc674 --- /dev/null +++ b/drivers/usb/gadget/fotg210-udc.c @@ -0,0 +1,1219 @@ +/* + * FOTG210 UDC Driver supports Bulk transfer so far + * + * Copyright (C) 2013 Faraday Technology Corporation + * + * Author : Yuan-Hsin Chen + * + * 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; version 2 of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fotg210.h" + +#define DRIVER_DESC "FOTG210 USB Device Controller Driver" +#define DRIVER_VERSION "30-April-2013" + +static const char udc_name[] = "fotg210_udc"; +static const char * const fotg210_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4"}; + +static void fotg210_disable_fifo_int(struct fotg210_ep *ep) +{ + u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); + + if (ep->dir_in) + value |= DMISGR1_MF_IN_INT(ep->epnum - 1); + else + value |= DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); + iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); +} + +static void fotg210_enable_fifo_int(struct fotg210_ep *ep) +{ + u32 value = ioread32(ep->fotg210->reg + FOTG210_DMISGR1); + + if (ep->dir_in) + value &= ~DMISGR1_MF_IN_INT(ep->epnum - 1); + else + value &= ~DMISGR1_MF_OUTSPK_INT(ep->epnum - 1); + iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR1); +} + +static void fotg210_set_cxdone(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); + + value |= DCFESR_CX_DONE; + iowrite32(value, fotg210->reg + FOTG210_DCFESR); +} + +static void fotg210_done(struct fotg210_ep *ep, struct fotg210_request *req, + int status) +{ + list_del_init(&req->queue); + + /* don't modify queue heads during completion callback */ + if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) + req->req.status = -ESHUTDOWN; + else + req->req.status = status; + + spin_unlock(&ep->fotg210->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->fotg210->lock); + + if (ep->epnum) { + if (list_empty(&ep->queue)) + fotg210_disable_fifo_int(ep); + } else { + fotg210_set_cxdone(ep->fotg210); + } +} + +static void fotg210_fifo_ep_mapping(struct fotg210_ep *ep, u32 epnum, + u32 dir_in) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 val; + + /* Driver should map an ep to a fifo and then map the fifo + * to the ep. What a brain-damaged design! + */ + + /* map a fifo to an ep */ + val = ioread32(fotg210->reg + FOTG210_EPMAP); + val &= ~EPMAP_FIFONOMSK(epnum, dir_in); + val |= EPMAP_FIFONO(epnum, dir_in); + iowrite32(val, fotg210->reg + FOTG210_EPMAP); + + /* map the ep to the fifo */ + val = ioread32(fotg210->reg + FOTG210_FIFOMAP); + val &= ~FIFOMAP_EPNOMSK(epnum); + val |= FIFOMAP_EPNO(epnum); + iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); + + /* enable fifo */ + val = ioread32(fotg210->reg + FOTG210_FIFOCF); + val |= FIFOCF_FIFO_EN(epnum - 1); + iowrite32(val, fotg210->reg + FOTG210_FIFOCF); +} + +static void fotg210_set_fifo_dir(struct fotg210_ep *ep, u32 epnum, u32 dir_in) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 val; + + val = ioread32(fotg210->reg + FOTG210_FIFOMAP); + val |= (dir_in ? FIFOMAP_DIRIN(epnum - 1) : FIFOMAP_DIROUT(epnum - 1)); + iowrite32(val, fotg210->reg + FOTG210_FIFOMAP); +} + +static void fotg210_set_tfrtype(struct fotg210_ep *ep, u32 epnum, u32 type) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 val; + + val = ioread32(fotg210->reg + FOTG210_FIFOCF); + val |= FIFOCF_TYPE(type, epnum - 1); + iowrite32(val, fotg210->reg + FOTG210_FIFOCF); +} + +static void fotg210_set_mps(struct fotg210_ep *ep, u32 epnum, u32 mps, + u32 dir_in) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 val; + u32 offset = dir_in ? FOTG210_INEPMPSR(epnum) : + FOTG210_OUTEPMPSR(epnum); + + val = ioread32(fotg210->reg + offset); + val |= INOUTEPMPSR_MPS(mps); + iowrite32(val, fotg210->reg + offset); +} + +static int fotg210_config_ep(struct fotg210_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + + fotg210_set_fifo_dir(ep, ep->epnum, ep->dir_in); + fotg210_set_tfrtype(ep, ep->epnum, ep->type); + fotg210_set_mps(ep, ep->epnum, ep->ep.maxpacket, ep->dir_in); + fotg210_fifo_ep_mapping(ep, ep->epnum, ep->dir_in); + + fotg210->ep[ep->epnum] = ep; + + return 0; +} + +static int fotg210_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct fotg210_ep *ep; + + ep = container_of(_ep, struct fotg210_ep, ep); + + ep->desc = desc; + ep->epnum = usb_endpoint_num(desc); + ep->type = usb_endpoint_type(desc); + ep->dir_in = usb_endpoint_dir_in(desc); + ep->ep.maxpacket = usb_endpoint_maxp(desc); + + return fotg210_config_ep(ep, desc); +} + +static void fotg210_reset_tseq(struct fotg210_udc *fotg210, u8 epnum) +{ + struct fotg210_ep *ep = fotg210->ep[epnum]; + u32 value; + void __iomem *reg; + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(epnum); + + /* Note: Driver needs to set and clear INOUTEPMPSR_RESET_TSEQ + * bit. Controller wouldn't clear this bit. WTF!!! + */ + + value = ioread32(reg); + value |= INOUTEPMPSR_RESET_TSEQ; + iowrite32(value, reg); + + value = ioread32(reg); + value &= ~INOUTEPMPSR_RESET_TSEQ; + iowrite32(value, reg); +} + +static int fotg210_ep_release(struct fotg210_ep *ep) +{ + if (!ep->epnum) + return 0; + ep->epnum = 0; + ep->stall = 0; + ep->wedged = 0; + + fotg210_reset_tseq(ep->fotg210, ep->epnum); + + return 0; +} + +static int fotg210_ep_disable(struct usb_ep *_ep) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + unsigned long flags; + + BUG_ON(!_ep); + + ep = container_of(_ep, struct fotg210_ep, ep); + + while (!list_empty(&ep->queue)) { + req = list_entry(ep->queue.next, + struct fotg210_request, queue); + spin_lock_irqsave(&ep->fotg210->lock, flags); + fotg210_done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + } + + return fotg210_ep_release(ep); +} + +static struct usb_request *fotg210_ep_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct fotg210_request *req; + + req = kzalloc(sizeof(struct fotg210_request), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void fotg210_ep_free_request(struct usb_ep *_ep, + struct usb_request *_req) +{ + struct fotg210_request *req; + + req = container_of(_req, struct fotg210_request, req); + kfree(req); +} + +static void fotg210_enable_dma(struct fotg210_ep *ep, + dma_addr_t d, u32 len) +{ + u32 value; + struct fotg210_udc *fotg210 = ep->fotg210; + + /* set transfer length and direction */ + value = ioread32(fotg210->reg + FOTG210_DMACPSR1); + value &= ~(DMACPSR1_DMA_LEN(0xFFFF) | DMACPSR1_DMA_TYPE(1)); + value |= DMACPSR1_DMA_LEN(len) | DMACPSR1_DMA_TYPE(ep->dir_in); + iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); + + /* set device DMA target FIFO number */ + value = ioread32(fotg210->reg + FOTG210_DMATFNR); + if (ep->epnum) + value |= DMATFNR_ACC_FN(ep->epnum - 1); + else + value |= DMATFNR_ACC_CXF; + iowrite32(value, fotg210->reg + FOTG210_DMATFNR); + + /* set DMA memory address */ + iowrite32(d, fotg210->reg + FOTG210_DMACPSR2); + + /* enable MDMA_EROR and MDMA_CMPLT interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMISGR2); + value &= ~(DMISGR2_MDMA_CMPLT | DMISGR2_MDMA_ERROR); + iowrite32(value, fotg210->reg + FOTG210_DMISGR2); + + /* start DMA */ + value = ioread32(fotg210->reg + FOTG210_DMACPSR1); + value |= DMACPSR1_DMA_START; + iowrite32(value, fotg210->reg + FOTG210_DMACPSR1); +} + +static void fotg210_disable_dma(struct fotg210_ep *ep) +{ + iowrite32(DMATFNR_DISDMA, ep->fotg210->reg + FOTG210_DMATFNR); +} + +static void fotg210_wait_dma_done(struct fotg210_ep *ep) +{ + u32 value; + + do { + value = ioread32(ep->fotg210->reg + FOTG210_DISGR2); + if ((value & DISGR2_USBRST_INT) || + (value & DISGR2_DMA_ERROR)) + goto dma_reset; + } while (!(value & DISGR2_DMA_CMPLT)); + + value &= ~DISGR2_DMA_CMPLT; + iowrite32(value, ep->fotg210->reg + FOTG210_DISGR2); + return; + +dma_reset: + value = ioread32(ep->fotg210->reg + FOTG210_DMACPSR1); + value |= DMACPSR1_DMA_ABORT; + iowrite32(value, ep->fotg210->reg + FOTG210_DMACPSR1); + + /* reset fifo */ + if (ep->epnum) { + value = ioread32(ep->fotg210->reg + + FOTG210_FIBCR(ep->epnum - 1)); + value |= FIBCR_FFRST; + iowrite32(value, ep->fotg210->reg + + FOTG210_FIBCR(ep->epnum - 1)); + } else { + value = ioread32(ep->fotg210->reg + FOTG210_DCFESR); + value |= DCFESR_CX_CLR; + iowrite32(value, ep->fotg210->reg + FOTG210_DCFESR); + } +} + +static void fotg210_start_dma(struct fotg210_ep *ep, + struct fotg210_request *req) +{ + dma_addr_t d; + u8 *buffer; + u32 length; + + if (ep->epnum) { + if (ep->dir_in) { + buffer = req->req.buf; + length = req->req.length; + } else { + buffer = req->req.buf + req->req.actual; + length = ioread32(ep->fotg210->reg + + FOTG210_FIBCR(ep->epnum - 1)); + length &= FIBCR_BCFX; + } + } else { + buffer = req->req.buf + req->req.actual; + if (req->req.length - req->req.actual > ep->ep.maxpacket) + length = ep->ep.maxpacket; + else + length = req->req.length; + } + + d = dma_map_single(NULL, buffer, length, + ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(NULL, d)) { + pr_err("dma_mapping_error\n"); + return; + } + + dma_sync_single_for_device(NULL, d, length, + ep->dir_in ? DMA_TO_DEVICE : + DMA_FROM_DEVICE); + + fotg210_enable_dma(ep, d, length); + + /* check if dma is done */ + fotg210_wait_dma_done(ep); + + fotg210_disable_dma(ep); + + /* update actual transfer length */ + req->req.actual += length; + + dma_unmap_single(NULL, d, length, DMA_TO_DEVICE); +} + +static void fotg210_ep0_queue(struct fotg210_ep *ep, + struct fotg210_request *req) +{ + if (!req->req.length) { + fotg210_done(ep, req, 0); + return; + } + if (ep->dir_in) { /* if IN */ + if (req->req.length) { + fotg210_start_dma(ep, req); + } else { + pr_err("%s : req->req.length = 0x%x\n", + __func__, req->req.length); + } + if ((req->req.length == req->req.actual) || + (req->req.actual < ep->ep.maxpacket)) + fotg210_done(ep, req, 0); + } else { /* OUT */ + if (!req->req.length) { + fotg210_done(ep, req, 0); + } else { + u32 value = ioread32(ep->fotg210->reg + + FOTG210_DMISGR0); + + value &= ~DMISGR0_MCX_OUT_INT; + iowrite32(value, ep->fotg210->reg + FOTG210_DMISGR0); + } + } +} + +static int fotg210_ep_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + unsigned long flags; + int request = 0; + + ep = container_of(_ep, struct fotg210_ep, ep); + req = container_of(_req, struct fotg210_request, req); + + if (ep->fotg210->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&ep->fotg210->lock, flags); + + if (list_empty(&ep->queue)) + request = 1; + + list_add_tail(&req->queue, &ep->queue); + + req->req.actual = 0; + req->req.status = -EINPROGRESS; + + if (!ep->epnum) /* ep0 */ + fotg210_ep0_queue(ep, req); + else if (request && !ep->stall) + fotg210_enable_fifo_int(ep); + + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + + return 0; +} + +static int fotg210_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fotg210_ep *ep; + struct fotg210_request *req; + unsigned long flags; + + ep = container_of(_ep, struct fotg210_ep, ep); + req = container_of(_req, struct fotg210_request, req); + + spin_lock_irqsave(&ep->fotg210->lock, flags); + if (!list_empty(&ep->queue)) + fotg210_done(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + + return 0; +} + +static void fotg210_set_epnstall(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 value; + void __iomem *reg; + + /* check if IN FIFO is empty before stall */ + if (ep->dir_in) { + do { + value = ioread32(fotg210->reg + FOTG210_DCFESR); + } while (!(value & DCFESR_FIFO_EMPTY(ep->epnum - 1))); + } + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); + value = ioread32(reg); + value |= INOUTEPMPSR_STL_EP; + iowrite32(value, reg); +} + +static void fotg210_clear_epnstall(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 value; + void __iomem *reg; + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); + value = ioread32(reg); + value &= ~INOUTEPMPSR_STL_EP; + iowrite32(value, reg); +} + +static int fotg210_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedge) +{ + struct fotg210_ep *ep; + struct fotg210_udc *fotg210; + unsigned long flags; + int ret = 0; + + ep = container_of(_ep, struct fotg210_ep, ep); + + fotg210 = ep->fotg210; + + spin_lock_irqsave(&ep->fotg210->lock, flags); + + if (value) { + fotg210_set_epnstall(ep); + ep->stall = 1; + if (wedge) + ep->wedged = 1; + } else { + fotg210_reset_tseq(fotg210, ep->epnum); + fotg210_clear_epnstall(ep); + ep->stall = 0; + ep->wedged = 0; + if (!list_empty(&ep->queue)) + fotg210_enable_fifo_int(ep); + } + + spin_unlock_irqrestore(&ep->fotg210->lock, flags); + return ret; +} + +static int fotg210_ep_set_halt(struct usb_ep *_ep, int value) +{ + return fotg210_set_halt_and_wedge(_ep, value, 0); +} + +static int fotg210_ep_set_wedge(struct usb_ep *_ep) +{ + return fotg210_set_halt_and_wedge(_ep, 1, 1); +} + +static void fotg210_ep_fifo_flush(struct usb_ep *_ep) +{ +} + +static struct usb_ep_ops fotg210_ep_ops = { + .enable = fotg210_ep_enable, + .disable = fotg210_ep_disable, + + .alloc_request = fotg210_ep_alloc_request, + .free_request = fotg210_ep_free_request, + + .queue = fotg210_ep_queue, + .dequeue = fotg210_ep_dequeue, + + .set_halt = fotg210_ep_set_halt, + .fifo_flush = fotg210_ep_fifo_flush, + .set_wedge = fotg210_ep_set_wedge, +}; + +static void fotg210_clear_tx0byte(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_TX0BYTE); + + value &= ~(TX0BYTE_EP1 | TX0BYTE_EP2 | TX0BYTE_EP3 + | TX0BYTE_EP4); + iowrite32(value, fotg210->reg + FOTG210_TX0BYTE); +} + +static void fotg210_clear_rx0byte(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_RX0BYTE); + + value &= ~(RX0BYTE_EP1 | RX0BYTE_EP2 | RX0BYTE_EP3 + | RX0BYTE_EP4); + iowrite32(value, fotg210->reg + FOTG210_RX0BYTE); +} + +/* read 8-byte setup packet only */ +static void fotg210_rdsetupp(struct fotg210_udc *fotg210, + u8 *buffer) +{ + int i = 0; + u8 *tmp = buffer; + u32 data; + u32 length = 8; + + iowrite32(DMATFNR_ACC_CXF, fotg210->reg + FOTG210_DMATFNR); + + for (i = (length >> 2); i > 0; i--) { + data = ioread32(fotg210->reg + FOTG210_CXPORT); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + *(tmp + 3) = (data >> 24) & 0xFF; + tmp = tmp + 4; + } + + switch (length % 4) { + case 1: + data = ioread32(fotg210->reg + FOTG210_CXPORT); + *tmp = data & 0xFF; + break; + case 2: + data = ioread32(fotg210->reg + FOTG210_CXPORT); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + break; + case 3: + data = ioread32(fotg210->reg + FOTG210_CXPORT); + *tmp = data & 0xFF; + *(tmp + 1) = (data >> 8) & 0xFF; + *(tmp + 2) = (data >> 16) & 0xFF; + break; + default: + break; + } + + iowrite32(DMATFNR_DISDMA, fotg210->reg + FOTG210_DMATFNR); +} + +static void fotg210_set_configuration(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DAR); + + value |= DAR_AFT_CONF; + iowrite32(value, fotg210->reg + FOTG210_DAR); +} + +static void fotg210_set_dev_addr(struct fotg210_udc *fotg210, u32 addr) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DAR); + + value |= (addr & 0x7F); + iowrite32(value, fotg210->reg + FOTG210_DAR); +} + +static void fotg210_set_cxstall(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DCFESR); + + value |= DCFESR_CX_STL; + iowrite32(value, fotg210->reg + FOTG210_DCFESR); +} + +static void fotg210_request_error(struct fotg210_udc *fotg210) +{ + fotg210_set_cxstall(fotg210); + pr_err("request error!!\n"); +} + +static void fotg210_set_address(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + if (ctrl->wValue >= 0x0100) { + fotg210_request_error(fotg210); + } else { + fotg210_set_dev_addr(fotg210, ctrl->wValue); + fotg210_set_cxdone(fotg210); + } +} + +static void fotg210_set_feature(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + fotg210_set_cxdone(fotg210); + break; + case USB_RECIP_INTERFACE: + fotg210_set_cxdone(fotg210); + break; + case USB_RECIP_ENDPOINT: { + u8 epnum; + epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; + if (epnum) + fotg210_set_epnstall(fotg210->ep[epnum]); + else + fotg210_set_cxstall(fotg210); + fotg210_set_cxdone(fotg210); + } + break; + default: + fotg210_request_error(fotg210); + break; + } +} + +static void fotg210_clear_feature(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + struct fotg210_ep *ep = + fotg210->ep[ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK]; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + fotg210_set_cxdone(fotg210); + break; + case USB_RECIP_INTERFACE: + fotg210_set_cxdone(fotg210); + break; + case USB_RECIP_ENDPOINT: + if (ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK) { + if (ep->wedged) { + fotg210_set_cxdone(fotg210); + break; + } + if (ep->stall) + fotg210_set_halt_and_wedge(&ep->ep, 0, 0); + } + fotg210_set_cxdone(fotg210); + break; + default: + fotg210_request_error(fotg210); + break; + } +} + +static int fotg210_is_epnstall(struct fotg210_ep *ep) +{ + struct fotg210_udc *fotg210 = ep->fotg210; + u32 value; + void __iomem *reg; + + reg = (ep->dir_in) ? + fotg210->reg + FOTG210_INEPMPSR(ep->epnum) : + fotg210->reg + FOTG210_OUTEPMPSR(ep->epnum); + value = ioread32(reg); + return value & INOUTEPMPSR_STL_EP ? 1 : 0; +} + +static void fotg210_get_status(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + u8 epnum; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + fotg210->ep0_data = 1 << USB_DEVICE_SELF_POWERED; + break; + case USB_RECIP_INTERFACE: + fotg210->ep0_data = 0; + break; + case USB_RECIP_ENDPOINT: + epnum = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; + if (epnum) + fotg210->ep0_data = + fotg210_is_epnstall(fotg210->ep[epnum]) + << USB_ENDPOINT_HALT; + else + fotg210_request_error(fotg210); + break; + + default: + fotg210_request_error(fotg210); + return; /* exit */ + } + + fotg210->ep0_req->buf = &fotg210->ep0_data; + fotg210->ep0_req->length = 2; + + spin_unlock(&fotg210->lock); + fotg210_ep_queue(fotg210->gadget.ep0, fotg210->ep0_req, GFP_KERNEL); + spin_lock(&fotg210->lock); +} + +static int fotg210_setup_packet(struct fotg210_udc *fotg210, + struct usb_ctrlrequest *ctrl) +{ + u8 *p = (u8 *)ctrl; + u8 ret = 0; + + fotg210_rdsetupp(fotg210, p); + + fotg210->ep[0]->dir_in = ctrl->bRequestType & USB_DIR_IN; + + if (fotg210->gadget.speed == USB_SPEED_UNKNOWN) { + u32 value = ioread32(fotg210->reg + FOTG210_DMCR); + fotg210->gadget.speed = value & DMCR_HS_EN ? + USB_SPEED_HIGH : USB_SPEED_FULL; + } + + /* check request */ + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + fotg210_get_status(fotg210, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + fotg210_clear_feature(fotg210, ctrl); + break; + case USB_REQ_SET_FEATURE: + fotg210_set_feature(fotg210, ctrl); + break; + case USB_REQ_SET_ADDRESS: + fotg210_set_address(fotg210, ctrl); + break; + case USB_REQ_SET_CONFIGURATION: + fotg210_set_configuration(fotg210); + ret = 1; + break; + default: + ret = 1; + break; + } + } else { + ret = 1; + } + + return ret; +} + +static void fotg210_ep0out(struct fotg210_udc *fotg210) +{ + struct fotg210_ep *ep = fotg210->ep[0]; + + if (!list_empty(&ep->queue) && !ep->dir_in) { + struct fotg210_request *req; + + req = list_first_entry(&ep->queue, + struct fotg210_request, queue); + + if (req->req.length) + fotg210_start_dma(ep, req); + + if ((req->req.length - req->req.actual) < ep->ep.maxpacket) + fotg210_done(ep, req, 0); + } else { + pr_err("%s : empty queue\n", __func__); + } +} + +static void fotg210_ep0in(struct fotg210_udc *fotg210) +{ + struct fotg210_ep *ep = fotg210->ep[0]; + + if ((!list_empty(&ep->queue)) && (ep->dir_in)) { + struct fotg210_request *req; + + req = list_entry(ep->queue.next, + struct fotg210_request, queue); + + if (req->req.length) + fotg210_start_dma(ep, req); + + if ((req->req.length - req->req.actual) < ep->ep.maxpacket) + fotg210_done(ep, req, 0); + } else { + fotg210_set_cxdone(fotg210); + } +} + +static void fotg210_clear_comabt_int(struct fotg210_udc *fotg210) +{ + u32 value = ioread32(fotg210->reg + FOTG210_DISGR0); + + value &= ~DISGR0_CX_COMABT_INT; + iowrite32(value, fotg210->reg + FOTG210_DISGR0); +} + +static void fotg210_in_fifo_handler(struct fotg210_ep *ep) +{ + struct fotg210_request *req = list_entry(ep->queue.next, + struct fotg210_request, queue); + + if (req->req.length) + fotg210_start_dma(ep, req); + fotg210_done(ep, req, 0); +} + +static void fotg210_out_fifo_handler(struct fotg210_ep *ep) +{ + struct fotg210_request *req = list_entry(ep->queue.next, + struct fotg210_request, queue); + + fotg210_start_dma(ep, req); + + /* finish out transfer */ + if (req->req.length == req->req.actual || + req->req.actual < ep->ep.maxpacket) + fotg210_done(ep, req, 0); +} + +static irqreturn_t fotg210_irq(int irq, void *_fotg210) +{ + struct fotg210_udc *fotg210 = _fotg210; + u32 int_grp = ioread32(fotg210->reg + FOTG210_DIGR); + u32 int_msk = ioread32(fotg210->reg + FOTG210_DMIGR); + + int_grp &= ~int_msk; + + spin_lock(&fotg210->lock); + + if (int_grp & DIGR_INT_G2) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR2; + u32 int_grp2 = ioread32(reg); + u32 int_msk2 = ioread32(fotg210->reg + FOTG210_DMISGR2); + u32 value; + + int_grp2 &= ~int_msk2; + + if (int_grp2 & DISGR2_USBRST_INT) { + value = ioread32(reg); + value &= ~DISGR2_USBRST_INT; + iowrite32(value, reg); + pr_info("fotg210 udc reset\n"); + } + if (int_grp2 & DISGR2_SUSP_INT) { + value = ioread32(reg); + value &= ~DISGR2_SUSP_INT; + iowrite32(value, reg); + pr_info("fotg210 udc suspend\n"); + } + if (int_grp2 & DISGR2_RESM_INT) { + value = ioread32(reg); + value &= ~DISGR2_RESM_INT; + iowrite32(value, reg); + pr_info("fotg210 udc resume\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ERR_INT) { + value = ioread32(reg); + value &= ~DISGR2_ISO_SEQ_ERR_INT; + iowrite32(value, reg); + pr_info("fotg210 iso sequence error\n"); + } + if (int_grp2 & DISGR2_ISO_SEQ_ABORT_INT) { + value = ioread32(reg); + value &= ~DISGR2_ISO_SEQ_ABORT_INT; + iowrite32(value, reg); + pr_info("fotg210 iso sequence abort\n"); + } + if (int_grp2 & DISGR2_TX0BYTE_INT) { + fotg210_clear_tx0byte(fotg210); + value = ioread32(reg); + value &= ~DISGR2_TX0BYTE_INT; + iowrite32(value, reg); + pr_info("fotg210 transferred 0 byte\n"); + } + if (int_grp2 & DISGR2_RX0BYTE_INT) { + fotg210_clear_rx0byte(fotg210); + value = ioread32(reg); + value &= ~DISGR2_RX0BYTE_INT; + iowrite32(value, reg); + pr_info("fotg210 received 0 byte\n"); + } + if (int_grp2 & DISGR2_DMA_ERROR) { + value = ioread32(reg); + value &= ~DISGR2_DMA_ERROR; + iowrite32(value, reg); + } + } + + if (int_grp & DIGR_INT_G0) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR0; + u32 int_grp0 = ioread32(reg); + u32 int_msk0 = ioread32(fotg210->reg + FOTG210_DMISGR0); + struct usb_ctrlrequest ctrl; + + int_grp0 &= ~int_msk0; + + /* the highest priority in this source register */ + if (int_grp0 & DISGR0_CX_COMABT_INT) { + fotg210_clear_comabt_int(fotg210); + pr_info("fotg210 CX command abort\n"); + } + + if (int_grp0 & DISGR0_CX_SETUP_INT) { + if (fotg210_setup_packet(fotg210, &ctrl)) { + spin_unlock(&fotg210->lock); + if (fotg210->driver->setup(&fotg210->gadget, + &ctrl) < 0) + fotg210_set_cxstall(fotg210); + spin_lock(&fotg210->lock); + } + } + if (int_grp0 & DISGR0_CX_COMEND_INT) + pr_info("fotg210 cmd end\n"); + + if (int_grp0 & DISGR0_CX_IN_INT) + fotg210_ep0in(fotg210); + + if (int_grp0 & DISGR0_CX_OUT_INT) + fotg210_ep0out(fotg210); + + if (int_grp0 & DISGR0_CX_COMFAIL_INT) { + fotg210_set_cxstall(fotg210); + pr_info("fotg210 ep0 fail\n"); + } + } + + if (int_grp & DIGR_INT_G1) { + void __iomem *reg = fotg210->reg + FOTG210_DISGR1; + u32 int_grp1 = ioread32(reg); + u32 int_msk1 = ioread32(fotg210->reg + FOTG210_DMISGR1); + int fifo; + + int_grp1 &= ~int_msk1; + + for (fifo = 0; fifo < FOTG210_MAX_FIFO_NUM; fifo++) { + if (int_grp1 & DISGR1_IN_INT(fifo)) + fotg210_in_fifo_handler(fotg210->ep[fifo + 1]); + + if ((int_grp1 & DISGR1_OUT_INT(fifo)) || + (int_grp1 & DISGR1_SPK_INT(fifo))) + fotg210_out_fifo_handler(fotg210->ep[fifo + 1]); + } + } + + spin_unlock(&fotg210->lock); + + return IRQ_HANDLED; +} + +static void fotg210_disable_unplug(struct fotg210_udc *fotg210) +{ + u32 reg = ioread32(fotg210->reg + FOTG210_PHYTMSR); + + reg &= ~PHYTMSR_UNPLUG; + iowrite32(reg, fotg210->reg + FOTG210_PHYTMSR); +} + +static int fotg210_udc_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct fotg210_udc *fotg210 = gadget_to_fotg210(g); + u32 value; + + /* hook up the driver */ + driver->driver.bus = NULL; + fotg210->driver = driver; + + /* enable device global interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMCR); + value |= DMCR_GLINT_EN; + iowrite32(value, fotg210->reg + FOTG210_DMCR); + + return 0; +} + +static void fotg210_init(struct fotg210_udc *fotg210) +{ + u32 value; + + /* disable global interrupt and set int polarity to active high */ + iowrite32(GMIR_MHC_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, + fotg210->reg + FOTG210_GMIR); + + /* disable device global interrupt */ + value = ioread32(fotg210->reg + FOTG210_DMCR); + value &= ~DMCR_GLINT_EN; + iowrite32(value, fotg210->reg + FOTG210_DMCR); + + /* disable all fifo interrupt */ + iowrite32(~(u32)0, fotg210->reg + FOTG210_DMISGR1); + + /* disable cmd end */ + value = ioread32(fotg210->reg + FOTG210_DMISGR0); + value |= DMISGR0_MCX_COMEND; + iowrite32(value, fotg210->reg + FOTG210_DMISGR0); +} + +static int fotg210_udc_stop(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct fotg210_udc *fotg210 = gadget_to_fotg210(g); + unsigned long flags; + + spin_lock_irqsave(&fotg210->lock, flags); + + fotg210_init(fotg210); + fotg210->driver = NULL; + + spin_unlock_irqrestore(&fotg210->lock, flags); + + return 0; +} + +static struct usb_gadget_ops fotg210_gadget_ops = { + .udc_start = fotg210_udc_start, + .udc_stop = fotg210_udc_stop, +}; + +static int __exit fotg210_udc_remove(struct platform_device *pdev) +{ + struct fotg210_udc *fotg210 = dev_get_drvdata(&pdev->dev); + + usb_del_gadget_udc(&fotg210->gadget); + iounmap(fotg210->reg); + free_irq(platform_get_irq(pdev, 0), fotg210); + + fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); + kfree(fotg210); + + return 0; +} + +static int __init fotg210_udc_probe(struct platform_device *pdev) +{ + struct resource *res, *ires; + struct fotg210_udc *fotg210 = NULL; + struct fotg210_ep *_ep[FOTG210_MAX_NUM_EP]; + int ret = 0; + int i; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_err("platform_get_resource error.\n"); + return -ENODEV; + } + + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!ires) { + pr_err("platform_get_resource IORESOURCE_IRQ error.\n"); + return -ENODEV; + } + + ret = -ENOMEM; + + /* initialize udc */ + fotg210 = kzalloc(sizeof(struct fotg210_udc), GFP_KERNEL); + if (fotg210 == NULL) { + pr_err("kzalloc error\n"); + goto err_alloc; + } + + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { + _ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL); + if (_ep[i] == NULL) { + pr_err("_ep kzalloc error\n"); + goto err_alloc; + } + fotg210->ep[i] = _ep[i]; + } + + fotg210->reg = ioremap(res->start, resource_size(res)); + if (fotg210->reg == NULL) { + pr_err("ioremap error.\n"); + goto err_map; + } + + spin_lock_init(&fotg210->lock); + + dev_set_drvdata(&pdev->dev, fotg210); + + fotg210->gadget.ops = &fotg210_gadget_ops; + + fotg210->gadget.max_speed = USB_SPEED_HIGH; + fotg210->gadget.dev.parent = &pdev->dev; + fotg210->gadget.dev.dma_mask = pdev->dev.dma_mask; + fotg210->gadget.name = udc_name; + + INIT_LIST_HEAD(&fotg210->gadget.ep_list); + + for (i = 0; i < FOTG210_MAX_NUM_EP; i++) { + struct fotg210_ep *ep = fotg210->ep[i]; + + if (i) { + INIT_LIST_HEAD(&fotg210->ep[i]->ep.ep_list); + list_add_tail(&fotg210->ep[i]->ep.ep_list, + &fotg210->gadget.ep_list); + } + ep->fotg210 = fotg210; + INIT_LIST_HEAD(&ep->queue); + ep->ep.name = fotg210_ep_name[i]; + ep->ep.ops = &fotg210_ep_ops; + } + fotg210->ep[0]->ep.maxpacket = 0x40; + fotg210->gadget.ep0 = &fotg210->ep[0]->ep; + INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list); + + fotg210->ep0_req = fotg210_ep_alloc_request(&fotg210->ep[0]->ep, + GFP_KERNEL); + if (fotg210->ep0_req == NULL) + goto err_req; + + fotg210_init(fotg210); + + fotg210_disable_unplug(fotg210); + + ret = request_irq(ires->start, fotg210_irq, IRQF_SHARED, + udc_name, fotg210); + if (ret < 0) { + pr_err("request_irq error (%d)\n", ret); + goto err_irq; + } + + ret = usb_add_gadget_udc(&pdev->dev, &fotg210->gadget); + if (ret) + goto err_add_udc; + + dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); + + return 0; + +err_add_udc: +err_irq: + free_irq(ires->start, fotg210); + +err_req: + fotg210_ep_free_request(&fotg210->ep[0]->ep, fotg210->ep0_req); + +err_map: + if (fotg210->reg) + iounmap(fotg210->reg); + +err_alloc: + kfree(fotg210); + + return ret; +} + +static struct platform_driver fotg210_driver = { + .driver = { + .name = (char *)udc_name, + .owner = THIS_MODULE, + }, + .probe = fotg210_udc_probe, + .remove = fotg210_udc_remove, +}; + +module_platform_driver(fotg210_driver); + +MODULE_AUTHOR("Yuan-Hsin Chen "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/usb/gadget/fotg210.h b/drivers/usb/gadget/fotg210.h new file mode 100644 index 00000000000000..bbf991bcbe7c9b --- /dev/null +++ b/drivers/usb/gadget/fotg210.h @@ -0,0 +1,253 @@ +/* + * Faraday FOTG210 USB OTG controller + * + * Copyright (C) 2013 Faraday Technology Corporation + * Author: Yuan-Hsin Chen + * + * 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 + +#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) From 594daba1bcb0510cdc9dccfbab9e6fd5d9cc94e6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 3 Jun 2013 21:43:39 +0530 Subject: [PATCH 91/95] usb: dwc3: omap: improve error handling of dwc3_omap_probe Improved the error handling of dwc3_omap_probe so that on error conditions dwc3_omap is left in the original state. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 34638b92500d06..f8f76e6b7b4679 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -347,7 +347,7 @@ 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); @@ -376,7 +376,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 +384,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) From 758b463d833f818a6416ad96d33c77267aab1afd Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Tue, 4 Jun 2013 15:27:54 +0200 Subject: [PATCH 92/95] usb: gadget: f_mass_storage: fix default product name If cfg->product name is not set, a default name is chosen depending on the common->luns->cdrom flag. If the flag is set the name should be "File-CD Gadget", and if the flag is not set the name should be "File-Stor Gadget". Acked-by: Michal Nazarewicz Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kyungmin Park Signed-off-by: Felipe Balbi --- drivers/usb/gadget/f_mass_storage.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index c35a9ecc576bb9..56f1fd1cba2552 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -2747,8 +2747,8 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, "%-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); /* From 1e2a064c7fa19ec6a9bfbe11cdc9bec34c7834a1 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Wed, 12 Jun 2013 14:53:45 +0530 Subject: [PATCH 93/95] usb: dwc3: omap: Adding am437x specific register map changes AM437x and OMAP5 dwc3 subsytem have different register map. Major differences are as follows. OMAP5 has one main interrupt and one misc interrupt Aegis has four main interrupts and one misc interrupt. Miscellanous Interrupt offsets are changed. UTMI OTG Control and Status Registers offsets are changed. DEBUG Configuration and Status Registers are changed. The main intend of the patch is to re-use the same wrapper driver for both OMAP5 and AM437x, by using the x_major in revision register and adjusting the offsets. This patch adds the register map offsets and adds offset variables in struct dwc3_omap to cache the offsets Signed-off-by: George Cherian Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index f8f76e6b7b4679..f67ff4c4eaca54 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -122,6 +122,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; }; From ff7307b534258c8864c356d15c52a84d0a5fbb35 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Wed, 12 Jun 2013 14:53:46 +0530 Subject: [PATCH 94/95] usb: dwc3: omap: initialize the register offset values for omap5 and AM437x This patch Initializes the register offset values depending on the X_MAJOR of USBOTGSS_REVISION register. Also adds register offset defines and new debug register defines. X_MAJOR is 2 for both OMAP5 and AM437x. But both have different glue register layout. Differentiate AM437x using dt compatible. Register offsets are cached in dwc3_omap struct for reg reads and writes. Signed-off-by: George Cherian Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index f67ff4c4eaca54..54dd6fe886e278 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -61,21 +61,38 @@ #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_IRQ0_OFFSET 0x0004 #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_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) @@ -300,6 +317,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) int irq; int utmi_mode = 0; + int x_major; u32 reg; @@ -356,6 +374,42 @@ static int dwc3_omap_probe(struct platform_device *pdev) goto err0; } + reg = dwc3_omap_readl(omap->base, USBOTGSS_REVISION); + omap->revision = reg; + x_major = USBOTGSS_REVISION_XMAJOR(reg); + + /* Differentiate between OMAP5,AM437x and others*/ + 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_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); of_property_read_u32(node, "utmi-mode", &utmi_mode); @@ -423,6 +477,9 @@ static const struct of_device_id of_dwc3_match[] = { { .compatible = "ti,dwc3" }, + { + .compatible = "ti,am437x-dwc3" + }, { }, }; MODULE_DEVICE_TABLE(of, of_dwc3_match); From b1fd6cb5ee2f97a553d1c4b8a88914bd970daf37 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Wed, 12 Jun 2013 14:53:47 +0530 Subject: [PATCH 95/95] usb: dwc3: omap: Adds dwc3_omap_readl/writel wrappers This patch adds wrappers to dwc3_omap_readl/writel calls to accomodate both OMAP5 and AM437x reg maps (It uses the cached register offsets). Also renames OMAP5 IRQ1 as IRQMISC and IRQ1 bits as IRQMISC bits. Signed-off-by: George Cherian Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 173 +++++++++++++++++++++++------------ 1 file changed, 116 insertions(+), 57 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 54dd6fe886e278..077f110bd74607 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -67,10 +67,18 @@ #define USBOTGSS_IRQENABLE_SET_0 0x002c #define USBOTGSS_IRQENABLE_CLR_0 0x0030 #define USBOTGSS_IRQ0_OFFSET 0x0004 -#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_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 @@ -102,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) @@ -161,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; @@ -173,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: @@ -222,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_read_irq0_status(omap); - reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0); - dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg); + dwc3_omap_write_irq0_status(omap, reg); spin_unlock(&omap->lock); @@ -281,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); - - 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); - - dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg); + dwc3_omap_write_irq0_set(omap, reg); + + 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_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); @@ -378,7 +439,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) omap->revision = reg; x_major = USBOTGSS_REVISION_XMAJOR(reg); - /* Differentiate between OMAP5,AM437x and others*/ + /* Differentiate between OMAP5 and AM437x */ switch (x_major) { case USBOTGSS_REVISION_XMAJOR1: case USBOTGSS_REVISION_XMAJOR2: @@ -410,7 +471,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) omap->debug_offset = USBOTGSS_DEBUG_OFFSET; } - reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS); + reg = dwc3_omap_read_utmi_status(omap); of_property_read_u32(node, "utmi-mode", &utmi_mode); @@ -425,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); @@ -505,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; } @@ -515,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);