From: blogic Date: Sun, 23 Jun 2013 15:50:49 +0000 (+0000) Subject: ralink: update patches X-Git-Url: https://git.archive.openwrt.org/?p=openwrt.git;a=commitdiff_plain;h=deaa1c19469b1b9917ddb58ba6c8417edfd21e94 ralink: update patches Signed-off-by: John Crispin git-svn-id: svn://svn.openwrt.org/openwrt/trunk@37016 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- diff --git a/target/linux/ramips/Makefile b/target/linux/ramips/Makefile index 1214a02ab3..2ddf37da80 100644 --- a/target/linux/ramips/Makefile +++ b/target/linux/ramips/Makefile @@ -9,11 +9,11 @@ include $(TOPDIR)/rules.mk ARCH:=mipsel BOARD:=ramips BOARDNAME:=Ralink RT288x/RT3xxx -SUBTARGETS:=rt288x rt305x rt3883 +SUBTARGETS:=rt288x rt305x rt3883 mt7620a CFLAGS:=-Os -pipe -fno-caller-saves -mno-branch-likely FEATURES:=squashfs gpio -LINUX_VERSION:=3.8.13 +LINUX_VERSION:=3.9.6 include $(INCLUDE_DIR)/target.mk DEFAULT_PACKAGES+=\ diff --git a/target/linux/ramips/dts/DIR-645.dts b/target/linux/ramips/dts/DIR-645.dts index e0b3752006..960d2cb352 100644 --- a/target/linux/ramips/dts/DIR-645.dts +++ b/target/linux/ramips/dts/DIR-645.dts @@ -72,7 +72,10 @@ status = "okay"; mtd-mac-address = <&factory 0x28>; - ralink,fixed-link = <1000 1 1 1>; + port@0 { + ralink,fixed-link = <1000 1 1 0>; + }; + }; wmac@10180000 { @@ -125,14 +128,16 @@ }; }; - gpio_export { - compatible = "gpio-export"; - #size-cells = <0>; + usb0: gpio-regulator { + compatible = "regulator-gpio"; - usb { - gpio-export,name = "usb"; - gpio-export,output = <1>; - gpios = <&gpio1 6 0>; - }; + regulator-type = "voltage"; + regulator-name = "usb-power"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + + enable-active-high; + enable-gpio = <&gpio1 6 0>; }; }; diff --git a/target/linux/ramips/dts/F5D8235_V1.dts b/target/linux/ramips/dts/F5D8235_V1.dts index 41a7f122c8..52b789b76c 100644 --- a/target/linux/ramips/dts/F5D8235_V1.dts +++ b/target/linux/ramips/dts/F5D8235_V1.dts @@ -54,7 +54,9 @@ ethernet@400000 { status = "okay"; - ralink,fixed-link = <1000 1 1 1>; + port@0 { + ralink,fixed-link = <1000 1 1 1>; + }; }; wmac@480000 { diff --git a/target/linux/ramips/dts/FREESTATION5.dts b/target/linux/ramips/dts/FREESTATION5.dts index a3cc8fc97d..442cd268dd 100644 --- a/target/linux/ramips/dts/FREESTATION5.dts +++ b/target/linux/ramips/dts/FREESTATION5.dts @@ -72,15 +72,16 @@ status = "okay"; }; - gpio-export { - compatible = "gpio-export"; + poe: gpio-regulator { // Used to enable power-over-ethernet passthrough from port0 to port1. // Disable passthrough by default to prevent accidental equipment damage. - poe { - gpio-export,name = "poe-passthrough"; - gpio-export,output = <1>; // OUT_INIT_HIGH - gpios = <&gpio0 11 1>; // GPIO 11, ACTIVE_LOW - }; + compatible = "regulator-gpio"; + + regulator-name = "poe-power"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + + enable-gpio = <&gpio0 12 0>; }; gpio-leds { diff --git a/target/linux/ramips/dts/MPRA2.dts b/target/linux/ramips/dts/MPRA2.dts index 8fb226b748..9f8edb6296 100644 --- a/target/linux/ramips/dts/MPRA2.dts +++ b/target/linux/ramips/dts/MPRA2.dts @@ -103,19 +103,27 @@ }; }; - gpio_export { - compatible = "gpio-export"; - #size-cells = <0>; + usb0: gpio-regulator { + compatible = "regulator-gpio"; - usb { - gpio-export,name = "usb"; - gpio-export,output = <1>; - gpios = <&gpio0 7 0>; - }; - root_hub { - gpio-export,name = "root_hub"; - gpio-export,output = <1>; - gpios = <&gpio0 12 0>; - }; + regulator-name = "usb0-power"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + + enable-active-high; + enable-gpio = <&gpio0 7 0>; + }; + + hub0: gpio-regulator { + compatible = "regulator-gpio"; + + regulator-name = "hub0-power"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + + enable-active-high; + enable-gpio = <&gpio0 12 0>; }; }; diff --git a/target/linux/ramips/dts/MT7620a.dts b/target/linux/ramips/dts/MT7620a.dts new file mode 100644 index 0000000000..773ac51fb6 --- /dev/null +++ b/target/linux/ramips/dts/MT7620a.dts @@ -0,0 +1,127 @@ +/dts-v1/; + +/include/ "mt7620a.dtsi" + +/ { + compatible = "ralink,mt7620a-eval-board", "ralink,mt7620a-soc"; + model = "Ralink MT7620a + MT7610e evaluation board"; + + memory@0 { + reg = <0x0 0x2000000>; + }; + + chosen { + bootargs = "console=ttyS0,57600"; + }; + + palmbus@10000000 { + sysc@0 { + ralink,pinmux = "spi", "uartlite", "mdio", "wled", "ephy", "rgmii1", "rgmii2"; + ralink,gpiomux = "i2c", "jtag"; + ralink,uartmux = "gpio"; + ralink,wdtmux = <1>; + }; + + gpio0: gpio@600 { + status = "okay"; + }; + + spi@b00 { + status = "okay"; + + m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "en25q64"; + reg = <0 0>; + linux,modalias = "m25p80", "en25q64"; + spi-max-frequency = <10000000>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x30000>; + read-only; + }; + + partition@30000 { + label = "u-boot-env"; + reg = <0x30000 0x10000>; + read-only; + }; + + factory: partition@40000 { + label = "factory"; + reg = <0x40000 0x10000>; + read-only; + }; + + partition@50000 { + label = "firmware"; + reg = <0x50000 0x7b0000>; + }; + }; + }; + }; + + ethernet@10100000 { + status = "okay"; + + port@4 { + compatible = "lantiq,mt7620a-gsw-port", "ralink,eth-port"; + reg = <4>; + phy-mode = "rgmii"; + phy-handle = <&phy4>; + }; + + port@5 { + compatible = "lantiq,mt7620a-gsw-port", "ralink,eth-port"; + reg = <5>; + phy-mode = "rgmii"; + phy-handle = <&phy5>; + }; + + mdio-bus { + status = "okay"; + + phy4: ethernet-phy@4 { + reg = <4>; + phy-mode = "rgmii"; + }; + + phy5: ethernet-phy@5 { + reg = <5>; + phy-mode = "rgmii"; + }; + }; + }; + + gsw@10110000 { + status = "okay"; + ralink,port4 = "gmac"; + }; + + sdhci@10130000 { + status = "okay"; + }; + + pcie@10140000 { + status = "okay"; + }; + + gpio-keys-polled { + compatible = "gpio-keys"; + #address-cells = <1>; + #size-cells = <0>; + poll-interval = <20>; + s2 { + label = "S2"; + gpios = <&gpio0 1 1>; + linux,code = <0x100>; + }; + s3 { + label = "S3"; + gpios = <&gpio0 2 1>; + linux,code = <0x101>; + }; + }; +}; diff --git a/target/linux/ramips/dts/OMNI-EMB-HPM.dts b/target/linux/ramips/dts/OMNI-EMB-HPM.dts index 8148294893..875df14c66 100644 --- a/target/linux/ramips/dts/OMNI-EMB-HPM.dts +++ b/target/linux/ramips/dts/OMNI-EMB-HPM.dts @@ -131,19 +131,28 @@ }; }; - gpio_export { - compatible = "gpio-export"; - #size-cells = <0>; - /* gpio 12 and 13 handle the OC input */ - usb0 { - gpio-export,name = "usb0"; - gpio-export,output = <1>; - gpios = <&gpio0 2 0>; - }; - usb1 { - gpio-export,name = "usb1"; - gpio-export,output = <1>; - gpios = <&gpio0 1 0>; - }; + /* gpio 12 and 13 handle the OC input */ + usb0: gpio-regulator { + compatible = "regulator-gpio"; + + regulator-name = "usb0-power"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + + enable-active-high; + enable-gpio = <&gpio0 2 0>; + }; + + usb1: gpio-regulator { + compatible = "regulator-gpio"; + + regulator-name = "usb1-power"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + + enable-active-high; + enable-gpio = <&gpio0 1 0>; }; }; diff --git a/target/linux/ramips/dts/RT-N15.dts b/target/linux/ramips/dts/RT-N15.dts index 2ff266cfdd..8351449ea1 100644 --- a/target/linux/ramips/dts/RT-N15.dts +++ b/target/linux/ramips/dts/RT-N15.dts @@ -54,7 +54,9 @@ ethernet@400000 { status = "okay"; - ralink,fixed-link = <1000 1 1 1>; + port@0 { + ralink,fixed-link = <1000 1 1 1>; + }; }; wmac@480000 { diff --git a/target/linux/ramips/dts/RTN56U.dts b/target/linux/ramips/dts/RTN56U.dts index 0c09904f51..9c36af5721 100644 --- a/target/linux/ramips/dts/RTN56U.dts +++ b/target/linux/ramips/dts/RTN56U.dts @@ -31,7 +31,9 @@ ethernet@10100000 { status = "okay"; - ralink,fixed-link = <1000 1 1 1>; + port@0 { + ralink,fixed-link = <1000 1 1 1>; + }; }; pci@10140000 { diff --git a/target/linux/ramips/dts/TEW-691GR.dts b/target/linux/ramips/dts/TEW-691GR.dts index eb2d4bc6af..002719583b 100644 --- a/target/linux/ramips/dts/TEW-691GR.dts +++ b/target/linux/ramips/dts/TEW-691GR.dts @@ -30,9 +30,10 @@ ethernet@10100000 { status = "okay"; - phy-handle = <&phy0>; - phy-mode = "rgmii"; - + port@0 { + phy-handle = <&phy0>; + phy-mode = "rgmii"; + }; mdio-bus { status = "okay"; diff --git a/target/linux/ramips/dts/TEW-692GR.dts b/target/linux/ramips/dts/TEW-692GR.dts index f5d0d04845..eee4b2af2d 100644 --- a/target/linux/ramips/dts/TEW-692GR.dts +++ b/target/linux/ramips/dts/TEW-692GR.dts @@ -30,8 +30,17 @@ ethernet@10100000 { status = "okay"; - ralink,phy-mask = <0x1>; - phy-mode = "rgmii"; + port@0 { + phy-handle = <&phy0>; + phy-mode = "rgmii"; + }; + mdio-bus { + status = "okay"; + + phy0: ethernet-phy@0 { + reg = <0>; + }; + }; }; pci@10140000 { diff --git a/target/linux/ramips/dts/WLI-TX4-AG300N.dts b/target/linux/ramips/dts/WLI-TX4-AG300N.dts index ff33a8a4dc..2296c3fc07 100644 --- a/target/linux/ramips/dts/WLI-TX4-AG300N.dts +++ b/target/linux/ramips/dts/WLI-TX4-AG300N.dts @@ -53,7 +53,9 @@ ethernet@400000 { status = "okay"; - ralink,fixed-link = <100 1 1 1>; + port@0 { + ralink,fixed-link = <1000 1 1 1>; + }; }; wmac@480000 { diff --git a/target/linux/ramips/dts/WR6202.dts b/target/linux/ramips/dts/WR6202.dts index a970849e31..1e41756d5a 100644 --- a/target/linux/ramips/dts/WR6202.dts +++ b/target/linux/ramips/dts/WR6202.dts @@ -102,14 +102,14 @@ status = "okay"; }; - gpio_export { - compatible = "gpio-export"; - #size-cells = <0>; + usb0: gpio-regulator { + compatible = "regulator-gpio"; - usb { - gpio-export,name = "usb"; - gpio-export,output = <0>; - gpios = <&gpio0 11 0>; - }; + regulator-name = "usb0-power"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + + enable-gpio = <&gpio0 11 0>; }; }; diff --git a/target/linux/ramips/dts/mt7620.dtsi b/target/linux/ramips/dts/mt7620.dtsi deleted file mode 100644 index 9f913cd85a..0000000000 --- a/target/linux/ramips/dts/mt7620.dtsi +++ /dev/null @@ -1,140 +0,0 @@ -/ { - #address-cells = <1>; - #size-cells = <1>; - compatible = "ralink,mtk7620n-soc", "ralink,mt7620-soc"; - - cpus { - cpu@0 { - compatible = "mips,mips24KEc"; - }; - }; - - chosen { - bootargs = "console=ttyS0,57600"; - }; - - cpuintc: cpuintc@0 { - #address-cells = <0>; - #interrupt-cells = <1>; - interrupt-controller; - compatible = "mti,cpu-interrupt-controller"; - }; - - palmbus@10000000 { - compatible = "palmbus"; - reg = <0x10000000 0x200000>; - ranges = <0x0 0x10000000 0x1FFFFF>; - - #address-cells = <1>; - #size-cells = <1>; - - sysc@0 { - compatible = "ralink,mt7620-sysc", "ralink,mt7620n-sysc"; - reg = <0x0 0x100>; - }; - - timer@100 { - compatible = "ralink,mt7620-timer", "ralink,rt2880-timer"; - reg = <0x100 0x20>; - - interrupt-parent = <&intc>; - interrupts = <1>; - }; - - watchdog@120 { - compatible = "ralink,mt7620-wdt", "ralink,rt2880-wdt"; - reg = <0x120 0x10>; - }; - - intc: intc@200 { - compatible = "ralink,mt7620-intc", "ralink,rt2880-intc"; - reg = <0x200 0x100>; - - interrupt-controller; - #interrupt-cells = <1>; - - interrupt-parent = <&cpuintc>; - interrupts = <2>; - }; - - memc@300 { - compatible = "ralink,mt7620-memc", "ralink,rt3050-memc"; - reg = <0x300 0x100>; - }; - - gpio0: gpio@600 { - compatible = "ralink,mt7620-gpio", "ralink,rt2880-gpio"; - reg = <0x600 0x34>; - - gpio-controller; - #gpio-cells = <2>; - - ralink,gpio-base = <0>; - ralink,num-gpios = <24>; - ralink,register-map = [ 00 04 08 0c - 20 24 28 2c - 30 34 ]; - }; - - gpio1: gpio@638 { - compatible = "ralink,mt7620-gpio", "ralink,rt2880-gpio"; - reg = <0x638 0x24>; - - gpio-controller; - #gpio-cells = <2>; - - ralink,gpio-base = <24>; - ralink,num-gpios = <16>; - ralink,register-map = [ 00 04 08 0c - 10 14 18 1c - 20 24 ]; - }; - - gpio2: gpio@660 { - compatible = "ralink,mt7620-gpio", "ralink,rt2880-gpio"; - reg = <0x660 0x24>; - - gpio-controller; - #gpio-cells = <2>; - - ralink,gpio-base = <40>; - ralink,num-gpios = <32>; - ralink,register-map = [ 00 04 08 0c - 10 14 18 1c - 20 24 ]; - }; - - gpio3: gpio@688 { - compatible = "ralink,mt7620-gpio", "ralink,rt2880-gpio"; - reg = <0x688 0x24>; - - gpio-controller; - #gpio-cells = <2>; - - ralink,gpio-base = <72>; - ralink,num-gpios = <1>; - ralink,register-map = [ 00 04 08 0c - 10 14 18 1c - 20 24 ]; - }; - - spi@b00 { - compatible = "ralink,rt3883-spi", "ralink,rt2880-spi"; - reg = <0xb00 0x100>; - #address-cells = <1>; - #size-cells = <0>; - - status = "disabled"; - }; - - uartlite@c00 { - compatible = "ralink,mt7620-uart", "ralink,rt2880-uart", "ns16550a"; - reg = <0xc00 0x100>; - - interrupt-parent = <&intc>; - interrupts = <12>; - - reg-shift = <2>; - }; - }; -}; diff --git a/target/linux/ramips/dts/mt7620a.dtsi b/target/linux/ramips/dts/mt7620a.dtsi new file mode 100644 index 0000000000..104abfbfbf --- /dev/null +++ b/target/linux/ramips/dts/mt7620a.dtsi @@ -0,0 +1,294 @@ +/ { + #address-cells = <1>; + #size-cells = <1>; + compatible = "ralink,mtk7620a-soc"; + + cpus { + cpu@0 { + compatible = "mips,mips24KEc"; + }; + }; + + cpuintc: cpuintc@0 { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + compatible = "mti,cpu-interrupt-controller"; + }; + + palmbus@10000000 { + compatible = "palmbus"; + reg = <0x10000000 0x200000>; + ranges = <0x0 0x10000000 0x1FFFFF>; + + #address-cells = <1>; + #size-cells = <1>; + + sysc@0 { + compatible = "ralink,mt7620a-sysc", "ralink,rt3050-sysc"; + reg = <0x0 0x100>; + }; + + timer@100 { + compatible = "ralink,mt7620a-timer", "ralink,rt2880-timer"; + reg = <0x100 0x20>; + + interrupt-parent = <&intc>; + interrupts = <1>; + }; + + watchdog@120 { + compatible = "ralink,mt7620a-wdt", "ralink,rt2880-wdt"; + reg = <0x120 0x10>; + + resets = <&rstctrl 8>; + reset-names = "wdt"; + + interrupt-parent = <&intc>; + interrupts = <1>; + }; + + intc: intc@200 { + compatible = "ralink,mt7620a-intc", "ralink,rt2880-intc"; + reg = <0x200 0x100>; + + resets = <&rstctrl 19>; + reset-names = "intc"; + + interrupt-controller; + #interrupt-cells = <1>; + + interrupt-parent = <&cpuintc>; + interrupts = <2>; + }; + + memc@300 { + compatible = "ralink,mt7620a-memc", "ralink,rt3050-memc"; + reg = <0x300 0x100>; + + resets = <&rstctrl 20>; + reset-names = "mc"; + + interrupt-parent = <&intc>; + interrupts = <3>; + }; + + uart@500 { + compatible = "ralink,mt7620a-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0x500 0x100>; + + resets = <&rstctrl 12>; + reset-names = "uart"; + + interrupt-parent = <&intc>; + interrupts = <5>; + + reg-shift = <2>; + + status = "disabled"; + }; + + gpio0: gpio@600 { + compatible = "ralink,mt7620a-gpio", "ralink,rt2880-gpio"; + reg = <0x600 0x34>; + + resets = <&rstctrl 13>; + reset-names = "pio"; + + interrupt-parent = <&intc>; + interrupts = <6>; + + gpio-controller; + #gpio-cells = <2>; + + ralink,gpio-base = <0>; + ralink,num-gpios = <24>; + ralink,register-map = [ 00 04 08 0c + 20 24 28 2c + 30 34 ]; + + status = "disabled"; + }; + + gpio1: gpio@638 { + compatible = "ralink,mt7620a-gpio", "ralink,rt2880-gpio"; + reg = <0x638 0x24>; + + interrupt-parent = <&intc>; + interrupts = <6>; + + gpio-controller; + #gpio-cells = <2>; + + ralink,gpio-base = <24>; + ralink,num-gpios = <16>; + ralink,register-map = [ 00 04 08 0c + 10 14 18 1c + 20 24 ]; + + status = "disabled"; + }; + + gpio2: gpio@660 { + compatible = "ralink,mt7620a-gpio", "ralink,rt2880-gpio"; + reg = <0x660 0x24>; + + interrupt-parent = <&intc>; + interrupts = <6>; + + gpio-controller; + #gpio-cells = <2>; + + ralink,gpio-base = <40>; + ralink,num-gpios = <32>; + ralink,register-map = [ 00 04 08 0c + 10 14 18 1c + 20 24 ]; + + status = "disabled"; + }; + + i2c@900 { + compatible = "link,mt7620a-i2c", "ralink,rt2880-i2c"; + reg = <0x900 0x100>; + + resets = <&rstctrl 16>; + reset-names = "i2c"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + + spi@b00 { + compatible = "ralink,mt7620a-spi", "ralink,rt2880-spi"; + reg = <0xb00 0x100>; + + resets = <&rstctrl 18>; + reset-names = "spi"; + + #address-cells = <1>; + #size-cells = <1>; + + status = "disabled"; + }; + + uartlite@c00 { + compatible = "ralink,mt7620a-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0xc00 0x100>; + + resets = <&rstctrl 19>; + reset-names = "uartl"; + + interrupt-parent = <&intc>; + interrupts = <12>; + + reg-shift = <2>; + }; + + systick@d00 { + compatible = "ralink,mt7620a-systick", "ralink,cevt-systick"; + reg = <0xd00 0x10>; + + resets = <&rstctrl 28>; + reset-names = "intc"; + + interrupt-parent = <&cpuintc>; + interrupts = <7>; + }; + + gdma@2800 { + compatible = "ralink,mt7620a-gdma", "ralink,rt2880-gdma"; + reg = <0x2800 0x800>; + + resets = <&rstctrl 14>; + reset-names = "dma"; + + interrupt-parent = <&intc>; + interrupts = <7>; + }; + }; + + rstctrl: rstctrl { + compatible = "ralink,mt7620a-reset", "ralink,rt2880-reset"; + #reset-cells = <1>; + }; + + ubsphy { + compatible = "ralink,mt7620a-usbphy"; + + resets = <&rstctrl 22 &rstctrl 25>; + reset-names = "host", "device"; + }; + + ethernet@10100000 { + compatible = "ralink,mt7620a-eth"; + reg = <0x10100000 10000>; + + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&cpuintc>; + interrupts = <5>; + + status = "disabled"; + + mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + }; + + gsw@10110000 { + compatible = "ralink,mt7620a-gsw"; + reg = <0x10110000 8000>; + + interrupt-parent = <&intc>; + interrupts = <17>; + + status = "disabled"; + }; + + sdhci@10130000 { + compatible = "ralink,mt7620a-sdhci"; + reg = <0x10130000 4000>; + + interrupt-parent = <&intc>; + interrupts = <14>; + + status = "disabled"; + }; + + ehci@101c0000 { + compatible = "ralink,rt3xxx-ehci"; + reg = <0x101c0000 0x1000>; + + interrupt-parent = <&intc>; + interrupts = <18>; + }; + + ohci@101c1000 { + compatible = "ralink,rt3xxx-ohci"; + reg = <0x101c1000 0x1000>; + + interrupt-parent = <&intc>; + interrupts = <18>; + }; + + pcie@10140000 { + compatible = "ralink,mt7620a-pci"; + reg = <0x10140000 0x100 + 0x10142000 0x100>; + + resets = <&rstctrl 26>; + reset-names = "pcie0"; + + interrupt-parent = <&cpuintc>; + interrupts = <4>; + + status = "disabled"; + }; +}; diff --git a/target/linux/ramips/dts/mt7620a_mt7610e_eval.dts b/target/linux/ramips/dts/mt7620a_mt7610e_eval.dts new file mode 100644 index 0000000000..0d7755b324 --- /dev/null +++ b/target/linux/ramips/dts/mt7620a_mt7610e_eval.dts @@ -0,0 +1,99 @@ +/dts-v1/; + +/include/ "mt7620a.dtsi" + +/ { + compatible = "ralink,mt7620a-eval-board", "ralink,mt7620a-soc"; + model = "Ralink MT7620A evaluation board"; + + memory@0 { + reg = <0x0 0x2000000>; + }; + + chosen { + bootargs = "console=ttyS0,57600"; + }; + + palmbus@10000000 { + sysc@0 { + ralink,pinmux = "spi", "uartlite", "mdio", "wled", "ephy", "rgmii1", "rgmii2"; + ralink,gpiomux = "i2c", "jtag"; + ralink,uartmux = "gpio"; + ralink,wdtmux = <1>; + }; + + gpio0: gpio@600 { + status = "okay"; + }; + + spi@b00 { + status = "okay"; + + m25p80@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "en25q64"; + reg = <0 0>; + linux,modalias = "m25p80", "en25q64"; + spi-max-frequency = <10000000>; + + partition@0 { + label = "u-boot"; + reg = <0x0 0x30000>; + read-only; + }; + + partition@30000 { + label = "u-boot-env"; + reg = <0x30000 0x10000>; + read-only; + }; + + factory: partition@40000 { + label = "factory"; + reg = <0x40000 0x10000>; + read-only; + }; + + partition@50000 { + label = "firmware"; + reg = <0x50000 0x7b0000>; + }; + }; + }; + }; + + ethernet@10100000 { + status = "okay"; + }; + + gsw@10110000 { + status = "okay"; + ralink,port4 = "ephy"; + }; + + sdhci@10130000 { + status = "okay"; + }; + + pcie@10140000 { + status = "okay"; + }; + + gpio-keys-polled { + compatible = "gpio-keys"; + #address-cells = <1>; + #size-cells = <0>; + poll-interval = <20>; + wps { + label = "wps"; + gpios = <&gpio0 12 1>; + linux,code = <0x100>; + }; + reset { + label = "reset"; + gpios = <&gpio0 13 1>; + linux,code = <0x101>; + }; + }; +}; diff --git a/target/linux/ramips/dts/rt2880.dtsi b/target/linux/ramips/dts/rt2880.dtsi index 637409f355..b51314856d 100644 --- a/target/linux/ramips/dts/rt2880.dtsi +++ b/target/linux/ramips/dts/rt2880.dtsi @@ -13,10 +13,6 @@ bootargs = "console=ttyS0,57600"; }; - memorydetect { - ralink,memory = <0x8000000 0x200000 0x8000000>; - }; - cpuintc: cpuintc@0 { #address-cells = <0>; #interrupt-cells = <1>; diff --git a/target/linux/ramips/dts/rt3050.dtsi b/target/linux/ramips/dts/rt3050.dtsi index 45d9238530..29cd536a51 100644 --- a/target/linux/ramips/dts/rt3050.dtsi +++ b/target/linux/ramips/dts/rt3050.dtsi @@ -13,10 +13,6 @@ bootargs = "console=ttyS0,57600"; }; - memorydetect { - ralink,memory = <0x0 0x200000 0x4000000>; - }; - cpuintc: cpuintc@0 { #address-cells = <0>; #interrupt-cells = <1>; @@ -48,12 +44,21 @@ watchdog@120 { compatible = "ralink,rt3052-wdt", "ralink,rt2880-wdt"; reg = <0x120 0x10>; + + resets = <&rstctrl 8>; + reset-names = "wdt"; + + interrupt-parent = <&intc>; + interrupts = <1>; }; intc: intc@200 { compatible = "ralink,rt3052-intc", "ralink,rt2880-intc"; reg = <0x200 0x100>; + resets = <&rstctrl 19>; + reset-names = "intc"; + interrupt-controller; #interrupt-cells = <1>; @@ -64,6 +69,27 @@ memc@300 { compatible = "ralink,rt3052-memc", "ralink,rt3050-memc"; reg = <0x300 0x100>; + + resets = <&rstctrl 20>; + reset-names = "mc"; + + interrupt-parent = <&intc>; + interrupts = <3>; + }; + + uart@500 { + compatible = "ralink,rt5350-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0x500 0x100>; + + resets = <&rstctrl 12>; + reset-names = "uart"; + + interrupt-parent = <&intc>; + interrupts = <5>; + + reg-shift = <2>; + + status = "disabled"; }; gpio0: gpio@600 { @@ -79,6 +105,12 @@ 20 24 28 2c 30 34 ]; + resets = <&rstctrl 13>; + reset-names = "pio"; + + interrupt-parent = <&intc>; + interrupts = <6>; + status = "disabled"; }; @@ -117,6 +149,10 @@ spi@b00 { compatible = "ralink,rt3050-spi", "ralink,rt2880-spi"; reg = <0xb00 0x100>; + + resets = <&rstctrl 18>; + reset-names = "spi"; + #address-cells = <1>; #size-cells = <0>; @@ -126,6 +162,9 @@ uartlite@c00 { compatible = "ralink,rt3052-uart", "ralink,rt2880-uart", "ns16550a"; reg = <0xc00 0x100>; + + resets = <&rstctrl 19>; + reset-names = "uartl"; interrupt-parent = <&intc>; interrupts = <12>; @@ -135,6 +174,11 @@ }; + rstctrl: rstctrl { + compatible = "ralink,rt3050-reset", "ralink,rt2880-reset"; + #reset-cells = <1>; + }; + ethernet@10100000 { compatible = "ralink,rt3050-eth"; reg = <0x10100000 10000>; diff --git a/target/linux/ramips/dts/rt3352.dtsi b/target/linux/ramips/dts/rt3352.dtsi index 9c7c268b88..e4d35d87c2 100644 --- a/target/linux/ramips/dts/rt3352.dtsi +++ b/target/linux/ramips/dts/rt3352.dtsi @@ -13,10 +13,6 @@ bootargs = "console=ttyS0,57600"; }; - memorydetect { - ralink,memory = <0x0 0x200000 0x10000000>; - }; - cpuintc: cpuintc@0 { #address-cells = <0>; #interrupt-cells = <1>; @@ -48,6 +44,12 @@ watchdog@120 { compatible = "ralink,rt3352-wdt", "ralink,rt2880-wdt"; reg = <0x120 0x10>; + + resets = <&rstctrl 8>; + reset-names = "wdt"; + + interrupt-parent = <&intc>; + interrupts = <1>; }; intc: intc@200 { @@ -64,6 +66,27 @@ memc@300 { compatible = "ralink,rt3352-memc", "ralink,rt3050-memc"; reg = <0x300 0x100>; + + resets = <&rstctrl 20>; + reset-names = "mc"; + + interrupt-parent = <&intc>; + interrupts = <3>; + }; + + uart@500 { + compatible = "ralink,rt3352-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0x500 0x100>; + + resets = <&rstctrl 12>; + reset-names = "uart"; + + interrupt-parent = <&intc>; + interrupts = <5>; + + reg-shift = <2>; + + status = "disabled"; }; gpio0: gpio@600 { @@ -78,6 +101,11 @@ ralink,register-map = [ 00 04 08 0c 20 24 28 2c 30 34 ]; + resets = <&rstctrl 13>; + reset-names = "pio"; + + interrupt-parent = <&intc>; + interrupts = <6>; status = "disabled"; }; @@ -120,6 +148,9 @@ #address-cells = <1>; #size-cells = <1>; + resets = <&rstctrl 18>; + reset-names = "spi"; + status = "disabled"; }; @@ -127,6 +158,9 @@ compatible = "ralink,rt3352-uart", "ralink,rt2880-uart", "ns16550a"; reg = <0xc00 0x100>; + resets = <&rstctrl 19>; + reset-names = "uartl"; + interrupt-parent = <&intc>; interrupts = <12>; @@ -134,6 +168,11 @@ }; }; + rstctrl: rstctrl { + compatible = "ralink,rt3352-reset", "ralink,rt2880-reset"; + #reset-cells = <1>; + }; + ethernet@10100000 { compatible = "ralink,rt3352-eth", "ralink,rt3050-eth"; reg = <0x10100000 10000>; diff --git a/target/linux/ramips/dts/rt3883.dtsi b/target/linux/ramips/dts/rt3883.dtsi index 6f824d5e86..046c9e9a92 100644 --- a/target/linux/ramips/dts/rt3883.dtsi +++ b/target/linux/ramips/dts/rt3883.dtsi @@ -17,10 +17,6 @@ spi0 = &spi0; }; - memorydetect { - ralink,memory = <0x0 0x200000 0x10000000>; - }; - cpuintc: cpuintc@0 { #address-cells = <0>; #interrupt-cells = <1>; @@ -52,12 +48,21 @@ watchdog@120 { compatible = "ralink,rt3883-wdt", "ralink,rt2880-wdt"; reg = <0x120 0x10>; + + resets = <&rstctrl 8>; + reset-names = "wdt"; + + interrupt-parent = <&intc>; + interrupts = <1>; }; intc: intc@200 { compatible = "ralink,rt3883-intc", "ralink,rt2880-intc"; reg = <0x200 0x100>; + resets = <&rstctrl 19>; + reset-names = "intc"; + interrupt-controller; #interrupt-cells = <1>; @@ -68,12 +73,39 @@ memc@300 { compatible = "ralink,rt3883-memc", "ralink,rt3050-memc"; reg = <0x300 0x100>; + + resets = <&rstctrl 20>; + reset-names = "mc"; + + interrupt-parent = <&intc>; + interrupts = <3>; + }; + + uart@500 { + compatible = "ralink,rt3883-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0x500 0x100>; + + resets = <&rstctrl 12>; + reset-names = "uart"; + + interrupt-parent = <&intc>; + interrupts = <5>; + + reg-shift = <2>; + + status = "disabled"; }; gpio0: gpio@600 { compatible = "ralink,rt3883-gpio", "ralink,rt2880-gpio"; reg = <0x600 0x34>; + resets = <&rstctrl 13>; + reset-names = "pio"; + + interrupt-parent = <&intc>; + interrupts = <6>; + gpio-controller; #gpio-cells = <2>; @@ -140,6 +172,9 @@ #address-cells = <1>; #size-cells = <0>; + resets = <&rstctrl 18>; + reset-names = "spi"; + status = "disabled"; }; @@ -147,6 +182,9 @@ compatible = "ralink,rt3883-uart", "ralink,rt2880-uart", "ns16550a"; reg = <0xc00 0x100>; + resets = <&rstctrl 19>; + reset-names = "uartl"; + interrupt-parent = <&intc>; interrupts = <12>; @@ -163,6 +201,11 @@ status = "disabled"; + port@0 { + compatible = "lantiq,rt3883-port", "ralink,eth-port"; + reg = <0>; + }; + mdio-bus { #address-cells = <1>; #size-cells = <0>; @@ -171,6 +214,11 @@ }; }; + rstctrl: rstctrl { + compatible = "ralink,rt3883-reset", "ralink,rt2880-reset"; + #reset-cells = <1>; + }; + pci@10140000 { compatible = "ralink,rt3883-pci"; reg = <0x10140000 0x20000>; @@ -257,6 +305,13 @@ }; }; + ubsphy { + compatible = "ralink,rt3xxx-usbphy"; + + resets = <&rstctrl 22 &rstctrl 25>; + reset-names = "host", "device"; + }; + wmac@10180000 { compatible = "ralink,rt3883-wmac", "ralink,rt2880-wmac"; reg = <0x10180000 40000>; diff --git a/target/linux/ramips/dts/rt5350.dtsi b/target/linux/ramips/dts/rt5350.dtsi index c48d7810f6..e132699da3 100644 --- a/target/linux/ramips/dts/rt5350.dtsi +++ b/target/linux/ramips/dts/rt5350.dtsi @@ -13,10 +13,6 @@ bootargs = "console=ttyS0,57600"; }; - memorydetect { - ralink,memory = <0x0 0x200000 0x4000000>; - }; - cpuintc: cpuintc@0 { #address-cells = <0>; #interrupt-cells = <1>; @@ -48,12 +44,21 @@ watchdog@120 { compatible = "ralink,rt5350-wdt", "ralink,rt2880-wdt"; reg = <0x120 0x10>; + + resets = <&rstctrl 8>; + reset-names = "wdt"; + + interrupt-parent = <&intc>; + interrupts = <1>; }; intc: intc@200 { compatible = "ralink,rt5350-intc", "ralink,rt2880-intc"; reg = <0x200 0x100>; + resets = <&rstctrl 19>; + reset-names = "intc"; + interrupt-controller; #interrupt-cells = <1>; @@ -64,12 +69,39 @@ memc@300 { compatible = "ralink,rt5350-memc", "ralink,rt3050-memc"; reg = <0x300 0x100>; + + resets = <&rstctrl 20>; + reset-names = "mc"; + + interrupt-parent = <&intc>; + interrupts = <3>; + }; + + uart@500 { + compatible = "ralink,rt5350-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0x500 0x100>; + + resets = <&rstctrl 12>; + reset-names = "uart"; + + interrupt-parent = <&intc>; + interrupts = <5>; + + reg-shift = <2>; + + status = "disabled"; }; gpio0: gpio@600 { compatible = "ralink,rt5350-gpio", "ralink,rt2880-gpio"; reg = <0x600 0x34>; + resets = <&rstctrl 13>; + reset-names = "pio"; + + interrupt-parent = <&intc>; + interrupts = <6>; + gpio-controller; #gpio-cells = <2>; @@ -86,6 +118,9 @@ compatible = "ralink,rt5350-gpio", "ralink,rt2880-gpio"; reg = <0x638 0x24>; + interrupt-parent = <&intc>; + interrupts = <6>; + gpio-controller; #gpio-cells = <2>; @@ -102,6 +137,9 @@ compatible = "ralink,rt5350-gpio", "ralink,rt2880-gpio"; reg = <0x660 0x24>; + interrupt-parent = <&intc>; + interrupts = <6>; + gpio-controller; #gpio-cells = <2>; @@ -114,9 +152,26 @@ status = "disabled"; }; + i2c@900 { + compatible = "link,rt5350-i2c", "ralink,rt2880-i2c"; + reg = <0x900 0x100>; + + resets = <&rstctrl 16>; + reset-names = "i2c"; + + #address-cells = <1>; + #size-cells = <0>; + + status = "disabled"; + }; + spi@b00 { compatible = "ralink,rt5350-spi", "ralink,rt2880-spi"; reg = <0xb00 0x100>; + + resets = <&rstctrl 18>; + reset-names = "spi"; + #address-cells = <1>; #size-cells = <1>; @@ -127,15 +182,38 @@ compatible = "ralink,rt5350-uart", "ralink,rt2880-uart", "ns16550a"; reg = <0xc00 0x100>; + resets = <&rstctrl 19>; + reset-names = "uartl"; + interrupt-parent = <&intc>; interrupts = <12>; reg-shift = <2>; }; + + systick@d00 { + compatible = "ralink,rt5350-systick", "ralink,cevt-systick"; + reg = <0xd00 0x10>; + + interrupt-parent = <&cpuintc>; + interrupts = <7>; + }; + }; + + rstctrl: rstctrl { + compatible = "ralink,rt5350-reset", "ralink,rt2880-reset"; + #reset-cells = <1>; + }; + + ubsphy { + compatible = "ralink,rt3xxx-usbphy"; + + resets = <&rstctrl 22 &rstctrl 25>; + reset-names = "host", "device"; }; ethernet@10100000 { - compatible = "ralink,rt5350-eth", "ralink,rt3050-eth"; + compatible = "ralink,rt5350-eth"; reg = <0x10100000 10000>; interrupt-parent = <&cpuintc>; @@ -145,7 +223,7 @@ }; esw@10110000 { - compatible = "ralink,rt5350-esw", "ralink,rt3050-esw"; + compatible = "ralink,rt3050-esw"; reg = <0x10110000 8000>; interrupt-parent = <&intc>; @@ -167,22 +245,18 @@ }; ehci@101c0000 { - compatible = "ralink,rt5350-ehci", "ehci-platform"; + compatible = "ralink,rt3xxx-ehci", "ehci-platform"; reg = <0x101c0000 0x1000>; interrupt-parent = <&intc>; interrupts = <18>; - - status = "disabled"; }; ohci@101c1000 { - compatible = "ralink,rt5350-ohci", "ohci-platform"; + compatible = "ralink,rt3xxx-ohci", "ohci-platform"; reg = <0x101c1000 0x1000>; interrupt-parent = <&intc>; interrupts = <18>; - - status = "disabled"; }; }; diff --git a/target/linux/ramips/image/Makefile b/target/linux/ramips/image/Makefile index f00eb6c1f4..5f2c3113d2 100644 --- a/target/linux/ramips/image/Makefile +++ b/target/linux/ramips/image/Makefile @@ -519,6 +519,18 @@ define Image/Build/Profile/Default endef endif +# +# MT7620A Profiles +# + +Image/Build/Profile/MT7620a=$(call BuildFirmware/Default8M/$(1),$(1),mt7620a,MT7620a) + +ifeq ($(SUBTARGET),mt7620a) +define Image/Build/Profile/Default + $(call Image/Build/Profile/MT7620a,$(1)) +endef +endif + # # Generic Targets diff --git a/target/linux/ramips/image/lzma-loader/Makefile b/target/linux/ramips/image/lzma-loader/Makefile new file mode 100644 index 0000000000..783301695f --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/Makefile @@ -0,0 +1,65 @@ +# +# Copyright (C) 2011 OpenWrt.org +# Copyright (C) 2011 Gabor Juhos +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk + +LZMA_TEXT_START := 0x80a00000 +LOADER := loader.bin +LOADER_NAME := $(basename $(notdir $(LOADER))) +LOADER_DATA := +TARGET_DIR := +FLASH_OFFS := +FLASH_MAX := +BOARD := + +ifeq ($(TARGET_DIR),) +TARGET_DIR := $(KDIR) +endif + +LOADER_BIN := $(TARGET_DIR)/$(LOADER_NAME).bin +LOADER_GZ := $(TARGET_DIR)/$(LOADER_NAME).gz +LOADER_ELF := $(TARGET_DIR)/$(LOADER_NAME).elf + +PKG_NAME := lzma-loader +PKG_BUILD_DIR := $(KDIR)/$(PKG_NAME) + +.PHONY : loader-compile loader.bin loader.elf loader.gz + +$(PKG_BUILD_DIR)/.prepared: + mkdir $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ + touch $@ + +loader-compile: $(PKG_BUILD_DIR)/.prepared + $(MAKE) -C $(PKG_BUILD_DIR) CROSS_COMPILE="$(TARGET_CROSS)" \ + LZMA_TEXT_START=$(LZMA_TEXT_START) \ + LOADER_DATA=$(LOADER_DATA) \ + FLASH_OFFS=$(FLASH_OFFS) \ + FLASH_MAX=$(FLASH_MAX) \ + BOARD="$(BOARD)" \ + PLATFORM="ralink" \ + clean all + +loader.gz: $(PKG_BUILD_DIR)/loader.bin + gzip -nc9 $< > $(LOADER_GZ) + +loader.elf: $(PKG_BUILD_DIR)/loader.elf + $(CP) $< $(LOADER_ELF) + +loader.bin: $(PKG_BUILD_DIR)/loader.bin + $(CP) $< $(LOADER_BIN) + +download: +prepare: $(PKG_BUILD_DIR)/.prepared +compile: loader-compile + +install: + +clean: + rm -rf $(PKG_BUILD_DIR) + diff --git a/target/linux/ramips/image/lzma-loader/src/LzmaDecode.c b/target/linux/ramips/image/lzma-loader/src/LzmaDecode.c new file mode 100644 index 0000000000..cb8345377e --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/LzmaDecode.c @@ -0,0 +1,584 @@ +/* + LzmaDecode.c + LZMA Decoder (optimized for Speed version) + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this Code, expressly permits you to + statically or dynamically link your Code (or bind by name) to the + interfaces of this file without subjecting your linked Code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#include "LzmaDecode.h" + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_READ_BYTE (*Buffer++) + +#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \ + { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }} + +#ifdef _LZMA_IN_CB + +#define RC_TEST { if (Buffer == BufferLim) \ + { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \ + BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }} + +#define RC_INIT Buffer = BufferLim = 0; RC_INIT2 + +#else + +#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; } + +#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2 + +#endif + +#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; } + +#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound) +#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits; +#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits; + +#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \ + { UpdateBit0(p); mi <<= 1; A0; } else \ + { UpdateBit1(p); mi = (mi + mi) + 1; A1; } + +#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;) + +#define RangeDecoderBitTreeDecode(probs, numLevels, res) \ + { int i = numLevels; res = 1; \ + do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \ + res -= (1 << numLevels); } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size) +{ + unsigned char prop0; + if (size < LZMA_PROPERTIES_SIZE) + return LZMA_RESULT_DATA_ERROR; + prop0 = propsData[0]; + if (prop0 >= (9 * 5 * 5)) + return LZMA_RESULT_DATA_ERROR; + { + for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5)); + for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9); + propsRes->lc = prop0; + /* + unsigned char remainder = (unsigned char)(prop0 / 9); + propsRes->lc = prop0 % 9; + propsRes->pb = remainder / 5; + propsRes->lp = remainder % 5; + */ + } + + #ifdef _LZMA_OUT_READ + { + int i; + propsRes->DictionarySize = 0; + for (i = 0; i < 4; i++) + propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8); + if (propsRes->DictionarySize == 0) + propsRes->DictionarySize = 1; + } + #endif + return LZMA_RESULT_OK; +} + +#define kLzmaStreamWasFinishedId (-1) + +int LzmaDecode(CLzmaDecoderState *vs, + #ifdef _LZMA_IN_CB + ILzmaInCallback *InCallback, + #else + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + #endif + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed) +{ + CProb *p = vs->Probs; + SizeT nowPos = 0; + Byte previousByte = 0; + UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1; + UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1; + int lc = vs->Properties.lc; + + #ifdef _LZMA_OUT_READ + + UInt32 Range = vs->Range; + UInt32 Code = vs->Code; + #ifdef _LZMA_IN_CB + const Byte *Buffer = vs->Buffer; + const Byte *BufferLim = vs->BufferLim; + #else + const Byte *Buffer = inStream; + const Byte *BufferLim = inStream + inSize; + #endif + int state = vs->State; + UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3]; + int len = vs->RemainLen; + UInt32 globalPos = vs->GlobalPos; + UInt32 distanceLimit = vs->DistanceLimit; + + Byte *dictionary = vs->Dictionary; + UInt32 dictionarySize = vs->Properties.DictionarySize; + UInt32 dictionaryPos = vs->DictionaryPos; + + Byte tempDictionary[4]; + + #ifndef _LZMA_IN_CB + *inSizeProcessed = 0; + #endif + *outSizeProcessed = 0; + if (len == kLzmaStreamWasFinishedId) + return LZMA_RESULT_OK; + + if (dictionarySize == 0) + { + dictionary = tempDictionary; + dictionarySize = 1; + tempDictionary[0] = vs->TempDictionary[0]; + } + + if (len == kLzmaNeedInitId) + { + { + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); + UInt32 i; + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + rep0 = rep1 = rep2 = rep3 = 1; + state = 0; + globalPos = 0; + distanceLimit = 0; + dictionaryPos = 0; + dictionary[dictionarySize - 1] = 0; + #ifdef _LZMA_IN_CB + RC_INIT; + #else + RC_INIT(inStream, inSize); + #endif + } + len = 0; + } + while(len != 0 && nowPos < outSize) + { + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos]; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + len--; + } + if (dictionaryPos == 0) + previousByte = dictionary[dictionarySize - 1]; + else + previousByte = dictionary[dictionaryPos - 1]; + + #else /* if !_LZMA_OUT_READ */ + + int state = 0; + UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1; + int len = 0; + const Byte *Buffer; + const Byte *BufferLim; + UInt32 Range; + UInt32 Code; + + #ifndef _LZMA_IN_CB + *inSizeProcessed = 0; + #endif + *outSizeProcessed = 0; + + { + UInt32 i; + UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp)); + for (i = 0; i < numProbs; i++) + p[i] = kBitModelTotal >> 1; + } + + #ifdef _LZMA_IN_CB + RC_INIT; + #else + RC_INIT(inStream, inSize); + #endif + + #endif /* _LZMA_OUT_READ */ + + while(nowPos < outSize) + { + CProb *prob; + UInt32 bound; + int posState = (int)( + (nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + & posStateMask); + + prob = p + IsMatch + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + int symbol = 1; + UpdateBit0(prob) + prob = p + Literal + (LZMA_LIT_SIZE * + ((( + (nowPos + #ifdef _LZMA_OUT_READ + + globalPos + #endif + ) + & literalPosMask) << lc) + (previousByte >> (8 - lc)))); + + if (state >= kNumLitStates) + { + int matchByte; + #ifdef _LZMA_OUT_READ + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + matchByte = dictionary[pos]; + #else + matchByte = outStream[nowPos - rep0]; + #endif + do + { + int bit; + CProb *probLit; + matchByte <<= 1; + bit = (matchByte & 0x100); + probLit = prob + 0x100 + bit + symbol; + RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break) + } + while (symbol < 0x100); + } + while (symbol < 0x100) + { + CProb *probLit = prob + symbol; + RC_GET_BIT(probLit, symbol) + } + previousByte = (Byte)symbol; + + outStream[nowPos++] = previousByte; + #ifdef _LZMA_OUT_READ + if (distanceLimit < dictionarySize) + distanceLimit++; + + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #endif + if (state < 4) state = 0; + else if (state < 10) state -= 3; + else state -= 6; + } + else + { + UpdateBit1(prob); + prob = p + IsRep + state; + IfBit0(prob) + { + UpdateBit0(prob); + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + state = state < kNumLitStates ? 0 : 3; + prob = p + LenCoder; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG0 + state; + IfBit0(prob) + { + UpdateBit0(prob); + prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState; + IfBit0(prob) + { + #ifdef _LZMA_OUT_READ + UInt32 pos; + #endif + UpdateBit0(prob); + + #ifdef _LZMA_OUT_READ + if (distanceLimit == 0) + #else + if (nowPos == 0) + #endif + return LZMA_RESULT_DATA_ERROR; + + state = state < kNumLitStates ? 9 : 11; + #ifdef _LZMA_OUT_READ + pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #else + previousByte = outStream[nowPos - rep0]; + #endif + outStream[nowPos++] = previousByte; + #ifdef _LZMA_OUT_READ + if (distanceLimit < dictionarySize) + distanceLimit++; + #endif + + continue; + } + else + { + UpdateBit1(prob); + } + } + else + { + UInt32 distance; + UpdateBit1(prob); + prob = p + IsRepG1 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep1; + } + else + { + UpdateBit1(prob); + prob = p + IsRepG2 + state; + IfBit0(prob) + { + UpdateBit0(prob); + distance = rep2; + } + else + { + UpdateBit1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = p + RepLenCoder; + } + { + int numBits, offset; + CProb *probLen = prob + LenChoice; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + numBits = kLenNumLowBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenChoice2; + IfBit0(probLen) + { + UpdateBit0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + numBits = kLenNumMidBits; + } + else + { + UpdateBit1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + numBits = kLenNumHighBits; + } + } + RangeDecoderBitTreeDecode(probLen, numBits, len); + len += offset; + } + + if (state < 4) + { + int posSlot; + state += kNumLitStates; + prob = p + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + int numDirectBits = ((posSlot >> 1) - 1); + rep0 = (2 | ((UInt32)posSlot & 1)); + if (posSlot < kEndPosModelIndex) + { + rep0 <<= numDirectBits; + prob = p + SpecPos + rep0 - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + RC_NORMALIZE + Range >>= 1; + rep0 <<= 1; + if (Code >= Range) + { + Code -= Range; + rep0 |= 1; + } + } + while (--numDirectBits != 0); + prob = p + Align; + rep0 <<= kNumAlignBits; + numDirectBits = kNumAlignBits; + } + { + int i = 1; + int mi = 1; + do + { + CProb *prob3 = prob + mi; + RC_GET_BIT2(prob3, mi, ; , rep0 |= i); + i <<= 1; + } + while(--numDirectBits != 0); + } + } + else + rep0 = posSlot; + if (++rep0 == (UInt32)(0)) + { + /* it's for stream version */ + len = kLzmaStreamWasFinishedId; + break; + } + } + + len += kMatchMinLen; + #ifdef _LZMA_OUT_READ + if (rep0 > distanceLimit) + #else + if (rep0 > nowPos) + #endif + return LZMA_RESULT_DATA_ERROR; + + #ifdef _LZMA_OUT_READ + if (dictionarySize - distanceLimit > (UInt32)len) + distanceLimit += len; + else + distanceLimit = dictionarySize; + #endif + + do + { + #ifdef _LZMA_OUT_READ + UInt32 pos = dictionaryPos - rep0; + if (pos >= dictionarySize) + pos += dictionarySize; + previousByte = dictionary[pos]; + dictionary[dictionaryPos] = previousByte; + if (++dictionaryPos == dictionarySize) + dictionaryPos = 0; + #else + previousByte = outStream[nowPos - rep0]; + #endif + len--; + outStream[nowPos++] = previousByte; + } + while(len != 0 && nowPos < outSize); + } + } + RC_NORMALIZE; + + #ifdef _LZMA_OUT_READ + vs->Range = Range; + vs->Code = Code; + vs->DictionaryPos = dictionaryPos; + vs->GlobalPos = globalPos + (UInt32)nowPos; + vs->DistanceLimit = distanceLimit; + vs->Reps[0] = rep0; + vs->Reps[1] = rep1; + vs->Reps[2] = rep2; + vs->Reps[3] = rep3; + vs->State = state; + vs->RemainLen = len; + vs->TempDictionary[0] = tempDictionary[0]; + #endif + + #ifdef _LZMA_IN_CB + vs->Buffer = Buffer; + vs->BufferLim = BufferLim; + #else + *inSizeProcessed = (SizeT)(Buffer - inStream); + #endif + *outSizeProcessed = nowPos; + return LZMA_RESULT_OK; +} diff --git a/target/linux/ramips/image/lzma-loader/src/LzmaDecode.h b/target/linux/ramips/image/lzma-loader/src/LzmaDecode.h new file mode 100644 index 0000000000..2870eeb9c9 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/LzmaDecode.h @@ -0,0 +1,113 @@ +/* + LzmaDecode.h + LZMA Decoder interface + + LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01) + http://www.7-zip.org/ + + LZMA SDK is licensed under two licenses: + 1) GNU Lesser General Public License (GNU LGPL) + 2) Common Public License (CPL) + It means that you can select one of these two licenses and + follow rules of that license. + + SPECIAL EXCEPTION: + Igor Pavlov, as the author of this code, expressly permits you to + statically or dynamically link your code (or bind by name) to the + interfaces of this file without subjecting your linked code to the + terms of the CPL or GNU LGPL. Any modifications or additions + to this file, however, are subject to the LGPL or CPL terms. +*/ + +#ifndef __LZMADECODE_H +#define __LZMADECODE_H + +#include "LzmaTypes.h" + +/* #define _LZMA_IN_CB */ +/* Use callback for input data */ + +/* #define _LZMA_OUT_READ */ +/* Use read function for output data */ + +/* #define _LZMA_PROB32 */ +/* It can increase speed on some 32-bit CPUs, + but memory usage will be doubled in that case */ + +/* #define _LZMA_LOC_OPT */ +/* Enable local speed optimizations inside code */ + +#ifdef _LZMA_PROB32 +#define CProb UInt32 +#else +#define CProb UInt16 +#endif + +#define LZMA_RESULT_OK 0 +#define LZMA_RESULT_DATA_ERROR 1 + +#ifdef _LZMA_IN_CB +typedef struct _ILzmaInCallback +{ + int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize); +} ILzmaInCallback; +#endif + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 768 + +#define LZMA_PROPERTIES_SIZE 5 + +typedef struct _CLzmaProperties +{ + int lc; + int lp; + int pb; + #ifdef _LZMA_OUT_READ + UInt32 DictionarySize; + #endif +}CLzmaProperties; + +int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size); + +#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp))) + +#define kLzmaNeedInitId (-2) + +typedef struct _CLzmaDecoderState +{ + CLzmaProperties Properties; + CProb *Probs; + + #ifdef _LZMA_IN_CB + const unsigned char *Buffer; + const unsigned char *BufferLim; + #endif + + #ifdef _LZMA_OUT_READ + unsigned char *Dictionary; + UInt32 Range; + UInt32 Code; + UInt32 DictionaryPos; + UInt32 GlobalPos; + UInt32 DistanceLimit; + UInt32 Reps[4]; + int State; + int RemainLen; + unsigned char TempDictionary[4]; + #endif +} CLzmaDecoderState; + +#ifdef _LZMA_OUT_READ +#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; } +#endif + +int LzmaDecode(CLzmaDecoderState *vs, + #ifdef _LZMA_IN_CB + ILzmaInCallback *inCallback, + #else + const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed, + #endif + unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed); + +#endif diff --git a/target/linux/ramips/image/lzma-loader/src/LzmaTypes.h b/target/linux/ramips/image/lzma-loader/src/LzmaTypes.h new file mode 100644 index 0000000000..9c27290757 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/LzmaTypes.h @@ -0,0 +1,45 @@ +/* +LzmaTypes.h + +Types for LZMA Decoder + +This file written and distributed to public domain by Igor Pavlov. +This file is part of LZMA SDK 4.40 (2006-05-01) +*/ + +#ifndef __LZMATYPES_H +#define __LZMATYPES_H + +#ifndef _7ZIP_BYTE_DEFINED +#define _7ZIP_BYTE_DEFINED +typedef unsigned char Byte; +#endif + +#ifndef _7ZIP_UINT16_DEFINED +#define _7ZIP_UINT16_DEFINED +typedef unsigned short UInt16; +#endif + +#ifndef _7ZIP_UINT32_DEFINED +#define _7ZIP_UINT32_DEFINED +#ifdef _LZMA_UINT32_IS_ULONG +typedef unsigned long UInt32; +#else +typedef unsigned int UInt32; +#endif +#endif + +/* #define _LZMA_NO_SYSTEM_SIZE_T */ +/* You can use it, if you don't want */ + +#ifndef _7ZIP_SIZET_DEFINED +#define _7ZIP_SIZET_DEFINED +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +#include +typedef size_t SizeT; +#endif +#endif + +#endif diff --git a/target/linux/ramips/image/lzma-loader/src/Makefile b/target/linux/ramips/image/lzma-loader/src/Makefile new file mode 100644 index 0000000000..f861666e62 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/Makefile @@ -0,0 +1,110 @@ +# +# Makefile for the LZMA compressed kernel loader for +# Atheros AR7XXX/AR9XXX based boards +# +# Copyright (C) 2011 Gabor Juhos +# +# Some parts of this file was based on the OpenWrt specific lzma-loader +# for the BCM47xx and ADM5120 based boards: +# Copyright (C) 2004 Manuel Novoa III (mjn3@codepoet.org) +# Copyright (C) 2005 Mineharu Takahara +# Copyright (C) 2005 by Oleg I. Vdovikin +# +# 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. +# + +LOADADDR := +LZMA_TEXT_START := 0x80a00000 +LOADER_DATA := +BOARD := +FLASH_OFFS := +FLASH_MAX := +PLATFORM := + +CC := $(CROSS_COMPILE)gcc +LD := $(CROSS_COMPILE)ld +OBJCOPY := $(CROSS_COMPILE)objcopy +OBJDUMP := $(CROSS_COMPILE)objdump + +BIN_FLAGS := -O binary -R .reginfo -R .note -R .comment -R .mdebug -S + +CFLAGS = -D__KERNEL__ -Wall -Wstrict-prototypes -Wno-trigraphs -Os \ + -fno-strict-aliasing -fno-common -fomit-frame-pointer -G 0 \ + -mno-abicalls -fno-pic -ffunction-sections -pipe -mlong-calls \ + -fno-common -ffreestanding -fhonour-copts \ + -mabi=32 -march=mips32r2 \ + -Wa,-32 -Wa,-march=mips32r2 -Wa,-mips32r2 -Wa,--trap +CFLAGS += -D_LZMA_PROB32 -DARCH=$(PLATFORM) + +ASFLAGS = $(CFLAGS) -D__ASSEMBLY__ + +LDFLAGS = -static --gc-sections -no-warn-mismatch +LDFLAGS += -e startup -T loader.lds -Ttext $(LZMA_TEXT_START) + +O_FORMAT = $(shell $(OBJDUMP) -i | head -2 | grep elf32) + +OBJECTS := head.o loader.o cache.o board-$(PLATFORM).o printf.o LzmaDecode.o + +include $(PLATFORM).mk +CFLAGS+=$(CACHE_FLAGS) +ASFLAGS+=$(CACHE_FLAGS) + +ifneq ($(strip $(LOADER_DATA)),) +OBJECTS += data.o +CFLAGS += -DLZMA_WRAPPER=1 -DLOADADDR=$(LOADADDR) +endif + +ifneq ($(strip $(KERNEL_CMDLINE)),) +CFLAGS += -DCONFIG_KERNEL_CMDLINE='"$(KERNEL_CMDLINE)"' +endif + +ifneq ($(strip $(FLASH_OFFS)),) +CFLAGS += -DCONFIG_FLASH_OFFS=$(FLASH_OFFS) +endif + +ifneq ($(strip $(FLASH_MAX)),) +CFLAGS += -DCONFIG_FLASH_MAX=$(FLASH_MAX) +endif + +BOARD_DEF := $(shell echo $(strip $(BOARD)) | tr a-z A-Z | tr - _) +ifneq ($(BOARD_DEF),) +CFLAGS += -DCONFIG_BOARD_$(BOARD_DEF) +endif + +all: loader.elf + +# Don't build dependencies, this may die if $(CC) isn't gcc +dep: + +install: + +%.o : %.c + $(CC) $(CFLAGS) -c -o $@ $< + +%.o : %.S + $(CC) $(ASFLAGS) -c -o $@ $< + +data.o: $(LOADER_DATA) + $(LD) -r -b binary --oformat $(O_FORMAT) -T lzma-data.lds -o $@ $< + +loader: $(OBJECTS) + $(LD) $(LDFLAGS) -o $@ $(OBJECTS) + +loader.bin: loader + $(OBJCOPY) $(BIN_FLAGS) $< $@ + +loader2.o: loader.bin + $(LD) -r -b binary --oformat $(O_FORMAT) -o $@ $< + +loader.elf: loader2.o + $(LD) -e startup -T loader2.lds -Ttext $(LOADADDR) -o $@ $< + +mrproper: clean + +clean: + rm -f loader *.elf *.bin *.o + + + diff --git a/target/linux/ramips/image/lzma-loader/src/board-ralink.c b/target/linux/ramips/image/lzma-loader/src/board-ralink.c new file mode 100644 index 0000000000..7c947ec6eb --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/board-ralink.c @@ -0,0 +1,42 @@ +/* + * Arch specific code for Ralink based boards + * + * Copyright (C) 2013 John Crispin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include "config.h" + +#define READREG(r) *(volatile unsigned int *)(r) +#define WRITEREG(r,v) *(volatile unsigned int *)(r) = v + +#define KSEG1ADDR(_x) (((_x) & 0x1fffffff) | 0xa0000000) + +#ifdef CONFIG_SOC_RT288X +#define UART_BASE 0xb0300c00 +#else +#define UART_BASE 0xb0000c00 +#endif + +#define UART_TX 1 +#define UART_LSR 7 + +#define UART_LSR_THRE 0x20 + +#define UART_READ(r) READREG(UART_BASE + 4 * (r)) +#define UART_WRITE(r,v) WRITEREG(UART_BASE + 4 * (r), (v)) + +void board_putc(int ch) +{ + while (((UART_READ(UART_LSR)) & UART_LSR_THRE) == 0); + UART_WRITE(UART_TX, ch); + while (((UART_READ(UART_LSR)) & UART_LSR_THRE) == 0); +} + +void board_init(void) +{ +} diff --git a/target/linux/ramips/image/lzma-loader/src/cache.c b/target/linux/ramips/image/lzma-loader/src/cache.c new file mode 100644 index 0000000000..28cc848333 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/cache.c @@ -0,0 +1,43 @@ +/* + * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards + * + * Copyright (C) 2011 Gabor Juhos + * + * The cache manipulation routine has been taken from the U-Boot project. + * (C) Copyright 2003 + * Wolfgang Denk, DENX Software Engineering, + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include "cache.h" +#include "cacheops.h" +#include "config.h" + +#define cache_op(op,addr) \ + __asm__ __volatile__( \ + " .set push \n" \ + " .set noreorder \n" \ + " .set mips3\n\t \n" \ + " cache %0, %1 \n" \ + " .set pop \n" \ + : \ + : "i" (op), "R" (*(unsigned char *)(addr))) + +void flush_cache(unsigned long start_addr, unsigned long size) +{ + unsigned long lsize = CONFIG_CACHELINE_SIZE; + unsigned long addr = start_addr & ~(lsize - 1); + unsigned long aend = (start_addr + size - 1) & ~(lsize - 1); + + while (1) { + cache_op(Hit_Writeback_Inv_D, addr); + cache_op(Hit_Invalidate_I, addr); + if (addr == aend) + break; + addr += lsize; + } +} diff --git a/target/linux/ramips/image/lzma-loader/src/cache.h b/target/linux/ramips/image/lzma-loader/src/cache.h new file mode 100644 index 0000000000..506a235884 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/cache.h @@ -0,0 +1,17 @@ +/* + * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards + * + * Copyright (C) 2011 Gabor Juhos + * + * 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 __CACHE_H +#define __CACHE_H + +void flush_cache(unsigned long start_addr, unsigned long size); + +#endif /* __CACHE_H */ diff --git a/target/linux/ramips/image/lzma-loader/src/cacheops.h b/target/linux/ramips/image/lzma-loader/src/cacheops.h new file mode 100644 index 0000000000..70bcad7694 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/cacheops.h @@ -0,0 +1,85 @@ +/* + * Cache operations for the cache instruction. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * (C) Copyright 1996, 97, 99, 2002, 03 Ralf Baechle + * (C) Copyright 1999 Silicon Graphics, Inc. + */ +#ifndef __ASM_CACHEOPS_H +#define __ASM_CACHEOPS_H + +/* + * Cache Operations available on all MIPS processors with R4000-style caches + */ +#define Index_Invalidate_I 0x00 +#define Index_Writeback_Inv_D 0x01 +#define Index_Load_Tag_I 0x04 +#define Index_Load_Tag_D 0x05 +#define Index_Store_Tag_I 0x08 +#define Index_Store_Tag_D 0x09 +#if defined(CONFIG_CPU_LOONGSON2) +#define Hit_Invalidate_I 0x00 +#else +#define Hit_Invalidate_I 0x10 +#endif +#define Hit_Invalidate_D 0x11 +#define Hit_Writeback_Inv_D 0x15 + +/* + * R4000-specific cacheops + */ +#define Create_Dirty_Excl_D 0x0d +#define Fill 0x14 +#define Hit_Writeback_I 0x18 +#define Hit_Writeback_D 0x19 + +/* + * R4000SC and R4400SC-specific cacheops + */ +#define Index_Invalidate_SI 0x02 +#define Index_Writeback_Inv_SD 0x03 +#define Index_Load_Tag_SI 0x06 +#define Index_Load_Tag_SD 0x07 +#define Index_Store_Tag_SI 0x0A +#define Index_Store_Tag_SD 0x0B +#define Create_Dirty_Excl_SD 0x0f +#define Hit_Invalidate_SI 0x12 +#define Hit_Invalidate_SD 0x13 +#define Hit_Writeback_Inv_SD 0x17 +#define Hit_Writeback_SD 0x1b +#define Hit_Set_Virtual_SI 0x1e +#define Hit_Set_Virtual_SD 0x1f + +/* + * R5000-specific cacheops + */ +#define R5K_Page_Invalidate_S 0x17 + +/* + * RM7000-specific cacheops + */ +#define Page_Invalidate_T 0x16 + +/* + * R10000-specific cacheops + * + * Cacheops 0x02, 0x06, 0x0a, 0x0c-0x0e, 0x16, 0x1a and 0x1e are unused. + * Most of the _S cacheops are identical to the R4000SC _SD cacheops. + */ +#define Index_Writeback_Inv_S 0x03 +#define Index_Load_Tag_S 0x07 +#define Index_Store_Tag_S 0x0B +#define Hit_Invalidate_S 0x13 +#define Cache_Barrier 0x14 +#define Hit_Writeback_Inv_S 0x17 +#define Index_Load_Data_I 0x18 +#define Index_Load_Data_D 0x19 +#define Index_Load_Data_S 0x1b +#define Index_Store_Data_I 0x1c +#define Index_Store_Data_D 0x1d +#define Index_Store_Data_S 0x1f + +#endif /* __ASM_CACHEOPS_H */ diff --git a/target/linux/ramips/image/lzma-loader/src/config.h b/target/linux/ramips/image/lzma-loader/src/config.h new file mode 100644 index 0000000000..b7719e9ca8 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/config.h @@ -0,0 +1,27 @@ +/* + * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards + * + * Copyright (C) 2011 Gabor Juhos + * + * 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 _CONFIG_H_ +#define _CONFIG_H_ + +#ifndef CONFIG_FLASH_OFFS +#define CONFIG_FLASH_OFFS 0 +#endif + +#ifndef CONFIG_FLASH_MAX +#define CONFIG_FLASH_MAX 0 +#endif + +#ifndef CONFIG_FLASH_STEP +#define CONFIG_FLASH_STEP 0x1000 +#endif + +#endif /* _CONFIG_H_ */ diff --git a/target/linux/ramips/image/lzma-loader/src/cp0regdef.h b/target/linux/ramips/image/lzma-loader/src/cp0regdef.h new file mode 100644 index 0000000000..c1188ad8c8 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/cp0regdef.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 1994, 1995, 1996, 1997, 2000, 2001 by Ralf Baechle + * + * Copyright (C) 2001, Monta Vista Software + * Author: jsun@mvista.com or jsun@junsun.net + */ +#ifndef _cp0regdef_h_ +#define _cp0regdef_h_ + +#define CP0_INDEX $0 +#define CP0_RANDOM $1 +#define CP0_ENTRYLO0 $2 +#define CP0_ENTRYLO1 $3 +#define CP0_CONTEXT $4 +#define CP0_PAGEMASK $5 +#define CP0_WIRED $6 +#define CP0_BADVADDR $8 +#define CP0_COUNT $9 +#define CP0_ENTRYHI $10 +#define CP0_COMPARE $11 +#define CP0_STATUS $12 +#define CP0_CAUSE $13 +#define CP0_EPC $14 +#define CP0_PRID $15 +#define CP0_CONFIG $16 +#define CP0_LLADDR $17 +#define CP0_WATCHLO $18 +#define CP0_WATCHHI $19 +#define CP0_XCONTEXT $20 +#define CP0_FRAMEMASK $21 +#define CP0_DIAGNOSTIC $22 +#define CP0_PERFORMANCE $25 +#define CP0_ECC $26 +#define CP0_CACHEERR $27 +#define CP0_TAGLO $28 +#define CP0_TAGHI $29 +#define CP0_ERROREPC $30 + +#endif diff --git a/target/linux/ramips/image/lzma-loader/src/head.S b/target/linux/ramips/image/lzma-loader/src/head.S new file mode 100644 index 0000000000..543996a0da --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/head.S @@ -0,0 +1,118 @@ +/* + * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards + * + * Copyright (C) 2011 Gabor Juhos + * + * Some parts of this code was based on the OpenWrt specific lzma-loader + * for the BCM47xx and ADM5120 based boards: + * Copyright (C) 2004 Manuel Novoa III (mjn3@codepoet.org) + * Copyright (C) 2005 by Oleg I. Vdovikin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include "cp0regdef.h" +#include "cacheops.h" +#include "config.h" + +#define KSEG0 0x80000000 + + .macro ehb + sll zero, 3 + .endm + + .text + +LEAF(startup) + .set noreorder + .set mips32 + + mtc0 zero, CP0_WATCHLO # clear watch registers + mtc0 zero, CP0_WATCHHI + mtc0 zero, CP0_CAUSE # clear before writing status register + + mfc0 t0, CP0_STATUS + li t1, 0x1000001f + or t0, t1 + xori t0, 0x1f + mtc0 t0, CP0_STATUS + ehb + + mtc0 zero, CP0_COUNT + mtc0 zero, CP0_COMPARE + ehb + + la t0, __reloc_label # get linked address of label + bal __reloc_label # branch and link to label to + nop # get actual address +__reloc_label: + subu t0, ra, t0 # get reloc_delta + + beqz t0, __reloc_done # if delta is 0 we are in the right place + nop + + /* Copy our code to the right place */ + la t1, _code_start # get linked address of _code_start + la t2, _code_end # get linked address of _code_end + addu t0, t0, t1 # calculate actual address of _code_start + +__reloc_copy: + lw t3, 0(t0) + sw t3, 0(t1) + add t1, 4 + blt t1, t2, __reloc_copy + add t0, 4 + + /* flush cache */ + la t0, _code_start + la t1, _code_end + + li t2, ~(CONFIG_CACHELINE_SIZE - 1) + and t0, t2 + and t1, t2 + li t2, CONFIG_CACHELINE_SIZE + + b __flush_check + nop + +__flush_line: + cache Hit_Writeback_Inv_D, 0(t0) + cache Hit_Invalidate_I, 0(t0) + add t0, t2 + +__flush_check: + bne t0, t1, __flush_line + nop + + sync + +__reloc_done: + + /* clear bss */ + la t0, _bss_start + la t1, _bss_end + b __bss_check + nop + +__bss_fill: + sw zero, 0(t0) + addi t0, 4 + +__bss_check: + bne t0, t1, __bss_fill + nop + + /* Setup new "C" stack */ + la sp, _stack + + /* jump to the decompressor routine */ + la t0, loader_main + jr t0 + nop + + .set reorder +END(startup) diff --git a/target/linux/ramips/image/lzma-loader/src/lantiq.mk b/target/linux/ramips/image/lzma-loader/src/lantiq.mk new file mode 100644 index 0000000000..413764593b --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/lantiq.mk @@ -0,0 +1 @@ +CACHE_FLAGS+=-DCONFIG_ICACHE_SIZE="(32 * 1024)" -DCONFIG_DCACHE_SIZE="(32 * 1024)" -DCONFIG_CACHELINE_SIZE=32 diff --git a/target/linux/ramips/image/lzma-loader/src/loader.c b/target/linux/ramips/image/lzma-loader/src/loader.c new file mode 100644 index 0000000000..1d42bfa2f7 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/loader.c @@ -0,0 +1,263 @@ +/* + * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards + * + * Copyright (C) 2011 Gabor Juhos + * + * Some parts of this code was based on the OpenWrt specific lzma-loader + * for the BCM47xx and ADM5120 based boards: + * Copyright (C) 2004 Manuel Novoa III (mjn3@codepoet.org) + * Copyright (C) 2005 Mineharu Takahara + * Copyright (C) 2005 by Oleg I. Vdovikin + * + * The image_header structure has been taken from the U-Boot project. + * (C) Copyright 2008 Semihalf + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include + +#include "config.h" +#include "cache.h" +#include "printf.h" +#include "LzmaDecode.h" + +#define AR71XX_FLASH_START 0x1f000000 +#define AR71XX_FLASH_END 0x1fe00000 + +#define KSEG0 0x80000000 +#define KSEG1 0xa0000000 + +#define KSEG1ADDR(a) ((((unsigned)(a)) & 0x1fffffffU) | KSEG1) + +#undef LZMA_DEBUG + +#ifdef LZMA_DEBUG +# define DBG(f, a...) printf(f, ## a) +#else +# define DBG(f, a...) do {} while (0) +#endif + +#define IH_MAGIC_OKLI 0x4f4b4c49 /* 'OKLI' */ + +#define IH_NMLEN 32 /* Image Name Length */ + +typedef struct image_header { + uint32_t ih_magic; /* Image Header Magic Number */ + uint32_t ih_hcrc; /* Image Header CRC Checksum */ + uint32_t ih_time; /* Image Creation Timestamp */ + uint32_t ih_size; /* Image Data Size */ + uint32_t ih_load; /* Data Load Address */ + uint32_t ih_ep; /* Entry Point Address */ + uint32_t ih_dcrc; /* Image Data CRC Checksum */ + uint8_t ih_os; /* Operating System */ + uint8_t ih_arch; /* CPU architecture */ + uint8_t ih_type; /* Image Type */ + uint8_t ih_comp; /* Compression Type */ + uint8_t ih_name[IH_NMLEN]; /* Image Name */ +} image_header_t; + +/* beyond the image end, size not known in advance */ +extern unsigned char workspace[]; +extern void board_init(void); + +static CLzmaDecoderState lzma_state; +static unsigned char *lzma_data; +static unsigned long lzma_datasize; +static unsigned long lzma_outsize; +static unsigned long kernel_la; + +#ifdef CONFIG_KERNEL_CMDLINE +#define kernel_argc 1 +static const char kernel_cmdline[] = CONFIG_KERNEL_CMDLINE; +static const char *kernel_argv[] = { + kernel_cmdline, + NULL, +}; +#endif /* CONFIG_KERNEL_CMDLINE */ + +static void halt(void) +{ + printf("\nSystem halted!\n"); + for(;;); +} + +static __inline__ unsigned long get_be32(void *buf) +{ + unsigned char *p = buf; + + return (((unsigned long) p[0] << 24) + + ((unsigned long) p[1] << 16) + + ((unsigned long) p[2] << 8) + + (unsigned long) p[3]); +} + +static __inline__ unsigned char lzma_get_byte(void) +{ + unsigned char c; + + lzma_datasize--; + c = *lzma_data++; + + return c; +} + +static int lzma_init_props(void) +{ + unsigned char props[LZMA_PROPERTIES_SIZE]; + int res; + int i; + + /* read lzma properties */ + for (i = 0; i < LZMA_PROPERTIES_SIZE; i++) + props[i] = lzma_get_byte(); + + /* read the lower half of uncompressed size in the header */ + lzma_outsize = ((SizeT) lzma_get_byte()) + + ((SizeT) lzma_get_byte() << 8) + + ((SizeT) lzma_get_byte() << 16) + + ((SizeT) lzma_get_byte() << 24); + + /* skip rest of the header (upper half of uncompressed size) */ + for (i = 0; i < 4; i++) + lzma_get_byte(); + + res = LzmaDecodeProperties(&lzma_state.Properties, props, + LZMA_PROPERTIES_SIZE); + return res; +} + +static int lzma_decompress(unsigned char *outStream) +{ + SizeT ip, op; + int ret; + + lzma_state.Probs = (CProb *) workspace; + + ret = LzmaDecode(&lzma_state, lzma_data, lzma_datasize, &ip, outStream, + lzma_outsize, &op); + + if (ret != LZMA_RESULT_OK) { + int i; + + DBG("LzmaDecode error %d at %08x, osize:%d ip:%d op:%d\n", + ret, lzma_data + ip, lzma_outsize, ip, op); + + for (i = 0; i < 16; i++) + DBG("%02x ", lzma_data[ip + i]); + + DBG("\n"); + } + + return ret; +} + +#if (LZMA_WRAPPER) +static void lzma_init_data(void) +{ + extern unsigned char _lzma_data_start[]; + extern unsigned char _lzma_data_end[]; + + kernel_la = LOADADDR; + lzma_data = _lzma_data_start; + lzma_datasize = _lzma_data_end - _lzma_data_start; +} +#else +static void lzma_init_data(void) +{ + struct image_header *hdr = NULL; + unsigned char *flash_base; + unsigned long flash_ofs; + unsigned long kernel_ofs; + unsigned long kernel_size; + + flash_base = (unsigned char *) KSEG1ADDR(AR71XX_FLASH_START); + + printf("Looking for OpenWrt image... "); + + for (flash_ofs = CONFIG_FLASH_OFFS; + flash_ofs <= (CONFIG_FLASH_OFFS + CONFIG_FLASH_MAX); + flash_ofs += CONFIG_FLASH_STEP) { + unsigned long magic; + unsigned char *p; + + p = flash_base + flash_ofs; + magic = get_be32(p); + if (magic == IH_MAGIC_OKLI) { + hdr = (struct image_header *) p; + break; + } + } + + if (hdr == NULL) { + printf("not found!\n"); + halt(); + } + + printf("found at 0x%08x\n", flash_base + flash_ofs); + + kernel_ofs = sizeof(struct image_header); + kernel_size = get_be32(&hdr->ih_size); + kernel_la = get_be32(&hdr->ih_load); + + lzma_data = flash_base + flash_ofs + kernel_ofs; + lzma_datasize = kernel_size; +} +#endif /* (LZMA_WRAPPER) */ + +void loader_main(unsigned long reg_a0, unsigned long reg_a1, + unsigned long reg_a2, unsigned long reg_a3) +{ + void (*kernel_entry) (unsigned long, unsigned long, unsigned long, + unsigned long); + int res; + + board_init(); + + printf("\n\nOpenWrt kernel loader for MIPS based SoC\n"); + printf("Copyright (C) 2011 Gabor Juhos \n"); + + lzma_init_data(); + + res = lzma_init_props(); + if (res != LZMA_RESULT_OK) { + printf("Incorrect LZMA stream properties!\n"); + halt(); + } + + printf("Decompressing kernel... "); + + res = lzma_decompress((unsigned char *) kernel_la); + if (res != LZMA_RESULT_OK) { + printf("failed, "); + switch (res) { + case LZMA_RESULT_DATA_ERROR: + printf("data error!\n"); + break; + default: + printf("unknown error %d!\n", res); + } + halt(); + } else { + printf("done!\n"); + } + + flush_cache(kernel_la, lzma_outsize); + + printf("Starting kernel at %08x...\n\n", kernel_la); + +#ifdef CONFIG_KERNEL_CMDLINE + reg_a0 = kernel_argc; + reg_a1 = (unsigned long) kernel_argv; + reg_a2 = 0; + reg_a3 = 0; +#endif + + kernel_entry = (void *) kernel_la; + kernel_entry(reg_a0, reg_a1, reg_a2, reg_a3); +} diff --git a/target/linux/ramips/image/lzma-loader/src/loader.lds b/target/linux/ramips/image/lzma-loader/src/loader.lds new file mode 100644 index 0000000000..80cc7ca3ec --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/loader.lds @@ -0,0 +1,35 @@ +OUTPUT_ARCH(mips) +SECTIONS { + .text : { + _code_start = .; + *(.text) + *(.text.*) + *(.rodata) + *(.rodata.*) + *(.data.lzma) + } + + . = ALIGN(32); + .data : { + *(.data) + *(.data.*) + . = . + 524288; /* workaround for buggy bootloaders */ + } + + . = ALIGN(32); + _code_end = .; + + _bss_start = .; + .bss : { + *(.bss) + *(.bss.*) + } + + . = ALIGN(32); + _bss_end = .; + + . = . + 8192; + _stack = .; + + workspace = .; +} diff --git a/target/linux/ramips/image/lzma-loader/src/loader2.lds b/target/linux/ramips/image/lzma-loader/src/loader2.lds new file mode 100644 index 0000000000..db0bb46424 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/loader2.lds @@ -0,0 +1,10 @@ +OUTPUT_ARCH(mips) +SECTIONS { + .text : { + startup = .; + *(.text) + *(.text.*) + *(.data) + *(.data.*) + } +} diff --git a/target/linux/ramips/image/lzma-loader/src/lzma-data.lds b/target/linux/ramips/image/lzma-loader/src/lzma-data.lds new file mode 100644 index 0000000000..abf756ba13 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/lzma-data.lds @@ -0,0 +1,8 @@ +OUTPUT_ARCH(mips) +SECTIONS { + .data.lzma : { + _lzma_data_start = .; + *(.data) + _lzma_data_end = .; + } +} diff --git a/target/linux/ramips/image/lzma-loader/src/printf.c b/target/linux/ramips/image/lzma-loader/src/printf.c new file mode 100644 index 0000000000..7bb5a86e18 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/printf.c @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * 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 "printf.h" + +extern void board_putc(int ch); + +/* this is the maximum width for a variable */ +#define LP_MAX_BUF 256 + +/* macros */ +#define IsDigit(x) ( ((x) >= '0') && ((x) <= '9') ) +#define Ctod(x) ( (x) - '0') + +/* forward declaration */ +static int PrintChar(char *, char, int, int); +static int PrintString(char *, char *, int, int); +static int PrintNum(char *, unsigned long, int, int, int, int, char, int); + +/* private variable */ +static const char theFatalMsg[] = "fatal error in lp_Print!"; + +/* -*- + * A low level printf() function. + */ +static void +lp_Print(void (*output)(void *, char *, int), + void * arg, + char *fmt, + va_list ap) +{ + +#define OUTPUT(arg, s, l) \ + { if (((l) < 0) || ((l) > LP_MAX_BUF)) { \ + (*output)(arg, (char*)theFatalMsg, sizeof(theFatalMsg)-1); for(;;); \ + } else { \ + (*output)(arg, s, l); \ + } \ + } + + char buf[LP_MAX_BUF]; + + char c; + char *s; + long int num; + + int longFlag; + int negFlag; + int width; + int prec; + int ladjust; + char padc; + + int length; + + for(;;) { + { + /* scan for the next '%' */ + char *fmtStart = fmt; + while ( (*fmt != '\0') && (*fmt != '%')) { + fmt ++; + } + + /* flush the string found so far */ + OUTPUT(arg, fmtStart, fmt-fmtStart); + + /* are we hitting the end? */ + if (*fmt == '\0') break; + } + + /* we found a '%' */ + fmt ++; + + /* check for long */ + if (*fmt == 'l') { + longFlag = 1; + fmt ++; + } else { + longFlag = 0; + } + + /* check for other prefixes */ + width = 0; + prec = -1; + ladjust = 0; + padc = ' '; + + if (*fmt == '-') { + ladjust = 1; + fmt ++; + } + + if (*fmt == '0') { + padc = '0'; + fmt++; + } + + if (IsDigit(*fmt)) { + while (IsDigit(*fmt)) { + width = 10 * width + Ctod(*fmt++); + } + } + + if (*fmt == '.') { + fmt ++; + if (IsDigit(*fmt)) { + prec = 0; + while (IsDigit(*fmt)) { + prec = prec*10 + Ctod(*fmt++); + } + } + } + + + /* check format flag */ + negFlag = 0; + switch (*fmt) { + case 'b': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + length = PrintNum(buf, num, 2, 0, width, ladjust, padc, 0); + OUTPUT(arg, buf, length); + break; + + case 'd': + case 'D': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + if (num < 0) { + num = - num; + negFlag = 1; + } + length = PrintNum(buf, num, 10, negFlag, width, ladjust, padc, 0); + OUTPUT(arg, buf, length); + break; + + case 'o': + case 'O': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + length = PrintNum(buf, num, 8, 0, width, ladjust, padc, 0); + OUTPUT(arg, buf, length); + break; + + case 'u': + case 'U': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + length = PrintNum(buf, num, 10, 0, width, ladjust, padc, 0); + OUTPUT(arg, buf, length); + break; + + case 'x': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + length = PrintNum(buf, num, 16, 0, width, ladjust, padc, 0); + OUTPUT(arg, buf, length); + break; + + case 'X': + if (longFlag) { + num = va_arg(ap, long int); + } else { + num = va_arg(ap, int); + } + length = PrintNum(buf, num, 16, 0, width, ladjust, padc, 1); + OUTPUT(arg, buf, length); + break; + + case 'c': + c = (char)va_arg(ap, int); + length = PrintChar(buf, c, width, ladjust); + OUTPUT(arg, buf, length); + break; + + case 's': + s = (char*)va_arg(ap, char *); + length = PrintString(buf, s, width, ladjust); + OUTPUT(arg, buf, length); + break; + + case '\0': + fmt --; + break; + + default: + /* output this char as it is */ + OUTPUT(arg, fmt, 1); + } /* switch (*fmt) */ + + fmt ++; + } /* for(;;) */ + + /* special termination call */ + OUTPUT(arg, "\0", 1); +} + + +/* --------------- local help functions --------------------- */ +static int +PrintChar(char * buf, char c, int length, int ladjust) +{ + int i; + + if (length < 1) length = 1; + if (ladjust) { + *buf = c; + for (i=1; i< length; i++) buf[i] = ' '; + } else { + for (i=0; i< length-1; i++) buf[i] = ' '; + buf[length - 1] = c; + } + return length; +} + +static int +PrintString(char * buf, char* s, int length, int ladjust) +{ + int i; + int len=0; + char* s1 = s; + while (*s1++) len++; + if (length < len) length = len; + + if (ladjust) { + for (i=0; i< len; i++) buf[i] = s[i]; + for (i=len; i< length; i++) buf[i] = ' '; + } else { + for (i=0; i< length-len; i++) buf[i] = ' '; + for (i=length-len; i < length; i++) buf[i] = s[i-length+len]; + } + return length; +} + +static int +PrintNum(char * buf, unsigned long u, int base, int negFlag, + int length, int ladjust, char padc, int upcase) +{ + /* algorithm : + * 1. prints the number from left to right in reverse form. + * 2. fill the remaining spaces with padc if length is longer than + * the actual length + * TRICKY : if left adjusted, no "0" padding. + * if negtive, insert "0" padding between "0" and number. + * 3. if (!ladjust) we reverse the whole string including paddings + * 4. otherwise we only reverse the actual string representing the num. + */ + + int actualLength =0; + char *p = buf; + int i; + + do { + int tmp = u %base; + if (tmp <= 9) { + *p++ = '0' + tmp; + } else if (upcase) { + *p++ = 'A' + tmp - 10; + } else { + *p++ = 'a' + tmp - 10; + } + u /= base; + } while (u != 0); + + if (negFlag) { + *p++ = '-'; + } + + /* figure out actual length and adjust the maximum length */ + actualLength = p - buf; + if (length < actualLength) length = actualLength; + + /* add padding */ + if (ladjust) { + padc = ' '; + } + if (negFlag && !ladjust && (padc == '0')) { + for (i = actualLength-1; i< length-1; i++) buf[i] = padc; + buf[length -1] = '-'; + } else { + for (i = actualLength; i< length; i++) buf[i] = padc; + } + + + /* prepare to reverse the string */ + { + int begin = 0; + int end; + if (ladjust) { + end = actualLength - 1; + } else { + end = length -1; + } + + while (end > begin) { + char tmp = buf[begin]; + buf[begin] = buf[end]; + buf[end] = tmp; + begin ++; + end --; + } + } + + /* adjust the string pointer */ + return length; +} + +static void printf_output(void *arg, char *s, int l) +{ + int i; + + // special termination call + if ((l==1) && (s[0] == '\0')) return; + + for (i=0; i< l; i++) { + board_putc(s[i]); + if (s[i] == '\n') board_putc('\r'); + } +} + +void printf(char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + lp_Print(printf_output, 0, fmt, ap); + va_end(ap); +} diff --git a/target/linux/ramips/image/lzma-loader/src/printf.h b/target/linux/ramips/image/lzma-loader/src/printf.h new file mode 100644 index 0000000000..9b1c1df232 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/printf.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2001 MontaVista Software Inc. + * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net + * + * 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. + * + */ + +#ifndef _printf_h_ +#define _printf_h_ + +#include +void printf(char *fmt, ...); + +#endif /* _printf_h_ */ diff --git a/target/linux/ramips/image/lzma-loader/src/ralink.mk b/target/linux/ramips/image/lzma-loader/src/ralink.mk new file mode 100644 index 0000000000..3ff5fddf98 --- /dev/null +++ b/target/linux/ramips/image/lzma-loader/src/ralink.mk @@ -0,0 +1 @@ +CACHE_FLAGS+=-DCONFIG_ICACHE_SIZE="(32 * 1024)" -DCONFIG_DCACHE_SIZE="(16 * 1024)" -DCONFIG_CACHELINE_SIZE=32 diff --git a/target/linux/ramips/mt7620a/config-3.8 b/target/linux/ramips/mt7620a/config-3.8 new file mode 100644 index 0000000000..5eeb11e83d --- /dev/null +++ b/target/linux/ramips/mt7620a/config-3.8 @@ -0,0 +1,168 @@ +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_CEVT_R4K=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKEVT_RT3352=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLKSRC_OF=y +CONFIG_CMDLINE="rootfstype=squashfs,jffs2" +CONFIG_CMDLINE_BOOL=y +# CONFIG_CMDLINE_OVERRIDE is not set +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_MIPS32=y +# CONFIG_CPU_MIPS32_R1 is not set +CONFIG_CPU_MIPS32_R2=y +CONFIG_CPU_MIPSR2=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_R4K_FPU=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CSRC_R4K=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DMA_NONCOHERENT=y +# CONFIG_DTB_MT7620A_EVAL is not set +# CONFIG_DTB_MT7620A_MT7610E_EVAL is not set +CONFIG_DTB_RT_NONE=y +CONFIG_DTC=y +CONFIG_EARLY_PRINTK=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_IO=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_RALINK=y +CONFIG_GPIO_SYSFS=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_HAVE_GENERIC_HARDIRQS=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_IRQ_WORK=y +CONFIG_HAVE_MACH_CLKDEV=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_HAVE_NET_DSA=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HW_HAS_PCI=y +CONFIG_HW_RANDOM=m +CONFIG_IMAGE_CMDLINE_HACK=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQ_CPU=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_M25PXX_USE_FAST_READ=y +CONFIG_MDIO_BOARDINFO=y +# CONFIG_MII is not set +CONFIG_MIPS=y +# CONFIG_MIPS_HUGE_TLB_SUPPORT is not set +CONFIG_MIPS_L1_CACHE_SHIFT=5 +# CONFIG_MIPS_MACHINE is not set +CONFIG_MIPS_MT_DISABLED=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +CONFIG_MMC_SDHCI_MT7620=y +# CONFIG_MMC_SDHCI_PCI is not set +CONFIG_MMC_SDHCI_PLTFM=y +# CONFIG_MMC_TIFM_SD is not set +CONFIG_MODULES_USE_ELF_REL=y +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_OF_PARTS=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_UIMAGE_SPLIT=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_RALINK=y +CONFIG_NET_RALINK_GSW_MT7620=y +CONFIG_NET_RALINK_MDIO=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_DEVICE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_MDIO=y +CONFIG_OF_MTD=y +CONFIG_OF_NET=y +CONFIG_OF_PCI=y +CONFIG_OF_PCI_IRQ=y +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_PCI=y +# CONFIG_PCIEAER is not set +CONFIG_PCIEPORTBUS=y +CONFIG_PCI_DOMAINS=y +CONFIG_PERCPU_RWSEM=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PHYLIB=y +# CONFIG_PREEMPT_RCU is not set +CONFIG_RALINK=y +CONFIG_RALINK_USBPHY=y +CONFIG_RALINK_WDT=y +CONFIG_RESET_CONTROLLER=y +# CONFIG_SCSI_DMA is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RT288X=y +CONFIG_SERIAL_OF_PLATFORM=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +CONFIG_SOC_MT7620=y +# CONFIG_SOC_RT288X is not set +# CONFIG_SOC_RT305X is not set +# CONFIG_SOC_RT3883 is not set +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_RALINK=y +CONFIG_SWCONFIG=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_HAS_EARLY_PRINTK=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_UIDGID_CONVERTED=y +CONFIG_USB_ARCH_HAS_XHCI=y +CONFIG_USB_OTG_UTILS=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_WATCHDOG_CORE=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/ramips/mt7620a/config-3.9 b/target/linux/ramips/mt7620a/config-3.9 new file mode 100644 index 0000000000..ea7d6bc11a --- /dev/null +++ b/target/linux/ramips/mt7620a/config-3.9 @@ -0,0 +1,172 @@ +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_CEVT_R4K=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKEVT_RT3352=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLKSRC_OF=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CMDLINE="rootfstype=squashfs,jffs2" +CONFIG_CMDLINE_BOOL=y +# CONFIG_CMDLINE_OVERRIDE is not set +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_MIPS32=y +# CONFIG_CPU_MIPS32_R1 is not set +CONFIG_CPU_MIPS32_R2=y +CONFIG_CPU_MIPSR2=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_R4K_FPU=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CSRC_R4K=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DMA_NONCOHERENT=y +# CONFIG_DTB_MT7620A_EVAL is not set +# CONFIG_DTB_MT7620A_MT7610E_EVAL is not set +CONFIG_DTB_RT_NONE=y +CONFIG_DTC=y +CONFIG_EARLY_PRINTK=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_IO=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_DEVRES=y +CONFIG_GPIO_RALINK=y +CONFIG_GPIO_SYSFS=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_HAVE_CLK=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_HAVE_GENERIC_HARDIRQS=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_MACH_CLKDEV=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_HAVE_NET_DSA=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HW_HAS_PCI=y +CONFIG_HW_RANDOM=m +CONFIG_IMAGE_CMDLINE_HACK=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_CPU=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +CONFIG_M25PXX_USE_FAST_READ=y +CONFIG_MDIO_BOARDINFO=y +# CONFIG_MII is not set +CONFIG_MIPS=y +# CONFIG_MIPS_HUGE_TLB_SUPPORT is not set +CONFIG_MIPS_L1_CACHE_SHIFT=5 +# CONFIG_MIPS_MACHINE is not set +CONFIG_MIPS_MT_DISABLED=y +CONFIG_MMC=y +CONFIG_MMC_BLOCK=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y +CONFIG_MMC_SDHCI_MT7620=y +# CONFIG_MMC_SDHCI_PCI is not set +CONFIG_MMC_SDHCI_PLTFM=y +# CONFIG_MMC_TIFM_SD is not set +CONFIG_MODULES_USE_ELF_REL=y +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_OF_PARTS=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_UIMAGE_SPLIT=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_RALINK=y +CONFIG_NET_RALINK_GSW_MT7620=y +CONFIG_NET_RALINK_MDIO=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_DEVICE=y +# CONFIG_OF_DISPLAY_TIMING is not set +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_MDIO=y +CONFIG_OF_MTD=y +CONFIG_OF_NET=y +CONFIG_OF_PCI=y +CONFIG_OF_PCI_IRQ=y +# CONFIG_OF_VIDEOMODE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_PCI=y +CONFIG_PCI_DOMAINS=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PHYLIB=y +# CONFIG_PREEMPT_RCU is not set +CONFIG_RALINK=y +CONFIG_RALINK_USBPHY=y +CONFIG_RALINK_WDT=y +# CONFIG_RCU_STALL_COMMON is not set +CONFIG_RESET_CONTROLLER=y +# CONFIG_SCSI_DMA is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RT288X=y +CONFIG_SERIAL_OF_PLATFORM=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +CONFIG_SOC_MT7620=y +# CONFIG_SOC_RT288X is not set +# CONFIG_SOC_RT305X is not set +# CONFIG_SOC_RT3883 is not set +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_RALINK=y +CONFIG_SWCONFIG=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_HAS_EARLY_PRINTK=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_UIDGID_CONVERTED=y +CONFIG_USB_ARCH_HAS_XHCI=y +CONFIG_USB_OTG_UTILS=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_WATCHDOG_CORE=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/ramips/mt7620a/profiles/00-default.mk b/target/linux/ramips/mt7620a/profiles/00-default.mk new file mode 100644 index 0000000000..55ac015bbe --- /dev/null +++ b/target/linux/ramips/mt7620a/profiles/00-default.mk @@ -0,0 +1,18 @@ +# +# Copyright (C) 2011 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +define Profile/Default + NAME:=Default Profile + PACKAGES:=\ + kmod-usb-core kmod-usb-rt305x-dwc_otg \ + kmod-ledtrig-usbdev +endef + +define Profile/Default/Description + Default package set compatible with most boards. +endef +$(eval $(call Profile,Default)) diff --git a/target/linux/ramips/mt7620a/target.mk b/target/linux/ramips/mt7620a/target.mk new file mode 100644 index 0000000000..4d4dd28ae9 --- /dev/null +++ b/target/linux/ramips/mt7620a/target.mk @@ -0,0 +1,14 @@ +# +# Copyright (C) 2009 OpenWrt.org +# + +SUBTARGET:=mt7620a +BOARDNAME:=MT7620a based boards +ARCH_PACKAGES:=ramips_24kec +FEATURES+=usb +CFLAGS+= -march=24kec -mdsp + +define Target/Description + Build firmware images for Ralink MT7620a based boards. +endef + diff --git a/target/linux/ramips/patches-3.8/0001-MIPS-ralink-adds-include-files.patch b/target/linux/ramips/patches-3.8/0001-MIPS-ralink-adds-include-files.patch index 3539ece0c7..cc32caa9c2 100644 --- a/target/linux/ramips/patches-3.8/0001-MIPS-ralink-adds-include-files.patch +++ b/target/linux/ramips/patches-3.8/0001-MIPS-ralink-adds-include-files.patch @@ -1,7 +1,7 @@ -From 8563991026ee98bb5e477167236972a45dfea0e3 Mon Sep 17 00:00:00 2001 +From 72bd3fcd16225f46ca318435a4d8f3f3f154b1bc Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 21 Jan 2013 18:25:59 +0100 -Subject: [PATCH 01/14] MIPS: ralink: adds include files +Subject: [PATCH 01/79] MIPS: ralink: adds include files Before we start adding the platform code we add the common include files. @@ -17,6 +17,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4893/ create mode 100644 arch/mips/include/asm/mach-ralink/war.h create mode 100644 arch/mips/ralink/common.h +diff --git a/arch/mips/include/asm/mach-ralink/ralink_regs.h b/arch/mips/include/asm/mach-ralink/ralink_regs.h +new file mode 100644 +index 0000000..5a508f9 --- /dev/null +++ b/arch/mips/include/asm/mach-ralink/ralink_regs.h @@ -0,0 +1,39 @@ @@ -59,6 +62,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4893/ +} + +#endif /* _RALINK_REGS_H_ */ +diff --git a/arch/mips/include/asm/mach-ralink/war.h b/arch/mips/include/asm/mach-ralink/war.h +new file mode 100644 +index 0000000..a7b712c --- /dev/null +++ b/arch/mips/include/asm/mach-ralink/war.h @@ -0,0 +1,25 @@ @@ -87,6 +93,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4893/ +#define MIPS34K_MISSED_ITLB_WAR 0 + +#endif /* __ASM_MACH_RALINK_WAR_H */ +diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h +new file mode 100644 +index 0000000..3009903 --- /dev/null +++ b/arch/mips/ralink/common.h @@ -0,0 +1,44 @@ @@ -134,3 +143,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4893/ +__iomem void *plat_of_remap_node(const char *node); + +#endif /* _RALINK_COMMON_H__ */ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0002-MIPS-ralink-adds-irq-code.patch b/target/linux/ramips/patches-3.8/0002-MIPS-ralink-adds-irq-code.patch index c559a8767d..01beef7730 100644 --- a/target/linux/ramips/patches-3.8/0002-MIPS-ralink-adds-irq-code.patch +++ b/target/linux/ramips/patches-3.8/0002-MIPS-ralink-adds-irq-code.patch @@ -1,7 +1,7 @@ -From 19d3814e7b325f8965fd71f329b3467a97f8d217 Mon Sep 17 00:00:00 2001 +From 833836f47b4191e93267b91fcab38dd15affcd28 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:00:50 +0100 -Subject: [PATCH 02/14] MIPS: ralink: adds irq code +Subject: [PATCH 02/79] MIPS: ralink: adds irq code All of the Ralink Wifi SoC currently supported by this series share the same interrupt controller (INTC). @@ -14,6 +14,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4890/ 1 file changed, 176 insertions(+) create mode 100644 arch/mips/ralink/irq.c +diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c +new file mode 100644 +index 0000000..e62c975 --- /dev/null +++ b/arch/mips/ralink/irq.c @@ -0,0 +1,176 @@ @@ -193,3 +196,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4890/ + of_irq_init(of_irq_ids); +} + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0003-MIPS-ralink-adds-reset-code.patch b/target/linux/ramips/patches-3.8/0003-MIPS-ralink-adds-reset-code.patch index 57cad76b9e..ee9a509bb5 100644 --- a/target/linux/ramips/patches-3.8/0003-MIPS-ralink-adds-reset-code.patch +++ b/target/linux/ramips/patches-3.8/0003-MIPS-ralink-adds-reset-code.patch @@ -1,7 +1,7 @@ -From c06e836ada59fbc6d1109277e693e5b3e056ac12 Mon Sep 17 00:00:00 2001 +From 3cdf3d713c81ffd3032d7c664f0be89d1ddce3e3 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:00:57 +0100 -Subject: [PATCH 03/14] MIPS: ralink: adds reset code +Subject: [PATCH 03/79] MIPS: ralink: adds reset code Resetting these SoCs requires no real magic. The code is straight forward. @@ -13,6 +13,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4891/ 1 file changed, 44 insertions(+) create mode 100644 arch/mips/ralink/reset.c +diff --git a/arch/mips/ralink/reset.c b/arch/mips/ralink/reset.c +new file mode 100644 +index 0000000..22120e5 --- /dev/null +++ b/arch/mips/ralink/reset.c @@ -0,0 +1,44 @@ @@ -60,3 +63,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4891/ +} + +arch_initcall(mips_reboot_setup); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0004-MIPS-ralink-adds-prom-and-cmdline-code.patch b/target/linux/ramips/patches-3.8/0004-MIPS-ralink-adds-prom-and-cmdline-code.patch index da38629a75..8b8bb56b38 100644 --- a/target/linux/ramips/patches-3.8/0004-MIPS-ralink-adds-prom-and-cmdline-code.patch +++ b/target/linux/ramips/patches-3.8/0004-MIPS-ralink-adds-prom-and-cmdline-code.patch @@ -1,7 +1,7 @@ -From 7e47cefa69c8ed2c889522ce29fcce73ce8cf08e Mon Sep 17 00:00:00 2001 +From 36424b3f1f184c752562d19d0df1a427c8c584a2 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:01:05 +0100 -Subject: [PATCH 04/14] MIPS: ralink: adds prom and cmdline code +Subject: [PATCH 04/79] MIPS: ralink: adds prom and cmdline code Add minimal code to handle commandlines. @@ -13,6 +13,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4892/ 1 file changed, 69 insertions(+) create mode 100644 arch/mips/ralink/prom.c +diff --git a/arch/mips/ralink/prom.c b/arch/mips/ralink/prom.c +new file mode 100644 +index 0000000..9c64f02 --- /dev/null +++ b/arch/mips/ralink/prom.c @@ -0,0 +1,69 @@ @@ -85,3 +88,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4892/ +void __init prom_free_prom_memory(void) +{ +} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0005-MIPS-ralink-adds-clkdev-code.patch b/target/linux/ramips/patches-3.8/0005-MIPS-ralink-adds-clkdev-code.patch index a8218d1c6d..1fd60578d0 100644 --- a/target/linux/ramips/patches-3.8/0005-MIPS-ralink-adds-clkdev-code.patch +++ b/target/linux/ramips/patches-3.8/0005-MIPS-ralink-adds-clkdev-code.patch @@ -1,7 +1,7 @@ -From 3f0a06b0368d25608841843e9d65a7289ad9f14a Mon Sep 17 00:00:00 2001 +From b99289db258ee8a84e1bd555b2897476acf390c1 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:01:29 +0100 -Subject: [PATCH 05/14] MIPS: ralink: adds clkdev code +Subject: [PATCH 05/79] MIPS: ralink: adds clkdev code These SoCs have a limited number of fixed rate clocks. Add support for the clk and clkdev api. @@ -14,6 +14,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4894/ 1 file changed, 72 insertions(+) create mode 100644 arch/mips/ralink/clk.c +diff --git a/arch/mips/ralink/clk.c b/arch/mips/ralink/clk.c +new file mode 100644 +index 0000000..8dfa22f --- /dev/null +++ b/arch/mips/ralink/clk.c @@ -0,0 +1,72 @@ @@ -89,3 +92,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4894/ + mips_hpt_frequency = clk_get_rate(clk) / 2; + clk_put(clk); +} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0006-MIPS-ralink-adds-OF-code.patch b/target/linux/ramips/patches-3.8/0006-MIPS-ralink-adds-OF-code.patch index b6bd78b9ce..7747c819ca 100644 --- a/target/linux/ramips/patches-3.8/0006-MIPS-ralink-adds-OF-code.patch +++ b/target/linux/ramips/patches-3.8/0006-MIPS-ralink-adds-OF-code.patch @@ -1,7 +1,7 @@ -From 3a5bfe7bdbfd37c9206d7c6dfd7eb9664ccc5038 Mon Sep 17 00:00:00 2001 +From 4b2f9abbbaf3463a0fc1a65afeb0d12f890ada35 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:02:01 +0100 -Subject: [PATCH 06/14] MIPS: ralink: adds OF code +Subject: [PATCH 06/79] MIPS: ralink: adds OF code Until there is a generic MIPS way of handing the DTB over from bootloader to kernel we rely on a built in devicetrees. The OF code also remaps those register @@ -15,6 +15,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4895/ 1 file changed, 107 insertions(+) create mode 100644 arch/mips/ralink/of.c +diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c +new file mode 100644 +index 0000000..4165e70 --- /dev/null +++ b/arch/mips/ralink/of.c @@ -0,0 +1,107 @@ @@ -125,3 +128,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4895/ +} + +arch_initcall(plat_of_setup); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0007-MIPS-ralink-adds-early_printk-support.patch b/target/linux/ramips/patches-3.8/0007-MIPS-ralink-adds-early_printk-support.patch index 80a5b1ea34..e22bbbdf0f 100644 --- a/target/linux/ramips/patches-3.8/0007-MIPS-ralink-adds-early_printk-support.patch +++ b/target/linux/ramips/patches-3.8/0007-MIPS-ralink-adds-early_printk-support.patch @@ -1,7 +1,7 @@ -From 5fff610b7c60195de98e68bec00c357f393ce634 Mon Sep 17 00:00:00 2001 +From 4efba82d0e4059588f2b2fc0ac2576eaf37f1d22 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:02:55 +0100 -Subject: [PATCH 07/14] MIPS: ralink: adds early_printk support +Subject: [PATCH 07/79] MIPS: ralink: adds early_printk support Add the code needed to make early printk work. @@ -13,6 +13,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4897/ 1 file changed, 44 insertions(+) create mode 100644 arch/mips/ralink/early_printk.c +diff --git a/arch/mips/ralink/early_printk.c b/arch/mips/ralink/early_printk.c +new file mode 100644 +index 0000000..c4ae47e --- /dev/null +++ b/arch/mips/ralink/early_printk.c @@ -0,0 +1,44 @@ @@ -60,3 +63,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4897/ + while ((uart_r32(UART_REG_LSR) & UART_LSR_THRE) == 0) + ; +} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0008-MIPS-ralink-adds-support-for-RT305x-SoC-family.patch b/target/linux/ramips/patches-3.8/0008-MIPS-ralink-adds-support-for-RT305x-SoC-family.patch index 083526b36d..01b0869ad1 100644 --- a/target/linux/ramips/patches-3.8/0008-MIPS-ralink-adds-support-for-RT305x-SoC-family.patch +++ b/target/linux/ramips/patches-3.8/0008-MIPS-ralink-adds-support-for-RT305x-SoC-family.patch @@ -1,7 +1,7 @@ -From 2809b31770d7fd934a748692e1922a5e613f06e5 Mon Sep 17 00:00:00 2001 +From 433f4f524aba81358353ca4ebc00c3e916521ec6 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:03:46 +0100 -Subject: [PATCH 08/14] MIPS: ralink: adds support for RT305x SoC family +Subject: [PATCH 08/79] MIPS: ralink: adds support for RT305x SoC family Add support code for rt3050, rt3052, rt3350, rt3352 and rt5350 SOC. @@ -17,6 +17,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4896/ create mode 100644 arch/mips/include/asm/mach-ralink/rt305x.h create mode 100644 arch/mips/ralink/rt305x.c +diff --git a/arch/mips/include/asm/mach-ralink/rt305x.h b/arch/mips/include/asm/mach-ralink/rt305x.h +new file mode 100644 +index 0000000..7d344f2 --- /dev/null +++ b/arch/mips/include/asm/mach-ralink/rt305x.h @@ -0,0 +1,139 @@ @@ -159,6 +162,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4896/ +#define RT305X_GPIO_MODE_RGMII BIT(9) + +#endif +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +new file mode 100644 +index 0000000..0a4bbdc --- /dev/null +++ b/arch/mips/ralink/rt305x.c @@ -0,0 +1,242 @@ @@ -404,3 +410,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4896/ + (id >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK, + (id & CHIP_ID_REV_MASK)); +} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0009-MIPS-ralink-adds-rt305x-devicetree.patch b/target/linux/ramips/patches-3.8/0009-MIPS-ralink-adds-rt305x-devicetree.patch index 2c2bb14e16..81d9feac9e 100644 --- a/target/linux/ramips/patches-3.8/0009-MIPS-ralink-adds-rt305x-devicetree.patch +++ b/target/linux/ramips/patches-3.8/0009-MIPS-ralink-adds-rt305x-devicetree.patch @@ -1,7 +1,7 @@ -From 5644da4f635a30fc03b4f12d81b2197d716d9cef Mon Sep 17 00:00:00 2001 +From 8208a43c301d9164802dedeec7455dbdd70ca286 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 22 Jan 2013 20:19:33 +0100 -Subject: [PATCH 09/14] MIPS: ralink: adds rt305x devicetree +Subject: [PATCH 09/79] MIPS: ralink: adds rt305x devicetree This adds the devicetree file that describes the rt305x evaluation kit. @@ -15,6 +15,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4898/ create mode 100644 arch/mips/ralink/dts/rt3050.dtsi create mode 100644 arch/mips/ralink/dts/rt3052_eval.dts +diff --git a/arch/mips/ralink/dts/rt3050.dtsi b/arch/mips/ralink/dts/rt3050.dtsi +new file mode 100644 +index 0000000..fd49daa --- /dev/null +++ b/arch/mips/ralink/dts/rt3050.dtsi @@ -0,0 +1,96 @@ @@ -114,6 +117,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4898/ + }; + }; +}; +diff --git a/arch/mips/ralink/dts/rt3052_eval.dts b/arch/mips/ralink/dts/rt3052_eval.dts +new file mode 100644 +index 0000000..dc56e58 --- /dev/null +++ b/arch/mips/ralink/dts/rt3052_eval.dts @@ -0,0 +1,52 @@ @@ -169,3 +175,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4898/ + }; + }; +}; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0010-MIPS-ralink-adds-Kbuild-files.patch b/target/linux/ramips/patches-3.8/0010-MIPS-ralink-adds-Kbuild-files.patch index bccc3b87d9..88c018c719 100644 --- a/target/linux/ramips/patches-3.8/0010-MIPS-ralink-adds-Kbuild-files.patch +++ b/target/linux/ramips/patches-3.8/0010-MIPS-ralink-adds-Kbuild-files.patch @@ -1,7 +1,7 @@ -From ae2b5bb6570481b50a7175c64176b82da0a81836 Mon Sep 17 00:00:00 2001 +From 79e69b7a01246e945448039f7dce170eef0b6e3b Mon Sep 17 00:00:00 2001 From: John Crispin Date: Sun, 20 Jan 2013 22:05:30 +0100 -Subject: [PATCH 10/14] MIPS: ralink: adds Kbuild files +Subject: [PATCH 10/79] MIPS: ralink: adds Kbuild files Add the Kbuild symbols and Makefiles needed to actually build the ralink code from this series @@ -22,6 +22,8 @@ Patchwork: http://patchwork.linux-mips.org/patch/4899/ create mode 100644 arch/mips/ralink/Platform create mode 100644 arch/mips/ralink/dts/Makefile +diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms +index 91b9d69..9a73ce6 100644 --- a/arch/mips/Kbuild.platforms +++ b/arch/mips/Kbuild.platforms @@ -22,6 +22,7 @@ platforms += pmc-sierra @@ -32,6 +34,8 @@ Patchwork: http://patchwork.linux-mips.org/patch/4899/ platforms += rb532 platforms += sgi-ip22 platforms += sgi-ip27 +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index 2ac626a..b5081b5 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -434,6 +434,22 @@ config POWERTV @@ -65,6 +69,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4899/ source "arch/mips/sgi-ip27/Kconfig" source "arch/mips/sibyte/Kconfig" source "arch/mips/txx9/Kconfig" +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +new file mode 100644 +index 0000000..a0b0197 --- /dev/null +++ b/arch/mips/ralink/Kconfig @@ -0,0 +1,32 @@ @@ -100,6 +107,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4899/ +endchoice + +endif +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +new file mode 100644 +index 0000000..939757f --- /dev/null +++ b/arch/mips/ralink/Makefile @@ -0,0 +1,15 @@ @@ -118,6 +128,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4899/ +obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + +obj-y += dts/ +diff --git a/arch/mips/ralink/Platform b/arch/mips/ralink/Platform +new file mode 100644 +index 0000000..6babd65 --- /dev/null +++ b/arch/mips/ralink/Platform @@ -0,0 +1,10 @@ @@ -131,7 +144,13 @@ Patchwork: http://patchwork.linux-mips.org/patch/4899/ +# Ralink RT305x +# +load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 +diff --git a/arch/mips/ralink/dts/Makefile b/arch/mips/ralink/dts/Makefile +new file mode 100644 +index 0000000..1a69fb3 --- /dev/null +++ b/arch/mips/ralink/dts/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0011-MIPS-ralink-adds-default-config-file.patch b/target/linux/ramips/patches-3.8/0011-MIPS-ralink-adds-default-config-file.patch index 5998b1130b..1f4a1e2b2c 100644 --- a/target/linux/ramips/patches-3.8/0011-MIPS-ralink-adds-default-config-file.patch +++ b/target/linux/ramips/patches-3.8/0011-MIPS-ralink-adds-default-config-file.patch @@ -1,7 +1,7 @@ -From 6d63d70f9fe4c1b3d293ac3b9d2fcaf937d95cea Mon Sep 17 00:00:00 2001 +From 428bb7af86ffb6171e11c36dfcdacd87ed5341e6 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 1 Feb 2013 12:50:49 +0100 -Subject: [PATCH 11/14] MIPS: ralink: adds default config file +Subject: [PATCH 11/79] MIPS: ralink: adds default config file Signed-off-by: John Crispin --- @@ -9,6 +9,9 @@ Signed-off-by: John Crispin 1 file changed, 167 insertions(+) create mode 100644 arch/mips/configs/rt305x_defconfig +diff --git a/arch/mips/configs/rt305x_defconfig b/arch/mips/configs/rt305x_defconfig +new file mode 100644 +index 0000000..d1741bc --- /dev/null +++ b/arch/mips/configs/rt305x_defconfig @@ -0,0 +1,167 @@ @@ -179,3 +182,6 @@ Signed-off-by: John Crispin +# CONFIG_XZ_DEC_ARMTHUMB is not set +# CONFIG_XZ_DEC_SPARC is not set +CONFIG_AVERAGE=y +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0012-Document-devicetree-add-OF-documents-for-MIPS-interr.patch b/target/linux/ramips/patches-3.8/0012-Document-devicetree-add-OF-documents-for-MIPS-interr.patch index 6a07d8de03..b7ce7c9427 100644 --- a/target/linux/ramips/patches-3.8/0012-Document-devicetree-add-OF-documents-for-MIPS-interr.patch +++ b/target/linux/ramips/patches-3.8/0012-Document-devicetree-add-OF-documents-for-MIPS-interr.patch @@ -1,7 +1,7 @@ -From dcc7310e144c3bf17a86d2f058d60fb525d4b34a Mon Sep 17 00:00:00 2001 +From dae867771332e7541783ebb6bacf33356ad449b3 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 31 Jan 2013 13:44:10 +0100 -Subject: [PATCH 12/14] Document: devicetree: add OF documents for MIPS +Subject: [PATCH 12/79] Document: devicetree: add OF documents for MIPS interrupt controller Signed-off-by: John Crispin @@ -12,6 +12,9 @@ Patchwork: http://patchwork.linux-mips.org/patch/4901/ 1 file changed, 47 insertions(+) create mode 100644 Documentation/devicetree/bindings/mips/cpu_irq.txt +diff --git a/Documentation/devicetree/bindings/mips/cpu_irq.txt b/Documentation/devicetree/bindings/mips/cpu_irq.txt +new file mode 100644 +index 0000000..13aa4b6 --- /dev/null +++ b/Documentation/devicetree/bindings/mips/cpu_irq.txt @@ -0,0 +1,47 @@ @@ -62,3 +65,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4901/ +{ + of_irq_init(of_irq_ids); +} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0013-MIPS-add-irqdomain-support-for-the-CPU-IRQ-controlle.patch b/target/linux/ramips/patches-3.8/0013-MIPS-add-irqdomain-support-for-the-CPU-IRQ-controlle.patch index 3139106763..e5387a2569 100644 --- a/target/linux/ramips/patches-3.8/0013-MIPS-add-irqdomain-support-for-the-CPU-IRQ-controlle.patch +++ b/target/linux/ramips/patches-3.8/0013-MIPS-add-irqdomain-support-for-the-CPU-IRQ-controlle.patch @@ -1,7 +1,7 @@ -From 0916b46962cbcac9465d253d0a398435b3965fd5 Mon Sep 17 00:00:00 2001 +From 65e39f763eeca6fb93f48ed5a9b296277a543429 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 31 Jan 2013 12:20:43 +0000 -Subject: [PATCH 13/14] MIPS: add irqdomain support for the CPU IRQ controller +Subject: [PATCH 13/79] MIPS: add irqdomain support for the CPU IRQ controller Add code to load a irq_domain for the MIPS IRQ controller from a devicetree file. @@ -15,6 +15,8 @@ Patchwork: http://patchwork.linux-mips.org/patch/4902/ arch/mips/kernel/irq_cpu.c | 42 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) +diff --git a/arch/mips/include/asm/irq_cpu.h b/arch/mips/include/asm/irq_cpu.h +index ef6a07c..3f11fdb 100644 --- a/arch/mips/include/asm/irq_cpu.h +++ b/arch/mips/include/asm/irq_cpu.h @@ -17,4 +17,10 @@ extern void mips_cpu_irq_init(void); @@ -28,6 +30,8 @@ Patchwork: http://patchwork.linux-mips.org/patch/4902/ +#endif + #endif /* _ASM_IRQ_CPU_H */ +diff --git a/arch/mips/kernel/irq_cpu.c b/arch/mips/kernel/irq_cpu.c +index 972263b..49bc9ca 100644 --- a/arch/mips/kernel/irq_cpu.c +++ b/arch/mips/kernel/irq_cpu.c @@ -31,6 +31,7 @@ @@ -83,3 +87,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4902/ + return 0; +} +#endif /* CONFIG_IRQ_DOMAIN */ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0014-MIPS-ralink-add-CPU-interrupt-controller-to-of_irq_i.patch b/target/linux/ramips/patches-3.8/0014-MIPS-ralink-add-CPU-interrupt-controller-to-of_irq_i.patch index e9e2f56d3a..e346131b0a 100644 --- a/target/linux/ramips/patches-3.8/0014-MIPS-ralink-add-CPU-interrupt-controller-to-of_irq_i.patch +++ b/target/linux/ramips/patches-3.8/0014-MIPS-ralink-add-CPU-interrupt-controller-to-of_irq_i.patch @@ -1,7 +1,7 @@ -From d3d2b4200b5a42851365e903d101f8f0882eb9eb Mon Sep 17 00:00:00 2001 +From 9afd2ba44145009578d9d445183480a698cc04f2 Mon Sep 17 00:00:00 2001 From: Gabor Juhos Date: Thu, 31 Jan 2013 20:43:30 +0100 -Subject: [PATCH 14/14] MIPS: ralink: add CPU interrupt controller to +Subject: [PATCH 14/79] MIPS: ralink: add CPU interrupt controller to of_irq_ids Convert the ralink IRQ code to make use of the new MIPS IRQ controller OF @@ -16,6 +16,8 @@ Patchwork: http://patchwork.linux-mips.org/patch/4900/ arch/mips/ralink/irq.c | 10 +++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) +diff --git a/arch/mips/ralink/dts/rt3050.dtsi b/arch/mips/ralink/dts/rt3050.dtsi +index fd49daa..069d066 100644 --- a/arch/mips/ralink/dts/rt3050.dtsi +++ b/arch/mips/ralink/dts/rt3050.dtsi @@ -13,6 +13,13 @@ @@ -42,9 +44,11 @@ Patchwork: http://patchwork.linux-mips.org/patch/4900/ }; memc@300 { +diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c +index e62c975..6d054c5 100644 --- a/arch/mips/ralink/irq.c +++ b/arch/mips/ralink/irq.c -@@ -128,8 +128,11 @@ static int __init intc_of_init(struct de +@@ -128,8 +128,11 @@ static int __init intc_of_init(struct device_node *node, { struct resource res; struct irq_domain *domain; @@ -57,7 +61,7 @@ Patchwork: http://patchwork.linux-mips.org/patch/4900/ if (of_address_to_resource(node, 0, &res)) panic("Failed to get intc memory range"); -@@ -156,8 +159,8 @@ static int __init intc_of_init(struct de +@@ -156,8 +159,8 @@ static int __init intc_of_init(struct device_node *node, rt_intc_w32(INTC_INT_GLOBAL, INTC_REG_ENABLE); @@ -68,7 +72,7 @@ Patchwork: http://patchwork.linux-mips.org/patch/4900/ cp0_perfcount_irq = irq_create_mapping(domain, 9); -@@ -165,6 +168,7 @@ static int __init intc_of_init(struct de +@@ -165,6 +168,7 @@ static int __init intc_of_init(struct device_node *node, } static struct of_device_id __initdata of_irq_ids[] = { @@ -76,3 +80,6 @@ Patchwork: http://patchwork.linux-mips.org/patch/4900/ { .compatible = "ralink,rt2880-intc", .data = intc_of_init }, {}, }; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0015-serial-ralink-adds-support-for-the-serial-core-found.patch b/target/linux/ramips/patches-3.8/0015-serial-ralink-adds-support-for-the-serial-core-found.patch index 38ef41abf7..b995422d13 100644 --- a/target/linux/ramips/patches-3.8/0015-serial-ralink-adds-support-for-the-serial-core-found.patch +++ b/target/linux/ramips/patches-3.8/0015-serial-ralink-adds-support-for-the-serial-core-found.patch @@ -1,8 +1,8 @@ -From c420811f117a59a4a7d4e34b362437b91c7fafa1 Mon Sep 17 00:00:00 2001 +From 219ec2244c2e9085e6900dc515a24f6655c79827 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 25 Jan 2013 19:39:51 +0100 -Subject: [PATCH] serial: ralink: adds support for the serial core found on - ralink wisoc +Subject: [PATCH 15/79] serial: ralink: adds support for the serial core found + on ralink wisoc The MIPS based Ralink WiSoC platform has 1 or more 8250 compatible serial cores. To make them work we require the same quirks that are used by AU1x00. @@ -15,9 +15,11 @@ Signed-off-by: Greg Kroah-Hartman include/linux/serial_core.h | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) +diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c +index f932043..f72eb7d 100644 --- a/drivers/tty/serial/8250/8250.c +++ b/drivers/tty/serial/8250/8250.c -@@ -345,9 +345,9 @@ static void default_serial_dl_write(stru +@@ -324,9 +324,9 @@ static void default_serial_dl_write(struct uart_8250_port *up, int value) serial_out(up, UART_DLM, value >> 8 & 0xff); } @@ -29,7 +31,7 @@ Signed-off-by: Greg Kroah-Hartman static const u8 au_io_in_map[] = { [UART_RX] = 0, [UART_IER] = 2, -@@ -527,7 +527,7 @@ static void set_io_from_upio(struct uart +@@ -506,7 +506,7 @@ static void set_io_from_upio(struct uart_port *p) break; #endif @@ -38,6 +40,8 @@ Signed-off-by: Greg Kroah-Hartman case UPIO_AU: p->serial_in = au_serial_in; p->serial_out = au_serial_out; +diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig +index c31133a..9e4febd 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -277,3 +277,11 @@ config SERIAL_8250_EM @@ -52,6 +56,8 @@ Signed-off-by: Greg Kroah-Hartman + If you have a Ralink RT288x/RT305x SoC based board and want to use the + serial port, say Y to this option. The driver can handle up to 2 serial + ports. If unsure, say N. +diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h +index c6690a2..0b428d6 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -134,7 +134,7 @@ struct uart_port { @@ -63,3 +69,6 @@ Signed-off-by: Greg Kroah-Hartman #define UPIO_TSI (5) /* Tsi108/109 type IO */ #define UPIO_RM9000 (6) /* RM9000 type IO */ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0016-MIPS-move-mips_-set-get-_machine_name-to-a-more-gene.patch b/target/linux/ramips/patches-3.8/0016-MIPS-move-mips_-set-get-_machine_name-to-a-more-gene.patch new file mode 100644 index 0000000000..8886cd791a --- /dev/null +++ b/target/linux/ramips/patches-3.8/0016-MIPS-move-mips_-set-get-_machine_name-to-a-more-gene.patch @@ -0,0 +1,167 @@ +From 231e989ef4a11073ad6be8af797f96d51256d07a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 11 Apr 2013 05:34:59 +0000 +Subject: [PATCH 16/79] MIPS: move mips_{set,get}_machine_name() to a more + generic place + +Previously this functionality was only available to users of the mips_machine +api. Moving the code to prom.c allows us to also add a OF wrapper. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5164/ +--- + arch/mips/include/asm/mips_machine.h | 4 ---- + arch/mips/include/asm/prom.h | 3 +++ + arch/mips/kernel/mips_machine.c | 21 --------------------- + arch/mips/kernel/proc.c | 2 +- + arch/mips/kernel/prom.c | 31 +++++++++++++++++++++++++++++++ + 5 files changed, 35 insertions(+), 26 deletions(-) + +diff --git a/arch/mips/include/asm/mips_machine.h b/arch/mips/include/asm/mips_machine.h +index 363bb35..9d00aeb 100644 +--- a/arch/mips/include/asm/mips_machine.h ++++ b/arch/mips/include/asm/mips_machine.h +@@ -42,13 +42,9 @@ extern long __mips_machines_end; + #ifdef CONFIG_MIPS_MACHINE + int mips_machtype_setup(char *id) __init; + void mips_machine_setup(void) __init; +-void mips_set_machine_name(const char *name) __init; +-char *mips_get_machine_name(void); + #else + static inline int mips_machtype_setup(char *id) { return 1; } + static inline void mips_machine_setup(void) { } +-static inline void mips_set_machine_name(const char *name) { } +-static inline char *mips_get_machine_name(void) { return NULL; } + #endif /* CONFIG_MIPS_MACHINE */ + + #endif /* __ASM_MIPS_MACHINE_H */ +diff --git a/arch/mips/include/asm/prom.h b/arch/mips/include/asm/prom.h +index 8808bf5..1e7e096 100644 +--- a/arch/mips/include/asm/prom.h ++++ b/arch/mips/include/asm/prom.h +@@ -48,4 +48,7 @@ extern void __dt_setup_arch(struct boot_param_header *bph); + static inline void device_tree_init(void) { } + #endif /* CONFIG_OF */ + ++extern char *mips_get_machine_name(void); ++extern void mips_set_machine_name(const char *name); ++ + #endif /* __ASM_PROM_H */ +diff --git a/arch/mips/kernel/mips_machine.c b/arch/mips/kernel/mips_machine.c +index 411a058..6dc5866 100644 +--- a/arch/mips/kernel/mips_machine.c ++++ b/arch/mips/kernel/mips_machine.c +@@ -13,7 +13,6 @@ + #include + + static struct mips_machine *mips_machine __initdata; +-static char *mips_machine_name = "Unknown"; + + #define for_each_machine(mach) \ + for ((mach) = (struct mips_machine *)&__mips_machines_start; \ +@@ -21,25 +20,6 @@ static char *mips_machine_name = "Unknown"; + (unsigned long)(mach) < (unsigned long)&__mips_machines_end; \ + (mach)++) + +-__init void mips_set_machine_name(const char *name) +-{ +- char *p; +- +- if (name == NULL) +- return; +- +- p = kstrdup(name, GFP_KERNEL); +- if (!p) +- pr_err("MIPS: no memory for machine_name\n"); +- +- mips_machine_name = p; +-} +- +-char *mips_get_machine_name(void) +-{ +- return mips_machine_name; +-} +- + __init int mips_machtype_setup(char *id) + { + struct mips_machine *mach; +@@ -79,7 +59,6 @@ __init void mips_machine_setup(void) + return; + + mips_set_machine_name(mips_machine->mach_name); +- pr_info("MIPS: machine is %s\n", mips_machine_name); + + if (mips_machine->mach_setup) + mips_machine->mach_setup(); +diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c +index 07dff54..8779237 100644 +--- a/arch/mips/kernel/proc.c ++++ b/arch/mips/kernel/proc.c +@@ -12,7 +12,7 @@ + #include + #include + #include +-#include ++#include + + unsigned int vced_count, vcei_count; + +diff --git a/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c +index 028f6f8..b68e53b 100644 +--- a/arch/mips/kernel/prom.c ++++ b/arch/mips/kernel/prom.c +@@ -23,6 +23,22 @@ + #include + #include + ++static char mips_machine_name[64] = "Unknown"; ++ ++__init void mips_set_machine_name(const char *name) ++{ ++ if (name == NULL) ++ return; ++ ++ strncpy(mips_machine_name, name, sizeof(mips_machine_name)); ++ pr_info("MIPS: machine is %s\n", mips_get_machine_name()); ++} ++ ++char *mips_get_machine_name(void) ++{ ++ return mips_machine_name; ++} ++ + int __init early_init_dt_scan_memory_arch(unsigned long node, + const char *uname, int depth, + void *data) +@@ -50,6 +66,18 @@ void __init early_init_dt_setup_initrd_arch(unsigned long start, + } + #endif + ++int __init early_init_dt_scan_model(unsigned long node, const char *uname, ++ int depth, void *data) ++{ ++ if (!depth) { ++ char *model = of_get_flat_dt_prop(node, "model", NULL); ++ ++ if (model) ++ mips_set_machine_name(model); ++ } ++ return 0; ++} ++ + void __init early_init_devtree(void *params) + { + /* Setup flat device-tree pointer */ +@@ -65,6 +93,9 @@ void __init early_init_devtree(void *params) + /* Scan memory nodes */ + of_scan_flat_dt(early_init_dt_scan_root, NULL); + of_scan_flat_dt(early_init_dt_scan_memory_arch, NULL); ++ ++ /* try to load the mips machine name */ ++ of_scan_flat_dt(early_init_dt_scan_model, NULL); + } + + void __init __dt_setup_arch(struct boot_param_header *bph) +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0017-MIPS-ralink-add-PCI-IRQ-handling.patch b/target/linux/ramips/patches-3.8/0017-MIPS-ralink-add-PCI-IRQ-handling.patch new file mode 100644 index 0000000000..c18817e54d --- /dev/null +++ b/target/linux/ramips/patches-3.8/0017-MIPS-ralink-add-PCI-IRQ-handling.patch @@ -0,0 +1,40 @@ +From 98ab228172e66e43766d9e2a0ddb73603c22dbd1 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Wed, 10 Apr 2013 09:07:27 +0200 +Subject: [PATCH 17/79] MIPS: ralink: add PCI IRQ handling + +The Ralink IRQ code was not handling the PCI IRQ yet. Add this functionaility +to make PCI work on rt3883. + +Signed-off-by: John Crispin +Signed-off-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5165/ +--- + arch/mips/ralink/irq.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c +index 6d054c5..d9807d0 100644 +--- a/arch/mips/ralink/irq.c ++++ b/arch/mips/ralink/irq.c +@@ -31,6 +31,7 @@ + #define INTC_INT_GLOBAL BIT(31) + + #define RALINK_CPU_IRQ_INTC (MIPS_CPU_IRQ_BASE + 2) ++#define RALINK_CPU_IRQ_PCI (MIPS_CPU_IRQ_BASE + 4) + #define RALINK_CPU_IRQ_FE (MIPS_CPU_IRQ_BASE + 5) + #define RALINK_CPU_IRQ_WIFI (MIPS_CPU_IRQ_BASE + 6) + #define RALINK_CPU_IRQ_COUNTER (MIPS_CPU_IRQ_BASE + 7) +@@ -104,6 +105,9 @@ asmlinkage void plat_irq_dispatch(void) + else if (pending & STATUSF_IP6) + do_IRQ(RALINK_CPU_IRQ_WIFI); + ++ else if (pending & STATUSF_IP4) ++ do_IRQ(RALINK_CPU_IRQ_PCI); ++ + else if (pending & STATUSF_IP2) + do_IRQ(RALINK_CPU_IRQ_INTC); + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0018-MIPS-ralink-add-RT3352-register-defines.patch b/target/linux/ramips/patches-3.8/0018-MIPS-ralink-add-RT3352-register-defines.patch new file mode 100644 index 0000000000..1a8950fcd6 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0018-MIPS-ralink-add-RT3352-register-defines.patch @@ -0,0 +1,40 @@ +From 8667d984d1b4f3be1c5da71788762b9945a25c90 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 21 Mar 2013 19:01:49 +0100 +Subject: [PATCH 18/79] MIPS: ralink: add RT3352 register defines + +Add a few missing defines that are needed to make USB and clock detection work +on the RT3352. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5166/ +--- + arch/mips/include/asm/mach-ralink/rt305x.h | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/rt305x.h b/arch/mips/include/asm/mach-ralink/rt305x.h +index 7d344f2..e36c3c5 100644 +--- a/arch/mips/include/asm/mach-ralink/rt305x.h ++++ b/arch/mips/include/asm/mach-ralink/rt305x.h +@@ -136,4 +136,17 @@ static inline int soc_is_rt5350(void) + #define RT305X_GPIO_MODE_SDRAM BIT(8) + #define RT305X_GPIO_MODE_RGMII BIT(9) + ++#define RT3352_SYSC_REG_SYSCFG0 0x010 ++#define RT3352_SYSC_REG_SYSCFG1 0x014 ++#define RT3352_SYSC_REG_CLKCFG1 0x030 ++#define RT3352_SYSC_REG_RSTCTRL 0x034 ++#define RT3352_SYSC_REG_USB_PS 0x05c ++ ++#define RT3352_CLKCFG0_XTAL_SEL BIT(20) ++#define RT3352_CLKCFG1_UPHY0_CLK_EN BIT(18) ++#define RT3352_CLKCFG1_UPHY1_CLK_EN BIT(20) ++#define RT3352_RSTCTRL_UHST BIT(22) ++#define RT3352_RSTCTRL_UDEV BIT(25) ++#define RT3352_SYSCFG1_USB0_HOST_MODE BIT(10) ++ + #endif +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0019-MIPS-ralink-fix-RT305x-clock-setup.patch b/target/linux/ramips/patches-3.8/0019-MIPS-ralink-fix-RT305x-clock-setup.patch new file mode 100644 index 0000000000..ebecdf2629 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0019-MIPS-ralink-fix-RT305x-clock-setup.patch @@ -0,0 +1,52 @@ +From 853823a469a8123657bf32bc5e1843c40529a20d Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 22 Mar 2013 19:25:59 +0100 +Subject: [PATCH 19/79] MIPS: ralink: fix RT305x clock setup + +Add a few missing clocks. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5167/ +--- + arch/mips/ralink/rt305x.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index 0a4bbdc..5d49a54 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -124,6 +124,8 @@ struct ralink_pinmux gpio_pinmux = { + void __init ralink_clk_init(void) + { + unsigned long cpu_rate, sys_rate, wdt_rate, uart_rate; ++ unsigned long wmac_rate = 40000000; ++ + u32 t = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG); + + if (soc_is_rt305x() || soc_is_rt3350()) { +@@ -176,11 +178,21 @@ void __init ralink_clk_init(void) + BUG(); + } + ++ if (soc_is_rt3352() || soc_is_rt5350()) { ++ u32 val = rt_sysc_r32(RT3352_SYSC_REG_SYSCFG0); ++ ++ if (!(val & RT3352_CLKCFG0_XTAL_SEL)) ++ wmac_rate = 20000000; ++ } ++ + ralink_clk_add("cpu", cpu_rate); + ralink_clk_add("10000b00.spi", sys_rate); + ralink_clk_add("10000100.timer", wdt_rate); ++ ralink_clk_add("10000120.watchdog", wdt_rate); + ralink_clk_add("10000500.uart", uart_rate); + ralink_clk_add("10000c00.uartlite", uart_rate); ++ ralink_clk_add("10100000.ethernet", sys_rate); ++ ralink_clk_add("10180000.wmac", wmac_rate); + } + + void __init ralink_of_remap(void) +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0020-MIPS-ralink-add-missing-comment-in-irq-driver.patch b/target/linux/ramips/patches-3.8/0020-MIPS-ralink-add-missing-comment-in-irq-driver.patch new file mode 100644 index 0000000000..51cfe0c4db --- /dev/null +++ b/target/linux/ramips/patches-3.8/0020-MIPS-ralink-add-missing-comment-in-irq-driver.patch @@ -0,0 +1,29 @@ +From 7c268f1b47669be2efce1607ee02193cb49424cf Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 16 Mar 2013 16:28:54 +0100 +Subject: [PATCH 20/79] MIPS: ralink: add missing comment in irq driver + +Trivial patch that adds a comment that makes the code more readable. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5168/ +--- + arch/mips/ralink/irq.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c +index d9807d0..320b1f1 100644 +--- a/arch/mips/ralink/irq.c ++++ b/arch/mips/ralink/irq.c +@@ -166,6 +166,7 @@ static int __init intc_of_init(struct device_node *node, + irq_set_chained_handler(irq, ralink_intc_irq_handler); + irq_set_handler_data(irq, domain); + ++ /* tell the kernel which irq is used for performance monitoring */ + cp0_perfcount_irq = irq_create_mapping(domain, 9); + + return 0; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0021-MIPS-ralink-add-RT5350-sdram-register-defines.patch b/target/linux/ramips/patches-3.8/0021-MIPS-ralink-add-RT5350-sdram-register-defines.patch new file mode 100644 index 0000000000..8ecaea20ab --- /dev/null +++ b/target/linux/ramips/patches-3.8/0021-MIPS-ralink-add-RT5350-sdram-register-defines.patch @@ -0,0 +1,37 @@ +From 0df8c2fdd0fe1095b834fbf2b098d6f1b3e56608 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 25 Mar 2013 11:19:58 +0100 +Subject: [PATCH 21/79] MIPS: ralink: add RT5350 sdram register defines + +Add a few missing defines that are needed to make memory detection work on the +RT5350. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5169/ +--- + arch/mips/include/asm/mach-ralink/rt305x.h | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/rt305x.h b/arch/mips/include/asm/mach-ralink/rt305x.h +index e36c3c5..80cda8a 100644 +--- a/arch/mips/include/asm/mach-ralink/rt305x.h ++++ b/arch/mips/include/asm/mach-ralink/rt305x.h +@@ -97,6 +97,14 @@ static inline int soc_is_rt5350(void) + #define RT5350_SYSCFG0_CPUCLK_320 0x2 + #define RT5350_SYSCFG0_CPUCLK_300 0x3 + ++#define RT5350_SYSCFG0_DRAM_SIZE_SHIFT 12 ++#define RT5350_SYSCFG0_DRAM_SIZE_MASK 7 ++#define RT5350_SYSCFG0_DRAM_SIZE_2M 0 ++#define RT5350_SYSCFG0_DRAM_SIZE_8M 1 ++#define RT5350_SYSCFG0_DRAM_SIZE_16M 2 ++#define RT5350_SYSCFG0_DRAM_SIZE_32M 3 ++#define RT5350_SYSCFG0_DRAM_SIZE_64M 4 ++ + /* multi function gpio pins */ + #define RT305X_GPIO_I2C_SD 1 + #define RT305X_GPIO_I2C_SCLK 2 +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0022-MIPS-ralink-make-early_printk-work-on-RT2880.patch b/target/linux/ramips/patches-3.8/0022-MIPS-ralink-make-early_printk-work-on-RT2880.patch new file mode 100644 index 0000000000..27883978d9 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0022-MIPS-ralink-make-early_printk-work-on-RT2880.patch @@ -0,0 +1,33 @@ +From 9ed190912864c8b96d888af2cb66efcf1dc5562a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 9 Apr 2013 18:31:15 +0200 +Subject: [PATCH 22/79] MIPS: ralink: make early_printk work on RT2880 + +RT2880 has a different location for the early serial port. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5170/ +--- + arch/mips/ralink/early_printk.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/mips/ralink/early_printk.c b/arch/mips/ralink/early_printk.c +index c4ae47e..b46d041 100644 +--- a/arch/mips/ralink/early_printk.c ++++ b/arch/mips/ralink/early_printk.c +@@ -11,7 +11,11 @@ + + #include + ++#ifdef CONFIG_SOC_RT288X ++#define EARLY_UART_BASE 0x300c00 ++#else + #define EARLY_UART_BASE 0x10000c00 ++#endif + + #define UART_REG_RX 0x00 + #define UART_REG_TX 0x04 +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0023-MIPS-ralink-rename-gpio_pinmux-to-rt_gpio_pinmux.patch b/target/linux/ramips/patches-3.8/0023-MIPS-ralink-rename-gpio_pinmux-to-rt_gpio_pinmux.patch new file mode 100644 index 0000000000..03d95dfb3d --- /dev/null +++ b/target/linux/ramips/patches-3.8/0023-MIPS-ralink-rename-gpio_pinmux-to-rt_gpio_pinmux.patch @@ -0,0 +1,43 @@ +From d5b75031f6ad0f9f82c3b8faeab3cda1cb4ebfe9 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 22:12:09 +0200 +Subject: [PATCH 23/79] MIPS: ralink: rename gpio_pinmux to rt_gpio_pinmux + +Add proper namespacing to the variable. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5171/ +--- + arch/mips/ralink/common.h | 2 +- + arch/mips/ralink/rt305x.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h +index 3009903..f4b19c6 100644 +--- a/arch/mips/ralink/common.h ++++ b/arch/mips/ralink/common.h +@@ -24,7 +24,7 @@ struct ralink_pinmux { + int uart_shift; + void (*wdt_reset)(void); + }; +-extern struct ralink_pinmux gpio_pinmux; ++extern struct ralink_pinmux rt_gpio_pinmux; + + struct ralink_soc_info { + unsigned char sys_type[RAMIPS_SYS_TYPE_LEN]; +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index 5d49a54..f1a6c33 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -114,7 +114,7 @@ void rt305x_wdt_reset(void) + rt_sysc_w32(t, SYSC_REG_SYSTEM_CONFIG); + } + +-struct ralink_pinmux gpio_pinmux = { ++struct ralink_pinmux rt_gpio_pinmux = { + .mode = mode_mux, + .uart = uart_mux, + .uart_shift = RT305X_GPIO_MODE_UART0_SHIFT, +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0024-MIPS-ralink-make-the-RT305x-pinmuxing-structure-stat.patch b/target/linux/ramips/patches-3.8/0024-MIPS-ralink-make-the-RT305x-pinmuxing-structure-stat.patch new file mode 100644 index 0000000000..60a8a53a06 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0024-MIPS-ralink-make-the-RT305x-pinmuxing-structure-stat.patch @@ -0,0 +1,49 @@ +From 2793deaf4d3d364ba2ed075abf2b9022a152f253 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 22:16:12 +0200 +Subject: [PATCH 24/79] MIPS: ralink: make the RT305x pinmuxing structure + static + +These structures are exported via struct ralink_pinmux rt_gpio_pinmux and can +hence be static. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5172/ +--- + arch/mips/ralink/rt305x.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index f1a6c33..5b42078 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -22,7 +22,7 @@ + + enum rt305x_soc_type rt305x_soc; + +-struct ralink_pinmux_grp mode_mux[] = { ++static struct ralink_pinmux_grp mode_mux[] = { + { + .name = "i2c", + .mask = RT305X_GPIO_MODE_I2C, +@@ -61,7 +61,7 @@ struct ralink_pinmux_grp mode_mux[] = { + }, {0} + }; + +-struct ralink_pinmux_grp uart_mux[] = { ++static struct ralink_pinmux_grp uart_mux[] = { + { + .name = "uartf", + .mask = RT305X_GPIO_MODE_UARTF, +@@ -103,7 +103,7 @@ struct ralink_pinmux_grp uart_mux[] = { + }, {0} + }; + +-void rt305x_wdt_reset(void) ++static void rt305x_wdt_reset(void) + { + u32 t; + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0025-MIPS-ralink-add-pci-group-to-struct-ralink_pinmux.patch b/target/linux/ramips/patches-3.8/0025-MIPS-ralink-add-pci-group-to-struct-ralink_pinmux.patch new file mode 100644 index 0000000000..368fcde680 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0025-MIPS-ralink-add-pci-group-to-struct-ralink_pinmux.patch @@ -0,0 +1,31 @@ +From e859bf709e73acb5735cf1207422f53fc3202632 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Fri, 12 Apr 2013 12:40:23 +0200 +Subject: [PATCH 25/79] MIPS: ralink: add pci group to struct ralink_pinmux + +This will be used for RT3662/RT3883. + +Signed-off-by: Gabor Juhos +Acked-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5173/ +--- + arch/mips/ralink/common.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h +index f4b19c6..bebd149 100644 +--- a/arch/mips/ralink/common.h ++++ b/arch/mips/ralink/common.h +@@ -23,6 +23,9 @@ struct ralink_pinmux { + struct ralink_pinmux_grp *uart; + int uart_shift; + void (*wdt_reset)(void); ++ struct ralink_pinmux_grp *pci; ++ int pci_shift; ++ u32 pci_mask; + }; + extern struct ralink_pinmux rt_gpio_pinmux; + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0026-MIPS-ralink-add-uart-mask-to-struct-ralink_pinmux.patch b/target/linux/ramips/patches-3.8/0026-MIPS-ralink-add-uart-mask-to-struct-ralink_pinmux.patch new file mode 100644 index 0000000000..1ee9fd4a5b --- /dev/null +++ b/target/linux/ramips/patches-3.8/0026-MIPS-ralink-add-uart-mask-to-struct-ralink_pinmux.patch @@ -0,0 +1,56 @@ +From 6e09e0465b342b52ecda583cbc41e6a31c363b3f Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 12:45:27 +0200 +Subject: [PATCH 26/79] MIPS: ralink: add uart mask to struct ralink_pinmux + +Add a field for the uart muxing mask and set it inside the rt305x setup code. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5744/ +--- + arch/mips/ralink/common.h | 1 + + arch/mips/ralink/rt305x.c | 5 +++-- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h +index bebd149..299119b 100644 +--- a/arch/mips/ralink/common.h ++++ b/arch/mips/ralink/common.h +@@ -22,6 +22,7 @@ struct ralink_pinmux { + struct ralink_pinmux_grp *mode; + struct ralink_pinmux_grp *uart; + int uart_shift; ++ u32 uart_mask; + void (*wdt_reset)(void); + struct ralink_pinmux_grp *pci; + int pci_shift; +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index 5b42078..6aa3cb1 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -91,12 +91,12 @@ static struct ralink_pinmux_grp uart_mux[] = { + .name = "gpio uartf", + .mask = RT305X_GPIO_MODE_GPIO_UARTF, + .gpio_first = RT305X_GPIO_7, +- .gpio_last = RT305X_GPIO_14, ++ .gpio_last = RT305X_GPIO_10, + }, { + .name = "gpio i2s", + .mask = RT305X_GPIO_MODE_GPIO_I2S, + .gpio_first = RT305X_GPIO_7, +- .gpio_last = RT305X_GPIO_14, ++ .gpio_last = RT305X_GPIO_10, + }, { + .name = "gpio", + .mask = RT305X_GPIO_MODE_GPIO, +@@ -118,6 +118,7 @@ struct ralink_pinmux rt_gpio_pinmux = { + .mode = mode_mux, + .uart = uart_mux, + .uart_shift = RT305X_GPIO_MODE_UART0_SHIFT, ++ .uart_mask = RT305X_GPIO_MODE_UART0_MASK, + .wdt_reset = rt305x_wdt_reset, + }; + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0027-MIPS-ralink-adds-support-for-RT2880-SoC-family.patch b/target/linux/ramips/patches-3.8/0027-MIPS-ralink-adds-support-for-RT2880-SoC-family.patch new file mode 100644 index 0000000000..bd8ad22460 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0027-MIPS-ralink-adds-support-for-RT2880-SoC-family.patch @@ -0,0 +1,281 @@ +From 2a0d9878985bb3274bb61535f76ea293991635a9 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 27 Jan 2013 09:17:20 +0100 +Subject: [PATCH 27/79] MIPS: ralink: adds support for RT2880 SoC family + +Add support code for rt2880 SOC. + +The code detects the SoC and registers the clk / pinmux settings. + +Signed-off-by: John Crispin +Signed-off-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5176/ +--- + arch/mips/Kconfig | 2 +- + arch/mips/include/asm/mach-ralink/rt288x.h | 49 ++++++++++ + arch/mips/ralink/Kconfig | 3 + + arch/mips/ralink/Makefile | 1 + + arch/mips/ralink/Platform | 5 + + arch/mips/ralink/rt288x.c | 139 ++++++++++++++++++++++++++++ + 6 files changed, 198 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/include/asm/mach-ralink/rt288x.h + create mode 100644 arch/mips/ralink/rt288x.c + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index b5081b5..b5fd476 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -1177,7 +1177,7 @@ config BOOT_ELF32 + + config MIPS_L1_CACHE_SHIFT + int +- default "4" if MACH_DECSTATION || MIKROTIK_RB532 || PMC_MSP4200_EVAL ++ default "4" if MACH_DECSTATION || MIKROTIK_RB532 || PMC_MSP4200_EVAL || SOC_RT288X + default "6" if MIPS_CPU_SCACHE + default "7" if SGI_IP22 || SGI_IP27 || SGI_IP28 || SNI_RM || CPU_CAVIUM_OCTEON + default "5" +diff --git a/arch/mips/include/asm/mach-ralink/rt288x.h b/arch/mips/include/asm/mach-ralink/rt288x.h +new file mode 100644 +index 0000000..ad8b42d +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt288x.h +@@ -0,0 +1,49 @@ ++/* ++ * 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. ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * Copyright (C) 2008-2011 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#ifndef _RT288X_REGS_H_ ++#define _RT288X_REGS_H_ ++ ++#define RT2880_SYSC_BASE 0x00300000 ++ ++#define SYSC_REG_CHIP_NAME0 0x00 ++#define SYSC_REG_CHIP_NAME1 0x04 ++#define SYSC_REG_CHIP_ID 0x0c ++#define SYSC_REG_SYSTEM_CONFIG 0x10 ++#define SYSC_REG_CLKCFG 0x30 ++ ++#define RT2880_CHIP_NAME0 0x38325452 ++#define RT2880_CHIP_NAME1 0x20203038 ++ ++#define CHIP_ID_ID_MASK 0xff ++#define CHIP_ID_ID_SHIFT 8 ++#define CHIP_ID_REV_MASK 0xff ++ ++#define SYSTEM_CONFIG_CPUCLK_SHIFT 20 ++#define SYSTEM_CONFIG_CPUCLK_MASK 0x3 ++#define SYSTEM_CONFIG_CPUCLK_250 0x0 ++#define SYSTEM_CONFIG_CPUCLK_266 0x1 ++#define SYSTEM_CONFIG_CPUCLK_280 0x2 ++#define SYSTEM_CONFIG_CPUCLK_300 0x3 ++ ++#define RT2880_GPIO_MODE_I2C BIT(0) ++#define RT2880_GPIO_MODE_UART0 BIT(1) ++#define RT2880_GPIO_MODE_SPI BIT(2) ++#define RT2880_GPIO_MODE_UART1 BIT(3) ++#define RT2880_GPIO_MODE_JTAG BIT(4) ++#define RT2880_GPIO_MODE_MDIO BIT(5) ++#define RT2880_GPIO_MODE_SDRAM BIT(6) ++#define RT2880_GPIO_MODE_PCI BIT(7) ++ ++#define CLKCFG_SRAM_CS_N_WDT BIT(9) ++ ++#endif +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index a0b0197..6723b94 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -6,6 +6,9 @@ choice + help + Select Ralink MIPS SoC type. + ++ config SOC_RT288X ++ bool "RT288x" ++ + config SOC_RT305X + bool "RT305x" + select USB_ARCH_HAS_HCD +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index 939757f..6d826f2 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -8,6 +8,7 @@ + + obj-y := prom.o of.o reset.o clk.o irq.o + ++obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o + + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +diff --git a/arch/mips/ralink/Platform b/arch/mips/ralink/Platform +index 6babd65..3f49e51 100644 +--- a/arch/mips/ralink/Platform ++++ b/arch/mips/ralink/Platform +@@ -5,6 +5,11 @@ core-$(CONFIG_RALINK) += arch/mips/ralink/ + cflags-$(CONFIG_RALINK) += -I$(srctree)/arch/mips/include/asm/mach-ralink + + # ++# Ralink RT288x ++# ++load-$(CONFIG_SOC_RT288X) += 0xffffffff88000000 ++ ++# + # Ralink RT305x + # + load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 +diff --git a/arch/mips/ralink/rt288x.c b/arch/mips/ralink/rt288x.c +new file mode 100644 +index 0000000..1e0788e +--- /dev/null ++++ b/arch/mips/ralink/rt288x.c +@@ -0,0 +1,139 @@ ++/* ++ * 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. ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * Copyright (C) 2008-2011 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "common.h" ++ ++static struct ralink_pinmux_grp mode_mux[] = { ++ { ++ .name = "i2c", ++ .mask = RT2880_GPIO_MODE_I2C, ++ .gpio_first = 1, ++ .gpio_last = 2, ++ }, { ++ .name = "spi", ++ .mask = RT2880_GPIO_MODE_SPI, ++ .gpio_first = 3, ++ .gpio_last = 6, ++ }, { ++ .name = "uartlite", ++ .mask = RT2880_GPIO_MODE_UART0, ++ .gpio_first = 7, ++ .gpio_last = 14, ++ }, { ++ .name = "jtag", ++ .mask = RT2880_GPIO_MODE_JTAG, ++ .gpio_first = 17, ++ .gpio_last = 21, ++ }, { ++ .name = "mdio", ++ .mask = RT2880_GPIO_MODE_MDIO, ++ .gpio_first = 22, ++ .gpio_last = 23, ++ }, { ++ .name = "sdram", ++ .mask = RT2880_GPIO_MODE_SDRAM, ++ .gpio_first = 24, ++ .gpio_last = 39, ++ }, { ++ .name = "pci", ++ .mask = RT2880_GPIO_MODE_PCI, ++ .gpio_first = 40, ++ .gpio_last = 71, ++ }, {0} ++}; ++ ++static void rt288x_wdt_reset(void) ++{ ++ u32 t; ++ ++ /* enable WDT reset output on pin SRAM_CS_N */ ++ t = rt_sysc_r32(SYSC_REG_CLKCFG); ++ t |= CLKCFG_SRAM_CS_N_WDT; ++ rt_sysc_w32(t, SYSC_REG_CLKCFG); ++} ++ ++struct ralink_pinmux rt_gpio_pinmux = { ++ .mode = mode_mux, ++ .wdt_reset = rt288x_wdt_reset, ++}; ++ ++void __init ralink_clk_init(void) ++{ ++ unsigned long cpu_rate; ++ u32 t = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG); ++ t = ((t >> SYSTEM_CONFIG_CPUCLK_SHIFT) & SYSTEM_CONFIG_CPUCLK_MASK); ++ ++ switch (t) { ++ case SYSTEM_CONFIG_CPUCLK_250: ++ cpu_rate = 250000000; ++ break; ++ case SYSTEM_CONFIG_CPUCLK_266: ++ cpu_rate = 266666667; ++ break; ++ case SYSTEM_CONFIG_CPUCLK_280: ++ cpu_rate = 280000000; ++ break; ++ case SYSTEM_CONFIG_CPUCLK_300: ++ cpu_rate = 300000000; ++ break; ++ } ++ ++ ralink_clk_add("cpu", cpu_rate); ++ ralink_clk_add("300100.timer", cpu_rate / 2); ++ ralink_clk_add("300120.watchdog", cpu_rate / 2); ++ ralink_clk_add("300500.uart", cpu_rate / 2); ++ ralink_clk_add("300c00.uartlite", cpu_rate / 2); ++ ralink_clk_add("400000.ethernet", cpu_rate / 2); ++} ++ ++void __init ralink_of_remap(void) ++{ ++ rt_sysc_membase = plat_of_remap_node("ralink,rt2880-sysc"); ++ rt_memc_membase = plat_of_remap_node("ralink,rt2880-memc"); ++ ++ if (!rt_sysc_membase || !rt_memc_membase) ++ panic("Failed to remap core resources"); ++} ++ ++void prom_soc_init(struct ralink_soc_info *soc_info) ++{ ++ void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT2880_SYSC_BASE); ++ const char *name; ++ u32 n0; ++ u32 n1; ++ u32 id; ++ ++ n0 = __raw_readl(sysc + SYSC_REG_CHIP_NAME0); ++ n1 = __raw_readl(sysc + SYSC_REG_CHIP_NAME1); ++ id = __raw_readl(sysc + SYSC_REG_CHIP_ID); ++ ++ if (n0 == RT2880_CHIP_NAME0 && n1 == RT2880_CHIP_NAME1) { ++ soc_info->compatible = "ralink,r2880-soc"; ++ name = "RT2880"; ++ } else { ++ panic("rt288x: unknown SoC, n0:%08x n1:%08x", n0, n1); ++ } ++ ++ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN, ++ "Ralink %s id:%u rev:%u", ++ name, ++ (id >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK, ++ (id & CHIP_ID_REV_MASK)); ++} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0028-MIPS-ralink-adds-support-for-RT3883-SoC-family.patch b/target/linux/ramips/patches-3.8/0028-MIPS-ralink-adds-support-for-RT3883-SoC-family.patch new file mode 100644 index 0000000000..f124fecce8 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0028-MIPS-ralink-adds-support-for-RT3883-SoC-family.patch @@ -0,0 +1,567 @@ +From c75f4a5af758494595fded27efb95732365d10db Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 27 Jan 2013 09:39:02 +0100 +Subject: [PATCH 28/79] MIPS: ralink: adds support for RT3883 SoC family + +Add support code for rt3883 SOC. + +The code detects the SoC and registers the clk / pinmux settings. + +Signed-off-by: John Crispin +Signed-off-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5185/ +--- + arch/mips/include/asm/mach-ralink/rt3883.h | 247 ++++++++++++++++++++++++++++ + arch/mips/ralink/Kconfig | 5 + + arch/mips/ralink/Makefile | 1 + + arch/mips/ralink/Platform | 5 + + arch/mips/ralink/rt3883.c | 242 +++++++++++++++++++++++++++ + 5 files changed, 500 insertions(+) + create mode 100644 arch/mips/include/asm/mach-ralink/rt3883.h + create mode 100644 arch/mips/ralink/rt3883.c + +diff --git a/arch/mips/include/asm/mach-ralink/rt3883.h b/arch/mips/include/asm/mach-ralink/rt3883.h +new file mode 100644 +index 0000000..b91c6c1 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt3883.h +@@ -0,0 +1,247 @@ ++/* ++ * Ralink RT3662/RT3883 SoC register definitions ++ * ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * ++ * 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 _RT3883_REGS_H_ ++#define _RT3883_REGS_H_ ++ ++#include ++ ++#define RT3883_SDRAM_BASE 0x00000000 ++#define RT3883_SYSC_BASE 0x10000000 ++#define RT3883_TIMER_BASE 0x10000100 ++#define RT3883_INTC_BASE 0x10000200 ++#define RT3883_MEMC_BASE 0x10000300 ++#define RT3883_UART0_BASE 0x10000500 ++#define RT3883_PIO_BASE 0x10000600 ++#define RT3883_FSCC_BASE 0x10000700 ++#define RT3883_NANDC_BASE 0x10000810 ++#define RT3883_I2C_BASE 0x10000900 ++#define RT3883_I2S_BASE 0x10000a00 ++#define RT3883_SPI_BASE 0x10000b00 ++#define RT3883_UART1_BASE 0x10000c00 ++#define RT3883_PCM_BASE 0x10002000 ++#define RT3883_GDMA_BASE 0x10002800 ++#define RT3883_CODEC1_BASE 0x10003000 ++#define RT3883_CODEC2_BASE 0x10003800 ++#define RT3883_FE_BASE 0x10100000 ++#define RT3883_ROM_BASE 0x10118000 ++#define RT3883_USBDEV_BASE 0x10112000 ++#define RT3883_PCI_BASE 0x10140000 ++#define RT3883_WLAN_BASE 0x10180000 ++#define RT3883_USBHOST_BASE 0x101c0000 ++#define RT3883_BOOT_BASE 0x1c000000 ++#define RT3883_SRAM_BASE 0x1e000000 ++#define RT3883_PCIMEM_BASE 0x20000000 ++ ++#define RT3883_EHCI_BASE (RT3883_USBHOST_BASE) ++#define RT3883_OHCI_BASE (RT3883_USBHOST_BASE + 0x1000) ++ ++#define RT3883_SYSC_SIZE 0x100 ++#define RT3883_TIMER_SIZE 0x100 ++#define RT3883_INTC_SIZE 0x100 ++#define RT3883_MEMC_SIZE 0x100 ++#define RT3883_UART0_SIZE 0x100 ++#define RT3883_UART1_SIZE 0x100 ++#define RT3883_PIO_SIZE 0x100 ++#define RT3883_FSCC_SIZE 0x100 ++#define RT3883_NANDC_SIZE 0x0f0 ++#define RT3883_I2C_SIZE 0x100 ++#define RT3883_I2S_SIZE 0x100 ++#define RT3883_SPI_SIZE 0x100 ++#define RT3883_PCM_SIZE 0x800 ++#define RT3883_GDMA_SIZE 0x800 ++#define RT3883_CODEC1_SIZE 0x800 ++#define RT3883_CODEC2_SIZE 0x800 ++#define RT3883_FE_SIZE 0x10000 ++#define RT3883_ROM_SIZE 0x4000 ++#define RT3883_USBDEV_SIZE 0x4000 ++#define RT3883_PCI_SIZE 0x40000 ++#define RT3883_WLAN_SIZE 0x40000 ++#define RT3883_USBHOST_SIZE 0x40000 ++#define RT3883_BOOT_SIZE (32 * 1024 * 1024) ++#define RT3883_SRAM_SIZE (32 * 1024 * 1024) ++ ++/* SYSC registers */ ++#define RT3883_SYSC_REG_CHIPID0_3 0x00 /* Chip ID 0 */ ++#define RT3883_SYSC_REG_CHIPID4_7 0x04 /* Chip ID 1 */ ++#define RT3883_SYSC_REG_REVID 0x0c /* Chip Revision Identification */ ++#define RT3883_SYSC_REG_SYSCFG0 0x10 /* System Configuration 0 */ ++#define RT3883_SYSC_REG_SYSCFG1 0x14 /* System Configuration 1 */ ++#define RT3883_SYSC_REG_CLKCFG0 0x2c /* Clock Configuration 0 */ ++#define RT3883_SYSC_REG_CLKCFG1 0x30 /* Clock Configuration 1 */ ++#define RT3883_SYSC_REG_RSTCTRL 0x34 /* Reset Control*/ ++#define RT3883_SYSC_REG_RSTSTAT 0x38 /* Reset Status*/ ++#define RT3883_SYSC_REG_USB_PS 0x5c /* USB Power saving control */ ++#define RT3883_SYSC_REG_GPIO_MODE 0x60 /* GPIO Purpose Select */ ++#define RT3883_SYSC_REG_PCIE_CLK_GEN0 0x7c ++#define RT3883_SYSC_REG_PCIE_CLK_GEN1 0x80 ++#define RT3883_SYSC_REG_PCIE_CLK_GEN2 0x84 ++#define RT3883_SYSC_REG_PMU 0x88 ++#define RT3883_SYSC_REG_PMU1 0x8c ++ ++#define RT3883_CHIP_NAME0 0x38335452 ++#define RT3883_CHIP_NAME1 0x20203338 ++ ++#define RT3883_REVID_VER_ID_MASK 0x0f ++#define RT3883_REVID_VER_ID_SHIFT 8 ++#define RT3883_REVID_ECO_ID_MASK 0x0f ++ ++#define RT3883_SYSCFG0_DRAM_TYPE_DDR2 BIT(17) ++#define RT3883_SYSCFG0_CPUCLK_SHIFT 8 ++#define RT3883_SYSCFG0_CPUCLK_MASK 0x3 ++#define RT3883_SYSCFG0_CPUCLK_250 0x0 ++#define RT3883_SYSCFG0_CPUCLK_384 0x1 ++#define RT3883_SYSCFG0_CPUCLK_480 0x2 ++#define RT3883_SYSCFG0_CPUCLK_500 0x3 ++ ++#define RT3883_SYSCFG1_USB0_HOST_MODE BIT(10) ++#define RT3883_SYSCFG1_PCIE_RC_MODE BIT(8) ++#define RT3883_SYSCFG1_PCI_HOST_MODE BIT(7) ++#define RT3883_SYSCFG1_PCI_66M_MODE BIT(6) ++#define RT3883_SYSCFG1_GPIO2_AS_WDT_OUT BIT(2) ++ ++#define RT3883_CLKCFG1_PCIE_CLK_EN BIT(21) ++#define RT3883_CLKCFG1_UPHY1_CLK_EN BIT(20) ++#define RT3883_CLKCFG1_PCI_CLK_EN BIT(19) ++#define RT3883_CLKCFG1_UPHY0_CLK_EN BIT(18) ++ ++#define RT3883_GPIO_MODE_I2C BIT(0) ++#define RT3883_GPIO_MODE_SPI BIT(1) ++#define RT3883_GPIO_MODE_UART0_SHIFT 2 ++#define RT3883_GPIO_MODE_UART0_MASK 0x7 ++#define RT3883_GPIO_MODE_UART0(x) ((x) << RT3883_GPIO_MODE_UART0_SHIFT) ++#define RT3883_GPIO_MODE_UARTF 0x0 ++#define RT3883_GPIO_MODE_PCM_UARTF 0x1 ++#define RT3883_GPIO_MODE_PCM_I2S 0x2 ++#define RT3883_GPIO_MODE_I2S_UARTF 0x3 ++#define RT3883_GPIO_MODE_PCM_GPIO 0x4 ++#define RT3883_GPIO_MODE_GPIO_UARTF 0x5 ++#define RT3883_GPIO_MODE_GPIO_I2S 0x6 ++#define RT3883_GPIO_MODE_GPIO 0x7 ++#define RT3883_GPIO_MODE_UART1 BIT(5) ++#define RT3883_GPIO_MODE_JTAG BIT(6) ++#define RT3883_GPIO_MODE_MDIO BIT(7) ++#define RT3883_GPIO_MODE_GE1 BIT(9) ++#define RT3883_GPIO_MODE_GE2 BIT(10) ++#define RT3883_GPIO_MODE_PCI_SHIFT 11 ++#define RT3883_GPIO_MODE_PCI_MASK 0x7 ++#define RT3883_GPIO_MODE_PCI (RT3883_GPIO_MODE_PCI_MASK << RT3883_GPIO_MODE_PCI_SHIFT) ++#define RT3883_GPIO_MODE_LNA_A_SHIFT 16 ++#define RT3883_GPIO_MODE_LNA_A_MASK 0x3 ++#define _RT3883_GPIO_MODE_LNA_A(_x) ((_x) << RT3883_GPIO_MODE_LNA_A_SHIFT) ++#define RT3883_GPIO_MODE_LNA_A_GPIO 0x3 ++#define RT3883_GPIO_MODE_LNA_A _RT3883_GPIO_MODE_LNA_A(RT3883_GPIO_MODE_LNA_A_MASK) ++#define RT3883_GPIO_MODE_LNA_G_SHIFT 18 ++#define RT3883_GPIO_MODE_LNA_G_MASK 0x3 ++#define _RT3883_GPIO_MODE_LNA_G(_x) ((_x) << RT3883_GPIO_MODE_LNA_G_SHIFT) ++#define RT3883_GPIO_MODE_LNA_G_GPIO 0x3 ++#define RT3883_GPIO_MODE_LNA_G _RT3883_GPIO_MODE_LNA_G(RT3883_GPIO_MODE_LNA_G_MASK) ++ ++#define RT3883_GPIO_I2C_SD 1 ++#define RT3883_GPIO_I2C_SCLK 2 ++#define RT3883_GPIO_SPI_CS0 3 ++#define RT3883_GPIO_SPI_CLK 4 ++#define RT3883_GPIO_SPI_MOSI 5 ++#define RT3883_GPIO_SPI_MISO 6 ++#define RT3883_GPIO_7 7 ++#define RT3883_GPIO_10 10 ++#define RT3883_GPIO_14 14 ++#define RT3883_GPIO_UART1_TXD 15 ++#define RT3883_GPIO_UART1_RXD 16 ++#define RT3883_GPIO_JTAG_TDO 17 ++#define RT3883_GPIO_JTAG_TDI 18 ++#define RT3883_GPIO_JTAG_TMS 19 ++#define RT3883_GPIO_JTAG_TCLK 20 ++#define RT3883_GPIO_JTAG_TRST_N 21 ++#define RT3883_GPIO_MDIO_MDC 22 ++#define RT3883_GPIO_MDIO_MDIO 23 ++#define RT3883_GPIO_LNA_PE_A0 32 ++#define RT3883_GPIO_LNA_PE_A1 33 ++#define RT3883_GPIO_LNA_PE_A2 34 ++#define RT3883_GPIO_LNA_PE_G0 35 ++#define RT3883_GPIO_LNA_PE_G1 36 ++#define RT3883_GPIO_LNA_PE_G2 37 ++#define RT3883_GPIO_PCI_AD0 40 ++#define RT3883_GPIO_PCI_AD31 71 ++#define RT3883_GPIO_GE2_TXD0 72 ++#define RT3883_GPIO_GE2_TXD1 73 ++#define RT3883_GPIO_GE2_TXD2 74 ++#define RT3883_GPIO_GE2_TXD3 75 ++#define RT3883_GPIO_GE2_TXEN 76 ++#define RT3883_GPIO_GE2_TXCLK 77 ++#define RT3883_GPIO_GE2_RXD0 78 ++#define RT3883_GPIO_GE2_RXD1 79 ++#define RT3883_GPIO_GE2_RXD2 80 ++#define RT3883_GPIO_GE2_RXD3 81 ++#define RT3883_GPIO_GE2_RXDV 82 ++#define RT3883_GPIO_GE2_RXCLK 83 ++#define RT3883_GPIO_GE1_TXD0 84 ++#define RT3883_GPIO_GE1_TXD1 85 ++#define RT3883_GPIO_GE1_TXD2 86 ++#define RT3883_GPIO_GE1_TXD3 87 ++#define RT3883_GPIO_GE1_TXEN 88 ++#define RT3883_GPIO_GE1_TXCLK 89 ++#define RT3883_GPIO_GE1_RXD0 90 ++#define RT3883_GPIO_GE1_RXD1 91 ++#define RT3883_GPIO_GE1_RXD2 92 ++#define RT3883_GPIO_GE1_RXD3 93 ++#define RT3883_GPIO_GE1_RXDV 94 ++#define RT3883_GPIO_GE1_RXCLK 95 ++ ++#define RT3883_RSTCTRL_PCIE_PCI_PDM BIT(27) ++#define RT3883_RSTCTRL_FLASH BIT(26) ++#define RT3883_RSTCTRL_UDEV BIT(25) ++#define RT3883_RSTCTRL_PCI BIT(24) ++#define RT3883_RSTCTRL_PCIE BIT(23) ++#define RT3883_RSTCTRL_UHST BIT(22) ++#define RT3883_RSTCTRL_FE BIT(21) ++#define RT3883_RSTCTRL_WLAN BIT(20) ++#define RT3883_RSTCTRL_UART1 BIT(29) ++#define RT3883_RSTCTRL_SPI BIT(18) ++#define RT3883_RSTCTRL_I2S BIT(17) ++#define RT3883_RSTCTRL_I2C BIT(16) ++#define RT3883_RSTCTRL_NAND BIT(15) ++#define RT3883_RSTCTRL_DMA BIT(14) ++#define RT3883_RSTCTRL_PIO BIT(13) ++#define RT3883_RSTCTRL_UART BIT(12) ++#define RT3883_RSTCTRL_PCM BIT(11) ++#define RT3883_RSTCTRL_MC BIT(10) ++#define RT3883_RSTCTRL_INTC BIT(9) ++#define RT3883_RSTCTRL_TIMER BIT(8) ++#define RT3883_RSTCTRL_SYS BIT(0) ++ ++#define RT3883_INTC_INT_SYSCTL BIT(0) ++#define RT3883_INTC_INT_TIMER0 BIT(1) ++#define RT3883_INTC_INT_TIMER1 BIT(2) ++#define RT3883_INTC_INT_IA BIT(3) ++#define RT3883_INTC_INT_PCM BIT(4) ++#define RT3883_INTC_INT_UART0 BIT(5) ++#define RT3883_INTC_INT_PIO BIT(6) ++#define RT3883_INTC_INT_DMA BIT(7) ++#define RT3883_INTC_INT_NAND BIT(8) ++#define RT3883_INTC_INT_PERFC BIT(9) ++#define RT3883_INTC_INT_I2S BIT(10) ++#define RT3883_INTC_INT_UART1 BIT(12) ++#define RT3883_INTC_INT_UHST BIT(18) ++#define RT3883_INTC_INT_UDEV BIT(19) ++ ++/* FLASH/SRAM/Codec Controller registers */ ++#define RT3883_FSCC_REG_FLASH_CFG0 0x00 ++#define RT3883_FSCC_REG_FLASH_CFG1 0x04 ++#define RT3883_FSCC_REG_CODEC_CFG0 0x40 ++#define RT3883_FSCC_REG_CODEC_CFG1 0x44 ++ ++#define RT3883_FLASH_CFG_WIDTH_SHIFT 26 ++#define RT3883_FLASH_CFG_WIDTH_MASK 0x3 ++#define RT3883_FLASH_CFG_WIDTH_8BIT 0x0 ++#define RT3883_FLASH_CFG_WIDTH_16BIT 0x1 ++#define RT3883_FLASH_CFG_WIDTH_32BIT 0x2 ++ ++#endif /* _RT3883_REGS_H_ */ +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 6723b94..ce57d3e 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -15,6 +15,11 @@ choice + select USB_ARCH_HAS_OHCI + select USB_ARCH_HAS_EHCI + ++ config SOC_RT3883 ++ bool "RT3883" ++ select USB_ARCH_HAS_OHCI ++ select USB_ARCH_HAS_EHCI ++ + endchoice + + choice +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index 6d826f2..ba9669c 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -10,6 +10,7 @@ obj-y := prom.o of.o reset.o clk.o irq.o + + obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o ++obj-$(CONFIG_SOC_RT3883) += rt3883.o + + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + +diff --git a/arch/mips/ralink/Platform b/arch/mips/ralink/Platform +index 3f49e51..f67c08d 100644 +--- a/arch/mips/ralink/Platform ++++ b/arch/mips/ralink/Platform +@@ -13,3 +13,8 @@ load-$(CONFIG_SOC_RT288X) += 0xffffffff88000000 + # Ralink RT305x + # + load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 ++ ++# ++# Ralink RT3883 ++# ++load-$(CONFIG_SOC_RT3883) += 0xffffffff80000000 +diff --git a/arch/mips/ralink/rt3883.c b/arch/mips/ralink/rt3883.c +new file mode 100644 +index 0000000..2d90aa9 +--- /dev/null ++++ b/arch/mips/ralink/rt3883.c +@@ -0,0 +1,242 @@ ++/* ++ * 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. ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * Copyright (C) 2008 Imre Kaloz ++ * Copyright (C) 2008-2011 Gabor Juhos ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "common.h" ++ ++static struct ralink_pinmux_grp mode_mux[] = { ++ { ++ .name = "i2c", ++ .mask = RT3883_GPIO_MODE_I2C, ++ .gpio_first = RT3883_GPIO_I2C_SD, ++ .gpio_last = RT3883_GPIO_I2C_SCLK, ++ }, { ++ .name = "spi", ++ .mask = RT3883_GPIO_MODE_SPI, ++ .gpio_first = RT3883_GPIO_SPI_CS0, ++ .gpio_last = RT3883_GPIO_SPI_MISO, ++ }, { ++ .name = "uartlite", ++ .mask = RT3883_GPIO_MODE_UART1, ++ .gpio_first = RT3883_GPIO_UART1_TXD, ++ .gpio_last = RT3883_GPIO_UART1_RXD, ++ }, { ++ .name = "jtag", ++ .mask = RT3883_GPIO_MODE_JTAG, ++ .gpio_first = RT3883_GPIO_JTAG_TDO, ++ .gpio_last = RT3883_GPIO_JTAG_TCLK, ++ }, { ++ .name = "mdio", ++ .mask = RT3883_GPIO_MODE_MDIO, ++ .gpio_first = RT3883_GPIO_MDIO_MDC, ++ .gpio_last = RT3883_GPIO_MDIO_MDIO, ++ }, { ++ .name = "ge1", ++ .mask = RT3883_GPIO_MODE_GE1, ++ .gpio_first = RT3883_GPIO_GE1_TXD0, ++ .gpio_last = RT3883_GPIO_GE1_RXCLK, ++ }, { ++ .name = "ge2", ++ .mask = RT3883_GPIO_MODE_GE2, ++ .gpio_first = RT3883_GPIO_GE2_TXD0, ++ .gpio_last = RT3883_GPIO_GE2_RXCLK, ++ }, { ++ .name = "pci", ++ .mask = RT3883_GPIO_MODE_PCI, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, { ++ .name = "lna a", ++ .mask = RT3883_GPIO_MODE_LNA_A, ++ .gpio_first = RT3883_GPIO_LNA_PE_A0, ++ .gpio_last = RT3883_GPIO_LNA_PE_A2, ++ }, { ++ .name = "lna g", ++ .mask = RT3883_GPIO_MODE_LNA_G, ++ .gpio_first = RT3883_GPIO_LNA_PE_G0, ++ .gpio_last = RT3883_GPIO_LNA_PE_G2, ++ }, {0} ++}; ++ ++static struct ralink_pinmux_grp uart_mux[] = { ++ { ++ .name = "uartf", ++ .mask = RT3883_GPIO_MODE_UARTF, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_14, ++ }, { ++ .name = "pcm uartf", ++ .mask = RT3883_GPIO_MODE_PCM_UARTF, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_14, ++ }, { ++ .name = "pcm i2s", ++ .mask = RT3883_GPIO_MODE_PCM_I2S, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_14, ++ }, { ++ .name = "i2s uartf", ++ .mask = RT3883_GPIO_MODE_I2S_UARTF, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_14, ++ }, { ++ .name = "pcm gpio", ++ .mask = RT3883_GPIO_MODE_PCM_GPIO, ++ .gpio_first = RT3883_GPIO_11, ++ .gpio_last = RT3883_GPIO_14, ++ }, { ++ .name = "gpio uartf", ++ .mask = RT3883_GPIO_MODE_GPIO_UARTF, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_10, ++ }, { ++ .name = "gpio i2s", ++ .mask = RT3883_GPIO_MODE_GPIO_I2S, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_10, ++ }, { ++ .name = "gpio", ++ .mask = RT3883_GPIO_MODE_GPIO, ++ }, {0} ++}; ++ ++static struct ralink_pinmux_grp pci_mux[] = { ++ { ++ .name = "pci-dev", ++ .mask = 0, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, { ++ .name = "pci-host2", ++ .mask = 1, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, { ++ .name = "pci-host1", ++ .mask = 2, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, { ++ .name = "pci-fnc", ++ .mask = 3, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, { ++ .name = "pci-gpio", ++ .mask = 7, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, {0} ++}; ++ ++static void rt3883_wdt_reset(void) ++{ ++ u32 t; ++ ++ /* enable WDT reset output on GPIO 2 */ ++ t = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG1); ++ t |= RT3883_SYSCFG1_GPIO2_AS_WDT_OUT; ++ rt_sysc_w32(t, RT3883_SYSC_REG_SYSCFG1); ++} ++ ++struct ralink_pinmux rt_gpio_pinmux = { ++ .mode = mode_mux, ++ .uart = uart_mux, ++ .uart_shift = RT3883_GPIO_MODE_UART0_SHIFT, ++ .uart_mask = RT3883_GPIO_MODE_GPIO, ++ .wdt_reset = rt3883_wdt_reset, ++ .pci = pci_mux, ++ .pci_shift = RT3883_GPIO_MODE_PCI_SHIFT, ++ .pci_mask = RT3883_GPIO_MODE_PCI_MASK, ++}; ++ ++void __init ralink_clk_init(void) ++{ ++ unsigned long cpu_rate, sys_rate; ++ u32 syscfg0; ++ u32 clksel; ++ u32 ddr2; ++ ++ syscfg0 = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG0); ++ clksel = ((syscfg0 >> RT3883_SYSCFG0_CPUCLK_SHIFT) & ++ RT3883_SYSCFG0_CPUCLK_MASK); ++ ddr2 = syscfg0 & RT3883_SYSCFG0_DRAM_TYPE_DDR2; ++ ++ switch (clksel) { ++ case RT3883_SYSCFG0_CPUCLK_250: ++ cpu_rate = 250000000; ++ sys_rate = (ddr2) ? 125000000 : 83000000; ++ break; ++ case RT3883_SYSCFG0_CPUCLK_384: ++ cpu_rate = 384000000; ++ sys_rate = (ddr2) ? 128000000 : 96000000; ++ break; ++ case RT3883_SYSCFG0_CPUCLK_480: ++ cpu_rate = 480000000; ++ sys_rate = (ddr2) ? 160000000 : 120000000; ++ break; ++ case RT3883_SYSCFG0_CPUCLK_500: ++ cpu_rate = 500000000; ++ sys_rate = (ddr2) ? 166000000 : 125000000; ++ break; ++ } ++ ++ ralink_clk_add("cpu", cpu_rate); ++ ralink_clk_add("10000100.timer", sys_rate); ++ ralink_clk_add("10000120.watchdog", sys_rate); ++ ralink_clk_add("10000500.uart", 40000000); ++ ralink_clk_add("10000b00.spi", sys_rate); ++ ralink_clk_add("10000c00.uartlite", 40000000); ++ ralink_clk_add("10100000.ethernet", sys_rate); ++} ++ ++void __init ralink_of_remap(void) ++{ ++ rt_sysc_membase = plat_of_remap_node("ralink,rt3883-sysc"); ++ rt_memc_membase = plat_of_remap_node("ralink,rt3883-memc"); ++ ++ if (!rt_sysc_membase || !rt_memc_membase) ++ panic("Failed to remap core resources"); ++} ++ ++void prom_soc_init(struct ralink_soc_info *soc_info) ++{ ++ void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT3883_SYSC_BASE); ++ const char *name; ++ u32 n0; ++ u32 n1; ++ u32 id; ++ ++ n0 = __raw_readl(sysc + RT3883_SYSC_REG_CHIPID0_3); ++ n1 = __raw_readl(sysc + RT3883_SYSC_REG_CHIPID4_7); ++ id = __raw_readl(sysc + RT3883_SYSC_REG_REVID); ++ ++ if (n0 == RT3883_CHIP_NAME0 && n1 == RT3883_CHIP_NAME1) { ++ soc_info->compatible = "ralink,rt3883-soc"; ++ name = "RT3883"; ++ } else { ++ panic("rt3883: unknown SoC, n0:%08x n1:%08x", n0, n1); ++ } ++ ++ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN, ++ "Ralink %s ver:%u eco:%u", ++ name, ++ (id >> RT3883_REVID_VER_ID_SHIFT) & RT3883_REVID_VER_ID_MASK, ++ (id & RT3883_REVID_ECO_ID_MASK)); ++} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0029-MIPS-ralink-adds-support-for-MT7620-SoC-family.patch b/target/linux/ramips/patches-3.8/0029-MIPS-ralink-adds-support-for-MT7620-SoC-family.patch new file mode 100644 index 0000000000..f03c10f06c --- /dev/null +++ b/target/linux/ramips/patches-3.8/0029-MIPS-ralink-adds-support-for-MT7620-SoC-family.patch @@ -0,0 +1,366 @@ +From 41b7b06b494eef5a081363566314960306437d73 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 21 Mar 2013 17:49:02 +0100 +Subject: [PATCH 29/79] MIPS: ralink: adds support for MT7620 SoC family + +Add support code for mt7620 SOC. + +The code detects the SoC and registers the clk / pinmux settings. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5177/ +--- + arch/mips/include/asm/mach-ralink/mt7620.h | 76 ++++++++++ + arch/mips/ralink/Kconfig | 3 + + arch/mips/ralink/Makefile | 1 + + arch/mips/ralink/Platform | 5 + + arch/mips/ralink/mt7620.c | 214 ++++++++++++++++++++++++++++ + 5 files changed, 299 insertions(+) + create mode 100644 arch/mips/include/asm/mach-ralink/mt7620.h + create mode 100644 arch/mips/ralink/mt7620.c + +diff --git a/arch/mips/include/asm/mach-ralink/mt7620.h b/arch/mips/include/asm/mach-ralink/mt7620.h +new file mode 100644 +index 0000000..b272649 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/mt7620.h +@@ -0,0 +1,76 @@ ++/* ++ * 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. ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * Copyright (C) 2008-2011 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#ifndef _MT7620_REGS_H_ ++#define _MT7620_REGS_H_ ++ ++#define MT7620_SYSC_BASE 0x10000000 ++ ++#define SYSC_REG_CHIP_NAME0 0x00 ++#define SYSC_REG_CHIP_NAME1 0x04 ++#define SYSC_REG_CHIP_REV 0x0c ++#define SYSC_REG_SYSTEM_CONFIG0 0x10 ++#define SYSC_REG_SYSTEM_CONFIG1 0x14 ++#define SYSC_REG_CPLL_CONFIG0 0x54 ++#define SYSC_REG_CPLL_CONFIG1 0x58 ++ ++#define MT7620N_CHIP_NAME0 0x33365452 ++#define MT7620N_CHIP_NAME1 0x20203235 ++ ++#define MT7620A_CHIP_NAME0 0x3637544d ++#define MT7620A_CHIP_NAME1 0x20203032 ++ ++#define CHIP_REV_PKG_MASK 0x1 ++#define CHIP_REV_PKG_SHIFT 16 ++#define CHIP_REV_VER_MASK 0xf ++#define CHIP_REV_VER_SHIFT 8 ++#define CHIP_REV_ECO_MASK 0xf ++ ++#define CPLL_SW_CONFIG_SHIFT 31 ++#define CPLL_SW_CONFIG_MASK 0x1 ++#define CPLL_CPU_CLK_SHIFT 24 ++#define CPLL_CPU_CLK_MASK 0x1 ++#define CPLL_MULT_RATIO_SHIFT 16 ++#define CPLL_MULT_RATIO 0x7 ++#define CPLL_DIV_RATIO_SHIFT 10 ++#define CPLL_DIV_RATIO 0x3 ++ ++#define SYSCFG0_DRAM_TYPE_MASK 0x3 ++#define SYSCFG0_DRAM_TYPE_SHIFT 4 ++#define SYSCFG0_DRAM_TYPE_SDRAM 0 ++#define SYSCFG0_DRAM_TYPE_DDR1 1 ++#define SYSCFG0_DRAM_TYPE_DDR2 2 ++ ++#define MT7620_GPIO_MODE_I2C BIT(0) ++#define MT7620_GPIO_MODE_UART0_SHIFT 2 ++#define MT7620_GPIO_MODE_UART0_MASK 0x7 ++#define MT7620_GPIO_MODE_UART0(x) ((x) << MT7620_GPIO_MODE_UART0_SHIFT) ++#define MT7620_GPIO_MODE_UARTF 0x0 ++#define MT7620_GPIO_MODE_PCM_UARTF 0x1 ++#define MT7620_GPIO_MODE_PCM_I2S 0x2 ++#define MT7620_GPIO_MODE_I2S_UARTF 0x3 ++#define MT7620_GPIO_MODE_PCM_GPIO 0x4 ++#define MT7620_GPIO_MODE_GPIO_UARTF 0x5 ++#define MT7620_GPIO_MODE_GPIO_I2S 0x6 ++#define MT7620_GPIO_MODE_GPIO 0x7 ++#define MT7620_GPIO_MODE_UART1 BIT(5) ++#define MT7620_GPIO_MODE_MDIO BIT(8) ++#define MT7620_GPIO_MODE_RGMII1 BIT(9) ++#define MT7620_GPIO_MODE_RGMII2 BIT(10) ++#define MT7620_GPIO_MODE_SPI BIT(11) ++#define MT7620_GPIO_MODE_SPI_REF_CLK BIT(12) ++#define MT7620_GPIO_MODE_WLED BIT(13) ++#define MT7620_GPIO_MODE_JTAG BIT(15) ++#define MT7620_GPIO_MODE_EPHY BIT(15) ++#define MT7620_GPIO_MODE_WDT BIT(22) ++ ++#endif +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index ce57d3e..86f6c77 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -20,6 +20,9 @@ choice + select USB_ARCH_HAS_OHCI + select USB_ARCH_HAS_EHCI + ++ config SOC_MT7620 ++ bool "MT7620" ++ + endchoice + + choice +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index ba9669c..38cf1a8 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -11,6 +11,7 @@ obj-y := prom.o of.o reset.o clk.o irq.o + obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o + obj-$(CONFIG_SOC_RT3883) += rt3883.o ++obj-$(CONFIG_SOC_MT7620) += mt7620.o + + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + +diff --git a/arch/mips/ralink/Platform b/arch/mips/ralink/Platform +index f67c08d..b2cbf16 100644 +--- a/arch/mips/ralink/Platform ++++ b/arch/mips/ralink/Platform +@@ -18,3 +18,8 @@ load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 + # Ralink RT3883 + # + load-$(CONFIG_SOC_RT3883) += 0xffffffff80000000 ++ ++# ++# Ralink MT7620 ++# ++load-$(CONFIG_SOC_MT7620) += 0xffffffff80000000 +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +new file mode 100644 +index 0000000..eb00ab8 +--- /dev/null ++++ b/arch/mips/ralink/mt7620.c +@@ -0,0 +1,214 @@ ++/* ++ * 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. ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * Copyright (C) 2008-2011 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "common.h" ++ ++/* does the board have sdram or ddram */ ++static int dram_type; ++ ++/* the pll dividers */ ++static u32 mt7620_clk_divider[] = { 2, 3, 4, 8 }; ++ ++static struct ralink_pinmux_grp mode_mux[] = { ++ { ++ .name = "i2c", ++ .mask = MT7620_GPIO_MODE_I2C, ++ .gpio_first = 1, ++ .gpio_last = 2, ++ }, { ++ .name = "spi", ++ .mask = MT7620_GPIO_MODE_SPI, ++ .gpio_first = 3, ++ .gpio_last = 6, ++ }, { ++ .name = "uartlite", ++ .mask = MT7620_GPIO_MODE_UART1, ++ .gpio_first = 15, ++ .gpio_last = 16, ++ }, { ++ .name = "wdt", ++ .mask = MT7620_GPIO_MODE_WDT, ++ .gpio_first = 17, ++ .gpio_last = 17, ++ }, { ++ .name = "mdio", ++ .mask = MT7620_GPIO_MODE_MDIO, ++ .gpio_first = 22, ++ .gpio_last = 23, ++ }, { ++ .name = "rgmii1", ++ .mask = MT7620_GPIO_MODE_RGMII1, ++ .gpio_first = 24, ++ .gpio_last = 35, ++ }, { ++ .name = "spi refclk", ++ .mask = MT7620_GPIO_MODE_SPI_REF_CLK, ++ .gpio_first = 37, ++ .gpio_last = 39, ++ }, { ++ .name = "jtag", ++ .mask = MT7620_GPIO_MODE_JTAG, ++ .gpio_first = 40, ++ .gpio_last = 44, ++ }, { ++ /* shared lines with jtag */ ++ .name = "ephy", ++ .mask = MT7620_GPIO_MODE_EPHY, ++ .gpio_first = 40, ++ .gpio_last = 44, ++ }, { ++ .name = "nand", ++ .mask = MT7620_GPIO_MODE_JTAG, ++ .gpio_first = 45, ++ .gpio_last = 59, ++ }, { ++ .name = "rgmii2", ++ .mask = MT7620_GPIO_MODE_RGMII2, ++ .gpio_first = 60, ++ .gpio_last = 71, ++ }, { ++ .name = "wled", ++ .mask = MT7620_GPIO_MODE_WLED, ++ .gpio_first = 72, ++ .gpio_last = 72, ++ }, {0} ++}; ++ ++static struct ralink_pinmux_grp uart_mux[] = { ++ { ++ .name = "uartf", ++ .mask = MT7620_GPIO_MODE_UARTF, ++ .gpio_first = 7, ++ .gpio_last = 14, ++ }, { ++ .name = "pcm uartf", ++ .mask = MT7620_GPIO_MODE_PCM_UARTF, ++ .gpio_first = 7, ++ .gpio_last = 14, ++ }, { ++ .name = "pcm i2s", ++ .mask = MT7620_GPIO_MODE_PCM_I2S, ++ .gpio_first = 7, ++ .gpio_last = 14, ++ }, { ++ .name = "i2s uartf", ++ .mask = MT7620_GPIO_MODE_I2S_UARTF, ++ .gpio_first = 7, ++ .gpio_last = 14, ++ }, { ++ .name = "pcm gpio", ++ .mask = MT7620_GPIO_MODE_PCM_GPIO, ++ .gpio_first = 11, ++ .gpio_last = 14, ++ }, { ++ .name = "gpio uartf", ++ .mask = MT7620_GPIO_MODE_GPIO_UARTF, ++ .gpio_first = 7, ++ .gpio_last = 10, ++ }, { ++ .name = "gpio i2s", ++ .mask = MT7620_GPIO_MODE_GPIO_I2S, ++ .gpio_first = 7, ++ .gpio_last = 10, ++ }, { ++ .name = "gpio", ++ .mask = MT7620_GPIO_MODE_GPIO, ++ }, {0} ++}; ++ ++struct ralink_pinmux rt_gpio_pinmux = { ++ .mode = mode_mux, ++ .uart = uart_mux, ++ .uart_shift = MT7620_GPIO_MODE_UART0_SHIFT, ++ .uart_mask = MT7620_GPIO_MODE_GPIO, ++}; ++ ++void __init ralink_clk_init(void) ++{ ++ unsigned long cpu_rate, sys_rate; ++ u32 c0 = rt_sysc_r32(SYSC_REG_CPLL_CONFIG0); ++ u32 c1 = rt_sysc_r32(SYSC_REG_CPLL_CONFIG1); ++ u32 swconfig = (c0 >> CPLL_SW_CONFIG_SHIFT) & CPLL_SW_CONFIG_MASK; ++ u32 cpu_clk = (c1 >> CPLL_CPU_CLK_SHIFT) & CPLL_CPU_CLK_MASK; ++ ++ if (cpu_clk) { ++ cpu_rate = 480000000; ++ } else if (!swconfig) { ++ cpu_rate = 600000000; ++ } else { ++ u32 m = (c0 >> CPLL_MULT_RATIO_SHIFT) & CPLL_MULT_RATIO; ++ u32 d = (c0 >> CPLL_DIV_RATIO_SHIFT) & CPLL_DIV_RATIO; ++ ++ cpu_rate = ((40 * (m + 24)) / mt7620_clk_divider[d]) * 1000000; ++ } ++ ++ if (dram_type == SYSCFG0_DRAM_TYPE_SDRAM) ++ sys_rate = cpu_rate / 4; ++ else ++ sys_rate = cpu_rate / 3; ++ ++ ralink_clk_add("cpu", cpu_rate); ++ ralink_clk_add("10000100.timer", 40000000); ++ ralink_clk_add("10000500.uart", 40000000); ++ ralink_clk_add("10000c00.uartlite", 40000000); ++} ++ ++void __init ralink_of_remap(void) ++{ ++ rt_sysc_membase = plat_of_remap_node("ralink,mt7620a-sysc"); ++ rt_memc_membase = plat_of_remap_node("ralink,mt7620a-memc"); ++ ++ if (!rt_sysc_membase || !rt_memc_membase) ++ panic("Failed to remap core resources"); ++} ++ ++void prom_soc_init(struct ralink_soc_info *soc_info) ++{ ++ void __iomem *sysc = (void __iomem *) KSEG1ADDR(MT7620_SYSC_BASE); ++ unsigned char *name = NULL; ++ u32 n0; ++ u32 n1; ++ u32 rev; ++ u32 cfg0; ++ ++ n0 = __raw_readl(sysc + SYSC_REG_CHIP_NAME0); ++ n1 = __raw_readl(sysc + SYSC_REG_CHIP_NAME1); ++ ++ if (n0 == MT7620N_CHIP_NAME0 && n1 == MT7620N_CHIP_NAME1) { ++ name = "MT7620N"; ++ soc_info->compatible = "ralink,mt7620n-soc"; ++ } else if (n0 == MT7620A_CHIP_NAME0 && n1 == MT7620A_CHIP_NAME1) { ++ name = "MT7620A"; ++ soc_info->compatible = "ralink,mt7620a-soc"; ++ } else { ++ panic("mt7620: unknown SoC, n0:%08x n1:%08x\n", n0, n1); ++ } ++ ++ rev = __raw_readl(sysc + SYSC_REG_CHIP_REV); ++ ++ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN, ++ "Ralink %s ver:%u eco:%u", ++ name, ++ (rev >> CHIP_REV_VER_SHIFT) & CHIP_REV_VER_MASK, ++ (rev & CHIP_REV_ECO_MASK)); ++ ++ cfg0 = __raw_readl(sysc + SYSC_REG_SYSTEM_CONFIG0); ++ dram_type = (cfg0 >> SYSCFG0_DRAM_TYPE_SHIFT) & SYSCFG0_DRAM_TYPE_MASK; ++} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0030-MIPS-ralink-add-cpu-feature-overrides.h.patch b/target/linux/ramips/patches-3.8/0030-MIPS-ralink-add-cpu-feature-overrides.h.patch new file mode 100644 index 0000000000..21205a3892 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0030-MIPS-ralink-add-cpu-feature-overrides.h.patch @@ -0,0 +1,232 @@ +From 8a7cac5e324f044f3970d686d79e3489260f6d21 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Wed, 10 Apr 2013 09:19:07 +0200 +Subject: [PATCH 30/79] MIPS: ralink: add cpu-feature-overrides.h + +Add cpu-feature-overrides.h for RT288x, RT305x and RT3883. + +Signed-off-by: Gabor Juhos +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5175/ +--- + .../asm/mach-ralink/rt288x/cpu-feature-overrides.h | 56 ++++++++++++++++++++ + .../asm/mach-ralink/rt305x/cpu-feature-overrides.h | 56 ++++++++++++++++++++ + .../asm/mach-ralink/rt3883/cpu-feature-overrides.h | 55 +++++++++++++++++++ + arch/mips/ralink/Platform | 3 ++ + 4 files changed, 170 insertions(+) + create mode 100644 arch/mips/include/asm/mach-ralink/rt288x/cpu-feature-overrides.h + create mode 100644 arch/mips/include/asm/mach-ralink/rt305x/cpu-feature-overrides.h + create mode 100644 arch/mips/include/asm/mach-ralink/rt3883/cpu-feature-overrides.h + +diff --git a/arch/mips/include/asm/mach-ralink/rt288x/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ralink/rt288x/cpu-feature-overrides.h +new file mode 100644 +index 0000000..72fc106 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt288x/cpu-feature-overrides.h +@@ -0,0 +1,56 @@ ++/* ++ * Ralink RT288x specific CPU feature overrides ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * This file was derived from: include/asm-mips/cpu-features.h ++ * Copyright (C) 2003, 2004 Ralf Baechle ++ * Copyright (C) 2004 Maciej W. Rozycki ++ * ++ * 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 _RT288X_CPU_FEATURE_OVERRIDES_H ++#define _RT288X_CPU_FEATURE_OVERRIDES_H ++ ++#define cpu_has_tlb 1 ++#define cpu_has_4kex 1 ++#define cpu_has_3k_cache 0 ++#define cpu_has_4k_cache 1 ++#define cpu_has_tx39_cache 0 ++#define cpu_has_sb1_cache 0 ++#define cpu_has_fpu 0 ++#define cpu_has_32fpr 0 ++#define cpu_has_counter 1 ++#define cpu_has_watch 1 ++#define cpu_has_divec 1 ++ ++#define cpu_has_prefetch 1 ++#define cpu_has_ejtag 1 ++#define cpu_has_llsc 1 ++ ++#define cpu_has_mips16 1 ++#define cpu_has_mdmx 0 ++#define cpu_has_mips3d 0 ++#define cpu_has_smartmips 0 ++ ++#define cpu_has_mips32r1 1 ++#define cpu_has_mips32r2 1 ++#define cpu_has_mips64r1 0 ++#define cpu_has_mips64r2 0 ++ ++#define cpu_has_dsp 0 ++#define cpu_has_mipsmt 0 ++ ++#define cpu_has_64bits 0 ++#define cpu_has_64bit_zero_reg 0 ++#define cpu_has_64bit_gp_regs 0 ++#define cpu_has_64bit_addresses 0 ++ ++#define cpu_dcache_line_size() 16 ++#define cpu_icache_line_size() 16 ++ ++#endif /* _RT288X_CPU_FEATURE_OVERRIDES_H */ +diff --git a/arch/mips/include/asm/mach-ralink/rt305x/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ralink/rt305x/cpu-feature-overrides.h +new file mode 100644 +index 0000000..917c286 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt305x/cpu-feature-overrides.h +@@ -0,0 +1,56 @@ ++/* ++ * Ralink RT305x specific CPU feature overrides ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * This file was derived from: include/asm-mips/cpu-features.h ++ * Copyright (C) 2003, 2004 Ralf Baechle ++ * Copyright (C) 2004 Maciej W. Rozycki ++ * ++ * 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 _RT305X_CPU_FEATURE_OVERRIDES_H ++#define _RT305X_CPU_FEATURE_OVERRIDES_H ++ ++#define cpu_has_tlb 1 ++#define cpu_has_4kex 1 ++#define cpu_has_3k_cache 0 ++#define cpu_has_4k_cache 1 ++#define cpu_has_tx39_cache 0 ++#define cpu_has_sb1_cache 0 ++#define cpu_has_fpu 0 ++#define cpu_has_32fpr 0 ++#define cpu_has_counter 1 ++#define cpu_has_watch 1 ++#define cpu_has_divec 1 ++ ++#define cpu_has_prefetch 1 ++#define cpu_has_ejtag 1 ++#define cpu_has_llsc 1 ++ ++#define cpu_has_mips16 1 ++#define cpu_has_mdmx 0 ++#define cpu_has_mips3d 0 ++#define cpu_has_smartmips 0 ++ ++#define cpu_has_mips32r1 1 ++#define cpu_has_mips32r2 1 ++#define cpu_has_mips64r1 0 ++#define cpu_has_mips64r2 0 ++ ++#define cpu_has_dsp 1 ++#define cpu_has_mipsmt 0 ++ ++#define cpu_has_64bits 0 ++#define cpu_has_64bit_zero_reg 0 ++#define cpu_has_64bit_gp_regs 0 ++#define cpu_has_64bit_addresses 0 ++ ++#define cpu_dcache_line_size() 32 ++#define cpu_icache_line_size() 32 ++ ++#endif /* _RT305X_CPU_FEATURE_OVERRIDES_H */ +diff --git a/arch/mips/include/asm/mach-ralink/rt3883/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ralink/rt3883/cpu-feature-overrides.h +new file mode 100644 +index 0000000..181fbf4 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt3883/cpu-feature-overrides.h +@@ -0,0 +1,55 @@ ++/* ++ * Ralink RT3662/RT3883 specific CPU feature overrides ++ * ++ * Copyright (C) 2011-2013 Gabor Juhos ++ * ++ * This file was derived from: include/asm-mips/cpu-features.h ++ * Copyright (C) 2003, 2004 Ralf Baechle ++ * Copyright (C) 2004 Maciej W. Rozycki ++ * ++ * 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 _RT3883_CPU_FEATURE_OVERRIDES_H ++#define _RT3883_CPU_FEATURE_OVERRIDES_H ++ ++#define cpu_has_tlb 1 ++#define cpu_has_4kex 1 ++#define cpu_has_3k_cache 0 ++#define cpu_has_4k_cache 1 ++#define cpu_has_tx39_cache 0 ++#define cpu_has_sb1_cache 0 ++#define cpu_has_fpu 0 ++#define cpu_has_32fpr 0 ++#define cpu_has_counter 1 ++#define cpu_has_watch 1 ++#define cpu_has_divec 1 ++ ++#define cpu_has_prefetch 1 ++#define cpu_has_ejtag 1 ++#define cpu_has_llsc 1 ++ ++#define cpu_has_mips16 1 ++#define cpu_has_mdmx 0 ++#define cpu_has_mips3d 0 ++#define cpu_has_smartmips 0 ++ ++#define cpu_has_mips32r1 1 ++#define cpu_has_mips32r2 1 ++#define cpu_has_mips64r1 0 ++#define cpu_has_mips64r2 0 ++ ++#define cpu_has_dsp 1 ++#define cpu_has_mipsmt 0 ++ ++#define cpu_has_64bits 0 ++#define cpu_has_64bit_zero_reg 0 ++#define cpu_has_64bit_gp_regs 0 ++#define cpu_has_64bit_addresses 0 ++ ++#define cpu_dcache_line_size() 32 ++#define cpu_icache_line_size() 32 ++ ++#endif /* _RT3883_CPU_FEATURE_OVERRIDES_H */ +diff --git a/arch/mips/ralink/Platform b/arch/mips/ralink/Platform +index b2cbf16..cda4b66 100644 +--- a/arch/mips/ralink/Platform ++++ b/arch/mips/ralink/Platform +@@ -8,16 +8,19 @@ cflags-$(CONFIG_RALINK) += -I$(srctree)/arch/mips/include/asm/mach-ralink + # Ralink RT288x + # + load-$(CONFIG_SOC_RT288X) += 0xffffffff88000000 ++cflags-$(CONFIG_SOC_RT288X) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt288x + + # + # Ralink RT305x + # + load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 ++cflags-$(CONFIG_SOC_RT305X) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt305x + + # + # Ralink RT3883 + # + load-$(CONFIG_SOC_RT3883) += 0xffffffff80000000 ++cflags-$(CONFIG_SOC_RT3883) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt3883 + + # + # Ralink MT7620 +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0031-DT-add-vendor-prefixes-for-Ralink.patch b/target/linux/ramips/patches-3.8/0031-DT-add-vendor-prefixes-for-Ralink.patch new file mode 100644 index 0000000000..7a81af6192 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0031-DT-add-vendor-prefixes-for-Ralink.patch @@ -0,0 +1,26 @@ +From f13cb76f8ad8714eaf691ef24aebfb57f62dab66 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 10:11:51 +0200 +Subject: [PATCH 31/79] DT: add vendor prefixes for Ralink + +Signed-off-by: John Crispin +Acked-by: Grant Likely +--- + Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt +index 902b1b1..d1cc7bb 100644 +--- a/Documentation/devicetree/bindings/vendor-prefixes.txt ++++ b/Documentation/devicetree/bindings/vendor-prefixes.txt +@@ -40,6 +40,7 @@ onnn ON Semiconductor Corp. + picochip Picochip Ltd + powervr PowerVR (deprecated, use img) + qcom Qualcomm, Inc. ++ralink Mediatek/Ralink Technology Corp. + ramtron Ramtron International + realtek Realtek Semiconductor Corp. + samsung Samsung Semiconductor +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0032-DT-add-documentation-for-the-Ralink-MIPS-SoCs.patch b/target/linux/ramips/patches-3.8/0032-DT-add-documentation-for-the-Ralink-MIPS-SoCs.patch new file mode 100644 index 0000000000..1a66e63d86 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0032-DT-add-documentation-for-the-Ralink-MIPS-SoCs.patch @@ -0,0 +1,44 @@ +From 441d0189a47391c6882bbc6a11494e7cd394f1fc Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Sat, 13 Apr 2013 09:02:40 +0200 +Subject: [PATCH 32/79] DT: add documentation for the Ralink MIPS SoCs + +This patch adds binding documentation for the +compatible values of the Ralink MIPS SoCs. + +Signed-off-by: Gabor Juhos +Signed-off-by: John Crispin +Acked-by: Grant Likely +Patchwork: http://patchwork.linux-mips.org/patch/5187/ +--- + Documentation/devicetree/bindings/mips/ralink.txt | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mips/ralink.txt + +diff --git a/Documentation/devicetree/bindings/mips/ralink.txt b/Documentation/devicetree/bindings/mips/ralink.txt +new file mode 100644 +index 0000000..59b6a35 +--- /dev/null ++++ b/Documentation/devicetree/bindings/mips/ralink.txt +@@ -0,0 +1,18 @@ ++Ralink MIPS SoC device tree bindings ++ ++1. SoCs ++ ++Each device tree must specify a compatible value for the Ralink SoC ++it uses in the compatible property of the root node. The compatible ++value must be one of the following values: ++ ++ ralink,rt2880-soc ++ ralink,rt3050-soc ++ ralink,rt3052-soc ++ ralink,rt3350-soc ++ ralink,rt3352-soc ++ ralink,rt3883-soc ++ ralink,rt5350-soc ++ ralink,mt7620a-soc ++ ralink,mt7620n-soc ++ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0033-DT-MIPS-ralink-clean-up-RT3050-dtsi-and-dts-file.patch b/target/linux/ramips/patches-3.8/0033-DT-MIPS-ralink-clean-up-RT3050-dtsi-and-dts-file.patch new file mode 100644 index 0000000000..a3a674fd06 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0033-DT-MIPS-ralink-clean-up-RT3050-dtsi-and-dts-file.patch @@ -0,0 +1,139 @@ +From 29d1bb6fc97d4391e4ecf96298b6ac42d0daefca Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 20 Jun 2013 18:44:43 +0200 +Subject: [PATCH 33/79] DT: MIPS: ralink: clean up RT3050 dtsi and dts file + +* remove nodes for cores whose drivers are not upstream yet +* add compat string for an additional soc +* fix a whitespace error + +Signed-off-by: John Crispin +Acked-by: Grant Likely +Patchwork: http://patchwork.linux-mips.org/patch/5186/ +--- + arch/mips/ralink/dts/rt3050.dtsi | 52 ++-------------------------------- + arch/mips/ralink/dts/rt3052_eval.dts | 10 ++----- + 2 files changed, 4 insertions(+), 58 deletions(-) + +diff --git a/arch/mips/ralink/dts/rt3050.dtsi b/arch/mips/ralink/dts/rt3050.dtsi +index 069d066..ef7da1e 100644 +--- a/arch/mips/ralink/dts/rt3050.dtsi ++++ b/arch/mips/ralink/dts/rt3050.dtsi +@@ -1,7 +1,7 @@ + / { + #address-cells = <1>; + #size-cells = <1>; +- compatible = "ralink,rt3050-soc", "ralink,rt3052-soc"; ++ compatible = "ralink,rt3050-soc", "ralink,rt3052-soc", "ralink,rt3350-soc"; + + cpus { + cpu@0 { +@@ -9,10 +9,6 @@ + }; + }; + +- chosen { +- bootargs = "console=ttyS0,57600 init=/init"; +- }; +- + cpuintc: cpuintc@0 { + #address-cells = <0>; + #interrupt-cells = <1>; +@@ -23,7 +19,7 @@ + palmbus@10000000 { + compatible = "palmbus"; + reg = <0x10000000 0x200000>; +- ranges = <0x0 0x10000000 0x1FFFFF>; ++ ranges = <0x0 0x10000000 0x1FFFFF>; + + #address-cells = <1>; + #size-cells = <1>; +@@ -33,11 +29,6 @@ + reg = <0x0 0x100>; + }; + +- timer@100 { +- compatible = "ralink,rt3052-wdt", "ralink,rt2880-wdt"; +- reg = <0x100 0x100>; +- }; +- + intc: intc@200 { + compatible = "ralink,rt3052-intc", "ralink,rt2880-intc"; + reg = <0x200 0x100>; +@@ -54,45 +45,6 @@ + reg = <0x300 0x100>; + }; + +- gpio0: gpio@600 { +- compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; +- reg = <0x600 0x34>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- ralink,ngpio = <24>; +- ralink,regs = [ 00 04 08 0c +- 20 24 28 2c +- 30 34 ]; +- }; +- +- gpio1: gpio@638 { +- compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; +- reg = <0x638 0x24>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- ralink,ngpio = <16>; +- ralink,regs = [ 00 04 08 0c +- 10 14 18 1c +- 20 24 ]; +- }; +- +- gpio2: gpio@660 { +- compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; +- reg = <0x660 0x24>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- ralink,ngpio = <12>; +- ralink,regs = [ 00 04 08 0c +- 10 14 18 1c +- 20 24 ]; +- }; +- + uartlite@c00 { + compatible = "ralink,rt3052-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0xc00 0x100>; +diff --git a/arch/mips/ralink/dts/rt3052_eval.dts b/arch/mips/ralink/dts/rt3052_eval.dts +index dc56e58..df17f5f 100644 +--- a/arch/mips/ralink/dts/rt3052_eval.dts ++++ b/arch/mips/ralink/dts/rt3052_eval.dts +@@ -3,8 +3,6 @@ + /include/ "rt3050.dtsi" + + / { +- #address-cells = <1>; +- #size-cells = <1>; + compatible = "ralink,rt3052-eval-board", "ralink,rt3052-soc"; + model = "Ralink RT3052 evaluation board"; + +@@ -12,12 +10,8 @@ + reg = <0x0 0x2000000>; + }; + +- palmbus@10000000 { +- sysc@0 { +- ralink,pinmux = "uartlite", "spi"; +- ralink,uartmux = "gpio"; +- ralink,wdtmux = <0>; +- }; ++ chosen { ++ bootargs = "console=ttyS0,57600"; + }; + + cfi@1f000000 { +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0034-DT-MIPS-ralink-add-RT2880-dts-files.patch b/target/linux/ramips/patches-3.8/0034-DT-MIPS-ralink-add-RT2880-dts-files.patch new file mode 100644 index 0000000000..affa7719b9 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0034-DT-MIPS-ralink-add-RT2880-dts-files.patch @@ -0,0 +1,160 @@ +From b3cda181b5f9986b05bd95ee322504a8f2ed0b69 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 06:27:37 +0000 +Subject: [PATCH 34/79] DT: MIPS: ralink: add RT2880 dts files + +Add a dtsi file for RT2880 SoC and a sample dts file. + +Signed-off-by: John Crispin +Acked-by: Grant Likely +Patchwork: http://patchwork.linux-mips.org/patch/5188/ +--- + arch/mips/ralink/Kconfig | 4 +++ + arch/mips/ralink/dts/Makefile | 1 + + arch/mips/ralink/dts/rt2880.dtsi | 58 ++++++++++++++++++++++++++++++++++ + arch/mips/ralink/dts/rt2880_eval.dts | 46 +++++++++++++++++++++++++++ + 4 files changed, 109 insertions(+) + create mode 100644 arch/mips/ralink/dts/rt2880.dtsi + create mode 100644 arch/mips/ralink/dts/rt2880_eval.dts + +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 86f6c77..2f6fbb8 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -34,6 +34,10 @@ choice + config DTB_RT_NONE + bool "None" + ++ config DTB_RT2880_EVAL ++ bool "RT2880 eval kit" ++ depends on SOC_RT288X ++ + config DTB_RT305X_EVAL + bool "RT305x eval kit" + depends on SOC_RT305X +diff --git a/arch/mips/ralink/dts/Makefile b/arch/mips/ralink/dts/Makefile +index 1a69fb3..f635a01 100644 +--- a/arch/mips/ralink/dts/Makefile ++++ b/arch/mips/ralink/dts/Makefile +@@ -1 +1,2 @@ ++obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o + obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o +diff --git a/arch/mips/ralink/dts/rt2880.dtsi b/arch/mips/ralink/dts/rt2880.dtsi +new file mode 100644 +index 0000000..182afde +--- /dev/null ++++ b/arch/mips/ralink/dts/rt2880.dtsi +@@ -0,0 +1,58 @@ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "ralink,rt2880-soc"; ++ ++ cpus { ++ cpu@0 { ++ compatible = "mips,mips4KEc"; ++ }; ++ }; ++ ++ cpuintc: cpuintc@0 { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ compatible = "mti,cpu-interrupt-controller"; ++ }; ++ ++ palmbus@300000 { ++ compatible = "palmbus"; ++ reg = <0x300000 0x200000>; ++ ranges = <0x0 0x300000 0x1FFFFF>; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ sysc@0 { ++ compatible = "ralink,rt2880-sysc"; ++ reg = <0x0 0x100>; ++ }; ++ ++ intc: intc@200 { ++ compatible = "ralink,rt2880-intc"; ++ reg = <0x200 0x100>; ++ ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <2>; ++ }; ++ ++ memc@300 { ++ compatible = "ralink,rt2880-memc"; ++ reg = <0x300 0x100>; ++ }; ++ ++ uartlite@c00 { ++ compatible = "ralink,rt2880-uart", "ns16550a"; ++ reg = <0xc00 0x100>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <8>; ++ ++ reg-shift = <2>; ++ }; ++ }; ++}; +diff --git a/arch/mips/ralink/dts/rt2880_eval.dts b/arch/mips/ralink/dts/rt2880_eval.dts +new file mode 100644 +index 0000000..322d700 +--- /dev/null ++++ b/arch/mips/ralink/dts/rt2880_eval.dts +@@ -0,0 +1,46 @@ ++/dts-v1/; ++ ++/include/ "rt2880.dtsi" ++ ++/ { ++ compatible = "ralink,rt2880-eval-board", "ralink,rt2880-soc"; ++ model = "Ralink RT2880 evaluation board"; ++ ++ memory@0 { ++ reg = <0x8000000 0x2000000>; ++ }; ++ ++ chosen { ++ bootargs = "console=ttyS0,57600"; ++ }; ++ ++ cfi@1f000000 { ++ compatible = "cfi-flash"; ++ reg = <0x1f000000 0x400000>; ++ ++ bank-width = <2>; ++ device-width = <2>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ label = "uboot"; ++ reg = <0x0 0x30000>; ++ read-only; ++ }; ++ partition@30000 { ++ label = "uboot-env"; ++ reg = <0x30000 0x10000>; ++ read-only; ++ }; ++ partition@40000 { ++ label = "calibration"; ++ reg = <0x40000 0x10000>; ++ read-only; ++ }; ++ partition@50000 { ++ label = "linux"; ++ reg = <0x50000 0x3b0000>; ++ }; ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0035-DT-MIPS-ralink-add-RT3883-dts-files.patch b/target/linux/ramips/patches-3.8/0035-DT-MIPS-ralink-add-RT3883-dts-files.patch new file mode 100644 index 0000000000..e416036391 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0035-DT-MIPS-ralink-add-RT3883-dts-files.patch @@ -0,0 +1,131 @@ +From d702c2e334db0a75298654b12755fd5879dd29df Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 06:27:39 +0000 +Subject: [PATCH 35/79] DT: MIPS: ralink: add RT3883 dts files + +Add a dtsi file for RT3883 SoC and a sample dts file. + +Signed-off-by: John Crispin +Acked-by: Grant Likely +Patchwork: http://patchwork.linux-mips.org/patch/5189/ +--- + arch/mips/ralink/Kconfig | 4 +++ + arch/mips/ralink/dts/Makefile | 1 + + arch/mips/ralink/dts/rt3883.dtsi | 58 ++++++++++++++++++++++++++++++++++ + arch/mips/ralink/dts/rt3883_eval.dts | 16 ++++++++++ + 4 files changed, 79 insertions(+) + create mode 100644 arch/mips/ralink/dts/rt3883.dtsi + create mode 100644 arch/mips/ralink/dts/rt3883_eval.dts + +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 2f6fbb8..493411f 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -42,6 +42,10 @@ choice + bool "RT305x eval kit" + depends on SOC_RT305X + ++ config DTB_RT3883_EVAL ++ bool "RT3883 eval kit" ++ depends on SOC_RT3883 ++ + endchoice + + endif +diff --git a/arch/mips/ralink/dts/Makefile b/arch/mips/ralink/dts/Makefile +index f635a01..040a986 100644 +--- a/arch/mips/ralink/dts/Makefile ++++ b/arch/mips/ralink/dts/Makefile +@@ -1,2 +1,3 @@ + obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o + obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o ++obj-$(CONFIG_DTB_RT3883_EVAL) := rt3883_eval.dtb.o +diff --git a/arch/mips/ralink/dts/rt3883.dtsi b/arch/mips/ralink/dts/rt3883.dtsi +new file mode 100644 +index 0000000..3b131dd +--- /dev/null ++++ b/arch/mips/ralink/dts/rt3883.dtsi +@@ -0,0 +1,58 @@ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "ralink,rt3883-soc"; ++ ++ cpus { ++ cpu@0 { ++ compatible = "mips,mips74Kc"; ++ }; ++ }; ++ ++ cpuintc: cpuintc@0 { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ compatible = "mti,cpu-interrupt-controller"; ++ }; ++ ++ palmbus@10000000 { ++ compatible = "palmbus"; ++ reg = <0x10000000 0x200000>; ++ ranges = <0x0 0x10000000 0x1FFFFF>; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ sysc@0 { ++ compatible = "ralink,rt3883-sysc", "ralink,rt3050-sysc"; ++ reg = <0x0 0x100>; ++ }; ++ ++ intc: intc@200 { ++ compatible = "ralink,rt3883-intc", "ralink,rt2880-intc"; ++ reg = <0x200 0x100>; ++ ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <2>; ++ }; ++ ++ memc@300 { ++ compatible = "ralink,rt3883-memc", "ralink,rt3050-memc"; ++ reg = <0x300 0x100>; ++ }; ++ ++ uartlite@c00 { ++ compatible = "ralink,rt3883-uart", "ralink,rt2880-uart", "ns16550a"; ++ reg = <0xc00 0x100>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <12>; ++ ++ reg-shift = <2>; ++ }; ++ }; ++}; +diff --git a/arch/mips/ralink/dts/rt3883_eval.dts b/arch/mips/ralink/dts/rt3883_eval.dts +new file mode 100644 +index 0000000..2fa6b33 +--- /dev/null ++++ b/arch/mips/ralink/dts/rt3883_eval.dts +@@ -0,0 +1,16 @@ ++/dts-v1/; ++ ++/include/ "rt3883.dtsi" ++ ++/ { ++ compatible = "ralink,rt3883-eval-board", "ralink,rt3883-soc"; ++ model = "Ralink RT3883 evaluation board"; ++ ++ memory@0 { ++ reg = <0x0 0x2000000>; ++ }; ++ ++ chosen { ++ bootargs = "console=ttyS0,57600"; ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0036-DT-MIPS-ralink-add-MT7620A-dts-files.patch b/target/linux/ramips/patches-3.8/0036-DT-MIPS-ralink-add-MT7620A-dts-files.patch new file mode 100644 index 0000000000..1013c1929b --- /dev/null +++ b/target/linux/ramips/patches-3.8/0036-DT-MIPS-ralink-add-MT7620A-dts-files.patch @@ -0,0 +1,132 @@ +From 0757f88781dca6b29de4e1578a4900715371a926 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 06:27:41 +0000 +Subject: [PATCH 36/79] DT: MIPS: ralink: add MT7620A dts files + +Add a dtsi file for MT7620A SoC and a sample dts file. + +Signed-off-by: John Crispin +Acked-by: Grant Likely +Patchwork: http://patchwork.linux-mips.org/patch/5190/ +--- + arch/mips/ralink/Kconfig | 4 +++ + arch/mips/ralink/dts/Makefile | 1 + + arch/mips/ralink/dts/mt7620a.dtsi | 58 +++++++++++++++++++++++++++++++++ + arch/mips/ralink/dts/mt7620a_eval.dts | 16 +++++++++ + 4 files changed, 79 insertions(+) + create mode 100644 arch/mips/ralink/dts/mt7620a.dtsi + create mode 100644 arch/mips/ralink/dts/mt7620a_eval.dts + +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 493411f..026e823 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -46,6 +46,10 @@ choice + bool "RT3883 eval kit" + depends on SOC_RT3883 + ++ config DTB_MT7620A_EVAL ++ bool "MT7620A eval kit" ++ depends on SOC_MT7620 ++ + endchoice + + endif +diff --git a/arch/mips/ralink/dts/Makefile b/arch/mips/ralink/dts/Makefile +index 040a986..18194fa 100644 +--- a/arch/mips/ralink/dts/Makefile ++++ b/arch/mips/ralink/dts/Makefile +@@ -1,3 +1,4 @@ + obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o + obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o + obj-$(CONFIG_DTB_RT3883_EVAL) := rt3883_eval.dtb.o ++obj-$(CONFIG_DTB_MT7620A_EVAL) := mt7620a_eval.dtb.o +diff --git a/arch/mips/ralink/dts/mt7620a.dtsi b/arch/mips/ralink/dts/mt7620a.dtsi +new file mode 100644 +index 0000000..08bf24f +--- /dev/null ++++ b/arch/mips/ralink/dts/mt7620a.dtsi +@@ -0,0 +1,58 @@ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "ralink,mtk7620a-soc"; ++ ++ cpus { ++ cpu@0 { ++ compatible = "mips,mips24KEc"; ++ }; ++ }; ++ ++ cpuintc: cpuintc@0 { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ compatible = "mti,cpu-interrupt-controller"; ++ }; ++ ++ palmbus@10000000 { ++ compatible = "palmbus"; ++ reg = <0x10000000 0x200000>; ++ ranges = <0x0 0x10000000 0x1FFFFF>; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ sysc@0 { ++ compatible = "ralink,mt7620a-sysc"; ++ reg = <0x0 0x100>; ++ }; ++ ++ intc: intc@200 { ++ compatible = "ralink,mt7620a-intc", "ralink,rt2880-intc"; ++ reg = <0x200 0x100>; ++ ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <2>; ++ }; ++ ++ memc@300 { ++ compatible = "ralink,mt7620a-memc", "ralink,rt3050-memc"; ++ reg = <0x300 0x100>; ++ }; ++ ++ uartlite@c00 { ++ compatible = "ralink,mt7620a-uart", "ralink,rt2880-uart", "ns16550a"; ++ reg = <0xc00 0x100>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <12>; ++ ++ reg-shift = <2>; ++ }; ++ }; ++}; +diff --git a/arch/mips/ralink/dts/mt7620a_eval.dts b/arch/mips/ralink/dts/mt7620a_eval.dts +new file mode 100644 +index 0000000..35eb874 +--- /dev/null ++++ b/arch/mips/ralink/dts/mt7620a_eval.dts +@@ -0,0 +1,16 @@ ++/dts-v1/; ++ ++/include/ "mt7620a.dtsi" ++ ++/ { ++ compatible = "ralink,mt7620a-eval-board", "ralink,mt7620a-soc"; ++ model = "Ralink MT7620A evaluation board"; ++ ++ memory@0 { ++ reg = <0x0 0x2000000>; ++ }; ++ ++ chosen { ++ bootargs = "console=ttyS0,57600"; ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0037-MIPS-add-detect_memory_region.patch b/target/linux/ramips/patches-3.8/0037-MIPS-add-detect_memory_region.patch new file mode 100644 index 0000000000..2f5626efd0 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0037-MIPS-add-detect_memory_region.patch @@ -0,0 +1,68 @@ +From 3350a0d29bc3f3d15b50835a20ffcc14a458e2d9 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 13:15:47 +0200 +Subject: [PATCH 37/79] MIPS: add detect_memory_region() + +Add a generic way of detecting the available RAM. This function is based on the +implementation already used by ath79. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5178/ +--- + arch/mips/include/asm/bootinfo.h | 1 + + arch/mips/kernel/setup.c | 20 ++++++++++++++++++++ + 2 files changed, 21 insertions(+) + +diff --git a/arch/mips/include/asm/bootinfo.h b/arch/mips/include/asm/bootinfo.h +index 7a51d87..6d6cfac 100644 +--- a/arch/mips/include/asm/bootinfo.h ++++ b/arch/mips/include/asm/bootinfo.h +@@ -104,6 +104,7 @@ struct boot_mem_map { + extern struct boot_mem_map boot_mem_map; + + extern void add_memory_region(phys_t start, phys_t size, long type); ++extern void detect_memory_region(phys_t start, phys_t sz_min, phys_t sz_max); + + extern void prom_init(void); + extern void prom_free_prom_memory(void); +diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c +index 8c41187..3937630 100644 +--- a/arch/mips/kernel/setup.c ++++ b/arch/mips/kernel/setup.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -122,6 +123,25 @@ void __init add_memory_region(phys_t start, phys_t size, long type) + boot_mem_map.nr_map++; + } + ++void __init detect_memory_region(phys_t start, phys_t sz_min, phys_t sz_max) ++{ ++ phys_t size; ++ ++ for (size = sz_min; size < sz_max; size <<= 1) { ++ if (!memcmp(detect_memory_region, ++ detect_memory_region + size, 1024)) ++ break; ++ } ++ ++ pr_debug("Memory: %lluMB of RAM detected at 0x%llx (min: %lluMB, max: %lluMB)\n", ++ ((unsigned long long) size) / SZ_1M, ++ (unsigned long long) start, ++ ((unsigned long long) sz_min) / SZ_1M, ++ ((unsigned long long) sz_max) / SZ_1M); ++ ++ add_memory_region(start, size, BOOT_MEM_RAM); ++} ++ + static void __init print_memory_map(void) + { + int i; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0038-MIPS-ralink-add-memory-definition-to-struct-ralink_s.patch b/target/linux/ramips/patches-3.8/0038-MIPS-ralink-add-memory-definition-to-struct-ralink_s.patch new file mode 100644 index 0000000000..36fc72a1b6 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0038-MIPS-ralink-add-memory-definition-to-struct-ralink_s.patch @@ -0,0 +1,34 @@ +From cfad83f59816f2be69a22955a398906d749ab108 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 15:10:14 +0200 +Subject: [PATCH 38/79] MIPS: ralink: add memory definition to struct + ralink_soc_info + +Depending on the actual SoC we have a different base address as well as minimum +and maximum size for RAM. Add these fields to the per SoC structure. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5179/ +--- + arch/mips/ralink/common.h | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h +index 299119b..83144c3 100644 +--- a/arch/mips/ralink/common.h ++++ b/arch/mips/ralink/common.h +@@ -33,6 +33,11 @@ extern struct ralink_pinmux rt_gpio_pinmux; + struct ralink_soc_info { + unsigned char sys_type[RAMIPS_SYS_TYPE_LEN]; + unsigned char *compatible; ++ ++ unsigned long mem_base; ++ unsigned long mem_size; ++ unsigned long mem_size_min; ++ unsigned long mem_size_max; + }; + extern struct ralink_soc_info soc_info; + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0039-MIPS-ralink-add-memory-definition-for-RT305x.patch b/target/linux/ramips/patches-3.8/0039-MIPS-ralink-add-memory-definition-for-RT305x.patch new file mode 100644 index 0000000000..6738d9e623 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0039-MIPS-ralink-add-memory-definition-for-RT305x.patch @@ -0,0 +1,96 @@ +From 7d0aa01494353532bbdc644469ef2a06ee089f3e Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 15:13:40 +0200 +Subject: [PATCH 39/79] MIPS: ralink: add memory definition for RT305x + +Populate struct soc_info with the data that describes our RAM window. + +As memory detection fails on RT5350 we read the amount of available memory +from the system controller. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5180/ +--- + arch/mips/include/asm/mach-ralink/rt305x.h | 6 ++++ + arch/mips/ralink/rt305x.c | 45 ++++++++++++++++++++++++++++ + 2 files changed, 51 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/rt305x.h b/arch/mips/include/asm/mach-ralink/rt305x.h +index 80cda8a..069bf37 100644 +--- a/arch/mips/include/asm/mach-ralink/rt305x.h ++++ b/arch/mips/include/asm/mach-ralink/rt305x.h +@@ -157,4 +157,10 @@ static inline int soc_is_rt5350(void) + #define RT3352_RSTCTRL_UDEV BIT(25) + #define RT3352_SYSCFG1_USB0_HOST_MODE BIT(10) + ++#define RT305X_SDRAM_BASE 0x00000000 ++#define RT305X_MEM_SIZE_MIN 2 ++#define RT305X_MEM_SIZE_MAX 64 ++#define RT3352_MEM_SIZE_MIN 2 ++#define RT3352_MEM_SIZE_MAX 256 ++ + #endif +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index 6aa3cb1..ca7ee3a 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -122,6 +122,40 @@ struct ralink_pinmux rt_gpio_pinmux = { + .wdt_reset = rt305x_wdt_reset, + }; + ++static unsigned long rt5350_get_mem_size(void) ++{ ++ void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT305X_SYSC_BASE); ++ unsigned long ret; ++ u32 t; ++ ++ t = __raw_readl(sysc + SYSC_REG_SYSTEM_CONFIG); ++ t = (t >> RT5350_SYSCFG0_DRAM_SIZE_SHIFT) & ++ RT5350_SYSCFG0_DRAM_SIZE_MASK; ++ ++ switch (t) { ++ case RT5350_SYSCFG0_DRAM_SIZE_2M: ++ ret = 2; ++ break; ++ case RT5350_SYSCFG0_DRAM_SIZE_8M: ++ ret = 8; ++ break; ++ case RT5350_SYSCFG0_DRAM_SIZE_16M: ++ ret = 16; ++ break; ++ case RT5350_SYSCFG0_DRAM_SIZE_32M: ++ ret = 32; ++ break; ++ case RT5350_SYSCFG0_DRAM_SIZE_64M: ++ ret = 64; ++ break; ++ default: ++ panic("rt5350: invalid DRAM size: %u", t); ++ break; ++ } ++ ++ return ret; ++} ++ + void __init ralink_clk_init(void) + { + unsigned long cpu_rate, sys_rate, wdt_rate, uart_rate; +@@ -252,4 +286,15 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + name, + (id >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK, + (id & CHIP_ID_REV_MASK)); ++ ++ soc_info->mem_base = RT305X_SDRAM_BASE; ++ if (soc_is_rt5350()) { ++ soc_info->mem_size = rt5350_get_mem_size(); ++ } else if (soc_is_rt305x() || soc_is_rt3350()) { ++ soc_info->mem_size_min = RT305X_MEM_SIZE_MIN; ++ soc_info->mem_size_max = RT305X_MEM_SIZE_MAX; ++ } else if (soc_is_rt3352()) { ++ soc_info->mem_size_min = RT3352_MEM_SIZE_MIN; ++ soc_info->mem_size_max = RT3352_MEM_SIZE_MAX; ++ } + } +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0040-MIPS-ralink-add-memory-definition-for-RT2880.patch b/target/linux/ramips/patches-3.8/0040-MIPS-ralink-add-memory-definition-for-RT2880.patch new file mode 100644 index 0000000000..e09d5b713f --- /dev/null +++ b/target/linux/ramips/patches-3.8/0040-MIPS-ralink-add-memory-definition-for-RT2880.patch @@ -0,0 +1,43 @@ +From 1ca89d8a262f27f4ecd45d40b6774c415842421a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 15:37:37 +0200 +Subject: [PATCH 40/79] MIPS: ralink: add memory definition for RT2880 + +Populate struct soc_info with the data that describes our RAM window. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5181/ +--- + arch/mips/include/asm/mach-ralink/rt288x.h | 4 ++++ + arch/mips/ralink/rt288x.c | 4 ++++ + 2 files changed, 8 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/rt288x.h b/arch/mips/include/asm/mach-ralink/rt288x.h +index ad8b42d..03ad716 100644 +--- a/arch/mips/include/asm/mach-ralink/rt288x.h ++++ b/arch/mips/include/asm/mach-ralink/rt288x.h +@@ -46,4 +46,8 @@ + + #define CLKCFG_SRAM_CS_N_WDT BIT(9) + ++#define RT2880_SDRAM_BASE 0x08000000 ++#define RT2880_MEM_SIZE_MIN 2 ++#define RT2880_MEM_SIZE_MAX 128 ++ + #endif +diff --git a/arch/mips/ralink/rt288x.c b/arch/mips/ralink/rt288x.c +index 1e0788e..f87de1a 100644 +--- a/arch/mips/ralink/rt288x.c ++++ b/arch/mips/ralink/rt288x.c +@@ -136,4 +136,8 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + name, + (id >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK, + (id & CHIP_ID_REV_MASK)); ++ ++ soc_info->mem_base = RT2880_SDRAM_BASE; ++ soc_info->mem_size_min = RT2880_MEM_SIZE_MIN; ++ soc_info->mem_size_max = RT2880_MEM_SIZE_MAX; + } +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0041-MIPS-ralink-add-memory-definition-for-RT3883.patch b/target/linux/ramips/patches-3.8/0041-MIPS-ralink-add-memory-definition-for-RT3883.patch new file mode 100644 index 0000000000..1160906c9e --- /dev/null +++ b/target/linux/ramips/patches-3.8/0041-MIPS-ralink-add-memory-definition-for-RT3883.patch @@ -0,0 +1,51 @@ +From 42a816a8312734e6b438e799378044365e229a07 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 20:23:19 +0200 +Subject: [PATCH 41/79] MIPS: ralink: add memory definition for RT3883 + +Populate struct soc_info with the data that describes our RAM window. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5182/ +--- + arch/mips/include/asm/mach-ralink/rt3883.h | 5 +++++ + arch/mips/ralink/rt3883.c | 4 ++++ + 2 files changed, 9 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/rt3883.h b/arch/mips/include/asm/mach-ralink/rt3883.h +index b91c6c1..058382f 100644 +--- a/arch/mips/include/asm/mach-ralink/rt3883.h ++++ b/arch/mips/include/asm/mach-ralink/rt3883.h +@@ -152,6 +152,7 @@ + #define RT3883_GPIO_SPI_MISO 6 + #define RT3883_GPIO_7 7 + #define RT3883_GPIO_10 10 ++#define RT3883_GPIO_11 11 + #define RT3883_GPIO_14 14 + #define RT3883_GPIO_UART1_TXD 15 + #define RT3883_GPIO_UART1_RXD 16 +@@ -244,4 +245,8 @@ + #define RT3883_FLASH_CFG_WIDTH_16BIT 0x1 + #define RT3883_FLASH_CFG_WIDTH_32BIT 0x2 + ++#define RT3883_SDRAM_BASE 0x00000000 ++#define RT3883_MEM_SIZE_MIN 2 ++#define RT3883_MEM_SIZE_MAX 256 ++ + #endif /* _RT3883_REGS_H_ */ +diff --git a/arch/mips/ralink/rt3883.c b/arch/mips/ralink/rt3883.c +index 2d90aa9..afbf2ce 100644 +--- a/arch/mips/ralink/rt3883.c ++++ b/arch/mips/ralink/rt3883.c +@@ -239,4 +239,8 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + name, + (id >> RT3883_REVID_VER_ID_SHIFT) & RT3883_REVID_VER_ID_MASK, + (id & RT3883_REVID_ECO_ID_MASK)); ++ ++ soc_info->mem_base = RT3883_SDRAM_BASE; ++ soc_info->mem_size_min = RT3883_MEM_SIZE_MIN; ++ soc_info->mem_size_max = RT3883_MEM_SIZE_MAX; + } +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0042-MIPS-ralink-add-memory-definition-for-MT7620.patch b/target/linux/ramips/patches-3.8/0042-MIPS-ralink-add-memory-definition-for-MT7620.patch new file mode 100644 index 0000000000..da2610ce9c --- /dev/null +++ b/target/linux/ramips/patches-3.8/0042-MIPS-ralink-add-memory-definition-for-MT7620.patch @@ -0,0 +1,65 @@ +From e7dfbb1eafed754442099a33492a9e90fa33d3fa Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 14 Apr 2013 09:55:29 +0200 +Subject: [PATCH 42/79] MIPS: ralink: add memory definition for MT7620 + +Populate struct soc_info with the data that describes our RAM window. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5183/ +--- + arch/mips/include/asm/mach-ralink/mt7620.h | 8 ++++++++ + arch/mips/ralink/mt7620.c | 20 ++++++++++++++++++++ + 2 files changed, 28 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/mt7620.h b/arch/mips/include/asm/mach-ralink/mt7620.h +index b272649..9809972 100644 +--- a/arch/mips/include/asm/mach-ralink/mt7620.h ++++ b/arch/mips/include/asm/mach-ralink/mt7620.h +@@ -50,6 +50,14 @@ + #define SYSCFG0_DRAM_TYPE_DDR1 1 + #define SYSCFG0_DRAM_TYPE_DDR2 2 + ++#define MT7620_DRAM_BASE 0x0 ++#define MT7620_SDRAM_SIZE_MIN 2 ++#define MT7620_SDRAM_SIZE_MAX 64 ++#define MT7620_DDR1_SIZE_MIN 32 ++#define MT7620_DDR1_SIZE_MAX 128 ++#define MT7620_DDR2_SIZE_MIN 32 ++#define MT7620_DDR2_SIZE_MAX 256 ++ + #define MT7620_GPIO_MODE_I2C BIT(0) + #define MT7620_GPIO_MODE_UART0_SHIFT 2 + #define MT7620_GPIO_MODE_UART0_MASK 0x7 +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index eb00ab8..98ddb93 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -211,4 +211,24 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + + cfg0 = __raw_readl(sysc + SYSC_REG_SYSTEM_CONFIG0); + dram_type = (cfg0 >> SYSCFG0_DRAM_TYPE_SHIFT) & SYSCFG0_DRAM_TYPE_MASK; ++ ++ switch (dram_type) { ++ case SYSCFG0_DRAM_TYPE_SDRAM: ++ soc_info->mem_size_min = MT7620_SDRAM_SIZE_MIN; ++ soc_info->mem_size_max = MT7620_SDRAM_SIZE_MAX; ++ break; ++ ++ case SYSCFG0_DRAM_TYPE_DDR1: ++ soc_info->mem_size_min = MT7620_DDR1_SIZE_MIN; ++ soc_info->mem_size_max = MT7620_DDR1_SIZE_MAX; ++ break; ++ ++ case SYSCFG0_DRAM_TYPE_DDR2: ++ soc_info->mem_size_min = MT7620_DDR2_SIZE_MIN; ++ soc_info->mem_size_max = MT7620_DDR2_SIZE_MAX; ++ break; ++ default: ++ BUG(); ++ } ++ soc_info->mem_base = MT7620_DRAM_BASE; + } +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0043-MIPS-ralink-make-use-of-the-new-memory-detection-cod.patch b/target/linux/ramips/patches-3.8/0043-MIPS-ralink-make-use-of-the-new-memory-detection-cod.patch new file mode 100644 index 0000000000..1b6acf0835 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0043-MIPS-ralink-make-use-of-the-new-memory-detection-cod.patch @@ -0,0 +1,45 @@ +From 6142d0fa1c7ba6050664a27d7cca87043217b01f Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 15:15:51 +0200 +Subject: [PATCH 43/79] MIPS: ralink: make use of the new memory detection + code + +Call detect_memory_region() from plat_mem_setup() unless the size was already +read from the system controller. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5184/ +--- + arch/mips/ralink/of.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c +index 4165e70..fb15695 100644 +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -85,6 +86,14 @@ void __init plat_mem_setup(void) + * parsed resulting in our memory appearing + */ + __dt_setup_arch(&__dtb_start); ++ ++ if (soc_info.mem_size) ++ add_memory_region(soc_info.mem_base, soc_info.mem_size, ++ BOOT_MEM_RAM); ++ else ++ detect_memory_region(soc_info.mem_base, ++ soc_info.mem_size_min * SZ_1M, ++ soc_info.mem_size_max * SZ_1M); + } + + static int __init plat_of_setup(void) +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0044-MIPS-ralink-upstream-v3.10.patch b/target/linux/ramips/patches-3.8/0044-MIPS-ralink-upstream-v3.10.patch new file mode 100644 index 0000000000..0ebbfe5fdf --- /dev/null +++ b/target/linux/ramips/patches-3.8/0044-MIPS-ralink-upstream-v3.10.patch @@ -0,0 +1,23 @@ +From b6038567015683bc9541e2bee1b9bc0db50589a3 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 30 May 2013 16:16:13 +0200 +Subject: [PATCH 44/79] MIPS: ralink: upstream v3.10 + +patches prior to this were sent upstream and accepted for v3.10 + +Signed-off-by: John Crispin +--- + dummy | 1 + + 1 file changed, 1 insertion(+) + create mode 100644 dummy + +diff --git a/dummy b/dummy +new file mode 100644 +index 0000000..421376d +--- /dev/null ++++ b/dummy +@@ -0,0 +1 @@ ++dummy +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0045-MIPS-ralink-add-pinmux-driver.patch b/target/linux/ramips/patches-3.8/0045-MIPS-ralink-add-pinmux-driver.patch new file mode 100644 index 0000000000..546719df98 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0045-MIPS-ralink-add-pinmux-driver.patch @@ -0,0 +1,141 @@ +From c7107291df4035794c67d7a904fe7419fedc5922 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Apr 2013 23:11:42 +0200 +Subject: [PATCH 45/79] MIPS: ralink: add pinmux driver + +Add code to setup the pinmux on ralonk SoC. The SoC has a single 32 bit register +for this functionality with simple on/off bits. Building a full featured pinctrl +driver would be overkill. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Makefile | 2 +- + arch/mips/ralink/common.h | 2 ++ + arch/mips/ralink/of.c | 2 ++ + arch/mips/ralink/pinmux.c | 77 +++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 82 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/ralink/pinmux.c + +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index 38cf1a8..341b4de 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -6,7 +6,7 @@ + # Copyright (C) 2009-2011 Gabor Juhos + # Copyright (C) 2013 John Crispin + +-obj-y := prom.o of.o reset.o clk.o irq.o ++obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o + + obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o +diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h +index 83144c3..f113fd6 100644 +--- a/arch/mips/ralink/common.h ++++ b/arch/mips/ralink/common.h +@@ -50,4 +50,6 @@ extern void prom_soc_init(struct ralink_soc_info *soc_info); + + __iomem void *plat_of_remap_node(const char *node); + ++void ralink_pinmux(void); ++ + #endif /* _RALINK_COMMON_H__ */ +diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c +index fb15695..f916774 100644 +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -110,6 +110,8 @@ static int __init plat_of_setup(void) + if (of_platform_populate(NULL, of_ids, NULL, NULL)) + panic("failed to populate DT\n"); + ++ ralink_pinmux(); ++ + return 0; + } + +diff --git a/arch/mips/ralink/pinmux.c b/arch/mips/ralink/pinmux.c +new file mode 100644 +index 0000000..1720216 +--- /dev/null ++++ b/arch/mips/ralink/pinmux.c +@@ -0,0 +1,77 @@ ++/* ++ * 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. ++ * ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "common.h" ++ ++#define SYSC_REG_GPIO_MODE 0x60 ++ ++static int ralink_mux_mask(const char *name, struct ralink_pinmux_grp *grps, u32* mask) ++{ ++ for (; grps && grps->name; grps++) ++ if (!strcmp(grps->name, name)) { ++ *mask = grps->mask; ++ return 0; ++ } ++ ++ return -1; ++} ++ ++void ralink_pinmux(void) ++{ ++ const __be32 *wdt; ++ struct device_node *np; ++ struct property *prop; ++ const char *uart, *pin; ++ u32 mode = 0; ++ int m; ++ ++ np = of_find_compatible_node(NULL, NULL, "ralink,rt3050-sysc"); ++ if (!np) ++ return; ++ ++ of_property_for_each_string(np, "ralink,gpiomux", prop, pin) { ++ if (!ralink_mux_mask(pin, rt_gpio_pinmux.mode, &m)) { ++ mode |= m; ++ pr_debug("pinmux: registered gpiomux \"%s\"\n", pin); ++ } else { ++ pr_err("pinmux: failed to load \"%s\"\n", pin); ++ } ++ } ++ ++ of_property_for_each_string(np, "ralink,pinmux", prop, pin) { ++ if (!ralink_mux_mask(pin, rt_gpio_pinmux.mode, &m)) { ++ mode &= ~m; ++ pr_debug("pinmux: registered pinmux \"%s\"\n", pin); ++ } else { ++ pr_err("pinmux: failed to load group \"%s\"\n", pin); ++ } ++ } ++ ++ of_property_read_string(np, "ralink,uartmux", &uart); ++ if (uart) { ++ mode &= ~(rt_gpio_pinmux.uart_mask << rt_gpio_pinmux.uart_shift); ++ if (ralink_mux_mask(uart, rt_gpio_pinmux.uart, &m)) { ++ pr_err("pinmux: failed to load uartmux \"%s\"\n", uart); ++ mode |= rt_gpio_pinmux.uart_mask << rt_gpio_pinmux.uart_shift; ++ } else { ++ mode |= m << rt_gpio_pinmux.uart_shift; ++ pr_debug("pinmux: registered uartmux \"%s\"\n", uart); ++ } ++ } ++ ++ wdt = of_get_property(np, "ralink,wdtmux", NULL); ++ if (wdt && *wdt && rt_gpio_pinmux.wdt_reset) ++ rt_gpio_pinmux.wdt_reset(); ++ ++ rt_sysc_w32(mode, SYSC_REG_GPIO_MODE); ++} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0046-MIPS-ralink-add-support-for-periodic-timer-irq.patch b/target/linux/ramips/patches-3.8/0046-MIPS-ralink-add-support-for-periodic-timer-irq.patch new file mode 100644 index 0000000000..18e001cc06 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0046-MIPS-ralink-add-support-for-periodic-timer-irq.patch @@ -0,0 +1,228 @@ +From f14edca84cd854dac7dd429b1fbeb80ac76fca16 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 23 Mar 2013 19:44:41 +0100 +Subject: [PATCH 46/79] MIPS: ralink: add support for periodic timer irq + +Adds a driver for the periodic timer found on Ralink SoC. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Makefile | 2 +- + arch/mips/ralink/timer.c | 192 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 193 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/ralink/timer.c + +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index 341b4de..cae7d88 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -6,7 +6,7 @@ + # Copyright (C) 2009-2011 Gabor Juhos + # Copyright (C) 2013 John Crispin + +-obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o ++obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o timer.o + + obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o +diff --git a/arch/mips/ralink/timer.c b/arch/mips/ralink/timer.c +new file mode 100644 +index 0000000..0a6856c +--- /dev/null ++++ b/arch/mips/ralink/timer.c +@@ -0,0 +1,192 @@ ++/* ++ * 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. ++ * ++ * Copyright (C) 2013 John Crispin ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define TIMER_REG_TMRSTAT 0x00 ++#define TIMER_REG_TMR0LOAD 0x10 ++#define TIMER_REG_TMR0CTL 0x18 ++ ++#define TMRSTAT_TMR0INT BIT(0) ++ ++#define TMR0CTL_ENABLE BIT(7) ++#define TMR0CTL_MODE_PERIODIC BIT(4) ++#define TMR0CTL_PRESCALER 1 ++#define TMR0CTL_PRESCALE_VAL (0xf - TMR0CTL_PRESCALER) ++#define TMR0CTL_PRESCALE_DIV (65536 / BIT(TMR0CTL_PRESCALER)) ++ ++struct rt_timer { ++ struct device *dev; ++ void __iomem *membase; ++ int irq; ++ unsigned long timer_freq; ++ unsigned long timer_div; ++}; ++ ++static inline void rt_timer_w32(struct rt_timer *rt, u8 reg, u32 val) ++{ ++ __raw_writel(val, rt->membase + reg); ++} ++ ++static inline u32 rt_timer_r32(struct rt_timer *rt, u8 reg) ++{ ++ return __raw_readl(rt->membase + reg); ++} ++ ++static irqreturn_t rt_timer_irq(int irq, void *_rt) ++{ ++ struct rt_timer *rt = (struct rt_timer *) _rt; ++ ++ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); ++ rt_timer_w32(rt, TIMER_REG_TMRSTAT, TMRSTAT_TMR0INT); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++static int rt_timer_request(struct rt_timer *rt) ++{ ++ int err = request_irq(rt->irq, rt_timer_irq, IRQF_DISABLED, ++ dev_name(rt->dev), rt); ++ if (err) { ++ dev_err(rt->dev, "failed to request irq\n"); ++ } else { ++ u32 t = TMR0CTL_MODE_PERIODIC | TMR0CTL_PRESCALE_VAL; ++ rt_timer_w32(rt, TIMER_REG_TMR0CTL, t); ++ } ++ return err; ++} ++ ++static void rt_timer_free(struct rt_timer *rt) ++{ ++ free_irq(rt->irq, rt); ++} ++ ++static int rt_timer_config(struct rt_timer *rt, unsigned long divisor) ++{ ++ if (rt->timer_freq < divisor) ++ rt->timer_div = rt->timer_freq; ++ else ++ rt->timer_div = divisor; ++ ++ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); ++ ++ return 0; ++} ++ ++static int rt_timer_enable(struct rt_timer *rt) ++{ ++ u32 t; ++ ++ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); ++ ++ t = rt_timer_r32(rt, TIMER_REG_TMR0CTL); ++ t |= TMR0CTL_ENABLE; ++ rt_timer_w32(rt, TIMER_REG_TMR0CTL, t); ++ ++ return 0; ++} ++ ++static void rt_timer_disable(struct rt_timer *rt) ++{ ++ u32 t; ++ ++ t = rt_timer_r32(rt, TIMER_REG_TMR0CTL); ++ t &= ~TMR0CTL_ENABLE; ++ rt_timer_w32(rt, TIMER_REG_TMR0CTL, t); ++} ++ ++static int rt_timer_probe(struct platform_device *pdev) ++{ ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ struct rt_timer *rt; ++ struct clk *clk; ++ ++ if (!res) { ++ dev_err(&pdev->dev, "no memory resource found\n"); ++ return -EINVAL; ++ } ++ ++ rt = devm_kzalloc(&pdev->dev, sizeof(*rt), GFP_KERNEL); ++ if (!rt) { ++ dev_err(&pdev->dev, "failed to allocate memory\n"); ++ return -ENOMEM; ++ } ++ ++ rt->irq = platform_get_irq(pdev, 0); ++ if (!rt->irq) { ++ dev_err(&pdev->dev, "failed to load irq\n"); ++ return -ENOENT; ++ } ++ ++ rt->membase = devm_request_and_ioremap(&pdev->dev, res); ++ if (!rt->membase) { ++ dev_err(&pdev->dev, "failed to ioremap\n"); ++ return -ENOMEM; ++ } ++ ++ clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "failed get clock rate\n"); ++ return PTR_ERR(clk); ++ } ++ ++ rt->timer_freq = clk_get_rate(clk) / TMR0CTL_PRESCALE_DIV; ++ if (!rt->timer_freq) ++ return -EINVAL; ++ ++ rt->dev = &pdev->dev; ++ platform_set_drvdata(pdev, rt); ++ ++ rt_timer_request(rt); ++ rt_timer_config(rt, 2); ++ rt_timer_enable(rt); ++ ++ dev_info(&pdev->dev, "maximum frequncy is %luHz\n", rt->timer_freq); ++ ++ return 0; ++} ++ ++static int rt_timer_remove(struct platform_device *pdev) ++{ ++ struct rt_timer *rt = platform_get_drvdata(pdev); ++ ++ rt_timer_disable(rt); ++ rt_timer_free(rt); ++ ++ return 0; ++} ++ ++static const struct of_device_id rt_timer_match[] = { ++ { .compatible = "ralink,rt2880-timer" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt_timer_match); ++ ++static struct platform_driver rt_timer_driver = { ++ .probe = rt_timer_probe, ++ .remove = rt_timer_remove, ++ .driver = { ++ .name = "rt-timer", ++ .owner = THIS_MODULE, ++ .of_match_table = rt_timer_match ++ }, ++}; ++ ++module_platform_driver(rt_timer_driver); ++ ++MODULE_DESCRIPTION("Ralink RT2880 timer"); ++MODULE_AUTHOR("John Crispin +Date: Sun, 19 May 2013 00:42:23 +0200 +Subject: [PATCH 47/79] MIPS: ralink: add rt_sysc_m32 helper + +Signed-off-by: John Crispin +--- + arch/mips/include/asm/mach-ralink/ralink_regs.h | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/ralink_regs.h b/arch/mips/include/asm/mach-ralink/ralink_regs.h +index 5a508f9..bd93014 100644 +--- a/arch/mips/include/asm/mach-ralink/ralink_regs.h ++++ b/arch/mips/include/asm/mach-ralink/ralink_regs.h +@@ -26,6 +26,13 @@ static inline u32 rt_sysc_r32(unsigned reg) + return __raw_readl(rt_sysc_membase + reg); + } + ++static inline void rt_sysc_m32(u32 clr, u32 set, unsigned reg) ++{ ++ u32 val = rt_sysc_r32(reg) & ~clr; ++ ++ __raw_writel(val | set, rt_sysc_membase + reg); ++} ++ + static inline void rt_memc_w32(u32 val, unsigned reg) + { + __raw_writel(val, rt_memc_membase + reg); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0048-MIPS-ralink-make-mt7620-ram-detect-verbose.patch b/target/linux/ramips/patches-3.8/0048-MIPS-ralink-make-mt7620-ram-detect-verbose.patch new file mode 100644 index 0000000000..d17c227649 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0048-MIPS-ralink-make-mt7620-ram-detect-verbose.patch @@ -0,0 +1,39 @@ +From 8c2015a6eed656d8578a78e392625869ba8f9bb4 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 20 May 2013 20:30:11 +0200 +Subject: [PATCH 48/79] MIPS: ralink: make mt7620 ram detect verbose + +Make the code print which of SDRAM, DDR1 or DDR2 was detected. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/mt7620.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index 98ddb93..28350d0 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -214,16 +214,19 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + + switch (dram_type) { + case SYSCFG0_DRAM_TYPE_SDRAM: ++ pr_info("Board has SDRAM\n"); + soc_info->mem_size_min = MT7620_SDRAM_SIZE_MIN; + soc_info->mem_size_max = MT7620_SDRAM_SIZE_MAX; + break; + + case SYSCFG0_DRAM_TYPE_DDR1: ++ pr_info("Board has DDR1\n"); + soc_info->mem_size_min = MT7620_DDR1_SIZE_MIN; + soc_info->mem_size_max = MT7620_DDR1_SIZE_MAX; + break; + + case SYSCFG0_DRAM_TYPE_DDR2: ++ pr_info("Board has DDR2\n"); + soc_info->mem_size_min = MT7620_DDR2_SIZE_MIN; + soc_info->mem_size_max = MT7620_DDR2_SIZE_MAX; + break; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0049-MIPS-ralink-add-verbose-pmu-info.patch b/target/linux/ramips/patches-3.8/0049-MIPS-ralink-add-verbose-pmu-info.patch new file mode 100644 index 0000000000..206e9d8473 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0049-MIPS-ralink-add-verbose-pmu-info.patch @@ -0,0 +1,64 @@ +From 1652cf71db797b249c0d384e6c3d31b312b7012e Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 20 May 2013 20:57:09 +0200 +Subject: [PATCH 49/79] MIPS: ralink: add verbose pmu info + +Print the PMU and LDO settings on boot. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/mt7620.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index 28350d0..69729a5 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -20,6 +20,22 @@ + + #include "common.h" + ++/* analog */ ++#define PMU0_CFG 0x88 ++#define PMU_SW_SET BIT(28) ++#define A_DCDC_EN BIT(24) ++#define A_SSC_PERI BIT(19) ++#define A_SSC_GEN BIT(18) ++#define A_SSC_M 0x3 ++#define A_SSC_S 16 ++#define A_DLY_M 0x7 ++#define A_DLY_S 8 ++#define A_VTUNE_M 0xff ++ ++/* digital */ ++#define PMU1_CFG 0x8C ++#define DIG_SW_SEL BIT(25) ++ + /* does the board have sdram or ddram */ + static int dram_type; + +@@ -187,6 +203,8 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + u32 n1; + u32 rev; + u32 cfg0; ++ u32 pmu0; ++ u32 pmu1; + + n0 = __raw_readl(sysc + SYSC_REG_CHIP_NAME0); + n1 = __raw_readl(sysc + SYSC_REG_CHIP_NAME1); +@@ -234,4 +252,12 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + BUG(); + } + soc_info->mem_base = MT7620_DRAM_BASE; ++ ++ pmu0 = __raw_readl(sysc + PMU0_CFG); ++ pmu1 = __raw_readl(sysc + PMU1_CFG); ++ ++ pr_info("Analog PMU set to %s control\n", ++ (pmu0 & PMU_SW_SET) ? ("sw") : ("hw")); ++ pr_info("Digital PMU set to %s control\n", ++ (pmu1 & DIG_SW_SEL) ? ("sw") : ("hw")); + } +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0050-MIPS-ralink-adds-a-bootrom-dumper-module.patch b/target/linux/ramips/patches-3.8/0050-MIPS-ralink-adds-a-bootrom-dumper-module.patch new file mode 100644 index 0000000000..893a4083ed --- /dev/null +++ b/target/linux/ramips/patches-3.8/0050-MIPS-ralink-adds-a-bootrom-dumper-module.patch @@ -0,0 +1,83 @@ +From d0d5dff525d921a7200d4919ea46c51fce27b7fb Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 21 May 2013 15:50:31 +0200 +Subject: [PATCH 50/79] MIPS: ralink: adds a bootrom dumper module + +This patch adds a trivial driver that allows userland to extract the bootrom of +a SoC via debugfs. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Makefile | 2 ++ + arch/mips/ralink/bootrom.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 50 insertions(+) + create mode 100644 arch/mips/ralink/bootrom.c + +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index cae7d88..5fa6129 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -15,4 +15,6 @@ obj-$(CONFIG_SOC_MT7620) += mt7620.o + + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + ++obj-$(CONFIG_DEBUG_FS) += bootrom.o ++ + obj-y += dts/ +diff --git a/arch/mips/ralink/bootrom.c b/arch/mips/ralink/bootrom.c +new file mode 100644 +index 0000000..f926f6f +--- /dev/null ++++ b/arch/mips/ralink/bootrom.c +@@ -0,0 +1,48 @@ ++/* ++ * 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. ++ * ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++ ++#define BOOTROM_OFFSET 0x10118000 ++#define BOOTROM_SIZE 0x8000 ++ ++static void __iomem *membase = (void __iomem*) KSEG1ADDR(BOOTROM_OFFSET); ++ ++static int bootrom_show(struct seq_file *s, void *unused) ++{ ++ seq_write(s, membase, BOOTROM_SIZE); ++ ++ return 0; ++} ++ ++static int bootrom_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, bootrom_show, NULL); ++} ++ ++static const struct file_operations bootrom_file_ops = { ++ .open = bootrom_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int bootrom_setup(void) ++{ ++ if (!debugfs_create_file("bootrom", 0444, ++ NULL, NULL, &bootrom_file_ops)) { ++ pr_err("Failed to create bootrom debugfs file\n"); ++ ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++postcore_initcall(bootrom_setup); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0051-MIPS-ralink-add-missing-SZ_1M-multiplier.patch b/target/linux/ramips/patches-3.8/0051-MIPS-ralink-add-missing-SZ_1M-multiplier.patch new file mode 100644 index 0000000000..161674ed23 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0051-MIPS-ralink-add-missing-SZ_1M-multiplier.patch @@ -0,0 +1,29 @@ +From 1996ffa03b1c6e0ab7db35dfd910923f4ab54b26 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 21 May 2013 17:15:54 +0200 +Subject: [PATCH 51/79] MIPS: ralink: add missing SZ_1M multiplier + +On RT5350 the memory size is set to Bytes and not MegaBytes due to a missing +multiplier. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/of.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c +index f916774..b25c1f2 100644 +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -88,7 +88,7 @@ void __init plat_mem_setup(void) + __dt_setup_arch(&__dtb_start); + + if (soc_info.mem_size) +- add_memory_region(soc_info.mem_base, soc_info.mem_size, ++ add_memory_region(soc_info.mem_base, soc_info.mem_size * SZ_1M, + BOOT_MEM_RAM); + else + detect_memory_region(soc_info.mem_base, +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0052-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch b/target/linux/ramips/patches-3.8/0052-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch new file mode 100644 index 0000000000..0a6ecc4134 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0052-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch @@ -0,0 +1,78 @@ +From b9afea3f5cf6c2da3b88f2902c4377e9c5b22a6d Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 3 May 2013 00:04:58 +0200 +Subject: [PATCH 52/79] MIPS: use set_mode() to enable/disable the cevt-r4k + irq + +Signed-off-by: John Crispin +--- + arch/mips/kernel/cevt-r4k.c | 39 ++++++++++++++++++++++++++------------- + 1 file changed, 26 insertions(+), 13 deletions(-) + +diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c +index 7532392..afc08e4 100644 +--- a/arch/mips/kernel/cevt-r4k.c ++++ b/arch/mips/kernel/cevt-r4k.c +@@ -39,12 +39,6 @@ static int mips_next_event(unsigned long delta, + + #endif /* CONFIG_MIPS_MT_SMTC */ + +-void mips_set_clock_mode(enum clock_event_mode mode, +- struct clock_event_device *evt) +-{ +- /* Nothing to do ... */ +-} +- + DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device); + int cp0_timer_irq_installed; + +@@ -89,6 +83,32 @@ struct irqaction c0_compare_irqaction = { + .name = "timer", + }; + ++void mips_set_clock_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_ONESHOT: ++ if (cp0_timer_irq_installed) ++ break; ++ ++ cp0_timer_irq_installed = 1; ++ ++ setup_irq(evt->irq, &c0_compare_irqaction); ++ break; ++ ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ if (!cp0_timer_irq_installed) ++ break; ++ ++ cp0_timer_irq_installed = 0; ++ free_irq(evt->irq, &c0_compare_irqaction); ++ break; ++ ++ default: ++ pr_err("Unhandeled mips clock_mode\n"); ++ break; ++ } ++} + + void mips_event_handler(struct clock_event_device *dev) + { +@@ -208,13 +228,6 @@ int __cpuinit r4k_clockevent_init(void) + + clockevents_register_device(cd); + +- if (cp0_timer_irq_installed) +- return 0; +- +- cp0_timer_irq_installed = 1; +- +- setup_irq(irq, &c0_compare_irqaction); +- + return 0; + } + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0053-MIPS-ralink-add-illegal-access-driver.patch b/target/linux/ramips/patches-3.8/0053-MIPS-ralink-add-illegal-access-driver.patch new file mode 100644 index 0000000000..ef2fdb9377 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0053-MIPS-ralink-add-illegal-access-driver.patch @@ -0,0 +1,121 @@ +From 69a5834d0a90588578df9689530fc8f3ab8ef59a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 16 May 2013 23:28:23 +0200 +Subject: [PATCH 53/79] MIPS: ralink: add illegal access driver + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Makefile | 2 +- + arch/mips/ralink/ill_acc.c | 87 ++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 88 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/ralink/ill_acc.c + +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index 5fa6129..55a5bfc 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -6,7 +6,7 @@ + # Copyright (C) 2009-2011 Gabor Juhos + # Copyright (C) 2013 John Crispin + +-obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o timer.o ++obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o timer.o ill_acc.o + + obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o +diff --git a/arch/mips/ralink/ill_acc.c b/arch/mips/ralink/ill_acc.c +new file mode 100644 +index 0000000..4a3f696 +--- /dev/null ++++ b/arch/mips/ralink/ill_acc.c +@@ -0,0 +1,87 @@ ++/* ++ * 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. ++ * ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++ ++#include ++ ++#define REG_ILL_ACC_ADDR 0x10 ++#define REG_ILL_ACC_TYPE 0x14 ++ ++#define ILL_INT_STATUS BIT(31) ++#define ILL_ACC_WRITE BIT(30) ++#define ILL_ACC_LEN_M 0xff ++#define ILL_ACC_OFF_M 0xf ++#define ILL_ACC_OFF_S 16 ++#define ILL_ACC_ID_M 0x7 ++#define ILL_ACC_ID_S 8 ++ ++#define DRV_NAME "ill_acc" ++ ++static const char *ill_acc_ids[] = { ++ "cpu", "dma", "ppe", "pdma rx","pdma tx", "pci/e", "wmac", "usb", ++}; ++ ++static irqreturn_t ill_acc_irq_handler(int irq, void *_priv) ++{ ++ struct device *dev = (struct device *) _priv; ++ u32 addr = rt_memc_r32(REG_ILL_ACC_ADDR); ++ u32 type = rt_memc_r32(REG_ILL_ACC_TYPE); ++ ++ dev_err(dev, "illegal %s access from %s - addr:0x%08x offset:%d len:%d\n", ++ (type & ILL_ACC_WRITE) ? ("write") : ("read"), ++ ill_acc_ids[(type >> ILL_ACC_ID_S) & ILL_ACC_ID_M], ++ addr, (type >> ILL_ACC_OFF_S) & ILL_ACC_OFF_M, ++ type & ILL_ACC_LEN_M); ++ ++ rt_memc_w32(REG_ILL_ACC_TYPE, REG_ILL_ACC_TYPE); ++ ++ return IRQ_HANDLED; ++} ++ ++static int __init ill_acc_of_setup(void) ++{ ++ struct platform_device *pdev; ++ struct device_node *np; ++ int irq; ++ ++ /* somehow this driver breaks on RT5350 */ ++ if (of_machine_is_compatible("ralink,rt5350-soc")) ++ return -EINVAL; ++ ++ np = of_find_compatible_node(NULL, NULL, "ralink,rt3050-memc"); ++ if (!np) ++ return -EINVAL; ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev) { ++ pr_err("%s: failed to lookup pdev\n", np->name); ++ return -EINVAL; ++ } ++ ++ irq = irq_of_parse_and_map(np, 0); ++ if (!irq) { ++ dev_err(&pdev->dev, "failed to get irq\n"); ++ return -EINVAL; ++ } ++ ++ if (request_irq(irq, ill_acc_irq_handler, 0, "ill_acc", &pdev->dev)) { ++ dev_err(&pdev->dev, "failed to request irq\n"); ++ return -EINVAL; ++ } ++ ++ rt_memc_w32(ILL_INT_STATUS, REG_ILL_ACC_TYPE); ++ ++ dev_info(&pdev->dev, "irq registered\n"); ++ ++ return 0; ++} ++ ++arch_initcall(ill_acc_of_setup); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0054-MIPS-ralink-workaround-DTB-memory-issue.patch b/target/linux/ramips/patches-3.8/0054-MIPS-ralink-workaround-DTB-memory-issue.patch new file mode 100644 index 0000000000..1a550ab89b --- /dev/null +++ b/target/linux/ramips/patches-3.8/0054-MIPS-ralink-workaround-DTB-memory-issue.patch @@ -0,0 +1,29 @@ +From 9b6da5fa57a40647f6d69ee1fcdabc5e5f7010dc Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 23 May 2013 18:50:56 +0200 +Subject: [PATCH 54/79] MIPS: ralink: workaround DTB memory issue + +If the DTB is too big a bug happens on boot when init ram is freed. +This is a temporary fix until the real cause is found. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/of.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c +index b25c1f2..8efb02b 100644 +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -74,7 +74,7 @@ void __init device_tree_init(void) + unflatten_device_tree(); + + /* free the space reserved for the dt blob */ +- free_bootmem(base, size); ++ //free_bootmem(base, size); + } + + void __init plat_mem_setup(void) +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0055-MIPS-ralink-add-spi-clock-definition-to-mt7620a.patch b/target/linux/ramips/patches-3.8/0055-MIPS-ralink-add-spi-clock-definition-to-mt7620a.patch new file mode 100644 index 0000000000..b6d91eb66f --- /dev/null +++ b/target/linux/ramips/patches-3.8/0055-MIPS-ralink-add-spi-clock-definition-to-mt7620a.patch @@ -0,0 +1,25 @@ +From 148d428995c21cc95350937d42ffd3b13e36daa5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 23 May 2013 18:46:25 +0200 +Subject: [PATCH 55/79] MIPS: ralink: add spi clock definition to mt7620a + +Signed-off-by: John Crispin +--- + arch/mips/ralink/mt7620.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index 69729a5..08c96db6 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -183,6 +183,7 @@ void __init ralink_clk_init(void) + ralink_clk_add("cpu", cpu_rate); + ralink_clk_add("10000100.timer", 40000000); + ralink_clk_add("10000500.uart", 40000000); ++ ralink_clk_add("10000b00.spi", 40000000); + ralink_clk_add("10000c00.uartlite", 40000000); + } + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0056-MIPS-ralink-DTS-file-updates.patch b/target/linux/ramips/patches-3.8/0056-MIPS-ralink-DTS-file-updates.patch new file mode 100644 index 0000000000..70816224bc --- /dev/null +++ b/target/linux/ramips/patches-3.8/0056-MIPS-ralink-DTS-file-updates.patch @@ -0,0 +1,1006 @@ +From 4f3d8fafc176d5d95957a4267c253d0c6ea5182d Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 30 Apr 2013 17:27:46 +0200 +Subject: [PATCH 56/79] MIPS: ralink DTS file updates + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Kconfig | 8 + + arch/mips/ralink/dts/Makefile | 2 + + arch/mips/ralink/dts/mt7620a.dtsi | 238 ++++++++++++++++++++++++- + arch/mips/ralink/dts/mt7620a_eval.dts | 111 ++++++++++++ + arch/mips/ralink/dts/mt7620a_mt7610e_eval.dts | 99 ++++++++++ + arch/mips/ralink/dts/rt2880.dtsi | 17 ++ + arch/mips/ralink/dts/rt2880_eval.dts | 6 + + arch/mips/ralink/dts/rt3050.dtsi | 31 +++- + arch/mips/ralink/dts/rt3052_eval.dts | 19 +- + arch/mips/ralink/dts/rt5350.dtsi | 227 +++++++++++++++++++++++ + arch/mips/ralink/dts/rt5350_eval.dts | 69 +++++++ + 11 files changed, 824 insertions(+), 3 deletions(-) + create mode 100644 arch/mips/ralink/dts/mt7620a_mt7610e_eval.dts + create mode 100644 arch/mips/ralink/dts/rt5350.dtsi + create mode 100644 arch/mips/ralink/dts/rt5350_eval.dts + +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 026e823..38540a4 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -42,6 +42,10 @@ choice + bool "RT305x eval kit" + depends on SOC_RT305X + ++ config DTB_RT5350_EVAL ++ bool "RT5350 eval kit" ++ depends on SOC_RT305X ++ + config DTB_RT3883_EVAL + bool "RT3883 eval kit" + depends on SOC_RT3883 +@@ -50,6 +54,10 @@ choice + bool "MT7620A eval kit" + depends on SOC_MT7620 + ++ config DTB_MT7620A_MT7610E_EVAL ++ bool "MT7620A + MT7610E eval kit" ++ depends on SOC_MT7620 ++ + endchoice + + endif +diff --git a/arch/mips/ralink/dts/Makefile b/arch/mips/ralink/dts/Makefile +index 18194fa..0bd12b5 100644 +--- a/arch/mips/ralink/dts/Makefile ++++ b/arch/mips/ralink/dts/Makefile +@@ -1,4 +1,6 @@ + obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o + obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o ++obj-$(CONFIG_DTB_RT5350_EVAL) := rt5350_eval.dtb.o + obj-$(CONFIG_DTB_RT3883_EVAL) := rt3883_eval.dtb.o + obj-$(CONFIG_DTB_MT7620A_EVAL) := mt7620a_eval.dtb.o ++obj-$(CONFIG_DTB_MT7620A_MT7610E_EVAL) := mt7620a_mt7610e_eval.dtb.o +diff --git a/arch/mips/ralink/dts/mt7620a.dtsi b/arch/mips/ralink/dts/mt7620a.dtsi +index 08bf24f..104abfb 100644 +--- a/arch/mips/ralink/dts/mt7620a.dtsi ++++ b/arch/mips/ralink/dts/mt7620a.dtsi +@@ -25,14 +25,36 @@ + #size-cells = <1>; + + sysc@0 { +- compatible = "ralink,mt7620a-sysc"; ++ compatible = "ralink,mt7620a-sysc", "ralink,rt3050-sysc"; + reg = <0x0 0x100>; + }; + ++ timer@100 { ++ compatible = "ralink,mt7620a-timer", "ralink,rt2880-timer"; ++ reg = <0x100 0x20>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <1>; ++ }; ++ ++ watchdog@120 { ++ compatible = "ralink,mt7620a-wdt", "ralink,rt2880-wdt"; ++ reg = <0x120 0x10>; ++ ++ resets = <&rstctrl 8>; ++ reset-names = "wdt"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <1>; ++ }; ++ + intc: intc@200 { + compatible = "ralink,mt7620a-intc", "ralink,rt2880-intc"; + reg = <0x200 0x100>; + ++ resets = <&rstctrl 19>; ++ reset-names = "intc"; ++ + interrupt-controller; + #interrupt-cells = <1>; + +@@ -43,16 +65,230 @@ + memc@300 { + compatible = "ralink,mt7620a-memc", "ralink,rt3050-memc"; + reg = <0x300 0x100>; ++ ++ resets = <&rstctrl 20>; ++ reset-names = "mc"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <3>; ++ }; ++ ++ uart@500 { ++ compatible = "ralink,mt7620a-uart", "ralink,rt2880-uart", "ns16550a"; ++ reg = <0x500 0x100>; ++ ++ resets = <&rstctrl 12>; ++ reset-names = "uart"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <5>; ++ ++ reg-shift = <2>; ++ ++ status = "disabled"; ++ }; ++ ++ gpio0: gpio@600 { ++ compatible = "ralink,mt7620a-gpio", "ralink,rt2880-gpio"; ++ reg = <0x600 0x34>; ++ ++ resets = <&rstctrl 13>; ++ reset-names = "pio"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <6>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ ralink,gpio-base = <0>; ++ ralink,num-gpios = <24>; ++ ralink,register-map = [ 00 04 08 0c ++ 20 24 28 2c ++ 30 34 ]; ++ ++ status = "disabled"; ++ }; ++ ++ gpio1: gpio@638 { ++ compatible = "ralink,mt7620a-gpio", "ralink,rt2880-gpio"; ++ reg = <0x638 0x24>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <6>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ ralink,gpio-base = <24>; ++ ralink,num-gpios = <16>; ++ ralink,register-map = [ 00 04 08 0c ++ 10 14 18 1c ++ 20 24 ]; ++ ++ status = "disabled"; ++ }; ++ ++ gpio2: gpio@660 { ++ compatible = "ralink,mt7620a-gpio", "ralink,rt2880-gpio"; ++ reg = <0x660 0x24>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <6>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ ralink,gpio-base = <40>; ++ ralink,num-gpios = <32>; ++ ralink,register-map = [ 00 04 08 0c ++ 10 14 18 1c ++ 20 24 ]; ++ ++ status = "disabled"; ++ }; ++ ++ i2c@900 { ++ compatible = "link,mt7620a-i2c", "ralink,rt2880-i2c"; ++ reg = <0x900 0x100>; ++ ++ resets = <&rstctrl 16>; ++ reset-names = "i2c"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ spi@b00 { ++ compatible = "ralink,mt7620a-spi", "ralink,rt2880-spi"; ++ reg = <0xb00 0x100>; ++ ++ resets = <&rstctrl 18>; ++ reset-names = "spi"; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ status = "disabled"; + }; + + uartlite@c00 { + compatible = "ralink,mt7620a-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0xc00 0x100>; + ++ resets = <&rstctrl 19>; ++ reset-names = "uartl"; ++ + interrupt-parent = <&intc>; + interrupts = <12>; + + reg-shift = <2>; + }; ++ ++ systick@d00 { ++ compatible = "ralink,mt7620a-systick", "ralink,cevt-systick"; ++ reg = <0xd00 0x10>; ++ ++ resets = <&rstctrl 28>; ++ reset-names = "intc"; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <7>; ++ }; ++ ++ gdma@2800 { ++ compatible = "ralink,mt7620a-gdma", "ralink,rt2880-gdma"; ++ reg = <0x2800 0x800>; ++ ++ resets = <&rstctrl 14>; ++ reset-names = "dma"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <7>; ++ }; ++ }; ++ ++ rstctrl: rstctrl { ++ compatible = "ralink,mt7620a-reset", "ralink,rt2880-reset"; ++ #reset-cells = <1>; ++ }; ++ ++ ubsphy { ++ compatible = "ralink,mt7620a-usbphy"; ++ ++ resets = <&rstctrl 22 &rstctrl 25>; ++ reset-names = "host", "device"; ++ }; ++ ++ ethernet@10100000 { ++ compatible = "ralink,mt7620a-eth"; ++ reg = <0x10100000 10000>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <5>; ++ ++ status = "disabled"; ++ ++ mdio-bus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ }; ++ ++ gsw@10110000 { ++ compatible = "ralink,mt7620a-gsw"; ++ reg = <0x10110000 8000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <17>; ++ ++ status = "disabled"; ++ }; ++ ++ sdhci@10130000 { ++ compatible = "ralink,mt7620a-sdhci"; ++ reg = <0x10130000 4000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <14>; ++ ++ status = "disabled"; ++ }; ++ ++ ehci@101c0000 { ++ compatible = "ralink,rt3xxx-ehci"; ++ reg = <0x101c0000 0x1000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <18>; ++ }; ++ ++ ohci@101c1000 { ++ compatible = "ralink,rt3xxx-ohci"; ++ reg = <0x101c1000 0x1000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <18>; ++ }; ++ ++ pcie@10140000 { ++ compatible = "ralink,mt7620a-pci"; ++ reg = <0x10140000 0x100 ++ 0x10142000 0x100>; ++ ++ resets = <&rstctrl 26>; ++ reset-names = "pcie0"; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <4>; ++ ++ status = "disabled"; + }; + }; +diff --git a/arch/mips/ralink/dts/mt7620a_eval.dts b/arch/mips/ralink/dts/mt7620a_eval.dts +index 35eb874..b56f449 100644 +--- a/arch/mips/ralink/dts/mt7620a_eval.dts ++++ b/arch/mips/ralink/dts/mt7620a_eval.dts +@@ -13,4 +13,115 @@ + chosen { + bootargs = "console=ttyS0,57600"; + }; ++ ++ palmbus@10000000 { ++ sysc@0 { ++ ralink,pinmux = "spi", "uartlite", "mdio", "wled", "ephy", "rgmii1", "rgmii2"; ++ ralink,gpiomux = "i2c", "jtag"; ++ ralink,uartmux = "gpio"; ++ ralink,wdtmux = <1>; ++ }; ++ ++ gpio0: gpio@600 { ++ status = "okay"; ++ }; ++ ++ spi@b00 { ++ status = "okay"; ++ ++ m25p80@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "en25q64"; ++ reg = <0 0>; ++ linux,modalias = "m25p80", "en25q64"; ++ spi-max-frequency = <10000000>; ++ ++ partition@0 { ++ label = "u-boot"; ++ reg = <0x0 0x30000>; ++ read-only; ++ }; ++ ++ partition@30000 { ++ label = "u-boot-env"; ++ reg = <0x30000 0x10000>; ++ read-only; ++ }; ++ ++ factory: partition@40000 { ++ label = "factory"; ++ reg = <0x40000 0x10000>; ++ read-only; ++ }; ++ ++ partition@50000 { ++ label = "firmware"; ++ reg = <0x50000 0x7b0000>; ++ }; ++ }; ++ }; ++ }; ++ ++ ethernet@10100000 { ++ status = "okay"; ++ ++ port@4 { ++ compatible = "lantiq,mt7620a-gsw-port", "ralink,eth-port"; ++ reg = <4>; ++ phy-mode = "rgmii"; ++ phy-handle = <&phy4>; ++ }; ++ ++ port@5 { ++ compatible = "lantiq,mt7620a-gsw-port", "ralink,eth-port"; ++ reg = <5>; ++ phy-mode = "rgmii"; ++ phy-handle = <&phy5>; ++ }; ++ ++ mdio-bus { ++ status = "okay"; ++ ++ phy4: ethernet-phy@4 { ++ reg = <4>; ++ phy-mode = "rgmii"; ++ }; ++ ++ phy5: ethernet-phy@5 { ++ reg = <5>; ++ phy-mode = "rgmii"; ++ }; ++ }; ++ }; ++ ++ gsw@10110000 { ++ status = "okay"; ++ ralink,port4 = "gmac"; ++ }; ++ ++ sdhci@10130000 { ++ status = "okay"; ++ }; ++ ++ pcie@10140000 { ++ status = "okay"; ++ }; ++ ++ gpio-keys-polled { ++ compatible = "gpio-keys"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ poll-interval = <20>; ++ s2 { ++ label = "S2"; ++ gpios = <&gpio0 1 1>; ++ linux,code = <0x100>; ++ }; ++ s3 { ++ label = "S3"; ++ gpios = <&gpio0 2 1>; ++ linux,code = <0x101>; ++ }; ++ }; + }; +diff --git a/arch/mips/ralink/dts/mt7620a_mt7610e_eval.dts b/arch/mips/ralink/dts/mt7620a_mt7610e_eval.dts +new file mode 100644 +index 0000000..0d7755b +--- /dev/null ++++ b/arch/mips/ralink/dts/mt7620a_mt7610e_eval.dts +@@ -0,0 +1,99 @@ ++/dts-v1/; ++ ++/include/ "mt7620a.dtsi" ++ ++/ { ++ compatible = "ralink,mt7620a-eval-board", "ralink,mt7620a-soc"; ++ model = "Ralink MT7620A evaluation board"; ++ ++ memory@0 { ++ reg = <0x0 0x2000000>; ++ }; ++ ++ chosen { ++ bootargs = "console=ttyS0,57600"; ++ }; ++ ++ palmbus@10000000 { ++ sysc@0 { ++ ralink,pinmux = "spi", "uartlite", "mdio", "wled", "ephy", "rgmii1", "rgmii2"; ++ ralink,gpiomux = "i2c", "jtag"; ++ ralink,uartmux = "gpio"; ++ ralink,wdtmux = <1>; ++ }; ++ ++ gpio0: gpio@600 { ++ status = "okay"; ++ }; ++ ++ spi@b00 { ++ status = "okay"; ++ ++ m25p80@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "en25q64"; ++ reg = <0 0>; ++ linux,modalias = "m25p80", "en25q64"; ++ spi-max-frequency = <10000000>; ++ ++ partition@0 { ++ label = "u-boot"; ++ reg = <0x0 0x30000>; ++ read-only; ++ }; ++ ++ partition@30000 { ++ label = "u-boot-env"; ++ reg = <0x30000 0x10000>; ++ read-only; ++ }; ++ ++ factory: partition@40000 { ++ label = "factory"; ++ reg = <0x40000 0x10000>; ++ read-only; ++ }; ++ ++ partition@50000 { ++ label = "firmware"; ++ reg = <0x50000 0x7b0000>; ++ }; ++ }; ++ }; ++ }; ++ ++ ethernet@10100000 { ++ status = "okay"; ++ }; ++ ++ gsw@10110000 { ++ status = "okay"; ++ ralink,port4 = "ephy"; ++ }; ++ ++ sdhci@10130000 { ++ status = "okay"; ++ }; ++ ++ pcie@10140000 { ++ status = "okay"; ++ }; ++ ++ gpio-keys-polled { ++ compatible = "gpio-keys"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ poll-interval = <20>; ++ wps { ++ label = "wps"; ++ gpios = <&gpio0 12 1>; ++ linux,code = <0x100>; ++ }; ++ reset { ++ label = "reset"; ++ gpios = <&gpio0 13 1>; ++ linux,code = <0x101>; ++ }; ++ }; ++}; +diff --git a/arch/mips/ralink/dts/rt2880.dtsi b/arch/mips/ralink/dts/rt2880.dtsi +index 182afde..2a34b8d 100644 +--- a/arch/mips/ralink/dts/rt2880.dtsi ++++ b/arch/mips/ralink/dts/rt2880.dtsi +@@ -55,4 +55,21 @@ + reg-shift = <2>; + }; + }; ++ ++ ethernet@400000 { ++ compatible = "ralink,rt2880-eth"; ++ reg = <0x00400000 10000>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <5>; ++ ++ status = "disabled"; ++ ++ mdio-bus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ }; + }; +diff --git a/arch/mips/ralink/dts/rt2880_eval.dts b/arch/mips/ralink/dts/rt2880_eval.dts +index 322d700..58a1edf 100644 +--- a/arch/mips/ralink/dts/rt2880_eval.dts ++++ b/arch/mips/ralink/dts/rt2880_eval.dts +@@ -43,4 +43,10 @@ + reg = <0x50000 0x3b0000>; + }; + }; ++ ++ ethernet@400000 { ++ status = "okay"; ++ ++ ralink,fixed-link = <1000 1 1 1>; ++ }; + }; +diff --git a/arch/mips/ralink/dts/rt3050.dtsi b/arch/mips/ralink/dts/rt3050.dtsi +index ef7da1e..b1ac940 100644 +--- a/arch/mips/ralink/dts/rt3050.dtsi ++++ b/arch/mips/ralink/dts/rt3050.dtsi +@@ -1,7 +1,7 @@ + / { + #address-cells = <1>; + #size-cells = <1>; +- compatible = "ralink,rt3050-soc", "ralink,rt3052-soc", "ralink,rt3350-soc"; ++ compatible = "ralink,rt3050-soc", "ralink,rt3052-soc"; + + cpus { + cpu@0 { +@@ -45,6 +45,15 @@ + reg = <0x300 0x100>; + }; + ++ i2c@900 { ++ compatible = "link,rt3052-i2c", "ralink,rt2880-i2c"; ++ reg = <0x900 0x100>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ + uartlite@c00 { + compatible = "ralink,rt3052-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0xc00 0x100>; +@@ -55,4 +64,24 @@ + reg-shift = <2>; + }; + }; ++ ++ ethernet@10100000 { ++ compatible = "ralink,rt3050-eth"; ++ reg = <0x10100000 10000>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <5>; ++ ++ status = "disabled"; ++ }; ++ ++ esw@10110000 { ++ compatible = "ralink,rt3050-esw"; ++ reg = <0x10110000 8000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <17>; ++ ++ status = "disabled"; ++ }; + }; +diff --git a/arch/mips/ralink/dts/rt3052_eval.dts b/arch/mips/ralink/dts/rt3052_eval.dts +index df17f5f..df02957 100644 +--- a/arch/mips/ralink/dts/rt3052_eval.dts ++++ b/arch/mips/ralink/dts/rt3052_eval.dts +@@ -3,7 +3,7 @@ + /include/ "rt3050.dtsi" + + / { +- compatible = "ralink,rt3052-eval-board", "ralink,rt3052-soc"; ++ compatible = "ralink,rt3052-eval-board", "ralink,rt3052-soc", "ralink,rt5350-soc"; + model = "Ralink RT3052 evaluation board"; + + memory@0 { +@@ -14,6 +14,14 @@ + bootargs = "console=ttyS0,57600"; + }; + ++ palmbus@10000000 { ++ sysc@0 { ++ ralink,pinmux = "i2c", "spi", "uartlite", "jtag", "mdio", "sdram", "rgmii"; ++ ralink,uartmux = "gpio"; ++ ralink,wdtmux = <1>; ++ }; ++ }; ++ + cfi@1f000000 { + compatible = "cfi-flash"; + reg = <0x1f000000 0x800000>; +@@ -43,4 +51,13 @@ + reg = <0x50000 0x7b0000>; + }; + }; ++ ++ ethernet@10100000 { ++ status = "okay"; ++ }; ++ ++ esw@10110000 { ++ status = "okay"; ++ ralink,portmap = <0x2f>; ++ }; + }; +diff --git a/arch/mips/ralink/dts/rt5350.dtsi b/arch/mips/ralink/dts/rt5350.dtsi +new file mode 100644 +index 0000000..3d6b3bc +--- /dev/null ++++ b/arch/mips/ralink/dts/rt5350.dtsi +@@ -0,0 +1,227 @@ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "ralink,rt5350-soc"; ++ ++ cpus { ++ cpu@0 { ++ compatible = "mips,mips24KEc"; ++ }; ++ }; ++ ++ cpuintc: cpuintc@0 { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ compatible = "mti,cpu-interrupt-controller"; ++ }; ++ ++ palmbus@10000000 { ++ compatible = "palmbus"; ++ reg = <0x10000000 0x200000>; ++ ranges = <0x0 0x10000000 0x1FFFFF>; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ sysc@0 { ++ compatible = "ralink,rt5350-sysc", "ralink,rt3050-sysc"; ++ reg = <0x0 0x100>; ++ }; ++ ++ timer@100 { ++ compatible = "ralink,rt5350-timer", "ralink,rt2880-timer"; ++ reg = <0x100 0x20>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <1>; ++ }; ++ ++ watchdog@120 { ++ compatible = "ralink,rt5350-wdt", "ralink,rt2880-wdt"; ++ reg = <0x120 0x10>; ++ ++ resets = <&rstctrl 8>; ++ reset-names = "wdt"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <1>; ++ }; ++ ++ intc: intc@200 { ++ compatible = "ralink,rt5350-intc", "ralink,rt2880-intc"; ++ reg = <0x200 0x100>; ++ ++ resets = <&rstctrl 19>; ++ reset-names = "intc"; ++ ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <2>; ++ }; ++ ++ memc@300 { ++ compatible = "ralink,rt5350-memc", "ralink,rt3050-memc"; ++ reg = <0x300 0x100>; ++ ++ resets = <&rstctrl 20>; ++ reset-names = "mc"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <3>; ++ }; ++ ++ uart@500 { ++ compatible = "ralink,rt5350-uart", "ralink,rt2880-uart", "ns16550a"; ++ reg = <0x500 0x100>; ++ ++ resets = <&rstctrl 12>; ++ reset-names = "uart"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <5>; ++ ++ reg-shift = <2>; ++ ++ status = "disabled"; ++ }; ++ ++ gpio0: gpio@600 { ++ compatible = "ralink,rt5350-gpio", "ralink,rt2880-gpio"; ++ reg = <0x600 0x34>; ++ ++ resets = <&rstctrl 13>; ++ reset-names = "pio"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <6>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ ralink,gpio-base = <0>; ++ ralink,num-gpios = <24>; ++ ralink,register-map = [ 00 04 08 0c ++ 20 24 28 2c ++ 30 34 ]; ++ ++ status = "disabled"; ++ }; ++ ++ gpio1: gpio@638 { ++ compatible = "ralink,rt5350-gpio", "ralink,rt2880-gpio"; ++ reg = <0x638 0x24>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <6>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ ralink,gpio-base = <24>; ++ ralink,num-gpios = <16>; ++ ralink,register-map = [ 00 04 08 0c ++ 10 14 18 1c ++ 20 24 ]; ++ ++ status = "disabled"; ++ }; ++ ++ i2c@900 { ++ compatible = "link,rt5350-i2c", "ralink,rt2880-i2c"; ++ reg = <0x900 0x100>; ++ ++ resets = <&rstctrl 16>; ++ reset-names = "i2c"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ spi@b00 { ++ compatible = "ralink,rt5350-spi", "ralink,rt2880-spi"; ++ reg = <0xb00 0x100>; ++ ++ resets = <&rstctrl 18>; ++ reset-names = "spi"; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ status = "disabled"; ++ }; ++ ++ uartlite@c00 { ++ compatible = "ralink,rt5350-uart", "ralink,rt2880-uart", "ns16550a"; ++ reg = <0xc00 0x100>; ++ ++ resets = <&rstctrl 19>; ++ reset-names = "uartl"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <12>; ++ ++ reg-shift = <2>; ++ }; ++ ++ systick@d00 { ++ compatible = "ralink,rt5350-systick", "ralink,cevt-systick"; ++ reg = <0xd00 0x10>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <7>; ++ }; ++ }; ++ ++ rstctrl: rstctrl { ++ compatible = "ralink,rt5350-reset", "ralink,rt2880-reset"; ++ #reset-cells = <1>; ++ }; ++ ++ ubsphy { ++ compatible = "ralink,rt3xxx-usbphy"; ++ ++ resets = <&rstctrl 22 &rstctrl 25>; ++ reset-names = "host", "device"; ++ }; ++ ++ ethernet@10100000 { ++ compatible = "ralink,rt5350-eth"; ++ reg = <0x10100000 10000>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <5>; ++ ++ status = "disabled"; ++ }; ++ ++ esw@10110000 { ++ compatible = "ralink,rt3050-esw"; ++ reg = <0x10110000 8000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <17>; ++ ++ status = "disabled"; ++ }; ++ ++ ehci@101c0000 { ++ compatible = "ralink,rt3xxx-ehci"; ++ reg = <0x101c0000 0x1000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <18>; ++ }; ++ ++ ohci@101c1000 { ++ compatible = "ralink,rt3xxx-ohci"; ++ reg = <0x101c1000 0x1000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <18>; ++ }; ++}; +diff --git a/arch/mips/ralink/dts/rt5350_eval.dts b/arch/mips/ralink/dts/rt5350_eval.dts +new file mode 100644 +index 0000000..ab92043 +--- /dev/null ++++ b/arch/mips/ralink/dts/rt5350_eval.dts +@@ -0,0 +1,69 @@ ++/dts-v1/; ++ ++/include/ "rt5350.dtsi" ++ ++/ { ++ compatible = "ralink,rt5350-eval-board", "ralink,rt5350-soc"; ++ model = "Ralink RT5350 evaluation board"; ++ ++ chosen { ++ bootargs = "console=ttyS0,57600"; ++ }; ++ ++ palmbus@10000000 { ++ sysc@0 { ++ ralink,pinmux = "i2c", "spi", "uartlite", "jtag", "mdio", "sdram", "rgmii"; ++ ralink,uartmux = "gpio"; ++ ralink,wdtmux = <1>; ++ }; ++ ++ gpio0: gpio@600 { ++ status = "okay"; ++ }; ++ ++ spi@b00 { ++ status = "okay"; ++ ++ m25p80@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "en25q64"; ++ reg = <0 0>; ++ linux,modalias = "m25p80", "mx25l3205d"; ++ spi-max-frequency = <10000000>; ++ ++ partition@0 { ++ label = "u-boot"; ++ reg = <0x0 0x30000>; ++ read-only; ++ }; ++ ++ partition@30000 { ++ label = "u-boot-env"; ++ reg = <0x30000 0x10000>; ++ read-only; ++ }; ++ ++ factory: partition@40000 { ++ label = "factory"; ++ reg = <0x40000 0x10000>; ++ read-only; ++ }; ++ ++ partition@50000 { ++ label = "firmware"; ++ reg = <0x50000 0x3b0000>; ++ }; ++ }; ++ }; ++ }; ++ ++ ethernet@10100000 { ++ status = "okay"; ++ }; ++ ++ esw@10110000 { ++ status = "okay"; ++ ralink,portmap = <0x2f>; ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0057-GPIO-MIPS-ralink-adds-ralink-gpio-support.patch b/target/linux/ramips/patches-3.8/0057-GPIO-MIPS-ralink-adds-ralink-gpio-support.patch new file mode 100644 index 0000000000..3149ac2731 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0057-GPIO-MIPS-ralink-adds-ralink-gpio-support.patch @@ -0,0 +1,425 @@ +From de5e1cd11d62d2e1a00210b757dad35e1372963b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 22 Jan 2013 18:24:34 +0100 +Subject: [PATCH 57/79] GPIO: MIPS: ralink: adds ralink gpio support + +Add gpio driver for Ralink SoC. This driver makes the gpio core on +RT2880, RT305x, rt3352, rt3662, rt3883, rt5350 and mt7620 work. + +Signed-off-by: John Crispin +--- + arch/mips/Kconfig | 1 + + arch/mips/include/asm/mach-ralink/gpio.h | 24 +++ + drivers/gpio/Kconfig | 6 + + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-ralink.c | 326 ++++++++++++++++++++++++++++++ + 5 files changed, 358 insertions(+) + create mode 100644 arch/mips/include/asm/mach-ralink/gpio.h + create mode 100644 drivers/gpio/gpio-ralink.c + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index b5fd476..2498972 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -449,6 +449,7 @@ config RALINK + select SYS_HAS_EARLY_PRINTK + select HAVE_MACH_CLKDEV + select CLKDEV_LOOKUP ++ select ARCH_REQUIRE_GPIOLIB + + config SGI_IP22 + bool "SGI IP22 (Indy/Indigo2)" +diff --git a/arch/mips/include/asm/mach-ralink/gpio.h b/arch/mips/include/asm/mach-ralink/gpio.h +new file mode 100644 +index 0000000..f68ee16 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/gpio.h +@@ -0,0 +1,24 @@ ++/* ++ * Ralink SoC GPIO API support ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 __ASM_MACH_RALINK_GPIO_H ++#define __ASM_MACH_RALINK_GPIO_H ++ ++#define ARCH_NR_GPIOS 128 ++#include ++ ++#define gpio_get_value __gpio_get_value ++#define gpio_set_value __gpio_set_value ++#define gpio_cansleep __gpio_cansleep ++#define gpio_to_irq __gpio_to_irq ++ ++#endif /* __ASM_MACH_RALINK_GPIO_H */ +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 682de75..2e6e81c 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -201,6 +201,12 @@ config GPIO_PXA + help + Say yes here to support the PXA GPIO device + ++config GPIO_RALINK ++ bool "Ralink GPIO Support" ++ depends on RALINK ++ help ++ Say yes here to support the Ralink SoC GPIO device ++ + config GPIO_SPEAR_SPICS + bool "ST SPEAr13xx SPI Chip Select as GPIO support" + depends on PLAT_SPEAR +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +index c5aebd0..a00adfc 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -54,6 +54,7 @@ obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o + obj-$(CONFIG_GPIO_PCH) += gpio-pch.o + obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o + obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o ++obj-$(CONFIG_GPIO_RALINK) += gpio-ralink.o + obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o + obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o + obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o +diff --git a/drivers/gpio/gpio-ralink.c b/drivers/gpio/gpio-ralink.c +new file mode 100644 +index 0000000..12984f1 +--- /dev/null ++++ b/drivers/gpio/gpio-ralink.c +@@ -0,0 +1,326 @@ ++/* ++ * 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. ++ * ++ * Copyright (C) 2009-2011 Gabor Juhos ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++enum ralink_gpio_reg { ++ GPIO_REG_INT = 0, ++ GPIO_REG_EDGE, ++ GPIO_REG_RENA, ++ GPIO_REG_FENA, ++ GPIO_REG_DATA, ++ GPIO_REG_DIR, ++ GPIO_REG_POL, ++ GPIO_REG_SET, ++ GPIO_REG_RESET, ++ GPIO_REG_TOGGLE, ++ GPIO_REG_MAX ++}; ++ ++struct ralink_gpio_chip { ++ struct gpio_chip chip; ++ u8 regs[GPIO_REG_MAX]; ++ ++ spinlock_t lock; ++ void __iomem *membase; ++ struct irq_domain *domain; ++ int irq; ++ ++ u32 rising; ++ u32 falling; ++}; ++ ++#define MAP_MAX 4 ++static struct irq_domain *irq_map[MAP_MAX]; ++static int irq_map_count; ++static atomic_t irq_refcount = ATOMIC_INIT(0); ++ ++static inline struct ralink_gpio_chip *to_ralink_gpio(struct gpio_chip *chip) ++{ ++ struct ralink_gpio_chip *rg; ++ ++ rg = container_of(chip, struct ralink_gpio_chip, chip); ++ ++ return rg; ++} ++ ++static inline void rt_gpio_w32(struct ralink_gpio_chip *rg, u8 reg, u32 val) ++{ ++ iowrite32(val, rg->membase + rg->regs[reg]); ++} ++ ++static inline u32 rt_gpio_r32(struct ralink_gpio_chip *rg, u8 reg) ++{ ++ return ioread32(rg->membase + rg->regs[reg]); ++} ++ ++static void ralink_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ ++ rt_gpio_w32(rg, (value) ? GPIO_REG_SET : GPIO_REG_RESET, BIT(offset)); ++} ++ ++static int ralink_gpio_get(struct gpio_chip *chip, unsigned offset) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ ++ return !!(rt_gpio_r32(rg, GPIO_REG_DATA) & BIT(offset)); ++} ++ ++static int ralink_gpio_direction_input(struct gpio_chip *chip, unsigned offset) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ unsigned long flags; ++ u32 t; ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ t = rt_gpio_r32(rg, GPIO_REG_DIR); ++ t &= ~BIT(offset); ++ rt_gpio_w32(rg, GPIO_REG_DIR, t); ++ spin_unlock_irqrestore(&rg->lock, flags); ++ ++ return 0; ++} ++ ++static int ralink_gpio_direction_output(struct gpio_chip *chip, ++ unsigned offset, int value) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ unsigned long flags; ++ u32 t; ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ ralink_gpio_set(chip, offset, value); ++ t = rt_gpio_r32(rg, GPIO_REG_DIR); ++ t |= BIT(offset); ++ rt_gpio_w32(rg, GPIO_REG_DIR, t); ++ spin_unlock_irqrestore(&rg->lock, flags); ++ ++ return 0; ++} ++ ++static int ralink_gpio_to_irq(struct gpio_chip *chip, unsigned pin) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ ++ if (rg->irq < 1) ++ return -1; ++ ++ ralink_gpio_direction_input(chip, pin); ++ ++ return irq_create_mapping(rg->domain, pin); ++} ++ ++static void ralink_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) ++{ ++ int i; ++ ++ for (i = 0; i < irq_map_count; i++) { ++ struct irq_domain *domain = irq_map[i]; ++ struct ralink_gpio_chip *rg = (struct ralink_gpio_chip *) domain->host_data; ++ unsigned long pending = rt_gpio_r32(rg, GPIO_REG_INT); ++ int bit; ++ ++ for_each_set_bit(bit, &pending, rg->chip.ngpio) { ++ u32 map = irq_find_mapping(domain, bit); ++ generic_handle_irq(map); ++ rt_gpio_w32(rg, GPIO_REG_INT, BIT(bit)); ++ } ++ } ++} ++ ++static void ralink_gpio_irq_unmask(struct irq_data *d) ++{ ++ struct ralink_gpio_chip *rg = (struct ralink_gpio_chip *) d->domain->host_data; ++ u32 val = rt_gpio_r32(rg, GPIO_REG_RENA); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ rt_gpio_w32(rg, GPIO_REG_RENA, val | (BIT(d->hwirq) & rg->rising)); ++ rt_gpio_w32(rg, GPIO_REG_FENA, val | (BIT(d->hwirq) & rg->falling)); ++ spin_unlock_irqrestore(&rg->lock, flags); ++} ++ ++static void ralink_gpio_irq_mask(struct irq_data *d) ++{ ++ struct ralink_gpio_chip *rg = (struct ralink_gpio_chip *) d->domain->host_data; ++ u32 val = rt_gpio_r32(rg, GPIO_REG_RENA); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ rt_gpio_w32(rg, GPIO_REG_FENA, val & ~BIT(d->hwirq)); ++ rt_gpio_w32(rg, GPIO_REG_RENA, val & ~BIT(d->hwirq)); ++ spin_unlock_irqrestore(&rg->lock, flags); ++} ++ ++static int ralink_gpio_irq_type(struct irq_data *d, unsigned int type) ++{ ++ struct ralink_gpio_chip *rg = (struct ralink_gpio_chip *) d->domain->host_data; ++ u32 mask = BIT(d->hwirq); ++ ++ if (type == IRQ_TYPE_PROBE) { ++ if ((rg->rising | rg->falling) & mask) ++ return 0; ++ ++ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_RISING; ++ } ++ ++ if (type & IRQ_TYPE_EDGE_RISING) ++ rg->rising |= mask; ++ else ++ rg->rising &= mask; ++ ++ if (type & IRQ_TYPE_EDGE_RISING) ++ rg->falling |= mask; ++ else ++ rg->falling &= mask; ++ ++ return 0; ++} ++ ++static struct irq_chip ralink_gpio_irq_chip = { ++ .name = "GPIO", ++ .irq_unmask = ralink_gpio_irq_unmask, ++ .irq_mask = ralink_gpio_irq_mask, ++ .irq_mask_ack = ralink_gpio_irq_mask, ++ .irq_set_type = ralink_gpio_irq_type, ++}; ++ ++static int gpio_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) ++{ ++ irq_set_chip_and_handler(irq, &ralink_gpio_irq_chip, handle_level_irq); ++ irq_set_handler_data(irq, d); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops irq_domain_ops = { ++ .xlate = irq_domain_xlate_onecell, ++ .map = gpio_map, ++}; ++ ++static void ralink_gpio_irq_init(struct device_node *np, struct ralink_gpio_chip *rg) ++{ ++ if (irq_map_count >= MAP_MAX) ++ return; ++ ++ rg->irq = irq_of_parse_and_map(np, 0); ++ if (!rg->irq) ++ return; ++ ++ rg->domain = irq_domain_add_linear(np, rg->chip.ngpio, &irq_domain_ops, rg); ++ if (!rg->domain) { ++ dev_err(rg->chip.dev, "irq_domain_add_linear failed\n"); ++ return; ++ } ++ ++ irq_map[irq_map_count++] = rg->domain; ++ ++ rt_gpio_w32(rg, GPIO_REG_RENA, 0x0); ++ rt_gpio_w32(rg, GPIO_REG_FENA, 0x0); ++ ++ if (!atomic_read(&irq_refcount)) ++ irq_set_chained_handler(rg->irq, ralink_gpio_irq_handler); ++ atomic_inc(&irq_refcount); ++ ++ dev_info(rg->chip.dev, "registering %d irq handlers\n", rg->chip.ngpio); ++} ++ ++static int ralink_gpio_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ struct ralink_gpio_chip *rg; ++ const __be32 *ngpio, *gpiobase; ++ ++ if (!res) { ++ dev_err(&pdev->dev, "failed to find resource\n"); ++ return -ENOMEM; ++ } ++ ++ rg = devm_kzalloc(&pdev->dev, ++ sizeof(struct ralink_gpio_chip), GFP_KERNEL); ++ if (!rg) ++ return -ENOMEM; ++ ++ rg->membase = devm_request_and_ioremap(&pdev->dev, res); ++ if (!rg->membase) { ++ dev_err(&pdev->dev, "cannot remap I/O memory region\n"); ++ return -ENOMEM; ++ } ++ ++ if (of_property_read_u8_array(np, "ralink,register-map", ++ rg->regs, GPIO_REG_MAX)) { ++ dev_err(&pdev->dev, "failed to read register definition\n"); ++ return -EINVAL; ++ } ++ ++ ngpio = of_get_property(np, "ralink,num-gpios", NULL); ++ if (!ngpio) { ++ dev_err(&pdev->dev, "failed to read number of pins\n"); ++ return -EINVAL; ++ } ++ ++ gpiobase = of_get_property(np, "ralink,gpio-base", NULL); ++ if (gpiobase) ++ rg->chip.base = be32_to_cpu(*gpiobase); ++ else ++ rg->chip.base = -1; ++ ++ spin_lock_init(&rg->lock); ++ ++ rg->chip.dev = &pdev->dev; ++ rg->chip.label = dev_name(&pdev->dev); ++ rg->chip.of_node = np; ++ rg->chip.ngpio = be32_to_cpu(*ngpio); ++ rg->chip.direction_input = ralink_gpio_direction_input; ++ rg->chip.direction_output = ralink_gpio_direction_output; ++ rg->chip.get = ralink_gpio_get; ++ rg->chip.set = ralink_gpio_set; ++ rg->chip.to_irq = ralink_gpio_to_irq; ++ ++ /* set polarity to low for all lines */ ++ rt_gpio_w32(rg, GPIO_REG_POL, 0); ++ ++ dev_info(&pdev->dev, "registering %d gpios\n", rg->chip.ngpio); ++ ++ ralink_gpio_irq_init(np, rg); ++ ++ return gpiochip_add(&rg->chip); ++} ++ ++static const struct of_device_id ralink_gpio_match[] = { ++ { .compatible = "ralink,rt2880-gpio" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ralink_gpio_match); ++ ++static struct platform_driver ralink_gpio_driver = { ++ .probe = ralink_gpio_probe, ++ .driver = { ++ .name = "rt2880_gpio", ++ .owner = THIS_MODULE, ++ .of_match_table = ralink_gpio_match, ++ }, ++}; ++ ++static int __init ralink_gpio_init(void) ++{ ++ return platform_driver_register(&ralink_gpio_driver); ++} ++ ++subsys_initcall(ralink_gpio_init); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0058-SPI-ralink-add-Ralink-SoC-spi-driver.patch b/target/linux/ramips/patches-3.8/0058-SPI-ralink-add-Ralink-SoC-spi-driver.patch new file mode 100644 index 0000000000..f1706f5742 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0058-SPI-ralink-add-Ralink-SoC-spi-driver.patch @@ -0,0 +1,528 @@ +From cfb23fbd5be0276507a8af15be0738a7834555b5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Apr 2013 23:16:18 +0200 +Subject: [PATCH 58/79] SPI: ralink: add Ralink SoC spi driver + +Add the driver needed to make SPI work on Ralink SoC. + +Signed-off-by: John Crispin +--- + drivers/spi/Kconfig | 6 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-ralink.c | 475 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 482 insertions(+) + create mode 100644 drivers/spi/spi-ralink.c + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 2e188e1..76c489a 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -315,6 +315,12 @@ config SPI_RSPI + help + SPI driver for Renesas RSPI blocks. + ++config SPI_RALINK ++ tristate "Ralink RT288x/RT305x/RT3662 SPI Controller" ++ depends on (SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620) ++ help ++ This selects a driver for the Ralink RT288x/RT305x SPI Controller. ++ + config SPI_S3C24XX + tristate "Samsung S3C24XX series SPI" + depends on ARCH_S3C24XX && EXPERIMENTAL +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index 64e970b..5b73cf8 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -50,6 +50,7 @@ obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o + obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx.o + obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o + obj-$(CONFIG_SPI_RSPI) += spi-rspi.o ++obj-$(CONFIG_SPI_RALINK) += spi-ralink.o + obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o + spi-s3c24xx-hw-y := spi-s3c24xx.o + spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o +diff --git a/drivers/spi/spi-ralink.c b/drivers/spi/spi-ralink.c +new file mode 100644 +index 0000000..b07cbaa +--- /dev/null ++++ b/drivers/spi/spi-ralink.c +@@ -0,0 +1,475 @@ ++/* ++ * spi-ralink.c -- Ralink RT288x/RT305x SPI controller driver ++ * ++ * Copyright (C) 2011 Sergiy ++ * Copyright (C) 2011-2013 Gabor Juhos ++ * ++ * Some parts are based on spi-orion.c: ++ * Author: Shadi Ammouri ++ * Copyright (C) 2007-2008 Marvell Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "spi-ralink" ++#define RALINK_NUM_CHIPSELECTS 1 /* only one slave is supported*/ ++#define RALINK_SPI_WAIT_RDY_MAX_LOOP 2000 /* in usec */ ++ ++#define RAMIPS_SPI_STAT 0x00 ++#define RAMIPS_SPI_CFG 0x10 ++#define RAMIPS_SPI_CTL 0x14 ++#define RAMIPS_SPI_DATA 0x20 ++ ++/* SPISTAT register bit field */ ++#define SPISTAT_BUSY BIT(0) ++ ++/* SPICFG register bit field */ ++#define SPICFG_LSBFIRST 0 ++#define SPICFG_MSBFIRST BIT(8) ++#define SPICFG_SPICLKPOL BIT(6) ++#define SPICFG_RXCLKEDGE_FALLING BIT(5) ++#define SPICFG_TXCLKEDGE_FALLING BIT(4) ++#define SPICFG_SPICLK_PRESCALE_MASK 0x7 ++#define SPICFG_SPICLK_DIV2 0 ++#define SPICFG_SPICLK_DIV4 1 ++#define SPICFG_SPICLK_DIV8 2 ++#define SPICFG_SPICLK_DIV16 3 ++#define SPICFG_SPICLK_DIV32 4 ++#define SPICFG_SPICLK_DIV64 5 ++#define SPICFG_SPICLK_DIV128 6 ++#define SPICFG_SPICLK_DISABLE 7 ++ ++/* SPICTL register bit field */ ++#define SPICTL_HIZSDO BIT(3) ++#define SPICTL_STARTWR BIT(2) ++#define SPICTL_STARTRD BIT(1) ++#define SPICTL_SPIENA BIT(0) ++ ++#ifdef DEBUG ++#define spi_debug(args...) printk(args) ++#else ++#define spi_debug(args...) ++#endif ++ ++struct ralink_spi { ++ struct spi_master *master; ++ void __iomem *base; ++ unsigned int sys_freq; ++ unsigned int speed; ++ struct clk *clk; ++}; ++ ++static inline struct ralink_spi *spidev_to_ralink_spi(struct spi_device *spi) ++{ ++ return spi_master_get_devdata(spi->master); ++} ++ ++static inline u32 ralink_spi_read(struct ralink_spi *rs, u32 reg) ++{ ++ return ioread32(rs->base + reg); ++} ++ ++static inline void ralink_spi_write(struct ralink_spi *rs, u32 reg, u32 val) ++{ ++ iowrite32(val, rs->base + reg); ++} ++ ++static inline void ralink_spi_setbits(struct ralink_spi *rs, u32 reg, u32 mask) ++{ ++ void __iomem *addr = rs->base + reg; ++ u32 val; ++ ++ val = ioread32(addr); ++ val |= mask; ++ iowrite32(val, addr); ++} ++ ++static inline void ralink_spi_clrbits(struct ralink_spi *rs, u32 reg, u32 mask) ++{ ++ void __iomem *addr = rs->base + reg; ++ u32 val; ++ ++ val = ioread32(addr); ++ val &= ~mask; ++ iowrite32(val, addr); ++} ++ ++static int ralink_spi_baudrate_set(struct spi_device *spi, unsigned int speed) ++{ ++ struct ralink_spi *rs = spidev_to_ralink_spi(spi); ++ u32 rate; ++ u32 prescale; ++ u32 reg; ++ ++ spi_debug("%s: speed:%u\n", __func__, speed); ++ ++ /* ++ * the supported rates are: 2, 4, 8, ... 128 ++ * round up as we look for equal or less speed ++ */ ++ rate = DIV_ROUND_UP(rs->sys_freq, speed); ++ spi_debug("%s: rate-1:%u\n", __func__, rate); ++ rate = roundup_pow_of_two(rate); ++ spi_debug("%s: rate-2:%u\n", __func__, rate); ++ ++ /* check if requested speed is too small */ ++ if (rate > 128) ++ return -EINVAL; ++ ++ if (rate < 2) ++ rate = 2; ++ ++ /* Convert the rate to SPI clock divisor value. */ ++ prescale = ilog2(rate/2); ++ spi_debug("%s: prescale:%u\n", __func__, prescale); ++ ++ reg = ralink_spi_read(rs, RAMIPS_SPI_CFG); ++ reg = ((reg & ~SPICFG_SPICLK_PRESCALE_MASK) | prescale); ++ ralink_spi_write(rs, RAMIPS_SPI_CFG, reg); ++ rs->speed = speed; ++ return 0; ++} ++ ++/* ++ * called only when no transfer is active on the bus ++ */ ++static int ++ralink_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) ++{ ++ struct ralink_spi *rs = spidev_to_ralink_spi(spi); ++ unsigned int speed = spi->max_speed_hz; ++ int rc; ++ unsigned int bits_per_word = 8; ++ ++ if ((t != NULL) && t->speed_hz) ++ speed = t->speed_hz; ++ ++ if ((t != NULL) && t->bits_per_word) ++ bits_per_word = t->bits_per_word; ++ ++ if (rs->speed != speed) { ++ spi_debug("%s: speed_hz:%u\n", __func__, speed); ++ rc = ralink_spi_baudrate_set(spi, speed); ++ if (rc) ++ return rc; ++ } ++ ++ if (bits_per_word != 8) { ++ spi_debug("%s: bad bits_per_word: %u\n", __func__, ++ bits_per_word); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void ralink_spi_set_cs(struct ralink_spi *rs, int enable) ++{ ++ if (enable) ++ ralink_spi_clrbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA); ++ else ++ ralink_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA); ++} ++ ++static inline int ralink_spi_wait_till_ready(struct ralink_spi *rs) ++{ ++ int i; ++ ++ for (i = 0; i < RALINK_SPI_WAIT_RDY_MAX_LOOP; i++) { ++ u32 status; ++ ++ status = ralink_spi_read(rs, RAMIPS_SPI_STAT); ++ if ((status & SPISTAT_BUSY) == 0) ++ return 0; ++ ++ udelay(1); ++ } ++ ++ return -ETIMEDOUT; ++} ++ ++static unsigned int ++ralink_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer) ++{ ++ struct ralink_spi *rs = spidev_to_ralink_spi(spi); ++ unsigned count = 0; ++ u8 *rx = xfer->rx_buf; ++ const u8 *tx = xfer->tx_buf; ++ int err; ++ ++ spi_debug("%s(%d): %s %s\n", __func__, xfer->len, ++ (tx != NULL) ? "tx" : " ", ++ (rx != NULL) ? "rx" : " "); ++ ++ if (tx) { ++ for (count = 0; count < xfer->len; count++) { ++ ralink_spi_write(rs, RAMIPS_SPI_DATA, tx[count]); ++ ralink_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTWR); ++ err = ralink_spi_wait_till_ready(rs); ++ if (err) { ++ dev_err(&spi->dev, "TX failed, err=%d\n", err); ++ goto out; ++ } ++ } ++ } ++ ++ if (rx) { ++ for (count = 0; count < xfer->len; count++) { ++ ralink_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTRD); ++ err = ralink_spi_wait_till_ready(rs); ++ if (err) { ++ dev_err(&spi->dev, "RX failed, err=%d\n", err); ++ goto out; ++ } ++ rx[count] = (u8) ralink_spi_read(rs, RAMIPS_SPI_DATA); ++ } ++ } ++ ++out: ++ return count; ++} ++ ++static int ralink_spi_transfer_one_message(struct spi_master *master, ++ struct spi_message *m) ++{ ++ struct ralink_spi *rs = spi_master_get_devdata(master); ++ struct spi_device *spi = m->spi; ++ struct spi_transfer *t = NULL; ++ int par_override = 0; ++ int status = 0; ++ int cs_active = 0; ++ ++ /* Load defaults */ ++ status = ralink_spi_setup_transfer(spi, NULL); ++ if (status < 0) ++ goto msg_done; ++ ++ list_for_each_entry(t, &m->transfers, transfer_list) { ++ unsigned int bits_per_word = spi->bits_per_word; ++ ++ if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { ++ dev_err(&spi->dev, ++ "message rejected: invalid transfer data buffers\n"); ++ status = -EIO; ++ goto msg_done; ++ } ++ ++ if (t->bits_per_word) ++ bits_per_word = t->bits_per_word; ++ ++ if (bits_per_word != 8) { ++ dev_err(&spi->dev, ++ "message rejected: invalid transfer bits_per_word (%d bits)\n", ++ bits_per_word); ++ status = -EIO; ++ goto msg_done; ++ } ++ ++ if (t->speed_hz && t->speed_hz < (rs->sys_freq / 128)) { ++ dev_err(&spi->dev, ++ "message rejected: device min speed (%d Hz) exceeds required transfer speed (%d Hz)\n", ++ (rs->sys_freq / 128), t->speed_hz); ++ status = -EIO; ++ goto msg_done; ++ } ++ ++ if (par_override || t->speed_hz || t->bits_per_word) { ++ par_override = 1; ++ status = ralink_spi_setup_transfer(spi, t); ++ if (status < 0) ++ goto msg_done; ++ if (!t->speed_hz && !t->bits_per_word) ++ par_override = 0; ++ } ++ ++ if (!cs_active) { ++ ralink_spi_set_cs(rs, 1); ++ cs_active = 1; ++ } ++ ++ if (t->len) ++ m->actual_length += ralink_spi_write_read(spi, t); ++ ++ if (t->delay_usecs) ++ udelay(t->delay_usecs); ++ ++ if (t->cs_change) { ++ ralink_spi_set_cs(rs, 0); ++ cs_active = 0; ++ } ++ } ++ ++msg_done: ++ if (cs_active) ++ ralink_spi_set_cs(rs, 0); ++ ++ m->status = status; ++ spi_finalize_current_message(master); ++ ++ return 0; ++} ++ ++static int ralink_spi_setup(struct spi_device *spi) ++{ ++ struct ralink_spi *rs = spidev_to_ralink_spi(spi); ++ ++ if ((spi->max_speed_hz == 0) || ++ (spi->max_speed_hz > (rs->sys_freq / 2))) ++ spi->max_speed_hz = (rs->sys_freq / 2); ++ ++ if (spi->max_speed_hz < (rs->sys_freq / 128)) { ++ dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n", ++ spi->max_speed_hz); ++ return -EINVAL; ++ } ++ ++ if (spi->bits_per_word != 0 && spi->bits_per_word != 8) { ++ dev_err(&spi->dev, ++ "setup: requested bits per words - os wrong %d bpw\n", ++ spi->bits_per_word); ++ return -EINVAL; ++ } ++ ++ if (spi->bits_per_word == 0) ++ spi->bits_per_word = 8; ++ ++ /* ++ * baudrate & width will be set ralink_spi_setup_transfer ++ */ ++ return 0; ++} ++ ++static void ralink_spi_reset(struct ralink_spi *rs) ++{ ++ ralink_spi_write(rs, RAMIPS_SPI_CFG, ++ SPICFG_MSBFIRST | SPICFG_TXCLKEDGE_FALLING | ++ SPICFG_SPICLK_DIV16 | SPICFG_SPICLKPOL); ++ ralink_spi_write(rs, RAMIPS_SPI_CTL, SPICTL_HIZSDO | SPICTL_SPIENA); ++} ++ ++static int ralink_spi_probe(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct ralink_spi *rs; ++ struct resource *r; ++ int status = 0; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*rs)); ++ if (master == NULL) { ++ dev_dbg(&pdev->dev, "master allocation failed\n"); ++ return -ENOMEM; ++ } ++ ++ //if (pdev->id != -1) ++ master->bus_num = 0; ++ ++ /* we support only mode 0, and no options */ ++ master->mode_bits = 0; ++ ++ master->setup = ralink_spi_setup; ++ master->transfer_one_message = ralink_spi_transfer_one_message; ++ master->num_chipselect = RALINK_NUM_CHIPSELECTS; ++ master->dev.of_node = pdev->dev.of_node; ++ ++ dev_set_drvdata(&pdev->dev, master); ++ ++ rs = spi_master_get_devdata(master); ++ rs->master = master; ++ ++ rs->clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(rs->clk)) { ++ status = PTR_ERR(rs->clk); ++ dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n", ++ status); ++ goto out_put_master; ++ } ++ ++ status = clk_enable(rs->clk); ++ if (status) ++ goto out_put_clk; ++ ++ rs->sys_freq = clk_get_rate(rs->clk); ++ spi_debug("%s: sys_freq: %u\n", __func__, rs->sys_freq); ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (r == NULL) { ++ status = -ENODEV; ++ goto out_disable_clk; ++ } ++ ++ rs->base = devm_request_and_ioremap(&pdev->dev, r); ++ if (!rs->base) { ++ status = -EADDRNOTAVAIL; ++ goto out_disable_clk; ++ } ++ ++ device_reset(&pdev->dev); ++ ++ ralink_spi_reset(rs); ++ ++ status = spi_register_master(master); ++ if (status) ++ goto out_disable_clk; ++ ++ return 0; ++ ++out_disable_clk: ++ clk_disable(rs->clk); ++out_put_clk: ++ clk_put(rs->clk); ++out_put_master: ++ spi_master_put(master); ++ return status; ++} ++ ++static int ralink_spi_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct ralink_spi *rs; ++ ++ master = dev_get_drvdata(&pdev->dev); ++ rs = spi_master_get_devdata(master); ++ ++ clk_disable(rs->clk); ++ clk_put(rs->clk); ++ spi_unregister_master(master); ++ ++ return 0; ++} ++ ++MODULE_ALIAS("platform:" DRIVER_NAME); ++ ++static const struct of_device_id ralink_spi_match[] = { ++ { .compatible = "ralink,rt2880-spi" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ralink_spi_match); ++ ++static struct platform_driver ralink_spi_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = ralink_spi_match, ++ }, ++ .probe = ralink_spi_probe, ++ .remove = ralink_spi_remove, ++}; ++ ++module_platform_driver(ralink_spi_driver); ++ ++MODULE_DESCRIPTION("Ralink SPI driver"); ++MODULE_AUTHOR("Sergiy "); ++MODULE_AUTHOR("Gabor Juhos "); ++MODULE_LICENSE("GPL"); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0059-serial-of-allow-au1x00-and-rt288x-to-load-from-OF.patch b/target/linux/ramips/patches-3.8/0059-serial-of-allow-au1x00-and-rt288x-to-load-from-OF.patch new file mode 100644 index 0000000000..16a82cae4e --- /dev/null +++ b/target/linux/ramips/patches-3.8/0059-serial-of-allow-au1x00-and-rt288x-to-load-from-OF.patch @@ -0,0 +1,32 @@ +From 15a3839fa080feec3b4ce6d92b08893bf1eefe56 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 22 Jan 2013 16:01:07 +0100 +Subject: [PATCH 59/79] serial: of: allow au1x00 and rt288x to load from OF + +In order to make serial_8250 loadable via OF on Au1x00 and Ralink WiSoC we need +to default the iotype to UPIO_AU. + +Signed-off-by: John Crispin +--- + drivers/tty/serial/of_serial.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c +index e7cae1c..026e5d6 100644 +--- a/drivers/tty/serial/of_serial.c ++++ b/drivers/tty/serial/of_serial.c +@@ -97,7 +97,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev, + port->regshift = prop; + + port->irq = irq_of_parse_and_map(np, 0); +- port->iotype = UPIO_MEM; ++ if (of_device_is_compatible(np, "ralink,rt2880-uart")) ++ port->iotype = UPIO_AU; ++ else ++ port->iotype = UPIO_MEM; + if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { + switch (prop) { + case 1: +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0060-serial-ralink-adds-mt7620-serial.patch b/target/linux/ramips/patches-3.8/0060-serial-ralink-adds-mt7620-serial.patch new file mode 100644 index 0000000000..c935a5a6a4 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0060-serial-ralink-adds-mt7620-serial.patch @@ -0,0 +1,28 @@ +From d76f99c928f0cd6e6cd3d99c916d890ae9a1e073 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 15 Mar 2013 18:16:01 +0100 +Subject: [PATCH 60/79] serial: ralink: adds mt7620 serial + +Add the config symbol for Mediatek7620 SoC to SERIAL_8250_RT288X + +Signed-off-by: John Crispin +--- + drivers/tty/serial/8250/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig +index 9e4febd..d1ec3a1 100644 +--- a/drivers/tty/serial/8250/Kconfig ++++ b/drivers/tty/serial/8250/Kconfig +@@ -280,7 +280,7 @@ config SERIAL_8250_EM + + config SERIAL_8250_RT288X + bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support" +- depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883) ++ depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620) + help + If you have a Ralink RT288x/RT305x SoC based board and want to use the + serial port, say Y to this option. The driver can handle up to 2 serial +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0061-DMA-MIPS-ralink-add-dmaengine-driver.patch b/target/linux/ramips/patches-3.8/0061-DMA-MIPS-ralink-add-dmaengine-driver.patch new file mode 100644 index 0000000000..a2f8f09ac1 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0061-DMA-MIPS-ralink-add-dmaengine-driver.patch @@ -0,0 +1,341 @@ +From 1db0d19afe5830f7d020c7c5386be8cc20cf0f15 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 23 May 2013 18:45:29 +0200 +Subject: [PATCH 61/79] DMA: MIPS: ralink: add dmaengine driver + +Signed-off-by: John Crispin +--- + drivers/dma/Kconfig | 7 ++ + drivers/dma/Makefile | 1 + + drivers/dma/ralink_gdma.c | 229 +++++++++++++++++++++++++++++++++++++++++++++ + drivers/dma/ralink_gdma.h | 55 +++++++++++ + 4 files changed, 292 insertions(+) + create mode 100644 drivers/dma/ralink_gdma.c + create mode 100644 drivers/dma/ralink_gdma.h + +diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig +index d4c1218..323f684 100644 +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -320,6 +320,13 @@ config MMP_PDMA + help + Support the MMP PDMA engine for PXA and MMP platfrom. + ++config RALINK_GDMA ++ bool "Ralink Generic DMA support" ++ depends on RALINK ++ select DMA_ENGINE ++ help ++ Support the GDMA engine for MIPS based Ralink SoC. ++ + config DMA_ENGINE + bool + +diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile +index 7428fea..a981e2c 100644 +--- a/drivers/dma/Makefile ++++ b/drivers/dma/Makefile +@@ -34,3 +34,4 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o + obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o + obj-$(CONFIG_DMA_OMAP) += omap-dma.o + obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o ++obj-$(CONFIG_RALINK_GDMA) += ralink_gdma.o +diff --git a/drivers/dma/ralink_gdma.c b/drivers/dma/ralink_gdma.c +new file mode 100644 +index 0000000..be7c317 +--- /dev/null ++++ b/drivers/dma/ralink_gdma.c +@@ -0,0 +1,229 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ralink_gdma.h" ++ ++#define SURFBOARDINT_DMA 10 ++#define MEMCPY_DMA_CH 8 ++#define to_rt2880_dma_chan(chan) \ ++ container_of(chan, struct rt2880_dma_chan, common) ++ ++static dma_cookie_t rt2880_dma_tx_submit(struct dma_async_tx_descriptor *tx) ++{ ++ dma_cookie_t cookie; ++ ++ cookie = tx->chan->cookie; ++ ++ return cookie; ++} ++ ++#define MIN_RTDMA_PKT_LEN 128 ++static struct dma_async_tx_descriptor * ++rt2880_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, ++ size_t len, unsigned long flags) ++{ ++ struct rt2880_dma_chan *rt_chan = to_rt2880_dma_chan(chan); ++ unsigned long mid_offset; ++ ++ spin_lock_bh(&rt_chan->lock); ++ ++ if(len < MIN_RTDMA_PKT_LEN) { ++ memcpy(phys_to_virt(dest), phys_to_virt(src), len); ++ } else { ++ mid_offset = len/2; ++ ++ /* Lower parts are transferred by GDMA. ++ * Upper parts are transferred by CPU. ++ */ ++ RT_DMA_WRITE_REG(RT_DMA_SRC_REG(MEMCPY_DMA_CH), src); ++ RT_DMA_WRITE_REG(RT_DMA_DST_REG(MEMCPY_DMA_CH), dest); ++ RT_DMA_WRITE_REG(RT_DMA_CTRL_REG(MEMCPY_DMA_CH), (mid_offset << 16) | (3 << 3) | (3 << 0)); ++ ++ memcpy(phys_to_virt(dest)+mid_offset, phys_to_virt(src)+mid_offset, len-mid_offset); ++ ++ dma_async_tx_descriptor_init(&rt_chan->txd, chan); ++ ++ while((RT_DMA_READ_REG(RT_DMA_DONEINT) & (0x1<lock); ++ ++ return &rt_chan->txd; ++} ++ ++/** ++ * rt2880_dma_status - poll the status of an XOR transaction ++ * @chan: XOR channel handle ++ * @cookie: XOR transaction identifier ++ * @txstate: XOR transactions state holder (or NULL) ++ */ ++static enum dma_status rt2880_dma_status(struct dma_chan *chan, ++ dma_cookie_t cookie, ++ struct dma_tx_state *txstate) ++{ ++ return 0; ++} ++ ++static irqreturn_t rt2880_dma_interrupt_handler(int irq, void *data) ++{ ++ ++ printk("%s\n",__FUNCTION__); ++ ++ return IRQ_HANDLED; ++} ++ ++static void rt2880_dma_issue_pending(struct dma_chan *chan) ++{ ++} ++ ++static int rt2880_dma_alloc_chan_resources(struct dma_chan *chan) ++{ ++// printk("%s\n",__FUNCTION__); ++ ++ return 0; ++} ++ ++static void rt2880_dma_free_chan_resources(struct dma_chan *chan) ++{ ++// printk("%s\n",__FUNCTION__); ++ ++} ++ ++static int rt2880_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case DMA_TERMINATE_ALL: ++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ break; ++ case DMA_SLAVE_CONFIG: ++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ break; ++ default: ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static int rt2880_dma_probe(struct platform_device *pdev) ++{ ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ __iomem void *membase; ++ struct dma_device *dma_dev; ++ struct rt2880_dma_chan *rt_chan; ++ int err; ++ int ret; ++ int reg; ++ int irq; ++ ++ membase = devm_request_and_ioremap(&pdev->dev, res); ++ if (IS_ERR(membase)) ++ return PTR_ERR(membase); ++ ++ dma_dev = devm_kzalloc(&pdev->dev, sizeof(*dma_dev), GFP_KERNEL); ++ if (!dma_dev) ++ return -ENOMEM; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (!irq) { ++ dev_err(&pdev->dev, "failed to load irq\n"); ++ return -ENOENT; ++ } ++ ++ ++ INIT_LIST_HEAD(&dma_dev->channels); ++ dma_cap_zero(dma_dev->cap_mask); ++ dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); ++ dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); ++ dma_dev->device_alloc_chan_resources = rt2880_dma_alloc_chan_resources; ++ dma_dev->device_free_chan_resources = rt2880_dma_free_chan_resources; ++ dma_dev->device_tx_status = rt2880_dma_status; ++ dma_dev->device_issue_pending = rt2880_dma_issue_pending; ++ dma_dev->device_prep_dma_memcpy = rt2880_dma_prep_dma_memcpy; ++ dma_dev->device_control = rt2880_dma_control; ++ dma_dev->dev = &pdev->dev; ++ ++ rt_chan = devm_kzalloc(&pdev->dev, sizeof(*rt_chan), GFP_KERNEL); ++ if (!rt_chan) { ++ return -ENOMEM; ++ } ++ ++ spin_lock_init(&rt_chan->lock); ++ INIT_LIST_HEAD(&rt_chan->chain); ++ INIT_LIST_HEAD(&rt_chan->completed_slots); ++ INIT_LIST_HEAD(&rt_chan->all_slots); ++ rt_chan->common.device = dma_dev; ++ rt_chan->txd.tx_submit = rt2880_dma_tx_submit; ++ ++ list_add_tail(&rt_chan->common.device_node, &dma_dev->channels); ++ ++ err = dma_async_device_register(dma_dev); ++ if (0 != err) { ++ pr_err("ERR_MDMA:device_register failed: %d\n", err); ++ return 1; ++ } ++ ++ ret = request_irq(irq, rt2880_dma_interrupt_handler, 0, dev_name(&pdev->dev), NULL); ++ if(ret){ ++ pr_err("IRQ %d is not free.\n", SURFBOARDINT_DMA); ++ return 1; ++ } ++ ++ //set GDMA register in advance. ++ reg = (32 << 16) | (32 << 8) | (MEMCPY_DMA_CH << 3); ++ RT_DMA_WRITE_REG(RT_DMA_CTRL_REG1(MEMCPY_DMA_CH), reg); ++ ++ dev_info(&pdev->dev, "running\n"); ++ ++ return 0; ++} ++ ++static int rt2880_dma_remove(struct platform_device *dev) ++{ ++ struct dma_device *dma_dev = platform_get_drvdata(dev); ++ ++ printk("%s\n",__FUNCTION__); ++ ++ dma_async_device_unregister(dma_dev); ++ ++ return 0; ++} ++ ++static const struct of_device_id rt2880_dma_match[] = { ++ { .compatible = "ralink,rt2880-gdma" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt2880_wdt_match); ++ ++static struct platform_driver rt2880_dma_driver = { ++ .probe = rt2880_dma_probe, ++ .remove = rt2880_dma_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = RT_DMA_NAME, ++ .of_match_table = rt2880_dma_match, ++ }, ++}; ++ ++static int __init rt2880_dma_init(void) ++{ ++ int rc; ++ ++ rc = platform_driver_register(&rt2880_dma_driver); ++ return rc; ++} ++module_init(rt2880_dma_init); ++ ++MODULE_AUTHOR("Steven Liu "); ++MODULE_AUTHOR("John Crispin "); ++MODULE_DESCRIPTION("DMA engine driver for Ralink DMA engine"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/dma/ralink_gdma.h b/drivers/dma/ralink_gdma.h +new file mode 100644 +index 0000000..73e1948 +--- /dev/null ++++ b/drivers/dma/ralink_gdma.h +@@ -0,0 +1,55 @@ ++/* ++ * Copyright (C) 2007, 2008, Marvell International Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef RT_DMA_H ++#define RT_DMA_H ++ ++#include ++#include ++#include ++#include ++ ++#define RT_DMA_NAME "rt2880_dma" ++ ++#define RALINK_GDMA_BASE 0xB0002800 ++ ++struct rt2880_dma_chan { ++ int pending; ++ dma_cookie_t completed_cookie; ++ spinlock_t lock; /* protects the descriptor slot pool */ ++ void __iomem *mmr_base; ++ unsigned int idx; ++ enum dma_transaction_type current_type; ++ struct dma_async_tx_descriptor txd; ++ struct list_head chain; ++ struct list_head completed_slots; ++ struct dma_chan common; ++ struct list_head all_slots; ++ int slots_allocated; ++ struct tasklet_struct irq_tasklet; ++}; ++ ++#define RT_DMA_READ_REG(addr) le32_to_cpu(*(volatile u32 *)(addr)) ++#define RT_DMA_WRITE_REG(addr, val) *((volatile uint32_t *)(addr)) = cpu_to_le32(val) ++ ++#define RT_DMA_SRC_REG(ch) (RALINK_GDMA_BASE + ch*16) ++#define RT_DMA_DST_REG(ch) (RT_DMA_SRC_REG(ch) + 4) ++#define RT_DMA_CTRL_REG(ch) (RT_DMA_DST_REG(ch) + 4) ++#define RT_DMA_CTRL_REG1(ch) (RT_DMA_CTRL_REG(ch) + 4) ++#define RT_DMA_DONEINT (RALINK_GDMA_BASE + 0x204) ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0062-PCI-MIPS-adds-rt2880-pci-support.patch b/target/linux/ramips/patches-3.8/0062-PCI-MIPS-adds-rt2880-pci-support.patch new file mode 100644 index 0000000000..108455d1c2 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0062-PCI-MIPS-adds-rt2880-pci-support.patch @@ -0,0 +1,329 @@ +From ac237657b04e464f28e8fe428d032386ca8b0b31 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 21 Mar 2013 18:27:29 +0100 +Subject: [PATCH 62/79] PCI: MIPS: adds rt2880 pci support + +Add support for the pci found on the rt2880 SoC. + +Signed-off-by: John Crispin +--- + arch/mips/pci/Makefile | 1 + + arch/mips/pci/pci-rt2880.c | 281 ++++++++++++++++++++++++++++++++++++++++++++ + arch/mips/ralink/Kconfig | 1 + + 3 files changed, 283 insertions(+) + create mode 100644 arch/mips/pci/pci-rt2880.c + +diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile +index ce995d3..c7c18ce 100644 +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -42,6 +42,7 @@ obj-$(CONFIG_SIBYTE_BCM1x80) += pci-bcm1480.o pci-bcm1480ht.o + obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o + obj-$(CONFIG_LANTIQ) += fixup-lantiq.o + obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o ++obj-$(CONFIG_SOC_RT2880) += pci-rt2880.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o + obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o +diff --git a/arch/mips/pci/pci-rt2880.c b/arch/mips/pci/pci-rt2880.c +new file mode 100644 +index 0000000..e2c4730 +--- /dev/null ++++ b/arch/mips/pci/pci-rt2880.c +@@ -0,0 +1,281 @@ ++/* ++ * Ralink RT288x SoC PCI register definitions ++ * ++ * Copyright (C) 2009 John Crispin ++ * Copyright (C) 2009 Gabor Juhos ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define RT2880_PCI_BASE 0x00440000 ++#define RT288X_CPU_IRQ_PCI 4 ++ ++#define RT2880_PCI_MEM_BASE 0x20000000 ++#define RT2880_PCI_MEM_SIZE 0x10000000 ++#define RT2880_PCI_IO_BASE 0x00460000 ++#define RT2880_PCI_IO_SIZE 0x00010000 ++ ++#define RT2880_PCI_REG_PCICFG_ADDR 0x00 ++#define RT2880_PCI_REG_PCIMSK_ADDR 0x0c ++#define RT2880_PCI_REG_BAR0SETUP_ADDR 0x10 ++#define RT2880_PCI_REG_IMBASEBAR0_ADDR 0x18 ++#define RT2880_PCI_REG_CONFIG_ADDR 0x20 ++#define RT2880_PCI_REG_CONFIG_DATA 0x24 ++#define RT2880_PCI_REG_MEMBASE 0x28 ++#define RT2880_PCI_REG_IOBASE 0x2c ++#define RT2880_PCI_REG_ID 0x30 ++#define RT2880_PCI_REG_CLASS 0x34 ++#define RT2880_PCI_REG_SUBID 0x38 ++#define RT2880_PCI_REG_ARBCTL 0x80 ++ ++static void __iomem *rt2880_pci_base; ++static DEFINE_SPINLOCK(rt2880_pci_lock); ++ ++static u32 rt2880_pci_reg_read(u32 reg) ++{ ++ return readl(rt2880_pci_base + reg); ++} ++ ++static void rt2880_pci_reg_write(u32 val, u32 reg) ++{ ++ writel(val, rt2880_pci_base + reg); ++} ++ ++static inline u32 rt2880_pci_get_cfgaddr(unsigned int bus, unsigned int slot, ++ unsigned int func, unsigned int where) ++{ ++ return ((bus << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | ++ 0x80000000); ++} ++ ++static int rt2880_pci_config_read(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *val) ++{ ++ unsigned long flags; ++ u32 address; ++ u32 data; ++ ++ address = rt2880_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), ++ PCI_FUNC(devfn), where); ++ ++ spin_lock_irqsave(&rt2880_pci_lock, flags); ++ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); ++ data = rt2880_pci_reg_read(RT2880_PCI_REG_CONFIG_DATA); ++ spin_unlock_irqrestore(&rt2880_pci_lock, flags); ++ ++ switch (size) { ++ case 1: ++ *val = (data >> ((where & 3) << 3)) & 0xff; ++ break; ++ case 2: ++ *val = (data >> ((where & 3) << 3)) & 0xffff; ++ break; ++ case 4: ++ *val = data; ++ break; ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int rt2880_pci_config_write(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 val) ++{ ++ unsigned long flags; ++ u32 address; ++ u32 data; ++ ++ address = rt2880_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), ++ PCI_FUNC(devfn), where); ++ ++ spin_lock_irqsave(&rt2880_pci_lock, flags); ++ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); ++ data = rt2880_pci_reg_read(RT2880_PCI_REG_CONFIG_DATA); ++ ++ switch (size) { ++ case 1: ++ data = (data & ~(0xff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 2: ++ data = (data & ~(0xffff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 4: ++ data = val; ++ break; ++ } ++ ++ rt2880_pci_reg_write(data, RT2880_PCI_REG_CONFIG_DATA); ++ spin_unlock_irqrestore(&rt2880_pci_lock, flags); ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static struct pci_ops rt2880_pci_ops = { ++ .read = rt2880_pci_config_read, ++ .write = rt2880_pci_config_write, ++}; ++ ++static struct resource rt2880_pci_mem_resource = { ++ .name = "PCI MEM space", ++ .start = RT2880_PCI_MEM_BASE, ++ .end = RT2880_PCI_MEM_BASE + RT2880_PCI_MEM_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++}; ++ ++static struct resource rt2880_pci_io_resource = { ++ .name = "PCI IO space", ++ .start = RT2880_PCI_IO_BASE, ++ .end = RT2880_PCI_IO_BASE + RT2880_PCI_IO_SIZE - 1, ++ .flags = IORESOURCE_IO, ++}; ++ ++static struct pci_controller rt2880_pci_controller = { ++ .pci_ops = &rt2880_pci_ops, ++ .mem_resource = &rt2880_pci_mem_resource, ++ .io_resource = &rt2880_pci_io_resource, ++}; ++ ++static inline u32 rt2880_pci_read_u32(unsigned long reg) ++{ ++ unsigned long flags; ++ u32 address; ++ u32 ret; ++ ++ address = rt2880_pci_get_cfgaddr(0, 0, 0, reg); ++ ++ spin_lock_irqsave(&rt2880_pci_lock, flags); ++ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); ++ ret = rt2880_pci_reg_read(RT2880_PCI_REG_CONFIG_DATA); ++ spin_unlock_irqrestore(&rt2880_pci_lock, flags); ++ ++ return ret; ++} ++ ++static inline void rt2880_pci_write_u32(unsigned long reg, u32 val) ++{ ++ unsigned long flags; ++ u32 address; ++ ++ address = rt2880_pci_get_cfgaddr(0, 0, 0, reg); ++ ++ spin_lock_irqsave(&rt2880_pci_lock, flags); ++ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); ++ rt2880_pci_reg_write(val, RT2880_PCI_REG_CONFIG_DATA); ++ spin_unlock_irqrestore(&rt2880_pci_lock, flags); ++} ++ ++int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ u16 cmd; ++ int irq = -1; ++ ++ if (dev->bus->number != 0) ++ return irq; ++ ++ switch (PCI_SLOT(dev->devfn)) { ++ case 0x00: ++ rt2880_pci_write_u32(PCI_BASE_ADDRESS_0, 0x08000000); ++ (void) rt2880_pci_read_u32(PCI_BASE_ADDRESS_0); ++ break; ++ case 0x11: ++ irq = RT288X_CPU_IRQ_PCI; ++ break; ++ default: ++ printk("%s:%s[%d] trying to alloc unknown pci irq\n", ++ __FILE__, __func__, __LINE__); ++ BUG(); ++ break; ++ } ++ ++ pci_write_config_byte((struct pci_dev*)dev, PCI_CACHE_LINE_SIZE, 0x14); ++ pci_write_config_byte((struct pci_dev*)dev, PCI_LATENCY_TIMER, 0xFF); ++ pci_read_config_word((struct pci_dev*)dev, PCI_COMMAND, &cmd); ++ cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | ++ PCI_COMMAND_INVALIDATE | PCI_COMMAND_FAST_BACK | ++ PCI_COMMAND_SERR | PCI_COMMAND_WAIT | PCI_COMMAND_PARITY; ++ pci_write_config_word((struct pci_dev*)dev, PCI_COMMAND, cmd); ++ pci_write_config_byte((struct pci_dev*)dev, PCI_INTERRUPT_LINE, ++ dev->irq); ++ return irq; ++} ++ ++static int rt288x_pci_probe(struct platform_device *pdev) ++{ ++ void __iomem *io_map_base; ++ int i; ++ ++ rt2880_pci_base = ioremap_nocache(RT2880_PCI_BASE, PAGE_SIZE); ++ ++ io_map_base = ioremap(RT2880_PCI_IO_BASE, RT2880_PCI_IO_SIZE); ++ rt2880_pci_controller.io_map_base = (unsigned long) io_map_base; ++ set_io_port_base((unsigned long) io_map_base); ++ ++ ioport_resource.start = RT2880_PCI_IO_BASE; ++ ioport_resource.end = RT2880_PCI_IO_BASE + RT2880_PCI_IO_SIZE - 1; ++ ++ rt2880_pci_reg_write(0, RT2880_PCI_REG_PCICFG_ADDR); ++ for(i = 0; i < 0xfffff; i++) {} ++ ++ rt2880_pci_reg_write(0x79, RT2880_PCI_REG_ARBCTL); ++ rt2880_pci_reg_write(0x07FF0001, RT2880_PCI_REG_BAR0SETUP_ADDR); ++ rt2880_pci_reg_write(RT2880_PCI_MEM_BASE, RT2880_PCI_REG_MEMBASE); ++ rt2880_pci_reg_write(RT2880_PCI_IO_BASE, RT2880_PCI_REG_IOBASE); ++ rt2880_pci_reg_write(0x08000000, RT2880_PCI_REG_IMBASEBAR0_ADDR); ++ rt2880_pci_reg_write(0x08021814, RT2880_PCI_REG_ID); ++ rt2880_pci_reg_write(0x00800001, RT2880_PCI_REG_CLASS); ++ rt2880_pci_reg_write(0x28801814, RT2880_PCI_REG_SUBID); ++ rt2880_pci_reg_write(0x000c0000, RT2880_PCI_REG_PCIMSK_ADDR); ++ ++ rt2880_pci_write_u32(PCI_BASE_ADDRESS_0, 0x08000000); ++ (void) rt2880_pci_read_u32(PCI_BASE_ADDRESS_0); ++ ++ register_pci_controller(&rt2880_pci_controller); ++ return 0; ++} ++ ++int pcibios_plat_dev_init(struct pci_dev *dev) ++{ ++ return 0; ++} ++ ++static const struct of_device_id rt288x_pci_match[] = { ++ { .compatible = "ralink,rt288x-pci" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt288x_pci_match); ++ ++static struct platform_driver rt288x_pci_driver = { ++ .probe = rt288x_pci_probe, ++ .driver = { ++ .name = "rt288x-pci", ++ .owner = THIS_MODULE, ++ .of_match_table = rt288x_pci_match, ++ }, ++}; ++ ++int __init pcibios_init(void) ++{ ++ int ret = platform_driver_register(&rt288x_pci_driver); ++ if (ret) ++ pr_info("rt288x-pci: Error registering platform driver!"); ++ return ret; ++} ++ ++arch_initcall(pcibios_init); +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 38540a4..2d39329 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -8,6 +8,7 @@ choice + + config SOC_RT288X + bool "RT288x" ++ select HW_HAS_PCI + + config SOC_RT305X + bool "RT305x" +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0063-PCI-MIPS-adds-rt3883-pci-support.patch b/target/linux/ramips/patches-3.8/0063-PCI-MIPS-adds-rt3883-pci-support.patch new file mode 100644 index 0000000000..c1c8f75f6e --- /dev/null +++ b/target/linux/ramips/patches-3.8/0063-PCI-MIPS-adds-rt3883-pci-support.patch @@ -0,0 +1,688 @@ +From 5b942b64c01b57cbde0dff2185728d5a8facea76 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 21 Mar 2013 17:34:08 +0100 +Subject: [PATCH 63/79] PCI: MIPS: adds rt3883 pci support + +Add support for the pcie found on the rt3883 SoC. + +Signed-off-by: John Crispin +--- + arch/mips/pci/Makefile | 1 + + arch/mips/pci/pci-rt3883.c | 640 ++++++++++++++++++++++++++++++++++++++++++++ + arch/mips/ralink/Kconfig | 1 + + 3 files changed, 642 insertions(+) + create mode 100644 arch/mips/pci/pci-rt3883.c + +diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile +index c7c18ce..a238158 100644 +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -43,6 +43,7 @@ obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o + obj-$(CONFIG_LANTIQ) += fixup-lantiq.o + obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o + obj-$(CONFIG_SOC_RT2880) += pci-rt2880.o ++obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o + obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o +diff --git a/arch/mips/pci/pci-rt3883.c b/arch/mips/pci/pci-rt3883.c +new file mode 100644 +index 0000000..212c90b +--- /dev/null ++++ b/arch/mips/pci/pci-rt3883.c +@@ -0,0 +1,640 @@ ++/* ++ * Ralink RT3662/RT3883 SoC PCI support ++ * ++ * Copyright (C) 2011-2013 Gabor Juhos ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define RT3883_MEMORY_BASE 0x00000000 ++#define RT3883_MEMORY_SIZE 0x02000000 ++ ++#define RT3883_PCI_REG_PCICFG 0x00 ++#define RT3883_PCICFG_P2P_BR_DEVNUM_M 0xf ++#define RT3883_PCICFG_P2P_BR_DEVNUM_S 16 ++#define RT3883_PCICFG_PCIRST BIT(1) ++#define RT3883_PCI_REG_PCIRAW 0x04 ++#define RT3883_PCI_REG_PCIINT 0x08 ++#define RT3883_PCI_REG_PCIENA 0x0c ++ ++#define RT3883_PCI_REG_CFGADDR 0x20 ++#define RT3883_PCI_REG_CFGDATA 0x24 ++#define RT3883_PCI_REG_MEMBASE 0x28 ++#define RT3883_PCI_REG_IOBASE 0x2c ++#define RT3883_PCI_REG_ARBCTL 0x80 ++ ++#define RT3883_PCI_REG_BASE(_x) (0x1000 + (_x) * 0x1000) ++#define RT3883_PCI_REG_BAR0SETUP(_x) (RT3883_PCI_REG_BASE((_x)) + 0x10) ++#define RT3883_PCI_REG_IMBASEBAR0(_x) (RT3883_PCI_REG_BASE((_x)) + 0x18) ++#define RT3883_PCI_REG_ID(_x) (RT3883_PCI_REG_BASE((_x)) + 0x30) ++#define RT3883_PCI_REG_CLASS(_x) (RT3883_PCI_REG_BASE((_x)) + 0x34) ++#define RT3883_PCI_REG_SUBID(_x) (RT3883_PCI_REG_BASE((_x)) + 0x38) ++#define RT3883_PCI_REG_STATUS(_x) (RT3883_PCI_REG_BASE((_x)) + 0x50) ++ ++#define RT3883_PCI_MODE_NONE 0 ++#define RT3883_PCI_MODE_PCI BIT(0) ++#define RT3883_PCI_MODE_PCIE BIT(1) ++#define RT3883_PCI_MODE_BOTH (RT3883_PCI_MODE_PCI | RT3883_PCI_MODE_PCIE) ++ ++#define RT3883_PCI_IRQ_COUNT 32 ++ ++#define RT3883_P2P_BR_DEVNUM 1 ++ ++struct rt3883_pci_controller { ++ void __iomem *base; ++ spinlock_t lock; ++ ++ struct irq_domain *irq_domain; ++ ++ struct pci_controller pci_controller; ++ struct resource io_res; ++ struct resource mem_res; ++ ++ bool pcie_ready; ++ unsigned char p2p_devnum; ++}; ++ ++static inline struct rt3883_pci_controller * ++pci_bus_to_rt3883_controller(struct pci_bus *bus) ++{ ++ struct pci_controller *hose; ++ ++ hose = (struct pci_controller *) bus->sysdata; ++ return container_of(hose, struct rt3883_pci_controller, pci_controller); ++} ++ ++static inline u32 rt3883_pci_r32(struct rt3883_pci_controller *rpc, ++ unsigned reg) ++{ ++ return ioread32(rpc->base + reg); ++} ++ ++static inline void rt3883_pci_w32(struct rt3883_pci_controller *rpc, ++ u32 val, unsigned reg) ++{ ++ iowrite32(val, rpc->base + reg); ++} ++ ++static inline u32 rt3883_pci_get_cfgaddr(unsigned int bus, unsigned int slot, ++ unsigned int func, unsigned int where) ++{ ++ return ((bus << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | ++ 0x80000000); ++} ++ ++static u32 rt3883_pci_read_cfg32(struct rt3883_pci_controller *rpc, ++ unsigned bus, unsigned slot, ++ unsigned func, unsigned reg) ++{ ++ unsigned long flags; ++ u32 address; ++ u32 ret; ++ ++ address = rt3883_pci_get_cfgaddr(bus, slot, func, reg); ++ ++ spin_lock_irqsave(&rpc->lock, flags); ++ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); ++ ret = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA); ++ spin_unlock_irqrestore(&rpc->lock, flags); ++ ++ return ret; ++} ++ ++static void rt3883_pci_write_cfg32(struct rt3883_pci_controller *rpc, ++ unsigned bus, unsigned slot, ++ unsigned func, unsigned reg, u32 val) ++{ ++ unsigned long flags; ++ u32 address; ++ ++ address = rt3883_pci_get_cfgaddr(bus, slot, func, reg); ++ ++ spin_lock_irqsave(&rpc->lock, flags); ++ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); ++ rt3883_pci_w32(rpc, val, RT3883_PCI_REG_CFGDATA); ++ spin_unlock_irqrestore(&rpc->lock, flags); ++} ++ ++static void rt3883_pci_irq_handler(unsigned int irq, struct irq_desc *desc) ++{ ++ struct rt3883_pci_controller *rpc; ++ u32 pending; ++ ++ rpc = irq_get_handler_data(irq); ++ ++ pending = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIINT) & ++ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); ++ ++ if (!pending) { ++ spurious_interrupt(); ++ return; ++ } ++ ++ while (pending) { ++ unsigned bit = __ffs(pending); ++ ++ irq = irq_find_mapping(rpc->irq_domain, bit); ++ generic_handle_irq(irq); ++ ++ pending &= ~BIT(bit); ++ } ++} ++ ++static void rt3883_pci_irq_unmask(struct irq_data *d) ++{ ++ struct rt3883_pci_controller *rpc; ++ u32 t; ++ ++ rpc = irq_data_get_irq_chip_data(d); ++ ++ t = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); ++ rt3883_pci_w32(rpc, t | BIT(d->hwirq), RT3883_PCI_REG_PCIENA); ++ /* flush write */ ++ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); ++} ++ ++static void rt3883_pci_irq_mask(struct irq_data *d) ++{ ++ struct rt3883_pci_controller *rpc; ++ u32 t; ++ ++ rpc = irq_data_get_irq_chip_data(d); ++ ++ t = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); ++ rt3883_pci_w32(rpc, t & ~BIT(d->hwirq), RT3883_PCI_REG_PCIENA); ++ /* flush write */ ++ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); ++} ++ ++static struct irq_chip rt3883_pci_irq_chip = { ++ .name = "RT3883 PCI", ++ .irq_mask = rt3883_pci_irq_mask, ++ .irq_unmask = rt3883_pci_irq_unmask, ++ .irq_mask_ack = rt3883_pci_irq_mask, ++}; ++ ++static int rt3883_pci_irq_map(struct irq_domain *d, unsigned int irq, ++ irq_hw_number_t hw) ++{ ++ irq_set_chip_and_handler(irq, &rt3883_pci_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, d->host_data); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops rt3883_pci_irq_domain_ops = { ++ .map = rt3883_pci_irq_map, ++ .xlate = irq_domain_xlate_onecell, ++}; ++ ++static int rt3883_pci_irq_init(struct device *dev, ++ struct rt3883_pci_controller *rpc) ++{ ++ struct device_node *np = dev->of_node; ++ struct device_node *intc_np; ++ int irq; ++ int err; ++ ++ intc_np = of_get_child_by_name(np, "interrupt-controller"); ++ if (!intc_np) { ++ dev_err(dev, "no %s child node found", "interrupt-controller"); ++ return -ENODEV; ++ } ++ ++ irq = irq_of_parse_and_map(intc_np, 0); ++ if (irq == 0) { ++ dev_err(dev, "%s has no IRQ", of_node_full_name(intc_np)); ++ err = -EINVAL; ++ goto err_put_intc; ++ } ++ ++ /* disable all interrupts */ ++ rt3883_pci_w32(rpc, 0, RT3883_PCI_REG_PCIENA); ++ ++ rpc->irq_domain = ++ irq_domain_add_linear(intc_np, RT3883_PCI_IRQ_COUNT, ++ &rt3883_pci_irq_domain_ops, ++ rpc); ++ if (!rpc->irq_domain) { ++ dev_err(dev, "unable to add IRQ domain\n"); ++ err = -ENODEV; ++ goto err_put_intc; ++ } ++ ++ irq_set_handler_data(irq, rpc); ++ irq_set_chained_handler(irq, rt3883_pci_irq_handler); ++ ++ return 0; ++ ++err_put_intc: ++ of_node_put(intc_np); ++ return err; ++} ++ ++static int rt3883_pci_config_read(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *val) ++{ ++ struct rt3883_pci_controller *rpc; ++ unsigned long flags; ++ u32 address; ++ u32 data; ++ ++ rpc = pci_bus_to_rt3883_controller(bus); ++ ++ if (!rpc->pcie_ready && bus->number == 1) ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ ++ address = rt3883_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), ++ PCI_FUNC(devfn), where); ++ ++ spin_lock_irqsave(&rpc->lock, flags); ++ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); ++ data = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA); ++ spin_unlock_irqrestore(&rpc->lock, flags); ++ ++ switch (size) { ++ case 1: ++ *val = (data >> ((where & 3) << 3)) & 0xff; ++ break; ++ case 2: ++ *val = (data >> ((where & 3) << 3)) & 0xffff; ++ break; ++ case 4: ++ *val = data; ++ break; ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int rt3883_pci_config_write(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 val) ++{ ++ struct rt3883_pci_controller *rpc; ++ unsigned long flags; ++ u32 address; ++ u32 data; ++ ++ rpc = pci_bus_to_rt3883_controller(bus); ++ ++ if (!rpc->pcie_ready && bus->number == 1) ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ ++ address = rt3883_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), ++ PCI_FUNC(devfn), where); ++ ++ spin_lock_irqsave(&rpc->lock, flags); ++ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); ++ data = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA); ++ ++ switch (size) { ++ case 1: ++ data = (data & ~(0xff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 2: ++ data = (data & ~(0xffff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 4: ++ data = val; ++ break; ++ } ++ ++ rt3883_pci_w32(rpc, data, RT3883_PCI_REG_CFGDATA); ++ spin_unlock_irqrestore(&rpc->lock, flags); ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static struct pci_ops rt3883_pci_ops = { ++ .read = rt3883_pci_config_read, ++ .write = rt3883_pci_config_write, ++}; ++ ++static void rt3883_pci_preinit(struct rt3883_pci_controller *rpc, unsigned mode) ++{ ++ u32 syscfg1; ++ u32 rstctrl; ++ u32 clkcfg1; ++ u32 t; ++ ++ rstctrl = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL); ++ syscfg1 = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG1); ++ clkcfg1 = rt_sysc_r32(RT3883_SYSC_REG_CLKCFG1); ++ ++ if (mode & RT3883_PCI_MODE_PCIE) { ++ rstctrl |= RT3883_RSTCTRL_PCIE; ++ rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL); ++ ++ /* setup PCI PAD drive mode */ ++ syscfg1 &= ~(0x30); ++ syscfg1 |= (2 << 4); ++ rt_sysc_w32(syscfg1, RT3883_SYSC_REG_SYSCFG1); ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ t &= ~BIT(31); ++ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN1); ++ t &= 0x80ffffff; ++ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN1); ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN1); ++ t |= 0xa << 24; ++ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN1); ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ t |= BIT(31); ++ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ ++ msleep(50); ++ ++ rstctrl &= ~RT3883_RSTCTRL_PCIE; ++ rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL); ++ } ++ ++ syscfg1 |= (RT3883_SYSCFG1_PCIE_RC_MODE | RT3883_SYSCFG1_PCI_HOST_MODE); ++ ++ clkcfg1 &= ~(RT3883_CLKCFG1_PCI_CLK_EN | RT3883_CLKCFG1_PCIE_CLK_EN); ++ ++ if (mode & RT3883_PCI_MODE_PCI) { ++ clkcfg1 |= RT3883_CLKCFG1_PCI_CLK_EN; ++ rstctrl &= ~RT3883_RSTCTRL_PCI; ++ } ++ ++ if (mode & RT3883_PCI_MODE_PCIE) { ++ clkcfg1 |= RT3883_CLKCFG1_PCIE_CLK_EN; ++ rstctrl &= ~RT3883_RSTCTRL_PCIE; ++ } ++ ++ rt_sysc_w32(syscfg1, RT3883_SYSC_REG_SYSCFG1); ++ rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL); ++ rt_sysc_w32(clkcfg1, RT3883_SYSC_REG_CLKCFG1); ++ ++ msleep(500); ++ ++ /* ++ * setup the device number of the P2P bridge ++ * and de-assert the reset line ++ */ ++ t = (RT3883_P2P_BR_DEVNUM << RT3883_PCICFG_P2P_BR_DEVNUM_S); ++ rt3883_pci_w32(rpc, t, RT3883_PCI_REG_PCICFG); ++ ++ /* flush write */ ++ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCICFG); ++ msleep(500); ++ ++ if (mode & RT3883_PCI_MODE_PCIE) { ++ msleep(500); ++ ++ t = rt3883_pci_r32(rpc, RT3883_PCI_REG_STATUS(1)); ++ ++ rpc->pcie_ready = t & BIT(0); ++ ++ if (!rpc->pcie_ready) { ++ /* reset the PCIe block */ ++ t = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL); ++ t |= RT3883_RSTCTRL_PCIE; ++ rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL); ++ t &= ~RT3883_RSTCTRL_PCIE; ++ rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL); ++ ++ /* turn off PCIe clock */ ++ t = rt_sysc_r32(RT3883_SYSC_REG_CLKCFG1); ++ t &= ~RT3883_CLKCFG1_PCIE_CLK_EN; ++ rt_sysc_w32(t, RT3883_SYSC_REG_CLKCFG1); ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ t &= ~0xf000c080; ++ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ } ++ } ++ ++ /* enable PCI arbiter */ ++ rt3883_pci_w32(rpc, 0x79, RT3883_PCI_REG_ARBCTL); ++} ++ ++static inline void ++rt3883_dump_pci_config(struct rt3883_pci_controller *rpc, ++ int bus, int slot) ++{ ++ int i; ++ ++ for (i = 0; i < 16; i++) { ++ u32 val; ++ ++ val = rt3883_pci_read_cfg32(rpc, bus, slot, 0, i << 2); ++ pr_info("pci %02x:%02x.0 0x%02x = %08x\n", ++ bus, slot, i << 2, val); ++ } ++} ++ ++static int rt3883_pci_probe(struct platform_device *pdev) ++{ ++ struct rt3883_pci_controller *rpc; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct resource *res; ++ struct device_node *child; ++ u32 val; ++ int err; ++ int mode; ++ ++ rpc = devm_kzalloc(dev, sizeof(*rpc), GFP_KERNEL); ++ if (!rpc) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -EINVAL; ++ ++ rpc->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(rpc->base)) ++ return PTR_ERR(rpc->base); ++ ++ rpc->pci_controller.of_node = of_get_child_by_name(np, "host-bridge"); ++ if (!rpc->pci_controller.of_node) { ++ dev_err(dev, "no %s child node found", "host-bridge"); ++ return -ENODEV; ++ } ++ ++ mode = RT3883_PCI_MODE_NONE; ++ for_each_child_of_node(rpc->pci_controller.of_node, child) { ++ u32 slot; ++ ++ if (!of_device_is_available(child)) ++ continue; ++ ++ if (of_property_read_u32(child, "ralink,pci-slot", ++ &slot)) { ++ dev_err(dev, "no '%s' property found for %s\n", ++ "ralink,pci-slot", ++ of_node_full_name(child)); ++ continue; ++ } ++ ++ switch (slot) { ++ case 1: ++ mode |= RT3883_PCI_MODE_PCIE; ++ break; ++ ++ case 17: ++ case 18: ++ mode |= RT3883_PCI_MODE_PCI; ++ break; ++ } ++ } ++ ++ if (mode == RT3883_PCI_MODE_NONE) { ++ dev_err(dev, "unable to determine PCI mode\n"); ++ err = -EINVAL; ++ goto err_put_hb_node; ++ } ++ ++ dev_info(dev, "mode:%s%s\n", ++ (mode & RT3883_PCI_MODE_PCI) ? " PCI" : "", ++ (mode & RT3883_PCI_MODE_PCIE) ? " PCIe" : ""); ++ ++ rt3883_pci_preinit(rpc, mode); ++ ++ rpc->pci_controller.pci_ops = &rt3883_pci_ops; ++ rpc->pci_controller.io_resource = &rpc->io_res; ++ rpc->pci_controller.mem_resource = &rpc->mem_res; ++ ++ /* Load PCI I/O and memory resources from DT */ ++ pci_load_of_ranges(&rpc->pci_controller, ++ rpc->pci_controller.of_node); ++ ++ rt3883_pci_w32(rpc, rpc->mem_res.start, RT3883_PCI_REG_MEMBASE); ++ rt3883_pci_w32(rpc, rpc->io_res.start, RT3883_PCI_REG_IOBASE); ++ ++ ioport_resource.start = rpc->io_res.start; ++ ioport_resource.end = rpc->io_res.end; ++ ++ /* PCI */ ++ rt3883_pci_w32(rpc, 0x03ff0000, RT3883_PCI_REG_BAR0SETUP(0)); ++ rt3883_pci_w32(rpc, RT3883_MEMORY_BASE, RT3883_PCI_REG_IMBASEBAR0(0)); ++ rt3883_pci_w32(rpc, 0x08021814, RT3883_PCI_REG_ID(0)); ++ rt3883_pci_w32(rpc, 0x00800001, RT3883_PCI_REG_CLASS(0)); ++ rt3883_pci_w32(rpc, 0x28801814, RT3883_PCI_REG_SUBID(0)); ++ ++ /* PCIe */ ++ rt3883_pci_w32(rpc, 0x03ff0000, RT3883_PCI_REG_BAR0SETUP(1)); ++ rt3883_pci_w32(rpc, RT3883_MEMORY_BASE, RT3883_PCI_REG_IMBASEBAR0(1)); ++ rt3883_pci_w32(rpc, 0x08021814, RT3883_PCI_REG_ID(1)); ++ rt3883_pci_w32(rpc, 0x06040001, RT3883_PCI_REG_CLASS(1)); ++ rt3883_pci_w32(rpc, 0x28801814, RT3883_PCI_REG_SUBID(1)); ++ ++ err = rt3883_pci_irq_init(dev, rpc); ++ if (err) ++ goto err_put_hb_node; ++ ++ /* PCIe */ ++ val = rt3883_pci_read_cfg32(rpc, 0, 0x01, 0, PCI_COMMAND); ++ val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; ++ rt3883_pci_write_cfg32(rpc, 0, 0x01, 0, PCI_COMMAND, val); ++ ++ /* PCI */ ++ val = rt3883_pci_read_cfg32(rpc, 0, 0x00, 0, PCI_COMMAND); ++ val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; ++ rt3883_pci_write_cfg32(rpc, 0, 0x00, 0, PCI_COMMAND, val); ++ ++ if (mode == RT3883_PCI_MODE_PCIE) { ++ rt3883_pci_w32(rpc, 0x03ff0001, RT3883_PCI_REG_BAR0SETUP(0)); ++ rt3883_pci_w32(rpc, 0x03ff0001, RT3883_PCI_REG_BAR0SETUP(1)); ++ ++ rt3883_pci_write_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0, ++ PCI_BASE_ADDRESS_0, ++ RT3883_MEMORY_BASE); ++ /* flush write */ ++ rt3883_pci_read_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0, ++ PCI_BASE_ADDRESS_0); ++ } else { ++ rt3883_pci_write_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0, ++ PCI_IO_BASE, 0x00000101); ++ } ++ ++ register_pci_controller(&rpc->pci_controller); ++ ++ return 0; ++ ++err_put_hb_node: ++ of_node_put(rpc->pci_controller.of_node); ++ return err; ++} ++ ++int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ struct rt3883_pci_controller *rpc; ++ struct of_irq dev_irq; ++ int err; ++ int irq; ++ ++ rpc = pci_bus_to_rt3883_controller(dev->bus); ++ err = of_irq_map_pci(dev, &dev_irq); ++ if (err) { ++ pr_err("pci %s: unable to get irq map, err=%d\n", ++ pci_name((struct pci_dev *) dev), err); ++ return 0; ++ } ++ ++ irq = irq_create_of_mapping(dev_irq.controller, ++ dev_irq.specifier, ++ dev_irq.size); ++ ++ if (irq == 0) ++ pr_crit("pci %s: no irq found for pin %u\n", ++ pci_name((struct pci_dev *) dev), pin); ++ else ++ pr_info("pci %s: using irq %d for pin %u\n", ++ pci_name((struct pci_dev *) dev), irq, pin); ++ ++ return irq; ++} ++ ++int pcibios_plat_dev_init(struct pci_dev *dev) ++{ ++ return 0; ++} ++ ++static const struct of_device_id rt3883_pci_ids[] = { ++ { .compatible = "ralink,rt3883-pci" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt3883_pci_ids); ++ ++static struct platform_driver rt3883_pci_driver = { ++ .probe = rt3883_pci_probe, ++ .driver = { ++ .name = "rt3883-pci", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(rt3883_pci_ids), ++ }, ++}; ++ ++static int __init rt3883_pci_init(void) ++{ ++ return platform_driver_register(&rt3883_pci_driver); ++} ++ ++postcore_initcall(rt3883_pci_init); +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 2d39329..f2f20ec 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -20,6 +20,7 @@ choice + bool "RT3883" + select USB_ARCH_HAS_OHCI + select USB_ARCH_HAS_EHCI ++ select HW_HAS_PCI + + config SOC_MT7620 + bool "MT7620" +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0064-PCI-MIPS-adds-mt7620a-pcie-driver.patch b/target/linux/ramips/patches-3.8/0064-PCI-MIPS-adds-mt7620a-pcie-driver.patch new file mode 100644 index 0000000000..b3b0ad4310 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0064-PCI-MIPS-adds-mt7620a-pcie-driver.patch @@ -0,0 +1,409 @@ +From 44712deb87f994f837796a55f670bf4c0d0d5b51 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 20 Jun 2013 18:48:58 +0200 +Subject: [PATCH 64/79] PCI: MIPS: adds mt7620a pcie driver + +Signed-off-by: John Crispin +--- + arch/mips/pci/Makefile | 1 + + arch/mips/pci/pci-mt7620a.c | 363 +++++++++++++++++++++++++++++++++++++++++++ + arch/mips/ralink/Kconfig | 1 + + 3 files changed, 365 insertions(+) + create mode 100644 arch/mips/pci/pci-mt7620a.c + +diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile +index a238158..168d477 100644 +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -44,6 +44,7 @@ obj-$(CONFIG_LANTIQ) += fixup-lantiq.o + obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o + obj-$(CONFIG_SOC_RT2880) += pci-rt2880.o + obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o ++obj-$(CONFIG_SOC_MT7620) += pci-mt7620a.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o + obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o +diff --git a/arch/mips/pci/pci-mt7620a.c b/arch/mips/pci/pci-mt7620a.c +new file mode 100644 +index 0000000..271763c +--- /dev/null ++++ b/arch/mips/pci/pci-mt7620a.c +@@ -0,0 +1,363 @@ ++/* ++ * Ralink MT7620A SoC PCI support ++ * ++ * Copyright (C) 2007-2013 Bruce Chang ++ * Copyright (C) 2013 John Crispin ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define RALINK_PCI_MM_MAP_BASE 0x20000000 ++#define RALINK_PCI_IO_MAP_BASE 0x10160000 ++ ++#define RALINK_INT_PCIE0 4 ++#define RALINK_SYSTEM_CONTROL_BASE 0xb0000000 ++#define RALINK_SYSCFG1 0x14 ++#define RALINK_CLKCFG1 0x30 ++#define RALINK_GPIOMODE 0x60 ++#define RALINK_PCIE_CLK_GEN 0x7c ++#define RALINK_PCIE_CLK_GEN1 0x80 ++#define PCIEPHY0_CFG 0x90 ++#define PPLL_CFG1 0x9c ++#define PPLL_DRV 0xa0 ++#define RALINK_PCI_HOST_MODE_EN (1<<7) ++#define RALINK_PCIE_RC_MODE_EN (1<<8) ++#define RALINK_PCIE_RST (1<<23) ++#define RALINK_PCI_RST (1<<24) ++#define RALINK_PCI_CLK_EN (1<<19) ++#define RALINK_PCIE_CLK_EN (1<<21) ++#define PCI_SLOTx2 (1<<11) ++#define PCI_SLOTx1 (2<<11) ++#define PDRV_SW_SET (1<<31) ++#define LC_CKDRVPD_ (1<<19) ++ ++#define RALINK_PCI_CONFIG_ADDR 0x20 ++#define RALINK_PCI_CONFIG_DATA_VIRTUAL_REG 0x24 ++#define MEMORY_BASE 0x0 ++#define RALINK_PCIE0_RST (1<<26) ++#define RALINK_PCI_BASE 0xB0140000 ++#define RALINK_PCI_MEMBASE 0x28 ++#define RALINK_PCI_IOBASE 0x2C ++ ++#define RT6855_PCIE0_OFFSET 0x2000 ++ ++#define RALINK_PCI_PCICFG_ADDR 0x00 ++#define RALINK_PCI0_BAR0SETUP_ADDR 0x10 ++#define RALINK_PCI0_IMBASEBAR0_ADDR 0x18 ++#define RALINK_PCI0_ID 0x30 ++#define RALINK_PCI0_CLASS 0x34 ++#define RALINK_PCI0_SUBID 0x38 ++#define RALINK_PCI0_STATUS 0x50 ++#define RALINK_PCI_PCIMSK_ADDR 0x0C ++ ++#define RALINK_PCIE0_CLK_EN (1 << 26) ++ ++#define BUSY 0x80000000 ++#define WAITRETRY_MAX 10 ++#define WRITE_MODE (1UL << 23) ++#define DATA_SHIFT 0 ++#define ADDR_SHIFT 8 ++ ++ ++static void __iomem *bridge_base; ++static void __iomem *pcie_base; ++ ++static struct reset_control *rstpcie0; ++ ++static inline void bridge_w32(u32 val, unsigned reg) ++{ ++ iowrite32(val, bridge_base + reg); ++} ++ ++static inline u32 bridge_r32(unsigned reg) ++{ ++ return ioread32(bridge_base + reg); ++} ++ ++static inline void pcie_w32(u32 val, unsigned reg) ++{ ++ iowrite32(val, pcie_base + reg); ++} ++ ++static inline u32 pcie_r32(unsigned reg) ++{ ++ return ioread32(pcie_base + reg); ++} ++ ++static inline void pcie_m32(u32 clr, u32 set, unsigned reg) ++{ ++ u32 val = pcie_r32(reg); ++ val &= ~clr; ++ val |= set; ++ pcie_w32(val, reg); ++} ++ ++int wait_pciephy_busy(void) ++{ ++ unsigned long reg_value = 0x0, retry = 0; ++ ++ while (1) { ++ //reg_value = rareg(READMODE, PCIEPHY0_CFG, 0); ++ reg_value = pcie_r32(PCIEPHY0_CFG); ++ ++ if (reg_value & BUSY) ++ mdelay(100); ++ else ++ break; ++ if (retry++ > WAITRETRY_MAX){ ++ printk("PCIE-PHY retry failed.\n"); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static void pcie_phy(unsigned long addr, unsigned long val) ++{ ++ wait_pciephy_busy(); ++ pcie_w32(WRITE_MODE | (val << DATA_SHIFT) | (addr << ADDR_SHIFT), PCIEPHY0_CFG); ++ mdelay(1); ++ wait_pciephy_busy(); ++} ++ ++static int pci_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 * val) ++{ ++ unsigned int slot = PCI_SLOT(devfn); ++ u8 func = PCI_FUNC(devfn); ++ u32 address; ++ u32 data; ++ ++ address = (((where & 0xF00) >> 8) << 24) | (bus->number << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | 0x80000000; ++ bridge_w32(address, RALINK_PCI_CONFIG_ADDR); ++ data = bridge_r32(RALINK_PCI_CONFIG_DATA_VIRTUAL_REG); ++ ++ switch (size) { ++ case 1: ++ *val = (data >> ((where & 3) << 3)) & 0xff; ++ break; ++ case 2: ++ *val = (data >> ((where & 3) << 3)) & 0xffff; ++ break; ++ case 4: ++ *val = data; ++ break; ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int pci_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) ++{ ++ unsigned int slot = PCI_SLOT(devfn); ++ u8 func = PCI_FUNC(devfn); ++ u32 address; ++ u32 data; ++ ++ address = (((where & 0xF00) >> 8) << 24) | (bus->number << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | 0x80000000; ++ bridge_w32(address, RALINK_PCI_CONFIG_ADDR); ++ data = bridge_r32(RALINK_PCI_CONFIG_DATA_VIRTUAL_REG); ++ ++ switch (size) { ++ case 1: ++ data = (data & ~(0xff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 2: ++ data = (data & ~(0xffff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 4: ++ data = val; ++ break; ++ } ++ ++ bridge_w32(data, RALINK_PCI_CONFIG_DATA_VIRTUAL_REG); ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++struct pci_ops mt7620a_pci_ops= { ++ .read = pci_config_read, ++ .write = pci_config_write, ++}; ++ ++static struct resource mt7620a_res_pci_mem1 = { ++ .name = "pci memory", ++ .start = RALINK_PCI_MM_MAP_BASE, ++ .end = (u32) ((RALINK_PCI_MM_MAP_BASE + (unsigned char *)0x0fffffff)), ++ .flags = IORESOURCE_MEM, ++}; ++static struct resource mt7620a_res_pci_io1 = { ++ .name = "pci io", ++ .start = RALINK_PCI_IO_MAP_BASE, ++ .end = (u32) ((RALINK_PCI_IO_MAP_BASE + (unsigned char *)0x0ffff)), ++ .flags = IORESOURCE_IO, ++}; ++ ++struct pci_controller mt7620a_controller = { ++ .pci_ops = &mt7620a_pci_ops, ++ .mem_resource = &mt7620a_res_pci_mem1, ++ .io_resource = &mt7620a_res_pci_io1, ++ .mem_offset = 0x00000000UL, ++ .io_offset = 0x00000000UL, ++ .io_map_base = 0xa0000000, ++}; ++ ++static int mt7620a_pci_probe(struct platform_device *pdev) ++{ ++ struct resource *bridge_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ struct resource *pcie_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ ++ rstpcie0 = devm_reset_control_get(&pdev->dev, "pcie0"); ++ if (IS_ERR(rstpcie0)) ++ return PTR_ERR(rstpcie0); ++ ++ bridge_base = devm_request_and_ioremap(&pdev->dev, bridge_res); ++ if (!bridge_base) ++ return -ENOMEM; ++ ++ pcie_base = devm_request_and_ioremap(&pdev->dev, pcie_res); ++ if (!pcie_base) ++ return -ENOMEM; ++ ++ iomem_resource.start = 0; ++ iomem_resource.end= ~0; ++ ioport_resource.start= 0; ++ ioport_resource.end = ~0; ++ ++ /* PCIE: bypass PCIe DLL */ ++ pcie_phy(0x0, 0x80); ++ pcie_phy(0x1, 0x04); ++ /* PCIE: Elastic buffer control */ ++ pcie_phy(0x68, 0xB4); ++ ++ reset_control_assert(rstpcie0); ++ rt_sysc_m32(RALINK_PCIE0_CLK_EN, 0, RALINK_CLKCFG1); ++ rt_sysc_m32(1<<19, 1<<31, PPLL_DRV); ++ rt_sysc_m32(0x3 << 16, 0, RALINK_GPIOMODE); ++ ++ reset_control_deassert(rstpcie0); ++ rt_sysc_m32(0, RALINK_PCIE0_CLK_EN, RALINK_CLKCFG1); ++ ++ mdelay(100); ++ ++ if (!(rt_sysc_r32(PPLL_CFG1) & 1<<23)) { ++ printk("MT7620 PPLL unlock\n"); ++ reset_control_assert(rstpcie0); ++ rt_sysc_m32(BIT(26), 0, RALINK_CLKCFG1); ++ return 0; ++ } ++ rt_sysc_m32((0x1<<18) | (0x1<<17), (0x1 << 19) | (0x1 << 31), PPLL_DRV); ++ ++ mdelay(100); ++ reset_control_assert(rstpcie0); ++ rt_sysc_m32(0x30, 2 << 4, RALINK_SYSCFG1); ++ ++ rt_sysc_m32(~0x7fffffff, 0x80000000, RALINK_PCIE_CLK_GEN); ++ rt_sysc_m32(~0x80ffffff, 0xa << 24, RALINK_PCIE_CLK_GEN1); ++ ++ mdelay(50); ++ reset_control_deassert(rstpcie0); ++ pcie_m32(BIT(1), 0, RALINK_PCI_PCICFG_ADDR); ++ mdelay(100); ++ ++ if (( pcie_r32(RALINK_PCI0_STATUS) & 0x1) == 0) { ++ reset_control_assert(rstpcie0); ++ rt_sysc_m32(RALINK_PCIE0_CLK_EN, 0, RALINK_CLKCFG1); ++ rt_sysc_m32(LC_CKDRVPD_, PDRV_SW_SET, PPLL_DRV); ++ printk("PCIE0 no card, disable it(RST&CLK)\n"); ++ } ++ ++ bridge_w32(0xffffffff, RALINK_PCI_MEMBASE); ++ bridge_w32(RALINK_PCI_IO_MAP_BASE, RALINK_PCI_IOBASE); ++ ++ pcie_w32(0x7FFF0000, RALINK_PCI0_BAR0SETUP_ADDR); ++ pcie_w32(MEMORY_BASE, RALINK_PCI0_IMBASEBAR0_ADDR); ++ pcie_w32(0x08021814, RALINK_PCI0_ID); ++ pcie_w32(0x06040001, RALINK_PCI0_CLASS); ++ pcie_w32(0x28801814, RALINK_PCI0_SUBID); ++ pcie_m32(0, BIT(20), RALINK_PCI_PCIMSK_ADDR); ++ ++ register_pci_controller(&mt7620a_controller); ++ ++ return 0; ++} ++ ++int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ const struct resource *res; ++ u16 cmd; ++ u32 val; ++ int i, irq = 0; ++ ++ if ((dev->bus->number == 0) && (slot == 0)) { ++ pcie_w32(0x7FFF0001, RALINK_PCI0_BAR0SETUP_ADDR); //open 7FFF:2G; ENABLE ++ pci_config_write(dev->bus, 0, PCI_BASE_ADDRESS_0, 4, MEMORY_BASE); ++ pci_config_read(dev->bus, 0, PCI_BASE_ADDRESS_0, 4, &val); ++ } else if ((dev->bus->number == 1) && (slot == 0x0)) { ++ irq = RALINK_INT_PCIE0; ++ } else { ++ printk("bus=0x%x, slot = 0x%x\n", dev->bus->number, slot); ++ return 0; ++ } ++ ++ for (i = 0; i < 6; i++) { ++ res = &dev->resource[i]; ++ } ++ ++ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x14); //configure cache line size 0x14 ++ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xFF); //configure latency timer 0x10 ++ pci_read_config_word(dev, PCI_COMMAND, &cmd); ++ ++ // FIXME ++ cmd = cmd | PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; ++ pci_write_config_word(dev, PCI_COMMAND, cmd); ++ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); ++ //pci_write_config_byte(dev, PCI_INTERRUPT_PIN, dev->irq); ++ ++ return irq; ++} ++ ++int pcibios_plat_dev_init(struct pci_dev *dev) ++{ ++ return 0; ++} ++ ++static const struct of_device_id mt7620a_pci_ids[] = { ++ { .compatible = "ralink,mt7620a-pci" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mt7620a_pci_ids); ++ ++static struct platform_driver mt7620a_pci_driver = { ++ .probe = mt7620a_pci_probe, ++ .driver = { ++ .name = "mt7620a-pci", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(mt7620a_pci_ids), ++ }, ++}; ++ ++static int __init mt7620a_pci_init(void) ++{ ++ return platform_driver_register(&mt7620a_pci_driver); ++} ++ ++arch_initcall(mt7620a_pci_init); +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index f2f20ec..f89fdf8 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -24,6 +24,7 @@ choice + + config SOC_MT7620 + bool "MT7620" ++ select HW_HAS_PCI + + endchoice + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0065-watchdog-adds-ralink-wdt.patch b/target/linux/ramips/patches-3.8/0065-watchdog-adds-ralink-wdt.patch new file mode 100644 index 0000000000..5d446eea9b --- /dev/null +++ b/target/linux/ramips/patches-3.8/0065-watchdog-adds-ralink-wdt.patch @@ -0,0 +1,275 @@ +From 8eaa4bef40d38a4954389a2552504ea2d1c4a68a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Apr 2013 23:23:07 +0200 +Subject: [PATCH 65/79] watchdog: adds ralink wdt + +Adds the watchdog driver for ralink SoC. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/mt7620.c | 1 + + drivers/watchdog/Kconfig | 7 ++ + drivers/watchdog/Makefile | 1 + + drivers/watchdog/rt2880_wdt.c | 208 +++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 217 insertions(+) + create mode 100644 drivers/watchdog/rt2880_wdt.c + +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index 08c96db6..4956d96 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -182,6 +182,7 @@ void __init ralink_clk_init(void) + + ralink_clk_add("cpu", cpu_rate); + ralink_clk_add("10000100.timer", 40000000); ++ ralink_clk_add("10000120.watchdog", 40000000); + ralink_clk_add("10000500.uart", 40000000); + ralink_clk_add("10000b00.spi", 40000000); + ralink_clk_add("10000c00.uartlite", 40000000); +diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig +index 7f809fd..c8c7454 100644 +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -1076,6 +1076,13 @@ config LANTIQ_WDT + help + Hardware driver for the Lantiq SoC Watchdog Timer. + ++config RALINK_WDT ++ tristate "Ralink SoC watchdog" ++ select WATCHDOG_CORE ++ depends on RALINK ++ help ++ Hardware driver for the Ralink SoC Watchdog Timer. ++ + # PARISC Architecture + + # POWERPC Architecture +diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile +index 97bbdb3a..0203784 100644 +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -132,6 +132,7 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o + obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o + octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o + obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o ++obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o + + # PARISC Architecture + +diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c +new file mode 100644 +index 0000000..3df65a4 +--- /dev/null ++++ b/drivers/watchdog/rt2880_wdt.c +@@ -0,0 +1,208 @@ ++/* ++ * Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer ++ * ++ * Copyright (C) 2011 Gabor Juhos ++ * Copyright (C) 2013 John Crispin ++ * ++ * This driver was based on: drivers/watchdog/softdog.c ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define SYSC_RSTSTAT 0x38 ++#define WDT_RST_CAUSE BIT(1) ++ ++#define RALINK_WDT_TIMEOUT 30 ++#define RALINK_WDT_PRESCALE 65536 ++ ++#define TIMER_REG_TMR1LOAD 0x00 ++#define TIMER_REG_TMR1CTL 0x08 ++ ++#define TMRSTAT_TMR1RST BIT(5) ++ ++#define TMR1CTL_ENABLE BIT(7) ++#define TMR1CTL_MODE_SHIFT 4 ++#define TMR1CTL_MODE_MASK 0x3 ++#define TMR1CTL_MODE_FREE_RUNNING 0x0 ++#define TMR1CTL_MODE_PERIODIC 0x1 ++#define TMR1CTL_MODE_TIMEOUT 0x2 ++#define TMR1CTL_MODE_WDT 0x3 ++#define TMR1CTL_PRESCALE_MASK 0xf ++#define TMR1CTL_PRESCALE_65536 0xf ++ ++static struct clk *rt288x_wdt_clk; ++static unsigned long rt288x_wdt_freq; ++static void __iomem *rt288x_wdt_base; ++ ++static bool nowayout = WATCHDOG_NOWAYOUT; ++module_param(nowayout, bool, 0); ++MODULE_PARM_DESC(nowayout, ++ "Watchdog cannot be stopped once started (default=" ++ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); ++ ++static inline void rt_wdt_w32(unsigned reg, u32 val) ++{ ++ iowrite32(val, rt288x_wdt_base + reg); ++} ++ ++static inline u32 rt_wdt_r32(unsigned reg) ++{ ++ return ioread32(rt288x_wdt_base + reg); ++} ++ ++static int rt288x_wdt_ping(struct watchdog_device *w) ++{ ++ rt_wdt_w32(TIMER_REG_TMR1LOAD, w->timeout * rt288x_wdt_freq); ++ ++ return 0; ++} ++ ++static int rt288x_wdt_start(struct watchdog_device *w) ++{ ++ u32 t; ++ ++ t = rt_wdt_r32(TIMER_REG_TMR1CTL); ++ t &= ~(TMR1CTL_MODE_MASK << TMR1CTL_MODE_SHIFT | ++ TMR1CTL_PRESCALE_MASK); ++ t |= (TMR1CTL_MODE_WDT << TMR1CTL_MODE_SHIFT | ++ TMR1CTL_PRESCALE_65536); ++ rt_wdt_w32(TIMER_REG_TMR1CTL, t); ++ ++ rt288x_wdt_ping(w); ++ ++ t = rt_wdt_r32(TIMER_REG_TMR1CTL); ++ t |= TMR1CTL_ENABLE; ++ rt_wdt_w32(TIMER_REG_TMR1CTL, t); ++ ++ return 0; ++} ++ ++static int rt288x_wdt_stop(struct watchdog_device *w) ++{ ++ u32 t; ++ ++ rt288x_wdt_ping(w); ++ ++ t = rt_wdt_r32(TIMER_REG_TMR1CTL); ++ t &= ~TMR1CTL_ENABLE; ++ rt_wdt_w32(TIMER_REG_TMR1CTL, t); ++ ++ return 0; ++} ++ ++static int rt288x_wdt_set_timeout(struct watchdog_device *w, unsigned int t) ++{ ++ w->timeout = t; ++ rt288x_wdt_ping(w); ++ ++ return 0; ++} ++ ++static int rt288x_wdt_bootcause(void) ++{ ++ if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE) ++ return WDIOF_CARDRESET; ++ ++ return 0; ++} ++ ++static struct watchdog_info rt288x_wdt_info = { ++ .identity = "Ralink Watchdog", ++ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, ++}; ++ ++static struct watchdog_ops rt288x_wdt_ops = { ++ .owner = THIS_MODULE, ++ .start = rt288x_wdt_start, ++ .stop = rt288x_wdt_stop, ++ .ping = rt288x_wdt_ping, ++ .set_timeout = rt288x_wdt_set_timeout, ++}; ++ ++static struct watchdog_device rt288x_wdt_dev = { ++ .info = &rt288x_wdt_info, ++ .ops = &rt288x_wdt_ops, ++ .min_timeout = 1, ++}; ++ ++static int rt288x_wdt_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ int ret; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ rt288x_wdt_base = devm_request_and_ioremap(&pdev->dev, res); ++ if (IS_ERR(rt288x_wdt_base)) ++ return PTR_ERR(rt288x_wdt_base); ++ ++ rt288x_wdt_clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(rt288x_wdt_clk)) ++ return PTR_ERR(rt288x_wdt_clk); ++ ++ device_reset(&pdev->dev); ++ ++ rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE; ++ ++ rt288x_wdt_dev.dev = &pdev->dev; ++ rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause(); ++ ++ rt288x_wdt_dev.timeout = RALINK_WDT_TIMEOUT; ++ rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq); ++ ++ watchdog_set_nowayout(&rt288x_wdt_dev, nowayout); ++ ++ ret = watchdog_register_device(&rt288x_wdt_dev); ++ if (!ret) ++ dev_info(&pdev->dev, "Initialized\n"); ++ ++ return 0; ++} ++ ++static int rt288x_wdt_remove(struct platform_device *pdev) ++{ ++ watchdog_unregister_device(&rt288x_wdt_dev); ++ ++ return 0; ++} ++ ++static void rt288x_wdt_shutdown(struct platform_device *pdev) ++{ ++ rt288x_wdt_stop(&rt288x_wdt_dev); ++} ++ ++static const struct of_device_id rt288x_wdt_match[] = { ++ { .compatible = "ralink,rt2880-wdt" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt288x_wdt_match); ++ ++static struct platform_driver rt288x_wdt_driver = { ++ .probe = rt288x_wdt_probe, ++ .remove = rt288x_wdt_remove, ++ .shutdown = rt288x_wdt_shutdown, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .of_match_table = rt288x_wdt_match, ++ }, ++}; ++ ++module_platform_driver(rt288x_wdt_driver); ++ ++MODULE_DESCRIPTION("MediaTek/Ralink RT288x/RT3xxx hardware watchdog driver"); ++MODULE_AUTHOR("Gabor Juhos +Date: Mon, 29 Apr 2013 14:40:43 +0200 +Subject: [PATCH 66/79] i2c: MIPS: adds ralink I2C driver + +Signed-off-by: John Crispin +--- + .../devicetree/bindings/i2c/i2c-ralink.txt | 27 ++ + drivers/i2c/busses/Kconfig | 4 + + drivers/i2c/busses/Makefile | 1 + + drivers/i2c/busses/i2c-ralink.c | 274 ++++++++++++++++++++ + 4 files changed, 306 insertions(+) + create mode 100644 Documentation/devicetree/bindings/i2c/i2c-ralink.txt + create mode 100644 drivers/i2c/busses/i2c-ralink.c + +diff --git a/Documentation/devicetree/bindings/i2c/i2c-ralink.txt b/Documentation/devicetree/bindings/i2c/i2c-ralink.txt +new file mode 100644 +index 0000000..8fa8ac3 +--- /dev/null ++++ b/Documentation/devicetree/bindings/i2c/i2c-ralink.txt +@@ -0,0 +1,27 @@ ++I2C for Ralink platforms ++ ++Required properties : ++- compatible : Must be "link,rt3052-i2c" ++- reg: physical base address of the controller and length of memory mapped ++ region. ++- #address-cells = <1>; ++- #size-cells = <0>; ++ ++Optional properties: ++- Child nodes conforming to i2c bus binding ++ ++Example : ++ ++palmbus@10000000 { ++ i2c@900 { ++ compatible = "link,rt3052-i2c"; ++ reg = <0x900 0x100>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hwmon@4b { ++ compatible = "national,lm92"; ++ reg = <0x4b>; ++ }; ++ }; ++}; +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index bdca511..19b1105 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -604,6 +604,10 @@ config I2C_PXA_SLAVE + is necessary for systems where the PXA may be a target on the + I2C bus. + ++config I2C_RALINK ++ tristate "Ralink I2C Controller" ++ select OF_I2C ++ + config HAVE_S3C2410_I2C + bool + help +diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile +index 6181f3f..4cf4a88 100644 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -60,6 +60,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o + obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o + obj-$(CONFIG_I2C_PXA) += i2c-pxa.o + obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o ++obj-$(CONFIG_I2C_RALINK) += i2c-ralink.o + obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o + obj-$(CONFIG_I2C_S6000) += i2c-s6000.o + obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o +diff --git a/drivers/i2c/busses/i2c-ralink.c b/drivers/i2c/busses/i2c-ralink.c +new file mode 100644 +index 0000000..b5abf0f +--- /dev/null ++++ b/drivers/i2c/busses/i2c-ralink.c +@@ -0,0 +1,274 @@ ++/* ++ * drivers/i2c/busses/i2c-ralink.c ++ * ++ * Copyright (C) 2013 Steven Liu ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define REG_CONFIG_REG 0x00 ++#define REG_CLKDIV_REG 0x04 ++#define REG_DEVADDR_REG 0x08 ++#define REG_ADDR_REG 0x0C ++#define REG_DATAOUT_REG 0x10 ++#define REG_DATAIN_REG 0x14 ++#define REG_STATUS_REG 0x18 ++#define REG_STARTXFR_REG 0x1C ++#define REG_BYTECNT_REG 0x20 ++ ++#define I2C_STARTERR BIT(4) ++#define I2C_ACKERR BIT(3) ++#define I2C_DATARDY BIT(2) ++#define I2C_SDOEMPTY BIT(1) ++#define I2C_BUSY BIT(0) ++ ++#define I2C_DEVADLEN_7 (6 << 2) ++#define I2C_ADDRDIS BIT(1) ++ ++#define I2C_RETRY 0x400 ++ ++#define CLKDIV_VALUE 600 ++ ++#define READ_CMD 0x01 ++#define WRITE_CMD 0x00 ++#define READ_BLOCK 64 ++ ++static void __iomem *membase; ++static struct i2c_adapter *adapter; ++ ++static void rt_i2c_w32(u32 val, unsigned reg) ++{ ++ iowrite32(val, membase + reg); ++} ++ ++static u32 rt_i2c_r32(unsigned reg) ++{ ++ return ioread32(membase + reg); ++} ++ ++static inline int rt_i2c_wait_rx_done(void) ++{ ++ int retries = I2C_RETRY; ++ ++ do { ++ if (!retries--) ++ break; ++ } while(!(rt_i2c_r32(REG_STATUS_REG) & I2C_DATARDY)); ++ ++ return (retries < 0); ++} ++ ++static inline int rt_i2c_wait_idle(void) ++{ ++ int retries = I2C_RETRY; ++ ++ do { ++ if (!retries--) ++ break; ++ } while(rt_i2c_r32(REG_STATUS_REG) & I2C_BUSY); ++ ++ return (retries < 0); ++} ++ ++static inline int rt_i2c_wait_tx_done(void) ++{ ++ int retries = I2C_RETRY; ++ ++ do { ++ if (!retries--) ++ break; ++ } while(!(rt_i2c_r32(REG_STATUS_REG) & I2C_SDOEMPTY)); ++ ++ return (retries < 0); ++} ++ ++static int rt_i2c_handle_msg(struct i2c_adapter *a, struct i2c_msg* msg) ++{ ++ int i = 0, j = 0, pos = 0; ++ int nblock = msg->len / READ_BLOCK; ++ int rem = msg->len % READ_BLOCK; ++ ++ if (msg->flags & I2C_M_TEN) { ++ printk("10 bits addr not supported\n"); ++ return -EINVAL; ++ } ++ ++ if (msg->flags & I2C_M_RD) { ++ for (i = 0; i < nblock; i++) { ++ rt_i2c_wait_idle(); ++ rt_i2c_w32(READ_BLOCK - 1, REG_BYTECNT_REG); ++ rt_i2c_w32(READ_CMD, REG_STARTXFR_REG); ++ for (j = 0; j < READ_BLOCK; j++) { ++ if (rt_i2c_wait_rx_done()) ++ return -1; ++ msg->buf[pos++] = rt_i2c_r32(REG_DATAIN_REG); ++ } ++ } ++ ++ rt_i2c_wait_idle(); ++ rt_i2c_w32(rem - 1, REG_BYTECNT_REG); ++ rt_i2c_w32(READ_CMD, REG_STARTXFR_REG); ++ for (i = 0; i < rem; i++) { ++ if (rt_i2c_wait_rx_done()) ++ return -1; ++ msg->buf[pos++] = rt_i2c_r32(REG_DATAIN_REG); ++ } ++ } else { ++ rt_i2c_wait_idle(); ++ rt_i2c_w32(msg->len - 1, REG_BYTECNT_REG); ++ rt_i2c_w32(msg->buf[0], REG_DATAOUT_REG); ++ rt_i2c_w32(WRITE_CMD, REG_STARTXFR_REG); ++ for (i = 1; i < msg->len; i++) { ++ rt_i2c_w32(msg->buf[i], REG_DATAOUT_REG); ++ if (rt_i2c_wait_tx_done()) ++ return -1; ++ } ++ } ++ ++ return msg->len; ++} ++ ++static int rt_i2c_master_xfer(struct i2c_adapter *a, struct i2c_msg *m, int n) ++{ ++ int i = 0; ++ int ret = 0; ++ ++ if (rt_i2c_wait_idle()) { ++ printk("i2c transfer failed\n"); ++ return 0; ++ } ++ ++ device_reset(a->dev.parent); ++ ++ rt_i2c_w32(m->addr, REG_DEVADDR_REG); ++ rt_i2c_w32(I2C_DEVADLEN_7 | I2C_ADDRDIS, REG_CONFIG_REG); ++ rt_i2c_w32(CLKDIV_VALUE, REG_CLKDIV_REG); ++ ++ for (i = 0; i < n && !ret; i++) ++ ret = rt_i2c_handle_msg(a, &m[i]); ++ ++ if (ret) { ++ printk("i2c transfer failed\n"); ++ return 0; ++ } ++ ++ return n; ++} ++ ++static u32 rt_i2c_func(struct i2c_adapter *a) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; ++} ++ ++static const struct i2c_algorithm rt_i2c_algo = { ++ .master_xfer = rt_i2c_master_xfer, ++ .functionality = rt_i2c_func, ++}; ++ ++static int rt_i2c_probe(struct platform_device *pdev) ++{ ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ int ret; ++ ++ if (!res) { ++ dev_err(&pdev->dev, "no memory resource found\n"); ++ return -ENODEV; ++ } ++ ++ adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter), GFP_KERNEL); ++ if (!adapter) { ++ dev_err(&pdev->dev, "failed to allocate i2c_adapter\n"); ++ return -ENOMEM; ++ } ++ ++ membase = devm_request_and_ioremap(&pdev->dev, res); ++ if (IS_ERR(membase)) ++ return PTR_ERR(membase); ++ ++ strlcpy(adapter->name, dev_name(&pdev->dev), sizeof(adapter->name)); ++ adapter->owner = THIS_MODULE; ++ adapter->nr = pdev->id; ++ adapter->timeout = HZ; ++ adapter->algo = &rt_i2c_algo; ++ adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; ++ adapter->dev.parent = &pdev->dev; ++ adapter->dev.of_node = pdev->dev.of_node; ++ ++ ret = i2c_add_numbered_adapter(adapter); ++ if (ret) ++ return ret; ++ ++ of_i2c_register_devices(adapter); ++ ++ platform_set_drvdata(pdev, adapter); ++ ++ dev_info(&pdev->dev, "loaded\n"); ++ ++ return 0; ++} ++ ++static int rt_i2c_remove(struct platform_device *pdev) ++{ ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++static const struct of_device_id i2c_rt_dt_ids[] = { ++ { .compatible = "ralink,rt2880-i2c", }, ++ { /* sentinel */ } ++}; ++ ++MODULE_DEVICE_TABLE(of, i2c_rt_dt_ids); ++ ++static struct platform_driver rt_i2c_driver = { ++ .probe = rt_i2c_probe, ++ .remove = rt_i2c_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "i2c-ralink", ++ .of_match_table = i2c_rt_dt_ids, ++ }, ++}; ++ ++static int __init i2c_rt_init (void) ++{ ++ return platform_driver_register(&rt_i2c_driver); ++} ++subsys_initcall(i2c_rt_init); ++ ++static void __exit i2c_rt_exit (void) ++{ ++ platform_driver_unregister(&rt_i2c_driver); ++} ++ ++module_exit (i2c_rt_exit); ++ ++MODULE_AUTHOR("Steven Liu "); ++MODULE_DESCRIPTION("Ralink I2c host driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:Ralink-I2C"); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0067-reset-Add-reset-controller-API.patch b/target/linux/ramips/patches-3.8/0067-reset-Add-reset-controller-API.patch new file mode 100644 index 0000000000..55bbb56d00 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0067-reset-Add-reset-controller-API.patch @@ -0,0 +1,554 @@ +From 8951e08252c6c254d68c350468c40f88a12bcc1d Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 20 May 2013 15:42:01 +0200 +Subject: [PATCH 67/79] reset: Add reset controller API + +backport from v3.10-rc1 +61fc41317666be400802ac793f47de816ef7bd57 +6034bb22d8387708075c083385e5d2e1072a4f33 +4e11f848c65b1c87782cb232a6e3b47a9d4c1f98 + +This adds a simple API for devices to request being reset +by separate reset controller hardware and implements the +reset signal device tree binding. + +Signed-off-by: Philipp Zabel +Reviewed-by: Stephen Warren +Reviewed-by: Shawn Guo +Reviewed-by: Marek Vasut +Reviewed-by: Pavel Machek +--- + Documentation/devicetree/bindings/reset/reset.txt | 75 ++++++ + drivers/Kconfig | 2 + + drivers/Makefile | 3 + + drivers/reset/Kconfig | 13 + + drivers/reset/Makefile | 1 + + drivers/reset/core.c | 297 +++++++++++++++++++++ + include/linux/reset-controller.h | 51 ++++ + include/linux/reset.h | 17 ++ + 8 files changed, 459 insertions(+) + create mode 100644 Documentation/devicetree/bindings/reset/reset.txt + create mode 100644 drivers/reset/Kconfig + create mode 100644 drivers/reset/Makefile + create mode 100644 drivers/reset/core.c + create mode 100644 include/linux/reset-controller.h + create mode 100644 include/linux/reset.h + +diff --git a/Documentation/devicetree/bindings/reset/reset.txt b/Documentation/devicetree/bindings/reset/reset.txt +new file mode 100644 +index 0000000..31db6ff +--- /dev/null ++++ b/Documentation/devicetree/bindings/reset/reset.txt +@@ -0,0 +1,75 @@ ++= Reset Signal Device Tree Bindings = ++ ++This binding is intended to represent the hardware reset signals present ++internally in most IC (SoC, FPGA, ...) designs. Reset signals for whole ++standalone chips are most likely better represented as GPIOs, although there ++are likely to be exceptions to this rule. ++ ++Hardware blocks typically receive a reset signal. This signal is generated by ++a reset provider (e.g. power management or clock module) and received by a ++reset consumer (the module being reset, or a module managing when a sub- ++ordinate module is reset). This binding exists to represent the provider and ++consumer, and provide a way to couple the two together. ++ ++A reset signal is represented by the phandle of the provider, plus a reset ++specifier - a list of DT cells that represents the reset signal within the ++provider. The length (number of cells) and semantics of the reset specifier ++are dictated by the binding of the reset provider, although common schemes ++are described below. ++ ++A word on where to place reset signal consumers in device tree: It is possible ++in hardware for a reset signal to affect multiple logically separate HW blocks ++at once. In this case, it would be unwise to represent this reset signal in ++the DT node of each affected HW block, since if activated, an unrelated block ++may be reset. Instead, reset signals should be represented in the DT node ++where it makes most sense to control it; this may be a bus node if all ++children of the bus are affected by the reset signal, or an individual HW ++block node for dedicated reset signals. The intent of this binding is to give ++appropriate software access to the reset signals in order to manage the HW, ++rather than to slavishly enumerate the reset signal that affects each HW ++block. ++ ++= Reset providers = ++ ++Required properties: ++#reset-cells: Number of cells in a reset specifier; Typically 0 for nodes ++ with a single reset output and 1 for nodes with multiple ++ reset outputs. ++ ++For example: ++ ++ rst: reset-controller { ++ #reset-cells = <1>; ++ }; ++ ++= Reset consumers = ++ ++Required properties: ++resets: List of phandle and reset specifier pairs, one pair ++ for each reset signal that affects the device, or that the ++ device manages. Note: if the reset provider specifies '0' for ++ #reset-cells, then only the phandle portion of the pair will ++ appear. ++ ++Optional properties: ++reset-names: List of reset signal name strings sorted in the same order as ++ the resets property. Consumers drivers will use reset-names to ++ match reset signal names with reset specifiers. ++ ++For example: ++ ++ device { ++ resets = <&rst 20>; ++ reset-names = "reset"; ++ }; ++ ++This represents a device with a single reset signal named "reset". ++ ++ bus { ++ resets = <&rst 10> <&rst 11> <&rst 12> <&rst 11>; ++ reset-names = "i2s1", "i2s2", "dma", "mixer"; ++ }; ++ ++This represents a bus that controls the reset signal of each of four sub- ++ordinate devices. Consider for example a bus that fails to operate unless no ++child device has reset asserted. +diff --git a/drivers/Kconfig b/drivers/Kconfig +index f5fb072..51f73ae 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -158,4 +158,6 @@ source "drivers/irqchip/Kconfig" + + source "drivers/ipack/Kconfig" + ++source "drivers/reset/Kconfig" ++ + endmenu +diff --git a/drivers/Makefile b/drivers/Makefile +index 7863b9f..2819ac0 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -37,6 +37,9 @@ obj-$(CONFIG_XEN) += xen/ + # regulators early, since some subsystems rely on them to initialize + obj-$(CONFIG_REGULATOR) += regulator/ + ++# reset controllers early, since gpu drivers might rely on them to initialize ++obj-$(CONFIG_RESET_CONTROLLER) += reset/ ++ + # tty/ comes before char/ so that the VT console is the boot-time + # default. + obj-y += tty/ +diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig +new file mode 100644 +index 0000000..c9d04f7 +--- /dev/null ++++ b/drivers/reset/Kconfig +@@ -0,0 +1,13 @@ ++config ARCH_HAS_RESET_CONTROLLER ++ bool ++ ++menuconfig RESET_CONTROLLER ++ bool "Reset Controller Support" ++ default y if ARCH_HAS_RESET_CONTROLLER ++ help ++ Generic Reset Controller support. ++ ++ This framework is designed to abstract reset handling of devices ++ via GPIOs or SoC-internal reset controller modules. ++ ++ If unsure, say no. +diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile +new file mode 100644 +index 0000000..1e2d83f +--- /dev/null ++++ b/drivers/reset/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_RESET_CONTROLLER) += core.o +diff --git a/drivers/reset/core.c b/drivers/reset/core.c +new file mode 100644 +index 0000000..d1b6089 +--- /dev/null ++++ b/drivers/reset/core.c +@@ -0,0 +1,297 @@ ++/* ++ * Reset Controller framework ++ * ++ * Copyright 2013 Philipp Zabel, Pengutronix ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static DEFINE_MUTEX(reset_controller_list_mutex); ++static LIST_HEAD(reset_controller_list); ++ ++/** ++ * struct reset_control - a reset control ++ * @rcdev: a pointer to the reset controller device ++ * this reset control belongs to ++ * @id: ID of the reset controller in the reset ++ * controller device ++ */ ++struct reset_control { ++ struct reset_controller_dev *rcdev; ++ struct device *dev; ++ unsigned int id; ++}; ++ ++/** ++ * of_reset_simple_xlate - translate reset_spec to the reset line number ++ * @rcdev: a pointer to the reset controller device ++ * @reset_spec: reset line specifier as found in the device tree ++ * @flags: a flags pointer to fill in (optional) ++ * ++ * This simple translation function should be used for reset controllers ++ * with 1:1 mapping, where reset lines can be indexed by number without gaps. ++ */ ++int of_reset_simple_xlate(struct reset_controller_dev *rcdev, ++ const struct of_phandle_args *reset_spec) ++{ ++ if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells)) ++ return -EINVAL; ++ ++ if (reset_spec->args[0] >= rcdev->nr_resets) ++ return -EINVAL; ++ ++ return reset_spec->args[0]; ++} ++EXPORT_SYMBOL_GPL(of_reset_simple_xlate); ++ ++/** ++ * reset_controller_register - register a reset controller device ++ * @rcdev: a pointer to the initialized reset controller device ++ */ ++int reset_controller_register(struct reset_controller_dev *rcdev) ++{ ++ if (!rcdev->of_xlate) { ++ rcdev->of_reset_n_cells = 1; ++ rcdev->of_xlate = of_reset_simple_xlate; ++ } ++ ++ mutex_lock(&reset_controller_list_mutex); ++ list_add(&rcdev->list, &reset_controller_list); ++ mutex_unlock(&reset_controller_list_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(reset_controller_register); ++ ++/** ++ * reset_controller_unregister - unregister a reset controller device ++ * @rcdev: a pointer to the reset controller device ++ */ ++void reset_controller_unregister(struct reset_controller_dev *rcdev) ++{ ++ mutex_lock(&reset_controller_list_mutex); ++ list_del(&rcdev->list); ++ mutex_unlock(&reset_controller_list_mutex); ++} ++EXPORT_SYMBOL_GPL(reset_controller_unregister); ++ ++/** ++ * reset_control_reset - reset the controlled device ++ * @rstc: reset controller ++ */ ++int reset_control_reset(struct reset_control *rstc) ++{ ++ if (rstc->rcdev->ops->reset) ++ return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); ++ ++ return -ENOSYS; ++} ++EXPORT_SYMBOL_GPL(reset_control_reset); ++ ++/** ++ * reset_control_assert - asserts the reset line ++ * @rstc: reset controller ++ */ ++int reset_control_assert(struct reset_control *rstc) ++{ ++ if (rstc->rcdev->ops->assert) ++ return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); ++ ++ return -ENOSYS; ++} ++EXPORT_SYMBOL_GPL(reset_control_assert); ++ ++/** ++ * reset_control_deassert - deasserts the reset line ++ * @rstc: reset controller ++ */ ++int reset_control_deassert(struct reset_control *rstc) ++{ ++ if (rstc->rcdev->ops->deassert) ++ return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); ++ ++ return -ENOSYS; ++} ++EXPORT_SYMBOL_GPL(reset_control_deassert); ++ ++/** ++ * reset_control_get - Lookup and obtain a reference to a reset controller. ++ * @dev: device to be reset by the controller ++ * @id: reset line name ++ * ++ * Returns a struct reset_control or IS_ERR() condition containing errno. ++ * ++ * Use of id names is optional. ++ */ ++struct reset_control *reset_control_get(struct device *dev, const char *id) ++{ ++ struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER); ++ struct reset_controller_dev *r, *rcdev; ++ struct of_phandle_args args; ++ int index = 0; ++ int rstc_id; ++ int ret; ++ ++ if (!dev) ++ return ERR_PTR(-EINVAL); ++ ++ if (id) ++ index = of_property_match_string(dev->of_node, ++ "reset-names", id); ++ ret = of_parse_phandle_with_args(dev->of_node, "resets", "#reset-cells", ++ index, &args); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ mutex_lock(&reset_controller_list_mutex); ++ rcdev = NULL; ++ list_for_each_entry(r, &reset_controller_list, list) { ++ if (args.np == r->of_node) { ++ rcdev = r; ++ break; ++ } ++ } ++ of_node_put(args.np); ++ ++ if (!rcdev) { ++ mutex_unlock(&reset_controller_list_mutex); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ rstc_id = rcdev->of_xlate(rcdev, &args); ++ if (rstc_id < 0) { ++ mutex_unlock(&reset_controller_list_mutex); ++ return ERR_PTR(rstc_id); ++ } ++ ++ try_module_get(rcdev->owner); ++ mutex_unlock(&reset_controller_list_mutex); ++ ++ rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); ++ if (!rstc) { ++ module_put(rcdev->owner); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ rstc->dev = dev; ++ rstc->rcdev = rcdev; ++ rstc->id = rstc_id; ++ ++ return rstc; ++} ++EXPORT_SYMBOL_GPL(reset_control_get); ++ ++/** ++ * reset_control_put - free the reset controller ++ * @rstc: reset controller ++ */ ++ ++void reset_control_put(struct reset_control *rstc) ++{ ++ if (IS_ERR(rstc)) ++ return; ++ ++ module_put(rstc->rcdev->owner); ++ kfree(rstc); ++} ++EXPORT_SYMBOL_GPL(reset_control_put); ++ ++static void devm_reset_control_release(struct device *dev, void *res) ++{ ++ reset_control_put(*(struct reset_control **)res); ++} ++ ++/** ++ * devm_reset_control_get - resource managed reset_control_get() ++ * @dev: device to be reset by the controller ++ * @id: reset line name ++ * ++ * Managed reset_control_get(). For reset controllers returned from this ++ * function, reset_control_put() is called automatically on driver detach. ++ * See reset_control_get() for more information. ++ */ ++struct reset_control *devm_reset_control_get(struct device *dev, const char *id) ++{ ++ struct reset_control **ptr, *rstc; ++ ++ ptr = devres_alloc(devm_reset_control_release, sizeof(*ptr), ++ GFP_KERNEL); ++ if (!ptr) ++ return ERR_PTR(-ENOMEM); ++ ++ rstc = reset_control_get(dev, id); ++ if (!IS_ERR(rstc)) { ++ *ptr = rstc; ++ devres_add(dev, ptr); ++ } else { ++ devres_free(ptr); ++ } ++ ++ return rstc; ++} ++EXPORT_SYMBOL_GPL(devm_reset_control_get); ++ ++static int devm_reset_control_match(struct device *dev, void *res, void *data) ++{ ++ struct reset_control **rstc = res; ++ if (WARN_ON(!rstc || !*rstc)) ++ return 0; ++ return *rstc == data; ++} ++ ++/** ++ * devm_reset_control_put - resource managed reset_control_put() ++ * @rstc: reset controller to free ++ * ++ * Deallocate a reset control allocated withd devm_reset_control_get(). ++ * This function will not need to be called normally, as devres will take ++ * care of freeing the resource. ++ */ ++void devm_reset_control_put(struct reset_control *rstc) ++{ ++ int ret; ++ ++ ret = devres_release(rstc->dev, devm_reset_control_release, ++ devm_reset_control_match, rstc); ++ if (ret) ++ WARN_ON(ret); ++} ++EXPORT_SYMBOL_GPL(devm_reset_control_put); ++ ++/** ++ * device_reset - find reset controller associated with the device ++ * and perform reset ++ * @dev: device to be reset by the controller ++ * ++ * Convenience wrapper for reset_control_get() and reset_control_reset(). ++ * This is useful for the common case of devices with single, dedicated reset ++ * lines. ++ */ ++int device_reset(struct device *dev) ++{ ++ struct reset_control *rstc; ++ int ret; ++ ++ rstc = reset_control_get(dev, NULL); ++ if (IS_ERR(rstc)) ++ return PTR_ERR(rstc); ++ ++ ret = reset_control_reset(rstc); ++ ++ reset_control_put(rstc); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(device_reset); +diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h +new file mode 100644 +index 0000000..2f61311 +--- /dev/null ++++ b/include/linux/reset-controller.h +@@ -0,0 +1,51 @@ ++#ifndef _LINUX_RESET_CONTROLLER_H_ ++#define _LINUX_RESET_CONTROLLER_H_ ++ ++#include ++ ++struct reset_controller_dev; ++ ++/** ++ * struct reset_control_ops ++ * ++ * @reset: for self-deasserting resets, does all necessary ++ * things to reset the device ++ * @assert: manually assert the reset line, if supported ++ * @deassert: manually deassert the reset line, if supported ++ */ ++struct reset_control_ops { ++ int (*reset)(struct reset_controller_dev *rcdev, unsigned long id); ++ int (*assert)(struct reset_controller_dev *rcdev, unsigned long id); ++ int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id); ++}; ++ ++struct module; ++struct device_node; ++ ++/** ++ * struct reset_controller_dev - reset controller entity that might ++ * provide multiple reset controls ++ * @ops: a pointer to device specific struct reset_control_ops ++ * @owner: kernel module of the reset controller driver ++ * @list: internal list of reset controller devices ++ * @of_node: corresponding device tree node as phandle target ++ * @of_reset_n_cells: number of cells in reset line specifiers ++ * @of_xlate: translation function to translate from specifier as found in the ++ * device tree to id as given to the reset control ops ++ * @nr_resets: number of reset controls in this reset controller device ++ */ ++struct reset_controller_dev { ++ struct reset_control_ops *ops; ++ struct module *owner; ++ struct list_head list; ++ struct device_node *of_node; ++ int of_reset_n_cells; ++ int (*of_xlate)(struct reset_controller_dev *rcdev, ++ const struct of_phandle_args *reset_spec); ++ unsigned int nr_resets; ++}; ++ ++int reset_controller_register(struct reset_controller_dev *rcdev); ++void reset_controller_unregister(struct reset_controller_dev *rcdev); ++ ++#endif +diff --git a/include/linux/reset.h b/include/linux/reset.h +new file mode 100644 +index 0000000..6082247 +--- /dev/null ++++ b/include/linux/reset.h +@@ -0,0 +1,17 @@ ++#ifndef _LINUX_RESET_H_ ++#define _LINUX_RESET_H_ ++ ++struct device; ++struct reset_control; ++ ++int reset_control_reset(struct reset_control *rstc); ++int reset_control_assert(struct reset_control *rstc); ++int reset_control_deassert(struct reset_control *rstc); ++ ++struct reset_control *reset_control_get(struct device *dev, const char *id); ++void reset_control_put(struct reset_control *rstc); ++struct reset_control *devm_reset_control_get(struct device *dev, const char *id); ++ ++int device_reset(struct device *dev); ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0068-reset-MIPS-ralink-add-core-device-reset-wrapper.patch b/target/linux/ramips/patches-3.8/0068-reset-MIPS-ralink-add-core-device-reset-wrapper.patch new file mode 100644 index 0000000000..29a0991595 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0068-reset-MIPS-ralink-add-core-device-reset-wrapper.patch @@ -0,0 +1,135 @@ +From 42c26796ae7bcfe0b33a4145de5a392e32bc9bac Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 8 May 2013 22:08:39 +0200 +Subject: [PATCH 68/79] reset: MIPS: ralink: add core/device reset wrapper + +Add a helper for reseting different devices ont he SoC. + +Signed-off-by: John Crispin +--- + arch/mips/Kconfig | 1 + + arch/mips/ralink/of.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ + arch/mips/ralink/reset.c | 1 + + 3 files changed, 61 insertions(+) + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index 2498972..ef5272f 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -450,6 +450,7 @@ config RALINK + select HAVE_MACH_CLKDEV + select CLKDEV_LOOKUP + select ARCH_REQUIRE_GPIOLIB ++ select ARCH_HAS_RESET_CONTROLLER + + config SGI_IP22 + bool "SGI IP22 (Indy/Indigo2)" +diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c +index 8efb02b..2faf478 100644 +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -14,16 +14,22 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + + #include + #include + #include + ++#include ++ + #include "common.h" + ++#define SYSC_REG_RESET_CTRL 0x034 ++ + __iomem void *rt_sysc_membase; + __iomem void *rt_memc_membase; + +@@ -96,6 +102,53 @@ void __init plat_mem_setup(void) + soc_info.mem_size_max * SZ_1M); + } + ++static int ralink_assert_device(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ u32 val; ++ ++ if (id < 8) ++ return -1; ++ ++ val = rt_sysc_r32(SYSC_REG_RESET_CTRL); ++ val |= BIT(id); ++ rt_sysc_w32(val, SYSC_REG_RESET_CTRL); ++ ++ return 0; ++} ++ ++static int ralink_deassert_device(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ u32 val; ++ ++ if (id < 8) ++ return -1; ++ ++ val = rt_sysc_r32(SYSC_REG_RESET_CTRL); ++ val &= ~BIT(id); ++ rt_sysc_w32(val, SYSC_REG_RESET_CTRL); ++ ++ return 0; ++} ++ ++static int ralink_reset_device(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ ralink_assert_device(rcdev, id); ++ return ralink_deassert_device(rcdev, id); ++} ++ ++static struct reset_control_ops reset_ops = { ++ .reset = ralink_reset_device, ++ .assert = ralink_assert_device, ++ .deassert = ralink_deassert_device, ++}; ++ ++static struct reset_controller_dev reset_dev = { ++ .ops = &reset_ops, ++ .owner = THIS_MODULE, ++ .nr_resets = 32, ++ .of_reset_n_cells = 1, ++}; ++ + static int __init plat_of_setup(void) + { + static struct of_device_id of_ids[3]; +@@ -110,6 +163,12 @@ static int __init plat_of_setup(void) + if (of_platform_populate(NULL, of_ids, NULL, NULL)) + panic("failed to populate DT\n"); + ++ reset_dev.of_node = of_find_compatible_node(NULL, NULL, "ralink,rt2880-reset"); ++ if (!reset_dev.of_node) ++ panic("Failed to find reset controller node"); ++ ++ reset_controller_register(&reset_dev); ++ + ralink_pinmux(); + + return 0; +diff --git a/arch/mips/ralink/reset.c b/arch/mips/ralink/reset.c +index 22120e5..6c15f4f 100644 +--- a/arch/mips/ralink/reset.c ++++ b/arch/mips/ralink/reset.c +@@ -10,6 +10,7 @@ + + #include + #include ++#include + + #include + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0069-NET-add-of_get_mac_address_mtd.patch b/target/linux/ramips/patches-3.8/0069-NET-add-of_get_mac_address_mtd.patch new file mode 100644 index 0000000000..6efccf0bb0 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0069-NET-add-of_get_mac_address_mtd.patch @@ -0,0 +1,83 @@ +From d30d4c01d2b97eed1fc109819b9c8747343ff4d7 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 17 Mar 2013 09:29:15 +0100 +Subject: [PATCH 69/79] NET: add of_get_mac_address_mtd() + +Many embedded devices have information such as mac addresses stored inside mtd +devices. This patch allows us to add a property inside a node describing a +network interface. The new property points at a mtd partition with an offset +where the mac address can be found. + +Signed-off-by: John Crispin +--- + drivers/of/of_net.c | 37 +++++++++++++++++++++++++++++++++++++ + include/linux/of_net.h | 1 + + 2 files changed, 38 insertions(+) + +diff --git a/drivers/of/of_net.c b/drivers/of/of_net.c +index ffab033..15f4a71 100644 +--- a/drivers/of/of_net.c ++++ b/drivers/of/of_net.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + + /** + * It maps 'enum phy_interface_t' found in include/linux/phy.h +@@ -92,3 +93,39 @@ const void *of_get_mac_address(struct device_node *np) + return NULL; + } + EXPORT_SYMBOL(of_get_mac_address); ++ ++int of_get_mac_address_mtd(struct device_node *np, void *mac) ++{ ++ struct device_node *mtd_np = NULL; ++ size_t retlen; ++ int size, ret; ++ struct mtd_info *mtd; ++ const char *part; ++ const __be32 *list; ++ phandle phandle; ++ ++ list = of_get_property(np, "mtd-mac-address", &size); ++ if (!list || (size != (2 * sizeof(*list)))) ++ return -ENOENT; ++ ++ phandle = be32_to_cpup(list++); ++ if (phandle) ++ mtd_np = of_find_node_by_phandle(phandle); ++ ++ if (!mtd_np) ++ return -ENOENT; ++ ++ part = of_get_property(mtd_np, "label", NULL); ++ if (!part) ++ part = mtd_np->name; ++ ++ mtd = get_mtd_device_nm(part); ++ if (IS_ERR(mtd)) ++ return PTR_ERR(mtd); ++ ++ ret = mtd_read(mtd, be32_to_cpup(list), 6, &retlen, (u_char *) mac); ++ put_mtd_device(mtd); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(of_get_mac_address_mtd); +diff --git a/include/linux/of_net.h b/include/linux/of_net.h +index f474641..9d3304f 100644 +--- a/include/linux/of_net.h ++++ b/include/linux/of_net.h +@@ -11,6 +11,7 @@ + #include + extern const int of_get_phy_mode(struct device_node *np); + extern const void *of_get_mac_address(struct device_node *np); ++extern int of_get_mac_address_mtd(struct device_node *np, void *mac); + #endif + + #endif /* __LINUX_OF_NET_H */ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0070-NET-multi-phy-support.patch b/target/linux/ramips/patches-3.8/0070-NET-multi-phy-support.patch new file mode 100644 index 0000000000..4e8fbdad28 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0070-NET-multi-phy-support.patch @@ -0,0 +1,61 @@ +From c00adb2a1ac75eaef1e71cd9819636b86eabebd9 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 11 May 2013 23:40:19 +0200 +Subject: [PATCH 70/79] NET: multi phy support + +Signed-off-by: John Crispin +--- + drivers/net/phy/phy.c | 9 ++++++--- + include/linux/phy.h | 2 +- + 2 files changed, 7 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c +index ef9ea92..27f9b45 100644 +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -800,7 +800,8 @@ void phy_state_machine(struct work_struct *work) + * negotiation for now */ + if (!phydev->link) { + phydev->state = PHY_NOLINK; +- netif_carrier_off(phydev->attached_dev); ++ if (!phydev->no_auto_carrier_off) ++ netif_carrier_off(phydev->attached_dev); + phydev->adjust_link(phydev->attached_dev); + break; + } +@@ -891,7 +892,8 @@ void phy_state_machine(struct work_struct *work) + netif_carrier_on(phydev->attached_dev); + } else { + phydev->state = PHY_NOLINK; +- netif_carrier_off(phydev->attached_dev); ++ if (!phydev->no_auto_carrier_off) ++ netif_carrier_off(phydev->attached_dev); + } + + phydev->adjust_link(phydev->attached_dev); +@@ -903,7 +905,8 @@ void phy_state_machine(struct work_struct *work) + case PHY_HALTED: + if (phydev->link) { + phydev->link = 0; +- netif_carrier_off(phydev->attached_dev); ++ if (!phydev->no_auto_carrier_off) ++ netif_carrier_off(phydev->attached_dev); + phydev->adjust_link(phydev->attached_dev); + } + break; +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 93b3cf7..c09b4ad 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -298,7 +298,7 @@ struct phy_device { + + struct phy_c45_device_ids c45_ids; + bool is_c45; +- ++ bool no_auto_carrier_off; + enum phy_state state; + + u32 dev_flags; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0072-NET-MIPS-add-ralink-SoC-ethernet-driver.patch b/target/linux/ramips/patches-3.8/0072-NET-MIPS-add-ralink-SoC-ethernet-driver.patch new file mode 100644 index 0000000000..5e35b5be40 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0072-NET-MIPS-add-ralink-SoC-ethernet-driver.patch @@ -0,0 +1,4732 @@ +From 34564578e57bd2bffddbd5f463c55db4a220f2a0 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 20 Jun 2013 18:52:00 +0200 +Subject: [PATCH 72/79] NET: MIPS: add ralink SoC ethernet driver + +Add support for Ralink FE and ESW. + +Signed-off-by: John Crispin +--- + .../include/asm/mach-ralink/rt305x_esw_platform.h | 27 + + arch/mips/ralink/rt305x.c | 1 + + drivers/net/ethernet/Kconfig | 1 + + drivers/net/ethernet/Makefile | 1 + + drivers/net/ethernet/ralink/Kconfig | 31 + + drivers/net/ethernet/ralink/Makefile | 18 + + drivers/net/ethernet/ralink/esw_rt3052.c | 1463 ++++++++++++++++++++ + drivers/net/ethernet/ralink/esw_rt3052.h | 32 + + drivers/net/ethernet/ralink/gsw_mt7620a.c | 1027 ++++++++++++++ + drivers/net/ethernet/ralink/gsw_mt7620a.h | 29 + + drivers/net/ethernet/ralink/mdio.c | 245 ++++ + drivers/net/ethernet/ralink/mdio.h | 29 + + drivers/net/ethernet/ralink/mdio_rt2880.c | 163 +++ + drivers/net/ethernet/ralink/mdio_rt2880.h | 25 + + drivers/net/ethernet/ralink/ralink_soc_eth.c | 759 ++++++++++ + drivers/net/ethernet/ralink/ralink_soc_eth.h | 372 +++++ + drivers/net/ethernet/ralink/soc_mt7620.c | 111 ++ + drivers/net/ethernet/ralink/soc_rt2880.c | 51 + + drivers/net/ethernet/ralink/soc_rt305x.c | 102 ++ + drivers/net/ethernet/ralink/soc_rt3883.c | 59 + + 20 files changed, 4546 insertions(+) + create mode 100644 arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h + create mode 100644 drivers/net/ethernet/ralink/Kconfig + create mode 100644 drivers/net/ethernet/ralink/Makefile + create mode 100644 drivers/net/ethernet/ralink/esw_rt3052.c + create mode 100644 drivers/net/ethernet/ralink/esw_rt3052.h + create mode 100644 drivers/net/ethernet/ralink/gsw_mt7620a.c + create mode 100644 drivers/net/ethernet/ralink/gsw_mt7620a.h + create mode 100644 drivers/net/ethernet/ralink/mdio.c + create mode 100644 drivers/net/ethernet/ralink/mdio.h + create mode 100644 drivers/net/ethernet/ralink/mdio_rt2880.c + create mode 100644 drivers/net/ethernet/ralink/mdio_rt2880.h + create mode 100644 drivers/net/ethernet/ralink/ralink_soc_eth.c + create mode 100644 drivers/net/ethernet/ralink/ralink_soc_eth.h + create mode 100644 drivers/net/ethernet/ralink/soc_mt7620.c + create mode 100644 drivers/net/ethernet/ralink/soc_rt2880.c + create mode 100644 drivers/net/ethernet/ralink/soc_rt305x.c + create mode 100644 drivers/net/ethernet/ralink/soc_rt3883.c + +diff --git a/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h b/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h +new file mode 100644 +index 0000000..2098c5c +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h +@@ -0,0 +1,27 @@ ++/* ++ * Ralink RT305x SoC platform device registration ++ * ++ * Copyright (C) 2010 Gabor Juhos ++ * ++ * 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 _RT305X_ESW_PLATFORM_H ++#define _RT305X_ESW_PLATFORM_H ++ ++enum { ++ RT305X_ESW_VLAN_CONFIG_NONE = 0, ++ RT305X_ESW_VLAN_CONFIG_LLLLW, ++ RT305X_ESW_VLAN_CONFIG_WLLLL, ++}; ++ ++struct rt305x_esw_platform_data ++{ ++ u8 vlan_config; ++ u32 reg_initval_fct2; ++ u32 reg_initval_fpa2; ++}; ++ ++#endif /* _RT305X_ESW_PLATFORM_H */ +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index ca7ee3a..1a6b458 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -221,6 +221,7 @@ void __init ralink_clk_init(void) + } + + ralink_clk_add("cpu", cpu_rate); ++ ralink_clk_add("sys", sys_rate); + ralink_clk_add("10000b00.spi", sys_rate); + ralink_clk_add("10000100.timer", wdt_rate); + ralink_clk_add("10000120.watchdog", wdt_rate); +diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig +index e4ff389..08c3c93 100644 +--- a/drivers/net/ethernet/Kconfig ++++ b/drivers/net/ethernet/Kconfig +@@ -137,6 +137,7 @@ source "drivers/net/ethernet/pasemi/Kconfig" + source "drivers/net/ethernet/qlogic/Kconfig" + source "drivers/net/ethernet/racal/Kconfig" + source "drivers/net/ethernet/realtek/Kconfig" ++source "drivers/net/ethernet/ralink/Kconfig" + source "drivers/net/ethernet/renesas/Kconfig" + source "drivers/net/ethernet/rdc/Kconfig" + +diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile +index d447307..f6bffb1 100644 +--- a/drivers/net/ethernet/Makefile ++++ b/drivers/net/ethernet/Makefile +@@ -55,6 +55,7 @@ obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/ + obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/ + obj-$(CONFIG_NET_VENDOR_RACAL) += racal/ + obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/ ++obj-$(CONFIG_NET_RALINK) += ralink/ + obj-$(CONFIG_SH_ETH) += renesas/ + obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ + obj-$(CONFIG_S6GMAC) += s6gmac.o +diff --git a/drivers/net/ethernet/ralink/Kconfig b/drivers/net/ethernet/ralink/Kconfig +new file mode 100644 +index 0000000..ca2c9ad +--- /dev/null ++++ b/drivers/net/ethernet/ralink/Kconfig +@@ -0,0 +1,31 @@ ++config NET_RALINK ++ tristate "Ralink RT288X/RT3X5X/RT3662/RT3883/MT7620 ethernet driver" ++ depends on RALINK ++ help ++ This driver supports the ethernet mac inside the ralink wisocs ++ ++if NET_RALINK ++ ++config NET_RALINK_MDIO ++ def_bool NET_RALINK ++ depends on (SOC_RT288X || SOC_RT3883 || SOC_MT7620) ++ select PHYLIB ++ ++config NET_RALINK_MDIO_RT2880 ++ def_bool NET_RALINK ++ depends on (SOC_RT288X || SOC_RT3883) ++ select NET_RALINK_MDIO ++ ++config NET_RALINK_ESW_RT3052 ++ def_bool NET_RALINK ++ depends on SOC_RT305X ++ select PHYLIB ++ select SWCONFIG ++ ++config NET_RALINK_GSW_MT7620 ++ def_bool NET_RALINK ++ depends on SOC_MT7620 ++ select NET_RALINK_MDIO ++ select PHYLIB ++ select SWCONFIG ++endif +diff --git a/drivers/net/ethernet/ralink/Makefile b/drivers/net/ethernet/ralink/Makefile +new file mode 100644 +index 0000000..a38fa21 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/Makefile +@@ -0,0 +1,18 @@ ++# ++# Makefile for the Ralink SoCs built-in ethernet macs ++# ++ ++ralink-eth-y += ralink_soc_eth.o ++ ++ralink-eth-$(CONFIG_NET_RALINK_MDIO) += mdio.o ++ralink-eth-$(CONFIG_NET_RALINK_MDIO_RT2880) += mdio_rt2880.o ++ ++ralink-eth-$(CONFIG_NET_RALINK_ESW_RT3052) += esw_rt3052.o ++ralink-eth-$(CONFIG_NET_RALINK_GSW_MT7620) += gsw_mt7620a.o ++ ++ralink-eth-$(CONFIG_SOC_RT288X) += soc_rt2880.o ++ralink-eth-$(CONFIG_SOC_RT305X) += soc_rt305x.o ++ralink-eth-$(CONFIG_SOC_RT3883) += soc_rt3883.o ++ralink-eth-$(CONFIG_SOC_MT7620) += soc_mt7620.o ++ ++obj-$(CONFIG_NET_RALINK) += ralink-eth.o +diff --git a/drivers/net/ethernet/ralink/esw_rt3052.c b/drivers/net/ethernet/ralink/esw_rt3052.c +new file mode 100644 +index 0000000..b937062 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/esw_rt3052.c +@@ -0,0 +1,1463 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* ++ * HW limitations for this switch: ++ * - No large frame support (PKT_MAX_LEN at most 1536) ++ * - Can't have untagged vlan and tagged vlan on one port at the same time, ++ * though this might be possible using the undocumented PPE. ++ */ ++ ++#define RT305X_ESW_REG_ISR 0x00 ++#define RT305X_ESW_REG_IMR 0x04 ++#define RT305X_ESW_REG_FCT0 0x08 ++#define RT305X_ESW_REG_PFC1 0x14 ++#define RT305X_ESW_REG_ATS 0x24 ++#define RT305X_ESW_REG_ATS0 0x28 ++#define RT305X_ESW_REG_ATS1 0x2c ++#define RT305X_ESW_REG_ATS2 0x30 ++#define RT305X_ESW_REG_PVIDC(_n) (0x40 + 4 * (_n)) ++#define RT305X_ESW_REG_VLANI(_n) (0x50 + 4 * (_n)) ++#define RT305X_ESW_REG_VMSC(_n) (0x70 + 4 * (_n)) ++#define RT305X_ESW_REG_POA 0x80 ++#define RT305X_ESW_REG_FPA 0x84 ++#define RT305X_ESW_REG_SOCPC 0x8c ++#define RT305X_ESW_REG_POC0 0x90 ++#define RT305X_ESW_REG_POC1 0x94 ++#define RT305X_ESW_REG_POC2 0x98 ++#define RT305X_ESW_REG_SGC 0x9c ++#define RT305X_ESW_REG_STRT 0xa0 ++#define RT305X_ESW_REG_PCR0 0xc0 ++#define RT305X_ESW_REG_PCR1 0xc4 ++#define RT305X_ESW_REG_FPA2 0xc8 ++#define RT305X_ESW_REG_FCT2 0xcc ++#define RT305X_ESW_REG_SGC2 0xe4 ++#define RT305X_ESW_REG_P0LED 0xa4 ++#define RT305X_ESW_REG_P1LED 0xa8 ++#define RT305X_ESW_REG_P2LED 0xac ++#define RT305X_ESW_REG_P3LED 0xb0 ++#define RT305X_ESW_REG_P4LED 0xb4 ++#define RT305X_ESW_REG_PXPC(_x) (0xe8 + (4 * _x)) ++#define RT305X_ESW_REG_P1PC 0xec ++#define RT305X_ESW_REG_P2PC 0xf0 ++#define RT305X_ESW_REG_P3PC 0xf4 ++#define RT305X_ESW_REG_P4PC 0xf8 ++#define RT305X_ESW_REG_P5PC 0xfc ++ ++#define RT305X_ESW_LED_LINK 0 ++#define RT305X_ESW_LED_100M 1 ++#define RT305X_ESW_LED_DUPLEX 2 ++#define RT305X_ESW_LED_ACTIVITY 3 ++#define RT305X_ESW_LED_COLLISION 4 ++#define RT305X_ESW_LED_LINKACT 5 ++#define RT305X_ESW_LED_DUPLCOLL 6 ++#define RT305X_ESW_LED_10MACT 7 ++#define RT305X_ESW_LED_100MACT 8 ++/* Additional led states not in datasheet: */ ++#define RT305X_ESW_LED_BLINK 10 ++#define RT305X_ESW_LED_ON 12 ++ ++#define RT305X_ESW_LINK_S 25 ++#define RT305X_ESW_DUPLEX_S 9 ++#define RT305X_ESW_SPD_S 0 ++ ++#define RT305X_ESW_PCR0_WT_NWAY_DATA_S 16 ++#define RT305X_ESW_PCR0_WT_PHY_CMD BIT(13) ++#define RT305X_ESW_PCR0_CPU_PHY_REG_S 8 ++ ++#define RT305X_ESW_PCR1_WT_DONE BIT(0) ++ ++#define RT305X_ESW_ATS_TIMEOUT (5 * HZ) ++#define RT305X_ESW_PHY_TIMEOUT (5 * HZ) ++ ++#define RT305X_ESW_PVIDC_PVID_M 0xfff ++#define RT305X_ESW_PVIDC_PVID_S 12 ++ ++#define RT305X_ESW_VLANI_VID_M 0xfff ++#define RT305X_ESW_VLANI_VID_S 12 ++ ++#define RT305X_ESW_VMSC_MSC_M 0xff ++#define RT305X_ESW_VMSC_MSC_S 8 ++ ++#define RT305X_ESW_SOCPC_DISUN2CPU_S 0 ++#define RT305X_ESW_SOCPC_DISMC2CPU_S 8 ++#define RT305X_ESW_SOCPC_DISBC2CPU_S 16 ++#define RT305X_ESW_SOCPC_CRC_PADDING BIT(25) ++ ++#define RT305X_ESW_POC0_EN_BP_S 0 ++#define RT305X_ESW_POC0_EN_FC_S 8 ++#define RT305X_ESW_POC0_DIS_RMC2CPU_S 16 ++#define RT305X_ESW_POC0_DIS_PORT_M 0x7f ++#define RT305X_ESW_POC0_DIS_PORT_S 23 ++ ++#define RT305X_ESW_POC2_UNTAG_EN_M 0xff ++#define RT305X_ESW_POC2_UNTAG_EN_S 0 ++#define RT305X_ESW_POC2_ENAGING_S 8 ++#define RT305X_ESW_POC2_DIS_UC_PAUSE_S 16 ++ ++#define RT305X_ESW_SGC2_DOUBLE_TAG_M 0x7f ++#define RT305X_ESW_SGC2_DOUBLE_TAG_S 0 ++#define RT305X_ESW_SGC2_LAN_PMAP_M 0x3f ++#define RT305X_ESW_SGC2_LAN_PMAP_S 24 ++ ++#define RT305X_ESW_PFC1_EN_VLAN_M 0xff ++#define RT305X_ESW_PFC1_EN_VLAN_S 16 ++#define RT305X_ESW_PFC1_EN_TOS_S 24 ++ ++#define RT305X_ESW_VLAN_NONE 0xfff ++ ++#define RT305X_ESW_GSC_BC_STROM_MASK 0x3 ++#define RT305X_ESW_GSC_BC_STROM_SHIFT 4 ++ ++#define RT305X_ESW_GSC_LED_FREQ_MASK 0x3 ++#define RT305X_ESW_GSC_LED_FREQ_SHIFT 23 ++ ++#define RT305X_ESW_POA_LINK_MASK 0x1f ++#define RT305X_ESW_POA_LINK_SHIFT 25 ++ ++#define RT305X_ESW_PORT_ST_CHG BIT(26) ++#define RT305X_ESW_PORT0 0 ++#define RT305X_ESW_PORT1 1 ++#define RT305X_ESW_PORT2 2 ++#define RT305X_ESW_PORT3 3 ++#define RT305X_ESW_PORT4 4 ++#define RT305X_ESW_PORT5 5 ++#define RT305X_ESW_PORT6 6 ++ ++#define RT305X_ESW_PORTS_NONE 0 ++ ++#define RT305X_ESW_PMAP_LLLLLL 0x3f ++#define RT305X_ESW_PMAP_LLLLWL 0x2f ++#define RT305X_ESW_PMAP_WLLLLL 0x3e ++ ++#define RT305X_ESW_PORTS_INTERNAL \ ++ (BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT1) | \ ++ BIT(RT305X_ESW_PORT2) | BIT(RT305X_ESW_PORT3) | \ ++ BIT(RT305X_ESW_PORT4)) ++ ++#define RT305X_ESW_PORTS_NOCPU \ ++ (RT305X_ESW_PORTS_INTERNAL | BIT(RT305X_ESW_PORT5)) ++ ++#define RT305X_ESW_PORTS_CPU BIT(RT305X_ESW_PORT6) ++ ++#define RT305X_ESW_PORTS_ALL \ ++ (RT305X_ESW_PORTS_NOCPU | RT305X_ESW_PORTS_CPU) ++ ++#define RT305X_ESW_NUM_VLANS 16 ++#define RT305X_ESW_NUM_VIDS 4096 ++#define RT305X_ESW_NUM_PORTS 7 ++#define RT305X_ESW_NUM_LANWAN 6 ++#define RT305X_ESW_NUM_LEDS 5 ++ ++#define RT5350_ESW_REG_PXTPC(_x) (0x150 + (4 * _x)) ++#define RT5350_EWS_REG_LED_POLARITY 0x168 ++#define RT5350_RESET_EPHY BIT(24) ++#define SYSC_REG_RESET_CTRL 0x34 ++ ++enum { ++ /* Global attributes. */ ++ RT305X_ESW_ATTR_ENABLE_VLAN, ++ RT305X_ESW_ATTR_ALT_VLAN_DISABLE, ++ RT305X_ESW_ATTR_BC_STATUS, ++ RT305X_ESW_ATTR_LED_FREQ, ++ /* Port attributes. */ ++ RT305X_ESW_ATTR_PORT_DISABLE, ++ RT305X_ESW_ATTR_PORT_DOUBLETAG, ++ RT305X_ESW_ATTR_PORT_UNTAG, ++ RT305X_ESW_ATTR_PORT_LED, ++ RT305X_ESW_ATTR_PORT_LAN, ++ RT305X_ESW_ATTR_PORT_RECV_BAD, ++ RT305X_ESW_ATTR_PORT_RECV_GOOD, ++ RT5350_ESW_ATTR_PORT_TR_BAD, ++ RT5350_ESW_ATTR_PORT_TR_GOOD, ++}; ++ ++struct esw_port { ++ bool disable; ++ bool doubletag; ++ bool untag; ++ u8 led; ++ u16 pvid; ++}; ++ ++struct esw_vlan { ++ u8 ports; ++ u16 vid; ++}; ++ ++struct rt305x_esw { ++ struct device *dev; ++ void __iomem *base; ++ int irq; ++ const struct rt305x_esw_platform_data *pdata; ++ /* Protects against concurrent register rmw operations. */ ++ spinlock_t reg_rw_lock; ++ ++ unsigned char port_map; ++ unsigned int reg_initval_fct2; ++ unsigned int reg_initval_fpa2; ++ unsigned int reg_led_polarity; ++ ++ ++ struct switch_dev swdev; ++ bool global_vlan_enable; ++ bool alt_vlan_disable; ++ int bc_storm_protect; ++ int led_frequency; ++ struct esw_vlan vlans[RT305X_ESW_NUM_VLANS]; ++ struct esw_port ports[RT305X_ESW_NUM_PORTS]; ++ ++}; ++ ++static inline void esw_w32(struct rt305x_esw *esw, u32 val, unsigned reg) ++{ ++ __raw_writel(val, esw->base + reg); ++} ++ ++static inline u32 esw_r32(struct rt305x_esw *esw, unsigned reg) ++{ ++ return __raw_readl(esw->base + reg); ++} ++ ++static inline void esw_rmw_raw(struct rt305x_esw *esw, unsigned reg, unsigned long mask, ++ unsigned long val) ++{ ++ unsigned long t; ++ ++ t = __raw_readl(esw->base + reg) & ~mask; ++ __raw_writel(t | val, esw->base + reg); ++} ++ ++static void esw_rmw(struct rt305x_esw *esw, unsigned reg, unsigned long mask, ++ unsigned long val) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&esw->reg_rw_lock, flags); ++ esw_rmw_raw(esw, reg, mask, val); ++ spin_unlock_irqrestore(&esw->reg_rw_lock, flags); ++} ++ ++static u32 rt305x_mii_write(struct rt305x_esw *esw, u32 phy_addr, u32 phy_register, ++ u32 write_data) ++{ ++ unsigned long t_start = jiffies; ++ int ret = 0; ++ ++ while (1) { ++ if (!(esw_r32(esw, RT305X_ESW_REG_PCR1) & ++ RT305X_ESW_PCR1_WT_DONE)) ++ break; ++ if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) { ++ ret = 1; ++ goto out; ++ } ++ } ++ ++ write_data &= 0xffff; ++ esw_w32(esw, ++ (write_data << RT305X_ESW_PCR0_WT_NWAY_DATA_S) | ++ (phy_register << RT305X_ESW_PCR0_CPU_PHY_REG_S) | ++ (phy_addr) | RT305X_ESW_PCR0_WT_PHY_CMD, ++ RT305X_ESW_REG_PCR0); ++ ++ t_start = jiffies; ++ while (1) { ++ if (esw_r32(esw, RT305X_ESW_REG_PCR1) & ++ RT305X_ESW_PCR1_WT_DONE) ++ break; ++ ++ if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) { ++ ret = 1; ++ break; ++ } ++ } ++out: ++ if (ret) ++ printk(KERN_ERR "ramips_eth: MDIO timeout\n"); ++ return ret; ++} ++ ++static unsigned esw_get_vlan_id(struct rt305x_esw *esw, unsigned vlan) ++{ ++ unsigned s; ++ unsigned val; ++ ++ s = RT305X_ESW_VLANI_VID_S * (vlan % 2); ++ val = esw_r32(esw, RT305X_ESW_REG_VLANI(vlan / 2)); ++ val = (val >> s) & RT305X_ESW_VLANI_VID_M; ++ ++ return val; ++} ++ ++static void esw_set_vlan_id(struct rt305x_esw *esw, unsigned vlan, unsigned vid) ++{ ++ unsigned s; ++ ++ s = RT305X_ESW_VLANI_VID_S * (vlan % 2); ++ esw_rmw(esw, ++ RT305X_ESW_REG_VLANI(vlan / 2), ++ RT305X_ESW_VLANI_VID_M << s, ++ (vid & RT305X_ESW_VLANI_VID_M) << s); ++} ++ ++static unsigned esw_get_pvid(struct rt305x_esw *esw, unsigned port) ++{ ++ unsigned s, val; ++ ++ s = RT305X_ESW_PVIDC_PVID_S * (port % 2); ++ val = esw_r32(esw, RT305X_ESW_REG_PVIDC(port / 2)); ++ return (val >> s) & RT305X_ESW_PVIDC_PVID_M; ++} ++ ++static void esw_set_pvid(struct rt305x_esw *esw, unsigned port, unsigned pvid) ++{ ++ unsigned s; ++ ++ s = RT305X_ESW_PVIDC_PVID_S * (port % 2); ++ esw_rmw(esw, ++ RT305X_ESW_REG_PVIDC(port / 2), ++ RT305X_ESW_PVIDC_PVID_M << s, ++ (pvid & RT305X_ESW_PVIDC_PVID_M) << s); ++} ++ ++static unsigned esw_get_vmsc(struct rt305x_esw *esw, unsigned vlan) ++{ ++ unsigned s, val; ++ ++ s = RT305X_ESW_VMSC_MSC_S * (vlan % 4); ++ val = esw_r32(esw, RT305X_ESW_REG_VMSC(vlan / 4)); ++ val = (val >> s) & RT305X_ESW_VMSC_MSC_M; ++ ++ return val; ++} ++ ++static void esw_set_vmsc(struct rt305x_esw *esw, unsigned vlan, unsigned msc) ++{ ++ unsigned s; ++ ++ s = RT305X_ESW_VMSC_MSC_S * (vlan % 4); ++ esw_rmw(esw, ++ RT305X_ESW_REG_VMSC(vlan / 4), ++ RT305X_ESW_VMSC_MSC_M << s, ++ (msc & RT305X_ESW_VMSC_MSC_M) << s); ++} ++ ++static unsigned esw_get_port_disable(struct rt305x_esw *esw) ++{ ++ unsigned reg; ++ reg = esw_r32(esw, RT305X_ESW_REG_POC0); ++ return (reg >> RT305X_ESW_POC0_DIS_PORT_S) & ++ RT305X_ESW_POC0_DIS_PORT_M; ++} ++ ++static void esw_set_port_disable(struct rt305x_esw *esw, unsigned disable_mask) ++{ ++ unsigned old_mask; ++ unsigned enable_mask; ++ unsigned changed; ++ int i; ++ ++ old_mask = esw_get_port_disable(esw); ++ changed = old_mask ^ disable_mask; ++ enable_mask = old_mask & disable_mask; ++ ++ /* enable before writing to MII */ ++ esw_rmw(esw, RT305X_ESW_REG_POC0, ++ (RT305X_ESW_POC0_DIS_PORT_M << ++ RT305X_ESW_POC0_DIS_PORT_S), ++ enable_mask << RT305X_ESW_POC0_DIS_PORT_S); ++ ++ for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) { ++ if (!(changed & (1 << i))) ++ continue; ++ if (disable_mask & (1 << i)) { ++ /* disable */ ++ rt305x_mii_write(esw, i, MII_BMCR, ++ BMCR_PDOWN); ++ } else { ++ /* enable */ ++ rt305x_mii_write(esw, i, MII_BMCR, ++ BMCR_FULLDPLX | ++ BMCR_ANENABLE | ++ BMCR_ANRESTART | ++ BMCR_SPEED100); ++ } ++ } ++ ++ /* disable after writing to MII */ ++ esw_rmw(esw, RT305X_ESW_REG_POC0, ++ (RT305X_ESW_POC0_DIS_PORT_M << ++ RT305X_ESW_POC0_DIS_PORT_S), ++ disable_mask << RT305X_ESW_POC0_DIS_PORT_S); ++} ++ ++static void esw_set_gsc(struct rt305x_esw *esw) ++{ ++ esw_rmw(esw, RT305X_ESW_REG_SGC, ++ RT305X_ESW_GSC_BC_STROM_MASK << RT305X_ESW_GSC_BC_STROM_SHIFT, ++ esw->bc_storm_protect << RT305X_ESW_GSC_BC_STROM_SHIFT); ++ esw_rmw(esw, RT305X_ESW_REG_SGC, ++ RT305X_ESW_GSC_LED_FREQ_MASK << RT305X_ESW_GSC_LED_FREQ_SHIFT, ++ esw->led_frequency << RT305X_ESW_GSC_LED_FREQ_SHIFT); ++} ++ ++static int esw_apply_config(struct switch_dev *dev); ++ ++static void esw_hw_init(struct rt305x_esw *esw) ++{ ++ int i; ++ u8 port_disable = 0; ++ u8 port_map = RT305X_ESW_PMAP_LLLLLL; ++ ++ /* vodoo from original driver */ ++ esw_w32(esw, 0xC8A07850, RT305X_ESW_REG_FCT0); ++ esw_w32(esw, 0x00000000, RT305X_ESW_REG_SGC2); ++ /* Port priority 1 for all ports, vlan enabled. */ ++ esw_w32(esw, 0x00005555 | ++ (RT305X_ESW_PORTS_ALL << RT305X_ESW_PFC1_EN_VLAN_S), ++ RT305X_ESW_REG_PFC1); ++ ++ /* Enable Back Pressure, and Flow Control */ ++ esw_w32(esw, ++ ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_BP_S) | ++ (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_FC_S)), ++ RT305X_ESW_REG_POC0); ++ ++ /* Enable Aging, and VLAN TAG removal */ ++ esw_w32(esw, ++ ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC2_ENAGING_S) | ++ (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC2_UNTAG_EN_S)), ++ RT305X_ESW_REG_POC2); ++ ++ if (esw->reg_initval_fct2) ++ esw_w32(esw, esw->reg_initval_fct2, RT305X_ESW_REG_FCT2); ++ else ++ esw_w32(esw, esw->pdata->reg_initval_fct2, RT305X_ESW_REG_FCT2); ++ ++ /* ++ * 300s aging timer, max packet len 1536, broadcast storm prevention ++ * disabled, disable collision abort, mac xor48 hash, 10 packet back ++ * pressure jam, GMII disable was_transmit, back pressure disabled, ++ * 30ms led flash, unmatched IGMP as broadcast, rmc tb fault to all ++ * ports. ++ */ ++ esw_w32(esw, 0x0008a301, RT305X_ESW_REG_SGC); ++ ++ /* Setup SoC Port control register */ ++ esw_w32(esw, ++ (RT305X_ESW_SOCPC_CRC_PADDING | ++ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISUN2CPU_S) | ++ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISMC2CPU_S) | ++ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISBC2CPU_S)), ++ RT305X_ESW_REG_SOCPC); ++ ++ if (esw->reg_initval_fpa2) ++ esw_w32(esw, esw->reg_initval_fpa2, RT305X_ESW_REG_FPA2); ++ else ++ esw_w32(esw, esw->pdata->reg_initval_fpa2, RT305X_ESW_REG_FPA2); ++ esw_w32(esw, 0x00000000, RT305X_ESW_REG_FPA); ++ ++ /* Force Link/Activity on ports */ ++ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P0LED); ++ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P1LED); ++ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P2LED); ++ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P3LED); ++ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P4LED); ++ ++ /* Copy disabled port configuration from bootloader setup */ ++ port_disable = esw_get_port_disable(esw); ++ for (i = 0; i < 6; i++) ++ esw->ports[i].disable = (port_disable & (1 << i)) != 0; ++ ++ if (soc_is_rt3352()) { ++ /* reset EPHY */ ++ u32 val = rt_sysc_r32(SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(val | RT5350_RESET_EPHY, SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(val, SYSC_REG_RESET_CTRL); ++ ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ for (i = 0; i < 5; i++) { ++ if (esw->ports[i].disable) { ++ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN); ++ } else { ++ rt305x_mii_write(esw, i, MII_BMCR, ++ BMCR_FULLDPLX | ++ BMCR_ANENABLE | ++ BMCR_SPEED100); ++ } ++ /* TX10 waveform coefficient LSB=0 disable PHY */ ++ rt305x_mii_write(esw, i, 26, 0x1601); ++ /* TX100/TX10 AD/DA current bias */ ++ rt305x_mii_write(esw, i, 29, 0x7016); ++ /* TX100 slew rate control */ ++ rt305x_mii_write(esw, i, 30, 0x0038); ++ } ++ ++ /* select global register */ ++ rt305x_mii_write(esw, 0, 31, 0x0); ++ /* enlarge agcsel threshold 3 and threshold 2 */ ++ rt305x_mii_write(esw, 0, 1, 0x4a40); ++ /* enlarge agcsel threshold 5 and threshold 4 */ ++ rt305x_mii_write(esw, 0, 2, 0x6254); ++ /* enlarge agcsel threshold */ ++ rt305x_mii_write(esw, 0, 3, 0xa17f); ++ rt305x_mii_write(esw, 0,12, 0x7eaa); ++ /* longer TP_IDL tail length */ ++ rt305x_mii_write(esw, 0, 14, 0x65); ++ /* increased squelch pulse count threshold. */ ++ rt305x_mii_write(esw, 0, 16, 0x0684); ++ /* set TX10 signal amplitude threshold to minimum */ ++ rt305x_mii_write(esw, 0, 17, 0x0fe0); ++ /* set squelch amplitude to higher threshold */ ++ rt305x_mii_write(esw, 0, 18, 0x40ba); ++ /* tune TP_IDL tail and head waveform, enable power down slew rate control */ ++ rt305x_mii_write(esw, 0, 22, 0x253f); ++ /* set PLL/Receive bias current are calibrated */ ++ rt305x_mii_write(esw, 0, 27, 0x2fda); ++ /* change PLL/Receive bias current to internal(RT3350) */ ++ rt305x_mii_write(esw, 0, 28, 0xc410); ++ /* change PLL bias current to internal(RT3052_MP3) */ ++ rt305x_mii_write(esw, 0, 29, 0x598b); ++ /* select local register */ ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ } else if (soc_is_rt5350()) { ++ /* reset EPHY */ ++ u32 val = rt_sysc_r32(SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(val | RT5350_RESET_EPHY, SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(val, SYSC_REG_RESET_CTRL); ++ ++ /* set the led polarity */ ++ esw_w32(esw, esw->reg_led_polarity & 0x1F, RT5350_EWS_REG_LED_POLARITY); ++ ++ /* local registers */ ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ for (i = 0; i < 5; i++) { ++ if (esw->ports[i].disable) { ++ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN); ++ } else { ++ rt305x_mii_write(esw, i, MII_BMCR, ++ BMCR_FULLDPLX | ++ BMCR_ANENABLE | ++ BMCR_SPEED100); ++ } ++ /* TX10 waveform coefficient LSB=0 disable PHY */ ++ rt305x_mii_write(esw, i, 26, 0x1601); ++ /* TX100/TX10 AD/DA current bias */ ++ rt305x_mii_write(esw, i, 29, 0x7015); ++ /* TX100 slew rate control */ ++ rt305x_mii_write(esw, i, 30, 0x0038); ++ } ++ ++ /* global registers */ ++ rt305x_mii_write(esw, 0, 31, 0x0); ++ /* enlarge agcsel threshold 3 and threshold 2 */ ++ rt305x_mii_write(esw, 0, 1, 0x4a40); ++ /* enlarge agcsel threshold 5 and threshold 4 */ ++ rt305x_mii_write(esw, 0, 2, 0x6254); ++ /* enlarge agcsel threshold 6 */ ++ rt305x_mii_write(esw, 0, 3, 0xa17f); ++ rt305x_mii_write(esw, 0, 12, 0x7eaa); ++ /* longer TP_IDL tail length */ ++ rt305x_mii_write(esw, 0, 14, 0x65); ++ /* increased squelch pulse count threshold. */ ++ rt305x_mii_write(esw, 0, 16, 0x0684); ++ /* set TX10 signal amplitude threshold to minimum */ ++ rt305x_mii_write(esw, 0, 17, 0x0fe0); ++ /* set squelch amplitude to higher threshold */ ++ rt305x_mii_write(esw, 0, 18, 0x40ba); ++ /* tune TP_IDL tail and head waveform, enable power down slew rate control */ ++ rt305x_mii_write(esw, 0, 22, 0x253f); ++ /* set PLL/Receive bias current are calibrated */ ++ rt305x_mii_write(esw, 0, 27, 0x2fda); ++ /* change PLL/Receive bias current to internal(RT3350) */ ++ rt305x_mii_write(esw, 0, 28, 0xc410); ++ /* change PLL bias current to internal(RT3052_MP3) */ ++ rt305x_mii_write(esw, 0, 29, 0x598b); ++ /* select local register */ ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ } else { ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ for (i = 0; i < 5; i++) { ++ if (esw->ports[i].disable) { ++ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN); ++ } else { ++ rt305x_mii_write(esw, i, MII_BMCR, ++ BMCR_FULLDPLX | ++ BMCR_ANENABLE | ++ BMCR_SPEED100); ++ } ++ /* TX10 waveform coefficient */ ++ rt305x_mii_write(esw, i, 26, 0x1601); ++ /* TX100/TX10 AD/DA current bias */ ++ rt305x_mii_write(esw, i, 29, 0x7058); ++ /* TX100 slew rate control */ ++ rt305x_mii_write(esw, i, 30, 0x0018); ++ } ++ ++ /* PHY IOT */ ++ /* select global register */ ++ rt305x_mii_write(esw, 0, 31, 0x0); ++ /* tune TP_IDL tail and head waveform */ ++ rt305x_mii_write(esw, 0, 22, 0x052f); ++ /* set TX10 signal amplitude threshold to minimum */ ++ rt305x_mii_write(esw, 0, 17, 0x0fe0); ++ /* set squelch amplitude to higher threshold */ ++ rt305x_mii_write(esw, 0, 18, 0x40ba); ++ /* longer TP_IDL tail length */ ++ rt305x_mii_write(esw, 0, 14, 0x65); ++ /* select local register */ ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ } ++ ++ if (esw->port_map) ++ port_map = esw->port_map; ++ else ++ port_map = RT305X_ESW_PMAP_LLLLLL; ++ ++ /* ++ * Unused HW feature, but still nice to be consistent here... ++ * This is also exported to userspace ('lan' attribute) so it's ++ * conveniently usable to decide which ports go into the wan vlan by ++ * default. ++ */ ++ esw_rmw(esw, RT305X_ESW_REG_SGC2, ++ RT305X_ESW_SGC2_LAN_PMAP_M << RT305X_ESW_SGC2_LAN_PMAP_S, ++ port_map << RT305X_ESW_SGC2_LAN_PMAP_S); ++ ++ /* make the switch leds blink */ ++ for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) ++ esw->ports[i].led = 0x05; ++ ++ /* Apply the empty config. */ ++ esw_apply_config(&esw->swdev); ++ ++ /* Only unmask the port change interrupt */ ++ esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR); ++} ++ ++static irqreturn_t esw_interrupt(int irq, void *_esw) ++{ ++ struct rt305x_esw *esw = (struct rt305x_esw *) _esw; ++ u32 status; ++ ++ status = esw_r32(esw, RT305X_ESW_REG_ISR); ++ if (status & RT305X_ESW_PORT_ST_CHG) { ++ u32 link = esw_r32(esw, RT305X_ESW_REG_POA); ++ link >>= RT305X_ESW_POA_LINK_SHIFT; ++ link &= RT305X_ESW_POA_LINK_MASK; ++ dev_info(esw->dev, "link changed 0x%02X\n", link); ++ } ++ esw_w32(esw, status, RT305X_ESW_REG_ISR); ++ ++ return IRQ_HANDLED; ++} ++ ++static int esw_apply_config(struct switch_dev *dev) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int i; ++ u8 disable = 0; ++ u8 doubletag = 0; ++ u8 en_vlan = 0; ++ u8 untag = 0; ++ ++ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { ++ u32 vid, vmsc; ++ if (esw->global_vlan_enable) { ++ vid = esw->vlans[i].vid; ++ vmsc = esw->vlans[i].ports; ++ } else { ++ vid = RT305X_ESW_VLAN_NONE; ++ vmsc = RT305X_ESW_PORTS_NONE; ++ } ++ esw_set_vlan_id(esw, i, vid); ++ esw_set_vmsc(esw, i, vmsc); ++ } ++ ++ for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) { ++ u32 pvid; ++ disable |= esw->ports[i].disable << i; ++ if (esw->global_vlan_enable) { ++ doubletag |= esw->ports[i].doubletag << i; ++ en_vlan |= 1 << i; ++ untag |= esw->ports[i].untag << i; ++ pvid = esw->ports[i].pvid; ++ } else { ++ int x = esw->alt_vlan_disable ? 0 : 1; ++ doubletag |= x << i; ++ en_vlan |= x << i; ++ untag |= x << i; ++ pvid = 0; ++ } ++ esw_set_pvid(esw, i, pvid); ++ if (i < RT305X_ESW_NUM_LEDS) ++ esw_w32(esw, esw->ports[i].led, ++ RT305X_ESW_REG_P0LED + 4*i); ++ } ++ ++ esw_set_gsc(esw); ++ esw_set_port_disable(esw, disable); ++ esw_rmw(esw, RT305X_ESW_REG_SGC2, ++ (RT305X_ESW_SGC2_DOUBLE_TAG_M << ++ RT305X_ESW_SGC2_DOUBLE_TAG_S), ++ doubletag << RT305X_ESW_SGC2_DOUBLE_TAG_S); ++ esw_rmw(esw, RT305X_ESW_REG_PFC1, ++ RT305X_ESW_PFC1_EN_VLAN_M << RT305X_ESW_PFC1_EN_VLAN_S, ++ en_vlan << RT305X_ESW_PFC1_EN_VLAN_S); ++ esw_rmw(esw, RT305X_ESW_REG_POC2, ++ RT305X_ESW_POC2_UNTAG_EN_M << RT305X_ESW_POC2_UNTAG_EN_S, ++ untag << RT305X_ESW_POC2_UNTAG_EN_S); ++ ++ if (!esw->global_vlan_enable) { ++ /* ++ * Still need to put all ports into vlan 0 or they'll be ++ * isolated. ++ * NOTE: vlan 0 is special, no vlan tag is prepended ++ */ ++ esw_set_vlan_id(esw, 0, 0); ++ esw_set_vmsc(esw, 0, RT305X_ESW_PORTS_ALL); ++ } ++ ++ return 0; ++} ++ ++static int esw_reset_switch(struct switch_dev *dev) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ esw->global_vlan_enable = 0; ++ memset(esw->ports, 0, sizeof(esw->ports)); ++ memset(esw->vlans, 0, sizeof(esw->vlans)); ++ esw_hw_init(esw); ++ ++ return 0; ++} ++ ++static int esw_get_vlan_enable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ val->value.i = esw->global_vlan_enable; ++ ++ return 0; ++} ++ ++static int esw_set_vlan_enable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ esw->global_vlan_enable = val->value.i != 0; ++ ++ return 0; ++} ++ ++static int esw_get_alt_vlan_disable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ val->value.i = esw->alt_vlan_disable; ++ ++ return 0; ++} ++ ++static int esw_set_alt_vlan_disable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ esw->alt_vlan_disable = val->value.i != 0; ++ ++ return 0; ++} ++ ++static int ++rt305x_esw_set_bc_status(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ esw->bc_storm_protect = val->value.i & RT305X_ESW_GSC_BC_STROM_MASK; ++ ++ return 0; ++} ++ ++static int ++rt305x_esw_get_bc_status(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ val->value.i = esw->bc_storm_protect; ++ ++ return 0; ++} ++ ++static int ++rt305x_esw_set_led_freq(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ esw->led_frequency = val->value.i & RT305X_ESW_GSC_LED_FREQ_MASK; ++ ++ return 0; ++} ++ ++static int ++rt305x_esw_get_led_freq(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ val->value.i = esw->led_frequency; ++ ++ return 0; ++} ++ ++static int esw_get_port_link(struct switch_dev *dev, ++ int port, ++ struct switch_port_link *link) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ u32 speed, poa; ++ ++ if (port < 0 || port >= RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ poa = esw_r32(esw, RT305X_ESW_REG_POA) >> port; ++ ++ link->link = (poa >> RT305X_ESW_LINK_S) & 1; ++ link->duplex = (poa >> RT305X_ESW_DUPLEX_S) & 1; ++ if (port < RT305X_ESW_NUM_LEDS) { ++ speed = (poa >> RT305X_ESW_SPD_S) & 1; ++ } else { ++ if (port == RT305X_ESW_NUM_PORTS - 1) ++ poa >>= 1; ++ speed = (poa >> RT305X_ESW_SPD_S) & 3; ++ } ++ switch (speed) { ++ case 0: ++ link->speed = SWITCH_PORT_SPEED_10; ++ break; ++ case 1: ++ link->speed = SWITCH_PORT_SPEED_100; ++ break; ++ case 2: ++ case 3: /* forced gige speed can be 2 or 3 */ ++ link->speed = SWITCH_PORT_SPEED_1000; ++ break; ++ default: ++ link->speed = SWITCH_PORT_SPEED_UNKNOWN; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int esw_get_port_bool(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int idx = val->port_vlan; ++ u32 x, reg, shift; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ switch (attr->id) { ++ case RT305X_ESW_ATTR_PORT_DISABLE: ++ reg = RT305X_ESW_REG_POC0; ++ shift = RT305X_ESW_POC0_DIS_PORT_S; ++ break; ++ case RT305X_ESW_ATTR_PORT_DOUBLETAG: ++ reg = RT305X_ESW_REG_SGC2; ++ shift = RT305X_ESW_SGC2_DOUBLE_TAG_S; ++ break; ++ case RT305X_ESW_ATTR_PORT_UNTAG: ++ reg = RT305X_ESW_REG_POC2; ++ shift = RT305X_ESW_POC2_UNTAG_EN_S; ++ break; ++ case RT305X_ESW_ATTR_PORT_LAN: ++ reg = RT305X_ESW_REG_SGC2; ++ shift = RT305X_ESW_SGC2_LAN_PMAP_S; ++ if (idx >= RT305X_ESW_NUM_LANWAN) ++ return -EINVAL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ x = esw_r32(esw, reg); ++ val->value.i = (x >> (idx + shift)) & 1; ++ ++ return 0; ++} ++ ++static int esw_set_port_bool(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int idx = val->port_vlan; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS || ++ val->value.i < 0 || val->value.i > 1) ++ return -EINVAL; ++ ++ switch (attr->id) { ++ case RT305X_ESW_ATTR_PORT_DISABLE: ++ esw->ports[idx].disable = val->value.i; ++ break; ++ case RT305X_ESW_ATTR_PORT_DOUBLETAG: ++ esw->ports[idx].doubletag = val->value.i; ++ break; ++ case RT305X_ESW_ATTR_PORT_UNTAG: ++ esw->ports[idx].untag = val->value.i; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int esw_get_port_recv_badgood(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int idx = val->port_vlan; ++ int shift = attr->id == RT305X_ESW_ATTR_PORT_RECV_GOOD ? 0 : 16; ++ u32 reg; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN) ++ return -EINVAL; ++ reg = esw_r32(esw, RT305X_ESW_REG_PXPC(idx)); ++ val->value.i = (reg >> shift) & 0xffff; ++ ++ return 0; ++} ++ ++static int ++esw_get_port_tr_badgood(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ int idx = val->port_vlan; ++ int shift = attr->id == RT5350_ESW_ATTR_PORT_TR_GOOD ? 0 : 16; ++ u32 reg; ++ ++ if (!soc_is_rt5350()) ++ return -EINVAL; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN) ++ return -EINVAL; ++ ++ reg = esw_r32(esw, RT5350_ESW_REG_PXTPC(idx)); ++ val->value.i = (reg >> shift) & 0xffff; ++ ++ return 0; ++} ++ ++static int esw_get_port_led(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int idx = val->port_vlan; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS || ++ idx >= RT305X_ESW_NUM_LEDS) ++ return -EINVAL; ++ ++ val->value.i = esw_r32(esw, RT305X_ESW_REG_P0LED + 4*idx); ++ ++ return 0; ++} ++ ++static int esw_set_port_led(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int idx = val->port_vlan; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_LEDS) ++ return -EINVAL; ++ ++ esw->ports[idx].led = val->value.i; ++ ++ return 0; ++} ++ ++static int esw_get_port_pvid(struct switch_dev *dev, int port, int *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ if (port >= RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ *val = esw_get_pvid(esw, port); ++ ++ return 0; ++} ++ ++static int esw_set_port_pvid(struct switch_dev *dev, int port, int val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ if (port >= RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ esw->ports[port].pvid = val; ++ ++ return 0; ++} ++ ++static int esw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ u32 vmsc, poc2; ++ int vlan_idx = -1; ++ int i; ++ ++ val->len = 0; ++ ++ if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS) ++ return -EINVAL; ++ ++ /* valid vlan? */ ++ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { ++ if (esw_get_vlan_id(esw, i) == val->port_vlan && ++ esw_get_vmsc(esw, i) != RT305X_ESW_PORTS_NONE) { ++ vlan_idx = i; ++ break; ++ } ++ } ++ ++ if (vlan_idx == -1) ++ return -EINVAL; ++ ++ vmsc = esw_get_vmsc(esw, vlan_idx); ++ poc2 = esw_r32(esw, RT305X_ESW_REG_POC2); ++ ++ for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) { ++ struct switch_port *p; ++ int port_mask = 1 << i; ++ ++ if (!(vmsc & port_mask)) ++ continue; ++ ++ p = &val->value.ports[val->len++]; ++ p->id = i; ++ if (poc2 & (port_mask << RT305X_ESW_POC2_UNTAG_EN_S)) ++ p->flags = 0; ++ else ++ p->flags = 1 << SWITCH_PORT_FLAG_TAGGED; ++ } ++ ++ return 0; ++} ++ ++static int esw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int ports; ++ int vlan_idx = -1; ++ int i; ++ ++ if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS || ++ val->len > RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ /* one of the already defined vlans? */ ++ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { ++ if (esw->vlans[i].vid == val->port_vlan && ++ esw->vlans[i].ports != RT305X_ESW_PORTS_NONE) { ++ vlan_idx = i; ++ break; ++ } ++ } ++ ++ /* select a free slot */ ++ for (i = 0; vlan_idx == -1 && i < RT305X_ESW_NUM_VLANS; i++) { ++ if (esw->vlans[i].ports == RT305X_ESW_PORTS_NONE) ++ vlan_idx = i; ++ } ++ ++ /* bail if all slots are in use */ ++ if (vlan_idx == -1) ++ return -EINVAL; ++ ++ ports = RT305X_ESW_PORTS_NONE; ++ for (i = 0; i < val->len; i++) { ++ struct switch_port *p = &val->value.ports[i]; ++ int port_mask = 1 << p->id; ++ bool untagged = !(p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)); ++ ++ if (p->id >= RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ ports |= port_mask; ++ esw->ports[p->id].untag = untagged; ++ } ++ esw->vlans[vlan_idx].ports = ports; ++ if (ports == RT305X_ESW_PORTS_NONE) ++ esw->vlans[vlan_idx].vid = RT305X_ESW_VLAN_NONE; ++ else ++ esw->vlans[vlan_idx].vid = val->port_vlan; ++ ++ return 0; ++} ++ ++static const struct switch_attr esw_global[] = { ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "enable_vlan", ++ .description = "VLAN mode (1:enabled)", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_ENABLE_VLAN, ++ .get = esw_get_vlan_enable, ++ .set = esw_set_vlan_enable, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "alternate_vlan_disable", ++ .description = "Use en_vlan instead of doubletag to disable" ++ " VLAN mode", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_ALT_VLAN_DISABLE, ++ .get = esw_get_alt_vlan_disable, ++ .set = esw_set_alt_vlan_disable, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "bc_storm_protect", ++ .description = "Global broadcast storm protection (0:Disable, 1:64 blocks, 2:96 blocks, 3:128 blocks)", ++ .max = 3, ++ .id = RT305X_ESW_ATTR_BC_STATUS, ++ .get = rt305x_esw_get_bc_status, ++ .set = rt305x_esw_set_bc_status, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "led_frequency", ++ .description = "LED Flash frequency (0:30mS, 1:60mS, 2:240mS, 3:480mS)", ++ .max = 3, ++ .id = RT305X_ESW_ATTR_LED_FREQ, ++ .get = rt305x_esw_get_led_freq, ++ .set = rt305x_esw_set_led_freq, ++ } ++}; ++ ++static const struct switch_attr esw_port[] = { ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "disable", ++ .description = "Port state (1:disabled)", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_PORT_DISABLE, ++ .get = esw_get_port_bool, ++ .set = esw_set_port_bool, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "doubletag", ++ .description = "Double tagging for incoming vlan packets " ++ "(1:enabled)", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_PORT_DOUBLETAG, ++ .get = esw_get_port_bool, ++ .set = esw_set_port_bool, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "untag", ++ .description = "Untag (1:strip outgoing vlan tag)", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_PORT_UNTAG, ++ .get = esw_get_port_bool, ++ .set = esw_set_port_bool, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "led", ++ .description = "LED mode (0:link, 1:100m, 2:duplex, 3:activity," ++ " 4:collision, 5:linkact, 6:duplcoll, 7:10mact," ++ " 8:100mact, 10:blink, 11:off, 12:on)", ++ .max = 15, ++ .id = RT305X_ESW_ATTR_PORT_LED, ++ .get = esw_get_port_led, ++ .set = esw_set_port_led, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "lan", ++ .description = "HW port group (0:wan, 1:lan)", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_PORT_LAN, ++ .get = esw_get_port_bool, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "recv_bad", ++ .description = "Receive bad packet counter", ++ .id = RT305X_ESW_ATTR_PORT_RECV_BAD, ++ .get = esw_get_port_recv_badgood, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "recv_good", ++ .description = "Receive good packet counter", ++ .id = RT305X_ESW_ATTR_PORT_RECV_GOOD, ++ .get = esw_get_port_recv_badgood, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "tr_bad", ++ ++ .description = "Transmit bad packet counter. rt5350 only", ++ .id = RT5350_ESW_ATTR_PORT_TR_BAD, ++ .get = esw_get_port_tr_badgood, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "tr_good", ++ ++ .description = "Transmit good packet counter. rt5350 only", ++ .id = RT5350_ESW_ATTR_PORT_TR_GOOD, ++ .get = esw_get_port_tr_badgood, ++ }, ++}; ++ ++static const struct switch_attr esw_vlan[] = { ++}; ++ ++static const struct switch_dev_ops esw_ops = { ++ .attr_global = { ++ .attr = esw_global, ++ .n_attr = ARRAY_SIZE(esw_global), ++ }, ++ .attr_port = { ++ .attr = esw_port, ++ .n_attr = ARRAY_SIZE(esw_port), ++ }, ++ .attr_vlan = { ++ .attr = esw_vlan, ++ .n_attr = ARRAY_SIZE(esw_vlan), ++ }, ++ .get_vlan_ports = esw_get_vlan_ports, ++ .set_vlan_ports = esw_set_vlan_ports, ++ .get_port_pvid = esw_get_port_pvid, ++ .set_port_pvid = esw_set_port_pvid, ++ .get_port_link = esw_get_port_link, ++ .apply_config = esw_apply_config, ++ .reset_switch = esw_reset_switch, ++}; ++ ++static struct rt305x_esw_platform_data rt3050_esw_data = { ++ /* All ports are LAN ports. */ ++ .vlan_config = RT305X_ESW_VLAN_CONFIG_NONE, ++ .reg_initval_fct2 = 0x00d6500c, ++ /* ++ * ext phy base addr 31, enable port 5 polling, rx/tx clock skew 1, ++ * turbo mii off, rgmi 3.3v off ++ * port5: disabled ++ * port6: enabled, gige, full-duplex, rx/tx-flow-control ++ */ ++ .reg_initval_fpa2 = 0x3f502b28, ++}; ++ ++static const struct of_device_id ralink_esw_match[] = { ++ { .compatible = "ralink,rt3050-esw", .data = &rt3050_esw_data }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ralink_esw_match); ++ ++static int esw_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ const struct rt305x_esw_platform_data *pdata; ++ const __be32 *port_map, *reg_init; ++ struct rt305x_esw *esw; ++ struct switch_dev *swdev; ++ struct resource *res, *irq; ++ int err; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) { ++ const struct of_device_id *match; ++ match = of_match_device(ralink_esw_match, &pdev->dev); ++ if (match) ++ pdata = (struct rt305x_esw_platform_data *) match->data; ++ } ++ if (!pdata) ++ return -EINVAL; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "no memory resource found\n"); ++ return -ENOMEM; ++ } ++ ++ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!irq) { ++ dev_err(&pdev->dev, "no irq resource found\n"); ++ return -ENOMEM; ++ } ++ ++ esw = kzalloc(sizeof(struct rt305x_esw), GFP_KERNEL); ++ if (!esw) { ++ dev_err(&pdev->dev, "no memory for private data\n"); ++ return -ENOMEM; ++ } ++ ++ esw->dev = &pdev->dev; ++ esw->irq = irq->start; ++ esw->base = ioremap(res->start, resource_size(res)); ++ if (!esw->base) { ++ dev_err(&pdev->dev, "ioremap failed\n"); ++ err = -ENOMEM; ++ goto free_esw; ++ } ++ ++ port_map = of_get_property(np, "ralink,portmap", NULL); ++ if (port_map) ++ esw->port_map = be32_to_cpu(*port_map); ++ ++ reg_init = of_get_property(np, "ralink,fct2", NULL); ++ if (reg_init) ++ esw->reg_initval_fct2 = be32_to_cpu(*reg_init); ++ ++ reg_init = of_get_property(np, "ralink,fpa2", NULL); ++ if (reg_init) ++ esw->reg_initval_fpa2 = be32_to_cpu(*reg_init); ++ ++ reg_init = of_get_property(np, "ralink,led_polarity", NULL); ++ if (reg_init) ++ esw->reg_led_polarity = be32_to_cpu(*reg_init); ++ ++ swdev = &esw->swdev; ++ swdev->of_node = pdev->dev.of_node; ++ swdev->name = "rt305x-esw"; ++ swdev->alias = "rt305x"; ++ swdev->cpu_port = RT305X_ESW_PORT6; ++ swdev->ports = RT305X_ESW_NUM_PORTS; ++ swdev->vlans = RT305X_ESW_NUM_VIDS; ++ swdev->ops = &esw_ops; ++ ++ err = register_switch(swdev, NULL); ++ if (err < 0) { ++ dev_err(&pdev->dev, "register_switch failed\n"); ++ goto unmap_base; ++ } ++ ++ platform_set_drvdata(pdev, esw); ++ ++ esw->pdata = pdata; ++ spin_lock_init(&esw->reg_rw_lock); ++ ++ esw_hw_init(esw); ++ ++ esw_w32(esw, RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_ISR); ++ esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR); ++ request_irq(esw->irq, esw_interrupt, 0, "esw", esw); ++ ++ return 0; ++ ++unmap_base: ++ iounmap(esw->base); ++free_esw: ++ kfree(esw); ++ return err; ++} ++ ++static int esw_remove(struct platform_device *pdev) ++{ ++ struct rt305x_esw *esw; ++ ++ esw = platform_get_drvdata(pdev); ++ if (esw) { ++ unregister_switch(&esw->swdev); ++ platform_set_drvdata(pdev, NULL); ++ iounmap(esw->base); ++ kfree(esw); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver esw_driver = { ++ .probe = esw_probe, ++ .remove = esw_remove, ++ .driver = { ++ .name = "rt305x-esw", ++ .owner = THIS_MODULE, ++ .of_match_table = ralink_esw_match, ++ }, ++}; ++ ++int __init rtesw_init(void) ++{ ++ return platform_driver_register(&esw_driver); ++} ++ ++void rtesw_exit(void) ++{ ++ platform_driver_unregister(&esw_driver); ++} +diff --git a/drivers/net/ethernet/ralink/esw_rt3052.h b/drivers/net/ethernet/ralink/esw_rt3052.h +new file mode 100644 +index 0000000..2ced3dff +--- /dev/null ++++ b/drivers/net/ethernet/ralink/esw_rt3052.h +@@ -0,0 +1,32 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#ifndef _RALINK_ESW_RT3052_H__ ++#define _RALINK_ESW_RT3052_H__ ++ ++#ifdef CONFIG_NET_RALINK_ESW_RT3052 ++ ++int __init rtesw_init(void); ++void rtesw_exit(void); ++ ++#else ++ ++static inline int __init rtesw_init(void) { return 0; } ++static inline void rtesw_exit(void) { } ++ ++#endif ++#endif +diff --git a/drivers/net/ethernet/ralink/gsw_mt7620a.c b/drivers/net/ethernet/ralink/gsw_mt7620a.c +new file mode 100644 +index 0000000..9fa6a54 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/gsw_mt7620a.c +@@ -0,0 +1,1027 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "ralink_soc_eth.h" ++#include "gsw_mt7620a.h" ++#include "mdio.h" ++ ++#define GSW_REG_PHY_TIMEOUT (5 * HZ) ++ ++#define MT7620A_GSW_REG_PIAC 0x7004 ++ ++#define GSW_NUM_VLANS 16 ++#define GSW_NUM_VIDS 4096 ++#define GSW_NUM_PORTS 7 ++#define GSW_PORT6 6 ++ ++#define GSW_MDIO_ACCESS BIT(31) ++#define GSW_MDIO_READ BIT(19) ++#define GSW_MDIO_WRITE BIT(18) ++#define GSW_MDIO_START BIT(16) ++#define GSW_MDIO_ADDR_SHIFT 20 ++#define GSW_MDIO_REG_SHIFT 25 ++ ++#define GSW_REG_PORT_PMCR(x) (0x3000 + (x * 0x100)) ++#define GSW_REG_PORT_STATUS(x) (0x3008 + (x * 0x100)) ++#define GSW_REG_SMACCR0 0x3fE4 ++#define GSW_REG_SMACCR1 0x3fE8 ++#define GSW_REG_CKGCR 0x3ff0 ++ ++#define GSW_REG_IMR 0x7008 ++#define GSW_REG_ISR 0x700c ++ ++#define SYSC_REG_CFG1 0x14 ++ ++#define PORT_IRQ_ST_CHG 0x7f ++ ++#define GSW_VLAN_VTCR 0x90 ++#define GSW_VLAN_VTCR_VID_M 0xfff ++#define GSW_VLAN_ID(_x) (0x100 + (4 * (_x))) ++#define GSW_VLAN_ID_VID_S 12 ++#define GSW_VLAN_ID_VID_M 0xfff ++ ++#define GSW_VAWD1 0x94 ++#define GSW_VAWD1_VTAG_EN BIT(28) ++#define GSW_VAWD1_PORTM_S 16 ++#define GSW_VAWD1_PORTM_M 0xff ++ ++#define GSW_VAWD2 0x98 ++#define GSW_VAWD2_PORTT_S 16 ++#define GSW_VAWD2_PORTT_M 0xff ++ ++#define GSW_VTIM(_x) (0x100 + (4 * (_x))) ++#define GSW_VTIM_M 0xfff ++#define GSW_VTIM_S 12 ++ ++#define GSW_REG_PCR(x) (0x2004 + (x * 0x100)) ++#define GSW_REG_PCR_EG_TAG_S 28 ++#define GSW_REG_PCR_EG_TAG_M 0x3 ++ ++#define SYSCFG1 0x14 ++ ++#define ESW_PHY_POLLING 0x7000 ++ ++#define PMCR_IPG BIT(18) ++#define PMCR_MAC_MODE BIT(16) ++#define PMCR_FORCE BIT(15) ++#define PMCR_TX_EN BIT(14) ++#define PMCR_RX_EN BIT(13) ++#define PMCR_BACKOFF BIT(9) ++#define PMCR_BACKPRES BIT(8) ++#define PMCR_RX_FC BIT(5) ++#define PMCR_TX_FC BIT(4) ++#define PMCR_SPEED(_x) (_x << 2) ++#define PMCR_DUPLEX BIT(1) ++#define PMCR_LINK BIT(0) ++ ++#define PHY_AN_EN BIT(31) ++#define PHY_PRE_EN BIT(30) ++#define PMY_MDC_CONF(_x) ((_x & 0x3f) << 24) ++ ++enum { ++ /* Global attributes. */ ++ GSW_ATTR_ENABLE_VLAN, ++ /* Port attributes. */ ++ GSW_ATTR_PORT_UNTAG, ++}; ++ ++enum { ++ PORT4_EPHY = 0, ++ PORT4_EXT, ++}; ++ ++struct gsw_port { ++ bool disable; ++ bool untag; ++ u16 pvid; ++}; ++ ++struct gsw_vlan { ++ u8 ports; ++ u16 vid; ++}; ++ ++struct mt7620_gsw { ++ struct device *dev; ++ void __iomem *base; ++ int irq; ++ ++ struct switch_dev swdev; ++ bool global_vlan_enable; ++ struct gsw_vlan vlans[GSW_NUM_VLANS]; ++ struct gsw_port ports[GSW_NUM_PORTS]; ++ long unsigned int autopoll; ++ int port4; ++}; ++ ++static inline void gsw_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg) ++{ ++ iowrite32(val, gsw->base + reg); ++} ++ ++static inline u32 gsw_r32(struct mt7620_gsw *gsw, unsigned reg) ++{ ++ return ioread32(gsw->base + reg); ++} ++ ++static int mt7620_mii_busy_wait(struct mt7620_gsw *gsw) ++{ ++ unsigned long t_start = jiffies; ++ ++ while (1) { ++ if (!(gsw_r32(gsw, MT7620A_GSW_REG_PIAC) & GSW_MDIO_ACCESS)) ++ return 0; ++ if (time_after(jiffies, t_start + GSW_REG_PHY_TIMEOUT)) { ++ break; ++ } ++ } ++ ++ printk(KERN_ERR "mdio: MDIO timeout\n"); ++ return -1; ++} ++ ++static u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr, u32 phy_register, ++ u32 write_data) ++{ ++ if (mt7620_mii_busy_wait(gsw)) ++ return -1; ++ ++ write_data &= 0xffff; ++ ++ gsw_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_WRITE | ++ (phy_register << GSW_MDIO_REG_SHIFT) | ++ (phy_addr << GSW_MDIO_ADDR_SHIFT) | write_data, ++ MT7620A_GSW_REG_PIAC); ++ ++ if (mt7620_mii_busy_wait(gsw)) ++ return -1; ++ ++ return 0; ++} ++ ++int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val) ++{ ++ struct fe_priv *priv = bus->priv; ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ ++ return _mt7620_mii_write(gsw, phy_addr, phy_reg, val); ++} ++ ++int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) ++{ ++ struct fe_priv *priv = bus->priv; ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ u32 d; ++ ++ if (mt7620_mii_busy_wait(gsw)) ++ return 0xffff; ++ ++ gsw_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_READ | ++ (phy_reg << GSW_MDIO_REG_SHIFT) | ++ (phy_addr << GSW_MDIO_ADDR_SHIFT), ++ MT7620A_GSW_REG_PIAC); ++ ++ if (mt7620_mii_busy_wait(gsw)) ++ return 0xffff; ++ ++ d = gsw_r32(gsw, MT7620A_GSW_REG_PIAC) & 0xffff; ++ ++ return d; ++} ++ ++static unsigned char *fe_speed_str(int speed) ++{ ++ switch (speed) { ++ case 2: ++ case SPEED_1000: ++ return "1000"; ++ case 1: ++ case SPEED_100: ++ return "100"; ++ case 0: ++ case SPEED_10: ++ return "10"; ++ } ++ ++ return "? "; ++} ++ ++int mt7620a_has_carrier(struct fe_priv *priv) ++{ ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ int i; ++ ++ for (i = 0; i < GSW_PORT6; i++) ++ if (gsw_r32(gsw, GSW_REG_PORT_STATUS(i)) & 0x1) ++ return 1; ++ return 0; ++} ++ ++static void mt7620a_handle_carrier(struct fe_priv *priv) ++{ ++ if (!priv->phy) ++ return; ++ ++ if (mt7620a_has_carrier(priv)) ++ netif_carrier_on(priv->netdev); ++ else ++ netif_carrier_off(priv->netdev); ++} ++ ++void mt7620_mdio_link_adjust(struct fe_priv *priv, int port) ++{ ++ if (priv->link[port]) ++ netdev_info(priv->netdev, "port %d link up (%sMbps/%s duplex)\n", ++ port, fe_speed_str(priv->phy->speed[port]), ++ (DUPLEX_FULL == priv->phy->duplex[port]) ? "Full" : "Half"); ++ else ++ netdev_info(priv->netdev, "port %d link down\n", port); ++ mt7620a_handle_carrier(priv); ++} ++ ++static irqreturn_t gsw_interrupt(int irq, void *_priv) ++{ ++ struct fe_priv *priv = (struct fe_priv *) _priv; ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ u32 status; ++ int i, max = (gsw->port4 == PORT4_EPHY) ? (4) : (3); ++ ++ status = gsw_r32(gsw, GSW_REG_ISR); ++ if (status & PORT_IRQ_ST_CHG) ++ for (i = 0; i <= max; i++) { ++ u32 status = gsw_r32(gsw, GSW_REG_PORT_STATUS(i)); ++ int link = status & 0x1; ++ ++ if (link != priv->link[i]) { ++ if (link) ++ netdev_info(priv->netdev, "port %d link up (%sMbps/%s duplex)\n", ++ i, fe_speed_str((status >> 2) & 3), ++ (status & 0x2) ? "Full" : "Half"); ++ else ++ netdev_info(priv->netdev, "port %d link down\n", i); ++ } ++ ++ priv->link[i] = link; ++ } ++ mt7620a_handle_carrier(priv); ++ ++ gsw_w32(gsw, status, GSW_REG_ISR); ++ ++ return IRQ_HANDLED; ++} ++ ++static int mt7620_is_bga(void) ++{ ++ u32 bga = rt_sysc_r32(0x0c); ++ ++ return (bga >> 16) & 1; ++} ++ ++static void gsw_auto_poll(struct mt7620_gsw *gsw) ++{ ++ int phy; ++ int lsb = -1, msb = 0; ++ ++ for_each_set_bit(phy, &gsw->autopoll, 32) { ++ if (lsb < 0) ++ lsb = phy; ++ msb = phy; ++ } ++ ++ gsw_w32(gsw, PHY_AN_EN | PHY_PRE_EN | PMY_MDC_CONF(5) | (msb << 8) | lsb, ESW_PHY_POLLING); ++} ++ ++void mt7620_port_init(struct fe_priv *priv, struct device_node *np) ++{ ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ const __be32 *_id = of_get_property(np, "reg", NULL); ++ int phy_mode, size, id; ++ int shift = 12; ++ u32 val, mask = 0; ++ int min = (gsw->port4 == PORT4_EPHY) ? (5) : (4); ++ ++ if (!_id || (be32_to_cpu(*_id) < min) || (be32_to_cpu(*_id) > 5)) { ++ if (_id) ++ pr_err("%s: invalid port id %d\n", np->name, be32_to_cpu(*_id)); ++ else ++ pr_err("%s: invalid port id\n", np->name); ++ return; ++ } ++ ++ id = be32_to_cpu(*_id); ++ ++ if (id == 4) ++ shift = 14; ++ ++ priv->phy->phy_fixed[id] = of_get_property(np, "ralink,fixed-link", &size); ++ if (priv->phy->phy_fixed[id] && (size != (4 * sizeof(*priv->phy->phy_fixed[id])))) { ++ pr_err("%s: invalid fixed link property\n", np->name); ++ priv->phy->phy_fixed[id] = NULL; ++ return; ++ } ++ ++ phy_mode = of_get_phy_mode(np); ++ switch (phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ mask = 0; ++ break; ++ case PHY_INTERFACE_MODE_MII: ++ mask = 1; ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ mask = 2; ++ break; ++ default: ++ dev_err(priv->device, "port %d - invalid phy mode\n", priv->phy->speed[id]); ++ return; ++ } ++ ++ priv->phy->phy_node[id] = of_parse_phandle(np, "phy-handle", 0); ++ if (!priv->phy->phy_node[id] && !priv->phy->phy_fixed[id]) ++ return; ++ ++ val = rt_sysc_r32(SYSCFG1); ++ val &= ~(3 << shift); ++ val |= mask << shift; ++ rt_sysc_w32(val, SYSCFG1); ++ ++ if (priv->phy->phy_fixed[id]) { ++ const __be32 *link = priv->phy->phy_fixed[id]; ++ int tx_fc = be32_to_cpup(link++); ++ int rx_fc = be32_to_cpup(link++); ++ u32 val = 0; ++ ++ priv->phy->speed[id] = be32_to_cpup(link++); ++ priv->phy->duplex[id] = be32_to_cpup(link++); ++ priv->link[id] = 1; ++ ++ switch (priv->phy->speed[id]) { ++ case SPEED_10: ++ val = 0; ++ break; ++ case SPEED_100: ++ val = 1; ++ break; ++ case SPEED_1000: ++ val = 2; ++ break; ++ default: ++ dev_err(priv->device, "invalid link speed: %d\n", priv->phy->speed[id]); ++ priv->phy->phy_fixed[id] = 0; ++ return; ++ } ++ val = PMCR_SPEED(val); ++ val |= PMCR_LINK | PMCR_BACKPRES | PMCR_BACKOFF | PMCR_RX_EN | ++ PMCR_TX_EN | PMCR_FORCE | PMCR_MAC_MODE | PMCR_IPG; ++ if (tx_fc) ++ val |= PMCR_TX_FC; ++ if (rx_fc) ++ val |= PMCR_RX_FC; ++ if (priv->phy->duplex[id]) ++ val |= PMCR_DUPLEX; ++ gsw_w32(gsw, val, GSW_REG_PORT_PMCR(id)); ++ dev_info(priv->device, "using fixed link parameters\n"); ++ return; ++ } ++ ++ if (priv->phy->phy_node[id] && priv->mii_bus->phy_map[id]) { ++ u32 val = PMCR_BACKPRES | PMCR_BACKOFF | PMCR_RX_EN | ++ PMCR_TX_EN | PMCR_MAC_MODE | PMCR_IPG; ++ ++ gsw_w32(gsw, val, GSW_REG_PORT_PMCR(id)); ++ fe_connect_phy_node(priv, priv->phy->phy_node[id]); ++ gsw->autopoll |= BIT(id); ++ gsw_auto_poll(gsw); ++ return; ++ } ++} ++ ++static void gsw_hw_init(struct mt7620_gsw *gsw) ++{ ++ u32 is_BGA = mt7620_is_bga(); ++ ++ rt_sysc_w32(rt_sysc_r32(SYSC_REG_CFG1) | BIT(8), SYSC_REG_CFG1); ++ gsw_w32(gsw, gsw_r32(gsw, GSW_REG_CKGCR) & ~(0x3 << 4), GSW_REG_CKGCR); ++ ++ /*correct PHY setting L3.0 BGA*/ ++ _mt7620_mii_write(gsw, 1, 31, 0x4000); //global, page 4 ++ ++ _mt7620_mii_write(gsw, 1, 17, 0x7444); ++ if (is_BGA) ++ _mt7620_mii_write(gsw, 1, 19, 0x0114); ++ else ++ _mt7620_mii_write(gsw, 1, 19, 0x0117); ++ ++ _mt7620_mii_write(gsw, 1, 22, 0x10cf); ++ _mt7620_mii_write(gsw, 1, 25, 0x6212); ++ _mt7620_mii_write(gsw, 1, 26, 0x0777); ++ _mt7620_mii_write(gsw, 1, 29, 0x4000); ++ _mt7620_mii_write(gsw, 1, 28, 0xc077); ++ _mt7620_mii_write(gsw, 1, 24, 0x0000); ++ ++ _mt7620_mii_write(gsw, 1, 31, 0x3000); //global, page 3 ++ _mt7620_mii_write(gsw, 1, 17, 0x4838); ++ ++ _mt7620_mii_write(gsw, 1, 31, 0x2000); //global, page 2 ++ if (is_BGA) { ++ _mt7620_mii_write(gsw, 1, 21, 0x0515); ++ _mt7620_mii_write(gsw, 1, 22, 0x0053); ++ _mt7620_mii_write(gsw, 1, 23, 0x00bf); ++ _mt7620_mii_write(gsw, 1, 24, 0x0aaf); ++ _mt7620_mii_write(gsw, 1, 25, 0x0fad); ++ _mt7620_mii_write(gsw, 1, 26, 0x0fc1); ++ } else { ++ _mt7620_mii_write(gsw, 1, 21, 0x0517); ++ _mt7620_mii_write(gsw, 1, 22, 0x0fd2); ++ _mt7620_mii_write(gsw, 1, 23, 0x00bf); ++ _mt7620_mii_write(gsw, 1, 24, 0x0aab); ++ _mt7620_mii_write(gsw, 1, 25, 0x00ae); ++ _mt7620_mii_write(gsw, 1, 26, 0x0fff); ++ } ++ _mt7620_mii_write(gsw, 1, 31, 0x1000); //global, page 1 ++ _mt7620_mii_write(gsw, 1, 17, 0xe7f8); ++ ++ _mt7620_mii_write(gsw, 1, 31, 0x8000); //local, page 0 ++ _mt7620_mii_write(gsw, 0, 30, 0xa000); ++ _mt7620_mii_write(gsw, 1, 30, 0xa000); ++ _mt7620_mii_write(gsw, 2, 30, 0xa000); ++ _mt7620_mii_write(gsw, 3, 30, 0xa000); ++ ++ _mt7620_mii_write(gsw, 0, 4, 0x05e1); ++ _mt7620_mii_write(gsw, 1, 4, 0x05e1); ++ _mt7620_mii_write(gsw, 2, 4, 0x05e1); ++ _mt7620_mii_write(gsw, 3, 4, 0x05e1); ++ _mt7620_mii_write(gsw, 1, 31, 0xa000); //local, page 2 ++ _mt7620_mii_write(gsw, 0, 16, 0x1111); ++ _mt7620_mii_write(gsw, 1, 16, 0x1010); ++ _mt7620_mii_write(gsw, 2, 16, 0x1515); ++ _mt7620_mii_write(gsw, 3, 16, 0x0f0f); ++ ++ /* CPU Port6 Force Link 1G, FC ON */ ++ gsw_w32(gsw, 0x5e33b, GSW_REG_PORT_PMCR(6)); ++ /* Set Port6 CPU Port */ ++ gsw_w32(gsw, 0x7f7f7fe0, 0x0010); ++ ++// GSW_VAWD2 ++ ++ /* setup port 4 */ ++ if (gsw->port4 == PORT4_EPHY) { ++ u32 val = rt_sysc_r32(SYSCFG1); ++ val |= 3 << 14; ++ rt_sysc_w32(val, SYSCFG1); ++ _mt7620_mii_write(gsw, 4, 30, 0xa000); ++ _mt7620_mii_write(gsw, 4, 4, 0x05e1); ++ _mt7620_mii_write(gsw, 4, 16, 0x1313); ++ pr_info("gsw: setting port4 to ephy mode\n"); ++ } ++} ++ ++static int gsw_reset_switch(struct switch_dev *dev) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ ++ gsw->global_vlan_enable = 0; ++ memset(gsw->ports, 0, sizeof(gsw->ports)); ++ memset(gsw->vlans, 0, sizeof(gsw->vlans)); ++ gsw_hw_init(gsw); ++ ++ return 0; ++} ++ ++static int gsw_get_vlan_enable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ ++ val->value.i = gsw->global_vlan_enable; ++ ++ return 0; ++} ++ ++static int gsw_set_vlan_enable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ ++ gsw->global_vlan_enable = val->value.i != 0; ++ ++ return 0; ++} ++ ++static unsigned gsw_get_pvid(struct mt7620_gsw *gsw, unsigned port) ++{ ++ unsigned s, val; ++ ++ s = GSW_VTIM_S * (port % 2); ++ val = gsw_r32(gsw, GSW_VTIM(port / 2)); ++ ++ return (val >> s) & GSW_VTIM_M; ++} ++ ++static void gsw_set_pvid(struct mt7620_gsw *gsw, unsigned port, unsigned pvid) ++{ ++ unsigned s, val; ++ ++ s = GSW_VTIM_S * (port % 2); ++ val = gsw_r32(gsw, GSW_VTIM(port / 2)); ++ val &= ~(GSW_VTIM_M << s); ++ val |= (pvid && GSW_VTIM_M) << s; ++ gsw_w32(gsw, val, GSW_VTIM(port / 2)); ++} ++ ++static int gsw_get_port_bool(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int idx = val->port_vlan; ++ ++ if (idx < 0 || idx >= GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ switch (attr->id) { ++ case GSW_ATTR_PORT_UNTAG: ++ return gsw->ports[idx].untag; ++ } ++ ++ return -EINVAL; ++} ++ ++static int gsw_get_port_pvid(struct switch_dev *dev, int port, int *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ ++ if (port >= GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ *val = gsw_get_pvid(gsw, port); ++ ++ return 0; ++} ++ ++static int gsw_set_port_pvid(struct switch_dev *dev, int port, int val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ ++ if (port >= GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ gsw->ports[port].pvid = val; ++ ++ return 0; ++} ++ ++static void gsw_set_vtcr(struct switch_dev *dev, u32 vid) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int retry = 1000; ++ ++ gsw_w32(gsw, 0x80000000 | (BIT(vid) & GSW_VLAN_VTCR_VID_M), GSW_VLAN_VTCR); ++ while (retry-- && (gsw_r32(gsw, GSW_VLAN_VTCR) & 0x80000000)) ++ ; ++} ++ ++static void gsw_apply_vtcr(struct switch_dev *dev, u32 vid) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int retry = 1000; ++ ++ gsw_w32(gsw, 0x80001000 | (BIT(vid) & GSW_VLAN_VTCR_VID_M), GSW_VLAN_VTCR); ++ while (retry-- && (gsw_r32(gsw, GSW_VLAN_VTCR) & 0x80000000)) ++ ; ++} ++ ++static unsigned gsw_get_vlan_id(struct mt7620_gsw *gsw, unsigned vlan) ++{ ++ unsigned s; ++ unsigned val; ++ ++ s = GSW_VLAN_ID_VID_S * (vlan % 2); ++ val = gsw_r32(gsw, GSW_VLAN_ID(vlan / 2)); ++ val = (val >> s) & GSW_VLAN_ID_VID_M; ++ ++ return val; ++} ++ ++static void gsw_set_vlan_id(struct mt7620_gsw *gsw, unsigned vlan, unsigned vid) ++{ ++ unsigned s; ++ unsigned val; ++ ++ s = GSW_VLAN_ID_VID_S * (vlan % 2); ++ val = gsw_r32(gsw, GSW_VLAN_ID(vlan / 2)); ++ val &= ~(GSW_VLAN_ID_VID_M << s); ++ val |= (vid << s); ++ gsw_w32(gsw, val, GSW_VLAN_ID(vlan / 2)); ++} ++ ++static void gsw_vlan_tagging_enable(struct mt7620_gsw *gsw, unsigned vlan, unsigned enable) ++{ ++ unsigned val; ++ ++ val = gsw_r32(gsw, GSW_VAWD1); ++ if (enable) ++ val |= GSW_VAWD1_VTAG_EN; ++ else ++ val &= ~GSW_VAWD1_VTAG_EN; ++ gsw_w32(gsw, val, GSW_VAWD1); ++} ++ ++static unsigned gsw_get_port_member(struct mt7620_gsw *gsw, unsigned vlan) ++{ ++ unsigned val; ++ ++ gsw_set_vtcr(&gsw->swdev, vlan); ++ ++ val = gsw_r32(gsw, GSW_VAWD1); ++ val = (val >> GSW_VAWD1_PORTM_S) & GSW_VAWD1_PORTM_M; ++ ++ return val; ++} ++ ++static void gsw_set_port_member(struct mt7620_gsw *gsw, unsigned vlan, unsigned member) ++{ ++ unsigned val; ++ ++ val = gsw_r32(gsw, GSW_VAWD1); ++ val = ~(GSW_VAWD1_PORTM_M << GSW_VAWD1_PORTM_S); ++ val |= (member & GSW_VAWD1_PORTM_M) << GSW_VAWD1_PORTM_S; ++ gsw_w32(gsw, val, GSW_VAWD1); ++} ++ ++static unsigned gsw_get_port_tag(struct mt7620_gsw *gsw, unsigned port) ++{ ++ unsigned val; ++ ++ val = gsw_r32(gsw, GSW_REG_PCR(port)); ++ val >>= GSW_REG_PCR_EG_TAG_S; ++ val &= GSW_REG_PCR_EG_TAG_M; ++ ++ return !!val; ++} ++ ++static void gsw_set_port_untag(struct mt7620_gsw *gsw, unsigned port, unsigned untag) ++{ ++ unsigned val; ++ ++ val = gsw_r32(gsw, GSW_REG_PCR(port)); ++ if (!untag) ++ untag = 0x2; ++ else ++ untag = 0; ++ val &= ~(GSW_REG_PCR_EG_TAG_M << GSW_REG_PCR_EG_TAG_S); ++ val |= (untag & GSW_REG_PCR_EG_TAG_M) << GSW_REG_PCR_EG_TAG_S; ++ gsw_w32(gsw, val, GSW_REG_PCR(port)); ++} ++ ++static int gsw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int vlan_idx = -1; ++ u32 member; ++ int i; ++ ++ val->len = 0; ++ ++ if (val->port_vlan < 0 || val->port_vlan >= GSW_NUM_VIDS) ++ return -EINVAL; ++ ++ /* valid vlan? */ ++ for (i = 0; i < GSW_NUM_VLANS; i++) { ++ if (gsw_get_vlan_id(gsw, i) != val->port_vlan) ++ continue; ++ member = gsw_get_port_member(gsw, i); ++ vlan_idx = i; ++ break; ++ } ++ ++ if (vlan_idx == -1) ++ return -EINVAL; ++ ++ for (i = 0; i < GSW_NUM_PORTS; i++) { ++ struct switch_port *p; ++ int port_mask = 1 << i; ++ ++ if (!(member & port_mask)) ++ continue; ++ ++ p = &val->value.ports[val->len++]; ++ p->id = i; ++ if (gsw_get_port_tag(gsw, i)) ++ p->flags = 1 << SWITCH_PORT_FLAG_TAGGED; ++ else ++ p->flags = 0; ++ } ++ ++ return 0; ++} ++ ++static int gsw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int ports; ++ int vlan_idx = -1; ++ int i; ++ ++ if (val->port_vlan < 0 || val->port_vlan >= GSW_NUM_VIDS || ++ val->len > GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ /* one of the already defined vlans? */ ++ for (i = 0; i < GSW_NUM_VLANS; i++) { ++ if (gsw->vlans[i].vid == val->port_vlan && ++ gsw->vlans[i].ports) { ++ vlan_idx = i; ++ break; ++ } ++ } ++ ++ /* select a free slot */ ++ for (i = 0; vlan_idx == -1 && i < GSW_NUM_VLANS; i++) { ++ if (!gsw->vlans[i].ports) ++ vlan_idx = i; ++ } ++ ++ /* bail if all slots are in use */ ++ if (vlan_idx == -1) ++ return -EINVAL; ++ ++ ports = 0; ++ for (i = 0; i < val->len; i++) { ++ struct switch_port *p = &val->value.ports[i]; ++ int port_mask = 1 << p->id; ++ bool untagged = !(p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)); ++ ++ if (p->id >= GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ ports |= port_mask; ++ gsw->ports[p->id].untag = untagged; ++ } ++ gsw->vlans[vlan_idx].ports = ports; ++ if (!ports) ++ gsw->vlans[vlan_idx].vid = 0xfff; ++ else ++ gsw->vlans[vlan_idx].vid = val->port_vlan; ++ ++ return 0; ++} ++ ++static int gsw_apply_config(struct switch_dev *dev) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int i; ++ ++ for (i = 0; i < GSW_NUM_VLANS; i++) { ++ gsw_set_vtcr(&gsw->swdev, i); ++ if (gsw->global_vlan_enable) { ++ gsw_set_vlan_id(gsw, i, gsw->vlans[i].vid); ++ gsw_set_port_member(gsw, i, gsw->vlans[i].ports); ++ gsw_vlan_tagging_enable(gsw, i, 1); ++ } else { ++ gsw_set_vlan_id(gsw, i, 0xfff); ++ gsw_set_port_member(gsw, i, 0); ++ gsw_vlan_tagging_enable(gsw, i, 0); ++ } ++ gsw_apply_vtcr(&gsw->swdev, i); ++ } ++ ++ for (i = 0; i < GSW_NUM_PORTS; i++) { ++ if (gsw->global_vlan_enable) { ++ gsw_set_port_untag(gsw, i, !gsw->ports[i].untag); ++ gsw_set_pvid(gsw, i, gsw->ports[i].pvid); ++ } else { ++ gsw_set_port_untag(gsw, i, 0); ++ gsw_set_pvid(gsw, i, 0); ++ } ++ } ++ ++ if (!gsw->global_vlan_enable) ++ gsw_set_vlan_id(gsw, 0, 0); ++ ++ return 0; ++} ++ ++static int gsw_get_port_link(struct switch_dev *dev, ++ int port, ++ struct switch_port_link *link) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ u32 status; ++ ++ if (port < 0 || port >= GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ status = gsw_r32(gsw, GSW_REG_PORT_STATUS(port)); ++ link->link = status & 0x1; ++ link->duplex = (status >> 1) & 1; ++ ++ switch ((status >> 2) & 0x3) { ++ case 0: ++ link->speed = SWITCH_PORT_SPEED_10; ++ break; ++ case 1: ++ link->speed = SWITCH_PORT_SPEED_100; ++ break; ++ case 2: ++ case 3: // forced gige speed can be 2 or 3 ++ link->speed = SWITCH_PORT_SPEED_1000; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int gsw_set_port_bool(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int idx = val->port_vlan; ++ ++ if (idx < 0 || idx >= GSW_NUM_PORTS || ++ val->value.i < 0 || val->value.i > 1) ++ return -EINVAL; ++ ++ switch (attr->id) { ++ case GSW_ATTR_PORT_UNTAG: ++ gsw->ports[idx].untag = val->value.i; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct switch_attr gsw_global[] = { ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "enable_vlan", ++ .description = "VLAN mode (1:enabled)", ++ .max = 1, ++ .id = GSW_ATTR_ENABLE_VLAN, ++ .get = gsw_get_vlan_enable, ++ .set = gsw_set_vlan_enable, ++ }, ++}; ++ ++static const struct switch_attr gsw_port[] = { ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "untag", ++ .description = "Untag (1:strip outgoing vlan tag)", ++ .max = 1, ++ .id = GSW_ATTR_PORT_UNTAG, ++ .get = gsw_get_port_bool, ++ .set = gsw_set_port_bool, ++ }, ++}; ++ ++static const struct switch_attr gsw_vlan[] = { ++}; ++ ++static const struct switch_dev_ops gsw_ops = { ++ .attr_global = { ++ .attr = gsw_global, ++ .n_attr = ARRAY_SIZE(gsw_global), ++ }, ++ .attr_port = { ++ .attr = gsw_port, ++ .n_attr = ARRAY_SIZE(gsw_port), ++ }, ++ .attr_vlan = { ++ .attr = gsw_vlan, ++ .n_attr = ARRAY_SIZE(gsw_vlan), ++ }, ++ .get_vlan_ports = gsw_get_vlan_ports, ++ .set_vlan_ports = gsw_set_vlan_ports, ++ .get_port_pvid = gsw_get_port_pvid, ++ .set_port_pvid = gsw_set_port_pvid, ++ .get_port_link = gsw_get_port_link, ++ .apply_config = gsw_apply_config, ++ .reset_switch = gsw_reset_switch, ++}; ++ ++void mt7620_set_mac(struct fe_priv *priv, unsigned char *mac) ++{ ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ gsw_w32(gsw, (mac[0] << 8) | mac[1], GSW_REG_SMACCR1); ++ gsw_w32(gsw, (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], ++ GSW_REG_SMACCR0); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++} ++ ++static struct of_device_id gsw_match[] = { ++ { .compatible = "ralink,mt7620a-gsw" }, ++ {} ++}; ++ ++int mt7620_gsw_probe(struct fe_priv *priv) ++{ ++ struct mt7620_gsw *gsw; ++ struct device_node *np; ++ struct switch_dev *swdev; ++ const char *port4 = NULL; ++ ++ np = of_find_matching_node(NULL, gsw_match); ++ if (!np) { ++ dev_err(priv->device, "no gsw node found\n"); ++ return -EINVAL; ++ } ++ np = of_node_get(np); ++ ++ gsw = devm_kzalloc(priv->device, sizeof(struct mt7620_gsw), GFP_KERNEL); ++ if (!gsw) { ++ dev_err(priv->device, "no gsw memory for private data\n"); ++ return -ENOMEM; ++ } ++ ++ gsw->irq = irq_of_parse_and_map(np, 0); ++ if (!gsw->irq) { ++ dev_err(priv->device, "no gsw irq resource found\n"); ++ return -ENOMEM; ++ } ++ ++ gsw->base = of_iomap(np, 0); ++ if (!gsw->base) { ++ dev_err(priv->device, "gsw ioremap failed\n"); ++ } ++ ++ gsw->dev = priv->device; ++ priv->soc->swpriv = gsw; ++ ++ swdev = &gsw->swdev; ++ swdev->of_node = np; ++ swdev->name = "mt7620a-gsw"; ++ swdev->alias = "mt7620x"; ++ swdev->cpu_port = GSW_PORT6; ++ swdev->ports = GSW_NUM_PORTS; ++ swdev->vlans = GSW_NUM_VLANS; ++ swdev->ops = &gsw_ops; ++ ++ if (register_switch(swdev, NULL)) ++ dev_err(priv->device, "register_switch failed\n"); ++ ++ of_property_read_string(np, "ralink,port4", &port4); ++ if (port4 && !strcmp(port4, "ephy")) ++ gsw->port4 = PORT4_EPHY; ++ else if (port4 && !strcmp(port4, "gmac")) ++ gsw->port4 = PORT4_EXT; ++ else ++ WARN_ON(port4); ++ ++ gsw_hw_init(gsw); ++ ++ gsw_w32(gsw, ~PORT_IRQ_ST_CHG, GSW_REG_IMR); ++ request_irq(gsw->irq, gsw_interrupt, 0, "gsw", priv); ++ ++ return 0; ++} +diff --git a/drivers/net/ethernet/ralink/gsw_mt7620a.h b/drivers/net/ethernet/ralink/gsw_mt7620a.h +new file mode 100644 +index 0000000..fd4add5 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/gsw_mt7620a.h +@@ -0,0 +1,29 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#ifndef _RALINK_GSW_MT7620_H__ ++#define _RALINK_GSW_MT7620_H__ ++ ++extern int mt7620_gsw_probe(struct fe_priv *priv); ++extern void mt7620_set_mac(struct fe_priv *priv, unsigned char *mac); ++extern int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val); ++extern int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg); ++extern void mt7620_mdio_link_adjust(struct fe_priv *priv, int port); ++extern void mt7620_port_init(struct fe_priv *priv, struct device_node *np); ++extern int mt7620a_has_carrier(struct fe_priv *priv); ++ ++#endif +diff --git a/drivers/net/ethernet/ralink/mdio.c b/drivers/net/ethernet/ralink/mdio.c +new file mode 100644 +index 0000000..b265c75 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/mdio.c +@@ -0,0 +1,245 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ralink_soc_eth.h" ++#include "mdio.h" ++ ++static int fe_mdio_reset(struct mii_bus *bus) ++{ ++ /* TODO */ ++ return 0; ++} ++ ++static void fe_phy_link_adjust(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&priv->phy->lock, flags); ++ for (i = 0; i < 8; i++) { ++ if (priv->phy->phy_node[i]) { ++ struct phy_device *phydev = priv->phy->phy[i]; ++ int status_change = 0; ++ ++ if (phydev->link) ++ if (priv->phy->duplex[i] != phydev->duplex || ++ priv->phy->speed[i] != phydev->speed) ++ status_change = 1; ++ ++ if (phydev->link != priv->link[i]) ++ status_change = 1; ++ ++ switch (phydev->speed) { ++ case SPEED_1000: ++ case SPEED_100: ++ case SPEED_10: ++ priv->link[i] = phydev->link; ++ priv->phy->duplex[i] = phydev->duplex; ++ priv->phy->speed[i] = phydev->speed; ++ ++ if (status_change && priv->soc->mdio_adjust_link) ++ priv->soc->mdio_adjust_link(priv, i); ++ break; ++ } ++ } ++ } ++ spin_unlock_irqrestore(&priv->phy->lock, flags); ++} ++ ++int fe_connect_phy_node(struct fe_priv *priv, struct device_node *phy_node) ++{ ++ const __be32 *_port = NULL; ++ struct phy_device *phydev; ++ int phy_mode, port; ++ ++ _port = of_get_property(phy_node, "reg", NULL); ++ ++ if (!_port || (be32_to_cpu(*_port) >= 8)) { ++ pr_err("%s: invalid port id\n", phy_node->name); ++ return -EINVAL; ++ } ++ port = be32_to_cpu(*_port); ++ phy_mode = of_get_phy_mode(phy_node); ++ if (phy_mode < 0) { ++ dev_err(priv->device, "incorrect phy-mode %d\n", phy_mode); ++ priv->phy->phy_node[port] = NULL; ++ return -EINVAL; ++ } ++ ++ phydev = of_phy_connect(priv->netdev, phy_node, fe_phy_link_adjust, ++ 0, phy_mode); ++ if (IS_ERR(phydev)) { ++ dev_err(priv->device, "could not connect to PHY\n"); ++ priv->phy->phy_node[port] = NULL; ++ return PTR_ERR(phydev); ++ } ++ ++ phydev->supported &= PHY_GBIT_FEATURES; ++ phydev->advertising = phydev->supported; ++ phydev->no_auto_carrier_off = 1; ++ ++ dev_info(priv->device, ++ "connected port %d to PHY at %s [uid=%08x, driver=%s]\n", ++ port, dev_name(&phydev->dev), phydev->phy_id, ++ phydev->drv->name); ++ ++ priv->phy->phy[port] = phydev; ++ priv->link[port] = 0; ++ ++ return 0; ++} ++ ++static int fe_phy_connect(struct fe_priv *priv) ++{ ++ return 0; ++} ++ ++static void fe_phy_disconnect(struct fe_priv *priv) ++{ ++ unsigned long flags; ++ int i; ++ ++ for (i = 0; i < 8; i++) ++ if (priv->phy->phy_fixed[i]) { ++ spin_lock_irqsave(&priv->phy->lock, flags); ++ priv->link[i] = 0; ++ if (priv->soc->mdio_adjust_link) ++ priv->soc->mdio_adjust_link(priv, i); ++ spin_unlock_irqrestore(&priv->phy->lock, flags); ++ } else if (priv->phy->phy[i]) { ++ phy_disconnect(priv->phy->phy[i]); ++ } ++} ++ ++static void fe_phy_start(struct fe_priv *priv) ++{ ++ unsigned long flags; ++ int i; ++ ++ for (i = 0; i < 8; i++) { ++ if (priv->phy->phy_fixed[i]) { ++ spin_lock_irqsave(&priv->phy->lock, flags); ++ priv->link[i] = 1; ++ if (priv->soc->mdio_adjust_link) ++ priv->soc->mdio_adjust_link(priv, i); ++ spin_unlock_irqrestore(&priv->phy->lock, flags); ++ } else if (priv->phy->phy[i]) { ++ phy_start(priv->phy->phy[i]); ++ } ++ } ++} ++ ++static void fe_phy_stop(struct fe_priv *priv) ++{ ++ unsigned long flags; ++ int i; ++ ++ for (i = 0; i < 8; i++) ++ if (priv->phy->phy_fixed[i]) { ++ spin_lock_irqsave(&priv->phy->lock, flags); ++ priv->link[i] = 0; ++ if (priv->soc->mdio_adjust_link) ++ priv->soc->mdio_adjust_link(priv, i); ++ spin_unlock_irqrestore(&priv->phy->lock, flags); ++ } else if (priv->phy->phy[i]) { ++ phy_stop(priv->phy->phy[i]); ++ } ++} ++ ++static struct fe_phy phy_ralink = { ++ .connect = fe_phy_connect, ++ .disconnect = fe_phy_disconnect, ++ .start = fe_phy_start, ++ .stop = fe_phy_stop, ++}; ++ ++int fe_mdio_init(struct fe_priv *priv) ++{ ++ struct device_node *mii_np; ++ int err; ++ ++ if (!priv->soc->mdio_read || !priv->soc->mdio_write) ++ return 0; ++ ++ spin_lock_init(&phy_ralink.lock); ++ priv->phy = &phy_ralink; ++ ++ mii_np = of_get_child_by_name(priv->device->of_node, "mdio-bus"); ++ if (!mii_np) { ++ dev_err(priv->device, "no %s child node found", "mdio-bus"); ++ return -ENODEV; ++ } ++ ++ if (!of_device_is_available(mii_np)) { ++ err = 0; ++ goto err_put_node; ++ } ++ ++ priv->mii_bus = mdiobus_alloc(); ++ if (priv->mii_bus == NULL) { ++ err = -ENOMEM; ++ goto err_put_node; ++ } ++ ++ priv->mii_bus->name = "mdio"; ++ priv->mii_bus->read = priv->soc->mdio_read; ++ priv->mii_bus->write = priv->soc->mdio_write; ++ priv->mii_bus->reset = fe_mdio_reset; ++ priv->mii_bus->irq = priv->mii_irq; ++ priv->mii_bus->priv = priv; ++ priv->mii_bus->parent = priv->device; ++ ++ snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s", mii_np->name); ++ err = of_mdiobus_register(priv->mii_bus, mii_np); ++ if (err) ++ goto err_free_bus; ++ ++ return 0; ++ ++err_free_bus: ++ kfree(priv->mii_bus); ++err_put_node: ++ of_node_put(mii_np); ++ priv->mii_bus = NULL; ++ return err; ++} ++ ++void fe_mdio_cleanup(struct fe_priv *priv) ++{ ++ if (!priv->mii_bus) ++ return; ++ ++ mdiobus_unregister(priv->mii_bus); ++ of_node_put(priv->mii_bus->dev.of_node); ++ kfree(priv->mii_bus); ++} +diff --git a/drivers/net/ethernet/ralink/mdio.h b/drivers/net/ethernet/ralink/mdio.h +new file mode 100644 +index 0000000..c3910a0 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/mdio.h +@@ -0,0 +1,29 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#ifndef _RALINK_MDIO_H__ ++#define _RALINK_MDIO_H__ ++ ++#ifdef CONFIG_NET_RALINK_MDIO ++extern int fe_mdio_init(struct fe_priv *priv); ++extern void fe_mdio_cleanup(struct fe_priv *priv); ++extern int fe_connect_phy_node(struct fe_priv *priv, struct device_node *phy_node); ++#else ++static inline int fe_mdio_init(struct fe_priv *priv) { return 0; } ++static inline void fe_mdio_cleanup(struct fe_priv *priv) {} ++#endif ++#endif +diff --git a/drivers/net/ethernet/ralink/mdio_rt2880.c b/drivers/net/ethernet/ralink/mdio_rt2880.c +new file mode 100644 +index 0000000..54dbc53 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/mdio_rt2880.c +@@ -0,0 +1,163 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ralink_soc_eth.h" ++#include "mdio_rt2880.h" ++ ++#define FE_MDIO_RETRY 1000 ++ ++static unsigned char *rt2880_speed_str(struct fe_priv *priv) ++{ ++ switch (priv->phy->speed[0]) { ++ case SPEED_1000: ++ return "1000"; ++ case SPEED_100: ++ return "100"; ++ case SPEED_10: ++ return "10"; ++ } ++ ++ return "?"; ++} ++ ++void rt2880_mdio_link_adjust(struct fe_priv *priv) ++{ ++ u32 mdio_cfg; ++ ++ if (!priv->link[0]) { ++ netif_carrier_off(priv->netdev); ++ netdev_info(priv->netdev, "link down\n"); ++ return; ++ } ++ ++ mdio_cfg = FE_MDIO_CFG_TX_CLK_SKEW_200 | ++ FE_MDIO_CFG_RX_CLK_SKEW_200 | ++ FE_MDIO_CFG_GP1_FRC_EN; ++ ++ if (priv->phy->duplex[0] == DUPLEX_FULL) ++ mdio_cfg |= FE_MDIO_CFG_GP1_DUPLEX; ++ ++ if (priv->phy->tx_fc) ++ mdio_cfg |= FE_MDIO_CFG_GP1_FC_TX; ++ ++ if (priv->phy->rx_fc) ++ mdio_cfg |= FE_MDIO_CFG_GP1_FC_RX; ++ ++ switch (priv->phy->speed[0]) { ++ case SPEED_10: ++ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_10; ++ break; ++ case SPEED_100: ++ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_100; ++ break; ++ case SPEED_1000: ++ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_1000; ++ break; ++ default: ++ BUG(); ++ } ++ ++ fe_w32(mdio_cfg, FE_MDIO_CFG); ++ ++ netif_carrier_on(priv->netdev); ++ netdev_info(priv->netdev, "link up (%sMbps/%s duplex)\n", ++ rt2880_speed_str(priv), ++ (DUPLEX_FULL == priv->phy->duplex[0]) ? "Full" : "Half"); ++} ++ ++static int rt2880_mdio_wait_ready(struct fe_priv *priv) ++{ ++ int retries; ++ ++ retries = FE_MDIO_RETRY; ++ while (1) { ++ u32 t; ++ ++ t = fe_r32(FE_MDIO_ACCESS); ++ if ((t & (0x1 << 31)) == 0) ++ return 0; ++ ++ if (retries-- == 0) ++ break; ++ ++ udelay(1); ++ } ++ ++ dev_err(priv->device, "MDIO operation timed out\n"); ++ return -ETIMEDOUT; ++} ++ ++int rt2880_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) ++{ ++ struct fe_priv *priv = bus->priv; ++ int err; ++ u32 t; ++ ++ err = rt2880_mdio_wait_ready(priv); ++ if (err) ++ return 0xffff; ++ ++ t = (phy_addr << 24) | (phy_reg << 16); ++ fe_w32(t, FE_MDIO_ACCESS); ++ t |= (1 << 31); ++ fe_w32(t, FE_MDIO_ACCESS); ++ ++ err = rt2880_mdio_wait_ready(priv); ++ if (err) ++ return 0xffff; ++ ++ pr_info("%s: addr=%04x, reg=%04x, value=%04x\n", __func__, ++ phy_addr, phy_reg, fe_r32(FE_MDIO_ACCESS) & 0xffff); ++ ++ return fe_r32(FE_MDIO_ACCESS) & 0xffff; ++} ++ ++int rt2880_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val) ++{ ++ struct fe_priv *priv = bus->priv; ++ int err; ++ u32 t; ++ ++ pr_info("%s: addr=%04x, reg=%04x, value=%04x\n", __func__, ++ phy_addr, phy_reg, fe_r32(FE_MDIO_ACCESS) & 0xffff); ++ ++ err = rt2880_mdio_wait_ready(priv); ++ if (err) ++ return err; ++ ++ t = (1 << 30) | (phy_addr << 24) | (phy_reg << 16) | val; ++ fe_w32(t, FE_MDIO_ACCESS); ++ t |= (1 << 31); ++ fe_w32(t, FE_MDIO_ACCESS); ++ ++ return rt2880_mdio_wait_ready(priv); ++} +diff --git a/drivers/net/ethernet/ralink/mdio_rt2880.h b/drivers/net/ethernet/ralink/mdio_rt2880.h +new file mode 100644 +index 0000000..c9ac0fe +--- /dev/null ++++ b/drivers/net/ethernet/ralink/mdio_rt2880.h +@@ -0,0 +1,25 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#ifndef _RALINK_MDIO_RT2880_H__ ++#define _RALINK_MDIO_RT2880_H__ ++ ++void rt2880_mdio_link_adjust(struct fe_priv *priv); ++int rt2880_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg); ++int rt2880_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val); ++ ++#endif +diff --git a/drivers/net/ethernet/ralink/ralink_soc_eth.c b/drivers/net/ethernet/ralink/ralink_soc_eth.c +new file mode 100644 +index 0000000..04e82eb +--- /dev/null ++++ b/drivers/net/ethernet/ralink/ralink_soc_eth.c +@@ -0,0 +1,759 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++#include "esw_rt3052.h" ++#include "mdio.h" ++ ++#define TX_TIMEOUT (20 * HZ / 100) ++#define MAX_RX_LENGTH 1536 ++ ++static const u32 fe_reg_table_default[FE_REG_COUNT] = { ++ [FE_REG_PDMA_GLO_CFG] = FE_PDMA_GLO_CFG, ++ [FE_REG_PDMA_RST_CFG] = FE_PDMA_RST_CFG, ++ [FE_REG_DLY_INT_CFG] = FE_DLY_INT_CFG, ++ [FE_REG_TX_BASE_PTR0] = FE_TX_BASE_PTR0, ++ [FE_REG_TX_MAX_CNT0] = FE_TX_MAX_CNT0, ++ [FE_REG_TX_CTX_IDX0] = FE_TX_CTX_IDX0, ++ [FE_REG_RX_BASE_PTR0] = FE_RX_BASE_PTR0, ++ [FE_REG_RX_MAX_CNT0] = FE_RX_MAX_CNT0, ++ [FE_REG_RX_CALC_IDX0] = FE_RX_CALC_IDX0, ++ [FE_REG_FE_INT_ENABLE] = FE_FE_INT_ENABLE, ++ [FE_REG_FE_INT_STATUS] = FE_FE_INT_STATUS, ++ [FE_REG_FE_DMA_VID_BASE] = FE_DMA_VID0, ++}; ++ ++static const u32 *fe_reg_table = fe_reg_table_default; ++ ++static void __iomem *fe_base = 0; ++ ++void fe_w32(u32 val, unsigned reg) ++{ ++ __raw_writel(val, fe_base + reg); ++} ++ ++u32 fe_r32(unsigned reg) ++{ ++ return __raw_readl(fe_base + reg); ++} ++ ++static inline void fe_reg_w32(u32 val, enum fe_reg reg) ++{ ++ fe_w32(val, fe_reg_table[reg]); ++} ++ ++static inline u32 fe_reg_r32(enum fe_reg reg) ++{ ++ return fe_r32(fe_reg_table[reg]); ++} ++ ++static inline void fe_int_disable(u32 mask) ++{ ++ fe_reg_w32(fe_reg_r32(FE_REG_FE_INT_ENABLE) & ~mask, ++ FE_REG_FE_INT_ENABLE); ++ /* flush write */ ++ fe_reg_r32(FE_REG_FE_INT_ENABLE); ++} ++ ++static inline void fe_int_enable(u32 mask) ++{ ++ fe_reg_w32(fe_reg_r32(FE_REG_FE_INT_ENABLE) | mask, ++ FE_REG_FE_INT_ENABLE); ++ /* flush write */ ++ fe_reg_r32(FE_REG_FE_INT_ENABLE); ++} ++ ++static inline void fe_hw_set_macaddr(struct fe_priv *priv, unsigned char *mac) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ fe_w32((mac[0] << 8) | mac[1], FE_GDMA1_MAC_ADRH); ++ fe_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], ++ FE_GDMA1_MAC_ADRL); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++} ++ ++static int fe_set_mac_address(struct net_device *dev, void *p) ++{ ++ int ret = eth_mac_addr(dev, p); ++ ++ if (!ret) { ++ struct fe_priv *priv = netdev_priv(dev); ++ ++ if (priv->soc->set_mac) ++ priv->soc->set_mac(priv, dev->dev_addr); ++ else ++ fe_hw_set_macaddr(priv, p); ++ } ++ ++ return ret; ++} ++ ++static struct sk_buff* fe_alloc_skb(struct fe_priv *priv) ++{ ++ struct sk_buff *skb; ++ ++ skb = netdev_alloc_skb(priv->netdev, MAX_RX_LENGTH + NET_IP_ALIGN); ++ if (!skb) ++ return NULL; ++ ++ skb_reserve(skb, NET_IP_ALIGN); ++ ++ return skb; ++} ++ ++static int fe_alloc_rx(struct fe_priv *priv) ++{ ++ int size = NUM_DMA_DESC * sizeof(struct fe_rx_dma); ++ int i; ++ ++ priv->rx_dma = dma_alloc_coherent(&priv->netdev->dev, size, ++ &priv->rx_phys, GFP_ATOMIC); ++ if (!priv->rx_dma) ++ return -ENOMEM; ++ ++ memset(priv->rx_dma, 0, size); ++ ++ for (i = 0; i < NUM_DMA_DESC; i++) { ++ priv->rx_skb[i] = fe_alloc_skb(priv); ++ if (!priv->rx_skb[i]) ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < NUM_DMA_DESC; i++) { ++ dma_addr_t dma_addr = dma_map_single(&priv->netdev->dev, ++ priv->rx_skb[i]->data, ++ MAX_RX_LENGTH, ++ DMA_FROM_DEVICE); ++ priv->rx_dma[i].rxd1 = (unsigned int) dma_addr; ++ ++ if (priv->soc->rx_dma) ++ priv->soc->rx_dma(priv, i, MAX_RX_LENGTH); ++ else ++ priv->rx_dma[i].rxd2 = RX_DMA_LSO; ++ } ++ wmb(); ++ ++ fe_reg_w32(priv->rx_phys, FE_REG_RX_BASE_PTR0); ++ fe_reg_w32(NUM_DMA_DESC, FE_REG_RX_MAX_CNT0); ++ fe_reg_w32((NUM_DMA_DESC - 1), FE_REG_RX_CALC_IDX0); ++ fe_reg_w32(FE_PST_DRX_IDX0, FE_REG_PDMA_RST_CFG); ++ ++ return 0; ++} ++ ++static int fe_alloc_tx(struct fe_priv *priv) ++{ ++ int size = NUM_DMA_DESC * sizeof(struct fe_tx_dma); ++ int i; ++ ++ priv->tx_free_idx = 0; ++ ++ priv->tx_dma = dma_alloc_coherent(&priv->netdev->dev, size, ++ &priv->tx_phys, GFP_ATOMIC); ++ if (!priv->tx_dma) ++ return -ENOMEM; ++ ++ memset(priv->tx_dma, 0, size); ++ ++ for (i = 0; i < NUM_DMA_DESC; i++) { ++ if (priv->soc->tx_dma) { ++ priv->soc->tx_dma(priv, i, 0); ++ continue; ++ } ++ ++ priv->tx_dma[i].txd2 = TX_DMA_LSO | TX_DMA_DONE; ++ priv->tx_dma[i].txd4 = TX_DMA_QN(3) | TX_DMA_PN(1); ++ } ++ ++ fe_reg_w32(priv->tx_phys, FE_REG_TX_BASE_PTR0); ++ fe_reg_w32(NUM_DMA_DESC, FE_REG_TX_MAX_CNT0); ++ fe_reg_w32(0, FE_REG_TX_CTX_IDX0); ++ fe_reg_w32(FE_PST_DTX_IDX0, FE_REG_PDMA_RST_CFG); ++ ++ return 0; ++} ++ ++static void fe_free_dma(struct fe_priv *priv) ++{ ++ int i; ++ ++ for (i = 0; i < NUM_DMA_DESC; i++) { ++ if (priv->rx_skb[i]) { ++ dma_unmap_single(&priv->netdev->dev, priv->rx_dma[i].rxd1, ++ MAX_RX_LENGTH, DMA_FROM_DEVICE); ++ dev_kfree_skb_any(priv->rx_skb[i]); ++ priv->rx_skb[i] = NULL; ++ } ++ ++ if (priv->tx_skb[i]) { ++ dev_kfree_skb_any(priv->tx_skb[i]); ++ priv->tx_skb[i] = NULL; ++ } ++ } ++ ++ if (priv->rx_dma) { ++ int size = NUM_DMA_DESC * sizeof(struct fe_rx_dma); ++ dma_free_coherent(&priv->netdev->dev, size, priv->rx_dma, ++ priv->rx_phys); ++ } ++ ++ if (priv->tx_dma) { ++ int size = NUM_DMA_DESC * sizeof(struct fe_tx_dma); ++ dma_free_coherent(&priv->netdev->dev, size, priv->tx_dma, ++ priv->tx_phys); ++ } ++ ++ netdev_reset_queue(priv->netdev); ++} ++ ++static int fe_start_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ dma_addr_t mapped_addr; ++ u32 tx_next; ++ u32 tx; ++ ++ if (priv->soc->min_pkt_len) { ++ if (skb->len < priv->soc->min_pkt_len) { ++ if (skb_padto(skb, priv->soc->min_pkt_len)) { ++ printk(KERN_ERR ++ "fe_eth: skb_padto failed\n"); ++ kfree_skb(skb); ++ return 0; ++ } ++ skb_put(skb, priv->soc->min_pkt_len - skb->len); ++ } ++ } ++ ++ dev->trans_start = jiffies; ++ mapped_addr = dma_map_single(&priv->netdev->dev, skb->data, ++ skb->len, DMA_TO_DEVICE); ++ ++ spin_lock(&priv->page_lock); ++ ++ tx = fe_reg_r32(FE_REG_TX_CTX_IDX0); ++ tx_next = (tx + 1) % NUM_DMA_DESC; ++ ++ if ((priv->tx_skb[tx]) || (priv->tx_skb[tx_next]) || ++ !(priv->tx_dma[tx].txd2 & TX_DMA_DONE) || ++ !(priv->tx_dma[tx_next].txd2 & TX_DMA_DONE)) ++ { ++ spin_unlock(&priv->page_lock); ++ dev->stats.tx_dropped++; ++ kfree_skb(skb); ++ ++ return NETDEV_TX_OK; ++ } ++ ++ priv->tx_skb[tx] = skb; ++ priv->tx_dma[tx].txd1 = (unsigned int) mapped_addr; ++ wmb(); ++ if (priv->soc->tx_dma) ++ priv->soc->tx_dma(priv, tx, skb->len); ++ else ++ priv->tx_dma[tx].txd2 = TX_DMA_LSO | TX_DMA_PLEN0(skb->len); ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) ++ priv->tx_dma[tx].txd4 |= TX_DMA_CHKSUM; ++ else ++ priv->tx_dma[tx].txd4 &= ~TX_DMA_CHKSUM; ++ ++ if (fe_reg_table[FE_REG_FE_DMA_VID_BASE] && vlan_tx_tag_present(skb)) ++ priv->tx_dma[tx].txd4 |= 0x80 | (vlan_tx_tag_get(skb) >> 13) << 4 | (vlan_tx_tag_get(skb) & 0xF); ++ else ++ priv->tx_dma[tx].txd4 &= ~0x80; ++ ++ dev->stats.tx_packets++; ++ dev->stats.tx_bytes += skb->len; ++ ++ fe_reg_w32(tx_next, FE_REG_TX_CTX_IDX0); ++ netdev_sent_queue(dev, skb->len); ++ ++ spin_unlock(&priv->page_lock); ++ ++ return NETDEV_TX_OK; ++} ++ ++static int fe_poll_rx(struct napi_struct *napi, int budget) ++{ ++ struct fe_priv *priv = container_of(napi, struct fe_priv, rx_napi); ++ int idx = fe_reg_r32(FE_REG_RX_CALC_IDX0); ++ unsigned long flags; ++ int complete = 0; ++ int rx = 0; ++ ++ while ((rx < budget) && !complete) { ++ ++ idx = (idx + 1) % NUM_DMA_DESC; ++ ++ if (priv->rx_dma[idx].rxd2 & RX_DMA_DONE) { ++ struct sk_buff *new_skb = fe_alloc_skb(priv); ++ ++ if (new_skb) { ++ int pktlen = RX_DMA_PLEN0(priv->rx_dma[idx].rxd2); ++ dma_addr_t dma_addr; ++ ++ dma_unmap_single(&priv->netdev->dev, priv->rx_dma[idx].rxd1, ++ MAX_RX_LENGTH, DMA_FROM_DEVICE); ++ ++ skb_put(priv->rx_skb[idx], pktlen); ++ priv->rx_skb[idx]->dev = priv->netdev; ++ priv->rx_skb[idx]->protocol = eth_type_trans(priv->rx_skb[idx], priv->netdev); ++ if (priv->rx_dma[idx].rxd4 & priv->soc->checksum_bit) ++ priv->rx_skb[idx]->ip_summed = CHECKSUM_UNNECESSARY; ++ else ++ priv->rx_skb[idx]->ip_summed = CHECKSUM_NONE; ++ priv->netdev->stats.rx_packets++; ++ priv->netdev->stats.rx_bytes += pktlen; ++ netif_rx(priv->rx_skb[idx]); ++ ++ priv->rx_skb[idx] = new_skb; ++ ++ dma_addr = dma_map_single(&priv->netdev->dev, ++ new_skb->data, ++ MAX_RX_LENGTH, ++ DMA_FROM_DEVICE); ++ priv->rx_dma[idx].rxd1 = (unsigned int) dma_addr; ++ wmb(); ++ } else { ++ priv->netdev->stats.rx_dropped++; ++ } ++ ++ if (priv->soc->rx_dma) ++ priv->soc->rx_dma(priv, idx, MAX_RX_LENGTH); ++ else ++ priv->rx_dma[idx].rxd2 = RX_DMA_LSO; ++ fe_reg_w32(idx, FE_REG_RX_CALC_IDX0); ++ rx++; ++ } else { ++ complete = 1; ++ } ++ } ++ ++ if (complete || !rx) { ++ napi_complete(&priv->rx_napi); ++ spin_lock_irqsave(&priv->page_lock, flags); ++ fe_int_enable(priv->soc->rx_dly_int); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++ } ++ ++ return rx; ++} ++ ++static int fe_poll_tx(struct napi_struct *napi, int budget) ++{ ++ struct fe_priv *priv = container_of(napi, struct fe_priv, tx_napi); ++ unsigned int bytes_compl = 0; ++ unsigned int pkts_compl = 0; ++ struct netdev_queue *txq; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ while (pkts_compl < budget) { ++ struct fe_tx_dma *txd; ++ ++ txd = &priv->tx_dma[priv->tx_free_idx]; ++ ++ if (!(txd->txd2 & TX_DMA_DONE) || !(priv->tx_skb[priv->tx_free_idx])) ++ break; ++ ++ bytes_compl += priv->tx_skb[priv->tx_free_idx]->len; ++ pkts_compl++; ++ ++ dev_kfree_skb_irq(priv->tx_skb[priv->tx_free_idx]); ++ priv->tx_skb[priv->tx_free_idx] = NULL; ++ priv->tx_free_idx++; ++ if (priv->tx_free_idx >= NUM_DMA_DESC) ++ priv->tx_free_idx = 0; ++ } ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++ ++ txq = netdev_get_tx_queue(priv->netdev, 0); ++ if (netif_tx_queue_stopped(txq)) ++ netif_tx_start_queue(txq); ++ ++ napi_complete(napi); ++ netdev_completed_queue(priv->netdev, pkts_compl, bytes_compl); ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ fe_int_enable(priv->soc->tx_dly_int); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++ ++ return pkts_compl; ++} ++ ++static void fe_tx_timeout(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ ++ priv->netdev->stats.tx_errors++; ++ netdev_err(dev, "transmit timed out, waking up the queue\n"); ++ netif_wake_queue(dev); ++} ++ ++static irqreturn_t fe_handle_irq(int irq, void *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ unsigned int status; ++ unsigned int mask; ++ ++ status = fe_reg_r32(FE_REG_FE_INT_STATUS); ++ mask = fe_reg_r32(FE_REG_FE_INT_ENABLE); ++ ++ if (!(status & mask)) ++ return IRQ_NONE; ++ ++ if (status & priv->soc->rx_dly_int) { ++ fe_int_disable(priv->soc->rx_dly_int); ++ napi_schedule(&priv->rx_napi); ++ } ++ ++ if (status & priv->soc->tx_dly_int) { ++ fe_int_disable(priv->soc->tx_dly_int); ++ napi_schedule(&priv->tx_napi); ++ } ++ ++ fe_reg_w32(status, FE_REG_FE_INT_STATUS); ++ ++ return IRQ_HANDLED; ++} ++ ++static int fe_hw_init(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ int err, i; ++ ++ err = devm_request_irq(priv->device, dev->irq, fe_handle_irq, 0, ++ dev_name(priv->device), dev); ++ if (err) ++ return err; ++ ++ err = fe_alloc_rx(priv); ++ if (!err) ++ err = fe_alloc_tx(priv); ++ if (err) ++ return err; ++ ++ if (priv->soc->set_mac) ++ priv->soc->set_mac(priv, dev->dev_addr); ++ else ++ fe_hw_set_macaddr(priv, dev->dev_addr); ++ ++ fe_reg_w32(FE_DELAY_INIT, FE_REG_DLY_INT_CFG); ++ ++ fe_int_disable(priv->soc->tx_dly_int | priv->soc->rx_dly_int); ++ ++ ++ if (fe_reg_table[FE_REG_FE_DMA_VID_BASE]) ++ for (i = 0; i < 16; i += 2) ++ fe_w32((i + 1) << 16 | i, fe_reg_table[FE_REG_FE_DMA_VID_BASE] + (i * 4)); ++ ++ if (priv->soc->fwd_config) { ++ priv->soc->fwd_config(priv); ++ } else { ++ unsigned long sysclk = priv->sysclk; ++ ++ if (!sysclk) { ++ netdev_err(dev, "unable to get clock\n"); ++ return -EINVAL; ++ } ++ ++ sysclk /= FE_US_CYC_CNT_DIVISOR; ++ sysclk <<= FE_US_CYC_CNT_SHIFT; ++ ++ fe_w32((fe_r32(FE_FE_GLO_CFG) & ++ ~(FE_US_CYC_CNT_MASK << FE_US_CYC_CNT_SHIFT)) | priv->sysclk, ++ FE_FE_GLO_CFG); ++ ++ fe_w32(fe_r32(FE_GDMA1_FWD_CFG) & ~0xffff, FE_GDMA1_FWD_CFG); ++ fe_w32(fe_r32(FE_GDMA1_FWD_CFG) | (FE_GDM1_ICS_EN | FE_GDM1_TCS_EN | FE_GDM1_UCS_EN), ++ FE_GDMA1_FWD_CFG); ++ fe_w32(fe_r32(FE_CDMA_CSG_CFG) | (FE_ICS_GEN_EN | FE_TCS_GEN_EN | FE_UCS_GEN_EN), ++ FE_CDMA_CSG_CFG); ++ fe_w32(FE_PSE_FQFC_CFG_INIT, FE_PSE_FQ_CFG); ++ } ++ ++ fe_w32(1, FE_FE_RST_GL); ++ fe_w32(0, FE_FE_RST_GL); ++ ++ return 0; ++} ++ ++static int fe_open(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ unsigned long flags; ++ u32 val; ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ napi_enable(&priv->rx_napi); ++ napi_enable(&priv->tx_napi); ++ ++ val = FE_TX_WB_DDONE | FE_RX_DMA_EN | FE_TX_DMA_EN; ++ val |= priv->soc->pdma_glo_cfg; ++ fe_reg_w32(val, FE_REG_PDMA_GLO_CFG); ++ ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++ ++ if (priv->phy) ++ priv->phy->start(priv); ++ ++ if (priv->soc->has_carrier && priv->soc->has_carrier(priv)) ++ netif_carrier_on(dev); ++ ++ netif_start_queue(dev); ++ fe_int_enable(priv->soc->tx_dly_int | priv->soc->rx_dly_int); ++ ++ return 0; ++} ++ ++static int fe_stop(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ unsigned long flags; ++ ++ fe_int_disable(priv->soc->tx_dly_int | priv->soc->rx_dly_int); ++ ++ netif_stop_queue(dev); ++ ++ if (priv->phy) ++ priv->phy->stop(priv); ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ napi_disable(&priv->rx_napi); ++ napi_disable(&priv->tx_napi); ++ ++ fe_reg_w32(fe_reg_r32(FE_REG_PDMA_GLO_CFG) & ++ ~(FE_TX_WB_DDONE | FE_RX_DMA_EN | FE_TX_DMA_EN), ++ FE_REG_PDMA_GLO_CFG); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++ ++ return 0; ++} ++ ++static int __init fe_init(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ struct device_node *port; ++ int err; ++ ++ BUG_ON(!priv->soc->reset_fe); ++ priv->soc->reset_fe(); ++ ++ if (priv->soc->switch_init) ++ priv->soc->switch_init(priv); ++ ++ net_srandom(jiffies); ++ memcpy(dev->dev_addr, priv->soc->mac, ETH_ALEN); ++ of_get_mac_address_mtd(priv->device->of_node, dev->dev_addr); ++ ++ err = fe_mdio_init(priv); ++ if (err) ++ return err; ++ ++ if (priv->phy) { ++ err = priv->phy->connect(priv); ++ if (err) ++ goto err_mdio_cleanup; ++ } ++ ++ if (priv->soc->port_init) ++ for_each_child_of_node(priv->device->of_node, port) ++ if (of_device_is_compatible(port, "ralink,eth-port")) ++ priv->soc->port_init(priv, port); ++ ++ err = fe_hw_init(dev); ++ if (err) ++ goto err_phy_disconnect; ++ ++ return 0; ++ ++err_phy_disconnect: ++ if (priv->phy) ++ priv->phy->disconnect(priv); ++err_mdio_cleanup: ++ fe_mdio_cleanup(priv); ++ ++ return err; ++} ++ ++static void fe_uninit(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ ++ if (priv->phy) ++ priv->phy->disconnect(priv); ++ fe_mdio_cleanup(priv); ++ ++ fe_reg_w32(0, FE_REG_FE_INT_ENABLE); ++ free_irq(dev->irq, dev); ++ ++ fe_free_dma(priv); ++} ++ ++static const struct net_device_ops fe_netdev_ops = { ++ .ndo_init = fe_init, ++ .ndo_uninit = fe_uninit, ++ .ndo_open = fe_open, ++ .ndo_stop = fe_stop, ++ .ndo_start_xmit = fe_start_xmit, ++ .ndo_tx_timeout = fe_tx_timeout, ++ .ndo_set_mac_address = fe_set_mac_address, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_validate_addr = eth_validate_addr, ++}; ++ ++static int fe_probe(struct platform_device *pdev) ++{ ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ const struct of_device_id *match; ++ struct fe_soc_data *soc = NULL; ++ struct net_device *netdev; ++ struct fe_priv *priv; ++ struct clk *sysclk; ++ int err; ++ ++ match = of_match_device(of_fe_match, &pdev->dev); ++ soc = (struct fe_soc_data *) match->data; ++ if (soc->reg_table) ++ fe_reg_table = soc->reg_table; ++ ++ fe_base = devm_request_and_ioremap(&pdev->dev, res); ++ if (!fe_base) ++ return -ENOMEM; ++ ++ netdev = alloc_etherdev(sizeof(struct fe_priv)); ++ if (!netdev) { ++ dev_err(&pdev->dev, "alloc_etherdev failed\n"); ++ return -ENOMEM; ++ } ++ ++ strcpy(netdev->name, "eth%d"); ++ netdev->netdev_ops = &fe_netdev_ops; ++ netdev->base_addr = (unsigned long) fe_base; ++ netdev->watchdog_timeo = TX_TIMEOUT; ++ netdev->features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM; ++ if (fe_reg_table[FE_REG_FE_DMA_VID_BASE]) ++ netdev->features |= NETIF_F_HW_VLAN_TX; ++ ++ netdev->irq = platform_get_irq(pdev, 0); ++ if (netdev->irq < 0) { ++ dev_err(&pdev->dev, "no IRQ resource found\n"); ++ kfree(netdev); ++ return -ENXIO; ++ } ++ ++ priv = netdev_priv(netdev); ++ memset(priv, 0, sizeof(struct fe_priv)); ++ spin_lock_init(&priv->page_lock); ++ ++ sysclk = devm_clk_get(&pdev->dev, NULL); ++ if (!IS_ERR(sysclk)) ++ priv->sysclk = clk_get_rate(sysclk); ++ ++ priv->netdev = netdev; ++ priv->device = &pdev->dev; ++ priv->soc = soc; ++ ++ err = register_netdev(netdev); ++ if (err) { ++ dev_err(&pdev->dev, "error bringing up device\n"); ++ kfree(netdev); ++ return err; ++ } ++ ++ netif_napi_add(netdev, &priv->rx_napi, fe_poll_rx, 32); ++ netif_napi_add(netdev, &priv->tx_napi, fe_poll_tx, 8); ++ ++ platform_set_drvdata(pdev, netdev); ++ ++ netdev_info(netdev, "done loading\n"); ++ ++ return 0; ++} ++ ++static int fe_remove(struct platform_device *pdev) ++{ ++ struct net_device *dev = platform_get_drvdata(pdev); ++ struct fe_priv *priv = netdev_priv(dev); ++ ++ netif_stop_queue(dev); ++ netif_napi_del(&priv->rx_napi); ++ netif_napi_del(&priv->tx_napi); ++ ++ unregister_netdev(dev); ++ free_netdev(dev); ++ ++ return 0; ++} ++ ++static struct platform_driver fe_driver = { ++ .probe = fe_probe, ++ .remove = fe_remove, ++ .driver = { ++ .name = "ralink_soc_eth", ++ .owner = THIS_MODULE, ++ .of_match_table = of_fe_match, ++ }, ++}; ++ ++static int __init init_rtfe(void) ++{ ++ int ret; ++ ++ ret = rtesw_init(); ++ if (ret) ++ return ret; ++ ++ ret = platform_driver_register(&fe_driver); ++ if (ret) ++ rtesw_exit(); ++ ++ return ret; ++} ++ ++static void __exit exit_rtfe(void) ++{ ++ platform_driver_unregister(&fe_driver); ++ rtesw_exit(); ++} ++ ++module_init(init_rtfe); ++module_exit(exit_rtfe); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("John Crispin "); ++MODULE_DESCRIPTION("Ethernet driver for Ralink SoC"); +diff --git a/drivers/net/ethernet/ralink/ralink_soc_eth.h b/drivers/net/ethernet/ralink/ralink_soc_eth.h +new file mode 100644 +index 0000000..0c769ef +--- /dev/null ++++ b/drivers/net/ethernet/ralink/ralink_soc_eth.h +@@ -0,0 +1,372 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * based on Ralink SDK3.3 ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#ifndef FE_ETH_H ++#define FE_ETH_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++enum fe_reg { ++ FE_REG_PDMA_GLO_CFG = 0, ++ FE_REG_PDMA_RST_CFG, ++ FE_REG_DLY_INT_CFG, ++ FE_REG_TX_BASE_PTR0, ++ FE_REG_TX_MAX_CNT0, ++ FE_REG_TX_CTX_IDX0, ++ FE_REG_RX_BASE_PTR0, ++ FE_REG_RX_MAX_CNT0, ++ FE_REG_RX_CALC_IDX0, ++ FE_REG_FE_INT_ENABLE, ++ FE_REG_FE_INT_STATUS, ++ FE_REG_FE_DMA_VID_BASE, ++ FE_REG_COUNT ++}; ++ ++#define NUM_DMA_DESC 0x100 ++ ++#define FE_DELAY_EN_INT 0x80 ++#define FE_DELAY_MAX_INT 0x04 ++#define FE_DELAY_MAX_TOUT 0x04 ++#define FE_DELAY_CHAN (((FE_DELAY_EN_INT | FE_DELAY_MAX_INT) << 8) | FE_DELAY_MAX_TOUT) ++#define FE_DELAY_INIT ((FE_DELAY_CHAN << 16) | FE_DELAY_CHAN) ++#define FE_PSE_FQFC_CFG_INIT 0x80504000 ++ ++/* interrupt bits */ ++#define FE_CNT_PPE_AF BIT(31) ++#define FE_CNT_GDM_AF BIT(29) ++#define FE_PSE_P2_FC BIT(26) ++#define FE_PSE_BUF_DROP BIT(24) ++#define FE_GDM_OTHER_DROP BIT(23) ++#define FE_PSE_P1_FC BIT(22) ++#define FE_PSE_P0_FC BIT(21) ++#define FE_PSE_FQ_EMPTY BIT(20) ++#define FE_GE1_STA_CHG BIT(18) ++#define FE_TX_COHERENT BIT(17) ++#define FE_RX_COHERENT BIT(16) ++#define FE_TX_DONE_INT3 BIT(11) ++#define FE_TX_DONE_INT2 BIT(10) ++#define FE_TX_DONE_INT1 BIT(9) ++#define FE_TX_DONE_INT0 BIT(8) ++#define FE_RX_DONE_INT0 BIT(2) ++#define FE_TX_DLY_INT BIT(1) ++#define FE_RX_DLY_INT BIT(0) ++ ++#define RT5350_RX_DLY_INT BIT(30) ++#define RT5350_TX_DLY_INT BIT(28) ++ ++/* registers */ ++#define FE_FE_OFFSET 0x0000 ++#define FE_GDMA_OFFSET 0x0020 ++#define FE_PSE_OFFSET 0x0040 ++#define FE_GDMA2_OFFSET 0x0060 ++#define FE_CDMA_OFFSET 0x0080 ++#define FE_DMA_VID0 0x00a8 ++#define FE_PDMA_OFFSET 0x0100 ++#define FE_PPE_OFFSET 0x0200 ++#define FE_CMTABLE_OFFSET 0x0400 ++#define FE_POLICYTABLE_OFFSET 0x1000 ++ ++#define RT5350_PDMA_OFFSET 0x0800 ++#define RT5350_SDM_OFFSET 0x0c00 ++ ++#define FE_MDIO_ACCESS (FE_FE_OFFSET + 0x00) ++#define FE_MDIO_CFG (FE_FE_OFFSET + 0x04) ++#define FE_FE_GLO_CFG (FE_FE_OFFSET + 0x08) ++#define FE_FE_RST_GL (FE_FE_OFFSET + 0x0C) ++#define FE_FE_INT_STATUS (FE_FE_OFFSET + 0x10) ++#define FE_FE_INT_ENABLE (FE_FE_OFFSET + 0x14) ++#define FE_MDIO_CFG2 (FE_FE_OFFSET + 0x18) ++#define FE_FOC_TS_T (FE_FE_OFFSET + 0x1C) ++ ++#define FE_GDMA1_FWD_CFG (FE_GDMA_OFFSET + 0x00) ++#define FE_GDMA1_SCH_CFG (FE_GDMA_OFFSET + 0x04) ++#define FE_GDMA1_SHPR_CFG (FE_GDMA_OFFSET + 0x08) ++#define FE_GDMA1_MAC_ADRL (FE_GDMA_OFFSET + 0x0C) ++#define FE_GDMA1_MAC_ADRH (FE_GDMA_OFFSET + 0x10) ++ ++#define FE_GDMA2_FWD_CFG (FE_GDMA2_OFFSET + 0x00) ++#define FE_GDMA2_SCH_CFG (FE_GDMA2_OFFSET + 0x04) ++#define FE_GDMA2_SHPR_CFG (FE_GDMA2_OFFSET + 0x08) ++#define FE_GDMA2_MAC_ADRL (FE_GDMA2_OFFSET + 0x0C) ++#define FE_GDMA2_MAC_ADRH (FE_GDMA2_OFFSET + 0x10) ++ ++#define FE_PSE_FQ_CFG (FE_PSE_OFFSET + 0x00) ++#define FE_CDMA_FC_CFG (FE_PSE_OFFSET + 0x04) ++#define FE_GDMA1_FC_CFG (FE_PSE_OFFSET + 0x08) ++#define FE_GDMA2_FC_CFG (FE_PSE_OFFSET + 0x0C) ++ ++#define FE_CDMA_CSG_CFG (FE_CDMA_OFFSET + 0x00) ++#define FE_CDMA_SCH_CFG (FE_CDMA_OFFSET + 0x04) ++ ++#define MT7620A_GDMA_OFFSET 0x0600 ++#define MT7620A_GDMA1_FWD_CFG (MT7620A_GDMA_OFFSET + 0x00) ++#define MT7620A_FE_GDMA1_SCH_CFG (MT7620A_GDMA_OFFSET + 0x04) ++#define MT7620A_FE_GDMA1_SHPR_CFG (MT7620A_GDMA_OFFSET + 0x08) ++#define MT7620A_FE_GDMA1_MAC_ADRL (MT7620A_GDMA_OFFSET + 0x0C) ++#define MT7620A_FE_GDMA1_MAC_ADRH (MT7620A_GDMA_OFFSET + 0x10) ++ ++#define RT5350_TX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x00) ++#define RT5350_TX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x04) ++#define RT5350_TX_CTX_IDX0 (RT5350_PDMA_OFFSET + 0x08) ++#define RT5350_TX_DTX_IDX0 (RT5350_PDMA_OFFSET + 0x0C) ++#define RT5350_TX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x10) ++#define RT5350_TX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x14) ++#define RT5350_TX_CTX_IDX1 (RT5350_PDMA_OFFSET + 0x18) ++#define RT5350_TX_DTX_IDX1 (RT5350_PDMA_OFFSET + 0x1C) ++#define RT5350_TX_BASE_PTR2 (RT5350_PDMA_OFFSET + 0x20) ++#define RT5350_TX_MAX_CNT2 (RT5350_PDMA_OFFSET + 0x24) ++#define RT5350_TX_CTX_IDX2 (RT5350_PDMA_OFFSET + 0x28) ++#define RT5350_TX_DTX_IDX2 (RT5350_PDMA_OFFSET + 0x2C) ++#define RT5350_TX_BASE_PTR3 (RT5350_PDMA_OFFSET + 0x30) ++#define RT5350_TX_MAX_CNT3 (RT5350_PDMA_OFFSET + 0x34) ++#define RT5350_TX_CTX_IDX3 (RT5350_PDMA_OFFSET + 0x38) ++#define RT5350_TX_DTX_IDX3 (RT5350_PDMA_OFFSET + 0x3C) ++#define RT5350_RX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x100) ++#define RT5350_RX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x104) ++#define RT5350_RX_CALC_IDX0 (RT5350_PDMA_OFFSET + 0x108) ++#define RT5350_RX_DRX_IDX0 (RT5350_PDMA_OFFSET + 0x10C) ++#define RT5350_RX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x110) ++#define RT5350_RX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x114) ++#define RT5350_RX_CALC_IDX1 (RT5350_PDMA_OFFSET + 0x118) ++#define RT5350_RX_DRX_IDX1 (RT5350_PDMA_OFFSET + 0x11C) ++#define RT5350_PDMA_GLO_CFG (RT5350_PDMA_OFFSET + 0x204) ++#define RT5350_PDMA_RST_CFG (RT5350_PDMA_OFFSET + 0x208) ++#define RT5350_DLY_INT_CFG (RT5350_PDMA_OFFSET + 0x20c) ++#define RT5350_FE_INT_STATUS (RT5350_PDMA_OFFSET + 0x220) ++#define RT5350_FE_INT_ENABLE (RT5350_PDMA_OFFSET + 0x228) ++#define RT5350_PDMA_SCH_CFG (RT5350_PDMA_OFFSET + 0x280) ++ ++#define FE_PDMA_GLO_CFG (FE_PDMA_OFFSET + 0x00) ++#define FE_PDMA_RST_CFG (FE_PDMA_OFFSET + 0x04) ++#define FE_PDMA_SCH_CFG (FE_PDMA_OFFSET + 0x08) ++#define FE_DLY_INT_CFG (FE_PDMA_OFFSET + 0x0C) ++#define FE_TX_BASE_PTR0 (FE_PDMA_OFFSET + 0x10) ++#define FE_TX_MAX_CNT0 (FE_PDMA_OFFSET + 0x14) ++#define FE_TX_CTX_IDX0 (FE_PDMA_OFFSET + 0x18) ++#define FE_TX_DTX_IDX0 (FE_PDMA_OFFSET + 0x1C) ++#define FE_TX_BASE_PTR1 (FE_PDMA_OFFSET + 0x20) ++#define FE_TX_MAX_CNT1 (FE_PDMA_OFFSET + 0x24) ++#define FE_TX_CTX_IDX1 (FE_PDMA_OFFSET + 0x28) ++#define FE_TX_DTX_IDX1 (FE_PDMA_OFFSET + 0x2C) ++#define FE_RX_BASE_PTR0 (FE_PDMA_OFFSET + 0x30) ++#define FE_RX_MAX_CNT0 (FE_PDMA_OFFSET + 0x34) ++#define FE_RX_CALC_IDX0 (FE_PDMA_OFFSET + 0x38) ++#define FE_RX_DRX_IDX0 (FE_PDMA_OFFSET + 0x3C) ++#define FE_TX_BASE_PTR2 (FE_PDMA_OFFSET + 0x40) ++#define FE_TX_MAX_CNT2 (FE_PDMA_OFFSET + 0x44) ++#define FE_TX_CTX_IDX2 (FE_PDMA_OFFSET + 0x48) ++#define FE_TX_DTX_IDX2 (FE_PDMA_OFFSET + 0x4C) ++#define FE_TX_BASE_PTR3 (FE_PDMA_OFFSET + 0x50) ++#define FE_TX_MAX_CNT3 (FE_PDMA_OFFSET + 0x54) ++#define FE_TX_CTX_IDX3 (FE_PDMA_OFFSET + 0x58) ++#define FE_TX_DTX_IDX3 (FE_PDMA_OFFSET + 0x5C) ++#define FE_RX_BASE_PTR1 (FE_PDMA_OFFSET + 0x60) ++#define FE_RX_MAX_CNT1 (FE_PDMA_OFFSET + 0x64) ++#define FE_RX_CALC_IDX1 (FE_PDMA_OFFSET + 0x68) ++#define FE_RX_DRX_IDX1 (FE_PDMA_OFFSET + 0x6C) ++ ++#define RT5350_SDM_CFG (RT5350_SDM_OFFSET + 0x00) //Switch DMA configuration ++#define RT5350_SDM_RRING (RT5350_SDM_OFFSET + 0x04) //Switch DMA Rx Ring ++#define RT5350_SDM_TRING (RT5350_SDM_OFFSET + 0x08) //Switch DMA Tx Ring ++#define RT5350_SDM_MAC_ADRL (RT5350_SDM_OFFSET + 0x0C) //Switch MAC address LSB ++#define RT5350_SDM_MAC_ADRH (RT5350_SDM_OFFSET + 0x10) //Switch MAC Address MSB ++#define RT5350_SDM_TPCNT (RT5350_SDM_OFFSET + 0x100) //Switch DMA Tx packet count ++#define RT5350_SDM_TBCNT (RT5350_SDM_OFFSET + 0x104) //Switch DMA Tx byte count ++#define RT5350_SDM_RPCNT (RT5350_SDM_OFFSET + 0x108) //Switch DMA rx packet count ++#define RT5350_SDM_RBCNT (RT5350_SDM_OFFSET + 0x10C) //Switch DMA rx byte count ++#define RT5350_SDM_CS_ERR (RT5350_SDM_OFFSET + 0x110) //Switch DMA rx checksum error count ++ ++#define RT5350_SDM_ICS_EN BIT(16) ++#define RT5350_SDM_TCS_EN BIT(17) ++#define RT5350_SDM_UCS_EN BIT(18) ++ ++ ++/* MDIO_CFG register bits */ ++#define FE_MDIO_CFG_AUTO_POLL_EN BIT(29) ++#define FE_MDIO_CFG_GP1_BP_EN BIT(16) ++#define FE_MDIO_CFG_GP1_FRC_EN BIT(15) ++#define FE_MDIO_CFG_GP1_SPEED_10 (0 << 13) ++#define FE_MDIO_CFG_GP1_SPEED_100 (1 << 13) ++#define FE_MDIO_CFG_GP1_SPEED_1000 (2 << 13) ++#define FE_MDIO_CFG_GP1_DUPLEX BIT(12) ++#define FE_MDIO_CFG_GP1_FC_TX BIT(11) ++#define FE_MDIO_CFG_GP1_FC_RX BIT(10) ++#define FE_MDIO_CFG_GP1_LNK_DWN BIT(9) ++#define FE_MDIO_CFG_GP1_AN_FAIL BIT(8) ++#define FE_MDIO_CFG_MDC_CLK_DIV_1 (0 << 6) ++#define FE_MDIO_CFG_MDC_CLK_DIV_2 (1 << 6) ++#define FE_MDIO_CFG_MDC_CLK_DIV_4 (2 << 6) ++#define FE_MDIO_CFG_MDC_CLK_DIV_8 (3 << 6) ++#define FE_MDIO_CFG_TURBO_MII_FREQ BIT(5) ++#define FE_MDIO_CFG_TURBO_MII_MODE BIT(4) ++#define FE_MDIO_CFG_RX_CLK_SKEW_0 (0 << 2) ++#define FE_MDIO_CFG_RX_CLK_SKEW_200 (1 << 2) ++#define FE_MDIO_CFG_RX_CLK_SKEW_400 (2 << 2) ++#define FE_MDIO_CFG_RX_CLK_SKEW_INV (3 << 2) ++#define FE_MDIO_CFG_TX_CLK_SKEW_0 0 ++#define FE_MDIO_CFG_TX_CLK_SKEW_200 1 ++#define FE_MDIO_CFG_TX_CLK_SKEW_400 2 ++#define FE_MDIO_CFG_TX_CLK_SKEW_INV 3 ++ ++/* uni-cast port */ ++#define FE_GDM1_ICS_EN BIT(22) ++#define FE_GDM1_TCS_EN BIT(21) ++#define FE_GDM1_UCS_EN BIT(20) ++#define FE_GDM1_JMB_EN BIT(19) ++#define FE_GDM1_STRPCRC BIT(16) ++#define FE_GDM1_UFRC_P_CPU (0 << 12) ++#define FE_GDM1_UFRC_P_GDMA1 (1 << 12) ++#define FE_GDM1_UFRC_P_PPE (6 << 12) ++ ++/* checksums */ ++#define FE_ICS_GEN_EN BIT(2) ++#define FE_UCS_GEN_EN BIT(1) ++#define FE_TCS_GEN_EN BIT(0) ++ ++/* dma ring */ ++#define FE_PST_DRX_IDX0 BIT(16) ++#define FE_PST_DTX_IDX3 BIT(3) ++#define FE_PST_DTX_IDX2 BIT(2) ++#define FE_PST_DTX_IDX1 BIT(1) ++#define FE_PST_DTX_IDX0 BIT(0) ++ ++#define FE_TX_WB_DDONE BIT(6) ++#define FE_RX_DMA_BUSY BIT(3) ++#define FE_TX_DMA_BUSY BIT(1) ++#define FE_RX_DMA_EN BIT(2) ++#define FE_TX_DMA_EN BIT(0) ++ ++#define FE_PDMA_SIZE_4DWORDS (0 << 4) ++#define FE_PDMA_SIZE_8DWORDS (1 << 4) ++#define FE_PDMA_SIZE_16DWORDS (2 << 4) ++ ++#define FE_US_CYC_CNT_MASK 0xff ++#define FE_US_CYC_CNT_SHIFT 0x8 ++#define FE_US_CYC_CNT_DIVISOR 1000000 ++ ++#define RX_DMA_PLEN0(_x) (((_x) >> 16) & 0x3fff) ++#define RX_DMA_LSO BIT(30) ++#define RX_DMA_DONE BIT(31) ++#define RX_DMA_L4VALID BIT(30) ++ ++struct fe_rx_dma { ++ unsigned int rxd1; ++ unsigned int rxd2; ++ unsigned int rxd3; ++ unsigned int rxd4; ++} __packed __aligned(4); ++ ++#define TX_DMA_PLEN0_MASK ((0x3fff) << 16) ++#define TX_DMA_PLEN0(_x) (((_x) & 0x3fff) << 16) ++#define TX_DMA_LSO BIT(30) ++#define TX_DMA_DONE BIT(31) ++#define TX_DMA_QN(_x) ((_x) << 16) ++#define TX_DMA_PN(_x) ((_x) << 24) ++#define TX_DMA_QN_MASK TX_DMA_QN(0x7) ++#define TX_DMA_PN_MASK TX_DMA_PN(0x7) ++#define TX_DMA_CHKSUM (0x7 << 29) ++ ++struct fe_tx_dma { ++ unsigned int txd1; ++ unsigned int txd2; ++ unsigned int txd3; ++ unsigned int txd4; ++} __packed __aligned(4); ++ ++struct fe_priv; ++ ++struct fe_phy { ++ struct phy_device *phy[8]; ++ struct device_node *phy_node[8]; ++ const __be32 *phy_fixed[8]; ++ int duplex[8]; ++ int speed[8]; ++ spinlock_t lock; ++ ++ int (*connect)(struct fe_priv *priv); ++ void (*disconnect)(struct fe_priv *priv); ++ void (*start)(struct fe_priv *priv); ++ void (*stop)(struct fe_priv *priv); ++}; ++ ++struct fe_soc_data ++{ ++ unsigned char mac[6]; ++ const u32 *reg_table; ++ ++ void (*reset_fe)(void); ++ void (*set_mac)(struct fe_priv *priv, unsigned char *mac); ++ void (*fwd_config)(struct fe_priv *priv); ++ void (*tx_dma)(struct fe_priv *priv, int idx, int len); ++ void (*rx_dma)(struct fe_priv *priv, int idx, int len); ++ int (*switch_init)(struct fe_priv *priv); ++ void (*port_init)(struct fe_priv *priv, struct device_node *port); ++ int (*has_carrier)(struct fe_priv *priv); ++ int (*mdio_init)(struct fe_priv *priv); ++ void (*mdio_cleanup)(struct fe_priv *priv); ++ int (*mdio_write)(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val); ++ int (*mdio_read)(struct mii_bus *bus, int phy_addr, int phy_reg); ++ void (*mdio_adjust_link)(struct fe_priv *priv, int port); ++ ++ void *swpriv; ++ u32 pdma_glo_cfg; ++ u32 rx_dly_int; ++ u32 tx_dly_int; ++ u32 checksum_bit; ++ ++ int min_pkt_len; ++}; ++ ++struct fe_priv ++{ ++ spinlock_t page_lock; ++ ++ struct fe_soc_data *soc; ++ struct net_device *netdev; ++ struct device *device; ++ unsigned long sysclk; ++ ++ struct fe_rx_dma *rx_dma; ++ struct sk_buff *rx_skb[NUM_DMA_DESC]; ++ struct napi_struct rx_napi; ++ dma_addr_t rx_phys; ++ ++ struct fe_tx_dma *tx_dma; ++ struct sk_buff *tx_skb[NUM_DMA_DESC]; ++ struct napi_struct tx_napi; ++ dma_addr_t tx_phys; ++ unsigned int tx_free_idx; ++ ++ struct fe_phy *phy; ++ struct mii_bus *mii_bus; ++ int mii_irq[PHY_MAX_ADDR]; ++ ++ int link[8]; ++}; ++ ++extern const struct of_device_id of_fe_match[]; ++ ++void fe_w32(u32 val, unsigned reg); ++u32 fe_r32(unsigned reg); ++ ++#endif /* FE_ETH_H */ +diff --git a/drivers/net/ethernet/ralink/soc_mt7620.c b/drivers/net/ethernet/ralink/soc_mt7620.c +new file mode 100644 +index 0000000..55e303f +--- /dev/null ++++ b/drivers/net/ethernet/ralink/soc_mt7620.c +@@ -0,0 +1,111 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++#include "gsw_mt7620a.h" ++ ++#define MT7620A_CDMA_CSG_CFG 0x400 ++#define MT7620_DMA_VID (MT7620A_CDMA_CSG_CFG | 0x30) ++#define MT7620A_DMA_2B_OFFSET BIT(31) ++#define MT7620A_RESET_FE BIT(21) ++#define MT7620A_RESET_ESW BIT(23) ++#define MT7620_L4_VALID BIT(23) ++ ++#define SYSC_REG_RESET_CTRL 0x34 ++#define MAX_RX_LENGTH 1536 ++ ++#define CDMA_ICS_EN BIT(2) ++#define CDMA_UCS_EN BIT(1) ++#define CDMA_TCS_EN BIT(0) ++ ++#define GDMA_ICS_EN BIT(22) ++#define GDMA_TCS_EN BIT(21) ++#define GDMA_UCS_EN BIT(20) ++ ++static const u32 rt5350_reg_table[FE_REG_COUNT] = { ++ [FE_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG, ++ [FE_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG, ++ [FE_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG, ++ [FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0, ++ [FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0, ++ [FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0, ++ [FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0, ++ [FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0, ++ [FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0, ++ [FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE, ++ [FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS, ++ [FE_REG_FE_DMA_VID_BASE] = MT7620_DMA_VID, ++}; ++ ++static void mt7620_fe_reset(void) ++{ ++ rt_sysc_w32(MT7620A_RESET_FE | MT7620A_RESET_ESW, SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(0, SYSC_REG_RESET_CTRL); ++} ++ ++static void mt7620_fwd_config(struct fe_priv *priv) ++{ ++ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) & ~7, MT7620A_GDMA1_FWD_CFG); ++ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) | (GDMA_ICS_EN | GDMA_TCS_EN | GDMA_UCS_EN), MT7620A_GDMA1_FWD_CFG); ++ fe_w32(fe_r32(MT7620A_CDMA_CSG_CFG) | (CDMA_ICS_EN | CDMA_UCS_EN | CDMA_TCS_EN), MT7620A_CDMA_CSG_CFG); ++} ++ ++static void mt7620_tx_dma(struct fe_priv *priv, int idx, int len) ++{ ++ if (len) ++ priv->tx_dma[idx].txd2 = TX_DMA_LSO | TX_DMA_PLEN0(len); ++ else ++ priv->tx_dma[idx].txd2 = TX_DMA_LSO | TX_DMA_DONE; ++} ++ ++static void mt7620_rx_dma(struct fe_priv *priv, int idx, int len) ++{ ++ priv->rx_dma[idx].rxd2 = RX_DMA_PLEN0(len); ++} ++ ++static struct fe_soc_data mt7620_data = { ++ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, ++ .reset_fe = mt7620_fe_reset, ++ .set_mac = mt7620_set_mac, ++ .fwd_config = mt7620_fwd_config, ++ .tx_dma = mt7620_tx_dma, ++ .rx_dma = mt7620_rx_dma, ++ .switch_init = mt7620_gsw_probe, ++ .port_init = mt7620_port_init, ++ .min_pkt_len = 0, ++ .reg_table = rt5350_reg_table, ++ .pdma_glo_cfg = FE_PDMA_SIZE_16DWORDS | MT7620A_DMA_2B_OFFSET, ++ .rx_dly_int = RT5350_RX_DLY_INT, ++ .tx_dly_int = RT5350_TX_DLY_INT, ++ .checksum_bit = MT7620_L4_VALID, ++ .has_carrier = mt7620a_has_carrier, ++ .mdio_read = mt7620_mdio_read, ++ .mdio_write = mt7620_mdio_write, ++ .mdio_adjust_link = mt7620_mdio_link_adjust, ++}; ++ ++const struct of_device_id of_fe_match[] = { ++ { .compatible = "ralink,mt7620a-eth", .data = &mt7620_data }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_fe_match); +diff --git a/drivers/net/ethernet/ralink/soc_rt2880.c b/drivers/net/ethernet/ralink/soc_rt2880.c +new file mode 100644 +index 0000000..fdbd118 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/soc_rt2880.c +@@ -0,0 +1,51 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++#include "mdio_rt2880.h" ++ ++#define SYSC_REG_RESET_CTRL 0x034 ++#define RT2880_RESET_FE BIT(18) ++ ++void rt2880_fe_reset(void) ++{ ++ rt_sysc_w32(RT2880_RESET_FE, SYSC_REG_RESET_CTRL); ++} ++ ++struct fe_soc_data rt2880_data = { ++ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, ++ .reset_fe = rt2880_fe_reset, ++ .min_pkt_len = 64, ++ .pdma_glo_cfg = FE_PDMA_SIZE_4DWORDS, ++ .checksum_bit = RX_DMA_L4VALID, ++ .rx_dly_int = FE_RX_DLY_INT, ++ .tx_dly_int = FE_TX_DLY_INT, ++ .mdio_read = rt2880_mdio_read, ++ .mdio_write = rt2880_mdio_write, ++ .mdio_link_adjust = rt2880_mdio_link_adjust, ++}; ++ ++const struct of_device_id of_fe_match[] = { ++ { .compatible = "ralink,rt2880-eth", .data = &rt2880_data }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_fe_match); +diff --git a/drivers/net/ethernet/ralink/soc_rt305x.c b/drivers/net/ethernet/ralink/soc_rt305x.c +new file mode 100644 +index 0000000..c43d3f9 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/soc_rt305x.c +@@ -0,0 +1,102 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++ ++#define RT305X_RESET_FE BIT(21) ++#define RT305X_RESET_ESW BIT(23) ++#define SYSC_REG_RESET_CTRL 0x034 ++ ++static const u32 rt5350_reg_table[FE_REG_COUNT] = { ++ [FE_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG, ++ [FE_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG, ++ [FE_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG, ++ [FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0, ++ [FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0, ++ [FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0, ++ [FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0, ++ [FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0, ++ [FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0, ++ [FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE, ++ [FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS, ++ [FE_REG_FE_DMA_VID_BASE] = 0, ++}; ++ ++static void rt305x_fe_reset(void) ++{ ++ rt_sysc_w32(RT305X_RESET_FE, SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(0, SYSC_REG_RESET_CTRL); ++} ++ ++static void rt5350_set_mac(struct fe_priv *priv, unsigned char *mac) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ fe_w32((mac[0] << 8) | mac[1], RT5350_SDM_MAC_ADRH); ++ fe_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], ++ RT5350_SDM_MAC_ADRL); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++} ++ ++static void rt5350_fwd_config(struct fe_priv *priv) ++{ ++ fe_w32(fe_r32(RT5350_SDM_CFG) & ~0xffff, RT5350_SDM_CFG); ++ fe_w32(fe_r32(RT5350_SDM_CFG) | RT5350_SDM_ICS_EN | RT5350_SDM_TCS_EN | RT5350_SDM_UCS_EN, ++ RT5350_SDM_CFG); ++} ++ ++static void rt5350_fe_reset(void) ++{ ++ rt_sysc_w32(RT305X_RESET_FE | RT305X_RESET_ESW, SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(0, SYSC_REG_RESET_CTRL); ++} ++ ++static struct fe_soc_data rt3050_data = { ++ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, ++ .reset_fe = rt305x_fe_reset, ++ .min_pkt_len = 64, ++ .pdma_glo_cfg = FE_PDMA_SIZE_4DWORDS, ++ .checksum_bit = RX_DMA_L4VALID, ++ .rx_dly_int = FE_RX_DLY_INT, ++ .tx_dly_int = FE_TX_DLY_INT, ++}; ++ ++static struct fe_soc_data rt5350_data = { ++ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, ++ .reg_table = rt5350_reg_table, ++ .reset_fe = rt5350_fe_reset, ++ .set_mac = rt5350_set_mac, ++ .fwd_config = rt5350_fwd_config, ++ .min_pkt_len = 64, ++ .pdma_glo_cfg = FE_PDMA_SIZE_4DWORDS, ++ .checksum_bit = RX_DMA_L4VALID, ++ .rx_dly_int = RT5350_RX_DLY_INT, ++ .tx_dly_int = RT5350_TX_DLY_INT, ++}; ++ ++const struct of_device_id of_fe_match[] = { ++ { .compatible = "ralink,rt3050-eth", .data = &rt3050_data }, ++ { .compatible = "ralink,rt5350-eth", .data = &rt5350_data }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_fe_match); +diff --git a/drivers/net/ethernet/ralink/soc_rt3883.c b/drivers/net/ethernet/ralink/soc_rt3883.c +new file mode 100644 +index 0000000..3886be1 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/soc_rt3883.c +@@ -0,0 +1,59 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++#include "mdio_rt2880.h" ++ ++#define RT3883_SYSC_REG_RSTCTRL 0x34 ++#define RT3883_RSTCTRL_FE BIT(21) ++ ++static void rt3883_fe_reset(void) ++{ ++ u32 t; ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL); ++ t |= RT3883_RSTCTRL_FE; ++ rt_sysc_w32(t , RT3883_SYSC_REG_RSTCTRL); ++ ++ t &= ~RT3883_RSTCTRL_FE; ++ rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL); ++} ++ ++static struct fe_soc_data rt3883_data = { ++ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, ++ .reset_fe = rt3883_fe_reset, ++ .min_pkt_len = 64, ++ .pdma_glo_cfg = FE_PDMA_SIZE_4DWORDS, ++ .rx_dly_int = FE_RX_DLY_INT, ++ .tx_dly_int = FE_TX_DLY_INT, ++ .checksum_bit = RX_DMA_L4VALID, ++ .mdio_read = rt2880_mdio_read, ++ .mdio_write = rt2880_mdio_write, ++ .mdio_link_adjust = rt2880_mdio_link_adjust, ++}; ++ ++const struct of_device_id of_fe_match[] = { ++ { .compatible = "ralink,rt3883-eth", .data = &rt3883_data }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_fe_match); ++ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0073-USB-phy-add-ralink-SoC-driver.patch b/target/linux/ramips/patches-3.8/0073-USB-phy-add-ralink-SoC-driver.patch new file mode 100644 index 0000000000..e6a63bd249 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0073-USB-phy-add-ralink-SoC-driver.patch @@ -0,0 +1,238 @@ +From 277c29fcf17b9e3ada6ddc3bda0f7780bb8a222a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 20 Jun 2013 19:06:09 +0200 +Subject: [PATCH 73/79] USB: phy: add ralink SoC driver + +Signed-off-by: John Crispin +--- + drivers/usb/phy/Kconfig | 8 ++ + drivers/usb/phy/Makefile | 1 + + drivers/usb/phy/ralink-phy.c | 191 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 200 insertions(+) + create mode 100644 drivers/usb/phy/ralink-phy.c + +diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig +index 5de6e7f..9b899b0 100644 +--- a/drivers/usb/phy/Kconfig ++++ b/drivers/usb/phy/Kconfig +@@ -45,3 +45,11 @@ config USB_RCAR_PHY + + To compile this driver as a module, choose M here: the + module will be called rcar-phy. ++ ++config RALINK_USBPHY ++ bool "Ralink USB PHY controller Driver" ++ depends on MIPS && RALINK ++ select USB_OTG_UTILS ++ help ++ Enable this to support ralink USB phy controller for ralink ++ SoCs. +diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile +index 1a579a8..52ba41a 100644 +--- a/drivers/usb/phy/Makefile ++++ b/drivers/usb/phy/Makefile +@@ -9,3 +9,4 @@ obj-$(CONFIG_USB_ISP1301) += isp1301.o + obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o + obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o + obj-$(CONFIG_USB_RCAR_PHY) += rcar-phy.o ++obj-$(CONFIG_RALINK_USBPHY) += ralink-phy.o +diff --git a/drivers/usb/phy/ralink-phy.c b/drivers/usb/phy/ralink-phy.c +new file mode 100644 +index 0000000..3fbabea +--- /dev/null ++++ b/drivers/usb/phy/ralink-phy.c +@@ -0,0 +1,191 @@ ++/* ++ * Copyright (C) 2013 John Crispin ++ * ++ * based on: Renesas R-Car USB phy driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define RT_SYSC_REG_SYSCFG1 0x014 ++#define RT_SYSC_REG_CLKCFG1 0x030 ++#define RT_SYSC_REG_USB_PHY_CFG 0x05c ++ ++#define RT_RSTCTRL_UDEV BIT(25) ++#define RT_RSTCTRL_UHST BIT(22) ++#define RT_SYSCFG1_USB0_HOST_MODE BIT(10) ++ ++#define MT7620_CLKCFG1_UPHY0_CLK_EN BIT(25) ++#define RT_CLKCFG1_UPHY1_CLK_EN BIT(20) ++#define RT_CLKCFG1_UPHY0_CLK_EN BIT(18) ++ ++#define USB_PHY_UTMI_8B60M BIT(1) ++#define UDEV_WAKEUP BIT(0) ++ ++static atomic_t usb_pwr_ref = ATOMIC_INIT(0); ++static struct reset_control *rstdev; ++static struct reset_control *rsthost; ++static u32 phy_clk; ++ ++static void usb_phy_enable(int state) ++{ ++ if (state) ++ rt_sysc_m32(0, phy_clk, RT_SYSC_REG_CLKCFG1); ++ else ++ rt_sysc_m32(phy_clk, 0, RT_SYSC_REG_CLKCFG1); ++ mdelay(100); ++} ++ ++static int usb_power_on(struct usb_phy *phy) ++{ ++ if (atomic_inc_return(&usb_pwr_ref) == 1) { ++ u32 t; ++ ++ usb_phy_enable(1); ++ ++ reset_control_assert(rstdev); ++ reset_control_assert(rsthost); ++ ++ if (OTG_STATE_B_HOST) { ++ rt_sysc_m32(0, RT_SYSCFG1_USB0_HOST_MODE, RT_SYSC_REG_SYSCFG1); ++ reset_control_deassert(rsthost); ++ } else { ++ rt_sysc_m32(RT_SYSCFG1_USB0_HOST_MODE, 0, RT_SYSC_REG_SYSCFG1); ++ reset_control_deassert(rstdev); ++ } ++ mdelay(100); ++ ++ t = rt_sysc_r32(RT_SYSC_REG_USB_PHY_CFG); ++ dev_info(phy->dev, "remote usb device wakeup %s\n", ++ (t & UDEV_WAKEUP) ? ("enabbled") : ("disabled")); ++ if (t & USB_PHY_UTMI_8B60M) ++ dev_info(phy->dev, "UTMI 8bit 60MHz\n"); ++ else ++ dev_info(phy->dev, "UTMI 16bit 30MHz\n"); ++ } ++ ++ return 0; ++} ++ ++static void usb_power_off(struct usb_phy *phy) ++{ ++ if (atomic_dec_return(&usb_pwr_ref) == 0) { ++ usb_phy_enable(0); ++ reset_control_assert(rstdev); ++ reset_control_assert(rsthost); ++ } ++} ++ ++static int usb_set_host(struct usb_otg *otg, struct usb_bus *host) ++{ ++ otg->gadget = NULL; ++ otg->host = host; ++ ++ return 0; ++} ++ ++static int usb_set_peripheral(struct usb_otg *otg, ++ struct usb_gadget *gadget) ++{ ++ otg->host = NULL; ++ otg->gadget = gadget; ++ ++ return 0; ++} ++ ++static const struct of_device_id ralink_usbphy_dt_match[] = { ++ { .compatible = "ralink,rt3xxx-usbphy", .data = (void *) (RT_CLKCFG1_UPHY1_CLK_EN | RT_CLKCFG1_UPHY0_CLK_EN) }, ++ { .compatible = "ralink,mt7620a-usbphy", .data = (void *) MT7620_CLKCFG1_UPHY0_CLK_EN }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ralink_usbphy_dt_match); ++ ++static int usb_phy_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *match; ++ struct device *dev = &pdev->dev; ++ struct usb_otg *otg; ++ struct usb_phy *phy; ++ int ret; ++ ++ match = of_match_device(ralink_usbphy_dt_match, &pdev->dev); ++ phy_clk = (int) match->data; ++ ++ rsthost = devm_reset_control_get(&pdev->dev, "host"); ++ if (IS_ERR(rsthost)) ++ return PTR_ERR(rsthost); ++ ++ rstdev = devm_reset_control_get(&pdev->dev, "device"); ++ if (IS_ERR(rstdev)) ++ return PTR_ERR(rstdev); ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) { ++ dev_err(&pdev->dev, "unable to allocate memory for USB PHY\n"); ++ return -ENOMEM; ++ } ++ ++ otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); ++ if (!otg) { ++ dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n"); ++ return -ENOMEM; ++ } ++ ++ phy->dev = dev; ++ phy->label = dev_name(dev); ++ phy->init = usb_power_on; ++ phy->shutdown = usb_power_off; ++ otg->set_host = usb_set_host; ++ otg->set_peripheral = usb_set_peripheral; ++ otg->phy = phy; ++ phy->otg = otg; ++ ret = usb_add_phy(phy, USB_PHY_TYPE_USB2); ++ ++ if (ret < 0) { ++ dev_err(dev, "usb phy addition error\n"); ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, phy); ++ ++ dev_info(&pdev->dev, "loaded\n"); ++ ++ return ret; ++} ++ ++static int usb_phy_remove(struct platform_device *pdev) ++{ ++ struct usb_phy *phy = platform_get_drvdata(pdev); ++ ++ usb_remove_phy(phy); ++ ++ return 0; ++} ++ ++static struct platform_driver usb_phy_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "rt3xxx-usbphy", ++ .of_match_table = of_match_ptr(ralink_usbphy_dt_match), ++ }, ++ .probe = usb_phy_probe, ++ .remove = usb_phy_remove, ++}; ++ ++module_platform_driver(usb_phy_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("Ralink USB phy"); ++MODULE_AUTHOR("John Crispin "); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0074-USB-MIPS-ralink-fix-usb-issue-on-mt7620.patch b/target/linux/ramips/patches-3.8/0074-USB-MIPS-ralink-fix-usb-issue-on-mt7620.patch new file mode 100644 index 0000000000..ad8269cb23 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0074-USB-MIPS-ralink-fix-usb-issue-on-mt7620.patch @@ -0,0 +1,48 @@ +From 93bb6d731e96dc373b88a910ca9db66c560c8f4e Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 24 May 2013 21:28:08 +0200 +Subject: [PATCH 74/79] USB: MIPS: ralink: fix usb issue on mt7620 + +USB fails when frequency scaling is enabled. Increase the idle cpu speed when +scaled. + +Signed-off-by: John Crispin +--- + arch/mips/include/asm/mach-ralink/mt7620.h | 1 + + arch/mips/ralink/mt7620.c | 8 ++++++++ + 2 files changed, 9 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/mt7620.h b/arch/mips/include/asm/mach-ralink/mt7620.h +index 9809972..d469c69 100644 +--- a/arch/mips/include/asm/mach-ralink/mt7620.h ++++ b/arch/mips/include/asm/mach-ralink/mt7620.h +@@ -20,6 +20,7 @@ + #define SYSC_REG_CHIP_REV 0x0c + #define SYSC_REG_SYSTEM_CONFIG0 0x10 + #define SYSC_REG_SYSTEM_CONFIG1 0x14 ++#define SYSC_REG_CPU_SYS_CLKCFG 0x3c + #define SYSC_REG_CPLL_CONFIG0 0x54 + #define SYSC_REG_CPLL_CONFIG1 0x58 + +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index 4956d96..d76eb85 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -186,6 +186,14 @@ void __init ralink_clk_init(void) + ralink_clk_add("10000500.uart", 40000000); + ralink_clk_add("10000b00.spi", 40000000); + ralink_clk_add("10000c00.uartlite", 40000000); ++ ++#ifdef CONFIG_USB ++ /* ++ * When the CPU goes into sleep mode, the BUS clock will be too low for ++ * USB to function properly ++ */ ++ rt_sysc_m32(0x1f1f, 0x303, SYSC_REG_CPU_SYS_CLKCFG); ++#endif + } + + void __init ralink_of_remap(void) +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0075-Kbuild-add-missing-space.patch b/target/linux/ramips/patches-3.8/0075-Kbuild-add-missing-space.patch new file mode 100644 index 0000000000..b66bd85696 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0075-Kbuild-add-missing-space.patch @@ -0,0 +1,38 @@ +From 5d5c270635a50b82931bb32f6fe28b84d45bdb59 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 17 May 2013 01:03:45 +0200 +Subject: [PATCH 75/79] Kbuild: add missing space + +Currently the output looks like this: +DTC arch/mips/ralink/dts/mt7620a_eval.dtb +DTB arch/mips/ralink/dts/mt7620a_eval.dtb.S +AS arch/mips/ralink/dts/mt7620a_eval.dtb.o + +Whitespace error was introduced by initial commit +commit aab94339cd85d726abeae78fc02351fc1910e6a4 +Author: Dirk Brandewie +Date: Wed Dec 22 11:57:26 2010 -0800 + +of: Add support for linking device tree blobs into vmlinux + +Signed-off-by: John Crispin +--- + scripts/Makefile.lib | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib +index bdf42fd..21c1359 100644 +--- a/scripts/Makefile.lib ++++ b/scripts/Makefile.lib +@@ -246,7 +246,7 @@ cmd_gzip = (cat $(filter-out FORCE,$^) | gzip -n -f -9 > $@) || \ + # --------------------------------------------------------------------------- + + # Generate an assembly file to wrap the output of the device tree compiler +-quiet_cmd_dt_S_dtb= DTB $@ ++quiet_cmd_dt_S_dtb= DTB $@ + cmd_dt_S_dtb= \ + ( \ + echo '\#include '; \ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0076-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch b/target/linux/ramips/patches-3.8/0076-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch new file mode 100644 index 0000000000..c1fe6ad2a0 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0076-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch @@ -0,0 +1,3446 @@ +From 3bebf4a4400ab70ccef98c069d240dbd17dd718f Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 20 Jun 2013 19:13:25 +0200 +Subject: [PATCH 76/79] mmc: MIPS: ralink: add sdhci for mt7620a SoC + +Signed-off-by: John Crispin +--- + drivers/mmc/host/Kconfig | 11 + + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/mt6575_sd.h | 1068 ++++++++++++++++++ + drivers/mmc/host/sdhci-mt7620.c | 2314 +++++++++++++++++++++++++++++++++++++++ + 4 files changed, 3394 insertions(+) + create mode 100644 drivers/mmc/host/mt6575_sd.h + create mode 100644 drivers/mmc/host/sdhci-mt7620.c + +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 8d13c65..04085b3 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -241,6 +241,17 @@ config MMC_SDHCI_S3C_DMA + + YMMV. + ++config MMC_SDHCI_MT7620 ++ tristate "SDHCI platform support for the MT7620 SD/MMC Controller" ++ depends on SOC_MT7620 ++ depends on MMC_SDHCI_PLTFM ++ select MMC_SDHCI_IO_ACCESSORS ++ help ++ This selects the BCM2835 SD/MMC controller. If you have a BCM2835 ++ platform with SD or MMC devices, say Y or M here. ++ ++ If unsure, say N. ++ + config MMC_OMAP + tristate "TI OMAP Multimedia Card Interface support" + depends on ARCH_OMAP +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index e4e218c..be79804 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -58,6 +58,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o + obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o + obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o + obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o ++obj-$(CONFIG_MMC_SDHCI_MT7620) += sdhci-mt7620.o + + ifeq ($(CONFIG_CB710_DEBUG),y) + CFLAGS-cb710-mmc += -DDEBUG +diff --git a/drivers/mmc/host/mt6575_sd.h b/drivers/mmc/host/mt6575_sd.h +new file mode 100644 +index 0000000..406382c +--- /dev/null ++++ b/drivers/mmc/host/mt6575_sd.h +@@ -0,0 +1,1068 @@ ++/* Copyright Statement: ++ * ++ * This software/firmware and related documentation ("MediaTek Software") are ++ * protected under relevant copyright laws. The information contained herein ++ * is confidential and proprietary to MediaTek Inc. and/or its licensors. ++ * Without the prior written permission of MediaTek inc. and/or its licensors, ++ * any reproduction, modification, use or disclosure of MediaTek Software, ++ * and information contained herein, in whole or in part, shall be strictly prohibited. ++ */ ++/* MediaTek Inc. (C) 2010. All rights reserved. ++ * ++ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES ++ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") ++ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON ++ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. ++ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE ++ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR ++ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH ++ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES ++ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES ++ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK ++ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR ++ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND ++ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE, ++ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE, ++ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO ++ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. ++ * ++ * The following software/firmware and/or related documentation ("MediaTek Software") ++ * have been modified by MediaTek Inc. All revisions are subject to any receiver's ++ * applicable license agreements with MediaTek Inc. ++ */ ++ ++#ifndef MT6575_SD_H ++#define MT6575_SD_H ++ ++#include ++#include ++ ++// #include /* --- by chhung */ ++ ++typedef void (*sdio_irq_handler_t)(void*); /* external irq handler */ ++typedef void (*pm_callback_t)(pm_message_t state, void *data); ++ ++#define MSDC_CD_PIN_EN (1 << 0) /* card detection pin is wired */ ++#define MSDC_WP_PIN_EN (1 << 1) /* write protection pin is wired */ ++#define MSDC_RST_PIN_EN (1 << 2) /* emmc reset pin is wired */ ++#define MSDC_SDIO_IRQ (1 << 3) /* use internal sdio irq (bus) */ ++#define MSDC_EXT_SDIO_IRQ (1 << 4) /* use external sdio irq */ ++#define MSDC_REMOVABLE (1 << 5) /* removable slot */ ++#define MSDC_SYS_SUSPEND (1 << 6) /* suspended by system */ ++#define MSDC_HIGHSPEED (1 << 7) /* high-speed mode support */ ++#define MSDC_UHS1 (1 << 8) /* uhs-1 mode support */ ++#define MSDC_DDR (1 << 9) /* ddr mode support */ ++#define MSDC_SPE (1 << 10) /* special support */ ++#define MSDC_INTERNAL_CLK (1 << 11) /* Force Internal clock */ ++#define MSDC_TABDRV (1 << 12) /* TABLET */ ++ ++ ++#define MSDC_SMPL_RISING (0) ++#define MSDC_SMPL_FALLING (1) ++ ++#define MSDC_CMD_PIN (0) ++#define MSDC_DAT_PIN (1) ++#define MSDC_CD_PIN (2) ++#define MSDC_WP_PIN (3) ++#define MSDC_RST_PIN (4) ++ ++enum { ++ MSDC_CLKSRC_26MHZ = 0, ++ MSDC_CLKSRC_197MHZ = 1, ++ MSDC_CLKSRC_208MHZ = 2 ++}; ++ ++struct msdc_hw { ++ unsigned char clk_src; /* host clock source */ ++ unsigned char cmd_edge; /* command latch edge */ ++ unsigned char data_edge; /* data latch edge */ ++ unsigned char clk_drv; /* clock pad driving */ ++ unsigned char cmd_drv; /* command pad driving */ ++ unsigned char dat_drv; /* data pad driving */ ++ unsigned long flags; /* hardware capability flags */ ++ unsigned long data_pins; /* data pins */ ++ unsigned long data_offset; /* data address offset */ ++ ++ /* config gpio pull mode */ ++ void (*config_gpio_pin)(int type, int pull); ++ ++ /* external power control for card */ ++ void (*ext_power_on)(void); ++ void (*ext_power_off)(void); ++ ++ /* external sdio irq operations */ ++ void (*request_sdio_eirq)(sdio_irq_handler_t sdio_irq_handler, void *data); ++ void (*enable_sdio_eirq)(void); ++ void (*disable_sdio_eirq)(void); ++ ++ /* external cd irq operations */ ++ void (*request_cd_eirq)(sdio_irq_handler_t cd_irq_handler, void *data); ++ void (*enable_cd_eirq)(void); ++ void (*disable_cd_eirq)(void); ++ int (*get_cd_status)(void); ++ ++ /* power management callback for external module */ ++ void (*register_pm)(pm_callback_t pm_cb, void *data); ++}; ++ ++extern struct msdc_hw msdc0_hw; ++extern struct msdc_hw msdc1_hw; ++extern struct msdc_hw msdc2_hw; ++extern struct msdc_hw msdc3_hw; ++ ++ ++/*--------------------------------------------------------------------------*/ ++/* Common Macro */ ++/*--------------------------------------------------------------------------*/ ++#define REG_ADDR(x) ((volatile u32*)(base + OFFSET_##x)) ++ ++/*--------------------------------------------------------------------------*/ ++/* Common Definition */ ++/*--------------------------------------------------------------------------*/ ++#define MSDC_FIFO_SZ (128) ++#define MSDC_FIFO_THD (64) // (128) ++#define MSDC_NUM (4) ++ ++#define MSDC_MS (0) ++#define MSDC_SDMMC (1) ++ ++#define MSDC_MODE_UNKNOWN (0) ++#define MSDC_MODE_PIO (1) ++#define MSDC_MODE_DMA_BASIC (2) ++#define MSDC_MODE_DMA_DESC (3) ++#define MSDC_MODE_DMA_ENHANCED (4) ++#define MSDC_MODE_MMC_STREAM (5) ++ ++#define MSDC_BUS_1BITS (0) ++#define MSDC_BUS_4BITS (1) ++#define MSDC_BUS_8BITS (2) ++ ++#define MSDC_BRUST_8B (3) ++#define MSDC_BRUST_16B (4) ++#define MSDC_BRUST_32B (5) ++#define MSDC_BRUST_64B (6) ++ ++#define MSDC_PIN_PULL_NONE (0) ++#define MSDC_PIN_PULL_DOWN (1) ++#define MSDC_PIN_PULL_UP (2) ++#define MSDC_PIN_KEEP (3) ++ ++#define MSDC_MAX_SCLK (48000000) /* +/- by chhung */ ++#define MSDC_MIN_SCLK (260000) ++ ++#define MSDC_AUTOCMD12 (0x0001) ++#define MSDC_AUTOCMD23 (0x0002) ++#define MSDC_AUTOCMD19 (0x0003) ++ ++#define MSDC_EMMC_BOOTMODE0 (0) /* Pull low CMD mode */ ++#define MSDC_EMMC_BOOTMODE1 (1) /* Reset CMD mode */ ++ ++enum { ++ RESP_NONE = 0, ++ RESP_R1, ++ RESP_R2, ++ RESP_R3, ++ RESP_R4, ++ RESP_R5, ++ RESP_R6, ++ RESP_R7, ++ RESP_R1B ++}; ++ ++/*--------------------------------------------------------------------------*/ ++/* Register Offset */ ++/*--------------------------------------------------------------------------*/ ++#define OFFSET_MSDC_CFG (0x0) ++#define OFFSET_MSDC_IOCON (0x04) ++#define OFFSET_MSDC_PS (0x08) ++#define OFFSET_MSDC_INT (0x0c) ++#define OFFSET_MSDC_INTEN (0x10) ++#define OFFSET_MSDC_FIFOCS (0x14) ++#define OFFSET_MSDC_TXDATA (0x18) ++#define OFFSET_MSDC_RXDATA (0x1c) ++#define OFFSET_SDC_CFG (0x30) ++#define OFFSET_SDC_CMD (0x34) ++#define OFFSET_SDC_ARG (0x38) ++#define OFFSET_SDC_STS (0x3c) ++#define OFFSET_SDC_RESP0 (0x40) ++#define OFFSET_SDC_RESP1 (0x44) ++#define OFFSET_SDC_RESP2 (0x48) ++#define OFFSET_SDC_RESP3 (0x4c) ++#define OFFSET_SDC_BLK_NUM (0x50) ++#define OFFSET_SDC_CSTS (0x58) ++#define OFFSET_SDC_CSTS_EN (0x5c) ++#define OFFSET_SDC_DCRC_STS (0x60) ++#define OFFSET_EMMC_CFG0 (0x70) ++#define OFFSET_EMMC_CFG1 (0x74) ++#define OFFSET_EMMC_STS (0x78) ++#define OFFSET_EMMC_IOCON (0x7c) ++#define OFFSET_SDC_ACMD_RESP (0x80) ++#define OFFSET_SDC_ACMD19_TRG (0x84) ++#define OFFSET_SDC_ACMD19_STS (0x88) ++#define OFFSET_MSDC_DMA_SA (0x90) ++#define OFFSET_MSDC_DMA_CA (0x94) ++#define OFFSET_MSDC_DMA_CTRL (0x98) ++#define OFFSET_MSDC_DMA_CFG (0x9c) ++#define OFFSET_MSDC_DBG_SEL (0xa0) ++#define OFFSET_MSDC_DBG_OUT (0xa4) ++#define OFFSET_MSDC_PATCH_BIT (0xb0) ++#define OFFSET_MSDC_PATCH_BIT1 (0xb4) ++#define OFFSET_MSDC_PAD_CTL0 (0xe0) ++#define OFFSET_MSDC_PAD_CTL1 (0xe4) ++#define OFFSET_MSDC_PAD_CTL2 (0xe8) ++#define OFFSET_MSDC_PAD_TUNE (0xec) ++#define OFFSET_MSDC_DAT_RDDLY0 (0xf0) ++#define OFFSET_MSDC_DAT_RDDLY1 (0xf4) ++#define OFFSET_MSDC_HW_DBG (0xf8) ++#define OFFSET_MSDC_VERSION (0x100) ++#define OFFSET_MSDC_ECO_VER (0x104) ++ ++/*--------------------------------------------------------------------------*/ ++/* Register Address */ ++/*--------------------------------------------------------------------------*/ ++ ++/* common register */ ++#define MSDC_CFG REG_ADDR(MSDC_CFG) ++#define MSDC_IOCON REG_ADDR(MSDC_IOCON) ++#define MSDC_PS REG_ADDR(MSDC_PS) ++#define MSDC_INT REG_ADDR(MSDC_INT) ++#define MSDC_INTEN REG_ADDR(MSDC_INTEN) ++#define MSDC_FIFOCS REG_ADDR(MSDC_FIFOCS) ++#define MSDC_TXDATA REG_ADDR(MSDC_TXDATA) ++#define MSDC_RXDATA REG_ADDR(MSDC_RXDATA) ++#define MSDC_PATCH_BIT0 REG_ADDR(MSDC_PATCH_BIT) ++ ++/* sdmmc register */ ++#define SDC_CFG REG_ADDR(SDC_CFG) ++#define SDC_CMD REG_ADDR(SDC_CMD) ++#define SDC_ARG REG_ADDR(SDC_ARG) ++#define SDC_STS REG_ADDR(SDC_STS) ++#define SDC_RESP0 REG_ADDR(SDC_RESP0) ++#define SDC_RESP1 REG_ADDR(SDC_RESP1) ++#define SDC_RESP2 REG_ADDR(SDC_RESP2) ++#define SDC_RESP3 REG_ADDR(SDC_RESP3) ++#define SDC_BLK_NUM REG_ADDR(SDC_BLK_NUM) ++#define SDC_CSTS REG_ADDR(SDC_CSTS) ++#define SDC_CSTS_EN REG_ADDR(SDC_CSTS_EN) ++#define SDC_DCRC_STS REG_ADDR(SDC_DCRC_STS) ++ ++/* emmc register*/ ++#define EMMC_CFG0 REG_ADDR(EMMC_CFG0) ++#define EMMC_CFG1 REG_ADDR(EMMC_CFG1) ++#define EMMC_STS REG_ADDR(EMMC_STS) ++#define EMMC_IOCON REG_ADDR(EMMC_IOCON) ++ ++/* auto command register */ ++#define SDC_ACMD_RESP REG_ADDR(SDC_ACMD_RESP) ++#define SDC_ACMD19_TRG REG_ADDR(SDC_ACMD19_TRG) ++#define SDC_ACMD19_STS REG_ADDR(SDC_ACMD19_STS) ++ ++/* dma register */ ++#define MSDC_DMA_SA REG_ADDR(MSDC_DMA_SA) ++#define MSDC_DMA_CA REG_ADDR(MSDC_DMA_CA) ++#define MSDC_DMA_CTRL REG_ADDR(MSDC_DMA_CTRL) ++#define MSDC_DMA_CFG REG_ADDR(MSDC_DMA_CFG) ++ ++/* pad ctrl register */ ++#define MSDC_PAD_CTL0 REG_ADDR(MSDC_PAD_CTL0) ++#define MSDC_PAD_CTL1 REG_ADDR(MSDC_PAD_CTL1) ++#define MSDC_PAD_CTL2 REG_ADDR(MSDC_PAD_CTL2) ++ ++/* data read delay */ ++#define MSDC_DAT_RDDLY0 REG_ADDR(MSDC_DAT_RDDLY0) ++#define MSDC_DAT_RDDLY1 REG_ADDR(MSDC_DAT_RDDLY1) ++ ++/* debug register */ ++#define MSDC_DBG_SEL REG_ADDR(MSDC_DBG_SEL) ++#define MSDC_DBG_OUT REG_ADDR(MSDC_DBG_OUT) ++ ++/* misc register */ ++#define MSDC_PATCH_BIT REG_ADDR(MSDC_PATCH_BIT) ++#define MSDC_PATCH_BIT1 REG_ADDR(MSDC_PATCH_BIT1) ++#define MSDC_PAD_TUNE REG_ADDR(MSDC_PAD_TUNE) ++#define MSDC_HW_DBG REG_ADDR(MSDC_HW_DBG) ++#define MSDC_VERSION REG_ADDR(MSDC_VERSION) ++#define MSDC_ECO_VER REG_ADDR(MSDC_ECO_VER) /* ECO Version */ ++ ++/*--------------------------------------------------------------------------*/ ++/* Register Mask */ ++/*--------------------------------------------------------------------------*/ ++ ++/* MSDC_CFG mask */ ++#define MSDC_CFG_MODE (0x1 << 0) /* RW */ ++#define MSDC_CFG_CKPDN (0x1 << 1) /* RW */ ++#define MSDC_CFG_RST (0x1 << 2) /* RW */ ++#define MSDC_CFG_PIO (0x1 << 3) /* RW */ ++#define MSDC_CFG_CKDRVEN (0x1 << 4) /* RW */ ++#define MSDC_CFG_BV18SDT (0x1 << 5) /* RW */ ++#define MSDC_CFG_BV18PSS (0x1 << 6) /* R */ ++#define MSDC_CFG_CKSTB (0x1 << 7) /* R */ ++#define MSDC_CFG_CKDIV (0xff << 8) /* RW */ ++#define MSDC_CFG_CKMOD (0x3 << 16) /* RW */ ++ ++/* MSDC_IOCON mask */ ++#define MSDC_IOCON_SDR104CKS (0x1 << 0) /* RW */ ++#define MSDC_IOCON_RSPL (0x1 << 1) /* RW */ ++#define MSDC_IOCON_DSPL (0x1 << 2) /* RW */ ++#define MSDC_IOCON_DDLSEL (0x1 << 3) /* RW */ ++#define MSDC_IOCON_DDR50CKD (0x1 << 4) /* RW */ ++#define MSDC_IOCON_DSPLSEL (0x1 << 5) /* RW */ ++#define MSDC_IOCON_D0SPL (0x1 << 16) /* RW */ ++#define MSDC_IOCON_D1SPL (0x1 << 17) /* RW */ ++#define MSDC_IOCON_D2SPL (0x1 << 18) /* RW */ ++#define MSDC_IOCON_D3SPL (0x1 << 19) /* RW */ ++#define MSDC_IOCON_D4SPL (0x1 << 20) /* RW */ ++#define MSDC_IOCON_D5SPL (0x1 << 21) /* RW */ ++#define MSDC_IOCON_D6SPL (0x1 << 22) /* RW */ ++#define MSDC_IOCON_D7SPL (0x1 << 23) /* RW */ ++#define MSDC_IOCON_RISCSZ (0x3 << 24) /* RW */ ++ ++/* MSDC_PS mask */ ++#define MSDC_PS_CDEN (0x1 << 0) /* RW */ ++#define MSDC_PS_CDSTS (0x1 << 1) /* R */ ++#define MSDC_PS_CDDEBOUNCE (0xf << 12) /* RW */ ++#define MSDC_PS_DAT (0xff << 16) /* R */ ++#define MSDC_PS_CMD (0x1 << 24) /* R */ ++#define MSDC_PS_WP (0x1UL<< 31) /* R */ ++ ++/* MSDC_INT mask */ ++#define MSDC_INT_MMCIRQ (0x1 << 0) /* W1C */ ++#define MSDC_INT_CDSC (0x1 << 1) /* W1C */ ++#define MSDC_INT_ACMDRDY (0x1 << 3) /* W1C */ ++#define MSDC_INT_ACMDTMO (0x1 << 4) /* W1C */ ++#define MSDC_INT_ACMDCRCERR (0x1 << 5) /* W1C */ ++#define MSDC_INT_DMAQ_EMPTY (0x1 << 6) /* W1C */ ++#define MSDC_INT_SDIOIRQ (0x1 << 7) /* W1C */ ++#define MSDC_INT_CMDRDY (0x1 << 8) /* W1C */ ++#define MSDC_INT_CMDTMO (0x1 << 9) /* W1C */ ++#define MSDC_INT_RSPCRCERR (0x1 << 10) /* W1C */ ++#define MSDC_INT_CSTA (0x1 << 11) /* R */ ++#define MSDC_INT_XFER_COMPL (0x1 << 12) /* W1C */ ++#define MSDC_INT_DXFER_DONE (0x1 << 13) /* W1C */ ++#define MSDC_INT_DATTMO (0x1 << 14) /* W1C */ ++#define MSDC_INT_DATCRCERR (0x1 << 15) /* W1C */ ++#define MSDC_INT_ACMD19_DONE (0x1 << 16) /* W1C */ ++ ++/* MSDC_INTEN mask */ ++#define MSDC_INTEN_MMCIRQ (0x1 << 0) /* RW */ ++#define MSDC_INTEN_CDSC (0x1 << 1) /* RW */ ++#define MSDC_INTEN_ACMDRDY (0x1 << 3) /* RW */ ++#define MSDC_INTEN_ACMDTMO (0x1 << 4) /* RW */ ++#define MSDC_INTEN_ACMDCRCERR (0x1 << 5) /* RW */ ++#define MSDC_INTEN_DMAQ_EMPTY (0x1 << 6) /* RW */ ++#define MSDC_INTEN_SDIOIRQ (0x1 << 7) /* RW */ ++#define MSDC_INTEN_CMDRDY (0x1 << 8) /* RW */ ++#define MSDC_INTEN_CMDTMO (0x1 << 9) /* RW */ ++#define MSDC_INTEN_RSPCRCERR (0x1 << 10) /* RW */ ++#define MSDC_INTEN_CSTA (0x1 << 11) /* RW */ ++#define MSDC_INTEN_XFER_COMPL (0x1 << 12) /* RW */ ++#define MSDC_INTEN_DXFER_DONE (0x1 << 13) /* RW */ ++#define MSDC_INTEN_DATTMO (0x1 << 14) /* RW */ ++#define MSDC_INTEN_DATCRCERR (0x1 << 15) /* RW */ ++#define MSDC_INTEN_ACMD19_DONE (0x1 << 16) /* RW */ ++ ++/* MSDC_FIFOCS mask */ ++#define MSDC_FIFOCS_RXCNT (0xff << 0) /* R */ ++#define MSDC_FIFOCS_TXCNT (0xff << 16) /* R */ ++#define MSDC_FIFOCS_CLR (0x1UL<< 31) /* RW */ ++ ++/* SDC_CFG mask */ ++#define SDC_CFG_SDIOINTWKUP (0x1 << 0) /* RW */ ++#define SDC_CFG_INSWKUP (0x1 << 1) /* RW */ ++#define SDC_CFG_BUSWIDTH (0x3 << 16) /* RW */ ++#define SDC_CFG_SDIO (0x1 << 19) /* RW */ ++#define SDC_CFG_SDIOIDE (0x1 << 20) /* RW */ ++#define SDC_CFG_INTATGAP (0x1 << 21) /* RW */ ++#define SDC_CFG_DTOC (0xffUL << 24) /* RW */ ++ ++/* SDC_CMD mask */ ++#define SDC_CMD_OPC (0x3f << 0) /* RW */ ++#define SDC_CMD_BRK (0x1 << 6) /* RW */ ++#define SDC_CMD_RSPTYP (0x7 << 7) /* RW */ ++#define SDC_CMD_DTYP (0x3 << 11) /* RW */ ++#define SDC_CMD_DTYP (0x3 << 11) /* RW */ ++#define SDC_CMD_RW (0x1 << 13) /* RW */ ++#define SDC_CMD_STOP (0x1 << 14) /* RW */ ++#define SDC_CMD_GOIRQ (0x1 << 15) /* RW */ ++#define SDC_CMD_BLKLEN (0xfff<< 16) /* RW */ ++#define SDC_CMD_AUTOCMD (0x3 << 28) /* RW */ ++#define SDC_CMD_VOLSWTH (0x1 << 30) /* RW */ ++ ++/* SDC_STS mask */ ++#define SDC_STS_SDCBUSY (0x1 << 0) /* RW */ ++#define SDC_STS_CMDBUSY (0x1 << 1) /* RW */ ++#define SDC_STS_SWR_COMPL (0x1 << 31) /* RW */ ++ ++/* SDC_DCRC_STS mask */ ++#define SDC_DCRC_STS_NEG (0xf << 8) /* RO */ ++#define SDC_DCRC_STS_POS (0xff << 0) /* RO */ ++ ++/* EMMC_CFG0 mask */ ++#define EMMC_CFG0_BOOTSTART (0x1 << 0) /* W */ ++#define EMMC_CFG0_BOOTSTOP (0x1 << 1) /* W */ ++#define EMMC_CFG0_BOOTMODE (0x1 << 2) /* RW */ ++#define EMMC_CFG0_BOOTACKDIS (0x1 << 3) /* RW */ ++#define EMMC_CFG0_BOOTWDLY (0x7 << 12) /* RW */ ++#define EMMC_CFG0_BOOTSUPP (0x1 << 15) /* RW */ ++ ++/* EMMC_CFG1 mask */ ++#define EMMC_CFG1_BOOTDATTMC (0xfffff << 0) /* RW */ ++#define EMMC_CFG1_BOOTACKTMC (0xfffUL << 20) /* RW */ ++ ++/* EMMC_STS mask */ ++#define EMMC_STS_BOOTCRCERR (0x1 << 0) /* W1C */ ++#define EMMC_STS_BOOTACKERR (0x1 << 1) /* W1C */ ++#define EMMC_STS_BOOTDATTMO (0x1 << 2) /* W1C */ ++#define EMMC_STS_BOOTACKTMO (0x1 << 3) /* W1C */ ++#define EMMC_STS_BOOTUPSTATE (0x1 << 4) /* R */ ++#define EMMC_STS_BOOTACKRCV (0x1 << 5) /* W1C */ ++#define EMMC_STS_BOOTDATRCV (0x1 << 6) /* R */ ++ ++/* EMMC_IOCON mask */ ++#define EMMC_IOCON_BOOTRST (0x1 << 0) /* RW */ ++ ++/* SDC_ACMD19_TRG mask */ ++#define SDC_ACMD19_TRG_TUNESEL (0xf << 0) /* RW */ ++ ++/* MSDC_DMA_CTRL mask */ ++#define MSDC_DMA_CTRL_START (0x1 << 0) /* W */ ++#define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */ ++#define MSDC_DMA_CTRL_RESUME (0x1 << 2) /* W */ ++#define MSDC_DMA_CTRL_MODE (0x1 << 8) /* RW */ ++#define MSDC_DMA_CTRL_LASTBUF (0x1 << 10) /* RW */ ++#define MSDC_DMA_CTRL_BRUSTSZ (0x7 << 12) /* RW */ ++#define MSDC_DMA_CTRL_XFERSZ (0xffffUL << 16)/* RW */ ++ ++/* MSDC_DMA_CFG mask */ ++#define MSDC_DMA_CFG_STS (0x1 << 0) /* R */ ++#define MSDC_DMA_CFG_DECSEN (0x1 << 1) /* RW */ ++#define MSDC_DMA_CFG_BDCSERR (0x1 << 4) /* R */ ++#define MSDC_DMA_CFG_GPDCSERR (0x1 << 5) /* R */ ++ ++/* MSDC_PATCH_BIT mask */ ++#define MSDC_PATCH_BIT_WFLSMODE (0x1 << 0) /* RW */ ++#define MSDC_PATCH_BIT_ODDSUPP (0x1 << 1) /* RW */ ++#define MSDC_PATCH_BIT_CKGEN_CK (0x1 << 6) /* E2: Fixed to 1 */ ++#define MSDC_PATCH_BIT_IODSSEL (0x1 << 16) /* RW */ ++#define MSDC_PATCH_BIT_IOINTSEL (0x1 << 17) /* RW */ ++#define MSDC_PATCH_BIT_BUSYDLY (0xf << 18) /* RW */ ++#define MSDC_PATCH_BIT_WDOD (0xf << 22) /* RW */ ++#define MSDC_PATCH_BIT_IDRTSEL (0x1 << 26) /* RW */ ++#define MSDC_PATCH_BIT_CMDFSEL (0x1 << 27) /* RW */ ++#define MSDC_PATCH_BIT_INTDLSEL (0x1 << 28) /* RW */ ++#define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */ ++#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */ ++ ++/* MSDC_PATCH_BIT1 mask */ ++#define MSDC_PATCH_BIT1_WRDAT_CRCS (0x7 << 3) ++#define MSDC_PATCH_BIT1_CMD_RSP (0x7 << 0) ++ ++/* MSDC_PAD_CTL0 mask */ ++#define MSDC_PAD_CTL0_CLKDRVN (0x7 << 0) /* RW */ ++#define MSDC_PAD_CTL0_CLKDRVP (0x7 << 4) /* RW */ ++#define MSDC_PAD_CTL0_CLKSR (0x1 << 8) /* RW */ ++#define MSDC_PAD_CTL0_CLKPD (0x1 << 16) /* RW */ ++#define MSDC_PAD_CTL0_CLKPU (0x1 << 17) /* RW */ ++#define MSDC_PAD_CTL0_CLKSMT (0x1 << 18) /* RW */ ++#define MSDC_PAD_CTL0_CLKIES (0x1 << 19) /* RW */ ++#define MSDC_PAD_CTL0_CLKTDSEL (0xf << 20) /* RW */ ++#define MSDC_PAD_CTL0_CLKRDSEL (0xffUL<< 24) /* RW */ ++ ++/* MSDC_PAD_CTL1 mask */ ++#define MSDC_PAD_CTL1_CMDDRVN (0x7 << 0) /* RW */ ++#define MSDC_PAD_CTL1_CMDDRVP (0x7 << 4) /* RW */ ++#define MSDC_PAD_CTL1_CMDSR (0x1 << 8) /* RW */ ++#define MSDC_PAD_CTL1_CMDPD (0x1 << 16) /* RW */ ++#define MSDC_PAD_CTL1_CMDPU (0x1 << 17) /* RW */ ++#define MSDC_PAD_CTL1_CMDSMT (0x1 << 18) /* RW */ ++#define MSDC_PAD_CTL1_CMDIES (0x1 << 19) /* RW */ ++#define MSDC_PAD_CTL1_CMDTDSEL (0xf << 20) /* RW */ ++#define MSDC_PAD_CTL1_CMDRDSEL (0xffUL<< 24) /* RW */ ++ ++/* MSDC_PAD_CTL2 mask */ ++#define MSDC_PAD_CTL2_DATDRVN (0x7 << 0) /* RW */ ++#define MSDC_PAD_CTL2_DATDRVP (0x7 << 4) /* RW */ ++#define MSDC_PAD_CTL2_DATSR (0x1 << 8) /* RW */ ++#define MSDC_PAD_CTL2_DATPD (0x1 << 16) /* RW */ ++#define MSDC_PAD_CTL2_DATPU (0x1 << 17) /* RW */ ++#define MSDC_PAD_CTL2_DATIES (0x1 << 19) /* RW */ ++#define MSDC_PAD_CTL2_DATSMT (0x1 << 18) /* RW */ ++#define MSDC_PAD_CTL2_DATTDSEL (0xf << 20) /* RW */ ++#define MSDC_PAD_CTL2_DATRDSEL (0xffUL<< 24) /* RW */ ++ ++/* MSDC_PAD_TUNE mask */ ++#define MSDC_PAD_TUNE_DATWRDLY (0x1F << 0) /* RW */ ++#define MSDC_PAD_TUNE_DATRRDLY (0x1F << 8) /* RW */ ++#define MSDC_PAD_TUNE_CMDRDLY (0x1F << 16) /* RW */ ++#define MSDC_PAD_TUNE_CMDRRDLY (0x1FUL << 22) /* RW */ ++#define MSDC_PAD_TUNE_CLKTXDLY (0x1FUL << 27) /* RW */ ++ ++/* MSDC_DAT_RDDLY0/1 mask */ ++#define MSDC_DAT_RDDLY0_D0 (0x1F << 0) /* RW */ ++#define MSDC_DAT_RDDLY0_D1 (0x1F << 8) /* RW */ ++#define MSDC_DAT_RDDLY0_D2 (0x1F << 16) /* RW */ ++#define MSDC_DAT_RDDLY0_D3 (0x1F << 24) /* RW */ ++ ++#define MSDC_DAT_RDDLY1_D4 (0x1F << 0) /* RW */ ++#define MSDC_DAT_RDDLY1_D5 (0x1F << 8) /* RW */ ++#define MSDC_DAT_RDDLY1_D6 (0x1F << 16) /* RW */ ++#define MSDC_DAT_RDDLY1_D7 (0x1F << 24) /* RW */ ++ ++#define MSDC_CKGEN_MSDC_DLY_SEL (0x1F<<10) ++#define MSDC_INT_DAT_LATCH_CK_SEL (0x7<<7) ++#define MSDC_CKGEN_MSDC_CK_SEL (0x1<<6) ++#define CARD_READY_FOR_DATA (1<<8) ++#define CARD_CURRENT_STATE(x) ((x&0x00001E00)>>9) ++ ++/*--------------------------------------------------------------------------*/ ++/* Descriptor Structure */ ++/*--------------------------------------------------------------------------*/ ++typedef struct { ++ u32 hwo:1; /* could be changed by hw */ ++ u32 bdp:1; ++ u32 rsv0:6; ++ u32 chksum:8; ++ u32 intr:1; ++ u32 rsv1:15; ++ void *next; ++ void *ptr; ++ u32 buflen:16; ++ u32 extlen:8; ++ u32 rsv2:8; ++ u32 arg; ++ u32 blknum; ++ u32 cmd; ++} gpd_t; ++ ++typedef struct { ++ u32 eol:1; ++ u32 rsv0:7; ++ u32 chksum:8; ++ u32 rsv1:1; ++ u32 blkpad:1; ++ u32 dwpad:1; ++ u32 rsv2:13; ++ void *next; ++ void *ptr; ++ u32 buflen:16; ++ u32 rsv3:16; ++} bd_t; ++ ++/*--------------------------------------------------------------------------*/ ++/* Register Debugging Structure */ ++/*--------------------------------------------------------------------------*/ ++ ++typedef struct { ++ u32 msdc:1; ++ u32 ckpwn:1; ++ u32 rst:1; ++ u32 pio:1; ++ u32 ckdrven:1; ++ u32 start18v:1; ++ u32 pass18v:1; ++ u32 ckstb:1; ++ u32 ckdiv:8; ++ u32 ckmod:2; ++ u32 pad:14; ++} msdc_cfg_reg; ++typedef struct { ++ u32 sdr104cksel:1; ++ u32 rsmpl:1; ++ u32 dsmpl:1; ++ u32 ddlysel:1; ++ u32 ddr50ckd:1; ++ u32 dsplsel:1; ++ u32 pad1:10; ++ u32 d0spl:1; ++ u32 d1spl:1; ++ u32 d2spl:1; ++ u32 d3spl:1; ++ u32 d4spl:1; ++ u32 d5spl:1; ++ u32 d6spl:1; ++ u32 d7spl:1; ++ u32 riscsz:1; ++ u32 pad2:7; ++} msdc_iocon_reg; ++typedef struct { ++ u32 cden:1; ++ u32 cdsts:1; ++ u32 pad1:10; ++ u32 cddebounce:4; ++ u32 dat:8; ++ u32 cmd:1; ++ u32 pad2:6; ++ u32 wp:1; ++} msdc_ps_reg; ++typedef struct { ++ u32 mmcirq:1; ++ u32 cdsc:1; ++ u32 pad1:1; ++ u32 atocmdrdy:1; ++ u32 atocmdtmo:1; ++ u32 atocmdcrc:1; ++ u32 dmaqempty:1; ++ u32 sdioirq:1; ++ u32 cmdrdy:1; ++ u32 cmdtmo:1; ++ u32 rspcrc:1; ++ u32 csta:1; ++ u32 xfercomp:1; ++ u32 dxferdone:1; ++ u32 dattmo:1; ++ u32 datcrc:1; ++ u32 atocmd19done:1; ++ u32 pad2:15; ++} msdc_int_reg; ++typedef struct { ++ u32 mmcirq:1; ++ u32 cdsc:1; ++ u32 pad1:1; ++ u32 atocmdrdy:1; ++ u32 atocmdtmo:1; ++ u32 atocmdcrc:1; ++ u32 dmaqempty:1; ++ u32 sdioirq:1; ++ u32 cmdrdy:1; ++ u32 cmdtmo:1; ++ u32 rspcrc:1; ++ u32 csta:1; ++ u32 xfercomp:1; ++ u32 dxferdone:1; ++ u32 dattmo:1; ++ u32 datcrc:1; ++ u32 atocmd19done:1; ++ u32 pad2:15; ++} msdc_inten_reg; ++typedef struct { ++ u32 rxcnt:8; ++ u32 pad1:8; ++ u32 txcnt:8; ++ u32 pad2:7; ++ u32 clr:1; ++} msdc_fifocs_reg; ++typedef struct { ++ u32 val; ++} msdc_txdat_reg; ++typedef struct { ++ u32 val; ++} msdc_rxdat_reg; ++typedef struct { ++ u32 sdiowkup:1; ++ u32 inswkup:1; ++ u32 pad1:14; ++ u32 buswidth:2; ++ u32 pad2:1; ++ u32 sdio:1; ++ u32 sdioide:1; ++ u32 intblkgap:1; ++ u32 pad4:2; ++ u32 dtoc:8; ++} sdc_cfg_reg; ++typedef struct { ++ u32 cmd:6; ++ u32 brk:1; ++ u32 rsptyp:3; ++ u32 pad1:1; ++ u32 dtype:2; ++ u32 rw:1; ++ u32 stop:1; ++ u32 goirq:1; ++ u32 blklen:12; ++ u32 atocmd:2; ++ u32 volswth:1; ++ u32 pad2:1; ++} sdc_cmd_reg; ++typedef struct { ++ u32 arg; ++} sdc_arg_reg; ++typedef struct { ++ u32 sdcbusy:1; ++ u32 cmdbusy:1; ++ u32 pad:29; ++ u32 swrcmpl:1; ++} sdc_sts_reg; ++typedef struct { ++ u32 val; ++} sdc_resp0_reg; ++typedef struct { ++ u32 val; ++} sdc_resp1_reg; ++typedef struct { ++ u32 val; ++} sdc_resp2_reg; ++typedef struct { ++ u32 val; ++} sdc_resp3_reg; ++typedef struct { ++ u32 num; ++} sdc_blknum_reg; ++typedef struct { ++ u32 sts; ++} sdc_csts_reg; ++typedef struct { ++ u32 sts; ++} sdc_cstsen_reg; ++typedef struct { ++ u32 datcrcsts:8; ++ u32 ddrcrcsts:4; ++ u32 pad:20; ++} sdc_datcrcsts_reg; ++typedef struct { ++ u32 bootstart:1; ++ u32 bootstop:1; ++ u32 bootmode:1; ++ u32 pad1:9; ++ u32 bootwaidly:3; ++ u32 bootsupp:1; ++ u32 pad2:16; ++} emmc_cfg0_reg; ++typedef struct { ++ u32 bootcrctmc:16; ++ u32 pad:4; ++ u32 bootacktmc:12; ++} emmc_cfg1_reg; ++typedef struct { ++ u32 bootcrcerr:1; ++ u32 bootackerr:1; ++ u32 bootdattmo:1; ++ u32 bootacktmo:1; ++ u32 bootupstate:1; ++ u32 bootackrcv:1; ++ u32 bootdatrcv:1; ++ u32 pad:25; ++} emmc_sts_reg; ++typedef struct { ++ u32 bootrst:1; ++ u32 pad:31; ++} emmc_iocon_reg; ++typedef struct { ++ u32 val; ++} msdc_acmd_resp_reg; ++typedef struct { ++ u32 tunesel:4; ++ u32 pad:28; ++} msdc_acmd19_trg_reg; ++typedef struct { ++ u32 val; ++} msdc_acmd19_sts_reg; ++typedef struct { ++ u32 addr; ++} msdc_dma_sa_reg; ++typedef struct { ++ u32 addr; ++} msdc_dma_ca_reg; ++typedef struct { ++ u32 start:1; ++ u32 stop:1; ++ u32 resume:1; ++ u32 pad1:5; ++ u32 mode:1; ++ u32 pad2:1; ++ u32 lastbuf:1; ++ u32 pad3:1; ++ u32 brustsz:3; ++ u32 pad4:1; ++ u32 xfersz:16; ++} msdc_dma_ctrl_reg; ++typedef struct { ++ u32 status:1; ++ u32 decsen:1; ++ u32 pad1:2; ++ u32 bdcsen:1; ++ u32 gpdcsen:1; ++ u32 pad2:26; ++} msdc_dma_cfg_reg; ++typedef struct { ++ u32 sel:16; ++ u32 pad2:16; ++} msdc_dbg_sel_reg; ++typedef struct { ++ u32 val; ++} msdc_dbg_out_reg; ++typedef struct { ++ u32 clkdrvn:3; ++ u32 rsv0:1; ++ u32 clkdrvp:3; ++ u32 rsv1:1; ++ u32 clksr:1; ++ u32 rsv2:7; ++ u32 clkpd:1; ++ u32 clkpu:1; ++ u32 clksmt:1; ++ u32 clkies:1; ++ u32 clktdsel:4; ++ u32 clkrdsel:8; ++} msdc_pad_ctl0_reg; ++typedef struct { ++ u32 cmddrvn:3; ++ u32 rsv0:1; ++ u32 cmddrvp:3; ++ u32 rsv1:1; ++ u32 cmdsr:1; ++ u32 rsv2:7; ++ u32 cmdpd:1; ++ u32 cmdpu:1; ++ u32 cmdsmt:1; ++ u32 cmdies:1; ++ u32 cmdtdsel:4; ++ u32 cmdrdsel:8; ++} msdc_pad_ctl1_reg; ++typedef struct { ++ u32 datdrvn:3; ++ u32 rsv0:1; ++ u32 datdrvp:3; ++ u32 rsv1:1; ++ u32 datsr:1; ++ u32 rsv2:7; ++ u32 datpd:1; ++ u32 datpu:1; ++ u32 datsmt:1; ++ u32 daties:1; ++ u32 dattdsel:4; ++ u32 datrdsel:8; ++} msdc_pad_ctl2_reg; ++typedef struct { ++ u32 wrrxdly:3; ++ u32 pad1:5; ++ u32 rdrxdly:8; ++ u32 pad2:16; ++} msdc_pad_tune_reg; ++typedef struct { ++ u32 dat0:5; ++ u32 rsv0:3; ++ u32 dat1:5; ++ u32 rsv1:3; ++ u32 dat2:5; ++ u32 rsv2:3; ++ u32 dat3:5; ++ u32 rsv3:3; ++} msdc_dat_rddly0; ++typedef struct { ++ u32 dat4:5; ++ u32 rsv4:3; ++ u32 dat5:5; ++ u32 rsv5:3; ++ u32 dat6:5; ++ u32 rsv6:3; ++ u32 dat7:5; ++ u32 rsv7:3; ++} msdc_dat_rddly1; ++typedef struct { ++ u32 dbg0sel:8; ++ u32 dbg1sel:6; ++ u32 pad1:2; ++ u32 dbg2sel:6; ++ u32 pad2:2; ++ u32 dbg3sel:6; ++ u32 pad3:2; ++} msdc_hw_dbg_reg; ++typedef struct { ++ u32 val; ++} msdc_version_reg; ++typedef struct { ++ u32 val; ++} msdc_eco_ver_reg; ++ ++struct msdc_regs { ++ msdc_cfg_reg msdc_cfg; /* base+0x00h */ ++ msdc_iocon_reg msdc_iocon; /* base+0x04h */ ++ msdc_ps_reg msdc_ps; /* base+0x08h */ ++ msdc_int_reg msdc_int; /* base+0x0ch */ ++ msdc_inten_reg msdc_inten; /* base+0x10h */ ++ msdc_fifocs_reg msdc_fifocs; /* base+0x14h */ ++ msdc_txdat_reg msdc_txdat; /* base+0x18h */ ++ msdc_rxdat_reg msdc_rxdat; /* base+0x1ch */ ++ u32 rsv1[4]; ++ sdc_cfg_reg sdc_cfg; /* base+0x30h */ ++ sdc_cmd_reg sdc_cmd; /* base+0x34h */ ++ sdc_arg_reg sdc_arg; /* base+0x38h */ ++ sdc_sts_reg sdc_sts; /* base+0x3ch */ ++ sdc_resp0_reg sdc_resp0; /* base+0x40h */ ++ sdc_resp1_reg sdc_resp1; /* base+0x44h */ ++ sdc_resp2_reg sdc_resp2; /* base+0x48h */ ++ sdc_resp3_reg sdc_resp3; /* base+0x4ch */ ++ sdc_blknum_reg sdc_blknum; /* base+0x50h */ ++ u32 rsv2[1]; ++ sdc_csts_reg sdc_csts; /* base+0x58h */ ++ sdc_cstsen_reg sdc_cstsen; /* base+0x5ch */ ++ sdc_datcrcsts_reg sdc_dcrcsta; /* base+0x60h */ ++ u32 rsv3[3]; ++ emmc_cfg0_reg emmc_cfg0; /* base+0x70h */ ++ emmc_cfg1_reg emmc_cfg1; /* base+0x74h */ ++ emmc_sts_reg emmc_sts; /* base+0x78h */ ++ emmc_iocon_reg emmc_iocon; /* base+0x7ch */ ++ msdc_acmd_resp_reg acmd_resp; /* base+0x80h */ ++ msdc_acmd19_trg_reg acmd19_trg; /* base+0x84h */ ++ msdc_acmd19_sts_reg acmd19_sts; /* base+0x88h */ ++ u32 rsv4[1]; ++ msdc_dma_sa_reg dma_sa; /* base+0x90h */ ++ msdc_dma_ca_reg dma_ca; /* base+0x94h */ ++ msdc_dma_ctrl_reg dma_ctrl; /* base+0x98h */ ++ msdc_dma_cfg_reg dma_cfg; /* base+0x9ch */ ++ msdc_dbg_sel_reg dbg_sel; /* base+0xa0h */ ++ msdc_dbg_out_reg dbg_out; /* base+0xa4h */ ++ u32 rsv5[2]; ++ u32 patch0; /* base+0xb0h */ ++ u32 patch1; /* base+0xb4h */ ++ u32 rsv6[10]; ++ msdc_pad_ctl0_reg pad_ctl0; /* base+0xe0h */ ++ msdc_pad_ctl1_reg pad_ctl1; /* base+0xe4h */ ++ msdc_pad_ctl2_reg pad_ctl2; /* base+0xe8h */ ++ msdc_pad_tune_reg pad_tune; /* base+0xech */ ++ msdc_dat_rddly0 dat_rddly0; /* base+0xf0h */ ++ msdc_dat_rddly1 dat_rddly1; /* base+0xf4h */ ++ msdc_hw_dbg_reg hw_dbg; /* base+0xf8h */ ++ u32 rsv7[1]; ++ msdc_version_reg version; /* base+0x100h */ ++ msdc_eco_ver_reg eco_ver; /* base+0x104h */ ++}; ++ ++struct scatterlist_ex { ++ u32 cmd; ++ u32 arg; ++ u32 sglen; ++ struct scatterlist *sg; ++}; ++ ++#define DMA_FLAG_NONE (0x00000000) ++#define DMA_FLAG_EN_CHKSUM (0x00000001) ++#define DMA_FLAG_PAD_BLOCK (0x00000002) ++#define DMA_FLAG_PAD_DWORD (0x00000004) ++ ++struct msdc_dma { ++ u32 flags; /* flags */ ++ u32 xfersz; /* xfer size in bytes */ ++ u32 sglen; /* size of scatter list */ ++ u32 blklen; /* block size */ ++ struct scatterlist *sg; /* I/O scatter list */ ++ struct scatterlist_ex *esg; /* extended I/O scatter list */ ++ u8 mode; /* dma mode */ ++ u8 burstsz; /* burst size */ ++ u8 intr; /* dma done interrupt */ ++ u8 padding; /* padding */ ++ u32 cmd; /* enhanced mode command */ ++ u32 arg; /* enhanced mode arg */ ++ u32 rsp; /* enhanced mode command response */ ++ u32 autorsp; /* auto command response */ ++ ++ gpd_t *gpd; /* pointer to gpd array */ ++ bd_t *bd; /* pointer to bd array */ ++ dma_addr_t gpd_addr; /* the physical address of gpd array */ ++ dma_addr_t bd_addr; /* the physical address of bd array */ ++ u32 used_gpd; /* the number of used gpd elements */ ++ u32 used_bd; /* the number of used bd elements */ ++}; ++ ++struct msdc_host ++{ ++ struct msdc_hw *hw; ++ ++ struct mmc_host *mmc; /* mmc structure */ ++ struct mmc_command *cmd; ++ struct mmc_data *data; ++ struct mmc_request *mrq; ++ int cmd_rsp; ++ int cmd_rsp_done; ++ int cmd_r1b_done; ++ ++ int error; ++ spinlock_t lock; /* mutex */ ++ struct semaphore sem; ++ ++ u32 blksz; /* host block size */ ++ u32 base; /* host base address */ ++ int id; /* host id */ ++ int pwr_ref; /* core power reference count */ ++ ++ u32 xfer_size; /* total transferred size */ ++ ++ struct msdc_dma dma; /* dma channel */ ++ u32 dma_addr; /* dma transfer address */ ++ u32 dma_left_size; /* dma transfer left size */ ++ u32 dma_xfer_size; /* dma transfer size in bytes */ ++ int dma_xfer; /* dma transfer mode */ ++ ++ u32 timeout_ns; /* data timeout ns */ ++ u32 timeout_clks; /* data timeout clks */ ++ ++ atomic_t abort; /* abort transfer */ ++ ++ int irq; /* host interrupt */ ++ ++ struct tasklet_struct card_tasklet; ++ ++ struct completion cmd_done; ++ struct completion xfer_done; ++ struct pm_message pm_state; ++ ++ u32 mclk; /* mmc subsystem clock */ ++ u32 hclk; /* host clock speed */ ++ u32 sclk; /* SD/MS clock speed */ ++ u8 core_clkon; /* Host core clock on ? */ ++ u8 card_clkon; /* Card clock on ? */ ++ u8 core_power; /* core power */ ++ u8 power_mode; /* host power mode */ ++ u8 card_inserted; /* card inserted ? */ ++ u8 suspend; /* host suspended ? */ ++ u8 reserved; ++ u8 app_cmd; /* for app command */ ++ u32 app_cmd_arg; ++ u64 starttime; ++}; ++ ++static inline unsigned int uffs(unsigned int x) ++{ ++ unsigned int r = 1; ++ ++ if (!x) ++ return 0; ++ if (!(x & 0xffff)) { ++ x >>= 16; ++ r += 16; ++ } ++ if (!(x & 0xff)) { ++ x >>= 8; ++ r += 8; ++ } ++ if (!(x & 0xf)) { ++ x >>= 4; ++ r += 4; ++ } ++ if (!(x & 3)) { ++ x >>= 2; ++ r += 2; ++ } ++ if (!(x & 1)) { ++ x >>= 1; ++ r += 1; ++ } ++ return r; ++} ++#define sdr_read8(reg) __raw_readb(reg) ++#define sdr_read16(reg) __raw_readw(reg) ++#define sdr_read32(reg) __raw_readl(reg) ++#define sdr_write8(reg,val) __raw_writeb(val,reg) ++#define sdr_write16(reg,val) __raw_writew(val,reg) ++#define sdr_write32(reg,val) __raw_writel(val,reg) ++ ++#define sdr_set_bits(reg,bs) ((*(volatile u32*)(reg)) |= (u32)(bs)) ++#define sdr_clr_bits(reg,bs) ((*(volatile u32*)(reg)) &= ~((u32)(bs))) ++ ++#define sdr_set_field(reg,field,val) \ ++ do { \ ++ volatile unsigned int tv = sdr_read32(reg); \ ++ tv &= ~(field); \ ++ tv |= ((val) << (uffs((unsigned int)field) - 1)); \ ++ sdr_write32(reg,tv); \ ++ } while(0) ++#define sdr_get_field(reg,field,val) \ ++ do { \ ++ volatile unsigned int tv = sdr_read32(reg); \ ++ val = ((tv & (field)) >> (uffs((unsigned int)field) - 1)); \ ++ } while(0) ++ ++#endif ++ +diff --git a/drivers/mmc/host/sdhci-mt7620.c b/drivers/mmc/host/sdhci-mt7620.c +new file mode 100644 +index 0000000..a3cb5e4 +--- /dev/null ++++ b/drivers/mmc/host/sdhci-mt7620.c +@@ -0,0 +1,2314 @@ ++/* Copyright Statement: ++ * ++ * This software/firmware and related documentation ("MediaTek Software") are ++ * protected under relevant copyright laws. The information contained herein ++ * is confidential and proprietary to MediaTek Inc. and/or its licensors. ++ * Without the prior written permission of MediaTek inc. and/or its licensors, ++ * any reproduction, modification, use or disclosure of MediaTek Software, ++ * and information contained herein, in whole or in part, shall be strictly prohibited. ++ * ++ * MediaTek Inc. (C) 2010. All rights reserved. ++ * ++ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES ++ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") ++ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON ++ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. ++ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE ++ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR ++ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH ++ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES ++ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES ++ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK ++ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR ++ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND ++ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE, ++ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE, ++ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO ++ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. ++ * ++ * The following software/firmware and/or related documentation ("MediaTek Software") ++ * have been modified by MediaTek Inc. All revisions are subject to any receiver's ++ * applicable license agreements with MediaTek Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define MSDC_SMPL_FALLING (1) ++#define MSDC_CD_PIN_EN (1 << 0) /* card detection pin is wired */ ++#define MSDC_WP_PIN_EN (1 << 1) /* write protection pin is wired */ ++#define MSDC_REMOVABLE (1 << 5) /* removable slot */ ++#define MSDC_SYS_SUSPEND (1 << 6) /* suspended by system */ ++#define MSDC_HIGHSPEED (1 << 7) ++ ++#define IRQ_SDC 22 ++ ++#include ++ ++#include "mt6575_sd.h" ++ ++#define DRV_NAME "mtk-sd" ++ ++#define HOST_MAX_NUM (1) /* +/- by chhung */ ++ ++#define HOST_MAX_MCLK (48000000) /* +/- by chhung */ ++#define HOST_MIN_MCLK (260000) ++ ++#define HOST_MAX_BLKSZ (2048) ++ ++#define MSDC_OCR_AVAIL (MMC_VDD_28_29 | MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33) ++ ++#define GPIO_PULL_DOWN (0) ++#define GPIO_PULL_UP (1) ++ ++#define DEFAULT_DEBOUNCE (8) /* 8 cycles */ ++#define DEFAULT_DTOC (40) /* data timeout counter. 65536x40 sclk. */ ++ ++#define CMD_TIMEOUT (HZ/10) /* 100ms */ ++#define DAT_TIMEOUT (HZ/2 * 5) /* 500ms x5 */ ++ ++#define MAX_DMA_CNT (64 * 1024 - 512) /* a single transaction for WIFI may be 50K*/ ++ ++#define MAX_GPD_NUM (1 + 1) /* one null gpd */ ++#define MAX_BD_NUM (1024) ++#define MAX_BD_PER_GPD (MAX_BD_NUM) ++ ++#define MAX_HW_SGMTS (MAX_BD_NUM) ++#define MAX_PHY_SGMTS (MAX_BD_NUM) ++#define MAX_SGMT_SZ (MAX_DMA_CNT) ++#define MAX_REQ_SZ (MAX_SGMT_SZ * 8) ++ ++#ifdef MT6575_SD_DEBUG ++static struct msdc_regs *msdc_reg[HOST_MAX_NUM]; ++#endif ++ ++//================================= ++#define PERI_MSDC0_PDN (15) ++//#define PERI_MSDC1_PDN (16) ++//#define PERI_MSDC2_PDN (17) ++//#define PERI_MSDC3_PDN (18) ++ ++struct msdc_host *msdc_6575_host[] = {NULL,NULL,NULL,NULL}; ++ ++struct msdc_hw msdc0_hw = { ++ .clk_src = 0, ++ .cmd_edge = MSDC_SMPL_FALLING, ++ .data_edge = MSDC_SMPL_FALLING, ++ .clk_drv = 4, ++ .cmd_drv = 4, ++ .dat_drv = 4, ++ .data_pins = 4, ++ .data_offset = 0, ++ .flags = MSDC_SYS_SUSPEND | MSDC_WP_PIN_EN | MSDC_CD_PIN_EN | MSDC_REMOVABLE | MSDC_HIGHSPEED, ++}; ++ ++static struct resource mtk_sd_resources[] = { ++ [0] = { ++ .start = 0xb0130000, ++ .end = 0xb0133fff, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_SDC, /*FIXME*/ ++ .end = IRQ_SDC, /*FIXME*/ ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct platform_device mtk_sd_device = { ++ .name = "mtk-sd", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(mtk_sd_resources), ++ .resource = mtk_sd_resources, ++}; ++/* end of +++ */ ++ ++static int msdc_rsp[] = { ++ 0, /* RESP_NONE */ ++ 1, /* RESP_R1 */ ++ 2, /* RESP_R2 */ ++ 3, /* RESP_R3 */ ++ 4, /* RESP_R4 */ ++ 1, /* RESP_R5 */ ++ 1, /* RESP_R6 */ ++ 1, /* RESP_R7 */ ++ 7, /* RESP_R1b */ ++}; ++ ++/* For Inhanced DMA */ ++#define msdc_init_gpd_ex(gpd,extlen,cmd,arg,blknum) \ ++ do { \ ++ ((gpd_t*)gpd)->extlen = extlen; \ ++ ((gpd_t*)gpd)->cmd = cmd; \ ++ ((gpd_t*)gpd)->arg = arg; \ ++ ((gpd_t*)gpd)->blknum = blknum; \ ++ }while(0) ++ ++#define msdc_init_bd(bd, blkpad, dwpad, dptr, dlen) \ ++ do { \ ++ BUG_ON(dlen > 0xFFFFUL); \ ++ ((bd_t*)bd)->blkpad = blkpad; \ ++ ((bd_t*)bd)->dwpad = dwpad; \ ++ ((bd_t*)bd)->ptr = (void*)dptr; \ ++ ((bd_t*)bd)->buflen = dlen; \ ++ }while(0) ++ ++#define msdc_txfifocnt() ((sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_TXCNT) >> 16) ++#define msdc_rxfifocnt() ((sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_RXCNT) >> 0) ++#define msdc_fifo_write32(v) sdr_write32(MSDC_TXDATA, (v)) ++#define msdc_fifo_write8(v) sdr_write8(MSDC_TXDATA, (v)) ++#define msdc_fifo_read32() sdr_read32(MSDC_RXDATA) ++#define msdc_fifo_read8() sdr_read8(MSDC_RXDATA) ++ ++ ++#define msdc_dma_on() sdr_clr_bits(MSDC_CFG, MSDC_CFG_PIO) ++#define msdc_dma_off() sdr_set_bits(MSDC_CFG, MSDC_CFG_PIO) ++ ++#define msdc_retry(expr,retry,cnt) \ ++ do { \ ++ int backup = cnt; \ ++ while (retry) { \ ++ if (!(expr)) break; \ ++ if (cnt-- == 0) { \ ++ retry--; mdelay(1); cnt = backup; \ ++ } \ ++ } \ ++ WARN_ON(retry == 0); \ ++ } while(0) ++ ++#if 0 /* +/- chhung */ ++#define msdc_reset() \ ++ do { \ ++ int retry = 3, cnt = 1000; \ ++ sdr_set_bits(MSDC_CFG, MSDC_CFG_RST); \ ++ dsb(); \ ++ msdc_retry(sdr_read32(MSDC_CFG) & MSDC_CFG_RST, retry, cnt); \ ++ } while(0) ++#else ++#define msdc_reset() \ ++ do { \ ++ int retry = 3, cnt = 1000; \ ++ sdr_set_bits(MSDC_CFG, MSDC_CFG_RST); \ ++ msdc_retry(sdr_read32(MSDC_CFG) & MSDC_CFG_RST, retry, cnt); \ ++ } while(0) ++#endif /* end of +/- */ ++ ++#define msdc_clr_int() \ ++ do { \ ++ volatile u32 val = sdr_read32(MSDC_INT); \ ++ sdr_write32(MSDC_INT, val); \ ++ } while(0) ++ ++#define msdc_clr_fifo() \ ++ do { \ ++ int retry = 3, cnt = 1000; \ ++ sdr_set_bits(MSDC_FIFOCS, MSDC_FIFOCS_CLR); \ ++ msdc_retry(sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_CLR, retry, cnt); \ ++ } while(0) ++ ++#define msdc_irq_save(val) \ ++ do { \ ++ val = sdr_read32(MSDC_INTEN); \ ++ sdr_clr_bits(MSDC_INTEN, val); \ ++ } while(0) ++ ++#define msdc_irq_restore(val) \ ++ do { \ ++ sdr_set_bits(MSDC_INTEN, val); \ ++ } while(0) ++ ++/* clock source for host: global */ ++static u32 hclks[] = {48000000}; /* +/- by chhung */ ++ ++//============================================ ++// the power for msdc host controller: global ++// always keep the VMC on. ++//============================================ ++#define msdc_vcore_on(host) \ ++ do { \ ++ printk("[+]VMC ref. count<%d>\n", ++host->pwr_ref); \ ++ (void)hwPowerOn(MT65XX_POWER_LDO_VMC, VOL_3300, "SD"); \ ++ } while (0) ++#define msdc_vcore_off(host) \ ++ do { \ ++ printk("[-]VMC ref. count<%d>\n", --host->pwr_ref); \ ++ (void)hwPowerDown(MT65XX_POWER_LDO_VMC, "SD"); \ ++ } while (0) ++ ++//==================================== ++// the vdd output for card: global ++// always keep the VMCH on. ++//==================================== ++#define msdc_vdd_on(host) \ ++ do { \ ++ (void)hwPowerOn(MT65XX_POWER_LDO_VMCH, VOL_3300, "SD"); \ ++ } while (0) ++#define msdc_vdd_off(host) \ ++ do { \ ++ (void)hwPowerDown(MT65XX_POWER_LDO_VMCH, "SD"); \ ++ } while (0) ++ ++#define sdc_is_busy() (sdr_read32(SDC_STS) & SDC_STS_SDCBUSY) ++#define sdc_is_cmd_busy() (sdr_read32(SDC_STS) & SDC_STS_CMDBUSY) ++ ++#define sdc_send_cmd(cmd,arg) \ ++ do { \ ++ sdr_write32(SDC_ARG, (arg)); \ ++ sdr_write32(SDC_CMD, (cmd)); \ ++ } while(0) ++ ++// can modify to read h/w register. ++//#define is_card_present(h) ((sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 0 : 1); ++#define is_card_present(h) (((struct msdc_host*)(h))->card_inserted) ++ ++/* +++ chhung */ ++#ifndef __ASSEMBLY__ ++#define PHYSADDR(a) (((unsigned long)(a)) & 0x1fffffff) ++#else ++#define PHYSADDR(a) ((a) & 0x1fffffff) ++#endif ++/* end of +++ */ ++static unsigned int msdc_do_command(struct msdc_host *host, ++ struct mmc_command *cmd, ++ int tune, ++ unsigned long timeout); ++ ++static int msdc_tune_cmdrsp(struct msdc_host*host,struct mmc_command *cmd); ++ ++#ifdef MT6575_SD_DEBUG ++static void msdc_dump_card_status(struct msdc_host *host, u32 status) ++{ ++ static char *state[] = { ++ "Idle", /* 0 */ ++ "Ready", /* 1 */ ++ "Ident", /* 2 */ ++ "Stby", /* 3 */ ++ "Tran", /* 4 */ ++ "Data", /* 5 */ ++ "Rcv", /* 6 */ ++ "Prg", /* 7 */ ++ "Dis", /* 8 */ ++ "Reserved", /* 9 */ ++ "Reserved", /* 10 */ ++ "Reserved", /* 11 */ ++ "Reserved", /* 12 */ ++ "Reserved", /* 13 */ ++ "Reserved", /* 14 */ ++ "I/O mode", /* 15 */ ++ }; ++ if (status & R1_OUT_OF_RANGE) ++ printk("[CARD_STATUS] Out of Range\n"); ++ if (status & R1_ADDRESS_ERROR) ++ printk("[CARD_STATUS] Address Error\n"); ++ if (status & R1_BLOCK_LEN_ERROR) ++ printk("[CARD_STATUS] Block Len Error\n"); ++ if (status & R1_ERASE_SEQ_ERROR) ++ printk("[CARD_STATUS] Erase Seq Error\n"); ++ if (status & R1_ERASE_PARAM) ++ printk("[CARD_STATUS] Erase Param\n"); ++ if (status & R1_WP_VIOLATION) ++ printk("[CARD_STATUS] WP Violation\n"); ++ if (status & R1_CARD_IS_LOCKED) ++ printk("[CARD_STATUS] Card is Locked\n"); ++ if (status & R1_LOCK_UNLOCK_FAILED) ++ printk("[CARD_STATUS] Lock/Unlock Failed\n"); ++ if (status & R1_COM_CRC_ERROR) ++ printk("[CARD_STATUS] Command CRC Error\n"); ++ if (status & R1_ILLEGAL_COMMAND) ++ printk("[CARD_STATUS] Illegal Command\n"); ++ if (status & R1_CARD_ECC_FAILED) ++ printk("[CARD_STATUS] Card ECC Failed\n"); ++ if (status & R1_CC_ERROR) ++ printk("[CARD_STATUS] CC Error\n"); ++ if (status & R1_ERROR) ++ printk("[CARD_STATUS] Error\n"); ++ if (status & R1_UNDERRUN) ++ printk("[CARD_STATUS] Underrun\n"); ++ if (status & R1_OVERRUN) ++ printk("[CARD_STATUS] Overrun\n"); ++ if (status & R1_CID_CSD_OVERWRITE) ++ printk("[CARD_STATUS] CID/CSD Overwrite\n"); ++ if (status & R1_WP_ERASE_SKIP) ++ printk("[CARD_STATUS] WP Eraser Skip\n"); ++ if (status & R1_CARD_ECC_DISABLED) ++ printk("[CARD_STATUS] Card ECC Disabled\n"); ++ if (status & R1_ERASE_RESET) ++ printk("[CARD_STATUS] Erase Reset\n"); ++ if (status & R1_READY_FOR_DATA) ++ printk("[CARD_STATUS] Ready for Data\n"); ++ if (status & R1_SWITCH_ERROR) ++ printk("[CARD_STATUS] Switch error\n"); ++ if (status & R1_APP_CMD) ++ printk("[CARD_STATUS] App Command\n"); ++ ++ printk("[CARD_STATUS] '%s' State\n", state[R1_CURRENT_STATE(status)]); ++} ++ ++static void msdc_dump_ocr_reg(struct msdc_host *host, u32 resp) ++{ ++ if (resp & (1 << 7)) ++ printk("[OCR] Low Voltage Range\n"); ++ if (resp & (1 << 15)) ++ printk("[OCR] 2.7-2.8 volt\n"); ++ if (resp & (1 << 16)) ++ printk("[OCR] 2.8-2.9 volt\n"); ++ if (resp & (1 << 17)) ++ printk("[OCR] 2.9-3.0 volt\n"); ++ if (resp & (1 << 18)) ++ printk("[OCR] 3.0-3.1 volt\n"); ++ if (resp & (1 << 19)) ++ printk("[OCR] 3.1-3.2 volt\n"); ++ if (resp & (1 << 20)) ++ printk("[OCR] 3.2-3.3 volt\n"); ++ if (resp & (1 << 21)) ++ printk("[OCR] 3.3-3.4 volt\n"); ++ if (resp & (1 << 22)) ++ printk("[OCR] 3.4-3.5 volt\n"); ++ if (resp & (1 << 23)) ++ printk("[OCR] 3.5-3.6 volt\n"); ++ if (resp & (1 << 24)) ++ printk("[OCR] Switching to 1.8V Accepted (S18A)\n"); ++ if (resp & (1 << 30)) ++ printk("[OCR] Card Capacity Status (CCS)\n"); ++ if (resp & (1 << 31)) ++ printk("[OCR] Card Power Up Status (Idle)\n"); ++ else ++ printk("[OCR] Card Power Up Status (Busy)\n"); ++} ++ ++static void msdc_dump_rca_resp(struct msdc_host *host, u32 resp) ++{ ++ u32 status = (((resp >> 15) & 0x1) << 23) | ++ (((resp >> 14) & 0x1) << 22) | ++ (((resp >> 13) & 0x1) << 19) | ++ (resp & 0x1fff); ++ ++ printk("[RCA] 0x%.4x\n", resp >> 16); ++ ++ msdc_dump_card_status(host, status); ++} ++ ++static void msdc_dump_io_resp(struct msdc_host *host, u32 resp) ++{ ++ u32 flags = (resp >> 8) & 0xFF; ++ char *state[] = {"DIS", "CMD", "TRN", "RFU"}; ++ ++ if (flags & (1 << 7)) ++ printk("[IO] COM_CRC_ERR\n"); ++ if (flags & (1 << 6)) ++ printk("[IO] Illgal command\n"); ++ if (flags & (1 << 3)) ++ printk("[IO] Error\n"); ++ if (flags & (1 << 2)) ++ printk("[IO] RFU\n"); ++ if (flags & (1 << 1)) ++ printk("[IO] Function number error\n"); ++ if (flags & (1 << 0)) ++ printk("[IO] Out of range\n"); ++ ++ printk("[IO] State: %s, Data:0x%x\n", state[(resp >> 12) & 0x3], resp & 0xFF); ++} ++#endif ++ ++static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks) ++{ ++ u32 base = host->base; ++ u32 timeout, clk_ns; ++ ++ host->timeout_ns = ns; ++ host->timeout_clks = clks; ++ ++ clk_ns = 1000000000UL / host->sclk; ++ timeout = ns / clk_ns + clks; ++ timeout = timeout >> 16; /* in 65536 sclk cycle unit */ ++ timeout = timeout > 1 ? timeout - 1 : 0; ++ timeout = timeout > 255 ? 255 : timeout; ++ ++ sdr_set_field(SDC_CFG, SDC_CFG_DTOC, timeout); ++ ++/* printk("Set read data timeout: %dns %dclks -> %d x 65536 cycles\n", ++ ns, clks, timeout + 1);*/ ++} ++ ++static void msdc_eirq_sdio(void *data) ++{ ++ struct msdc_host *host = (struct msdc_host *)data; ++ ++// printk("SDIO EINT\n"); ++ ++ mmc_signal_sdio_irq(host->mmc); ++} ++ ++static void msdc_eirq_cd(void *data) ++{ ++ struct msdc_host *host = (struct msdc_host *)data; ++ ++// printk("CD EINT\n"); ++ ++ tasklet_hi_schedule(&host->card_tasklet); ++} ++ ++static void msdc_tasklet_card(unsigned long arg) ++{ ++ struct msdc_host *host = (struct msdc_host *)arg; ++ struct msdc_hw *hw = host->hw; ++ u32 base = host->base; ++ u32 inserted; ++ u32 status = 0; ++ ++ spin_lock(&host->lock); ++ ++ if (hw->get_cd_status) { ++ inserted = hw->get_cd_status(); ++ } else { ++ status = sdr_read32(MSDC_PS); ++ inserted = (status & MSDC_PS_CDSTS) ? 0 : 1; ++ } ++ ++ host->card_inserted = inserted; ++ ++ if (!host->suspend) { ++ host->mmc->f_max = HOST_MAX_MCLK; ++ mmc_detect_change(host->mmc, msecs_to_jiffies(20)); ++ } ++ ++// printk("card found<%s>\n", inserted ? "inserted" : "removed"); ++ ++ spin_unlock(&host->lock); ++} ++ ++static void msdc_set_mclk(struct msdc_host *host, int ddr, unsigned int hz) ++{ ++ u32 base = host->base; ++ u32 hclk = host->hclk; ++ u32 mode, flags, div, sclk; ++ ++ if (!hz) { ++// printk("set mclk to 0!!!\n"); ++ msdc_reset(); ++ return; ++ } ++ ++ msdc_irq_save(flags); ++ ++ if (ddr) { ++ mode = 0x2; ++ if (hz >= (hclk >> 2)) { ++ div = 1; ++ sclk = hclk >> 2; ++ } else { ++ div = (hclk + ((hz << 2) - 1)) / (hz << 2); ++ sclk = (hclk >> 2) / div; ++ } ++ } else if (hz >= hclk) { ++ mode = 0x1; ++ div = 0; ++ sclk = hclk; ++ } else { ++ mode = 0x0; ++ if (hz >= (hclk >> 1)) { ++ div = 0; ++ sclk = hclk >> 1; ++ } else { ++ div = (hclk + ((hz << 2) - 1)) / (hz << 2); ++ sclk = (hclk >> 2) / div; ++ } ++ } ++ ++ sdr_set_field(MSDC_CFG, MSDC_CFG_CKMOD, mode); ++ sdr_set_field(MSDC_CFG, MSDC_CFG_CKDIV, div); ++ ++ while (!(sdr_read32(MSDC_CFG) & MSDC_CFG_CKSTB)); ++ ++ host->sclk = sclk; ++ host->mclk = hz; ++ msdc_set_timeout(host, host->timeout_ns, host->timeout_clks); ++ ++/* printk("!!! Set<%dKHz> Source<%dKHz> -> sclk<%dKHz>\n", ++ hz / 1000, hclk / 1000, sclk / 1000); ++*/ ++ msdc_irq_restore(flags); ++} ++ ++static void msdc_abort_data(struct msdc_host *host) ++{ ++ u32 base = host->base; ++ struct mmc_command *stop = host->mrq->stop; ++ ++// printk("Need to Abort. dma<%d>\n", host->dma_xfer); ++ ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ ++ if (stop) { ++// printk("stop when abort CMD<%d>\n", stop->opcode); ++ msdc_do_command(host, stop, 0, CMD_TIMEOUT); ++ } ++} ++ ++static unsigned int msdc_command_start(struct msdc_host *host, ++ struct mmc_command *cmd, int tune, unsigned long timeout) ++{ ++ u32 wints = MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO | ++ MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO | ++ MSDC_INT_ACMD19_DONE; ++ u32 base = host->base; ++ u32 opcode = cmd->opcode; ++ u32 rawcmd; ++ u32 resp; ++ unsigned long tmo; ++ ++ if (opcode == MMC_SEND_OP_COND || opcode == SD_APP_OP_COND) ++ resp = RESP_R3; ++ else if (opcode == MMC_SET_RELATIVE_ADDR || opcode == SD_SEND_RELATIVE_ADDR) ++ resp = (mmc_cmd_type(cmd) == MMC_CMD_BCR) ? RESP_R6 : RESP_R1; ++ else if (opcode == MMC_FAST_IO) ++ resp = RESP_R4; ++ else if (opcode == MMC_GO_IRQ_STATE) ++ resp = RESP_R5; ++ else if (opcode == MMC_SELECT_CARD) ++ resp = (cmd->arg != 0) ? RESP_R1B : RESP_NONE; ++ else if (opcode == SD_IO_RW_DIRECT || opcode == SD_IO_RW_EXTENDED) ++ resp = RESP_R1; ++ else if (opcode == SD_SEND_IF_COND && (mmc_cmd_type(cmd) == MMC_CMD_BCR)) ++ resp = RESP_R1; ++ else { ++ switch (mmc_resp_type(cmd)) { ++ case MMC_RSP_R1: ++ resp = RESP_R1; ++ break; ++ case MMC_RSP_R1B: ++ resp = RESP_R1B; ++ break; ++ case MMC_RSP_R2: ++ resp = RESP_R2; ++ break; ++ case MMC_RSP_R3: ++ resp = RESP_R3; ++ break; ++ case MMC_RSP_NONE: ++ default: ++ resp = RESP_NONE; ++ break; ++ } ++ } ++ ++ cmd->error = 0; ++ rawcmd = opcode | msdc_rsp[resp] << 7 | host->blksz << 16; ++ ++ if (opcode == MMC_READ_MULTIPLE_BLOCK) { ++ rawcmd |= (2 << 11); ++ } else if (opcode == MMC_READ_SINGLE_BLOCK) { ++ rawcmd |= (1 << 11); ++ } else if (opcode == MMC_WRITE_MULTIPLE_BLOCK) { ++ rawcmd |= ((2 << 11) | (1 << 13)); ++ } else if (opcode == MMC_WRITE_BLOCK) { ++ rawcmd |= ((1 << 11) | (1 << 13)); ++ } else if (opcode == SD_IO_RW_EXTENDED) { ++ if (cmd->data->flags & MMC_DATA_WRITE) ++ rawcmd |= (1 << 13); ++ if (cmd->data->blocks > 1) ++ rawcmd |= (2 << 11); ++ else ++ rawcmd |= (1 << 11); ++ } else if (opcode == SD_IO_RW_DIRECT && cmd->flags == (unsigned int)-1) { ++ rawcmd |= (1 << 14); ++ } else if ((opcode == SD_APP_SEND_SCR) || ++ (opcode == SD_APP_SEND_NUM_WR_BLKS) || ++ (opcode == SD_SWITCH && (mmc_cmd_type(cmd) == MMC_CMD_ADTC)) || ++ (opcode == SD_APP_SD_STATUS && (mmc_cmd_type(cmd) == MMC_CMD_ADTC)) || ++ (opcode == MMC_SEND_EXT_CSD && (mmc_cmd_type(cmd) == MMC_CMD_ADTC))) { ++ rawcmd |= (1 << 11); ++ } else if (opcode == MMC_STOP_TRANSMISSION) { ++ rawcmd |= (1 << 14); ++ rawcmd &= ~(0x0FFF << 16); ++ } ++ ++// printk("CMD<%d><0x%.8x> Arg<0x%.8x>\n", opcode , rawcmd, cmd->arg); ++ ++ tmo = jiffies + timeout; ++ ++ if (opcode == MMC_SEND_STATUS) { ++ for (;;) { ++ if (!sdc_is_cmd_busy()) ++ break; ++ ++ if (time_after(jiffies, tmo)) { ++ //printk("XXX cmd_busy timeout: before CMD<%d>\n", opcode); ++ cmd->error = (unsigned int)-ETIMEDOUT; ++ msdc_reset(); ++ goto end; ++ } ++ } ++ } else { ++ for (;;) { ++ if (!sdc_is_busy()) ++ break; ++ if (time_after(jiffies, tmo)) { ++ //printk("XXX sdc_busy timeout: before CMD<%d>\n", opcode); ++ cmd->error = (unsigned int)-ETIMEDOUT; ++ msdc_reset(); ++ goto end; ++ } ++ } ++ } ++ ++ //BUG_ON(in_interrupt()); ++ host->cmd = cmd; ++ host->cmd_rsp = resp; ++ init_completion(&host->cmd_done); ++ sdr_set_bits(MSDC_INTEN, wints); ++ sdc_send_cmd(rawcmd, cmd->arg); ++ ++end: ++ return cmd->error; ++} ++ ++static unsigned int msdc_command_resp(struct msdc_host *host, struct mmc_command *cmd, ++ int tune, unsigned long timeout) ++{ ++ u32 base = host->base; ++ //u32 opcode = cmd->opcode; ++ u32 resp; ++ u32 wints = MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO | ++ MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO | ++ MSDC_INT_ACMD19_DONE; ++ ++ resp = host->cmd_rsp; ++ ++ BUG_ON(in_interrupt()); ++ spin_unlock(&host->lock); ++ if (!wait_for_completion_timeout(&host->cmd_done, 10*timeout)) { ++ //printk("XXX CMD<%d> wait_for_completion timeout ARG<0x%.8x>\n", opcode, cmd->arg); ++ cmd->error = (unsigned int)-ETIMEDOUT; ++ msdc_reset(); ++ } ++ spin_lock(&host->lock); ++ ++ sdr_clr_bits(MSDC_INTEN, wints); ++ host->cmd = NULL; ++ ++ if (!tune) ++ return cmd->error; ++ ++ /* memory card CRC */ ++ if (host->hw->flags & MSDC_REMOVABLE && cmd->error == (unsigned int)(-EIO) ) { ++ if (sdr_read32(SDC_CMD) & 0x1800) { ++ msdc_abort_data(host); ++ } else { ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ } ++ cmd->error = msdc_tune_cmdrsp(host,cmd); ++ } ++ ++ return cmd->error; ++} ++ ++static unsigned int msdc_do_command(struct msdc_host *host, struct mmc_command *cmd, ++ int tune, unsigned long timeout) ++{ ++ if (!msdc_command_start(host, cmd, tune, timeout)) ++ msdc_command_resp(host, cmd, tune, timeout); ++ ++ //printk(" return<%d> resp<0x%.8x>\n", cmd->error, cmd->resp[0]); ++ return cmd->error; ++} ++ ++static int msdc_pio_abort(struct msdc_host *host, struct mmc_data *data, unsigned long tmo) ++{ ++ u32 base = host->base; ++ int ret = 0; ++ ++ if (atomic_read(&host->abort)) ++ ret = 1; ++ ++ if (time_after(jiffies, tmo)) { ++ data->error = (unsigned int)-ETIMEDOUT; ++ //printk("XXX PIO Data Timeout: CMD<%d>\n", host->mrq->cmd->opcode); ++ ret = 1; ++ } ++ ++ if (ret) { ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ //printk("msdc pio find abort\n"); ++ } ++ ++ return ret; ++} ++ ++static int msdc_pio_read(struct msdc_host *host, struct mmc_data *data) ++{ ++ struct scatterlist *sg = data->sg; ++ u32 base = host->base; ++ u32 num = data->sg_len; ++ u32 *ptr; ++ u8 *u8ptr; ++ u32 left; ++ u32 count, size = 0; ++ u32 wints = MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR; ++ unsigned long tmo = jiffies + DAT_TIMEOUT; ++ ++ sdr_set_bits(MSDC_INTEN, wints); ++ while (num) { ++ left = sg_dma_len(sg); ++ ptr = sg_virt(sg); ++ while (left) { ++ if ((left >= MSDC_FIFO_THD) && (msdc_rxfifocnt() >= MSDC_FIFO_THD)) { ++ count = MSDC_FIFO_THD >> 2; ++ do { ++ *ptr++ = msdc_fifo_read32(); ++ } while (--count); ++ left -= MSDC_FIFO_THD; ++ } else if ((left < MSDC_FIFO_THD) && msdc_rxfifocnt() >= left) { ++ while (left > 3) { ++ *ptr++ = msdc_fifo_read32(); ++ left -= 4; ++ } ++ ++ u8ptr = (u8 *)ptr; ++ while(left) { ++ * u8ptr++ = msdc_fifo_read8(); ++ left--; ++ } ++ } ++ ++ if (msdc_pio_abort(host, data, tmo)) ++ goto end; ++ } ++ size += sg_dma_len(sg); ++ sg = sg_next(sg); num--; ++ } ++end: ++ data->bytes_xfered += size; ++ //printk(" PIO Read<%d>bytes\n", size); ++ ++ sdr_clr_bits(MSDC_INTEN, wints); ++ if(data->error) ++ printk("read pio data->error<%d> left<%d> size<%d>\n", data->error, left, size); ++ ++ return data->error; ++} ++ ++static int msdc_pio_write(struct msdc_host* host, struct mmc_data *data) ++{ ++ u32 base = host->base; ++ struct scatterlist *sg = data->sg; ++ u32 num = data->sg_len; ++ u32 *ptr; ++ u8 *u8ptr; ++ u32 left; ++ u32 count, size = 0; ++ u32 wints = MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR; ++ unsigned long tmo = jiffies + DAT_TIMEOUT; ++ ++ sdr_set_bits(MSDC_INTEN, wints); ++ while (num) { ++ left = sg_dma_len(sg); ++ ptr = sg_virt(sg); ++ ++ while (left) { ++ if (left >= MSDC_FIFO_SZ && msdc_txfifocnt() == 0) { ++ count = MSDC_FIFO_SZ >> 2; ++ do { ++ msdc_fifo_write32(*ptr); ptr++; ++ } while (--count); ++ left -= MSDC_FIFO_SZ; ++ } else if (left < MSDC_FIFO_SZ && msdc_txfifocnt() == 0) { ++ while (left > 3) { ++ msdc_fifo_write32(*ptr); ptr++; ++ left -= 4; ++ } ++ ++ u8ptr = (u8*)ptr; ++ while( left) { ++ msdc_fifo_write8(*u8ptr); ++ u8ptr++; ++ left--; ++ } ++ } ++ ++ if (msdc_pio_abort(host, data, tmo)) ++ goto end; ++ } ++ size += sg_dma_len(sg); ++ sg = sg_next(sg); num--; ++ } ++end: ++ data->bytes_xfered += size; ++ //printk(" PIO Write<%d>bytes\n", size); ++ if(data->error) ++ printk("write pio data->error<%d>\n", data->error); ++ ++ sdr_clr_bits(MSDC_INTEN, wints); ++ ++ return data->error; ++} ++ ++static void msdc_dma_start(struct msdc_host *host) ++{ ++ u32 base = host->base; ++ u32 wints = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR; ++ ++ sdr_set_bits(MSDC_INTEN, wints); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1); ++ ++ //printk("DMA start\n"); ++} ++ ++static void msdc_dma_stop(struct msdc_host *host) ++{ ++ u32 base = host->base; ++ u32 wints = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR; ++ ++ //printk("DMA status: 0x%.8x\n",sdr_read32(MSDC_DMA_CFG)); ++ ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP, 1); ++ while (sdr_read32(MSDC_DMA_CFG) & MSDC_DMA_CFG_STS); ++ sdr_clr_bits(MSDC_INTEN, wints); /* Not just xfer_comp */ ++ ++ //printk("DMA stop\n"); ++} ++ ++static u8 msdc_dma_calcs(u8 *buf, u32 len) ++{ ++ u32 i, sum = 0; ++ ++ for (i = 0; i < len; i++) ++ sum += buf[i]; ++ ++ return 0xFF - (u8)sum; ++} ++ ++static int msdc_dma_config(struct msdc_host *host, struct msdc_dma *dma) ++{ ++ u32 base = host->base; ++ u32 sglen = dma->sglen; ++ u32 j, num, bdlen; ++ u8 blkpad, dwpad, chksum; ++ struct scatterlist *sg = dma->sg; ++ gpd_t *gpd; ++ bd_t *bd; ++ ++ switch (dma->mode) { ++ case MSDC_MODE_DMA_BASIC: ++ BUG_ON(dma->xfersz > 65535); ++ BUG_ON(dma->sglen != 1); ++ sdr_write32(MSDC_DMA_SA, PHYSADDR(sg_dma_address(sg))); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_LASTBUF, 1); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_XFERSZ, sg_dma_len(sg)); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_BRUSTSZ, dma->burstsz); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, 0); ++ break; ++ ++ case MSDC_MODE_DMA_DESC: ++ blkpad = (dma->flags & DMA_FLAG_PAD_BLOCK) ? 1 : 0; ++ dwpad = (dma->flags & DMA_FLAG_PAD_DWORD) ? 1 : 0; ++ chksum = (dma->flags & DMA_FLAG_EN_CHKSUM) ? 1 : 0; ++ ++ num = (sglen + MAX_BD_PER_GPD - 1) / MAX_BD_PER_GPD; ++ BUG_ON(num !=1 ); ++ ++ gpd = dma->gpd; ++ bd = dma->bd; ++ bdlen = sglen; ++ ++ gpd->hwo = 1; /* hw will clear it */ ++ gpd->bdp = 1; ++ gpd->chksum = 0; /* need to clear first. */ ++ gpd->chksum = (chksum ? msdc_dma_calcs((u8 *)gpd, 16) : 0); ++ ++ for (j = 0; j < bdlen; j++) { ++ msdc_init_bd(&bd[j], blkpad, dwpad, sg_dma_address(sg), sg_dma_len(sg)); ++ if( j == bdlen - 1) ++ bd[j].eol = 1; ++ else ++ bd[j].eol = 0; ++ bd[j].chksum = 0; /* checksume need to clear first */ ++ bd[j].chksum = (chksum ? msdc_dma_calcs((u8 *)(&bd[j]), 16) : 0); ++ sg++; ++ } ++ ++ dma->used_gpd += 2; ++ dma->used_bd += bdlen; ++ ++ sdr_set_field(MSDC_DMA_CFG, MSDC_DMA_CFG_DECSEN, chksum); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_BRUSTSZ, dma->burstsz); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, 1); ++ sdr_write32(MSDC_DMA_SA, PHYSADDR((u32)dma->gpd_addr)); ++ break; ++ } ++ ++// printk("DMA_CTRL = 0x%x\n", sdr_read32(MSDC_DMA_CTRL)); ++// printk("DMA_CFG = 0x%x\n", sdr_read32(MSDC_DMA_CFG)); ++// printk("DMA_SA = 0x%x\n", sdr_read32(MSDC_DMA_SA)); ++ ++ return 0; ++} ++ ++static void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma, ++ struct scatterlist *sg, unsigned int sglen) ++{ ++ BUG_ON(sglen > MAX_BD_NUM); ++ ++ dma->sg = sg; ++ dma->flags = DMA_FLAG_EN_CHKSUM; ++ dma->sglen = sglen; ++ dma->xfersz = host->xfer_size; ++ dma->burstsz = MSDC_BRUST_64B; ++ ++ if (sglen == 1 && sg_dma_len(sg) <= MAX_DMA_CNT) ++ dma->mode = MSDC_MODE_DMA_BASIC; ++ else ++ dma->mode = MSDC_MODE_DMA_DESC; ++ ++// printk("DMA mode<%d> sglen<%d> xfersz<%d>\n", dma->mode, dma->sglen, dma->xfersz); ++ ++ msdc_dma_config(host, dma); ++} ++ ++static void msdc_set_blknum(struct msdc_host *host, u32 blknum) ++{ ++ u32 base = host->base; ++ ++ sdr_write32(SDC_BLK_NUM, blknum); ++} ++ ++static int msdc_do_request(struct mmc_host*mmc, struct mmc_request*mrq) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ struct mmc_command *cmd; ++ struct mmc_data *data; ++ u32 base = host->base; ++ unsigned int left=0; ++ int dma = 0, read = 1, dir = DMA_FROM_DEVICE, send_type=0; ++ ++#define SND_DAT 0 ++#define SND_CMD 1 ++ ++ BUG_ON(mmc == NULL); ++ BUG_ON(mrq == NULL); ++ ++ host->error = 0; ++ atomic_set(&host->abort, 0); ++ ++ cmd = mrq->cmd; ++ data = mrq->cmd->data; ++ ++ if (!data) { ++ send_type = SND_CMD; ++ if (msdc_do_command(host, cmd, 1, CMD_TIMEOUT) != 0) ++ goto done; ++ } else { ++ BUG_ON(data->blksz > HOST_MAX_BLKSZ); ++ send_type=SND_DAT; ++ ++ data->error = 0; ++ read = data->flags & MMC_DATA_READ ? 1 : 0; ++ host->data = data; ++ host->xfer_size = data->blocks * data->blksz; ++ host->blksz = data->blksz; ++ ++ host->dma_xfer = dma = ((host->xfer_size >= 512) ? 1 : 0); ++ ++ if (read) ++ if ((host->timeout_ns != data->timeout_ns) || ++ (host->timeout_clks != data->timeout_clks)) ++ msdc_set_timeout(host, data->timeout_ns, data->timeout_clks); ++ ++ msdc_set_blknum(host, data->blocks); ++ ++ if (dma) { ++ msdc_dma_on(); ++ init_completion(&host->xfer_done); ++ ++ if (msdc_command_start(host, cmd, 1, CMD_TIMEOUT) != 0) ++ goto done; ++ ++ dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; ++ dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, dir); ++ msdc_dma_setup(host, &host->dma, data->sg, data->sg_len); ++ ++ if (msdc_command_resp(host, cmd, 1, CMD_TIMEOUT) != 0) ++ goto done; ++ ++ msdc_dma_start(host); ++ ++ spin_unlock(&host->lock); ++ if (!wait_for_completion_timeout(&host->xfer_done, DAT_TIMEOUT)) { ++ /*printk("XXX CMD<%d> wait xfer_done<%d> timeout!!\n", cmd->opcode, data->blocks * data->blksz); ++ printk(" DMA_SA = 0x%x\n", sdr_read32(MSDC_DMA_SA)); ++ printk(" DMA_CA = 0x%x\n", sdr_read32(MSDC_DMA_CA)); ++ printk(" DMA_CTRL = 0x%x\n", sdr_read32(MSDC_DMA_CTRL)); ++ printk(" DMA_CFG = 0x%x\n", sdr_read32(MSDC_DMA_CFG));*/ ++ data->error = (unsigned int)-ETIMEDOUT; ++ ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ } ++ spin_lock(&host->lock); ++ msdc_dma_stop(host); ++ } else { ++ if (msdc_do_command(host, cmd, 1, CMD_TIMEOUT) != 0) ++ goto done; ++ ++ if (read) { ++ if (msdc_pio_read(host, data)) ++ goto done; ++ } else { ++ if (msdc_pio_write(host, data)) ++ goto done; ++ } ++ ++ if (!read) { ++ while (1) { ++ left = msdc_txfifocnt(); ++ if (left == 0) { ++ break; ++ } ++ if (msdc_pio_abort(host, data, jiffies + DAT_TIMEOUT)) { ++ break; ++ /* Fix me: what about if data error, when stop ? how to? */ ++ } ++ } ++ } else { ++ /* Fix me: read case: need to check CRC error */ ++ } ++ ++ /* For write case: SDCBUSY and Xfer_Comp will assert when DAT0 not busy. ++ For read case : SDCBUSY and Xfer_Comp will assert when last byte read out from FIFO. ++ */ ++ ++ /* try not to wait xfer_comp interrupt. ++ the next command will check SDC_BUSY. ++ SDC_BUSY means xfer_comp assert ++ */ ++ ++ } // PIO mode ++ ++ /* Last: stop transfer */ ++ if (data->stop){ ++ if (msdc_do_command(host, data->stop, 0, CMD_TIMEOUT) != 0) { ++ goto done; ++ } ++ } ++ } ++ ++done: ++ if (data != NULL) { ++ host->data = NULL; ++ host->dma_xfer = 0; ++ if (dma != 0) { ++ msdc_dma_off(); ++ host->dma.used_bd = 0; ++ host->dma.used_gpd = 0; ++ dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, dir); ++ } ++ host->blksz = 0; ++ ++ // printk("CMD<%d> data<%s %s> blksz<%d> block<%d> error<%d>",cmd->opcode, (dma? "dma":"pio\n"), ++ // (read ? "read ":"write") ,data->blksz, data->blocks, data->error); ++ } ++ ++ if (mrq->cmd->error) host->error = 0x001; ++ if (mrq->data && mrq->data->error) host->error |= 0x010; ++ if (mrq->stop && mrq->stop->error) host->error |= 0x100; ++ ++ //if (host->error) printk("host->error<%d>\n", host->error); ++ ++ return host->error; ++} ++ ++static int msdc_app_cmd(struct mmc_host *mmc, struct msdc_host *host) ++{ ++ struct mmc_command cmd; ++ struct mmc_request mrq; ++ u32 err; ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ cmd.opcode = MMC_APP_CMD; ++#if 0 /* bug: we meet mmc->card is null when ACMD6 */ ++ cmd.arg = mmc->card->rca << 16; ++#else ++ cmd.arg = host->app_cmd_arg; ++#endif ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; ++ ++ memset(&mrq, 0, sizeof(struct mmc_request)); ++ mrq.cmd = &cmd; cmd.mrq = &mrq; ++ cmd.data = NULL; ++ ++ err = msdc_do_command(host, &cmd, 0, CMD_TIMEOUT); ++ return err; ++} ++ ++static int msdc_tune_cmdrsp(struct msdc_host*host, struct mmc_command *cmd) ++{ ++ int result = -1; ++ u32 base = host->base; ++ u32 rsmpl, cur_rsmpl, orig_rsmpl; ++ u32 rrdly, cur_rrdly = 0, orig_rrdly; ++ u32 skip = 1; ++ ++ /* ==== don't support 3.0 now ==== ++ 1: R_SMPL[1] ++ 2: PAD_CMD_RESP_RXDLY[26:22] ++ ==========================*/ ++ ++ // save the previous tune result ++ sdr_get_field(MSDC_IOCON, MSDC_IOCON_RSPL, orig_rsmpl); ++ sdr_get_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY, orig_rrdly); ++ ++ rrdly = 0; ++ do { ++ for (rsmpl = 0; rsmpl < 2; rsmpl++) { ++ /* Lv1: R_SMPL[1] */ ++ cur_rsmpl = (orig_rsmpl + rsmpl) % 2; ++ if (skip == 1) { ++ skip = 0; ++ continue; ++ } ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl); ++ ++ if (host->app_cmd) { ++ result = msdc_app_cmd(host->mmc, host); ++ if (result) { ++ //printk("TUNE_CMD app_cmd<%d> failed: RESP_RXDLY<%d>,R_SMPL<%d>\n", ++ // host->mrq->cmd->opcode, cur_rrdly, cur_rsmpl); ++ continue; ++ } ++ } ++ result = msdc_do_command(host, cmd, 0, CMD_TIMEOUT); // not tune. ++ //printk("TUNE_CMD<%d> %s PAD_CMD_RESP_RXDLY[26:22]<%d> R_SMPL[1]<%d>\n", cmd->opcode, ++// (result == 0) ? "PASS" : "FAIL", cur_rrdly, cur_rsmpl); ++ ++ if (result == 0) { ++ return 0; ++ } ++ if (result != (unsigned int)(-EIO)) { ++ // printk("TUNE_CMD<%d> Error<%d> not -EIO\n", cmd->opcode, result); ++ return result; ++ } ++ ++ /* should be EIO */ ++ if (sdr_read32(SDC_CMD) & 0x1800) { /* check if has data phase */ ++ msdc_abort_data(host); ++ } ++ } ++ ++ /* Lv2: PAD_CMD_RESP_RXDLY[26:22] */ ++ cur_rrdly = (orig_rrdly + rrdly + 1) % 32; ++ sdr_set_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY, cur_rrdly); ++ }while (++rrdly < 32); ++ ++ return result; ++} ++ ++/* Support SD2.0 Only */ ++static int msdc_tune_bread(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ u32 base = host->base; ++ u32 ddr=0; ++ u32 dcrc = 0; ++ u32 rxdly, cur_rxdly0, cur_rxdly1; ++ u32 dsmpl, cur_dsmpl, orig_dsmpl; ++ u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3; ++ u32 cur_dat4, cur_dat5, cur_dat6, cur_dat7; ++ u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3; ++ u32 orig_dat4, orig_dat5, orig_dat6, orig_dat7; ++ int result = -1; ++ u32 skip = 1; ++ ++ sdr_get_field(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl); ++ ++ /* Tune Method 2. */ ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1); ++ ++ rxdly = 0; ++ do { ++ for (dsmpl = 0; dsmpl < 2; dsmpl++) { ++ cur_dsmpl = (orig_dsmpl + dsmpl) % 2; ++ if (skip == 1) { ++ skip = 0; ++ continue; ++ } ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl); ++ ++ if (host->app_cmd) { ++ result = msdc_app_cmd(host->mmc, host); ++ if (result) { ++ //printk("TUNE_BREAD app_cmd<%d> failed\n", host->mrq->cmd->opcode); ++ continue; ++ } ++ } ++ result = msdc_do_request(mmc,mrq); ++ ++ sdr_get_field(SDC_DCRC_STS, SDC_DCRC_STS_POS|SDC_DCRC_STS_NEG, dcrc); /* RO */ ++ if (!ddr) dcrc &= ~SDC_DCRC_STS_NEG; ++ //printk("TUNE_BREAD<%s> dcrc<0x%x> DATRDDLY0/1<0x%x><0x%x> dsmpl<0x%x>\n", ++ // (result == 0 && dcrc == 0) ? "PASS" : "FAIL", dcrc, ++ // sdr_read32(MSDC_DAT_RDDLY0), sdr_read32(MSDC_DAT_RDDLY1), cur_dsmpl); ++ ++ /* Fix me: result is 0, but dcrc is still exist */ ++ if (result == 0 && dcrc == 0) { ++ goto done; ++ } else { ++ /* there is a case: command timeout, and data phase not processed */ ++ if (mrq->data->error != 0 && mrq->data->error != (unsigned int)(-EIO)) { ++ //printk("TUNE_READ: result<0x%x> cmd_error<%d> data_error<%d>\n", ++ // result, mrq->cmd->error, mrq->data->error); ++ goto done; ++ } ++ } ++ } ++ ++ cur_rxdly0 = sdr_read32(MSDC_DAT_RDDLY0); ++ cur_rxdly1 = sdr_read32(MSDC_DAT_RDDLY1); ++ ++ /* E1 ECO. YD: Reverse */ ++ if (sdr_read32(MSDC_ECO_VER) >= 4) { ++ orig_dat0 = (cur_rxdly0 >> 24) & 0x1F; ++ orig_dat1 = (cur_rxdly0 >> 16) & 0x1F; ++ orig_dat2 = (cur_rxdly0 >> 8) & 0x1F; ++ orig_dat3 = (cur_rxdly0 >> 0) & 0x1F; ++ orig_dat4 = (cur_rxdly1 >> 24) & 0x1F; ++ orig_dat5 = (cur_rxdly1 >> 16) & 0x1F; ++ orig_dat6 = (cur_rxdly1 >> 8) & 0x1F; ++ orig_dat7 = (cur_rxdly1 >> 0) & 0x1F; ++ } else { ++ orig_dat0 = (cur_rxdly0 >> 0) & 0x1F; ++ orig_dat1 = (cur_rxdly0 >> 8) & 0x1F; ++ orig_dat2 = (cur_rxdly0 >> 16) & 0x1F; ++ orig_dat3 = (cur_rxdly0 >> 24) & 0x1F; ++ orig_dat4 = (cur_rxdly1 >> 0) & 0x1F; ++ orig_dat5 = (cur_rxdly1 >> 8) & 0x1F; ++ orig_dat6 = (cur_rxdly1 >> 16) & 0x1F; ++ orig_dat7 = (cur_rxdly1 >> 24) & 0x1F; ++ } ++ ++ if (ddr) { ++ cur_dat0 = (dcrc & (1 << 0) || dcrc & (1 << 8)) ? ((orig_dat0 + 1) % 32) : orig_dat0; ++ cur_dat1 = (dcrc & (1 << 1) || dcrc & (1 << 9)) ? ((orig_dat1 + 1) % 32) : orig_dat1; ++ cur_dat2 = (dcrc & (1 << 2) || dcrc & (1 << 10)) ? ((orig_dat2 + 1) % 32) : orig_dat2; ++ cur_dat3 = (dcrc & (1 << 3) || dcrc & (1 << 11)) ? ((orig_dat3 + 1) % 32) : orig_dat3; ++ } else { ++ cur_dat0 = (dcrc & (1 << 0)) ? ((orig_dat0 + 1) % 32) : orig_dat0; ++ cur_dat1 = (dcrc & (1 << 1)) ? ((orig_dat1 + 1) % 32) : orig_dat1; ++ cur_dat2 = (dcrc & (1 << 2)) ? ((orig_dat2 + 1) % 32) : orig_dat2; ++ cur_dat3 = (dcrc & (1 << 3)) ? ((orig_dat3 + 1) % 32) : orig_dat3; ++ } ++ cur_dat4 = (dcrc & (1 << 4)) ? ((orig_dat4 + 1) % 32) : orig_dat4; ++ cur_dat5 = (dcrc & (1 << 5)) ? ((orig_dat5 + 1) % 32) : orig_dat5; ++ cur_dat6 = (dcrc & (1 << 6)) ? ((orig_dat6 + 1) % 32) : orig_dat6; ++ cur_dat7 = (dcrc & (1 << 7)) ? ((orig_dat7 + 1) % 32) : orig_dat7; ++ ++ cur_rxdly0 = (cur_dat0 << 24) | (cur_dat1 << 16) | (cur_dat2 << 8) | (cur_dat3 << 0); ++ cur_rxdly1 = (cur_dat4 << 24) | (cur_dat5 << 16) | (cur_dat6 << 8) | (cur_dat7 << 0); ++ ++ sdr_write32(MSDC_DAT_RDDLY0, cur_rxdly0); ++ sdr_write32(MSDC_DAT_RDDLY1, cur_rxdly1); ++ ++ } while (++rxdly < 32); ++ ++done: ++ return result; ++} ++ ++static int msdc_tune_bwrite(struct mmc_host *mmc,struct mmc_request *mrq) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ u32 base = host->base; ++ ++ u32 wrrdly, cur_wrrdly = 0, orig_wrrdly; ++ u32 dsmpl, cur_dsmpl, orig_dsmpl; ++ u32 rxdly, cur_rxdly0; ++ u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3; ++ u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3; ++ int result = -1; ++ u32 skip = 1; ++ ++ // MSDC_IOCON_DDR50CKD need to check. [Fix me] ++ ++ sdr_get_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, orig_wrrdly); ++ sdr_get_field(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl ); ++ ++ /* Tune Method 2. just DAT0 */ ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1); ++ cur_rxdly0 = sdr_read32(MSDC_DAT_RDDLY0); ++ ++ /* E1 ECO. YD: Reverse */ ++ if (sdr_read32(MSDC_ECO_VER) >= 4) { ++ orig_dat0 = (cur_rxdly0 >> 24) & 0x1F; ++ orig_dat1 = (cur_rxdly0 >> 16) & 0x1F; ++ orig_dat2 = (cur_rxdly0 >> 8) & 0x1F; ++ orig_dat3 = (cur_rxdly0 >> 0) & 0x1F; ++ } else { ++ orig_dat0 = (cur_rxdly0 >> 0) & 0x1F; ++ orig_dat1 = (cur_rxdly0 >> 8) & 0x1F; ++ orig_dat2 = (cur_rxdly0 >> 16) & 0x1F; ++ orig_dat3 = (cur_rxdly0 >> 24) & 0x1F; ++ } ++ ++ rxdly = 0; ++ do { ++ wrrdly = 0; ++ do { ++ for (dsmpl = 0; dsmpl < 2; dsmpl++) { ++ cur_dsmpl = (orig_dsmpl + dsmpl) % 2; ++ if (skip == 1) { ++ skip = 0; ++ continue; ++ } ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl); ++ ++ if (host->app_cmd) { ++ result = msdc_app_cmd(host->mmc, host); ++ if (result) { ++ //printk("TUNE_BWRITE app_cmd<%d> failed\n", host->mrq->cmd->opcode); ++ continue; ++ } ++ } ++ result = msdc_do_request(mmc,mrq); ++ ++ //printk("TUNE_BWRITE<%s> DSPL<%d> DATWRDLY<%d> MSDC_DAT_RDDLY0<0x%x>\n", ++ // result == 0 ? "PASS" : "FAIL", ++ // cur_dsmpl, cur_wrrdly, cur_rxdly0); ++ ++ if (result == 0) { ++ goto done; ++ } ++ else { ++ /* there is a case: command timeout, and data phase not processed */ ++ if (mrq->data->error != (unsigned int)(-EIO)) { ++ //printk("TUNE_READ: result<0x%x> cmd_error<%d> data_error<%d>\n", ++ // && result, mrq->cmd->error, mrq->data->error); ++ goto done; ++ } ++ } ++ } ++ cur_wrrdly = (orig_wrrdly + wrrdly + 1) % 32; ++ sdr_set_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, cur_wrrdly); ++ } while (++wrrdly < 32); ++ ++ cur_dat0 = (orig_dat0 + rxdly) % 32; /* only adjust bit-1 for crc */ ++ cur_dat1 = orig_dat1; ++ cur_dat2 = orig_dat2; ++ cur_dat3 = orig_dat3; ++ ++ cur_rxdly0 = (cur_dat0 << 24) | (cur_dat1 << 16) | (cur_dat2 << 8) | (cur_dat3 << 0); ++ sdr_write32(MSDC_DAT_RDDLY0, cur_rxdly0); ++ } while (++rxdly < 32); ++ ++done: ++ return result; ++} ++ ++static int msdc_get_card_status(struct mmc_host *mmc, struct msdc_host *host, u32 *status) ++{ ++ struct mmc_command cmd; ++ struct mmc_request mrq; ++ u32 err; ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ cmd.opcode = MMC_SEND_STATUS; ++ if (mmc->card) { ++ cmd.arg = mmc->card->rca << 16; ++ } else { ++ //printk("cmd13 mmc card is null\n"); ++ cmd.arg = host->app_cmd_arg; ++ } ++ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; ++ ++ memset(&mrq, 0, sizeof(struct mmc_request)); ++ mrq.cmd = &cmd; cmd.mrq = &mrq; ++ cmd.data = NULL; ++ ++ err = msdc_do_command(host, &cmd, 1, CMD_TIMEOUT); ++ ++ if (status) ++ *status = cmd.resp[0]; ++ ++ return err; ++} ++ ++static int msdc_check_busy(struct mmc_host *mmc, struct msdc_host *host) ++{ ++ u32 err = 0; ++ u32 status = 0; ++ ++ do { ++ err = msdc_get_card_status(mmc, host, &status); ++ if (err) ++ return err; ++ /* need cmd12? */ ++ //printk("cmd<13> resp<0x%x>\n", status); ++ } while (R1_CURRENT_STATE(status) == 7); ++ ++ return err; ++} ++ ++static int msdc_tune_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ struct mmc_command *cmd; ++ struct mmc_data *data; ++ int ret=0, read; ++ ++ cmd = mrq->cmd; ++ data = mrq->cmd->data; ++ ++ read = data->flags & MMC_DATA_READ ? 1 : 0; ++ ++ if (read) { ++ if (data->error == (unsigned int)(-EIO)) ++ ret = msdc_tune_bread(mmc,mrq); ++ } else { ++ ret = msdc_check_busy(mmc, host); ++ if (ret){ ++ //printk("XXX cmd13 wait program done failed\n"); ++ return ret; ++ } ++ /* CRC and TO */ ++ /* Fix me: don't care card status? */ ++ ret = msdc_tune_bwrite(mmc,mrq); ++ } ++ ++ return ret; ++} ++ ++static void msdc_ops_request(struct mmc_host *mmc,struct mmc_request *mrq) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ ++ if (host->mrq) { ++ //printk("XXX host->mrq<0x%.8x>\n", (int)host->mrq); ++ BUG(); ++ } ++ if (!is_card_present(host) || host->power_mode == MMC_POWER_OFF) { ++ //printk("cmd<%d> card<%d> power<%d>\n", mrq->cmd->opcode, is_card_present(host), host->power_mode); ++ mrq->cmd->error = (unsigned int)-ENOMEDIUM; ++ mrq->done(mrq); ++ return; ++ } ++ spin_lock(&host->lock); ++ ++ host->mrq = mrq; ++ ++ if (msdc_do_request(mmc,mrq)) ++ if(host->hw->flags & MSDC_REMOVABLE && mrq->data && mrq->data->error) ++ msdc_tune_request(mmc,mrq); ++ ++ if (mrq->cmd->opcode == MMC_APP_CMD) { ++ host->app_cmd = 1; ++ host->app_cmd_arg = mrq->cmd->arg; /* save the RCA */ ++ } else { ++ host->app_cmd = 0; ++ } ++ ++ host->mrq = NULL; ++ ++ spin_unlock(&host->lock); ++ ++ mmc_request_done(mmc, mrq); ++} ++ ++/* called by ops.set_ios */ ++static void msdc_set_buswidth(struct msdc_host *host, u32 width) ++{ ++ u32 base = host->base; ++ u32 val = sdr_read32(SDC_CFG); ++ ++ val &= ~SDC_CFG_BUSWIDTH; ++ ++ switch (width) { ++ default: ++ case MMC_BUS_WIDTH_1: ++ width = 1; ++ val |= (MSDC_BUS_1BITS << 16); ++ break; ++ case MMC_BUS_WIDTH_4: ++ val |= (MSDC_BUS_4BITS << 16); ++ break; ++ case MMC_BUS_WIDTH_8: ++ val |= (MSDC_BUS_8BITS << 16); ++ break; ++ } ++ ++ sdr_write32(SDC_CFG, val); ++ ++ //printk("Bus Width = %d\n", width); ++} ++ ++/* ops.set_ios */ ++static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ struct msdc_hw *hw=host->hw; ++ u32 base = host->base; ++ u32 ddr = 0; ++ ++#ifdef MT6575_SD_DEBUG ++ static char *vdd[] = { ++ "1.50v", "1.55v", "1.60v", "1.65v", "1.70v", "1.80v", "1.90v", ++ "2.00v", "2.10v", "2.20v", "2.30v", "2.40v", "2.50v", "2.60v", ++ "2.70v", "2.80v", "2.90v", "3.00v", "3.10v", "3.20v", "3.30v", ++ "3.40v", "3.50v", "3.60v" ++ }; ++ static char *power_mode[] = { ++ "OFF", "UP", "ON" ++ }; ++ static char *bus_mode[] = { ++ "UNKNOWN", "OPENDRAIN", "PUSHPULL" ++ }; ++ static char *timing[] = { ++ "LEGACY", "MMC_HS", "SD_HS" ++ }; ++ ++ /*printk("SET_IOS: CLK(%dkHz), BUS(%s), BW(%u), PWR(%s), VDD(%s), TIMING(%s)\n", ++ ios->clock / 1000, bus_mode[ios->bus_mode], ++ (ios->bus_width == MMC_BUS_WIDTH_4) ? 4 : 1, ++ power_mode[ios->power_mode], vdd[ios->vdd], timing[ios->timing]);*/ ++#endif ++ ++ msdc_set_buswidth(host, ios->bus_width); ++ ++ /* Power control ??? */ ++ switch (ios->power_mode) { ++ case MMC_POWER_OFF: ++ case MMC_POWER_UP: ++ // msdc_set_power_mode(host, ios->power_mode); /* --- by chhung */ ++ break; ++ case MMC_POWER_ON: ++ host->power_mode = MMC_POWER_ON; ++ break; ++ default: ++ break; ++ } ++ ++ /* Clock control */ ++ if (host->mclk != ios->clock) { ++ if(ios->clock > 25000000) { ++ //printk("SD data latch edge<%d>\n", hw->data_edge); ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_RSPL, hw->cmd_edge); ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, hw->data_edge); ++ } else { ++ sdr_write32(MSDC_IOCON, 0x00000000); ++ sdr_write32(MSDC_DAT_RDDLY0, 0x10101010); // for MT7620 E2 and afterward ++ sdr_write32(MSDC_DAT_RDDLY1, 0x00000000); ++ sdr_write32(MSDC_PAD_TUNE, 0x84101010); // for MT7620 E2 and afterward ++ } ++ msdc_set_mclk(host, ddr, ios->clock); ++ } ++} ++ ++/* ops.get_ro */ ++static int msdc_ops_get_ro(struct mmc_host *mmc) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ u32 base = host->base; ++ unsigned long flags; ++ int ro = 0; ++ ++ if (host->hw->flags & MSDC_WP_PIN_EN) { /* set for card */ ++ spin_lock_irqsave(&host->lock, flags); ++ ro = (sdr_read32(MSDC_PS) >> 31); ++ spin_unlock_irqrestore(&host->lock, flags); ++ } ++ return ro; ++} ++ ++/* ops.get_cd */ ++static int msdc_ops_get_cd(struct mmc_host *mmc) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ u32 base = host->base; ++ unsigned long flags; ++ int present = 1; ++ ++ /* for sdio, MSDC_REMOVABLE not set, always return 1 */ ++ if (!(host->hw->flags & MSDC_REMOVABLE)) { ++ /* For sdio, read H/W always get<1>, but may timeout some times */ ++#if 1 ++ host->card_inserted = 1; ++ return 1; ++#else ++ host->card_inserted = (host->pm_state.event == PM_EVENT_USER_RESUME) ? 1 : 0; ++ printk("sdio ops_get_cd<%d>\n", host->card_inserted); ++ return host->card_inserted; ++#endif ++ } ++ ++ /* MSDC_CD_PIN_EN set for card */ ++ if (host->hw->flags & MSDC_CD_PIN_EN) { ++ spin_lock_irqsave(&host->lock, flags); ++#if 0 ++ present = host->card_inserted; /* why not read from H/W: Fix me*/ ++#else ++ present = (sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 0 : 1; ++ host->card_inserted = present; ++#endif ++ spin_unlock_irqrestore(&host->lock, flags); ++ } else { ++ present = 0; /* TODO? Check DAT3 pins for card detection */ ++ } ++ ++ //printk("ops_get_cd return<%d>\n", present); ++ return present; ++} ++ ++/* ops.enable_sdio_irq */ ++static void msdc_ops_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ struct msdc_hw *hw = host->hw; ++ u32 base = host->base; ++ u32 tmp; ++ ++ if (hw->flags & MSDC_EXT_SDIO_IRQ) { /* yes for sdio */ ++ if (enable) { ++ hw->enable_sdio_eirq(); /* combo_sdio_enable_eirq */ ++ } else { ++ hw->disable_sdio_eirq(); /* combo_sdio_disable_eirq */ ++ } ++ } else { ++ //printk("XXX \n"); /* so never enter here */ ++ tmp = sdr_read32(SDC_CFG); ++ /* FIXME. Need to interrupt gap detection */ ++ if (enable) { ++ tmp |= (SDC_CFG_SDIOIDE | SDC_CFG_SDIOINTWKUP); ++ } else { ++ tmp &= ~(SDC_CFG_SDIOIDE | SDC_CFG_SDIOINTWKUP); ++ } ++ sdr_write32(SDC_CFG, tmp); ++ } ++} ++ ++static struct mmc_host_ops mt_msdc_ops = { ++ .request = msdc_ops_request, ++ .set_ios = msdc_ops_set_ios, ++ .get_ro = msdc_ops_get_ro, ++ .get_cd = msdc_ops_get_cd, ++ .enable_sdio_irq = msdc_ops_enable_sdio_irq, ++}; ++ ++/*--------------------------------------------------------------------------*/ ++/* interrupt handler */ ++/*--------------------------------------------------------------------------*/ ++static irqreturn_t msdc_irq(int irq, void *dev_id) ++{ ++ struct msdc_host *host = (struct msdc_host *)dev_id; ++ struct mmc_data *data = host->data; ++ struct mmc_command *cmd = host->cmd; ++ u32 base = host->base; ++ ++ u32 cmdsts = MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO | MSDC_INT_CMDRDY | ++ MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO | MSDC_INT_ACMDRDY | ++ MSDC_INT_ACMD19_DONE; ++ u32 datsts = MSDC_INT_DATCRCERR |MSDC_INT_DATTMO; ++ ++ u32 intsts = sdr_read32(MSDC_INT); ++ u32 inten = sdr_read32(MSDC_INTEN); inten &= intsts; ++ ++ sdr_write32(MSDC_INT, intsts); /* clear interrupts */ ++ /* MSG will cause fatal error */ ++ ++ /* card change interrupt */ ++ if (intsts & MSDC_INT_CDSC){ ++ //printk("MSDC_INT_CDSC irq<0x%.8x>\n", intsts); ++ tasklet_hi_schedule(&host->card_tasklet); ++ /* tuning when plug card ? */ ++ } ++ ++ /* sdio interrupt */ ++ if (intsts & MSDC_INT_SDIOIRQ){ ++ //printk("XXX MSDC_INT_SDIOIRQ\n"); /* seems not sdio irq */ ++ //mmc_signal_sdio_irq(host->mmc); ++ } ++ ++ /* transfer complete interrupt */ ++ if (data != NULL) { ++ if (inten & MSDC_INT_XFER_COMPL) { ++ data->bytes_xfered = host->dma.xfersz; ++ complete(&host->xfer_done); ++ } ++ ++ if (intsts & datsts) { ++ /* do basic reset, or stop command will sdc_busy */ ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ atomic_set(&host->abort, 1); /* For PIO mode exit */ ++ ++ if (intsts & MSDC_INT_DATTMO){ ++ //printk("XXX CMD<%d> MSDC_INT_DATTMO\n", host->mrq->cmd->opcode); ++ data->error = (unsigned int)-ETIMEDOUT; ++ } ++ else if (intsts & MSDC_INT_DATCRCERR){ ++ //printk("XXX CMD<%d> MSDC_INT_DATCRCERR, SDC_DCRC_STS<0x%x>\n", host->mrq->cmd->opcode, sdr_read32(SDC_DCRC_STS)); ++ data->error = (unsigned int)-EIO; ++ } ++ ++ //if(sdr_read32(MSDC_INTEN) & MSDC_INT_XFER_COMPL) { ++ if (host->dma_xfer) { ++ complete(&host->xfer_done); /* Read CRC come fast, XFER_COMPL not enabled */ ++ } /* PIO mode can't do complete, because not init */ ++ } ++ } ++ ++ /* command interrupts */ ++ if ((cmd != NULL) && (intsts & cmdsts)) { ++ if ((intsts & MSDC_INT_CMDRDY) || (intsts & MSDC_INT_ACMDRDY) || ++ (intsts & MSDC_INT_ACMD19_DONE)) { ++ u32 *rsp = &cmd->resp[0]; ++ ++ switch (host->cmd_rsp) { ++ case RESP_NONE: ++ break; ++ case RESP_R2: ++ *rsp++ = sdr_read32(SDC_RESP3); *rsp++ = sdr_read32(SDC_RESP2); ++ *rsp++ = sdr_read32(SDC_RESP1); *rsp++ = sdr_read32(SDC_RESP0); ++ break; ++ default: /* Response types 1, 3, 4, 5, 6, 7(1b) */ ++ if ((intsts & MSDC_INT_ACMDRDY) || (intsts & MSDC_INT_ACMD19_DONE)) { ++ *rsp = sdr_read32(SDC_ACMD_RESP); ++ } else { ++ *rsp = sdr_read32(SDC_RESP0); ++ } ++ break; ++ } ++ } else if ((intsts & MSDC_INT_RSPCRCERR) || (intsts & MSDC_INT_ACMDCRCERR)) { ++ if(intsts & MSDC_INT_ACMDCRCERR){ ++ //printk("XXX CMD<%d> MSDC_INT_ACMDCRCERR\n",cmd->opcode); ++ } ++ else { ++ //printk("XXX CMD<%d> MSDC_INT_RSPCRCERR\n",cmd->opcode); ++ } ++ cmd->error = (unsigned int)-EIO; ++ } else if ((intsts & MSDC_INT_CMDTMO) || (intsts & MSDC_INT_ACMDTMO)) { ++ if(intsts & MSDC_INT_ACMDTMO){ ++ //printk("XXX CMD<%d> MSDC_INT_ACMDTMO\n",cmd->opcode); ++ } ++ else { ++ //printk("XXX CMD<%d> MSDC_INT_CMDTMO\n",cmd->opcode); ++ } ++ cmd->error = (unsigned int)-ETIMEDOUT; ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ } ++ complete(&host->cmd_done); ++ } ++ ++ /* mmc irq interrupts */ ++ if (intsts & MSDC_INT_MMCIRQ) { ++ //printk(KERN_INFO "msdc[%d] MMCIRQ: SDC_CSTS=0x%.8x\r\n", host->id, sdr_read32(SDC_CSTS)); ++ } ++ ++#ifdef MT6575_SD_DEBUG ++ { ++ msdc_int_reg *int_reg = (msdc_int_reg*)&intsts; ++ /*printk("IRQ_EVT(0x%x): MMCIRQ(%d) CDSC(%d), ACRDY(%d), ACTMO(%d), ACCRE(%d) AC19DN(%d)\n", ++ intsts, ++ int_reg->mmcirq, ++ int_reg->cdsc, ++ int_reg->atocmdrdy, ++ int_reg->atocmdtmo, ++ int_reg->atocmdcrc, ++ int_reg->atocmd19done); ++ printk("IRQ_EVT(0x%x): SDIO(%d) CMDRDY(%d), CMDTMO(%d), RSPCRC(%d), CSTA(%d)\n", ++ intsts, ++ int_reg->sdioirq, ++ int_reg->cmdrdy, ++ int_reg->cmdtmo, ++ int_reg->rspcrc, ++ int_reg->csta); ++ printk("IRQ_EVT(0x%x): XFCMP(%d) DXDONE(%d), DATTMO(%d), DATCRC(%d), DMAEMP(%d)\n", ++ intsts, ++ int_reg->xfercomp, ++ int_reg->dxferdone, ++ int_reg->dattmo, ++ int_reg->datcrc, ++ int_reg->dmaqempty);*/ ++ ++ } ++#endif ++ ++ return IRQ_HANDLED; ++} ++ ++/*--------------------------------------------------------------------------*/ ++/* platform_driver members */ ++/*--------------------------------------------------------------------------*/ ++/* called by msdc_drv_probe/remove */ ++static void msdc_enable_cd_irq(struct msdc_host *host, int enable) ++{ ++ struct msdc_hw *hw = host->hw; ++ u32 base = host->base; ++ ++ /* for sdio, not set */ ++ if ((hw->flags & MSDC_CD_PIN_EN) == 0) { ++ /* Pull down card detection pin since it is not avaiable */ ++ /* ++ if (hw->config_gpio_pin) ++ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_DOWN); ++ */ ++ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN); ++ sdr_clr_bits(MSDC_INTEN, MSDC_INTEN_CDSC); ++ sdr_clr_bits(SDC_CFG, SDC_CFG_INSWKUP); ++ return; ++ } ++ ++ //printk("CD IRQ Eanable(%d)\n", enable); ++ ++ if (enable) { ++ if (hw->enable_cd_eirq) { /* not set, never enter */ ++ hw->enable_cd_eirq(); ++ } else { ++ /* card detection circuit relies on the core power so that the core power ++ * shouldn't be turned off. Here adds a reference count to keep ++ * the core power alive. ++ */ ++ //msdc_vcore_on(host); //did in msdc_init_hw() ++ ++ if (hw->config_gpio_pin) /* NULL */ ++ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_UP); ++ ++ sdr_set_field(MSDC_PS, MSDC_PS_CDDEBOUNCE, DEFAULT_DEBOUNCE); ++ sdr_set_bits(MSDC_PS, MSDC_PS_CDEN); ++ sdr_set_bits(MSDC_INTEN, MSDC_INTEN_CDSC); ++ sdr_set_bits(SDC_CFG, SDC_CFG_INSWKUP); /* not in document! Fix me */ ++ } ++ } else { ++ if (hw->disable_cd_eirq) { ++ hw->disable_cd_eirq(); ++ } else { ++ if (hw->config_gpio_pin) /* NULL */ ++ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_DOWN); ++ ++ sdr_clr_bits(SDC_CFG, SDC_CFG_INSWKUP); ++ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN); ++ sdr_clr_bits(MSDC_INTEN, MSDC_INTEN_CDSC); ++ ++ /* Here decreases a reference count to core power since card ++ * detection circuit is shutdown. ++ */ ++ //msdc_vcore_off(host); ++ } ++ } ++} ++ ++/* called by msdc_drv_probe */ ++static void msdc_init_hw(struct msdc_host *host) ++{ ++ u32 base = host->base; ++ struct msdc_hw *hw = host->hw; ++ ++#ifdef MT6575_SD_DEBUG ++ msdc_reg[host->id] = (struct msdc_regs *)host->base; ++#endif ++ ++ /* Power on */ ++#if 0 /* --- chhung */ ++ msdc_vcore_on(host); ++ msdc_pin_reset(host, MSDC_PIN_PULL_UP); ++ msdc_select_clksrc(host, hw->clk_src); ++ enable_clock(PERI_MSDC0_PDN + host->id, "SD"); ++ msdc_vdd_on(host); ++#endif /* end of --- */ ++ /* Configure to MMC/SD mode */ ++ sdr_set_field(MSDC_CFG, MSDC_CFG_MODE, MSDC_SDMMC); ++ ++ /* Reset */ ++ msdc_reset(); ++ msdc_clr_fifo(); ++ ++ /* Disable card detection */ ++ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN); ++ ++ /* Disable and clear all interrupts */ ++ sdr_clr_bits(MSDC_INTEN, sdr_read32(MSDC_INTEN)); ++ sdr_write32(MSDC_INT, sdr_read32(MSDC_INT)); ++ ++#if 1 ++ /* reset tuning parameter */ ++ sdr_write32(MSDC_PAD_CTL0, 0x00090000); ++ sdr_write32(MSDC_PAD_CTL1, 0x000A0000); ++ sdr_write32(MSDC_PAD_CTL2, 0x000A0000); ++ // sdr_write32(MSDC_PAD_TUNE, 0x00000000); ++ sdr_write32(MSDC_PAD_TUNE, 0x84101010); // for MT7620 E2 and afterward ++ // sdr_write32(MSDC_DAT_RDDLY0, 0x00000000); ++ sdr_write32(MSDC_DAT_RDDLY0, 0x10101010); // for MT7620 E2 and afterward ++ sdr_write32(MSDC_DAT_RDDLY1, 0x00000000); ++ sdr_write32(MSDC_IOCON, 0x00000000); ++#if 0 // use MT7620 default value: 0x403c004f ++ sdr_write32(MSDC_PATCH_BIT0, 0x003C000F); /* bit0 modified: Rx Data Clock Source: 1 -> 2.0*/ ++#endif ++ ++ if (sdr_read32(MSDC_ECO_VER) >= 4) { ++ if (host->id == 1) { ++ sdr_set_field(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_WRDAT_CRCS, 1); ++ sdr_set_field(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_CMD_RSP, 1); ++ ++ /* internal clock: latch read data */ ++ sdr_set_bits(MSDC_PATCH_BIT0, MSDC_PATCH_BIT_CKGEN_CK); ++ } ++ } ++#endif ++ ++ /* for safety, should clear SDC_CFG.SDIO_INT_DET_EN & set SDC_CFG.SDIO in ++ pre-loader,uboot,kernel drivers. and SDC_CFG.SDIO_INT_DET_EN will be only ++ set when kernel driver wants to use SDIO bus interrupt */ ++ /* Configure to enable SDIO mode. it's must otherwise sdio cmd5 failed */ ++ sdr_set_bits(SDC_CFG, SDC_CFG_SDIO); ++ ++ /* disable detect SDIO device interupt function */ ++ sdr_clr_bits(SDC_CFG, SDC_CFG_SDIOIDE); ++ ++ /* eneable SMT for glitch filter */ ++ sdr_set_bits(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKSMT); ++ sdr_set_bits(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDSMT); ++ sdr_set_bits(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATSMT); ++ ++#if 1 ++ /* set clk, cmd, dat pad driving */ ++ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVN, hw->clk_drv); ++ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVP, hw->clk_drv); ++ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVN, hw->cmd_drv); ++ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVP, hw->cmd_drv); ++ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVN, hw->dat_drv); ++ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVP, hw->dat_drv); ++#else ++ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVN, 0); ++ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVP, 0); ++ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVN, 0); ++ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVP, 0); ++ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVN, 0); ++ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVP, 0); ++#endif ++ ++ /* set sampling edge */ ++ ++ /* write crc timeout detection */ ++ sdr_set_field(MSDC_PATCH_BIT0, 1 << 30, 1); ++ ++ /* Configure to default data timeout */ ++ sdr_set_field(SDC_CFG, SDC_CFG_DTOC, DEFAULT_DTOC); ++ ++ msdc_set_buswidth(host, MMC_BUS_WIDTH_1); ++ ++ //printk("init hardware done!\n"); ++} ++ ++/* called by msdc_drv_remove */ ++static void msdc_deinit_hw(struct msdc_host *host) ++{ ++ u32 base = host->base; ++ ++ /* Disable and clear all interrupts */ ++ sdr_clr_bits(MSDC_INTEN, sdr_read32(MSDC_INTEN)); ++ sdr_write32(MSDC_INT, sdr_read32(MSDC_INT)); ++ ++ /* Disable card detection */ ++ msdc_enable_cd_irq(host, 0); ++ // msdc_set_power_mode(host, MMC_POWER_OFF); /* make sure power down */ /* --- by chhung */ ++} ++ ++/* init gpd and bd list in msdc_drv_probe */ ++static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma) ++{ ++ gpd_t *gpd = dma->gpd; ++ bd_t *bd = dma->bd; ++ bd_t *ptr, *prev; ++ ++ /* we just support one gpd */ ++ int bdlen = MAX_BD_PER_GPD; ++ ++ /* init the 2 gpd */ ++ memset(gpd, 0, sizeof(gpd_t) * 2); ++ //gpd->next = (void *)virt_to_phys(gpd + 1); /* pointer to a null gpd, bug! kmalloc <-> virt_to_phys */ ++ //gpd->next = (dma->gpd_addr + 1); /* bug */ ++ gpd->next = (void *)((u32)dma->gpd_addr + sizeof(gpd_t)); ++ ++ //gpd->intr = 0; ++ gpd->bdp = 1; /* hwo, cs, bd pointer */ ++ //gpd->ptr = (void*)virt_to_phys(bd); ++ gpd->ptr = (void *)dma->bd_addr; /* physical address */ ++ ++ memset(bd, 0, sizeof(bd_t) * bdlen); ++ ptr = bd + bdlen - 1; ++ //ptr->eol = 1; /* 0 or 1 [Fix me]*/ ++ //ptr->next = 0; ++ ++ while (ptr != bd) { ++ prev = ptr - 1; ++ prev->next = (void *)(dma->bd_addr + sizeof(bd_t) *(ptr - bd)); ++ ptr = prev; ++ } ++} ++ ++static int msdc_drv_probe(struct platform_device *pdev) ++{ ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ __iomem void *base; ++ struct mmc_host *mmc; ++ struct resource *mem; ++ struct msdc_host *host; ++ struct msdc_hw *hw; ++ int ret, irq; ++ pdev->dev.platform_data = &msdc0_hw; ++ ++ /* Allocate MMC host for this device */ ++ mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev); ++ if (!mmc) return -ENOMEM; ++ ++ hw = (struct msdc_hw*)pdev->dev.platform_data; ++ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irq = platform_get_irq(pdev, 0); ++ ++ //BUG_ON((!hw) || (!mem) || (irq < 0)); /* --- by chhung */ ++ ++ base = devm_request_and_ioremap(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++/* mem = request_mem_region(mem->start - 0xa0000000, (mem->end - mem->start + 1) - 0xa0000000, dev_name(&pdev->dev)); ++ if (mem == NULL) { ++ mmc_free_host(mmc); ++ return -EBUSY; ++ } ++*/ ++ /* Set host parameters to mmc */ ++ mmc->ops = &mt_msdc_ops; ++ mmc->f_min = HOST_MIN_MCLK; ++ mmc->f_max = HOST_MAX_MCLK; ++ mmc->ocr_avail = MSDC_OCR_AVAIL; ++ ++ /* For sd card: MSDC_SYS_SUSPEND | MSDC_WP_PIN_EN | MSDC_CD_PIN_EN | MSDC_REMOVABLE | MSDC_HIGHSPEED, ++ For sdio : MSDC_EXT_SDIO_IRQ | MSDC_HIGHSPEED */ ++ if (hw->flags & MSDC_HIGHSPEED) { ++ mmc->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; ++ } ++ if (hw->data_pins == 4) { /* current data_pins are all 4*/ ++ mmc->caps |= MMC_CAP_4_BIT_DATA; ++ } else if (hw->data_pins == 8) { ++ mmc->caps |= MMC_CAP_8_BIT_DATA; ++ } ++ if ((hw->flags & MSDC_SDIO_IRQ) || (hw->flags & MSDC_EXT_SDIO_IRQ)) ++ mmc->caps |= MMC_CAP_SDIO_IRQ; /* yes for sdio */ ++ ++ /* MMC core transfer sizes tunable parameters */ ++ // mmc->max_hw_segs = MAX_HW_SGMTS; ++// mmc->max_phys_segs = MAX_PHY_SGMTS; ++ mmc->max_seg_size = MAX_SGMT_SZ; ++ mmc->max_blk_size = HOST_MAX_BLKSZ; ++ mmc->max_req_size = MAX_REQ_SZ; ++ mmc->max_blk_count = mmc->max_req_size; ++ ++ host = mmc_priv(mmc); ++ host->hw = hw; ++ host->mmc = mmc; ++ host->id = pdev->id; ++ host->error = 0; ++ host->irq = irq; ++ host->base = (unsigned long) base; ++ host->mclk = 0; /* mclk: the request clock of mmc sub-system */ ++ host->hclk = hclks[hw->clk_src]; /* hclk: clock of clock source to msdc controller */ ++ host->sclk = 0; /* sclk: the really clock after divition */ ++ host->pm_state = PMSG_RESUME; ++ host->suspend = 0; ++ host->core_clkon = 0; ++ host->card_clkon = 0; ++ host->core_power = 0; ++ host->power_mode = MMC_POWER_OFF; ++// host->card_inserted = hw->flags & MSDC_REMOVABLE ? 0 : 1; ++ host->timeout_ns = 0; ++ host->timeout_clks = DEFAULT_DTOC * 65536; ++ ++ host->mrq = NULL; ++ //init_MUTEX(&host->sem); /* we don't need to support multiple threads access */ ++ ++ host->dma.used_gpd = 0; ++ host->dma.used_bd = 0; ++ ++ /* using dma_alloc_coherent*/ /* todo: using 1, for all 4 slots */ ++ host->dma.gpd = dma_alloc_coherent(NULL, MAX_GPD_NUM * sizeof(gpd_t), &host->dma.gpd_addr, GFP_KERNEL); ++ host->dma.bd = dma_alloc_coherent(NULL, MAX_BD_NUM * sizeof(bd_t), &host->dma.bd_addr, GFP_KERNEL); ++ BUG_ON((!host->dma.gpd) || (!host->dma.bd)); ++ msdc_init_gpd_bd(host, &host->dma); ++ /*for emmc*/ ++ msdc_6575_host[pdev->id] = host; ++ ++ tasklet_init(&host->card_tasklet, msdc_tasklet_card, (ulong)host); ++ spin_lock_init(&host->lock); ++ msdc_init_hw(host); ++ ++ ret = request_irq((unsigned int)irq, msdc_irq, IRQF_TRIGGER_LOW, dev_name(&pdev->dev), host); ++ if (ret) goto release; ++ // mt65xx_irq_unmask(irq); /* --- by chhung */ ++ ++ if (hw->flags & MSDC_CD_PIN_EN) { /* not set for sdio */ ++ if (hw->request_cd_eirq) { /* not set for MT6575 */ ++ hw->request_cd_eirq(msdc_eirq_cd, (void*)host); /* msdc_eirq_cd will not be used! */ ++ } ++ } ++ ++ if (hw->request_sdio_eirq) /* set to combo_sdio_request_eirq() for WIFI */ ++ hw->request_sdio_eirq(msdc_eirq_sdio, (void*)host); /* msdc_eirq_sdio() will be called when EIRQ */ ++ ++ if (hw->register_pm) {/* yes for sdio */ ++ if(hw->flags & MSDC_SYS_SUSPEND) { /* will not set for WIFI */ ++ //printk("MSDC_SYS_SUSPEND and register_pm both set\n"); ++ } ++ //mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; /* pm not controlled by system but by client. */ /* --- by chhung */ ++ } ++ ++ platform_set_drvdata(pdev, mmc); ++ ++ ret = mmc_add_host(mmc); ++ if (ret) goto free_irq; ++ ++ /* Config card detection pin and enable interrupts */ ++ if (hw->flags & MSDC_CD_PIN_EN) { /* set for card */ ++ msdc_enable_cd_irq(host, 1); ++ } else { ++ msdc_enable_cd_irq(host, 0); ++ } ++ ++ return 0; ++ ++free_irq: ++ free_irq(irq, host); ++release: ++ platform_set_drvdata(pdev, NULL); ++ msdc_deinit_hw(host); ++ ++ tasklet_kill(&host->card_tasklet); ++ ++/* if (mem) ++ release_mem_region(mem->start, mem->end - mem->start + 1); ++*/ ++ mmc_free_host(mmc); ++ ++ return ret; ++} ++ ++/* 4 device share one driver, using "drvdata" to show difference */ ++static int msdc_drv_remove(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc; ++ struct msdc_host *host; ++ struct resource *mem; ++ ++ ++ mmc = platform_get_drvdata(pdev); ++ BUG_ON(!mmc); ++ ++ host = mmc_priv(mmc); ++ BUG_ON(!host); ++ ++ //printk("removed !!!\n"); ++ ++ platform_set_drvdata(pdev, NULL); ++ mmc_remove_host(host->mmc); ++ msdc_deinit_hw(host); ++ ++ tasklet_kill(&host->card_tasklet); ++ free_irq(host->irq, host); ++ ++ dma_free_coherent(NULL, MAX_GPD_NUM * sizeof(gpd_t), host->dma.gpd, host->dma.gpd_addr); ++ dma_free_coherent(NULL, MAX_BD_NUM * sizeof(bd_t), host->dma.bd, host->dma.bd_addr); ++ ++ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ if (mem) ++ release_mem_region(mem->start, mem->end - mem->start + 1); ++ ++ mmc_free_host(host->mmc); ++ ++ return 0; ++} ++ ++static const struct of_device_id mt7620a_sdhci_match[] = { ++ { .compatible = "ralink,mt7620a-sdhci" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt288x_wdt_match); ++ ++/* Fix me: Power Flow */ ++static struct platform_driver mt_msdc_driver = { ++ .probe = msdc_drv_probe, ++ .remove = msdc_drv_remove, ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = mt7620a_sdhci_match, ++ ++ }, ++}; ++ ++static int __init mt_msdc_init(void) ++{ ++ int ret; ++/* +++ chhung */ ++ unsigned int reg; ++ ++ mtk_sd_device.dev.platform_data = &msdc0_hw; ++ printk("MTK MSDC device init.\n"); ++ reg = sdr_read32((__iomem void *) 0xb0000060) & ~(0x3<<18); ++ reg |= 0x1 << 18; ++ sdr_write32((__iomem void *) 0xb0000060, reg); ++/* end of +++ */ ++ ret = platform_driver_register(&mt_msdc_driver); ++ if (ret) { ++ printk(KERN_ERR DRV_NAME ": Can't register driver"); ++ return ret; ++ } ++ printk(KERN_INFO DRV_NAME ": MediaTek MT6575 MSDC Driver\n"); ++ ++ //msdc_debug_proc_init(); ++ return 0; ++} ++ ++static void __exit mt_msdc_exit(void) ++{ ++ platform_driver_unregister(&mt_msdc_driver); ++} ++ ++module_init(mt_msdc_init); ++module_exit(mt_msdc_exit); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MediaTek MT6575 SD/MMC Card Driver"); ++MODULE_AUTHOR("Infinity Chen "); ++ ++EXPORT_SYMBOL(msdc_6575_host); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0077-clocksource-add-common-of_clksrc_init-function.patch b/target/linux/ramips/patches-3.8/0077-clocksource-add-common-of_clksrc_init-function.patch new file mode 100644 index 0000000000..62f043a6d4 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0077-clocksource-add-common-of_clksrc_init-function.patch @@ -0,0 +1,132 @@ +From 517d8e6ba345620c6704ec3db5b23c56fde06392 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 20 Jun 2013 19:16:03 +0200 +Subject: [PATCH 77/79] clocksource: add common of_clksrc_init() function + +It is desirable to move all clocksource drivers to drivers/clocksource, +yet each requires its own initialization function. We'd rather not +pollute with a header for each function. Instead, create a +single of_clksrc_init() function which will determine which clocksource +driver to initialize based on device tree. + +Based on a similar patch for drivers/irqchip by Thomas Petazzoni. + +Signed-off-by: Stephen Warren +--- + drivers/clocksource/Kconfig | 3 +++ + drivers/clocksource/Makefile | 1 + + drivers/clocksource/clksrc-of.c | 35 +++++++++++++++++++++++++++++++++++ + include/asm-generic/vmlinux.lds.h | 9 +++++++++ + include/linux/clocksource.h | 9 +++++++++ + 5 files changed, 57 insertions(+) + create mode 100644 drivers/clocksource/clksrc-of.c + +diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig +index 7fdcbd3..a32b7a9 100644 +--- a/drivers/clocksource/Kconfig ++++ b/drivers/clocksource/Kconfig +@@ -1,3 +1,6 @@ ++config CLKSRC_OF ++ bool ++ + config CLKSRC_I8253 + bool + +diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile +index f93453d..a33f792 100644 +--- a/drivers/clocksource/Makefile ++++ b/drivers/clocksource/Makefile +@@ -1,3 +1,4 @@ ++obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o + obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o + obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o + obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o +diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c +new file mode 100644 +index 0000000..bdabdaa +--- /dev/null ++++ b/drivers/clocksource/clksrc-of.c +@@ -0,0 +1,35 @@ ++/* ++ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program. If not, see . ++ */ ++ ++#include ++#include ++ ++extern struct of_device_id __clksrc_of_table[]; ++ ++static const struct of_device_id __clksrc_of_table_sentinel ++ __used __section(__clksrc_of_table_end); ++ ++void __init clocksource_of_init(void) ++{ ++ struct device_node *np; ++ const struct of_device_id *match; ++ void (*init_func)(void); ++ ++ for_each_matching_node_and_match(np, __clksrc_of_table, &match) { ++ init_func = match->data; ++ init_func(); ++ } ++} +diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h +index d1ea7ce..1e744c5 100644 +--- a/include/asm-generic/vmlinux.lds.h ++++ b/include/asm-generic/vmlinux.lds.h +@@ -149,6 +149,14 @@ + #define TRACE_SYSCALLS() + #endif + ++#ifdef CONFIG_CLKSRC_OF ++#define CLKSRC_OF_TABLES() . = ALIGN(8); \ ++ VMLINUX_SYMBOL(__clksrc_of_table) = .; \ ++ *(__clksrc_of_table) \ ++ *(__clksrc_of_table_end) ++#else ++#define CLKSRC_OF_TABLES() ++#endif + + #define KERNEL_DTB() \ + STRUCT_ALIGN(); \ +@@ -493,6 +501,7 @@ + DEV_DISCARD(init.rodata) \ + CPU_DISCARD(init.rodata) \ + MEM_DISCARD(init.rodata) \ ++ CLKSRC_OF_TABLES() \ + KERNEL_DTB() + + #define INIT_TEXT \ +diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h +index 4dceaf8..7944f14 100644 +--- a/include/linux/clocksource.h ++++ b/include/linux/clocksource.h +@@ -332,4 +332,13 @@ extern int clocksource_mmio_init(void __iomem *, const char *, + + extern int clocksource_i8253_init(void); + ++#ifdef CONFIG_CLKSRC_OF ++extern void clocksource_of_init(void); ++ ++#define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \ ++ static const struct of_device_id __clksrc_of_table_##name \ ++ __used __section(__clksrc_of_table) \ ++ = { .compatible = compat, .data = fn }; ++#endif ++ + #endif /* _LINUX_CLOCKSOURCE_H */ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0078-clocksource-make-clocksource_of_init-pass-a-device_n.patch b/target/linux/ramips/patches-3.8/0078-clocksource-make-clocksource_of_init-pass-a-device_n.patch new file mode 100644 index 0000000000..218dfc389e --- /dev/null +++ b/target/linux/ramips/patches-3.8/0078-clocksource-make-clocksource_of_init-pass-a-device_n.patch @@ -0,0 +1,36 @@ +From 0e00abf87d50e80b1ce5bda65a4d89adc530ba10 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 23 May 2013 16:58:12 +0200 +Subject: [PATCH 78/79] clocksource: make clocksource_of_init() pass a + device_node pointer + +If we look at the clocksources that are OF enabled we will notice, that they +all do a of_find_matching_node() when being called. This patch changes +clocksource_of_init() to always pass the struct device_node pointer to the +init function. + +Signed-off-by: John Crispin +--- + drivers/clocksource/clksrc-of.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c +index bdabdaa..3ef11fb 100644 +--- a/drivers/clocksource/clksrc-of.c ++++ b/drivers/clocksource/clksrc-of.c +@@ -26,10 +26,10 @@ void __init clocksource_of_init(void) + { + struct device_node *np; + const struct of_device_id *match; +- void (*init_func)(void); ++ void (*init_func)(struct device_node *); + + for_each_matching_node_and_match(np, __clksrc_of_table, &match) { + init_func = match->data; +- init_func(); ++ init_func(np); + } + } +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0079-clocksource-MIPS-ralink-add-support-for-systick-time.patch b/target/linux/ramips/patches-3.8/0079-clocksource-MIPS-ralink-add-support-for-systick-time.patch new file mode 100644 index 0000000000..32c96ec046 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0079-clocksource-MIPS-ralink-add-support-for-systick-time.patch @@ -0,0 +1,282 @@ +From 4f3ae2a7ee1b1c9b9cab287c828f2ed7b1858495 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 20 Jun 2013 19:21:52 +0200 +Subject: [PATCH 79/79] clocksource: MIPS: ralink: add support for systick + timer found on newer ralink SoC + +Signed-off-by: John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define SYSTICK_FREQ (50 * 1000) ++ ++#define SYSTICK_CONFIG 0x00 ++#define SYSTICK_COMPARE 0x04 ++#define SYSTICK_COUNT 0x08 ++ ++/* route systick irq to mips irq 7 instead of the r4k-timer */ ++#define CFG_EXT_STK_EN 0x2 ++/* enable the counter */ ++#define CFG_CNT_EN 0x1 ++ ++struct systick_device { ++ void __iomem *membase; ++ struct clock_event_device dev; ++ int irq_requested; ++ int freq_scale; ++}; ++ ++static void systick_set_clock_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt); ++ ++static int systick_next_event(unsigned long delta, ++ struct clock_event_device *evt) ++{ ++ struct systick_device *sdev = container_of(evt, struct systick_device, dev); ++ u32 count; ++ ++ count = ioread32(sdev->membase + SYSTICK_COUNT); ++ count = (count + delta) % SYSTICK_FREQ; ++ iowrite32(count + delta, sdev->membase + SYSTICK_COMPARE); ++ ++ return 0; ++} ++ ++static void systick_event_handler(struct clock_event_device *dev) ++{ ++ /* noting to do here */ ++} ++ ++static irqreturn_t systick_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *dev = (struct clock_event_device *) dev_id; ++ ++ dev->event_handler(dev); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct systick_device systick = { ++ .dev = { ++ /* cevt-r4k uses 300, make sure systick gets used if available */ ++ .rating = 310, ++ .features = CLOCK_EVT_FEAT_ONESHOT, ++ .set_next_event = systick_next_event, ++ .set_mode = systick_set_clock_mode, ++ .event_handler = systick_event_handler, ++ }, ++}; ++ ++static struct irqaction systick_irqaction = { ++ .handler = systick_interrupt, ++ .flags = IRQF_PERCPU | IRQF_TIMER, ++ .dev_id = &systick.dev, ++}; ++ ++/* ugly hack */ ++#ifdef CONFIG_SOC_MT7620 ++ ++#define CLK_LUT_CFG 0x40 ++#define SLEEP_EN BIT(31) ++ ++static inline void mt7620_freq_scaling(struct systick_device *sdev, int status) ++{ ++ if (sdev->freq_scale == status) ++ return; ++ ++ sdev->freq_scale = status; ++ ++ pr_info("%s: %s autosleep mode\n", systick.dev.name, (status) ? ("enable") : ("disable")); ++ if (status) ++ rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) | SLEEP_EN, CLK_LUT_CFG); ++ else ++ rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) & ~SLEEP_EN, CLK_LUT_CFG); ++} ++#else ++static inline void mt7620_freq_scaling(struct systick_device *sdev, int status) {} ++#endif ++ ++static void systick_set_clock_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ struct systick_device *sdev = container_of(evt, struct systick_device, dev); ++ ++ switch (mode) { ++ case CLOCK_EVT_MODE_ONESHOT: ++ if (!sdev->irq_requested) ++ setup_irq(systick.dev.irq, &systick_irqaction); ++ mt7620_freq_scaling(sdev, 1); ++ sdev->irq_requested = 1; ++ iowrite32(CFG_EXT_STK_EN | CFG_CNT_EN, systick.membase + SYSTICK_CONFIG); ++ break; ++ ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ if (sdev->irq_requested) ++ free_irq(systick.dev.irq, &systick_irqaction); ++ mt7620_freq_scaling(sdev, 0); ++ sdev->irq_requested = 0; ++ iowrite32(0, systick.membase + SYSTICK_CONFIG); ++ break; ++ ++ default: ++ pr_err("%s: Unhandeled mips clock_mode\n", systick.dev.name); ++ break; ++ } ++} ++ ++static void __init ralink_systick_init(struct device_node *np) ++{ ++ systick.membase = of_iomap(np, 0); ++ if (!systick.membase) { ++ pr_err("%s: of_iomap failed", np->name); ++ return; ++ } ++ ++ clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name, ++ SYSTICK_FREQ, 301, 16, clocksource_mmio_readl_up); ++ ++ systick_irqaction.name = np->name; ++ systick.dev.name = np->name; ++ clockevent_set_clock(&systick.dev, SYSTICK_FREQ); ++ systick.dev.max_delta_ns = clockevent_delta2ns(0x7fff, &systick.dev); ++ systick.dev.min_delta_ns = clockevent_delta2ns(0x3, &systick.dev); ++ systick.dev.irq = irq_of_parse_and_map(np, 0); ++ if (!systick.dev.irq) ++ panic("%s: request_irq failed", np->name); ++ ++ clockevents_register_device(&systick.dev); ++ ++ pr_info("%s: runing - mult: %d, shift: %d\n", np->name, systick.dev.mult, systick.dev.shift); ++} ++ ++CLOCKSOURCE_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0080-MIPS-add-ohci-ehci-support.patch b/target/linux/ramips/patches-3.8/0080-MIPS-add-ohci-ehci-support.patch new file mode 100644 index 0000000000..0726ac8df6 --- /dev/null +++ b/target/linux/ramips/patches-3.8/0080-MIPS-add-ohci-ehci-support.patch @@ -0,0 +1,227 @@ +From ec6f3aa022ef6e61f6ca80c6e30610d879654466 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 20 Jun 2013 23:33:05 +0200 +Subject: [PATCH] MIPS: add ohci/ehci support + +Signed-off-by: John Crsipin +--- + arch/mips/ralink/Kconfig | 2 ++ + drivers/usb/Makefile | 3 ++- + drivers/usb/host/ehci-platform.c | 43 ++++++++++++++++++++++++++++++++------ + drivers/usb/host/ohci-platform.c | 37 +++++++++++++++++++++++++++----- + 4 files changed, 73 insertions(+), 12 deletions(-) + +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index c8d5b6c..7cd1188 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -27,6 +27,8 @@ choice + bool "MT7620" + select CLKEVT_RT3352 + select HW_HAS_PCI ++ select USB_ARCH_HAS_OHCI ++ select USB_ARCH_HAS_EHCI + + endchoice + +diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile +index f5ed3d7..41a4741 100644 +--- a/drivers/usb/Makefile ++++ b/drivers/usb/Makefile +@@ -12,6 +12,8 @@ obj-$(CONFIG_USB_DWC3) += dwc3/ + + obj-$(CONFIG_USB_MON) += mon/ + ++obj-$(CONFIG_USB_COMMON) += phy/ ++ + obj-$(CONFIG_PCI) += host/ + obj-$(CONFIG_USB_EHCI_HCD) += host/ + obj-$(CONFIG_USB_ISP116X_HCD) += host/ +@@ -46,7 +48,6 @@ obj-$(CONFIG_USB_MICROTEK) += image/ + obj-$(CONFIG_USB_SERIAL) += serial/ + + obj-$(CONFIG_USB) += misc/ +-obj-$(CONFIG_USB_COMMON) += phy/ + obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/ + + obj-$(CONFIG_USB_ATM) += atm/ +diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c +index 58fa0c9..0050df7 100644 +--- a/drivers/usb/host/ehci-platform.c ++++ b/drivers/usb/host/ehci-platform.c +@@ -23,6 +23,10 @@ + #include + #include + #include ++#include ++#include ++#include ++#include + #include + #include + #include +@@ -61,22 +65,40 @@ static const struct ehci_driver_overrides platform_overrides __initdata = { + .reset = ehci_platform_reset, + }; + ++static struct usb_ehci_pdata ehci_platform_defaults; ++ + static int ehci_platform_probe(struct platform_device *dev) + { + struct usb_hcd *hcd; + struct resource *res_mem; +- struct usb_ehci_pdata *pdata = dev->dev.platform_data; ++ struct usb_ehci_pdata *pdata; + int irq; + int err = -ENOMEM; + +- if (!pdata) { +- WARN_ON(1); +- return -ENODEV; +- } +- + if (usb_disabled()) + return -ENODEV; + ++ /* ++ * use reasonable defaults so platforms don't have to provide these. ++ * with DT probing on ARM, none of these are set. ++ */ ++ if (!dev->dev.platform_data) ++ dev->dev.platform_data = &ehci_platform_defaults; ++ if (!dev->dev.dma_mask) ++ dev->dev.dma_mask = &dev->dev.coherent_dma_mask; ++ if (!dev->dev.coherent_dma_mask) ++ dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); ++ ++ pdata = dev->dev.platform_data; ++ ++#ifdef CONFIG_USB_COMMON ++ hcd->phy = devm_usb_get_phy(&dev->dev, USB_PHY_TYPE_USB2); ++ if (!IS_ERR_OR_NULL(hcd->phy)) { ++ otg_set_host(hcd->phy->otg, ++ &hcd->self); ++ usb_phy_init(hcd->phy); ++ } ++#endif + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "no irq provided"); +@@ -138,6 +160,9 @@ static int ehci_platform_remove(struct platform_device *dev) + if (pdata->power_off) + pdata->power_off(dev); + ++ if (pdata == &ehci_platform_defaults) ++ dev->dev.platform_data = NULL; ++ + return 0; + } + +@@ -182,6 +207,11 @@ static int ehci_platform_resume(struct device *dev) + #define ehci_platform_resume NULL + #endif /* CONFIG_PM */ + ++static const struct of_device_id ralink_ehci_ids[] = { ++ { .compatible = "ralink,rt3xxx-ehci", }, ++ {} ++}; ++ + static const struct platform_device_id ehci_platform_table[] = { + { "ehci-platform", 0 }, + { } +@@ -202,6 +232,7 @@ static struct platform_driver ehci_platform_driver = { + .owner = THIS_MODULE, + .name = "ehci-platform", + .pm = &ehci_platform_pm_ops, ++ .of_match_table = of_match_ptr(ralink_ehci_ids), + } + }; + +diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c +index 084503b..3a60f96 100644 +--- a/drivers/usb/host/ohci-platform.c ++++ b/drivers/usb/host/ohci-platform.c +@@ -15,6 +15,10 @@ + */ + #include + #include ++#include ++#include ++ ++static struct usb_ohci_pdata ohci_platform_defaults; + + static int ohci_platform_reset(struct usb_hcd *hcd) + { +@@ -87,14 +91,22 @@ static int ohci_platform_probe(struct platform_device *dev) + { + struct usb_hcd *hcd; + struct resource *res_mem; +- struct usb_ohci_pdata *pdata = dev->dev.platform_data; ++ struct usb_ohci_pdata *pdata; + int irq; + int err = -ENOMEM; + +- if (!pdata) { +- WARN_ON(1); +- return -ENODEV; +- } ++ /* ++ * use reasonable defaults so platforms don't have to provide these. ++ * with DT probing on ARM, none of these are set. ++ */ ++ if (!dev->dev.platform_data) ++ dev->dev.platform_data = &ohci_platform_defaults; ++ if (!dev->dev.dma_mask) ++ dev->dev.dma_mask = &dev->dev.coherent_dma_mask; ++ if (!dev->dev.coherent_dma_mask) ++ dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); ++ ++ pdata = dev->dev.platform_data; + + if (usb_disabled()) + return -ENODEV; +@@ -124,6 +136,12 @@ static int ohci_platform_probe(struct platform_device *dev) + goto err_power; + } + ++#ifdef CONFIG_USB_COMMON ++ hcd->phy = devm_usb_get_phy(&dev->dev, USB_PHY_TYPE_USB2); ++ if (!IS_ERR_OR_NULL(hcd->phy)) ++ usb_phy_init(hcd->phy); ++#endif ++ + hcd->rsrc_start = res_mem->start; + hcd->rsrc_len = resource_size(res_mem); + +@@ -161,6 +179,9 @@ static int ohci_platform_remove(struct platform_device *dev) + if (pdata->power_off) + pdata->power_off(dev); + ++ if (pdata == &ohci_platform_defaults) ++ dev->dev.platform_data = NULL; ++ + return 0; + } + +@@ -200,6 +221,11 @@ static int ohci_platform_resume(struct device *dev) + #define ohci_platform_resume NULL + #endif /* CONFIG_PM */ + ++static const struct of_device_id ralink_ohci_ids[] = { ++ { .compatible = "ralink,rt3xxx-ohci", }, ++ {} ++}; ++ + static const struct platform_device_id ohci_platform_table[] = { + { "ohci-platform", 0 }, + { } +@@ -220,5 +246,6 @@ static struct platform_driver ohci_platform_driver = { + .owner = THIS_MODULE, + .name = "ohci-platform", + .pm = &ohci_platform_pm_ops, ++ .of_match_table = of_match_ptr(ralink_ohci_ids), + } + }; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.8/0100-MIPS-move-mips_-set-get-_machine_name-to-a-more-gene.patch b/target/linux/ramips/patches-3.8/0100-MIPS-move-mips_-set-get-_machine_name-to-a-more-gene.patch deleted file mode 100644 index d5fe653c38..0000000000 --- a/target/linux/ramips/patches-3.8/0100-MIPS-move-mips_-set-get-_machine_name-to-a-more-gene.patch +++ /dev/null @@ -1,154 +0,0 @@ -From dd4bb7e821d112bff981016fd4e7c014ca9425f9 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 11 Apr 2013 05:34:59 +0000 -Subject: [PATCH 100/137] MIPS: move mips_{set,get}_machine_name() to a more - generic place - -Previously this functionality was only available to users of the mips_machine -api. Moving the code to prom.c allows us to also add a OF wrapper. - -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5164/ ---- - arch/mips/include/asm/mips_machine.h | 4 ---- - arch/mips/include/asm/prom.h | 3 +++ - arch/mips/kernel/mips_machine.c | 21 --------------------- - arch/mips/kernel/proc.c | 2 +- - arch/mips/kernel/prom.c | 31 +++++++++++++++++++++++++++++++ - 5 files changed, 35 insertions(+), 26 deletions(-) - ---- a/arch/mips/include/asm/mips_machine.h -+++ b/arch/mips/include/asm/mips_machine.h -@@ -42,13 +42,9 @@ extern long __mips_machines_end; - #ifdef CONFIG_MIPS_MACHINE - int mips_machtype_setup(char *id) __init; - void mips_machine_setup(void) __init; --void mips_set_machine_name(const char *name) __init; --char *mips_get_machine_name(void); - #else - static inline int mips_machtype_setup(char *id) { return 1; } - static inline void mips_machine_setup(void) { } --static inline void mips_set_machine_name(const char *name) { } --static inline char *mips_get_machine_name(void) { return NULL; } - #endif /* CONFIG_MIPS_MACHINE */ - - #endif /* __ASM_MIPS_MACHINE_H */ ---- a/arch/mips/include/asm/prom.h -+++ b/arch/mips/include/asm/prom.h -@@ -48,4 +48,7 @@ extern void __dt_setup_arch(struct boot_ - static inline void device_tree_init(void) { } - #endif /* CONFIG_OF */ - -+extern char *mips_get_machine_name(void); -+extern void mips_set_machine_name(const char *name); -+ - #endif /* __ASM_PROM_H */ ---- a/arch/mips/kernel/mips_machine.c -+++ b/arch/mips/kernel/mips_machine.c -@@ -13,7 +13,6 @@ - #include - - static struct mips_machine *mips_machine __initdata; --static char *mips_machine_name = "Unknown"; - - #define for_each_machine(mach) \ - for ((mach) = (struct mips_machine *)&__mips_machines_start; \ -@@ -21,25 +20,6 @@ static char *mips_machine_name = "Unknow - (unsigned long)(mach) < (unsigned long)&__mips_machines_end; \ - (mach)++) - --__init void mips_set_machine_name(const char *name) --{ -- char *p; -- -- if (name == NULL) -- return; -- -- p = kstrdup(name, GFP_KERNEL); -- if (!p) -- pr_err("MIPS: no memory for machine_name\n"); -- -- mips_machine_name = p; --} -- --char *mips_get_machine_name(void) --{ -- return mips_machine_name; --} -- - __init int mips_machtype_setup(char *id) - { - struct mips_machine *mach; -@@ -79,7 +59,6 @@ __init void mips_machine_setup(void) - return; - - mips_set_machine_name(mips_machine->mach_name); -- pr_info("MIPS: machine is %s\n", mips_machine_name); - - if (mips_machine->mach_setup) - mips_machine->mach_setup(); ---- a/arch/mips/kernel/proc.c -+++ b/arch/mips/kernel/proc.c -@@ -12,7 +12,7 @@ - #include - #include - #include --#include -+#include - - unsigned int vced_count, vcei_count; - ---- a/arch/mips/kernel/prom.c -+++ b/arch/mips/kernel/prom.c -@@ -23,6 +23,22 @@ - #include - #include - -+static char mips_machine_name[64] = "Unknown"; -+ -+__init void mips_set_machine_name(const char *name) -+{ -+ if (name == NULL) -+ return; -+ -+ strncpy(mips_machine_name, name, sizeof(mips_machine_name)); -+ pr_info("MIPS: machine is %s\n", mips_get_machine_name()); -+} -+ -+char *mips_get_machine_name(void) -+{ -+ return mips_machine_name; -+} -+ - int __init early_init_dt_scan_memory_arch(unsigned long node, - const char *uname, int depth, - void *data) -@@ -50,6 +66,18 @@ void __init early_init_dt_setup_initrd_a - } - #endif - -+int __init early_init_dt_scan_model(unsigned long node, const char *uname, -+ int depth, void *data) -+{ -+ if (!depth) { -+ char *model = of_get_flat_dt_prop(node, "model", NULL); -+ -+ if (model) -+ mips_set_machine_name(model); -+ } -+ return 0; -+} -+ - void __init early_init_devtree(void *params) - { - /* Setup flat device-tree pointer */ -@@ -65,6 +93,9 @@ void __init early_init_devtree(void *par - /* Scan memory nodes */ - of_scan_flat_dt(early_init_dt_scan_root, NULL); - of_scan_flat_dt(early_init_dt_scan_memory_arch, NULL); -+ -+ /* try to load the mips machine name */ -+ of_scan_flat_dt(early_init_dt_scan_model, NULL); - } - - void __init __dt_setup_arch(struct boot_param_header *bph) diff --git a/target/linux/ramips/patches-3.8/0101-MIPS-ralink-add-PCI-IRQ-handling.patch b/target/linux/ramips/patches-3.8/0101-MIPS-ralink-add-PCI-IRQ-handling.patch deleted file mode 100644 index bdb5cfc574..0000000000 --- a/target/linux/ramips/patches-3.8/0101-MIPS-ralink-add-PCI-IRQ-handling.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 16d9eaf22f30ed0b0deddfe8e11426889ccdb556 Mon Sep 17 00:00:00 2001 -From: Gabor Juhos -Date: Wed, 10 Apr 2013 09:07:27 +0200 -Subject: [PATCH 101/137] MIPS: ralink: add PCI IRQ handling - -The Ralink IRQ code was not handling the PCI IRQ yet. Add this functionaility -to make PCI work on rt3883. - -Signed-off-by: John Crispin -Signed-off-by: Gabor Juhos -Patchwork: http://patchwork.linux-mips.org/patch/5165/ ---- - arch/mips/ralink/irq.c | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/arch/mips/ralink/irq.c -+++ b/arch/mips/ralink/irq.c -@@ -31,6 +31,7 @@ - #define INTC_INT_GLOBAL BIT(31) - - #define RALINK_CPU_IRQ_INTC (MIPS_CPU_IRQ_BASE + 2) -+#define RALINK_CPU_IRQ_PCI (MIPS_CPU_IRQ_BASE + 4) - #define RALINK_CPU_IRQ_FE (MIPS_CPU_IRQ_BASE + 5) - #define RALINK_CPU_IRQ_WIFI (MIPS_CPU_IRQ_BASE + 6) - #define RALINK_CPU_IRQ_COUNTER (MIPS_CPU_IRQ_BASE + 7) -@@ -104,6 +105,9 @@ asmlinkage void plat_irq_dispatch(void) - else if (pending & STATUSF_IP6) - do_IRQ(RALINK_CPU_IRQ_WIFI); - -+ else if (pending & STATUSF_IP4) -+ do_IRQ(RALINK_CPU_IRQ_PCI); -+ - else if (pending & STATUSF_IP2) - do_IRQ(RALINK_CPU_IRQ_INTC); - diff --git a/target/linux/ramips/patches-3.8/0102-MIPS-ralink-add-RT3352-register-defines.patch b/target/linux/ramips/patches-3.8/0102-MIPS-ralink-add-RT3352-register-defines.patch deleted file mode 100644 index 2d296db665..0000000000 --- a/target/linux/ramips/patches-3.8/0102-MIPS-ralink-add-RT3352-register-defines.patch +++ /dev/null @@ -1,35 +0,0 @@ -From e6bcdad6f0811daedc2a448f5d7fb98c116a5241 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 21 Mar 2013 19:01:49 +0100 -Subject: [PATCH 102/137] MIPS: ralink: add RT3352 register defines - -Add a few missing defines that are needed to make USB and clock detection work -on the RT3352. - -Signed-off-by: John Crispin -Acked-by: Gabor Juhos -Patchwork: http://patchwork.linux-mips.org/patch/5166/ ---- - arch/mips/include/asm/mach-ralink/rt305x.h | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - ---- a/arch/mips/include/asm/mach-ralink/rt305x.h -+++ b/arch/mips/include/asm/mach-ralink/rt305x.h -@@ -136,4 +136,17 @@ static inline int soc_is_rt5350(void) - #define RT305X_GPIO_MODE_SDRAM BIT(8) - #define RT305X_GPIO_MODE_RGMII BIT(9) - -+#define RT3352_SYSC_REG_SYSCFG0 0x010 -+#define RT3352_SYSC_REG_SYSCFG1 0x014 -+#define RT3352_SYSC_REG_CLKCFG1 0x030 -+#define RT3352_SYSC_REG_RSTCTRL 0x034 -+#define RT3352_SYSC_REG_USB_PS 0x05c -+ -+#define RT3352_CLKCFG0_XTAL_SEL BIT(20) -+#define RT3352_CLKCFG1_UPHY0_CLK_EN BIT(18) -+#define RT3352_CLKCFG1_UPHY1_CLK_EN BIT(20) -+#define RT3352_RSTCTRL_UHST BIT(22) -+#define RT3352_RSTCTRL_UDEV BIT(25) -+#define RT3352_SYSCFG1_USB0_HOST_MODE BIT(10) -+ - #endif diff --git a/target/linux/ramips/patches-3.8/0103-MIPS-ralink-fix-RT305x-clock-setup.patch b/target/linux/ramips/patches-3.8/0103-MIPS-ralink-fix-RT305x-clock-setup.patch deleted file mode 100644 index 7d682e75bd..0000000000 --- a/target/linux/ramips/patches-3.8/0103-MIPS-ralink-fix-RT305x-clock-setup.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 845f786c561c0991d9b4088a2d77b8fd4831d487 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Fri, 22 Mar 2013 19:25:59 +0100 -Subject: [PATCH 103/137] MIPS: ralink: fix RT305x clock setup - -Add a few missing clocks. - -Signed-off-by: John Crispin -Acked-by: Gabor Juhos -Patchwork: http://patchwork.linux-mips.org/patch/5167/ ---- - arch/mips/ralink/rt305x.c | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - ---- a/arch/mips/ralink/rt305x.c -+++ b/arch/mips/ralink/rt305x.c -@@ -124,6 +124,8 @@ struct ralink_pinmux gpio_pinmux = { - void __init ralink_clk_init(void) - { - unsigned long cpu_rate, sys_rate, wdt_rate, uart_rate; -+ unsigned long wmac_rate = 40000000; -+ - u32 t = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG); - - if (soc_is_rt305x() || soc_is_rt3350()) { -@@ -176,11 +178,21 @@ void __init ralink_clk_init(void) - BUG(); - } - -+ if (soc_is_rt3352() || soc_is_rt5350()) { -+ u32 val = rt_sysc_r32(RT3352_SYSC_REG_SYSCFG0); -+ -+ if (!(val & RT3352_CLKCFG0_XTAL_SEL)) -+ wmac_rate = 20000000; -+ } -+ - ralink_clk_add("cpu", cpu_rate); - ralink_clk_add("10000b00.spi", sys_rate); - ralink_clk_add("10000100.timer", wdt_rate); -+ ralink_clk_add("10000120.watchdog", wdt_rate); - ralink_clk_add("10000500.uart", uart_rate); - ralink_clk_add("10000c00.uartlite", uart_rate); -+ ralink_clk_add("10100000.ethernet", sys_rate); -+ ralink_clk_add("10180000.wmac", wmac_rate); - } - - void __init ralink_of_remap(void) diff --git a/target/linux/ramips/patches-3.8/0104-MIPS-ralink-add-missing-comment-in-irq-driver.patch b/target/linux/ramips/patches-3.8/0104-MIPS-ralink-add-missing-comment-in-irq-driver.patch deleted file mode 100644 index 3218384973..0000000000 --- a/target/linux/ramips/patches-3.8/0104-MIPS-ralink-add-missing-comment-in-irq-driver.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 2747613b1bba0d4497ed2c4a77e2011d02029153 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sat, 16 Mar 2013 16:28:54 +0100 -Subject: [PATCH 104/137] MIPS: ralink: add missing comment in irq driver - -Trivial patch that adds a comment that makes the code more readable. - -Signed-off-by: John Crispin -Acked-by: Gabor Juhos -Patchwork: http://patchwork.linux-mips.org/patch/5168/ ---- - arch/mips/ralink/irq.c | 1 + - 1 file changed, 1 insertion(+) - ---- a/arch/mips/ralink/irq.c -+++ b/arch/mips/ralink/irq.c -@@ -166,6 +166,7 @@ static int __init intc_of_init(struct de - irq_set_chained_handler(irq, ralink_intc_irq_handler); - irq_set_handler_data(irq, domain); - -+ /* tell the kernel which irq is used for performance monitoring */ - cp0_perfcount_irq = irq_create_mapping(domain, 9); - - return 0; diff --git a/target/linux/ramips/patches-3.8/0105-MIPS-ralink-add-RT5350-sdram-register-defines.patch b/target/linux/ramips/patches-3.8/0105-MIPS-ralink-add-RT5350-sdram-register-defines.patch deleted file mode 100644 index ccabcbd64b..0000000000 --- a/target/linux/ramips/patches-3.8/0105-MIPS-ralink-add-RT5350-sdram-register-defines.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 31f4b3ca1c9bb4bcbbebbe5db5a33ac82f130d9c Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Mon, 25 Mar 2013 11:19:58 +0100 -Subject: [PATCH 105/137] MIPS: ralink: add RT5350 sdram register defines - -Add a few missing defines that are needed to make memory detection work on the -RT5350. - -Signed-off-by: John Crispin -Acked-by: Gabor Juhos -Patchwork: http://patchwork.linux-mips.org/patch/5169/ ---- - arch/mips/include/asm/mach-ralink/rt305x.h | 8 ++++++++ - 1 file changed, 8 insertions(+) - ---- a/arch/mips/include/asm/mach-ralink/rt305x.h -+++ b/arch/mips/include/asm/mach-ralink/rt305x.h -@@ -97,6 +97,14 @@ static inline int soc_is_rt5350(void) - #define RT5350_SYSCFG0_CPUCLK_320 0x2 - #define RT5350_SYSCFG0_CPUCLK_300 0x3 - -+#define RT5350_SYSCFG0_DRAM_SIZE_SHIFT 12 -+#define RT5350_SYSCFG0_DRAM_SIZE_MASK 7 -+#define RT5350_SYSCFG0_DRAM_SIZE_2M 0 -+#define RT5350_SYSCFG0_DRAM_SIZE_8M 1 -+#define RT5350_SYSCFG0_DRAM_SIZE_16M 2 -+#define RT5350_SYSCFG0_DRAM_SIZE_32M 3 -+#define RT5350_SYSCFG0_DRAM_SIZE_64M 4 -+ - /* multi function gpio pins */ - #define RT305X_GPIO_I2C_SD 1 - #define RT305X_GPIO_I2C_SCLK 2 diff --git a/target/linux/ramips/patches-3.8/0106-MIPS-ralink-make-early_printk-work-on-RT2880.patch b/target/linux/ramips/patches-3.8/0106-MIPS-ralink-make-early_printk-work-on-RT2880.patch deleted file mode 100644 index 4636862fe5..0000000000 --- a/target/linux/ramips/patches-3.8/0106-MIPS-ralink-make-early_printk-work-on-RT2880.patch +++ /dev/null @@ -1,28 +0,0 @@ -From d83e83a544258b68b4411232a31ccce134244a19 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Tue, 9 Apr 2013 18:31:15 +0200 -Subject: [PATCH 106/137] MIPS: ralink: make early_printk work on RT2880 - -RT2880 has a different location for the early serial port. - -Signed-off-by: John Crispin -Acked-by: Gabor Juhos -Patchwork: http://patchwork.linux-mips.org/patch/5170/ ---- - arch/mips/ralink/early_printk.c | 4 ++++ - 1 file changed, 4 insertions(+) - ---- a/arch/mips/ralink/early_printk.c -+++ b/arch/mips/ralink/early_printk.c -@@ -11,7 +11,11 @@ - - #include - -+#ifdef CONFIG_SOC_RT288X -+#define EARLY_UART_BASE 0x300c00 -+#else - #define EARLY_UART_BASE 0x10000c00 -+#endif - - #define UART_REG_RX 0x00 - #define UART_REG_TX 0x04 diff --git a/target/linux/ramips/patches-3.8/0107-MIPS-ralink-rename-gpio_pinmux-to-rt_gpio_pinmux.patch b/target/linux/ramips/patches-3.8/0107-MIPS-ralink-rename-gpio_pinmux-to-rt_gpio_pinmux.patch deleted file mode 100644 index d10b159ac8..0000000000 --- a/target/linux/ramips/patches-3.8/0107-MIPS-ralink-rename-gpio_pinmux-to-rt_gpio_pinmux.patch +++ /dev/null @@ -1,36 +0,0 @@ -From b4c597bd073d5e4c9cee800ac5a25fb9ff1c0ef7 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Fri, 12 Apr 2013 22:12:09 +0200 -Subject: [PATCH 107/137] MIPS: ralink: rename gpio_pinmux to rt_gpio_pinmux - -Add proper namespacing to the variable. - -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5171/ ---- - arch/mips/ralink/common.h | 2 +- - arch/mips/ralink/rt305x.c | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) - ---- a/arch/mips/ralink/common.h -+++ b/arch/mips/ralink/common.h -@@ -24,7 +24,7 @@ struct ralink_pinmux { - int uart_shift; - void (*wdt_reset)(void); - }; --extern struct ralink_pinmux gpio_pinmux; -+extern struct ralink_pinmux rt_gpio_pinmux; - - struct ralink_soc_info { - unsigned char sys_type[RAMIPS_SYS_TYPE_LEN]; ---- a/arch/mips/ralink/rt305x.c -+++ b/arch/mips/ralink/rt305x.c -@@ -114,7 +114,7 @@ void rt305x_wdt_reset(void) - rt_sysc_w32(t, SYSC_REG_SYSTEM_CONFIG); - } - --struct ralink_pinmux gpio_pinmux = { -+struct ralink_pinmux rt_gpio_pinmux = { - .mode = mode_mux, - .uart = uart_mux, - .uart_shift = RT305X_GPIO_MODE_UART0_SHIFT, diff --git a/target/linux/ramips/patches-3.8/0108-MIPS-ralink-make-the-RT305x-pinmuxing-structure-stat.patch b/target/linux/ramips/patches-3.8/0108-MIPS-ralink-make-the-RT305x-pinmuxing-structure-stat.patch deleted file mode 100644 index f09d57f818..0000000000 --- a/target/linux/ramips/patches-3.8/0108-MIPS-ralink-make-the-RT305x-pinmuxing-structure-stat.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 96eba63bf18cd3d96ded62fb809c8cf7e0f2e2c1 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Fri, 12 Apr 2013 22:16:12 +0200 -Subject: [PATCH 108/137] MIPS: ralink: make the RT305x pinmuxing structure - static - -These structures are exported via struct ralink_pinmux rt_gpio_pinmux and can -hence be static. - -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5172/ ---- - arch/mips/ralink/rt305x.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - ---- a/arch/mips/ralink/rt305x.c -+++ b/arch/mips/ralink/rt305x.c -@@ -22,7 +22,7 @@ - - enum rt305x_soc_type rt305x_soc; - --struct ralink_pinmux_grp mode_mux[] = { -+static struct ralink_pinmux_grp mode_mux[] = { - { - .name = "i2c", - .mask = RT305X_GPIO_MODE_I2C, -@@ -61,7 +61,7 @@ struct ralink_pinmux_grp mode_mux[] = { - }, {0} - }; - --struct ralink_pinmux_grp uart_mux[] = { -+static struct ralink_pinmux_grp uart_mux[] = { - { - .name = "uartf", - .mask = RT305X_GPIO_MODE_UARTF, -@@ -103,7 +103,7 @@ struct ralink_pinmux_grp uart_mux[] = { - }, {0} - }; - --void rt305x_wdt_reset(void) -+static void rt305x_wdt_reset(void) - { - u32 t; - diff --git a/target/linux/ramips/patches-3.8/0109-MIPS-ralink-add-pci-group-to-struct-ralink_pinmux.patch b/target/linux/ramips/patches-3.8/0109-MIPS-ralink-add-pci-group-to-struct-ralink_pinmux.patch deleted file mode 100644 index 36e187db2d..0000000000 --- a/target/linux/ramips/patches-3.8/0109-MIPS-ralink-add-pci-group-to-struct-ralink_pinmux.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 61d50d9625dcb454759950ebd45a335c3aaacf84 Mon Sep 17 00:00:00 2001 -From: Gabor Juhos -Date: Fri, 12 Apr 2013 12:40:23 +0200 -Subject: [PATCH 109/137] MIPS: ralink: add pci group to struct ralink_pinmux - -This will be used for RT3662/RT3883. - -Signed-off-by: Gabor Juhos -Acked-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5173/ ---- - arch/mips/ralink/common.h | 3 +++ - 1 file changed, 3 insertions(+) - ---- a/arch/mips/ralink/common.h -+++ b/arch/mips/ralink/common.h -@@ -23,6 +23,9 @@ struct ralink_pinmux { - struct ralink_pinmux_grp *uart; - int uart_shift; - void (*wdt_reset)(void); -+ struct ralink_pinmux_grp *pci; -+ int pci_shift; -+ u32 pci_mask; - }; - extern struct ralink_pinmux rt_gpio_pinmux; - diff --git a/target/linux/ramips/patches-3.8/0110-MIPS-ralink-add-uart-mask-to-struct-ralink_pinmux.patch b/target/linux/ramips/patches-3.8/0110-MIPS-ralink-add-uart-mask-to-struct-ralink_pinmux.patch deleted file mode 100644 index d4474e1022..0000000000 --- a/target/linux/ramips/patches-3.8/0110-MIPS-ralink-add-uart-mask-to-struct-ralink_pinmux.patch +++ /dev/null @@ -1,49 +0,0 @@ -From faf5989efed503b2ee689dad82bb2d60da718d99 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Fri, 12 Apr 2013 12:45:27 +0200 -Subject: [PATCH 110/137] MIPS: ralink: add uart mask to struct ralink_pinmux - -Add a field for the uart muxing mask and set it inside the rt305x setup code. - -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5744/ ---- - arch/mips/ralink/common.h | 1 + - arch/mips/ralink/rt305x.c | 5 +++-- - 2 files changed, 4 insertions(+), 2 deletions(-) - ---- a/arch/mips/ralink/common.h -+++ b/arch/mips/ralink/common.h -@@ -22,6 +22,7 @@ struct ralink_pinmux { - struct ralink_pinmux_grp *mode; - struct ralink_pinmux_grp *uart; - int uart_shift; -+ u32 uart_mask; - void (*wdt_reset)(void); - struct ralink_pinmux_grp *pci; - int pci_shift; ---- a/arch/mips/ralink/rt305x.c -+++ b/arch/mips/ralink/rt305x.c -@@ -91,12 +91,12 @@ static struct ralink_pinmux_grp uart_mux - .name = "gpio uartf", - .mask = RT305X_GPIO_MODE_GPIO_UARTF, - .gpio_first = RT305X_GPIO_7, -- .gpio_last = RT305X_GPIO_14, -+ .gpio_last = RT305X_GPIO_10, - }, { - .name = "gpio i2s", - .mask = RT305X_GPIO_MODE_GPIO_I2S, - .gpio_first = RT305X_GPIO_7, -- .gpio_last = RT305X_GPIO_14, -+ .gpio_last = RT305X_GPIO_10, - }, { - .name = "gpio", - .mask = RT305X_GPIO_MODE_GPIO, -@@ -118,6 +118,7 @@ struct ralink_pinmux rt_gpio_pinmux = { - .mode = mode_mux, - .uart = uart_mux, - .uart_shift = RT305X_GPIO_MODE_UART0_SHIFT, -+ .uart_mask = RT305X_GPIO_MODE_UART0_MASK, - .wdt_reset = rt305x_wdt_reset, - }; - diff --git a/target/linux/ramips/patches-3.8/0111-MIPS-ralink-adds-support-for-RT2880-SoC-family.patch b/target/linux/ramips/patches-3.8/0111-MIPS-ralink-adds-support-for-RT2880-SoC-family.patch deleted file mode 100644 index 9cfa509e54..0000000000 --- a/target/linux/ramips/patches-3.8/0111-MIPS-ralink-adds-support-for-RT2880-SoC-family.patch +++ /dev/null @@ -1,264 +0,0 @@ -From cccb9a7b42227a442ca42d590c838c8b6fa0eba1 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sun, 27 Jan 2013 09:17:20 +0100 -Subject: [PATCH 111/137] MIPS: ralink: adds support for RT2880 SoC family - -Add support code for rt2880 SOC. - -The code detects the SoC and registers the clk / pinmux settings. - -Signed-off-by: John Crispin -Signed-off-by: Gabor Juhos -Patchwork: http://patchwork.linux-mips.org/patch/5176/ ---- - arch/mips/Kconfig | 2 +- - arch/mips/include/asm/mach-ralink/rt288x.h | 49 ++++++++++ - arch/mips/ralink/Kconfig | 3 + - arch/mips/ralink/Makefile | 1 + - arch/mips/ralink/Platform | 5 + - arch/mips/ralink/rt288x.c | 139 ++++++++++++++++++++++++++++ - 6 files changed, 198 insertions(+), 1 deletion(-) - create mode 100644 arch/mips/include/asm/mach-ralink/rt288x.h - create mode 100644 arch/mips/ralink/rt288x.c - ---- a/arch/mips/Kconfig -+++ b/arch/mips/Kconfig -@@ -1189,7 +1189,7 @@ config BOOT_ELF32 - - config MIPS_L1_CACHE_SHIFT - int -- default "4" if MACH_DECSTATION || MIKROTIK_RB532 || PMC_MSP4200_EVAL -+ default "4" if MACH_DECSTATION || MIKROTIK_RB532 || PMC_MSP4200_EVAL || SOC_RT288X - default "6" if MIPS_CPU_SCACHE - default "7" if SGI_IP22 || SGI_IP27 || SGI_IP28 || SNI_RM || CPU_CAVIUM_OCTEON - default "5" ---- /dev/null -+++ b/arch/mips/include/asm/mach-ralink/rt288x.h -@@ -0,0 +1,49 @@ -+/* -+ * 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. -+ * -+ * Parts of this file are based on Ralink's 2.6.21 BSP -+ * -+ * Copyright (C) 2008-2011 Gabor Juhos -+ * Copyright (C) 2008 Imre Kaloz -+ * Copyright (C) 2013 John Crispin -+ */ -+ -+#ifndef _RT288X_REGS_H_ -+#define _RT288X_REGS_H_ -+ -+#define RT2880_SYSC_BASE 0x00300000 -+ -+#define SYSC_REG_CHIP_NAME0 0x00 -+#define SYSC_REG_CHIP_NAME1 0x04 -+#define SYSC_REG_CHIP_ID 0x0c -+#define SYSC_REG_SYSTEM_CONFIG 0x10 -+#define SYSC_REG_CLKCFG 0x30 -+ -+#define RT2880_CHIP_NAME0 0x38325452 -+#define RT2880_CHIP_NAME1 0x20203038 -+ -+#define CHIP_ID_ID_MASK 0xff -+#define CHIP_ID_ID_SHIFT 8 -+#define CHIP_ID_REV_MASK 0xff -+ -+#define SYSTEM_CONFIG_CPUCLK_SHIFT 20 -+#define SYSTEM_CONFIG_CPUCLK_MASK 0x3 -+#define SYSTEM_CONFIG_CPUCLK_250 0x0 -+#define SYSTEM_CONFIG_CPUCLK_266 0x1 -+#define SYSTEM_CONFIG_CPUCLK_280 0x2 -+#define SYSTEM_CONFIG_CPUCLK_300 0x3 -+ -+#define RT2880_GPIO_MODE_I2C BIT(0) -+#define RT2880_GPIO_MODE_UART0 BIT(1) -+#define RT2880_GPIO_MODE_SPI BIT(2) -+#define RT2880_GPIO_MODE_UART1 BIT(3) -+#define RT2880_GPIO_MODE_JTAG BIT(4) -+#define RT2880_GPIO_MODE_MDIO BIT(5) -+#define RT2880_GPIO_MODE_SDRAM BIT(6) -+#define RT2880_GPIO_MODE_PCI BIT(7) -+ -+#define CLKCFG_SRAM_CS_N_WDT BIT(9) -+ -+#endif ---- a/arch/mips/ralink/Kconfig -+++ b/arch/mips/ralink/Kconfig -@@ -6,6 +6,9 @@ choice - help - Select Ralink MIPS SoC type. - -+ config SOC_RT288X -+ bool "RT288x" -+ - config SOC_RT305X - bool "RT305x" - select USB_ARCH_HAS_HCD ---- a/arch/mips/ralink/Makefile -+++ b/arch/mips/ralink/Makefile -@@ -8,6 +8,7 @@ - - obj-y := prom.o of.o reset.o clk.o irq.o - -+obj-$(CONFIG_SOC_RT288X) += rt288x.o - obj-$(CONFIG_SOC_RT305X) += rt305x.o - - obj-$(CONFIG_EARLY_PRINTK) += early_printk.o ---- a/arch/mips/ralink/Platform -+++ b/arch/mips/ralink/Platform -@@ -5,6 +5,11 @@ core-$(CONFIG_RALINK) += arch/mips/rali - cflags-$(CONFIG_RALINK) += -I$(srctree)/arch/mips/include/asm/mach-ralink - - # -+# Ralink RT288x -+# -+load-$(CONFIG_SOC_RT288X) += 0xffffffff88000000 -+ -+# - # Ralink RT305x - # - load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 ---- /dev/null -+++ b/arch/mips/ralink/rt288x.c -@@ -0,0 +1,139 @@ -+/* -+ * 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. -+ * -+ * Parts of this file are based on Ralink's 2.6.21 BSP -+ * -+ * Copyright (C) 2008-2011 Gabor Juhos -+ * Copyright (C) 2008 Imre Kaloz -+ * Copyright (C) 2013 John Crispin -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "common.h" -+ -+static struct ralink_pinmux_grp mode_mux[] = { -+ { -+ .name = "i2c", -+ .mask = RT2880_GPIO_MODE_I2C, -+ .gpio_first = 1, -+ .gpio_last = 2, -+ }, { -+ .name = "spi", -+ .mask = RT2880_GPIO_MODE_SPI, -+ .gpio_first = 3, -+ .gpio_last = 6, -+ }, { -+ .name = "uartlite", -+ .mask = RT2880_GPIO_MODE_UART0, -+ .gpio_first = 7, -+ .gpio_last = 14, -+ }, { -+ .name = "jtag", -+ .mask = RT2880_GPIO_MODE_JTAG, -+ .gpio_first = 17, -+ .gpio_last = 21, -+ }, { -+ .name = "mdio", -+ .mask = RT2880_GPIO_MODE_MDIO, -+ .gpio_first = 22, -+ .gpio_last = 23, -+ }, { -+ .name = "sdram", -+ .mask = RT2880_GPIO_MODE_SDRAM, -+ .gpio_first = 24, -+ .gpio_last = 39, -+ }, { -+ .name = "pci", -+ .mask = RT2880_GPIO_MODE_PCI, -+ .gpio_first = 40, -+ .gpio_last = 71, -+ }, {0} -+}; -+ -+static void rt288x_wdt_reset(void) -+{ -+ u32 t; -+ -+ /* enable WDT reset output on pin SRAM_CS_N */ -+ t = rt_sysc_r32(SYSC_REG_CLKCFG); -+ t |= CLKCFG_SRAM_CS_N_WDT; -+ rt_sysc_w32(t, SYSC_REG_CLKCFG); -+} -+ -+struct ralink_pinmux rt_gpio_pinmux = { -+ .mode = mode_mux, -+ .wdt_reset = rt288x_wdt_reset, -+}; -+ -+void __init ralink_clk_init(void) -+{ -+ unsigned long cpu_rate; -+ u32 t = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG); -+ t = ((t >> SYSTEM_CONFIG_CPUCLK_SHIFT) & SYSTEM_CONFIG_CPUCLK_MASK); -+ -+ switch (t) { -+ case SYSTEM_CONFIG_CPUCLK_250: -+ cpu_rate = 250000000; -+ break; -+ case SYSTEM_CONFIG_CPUCLK_266: -+ cpu_rate = 266666667; -+ break; -+ case SYSTEM_CONFIG_CPUCLK_280: -+ cpu_rate = 280000000; -+ break; -+ case SYSTEM_CONFIG_CPUCLK_300: -+ cpu_rate = 300000000; -+ break; -+ } -+ -+ ralink_clk_add("cpu", cpu_rate); -+ ralink_clk_add("300100.timer", cpu_rate / 2); -+ ralink_clk_add("300120.watchdog", cpu_rate / 2); -+ ralink_clk_add("300500.uart", cpu_rate / 2); -+ ralink_clk_add("300c00.uartlite", cpu_rate / 2); -+ ralink_clk_add("400000.ethernet", cpu_rate / 2); -+} -+ -+void __init ralink_of_remap(void) -+{ -+ rt_sysc_membase = plat_of_remap_node("ralink,rt2880-sysc"); -+ rt_memc_membase = plat_of_remap_node("ralink,rt2880-memc"); -+ -+ if (!rt_sysc_membase || !rt_memc_membase) -+ panic("Failed to remap core resources"); -+} -+ -+void prom_soc_init(struct ralink_soc_info *soc_info) -+{ -+ void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT2880_SYSC_BASE); -+ const char *name; -+ u32 n0; -+ u32 n1; -+ u32 id; -+ -+ n0 = __raw_readl(sysc + SYSC_REG_CHIP_NAME0); -+ n1 = __raw_readl(sysc + SYSC_REG_CHIP_NAME1); -+ id = __raw_readl(sysc + SYSC_REG_CHIP_ID); -+ -+ if (n0 == RT2880_CHIP_NAME0 && n1 == RT2880_CHIP_NAME1) { -+ soc_info->compatible = "ralink,r2880-soc"; -+ name = "RT2880"; -+ } else { -+ panic("rt288x: unknown SoC, n0:%08x n1:%08x", n0, n1); -+ } -+ -+ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN, -+ "Ralink %s id:%u rev:%u", -+ name, -+ (id >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK, -+ (id & CHIP_ID_REV_MASK)); -+} diff --git a/target/linux/ramips/patches-3.8/0112-MIPS-ralink-adds-support-for-RT3883-SoC-family.patch b/target/linux/ramips/patches-3.8/0112-MIPS-ralink-adds-support-for-RT3883-SoC-family.patch deleted file mode 100644 index 162d0cc180..0000000000 --- a/target/linux/ramips/patches-3.8/0112-MIPS-ralink-adds-support-for-RT3883-SoC-family.patch +++ /dev/null @@ -1,553 +0,0 @@ -From 5eb4dfe5072595e0706de3364f2da45378dbaca6 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sun, 27 Jan 2013 09:39:02 +0100 -Subject: [PATCH 112/137] MIPS: ralink: adds support for RT3883 SoC family - -Add support code for rt3883 SOC. - -The code detects the SoC and registers the clk / pinmux settings. - -Signed-off-by: John Crispin -Signed-off-by: Gabor Juhos -Patchwork: http://patchwork.linux-mips.org/patch/5185/ ---- - arch/mips/include/asm/mach-ralink/rt3883.h | 247 ++++++++++++++++++++++++++++ - arch/mips/ralink/Kconfig | 5 + - arch/mips/ralink/Makefile | 1 + - arch/mips/ralink/Platform | 5 + - arch/mips/ralink/rt3883.c | 242 +++++++++++++++++++++++++++ - 5 files changed, 500 insertions(+) - create mode 100644 arch/mips/include/asm/mach-ralink/rt3883.h - create mode 100644 arch/mips/ralink/rt3883.c - ---- /dev/null -+++ b/arch/mips/include/asm/mach-ralink/rt3883.h -@@ -0,0 +1,248 @@ -+/* -+ * Ralink RT3662/RT3883 SoC register definitions -+ * -+ * Copyright (C) 2011-2012 Gabor Juhos -+ * -+ * 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 _RT3883_REGS_H_ -+#define _RT3883_REGS_H_ -+ -+#include -+ -+#define RT3883_SDRAM_BASE 0x00000000 -+#define RT3883_SYSC_BASE 0x10000000 -+#define RT3883_TIMER_BASE 0x10000100 -+#define RT3883_INTC_BASE 0x10000200 -+#define RT3883_MEMC_BASE 0x10000300 -+#define RT3883_UART0_BASE 0x10000500 -+#define RT3883_PIO_BASE 0x10000600 -+#define RT3883_FSCC_BASE 0x10000700 -+#define RT3883_NANDC_BASE 0x10000810 -+#define RT3883_I2C_BASE 0x10000900 -+#define RT3883_I2S_BASE 0x10000a00 -+#define RT3883_SPI_BASE 0x10000b00 -+#define RT3883_UART1_BASE 0x10000c00 -+#define RT3883_PCM_BASE 0x10002000 -+#define RT3883_GDMA_BASE 0x10002800 -+#define RT3883_CODEC1_BASE 0x10003000 -+#define RT3883_CODEC2_BASE 0x10003800 -+#define RT3883_FE_BASE 0x10100000 -+#define RT3883_ROM_BASE 0x10118000 -+#define RT3883_USBDEV_BASE 0x10112000 -+#define RT3883_PCI_BASE 0x10140000 -+#define RT3883_WLAN_BASE 0x10180000 -+#define RT3883_USBHOST_BASE 0x101c0000 -+#define RT3883_BOOT_BASE 0x1c000000 -+#define RT3883_SRAM_BASE 0x1e000000 -+#define RT3883_PCIMEM_BASE 0x20000000 -+ -+#define RT3883_EHCI_BASE (RT3883_USBHOST_BASE) -+#define RT3883_OHCI_BASE (RT3883_USBHOST_BASE + 0x1000) -+ -+#define RT3883_SYSC_SIZE 0x100 -+#define RT3883_TIMER_SIZE 0x100 -+#define RT3883_INTC_SIZE 0x100 -+#define RT3883_MEMC_SIZE 0x100 -+#define RT3883_UART0_SIZE 0x100 -+#define RT3883_UART1_SIZE 0x100 -+#define RT3883_PIO_SIZE 0x100 -+#define RT3883_FSCC_SIZE 0x100 -+#define RT3883_NANDC_SIZE 0x0f0 -+#define RT3883_I2C_SIZE 0x100 -+#define RT3883_I2S_SIZE 0x100 -+#define RT3883_SPI_SIZE 0x100 -+#define RT3883_PCM_SIZE 0x800 -+#define RT3883_GDMA_SIZE 0x800 -+#define RT3883_CODEC1_SIZE 0x800 -+#define RT3883_CODEC2_SIZE 0x800 -+#define RT3883_FE_SIZE 0x10000 -+#define RT3883_ROM_SIZE 0x4000 -+#define RT3883_USBDEV_SIZE 0x4000 -+#define RT3883_PCI_SIZE 0x40000 -+#define RT3883_WLAN_SIZE 0x40000 -+#define RT3883_USBHOST_SIZE 0x40000 -+#define RT3883_BOOT_SIZE (32 * 1024 * 1024) -+#define RT3883_SRAM_SIZE (32 * 1024 * 1024) -+ -+/* SYSC registers */ -+#define RT3883_SYSC_REG_CHIPID0_3 0x00 /* Chip ID 0 */ -+#define RT3883_SYSC_REG_CHIPID4_7 0x04 /* Chip ID 1 */ -+#define RT3883_SYSC_REG_REVID 0x0c /* Chip Revision Identification */ -+#define RT3883_SYSC_REG_SYSCFG0 0x10 /* System Configuration 0 */ -+#define RT3883_SYSC_REG_SYSCFG1 0x14 /* System Configuration 1 */ -+#define RT3883_SYSC_REG_CLKCFG0 0x2c /* Clock Configuration 0 */ -+#define RT3883_SYSC_REG_CLKCFG1 0x30 /* Clock Configuration 1 */ -+#define RT3883_SYSC_REG_RSTCTRL 0x34 /* Reset Control*/ -+#define RT3883_SYSC_REG_RSTSTAT 0x38 /* Reset Status*/ -+#define RT3883_SYSC_REG_USB_PS 0x5c /* USB Power saving control */ -+#define RT3883_SYSC_REG_GPIO_MODE 0x60 /* GPIO Purpose Select */ -+#define RT3883_SYSC_REG_PCIE_CLK_GEN0 0x7c -+#define RT3883_SYSC_REG_PCIE_CLK_GEN1 0x80 -+#define RT3883_SYSC_REG_PCIE_CLK_GEN2 0x84 -+#define RT3883_SYSC_REG_PMU 0x88 -+#define RT3883_SYSC_REG_PMU1 0x8c -+ -+#define RT3883_CHIP_NAME0 0x38335452 -+#define RT3883_CHIP_NAME1 0x20203338 -+ -+#define RT3883_REVID_VER_ID_MASK 0x0f -+#define RT3883_REVID_VER_ID_SHIFT 8 -+#define RT3883_REVID_ECO_ID_MASK 0x0f -+ -+#define RT3883_SYSCFG0_DRAM_TYPE_DDR2 BIT(17) -+#define RT3883_SYSCFG0_CPUCLK_SHIFT 8 -+#define RT3883_SYSCFG0_CPUCLK_MASK 0x3 -+#define RT3883_SYSCFG0_CPUCLK_250 0x0 -+#define RT3883_SYSCFG0_CPUCLK_384 0x1 -+#define RT3883_SYSCFG0_CPUCLK_480 0x2 -+#define RT3883_SYSCFG0_CPUCLK_500 0x3 -+ -+#define RT3883_SYSCFG1_USB0_HOST_MODE BIT(10) -+#define RT3883_SYSCFG1_PCIE_RC_MODE BIT(8) -+#define RT3883_SYSCFG1_PCI_HOST_MODE BIT(7) -+#define RT3883_SYSCFG1_PCI_66M_MODE BIT(6) -+#define RT3883_SYSCFG1_GPIO2_AS_WDT_OUT BIT(2) -+ -+#define RT3883_CLKCFG1_PCIE_CLK_EN BIT(21) -+#define RT3883_CLKCFG1_UPHY1_CLK_EN BIT(20) -+#define RT3883_CLKCFG1_PCI_CLK_EN BIT(19) -+#define RT3883_CLKCFG1_UPHY0_CLK_EN BIT(18) -+ -+#define RT3883_GPIO_MODE_I2C BIT(0) -+#define RT3883_GPIO_MODE_SPI BIT(1) -+#define RT3883_GPIO_MODE_UART0_SHIFT 2 -+#define RT3883_GPIO_MODE_UART0_MASK 0x7 -+#define RT3883_GPIO_MODE_UART0(x) ((x) << RT3883_GPIO_MODE_UART0_SHIFT) -+#define RT3883_GPIO_MODE_UARTF 0x0 -+#define RT3883_GPIO_MODE_PCM_UARTF 0x1 -+#define RT3883_GPIO_MODE_PCM_I2S 0x2 -+#define RT3883_GPIO_MODE_I2S_UARTF 0x3 -+#define RT3883_GPIO_MODE_PCM_GPIO 0x4 -+#define RT3883_GPIO_MODE_GPIO_UARTF 0x5 -+#define RT3883_GPIO_MODE_GPIO_I2S 0x6 -+#define RT3883_GPIO_MODE_GPIO 0x7 -+#define RT3883_GPIO_MODE_UART1 BIT(5) -+#define RT3883_GPIO_MODE_JTAG BIT(6) -+#define RT3883_GPIO_MODE_MDIO BIT(7) -+#define RT3883_GPIO_MODE_GE1 BIT(9) -+#define RT3883_GPIO_MODE_GE2 BIT(10) -+#define RT3883_GPIO_MODE_PCI_SHIFT 11 -+#define RT3883_GPIO_MODE_PCI_MASK 0x7 -+#define RT3883_GPIO_MODE_PCI (RT3883_GPIO_MODE_PCI_MASK << RT3883_GPIO_MODE_PCI_SHIFT) -+#define RT3883_GPIO_MODE_LNA_A_SHIFT 16 -+#define RT3883_GPIO_MODE_LNA_A_MASK 0x3 -+#define _RT3883_GPIO_MODE_LNA_A(_x) ((_x) << RT3883_GPIO_MODE_LNA_A_SHIFT) -+#define RT3883_GPIO_MODE_LNA_A_GPIO 0x3 -+#define RT3883_GPIO_MODE_LNA_A _RT3883_GPIO_MODE_LNA_A(RT3883_GPIO_MODE_LNA_A_MASK) -+#define RT3883_GPIO_MODE_LNA_G_SHIFT 18 -+#define RT3883_GPIO_MODE_LNA_G_MASK 0x3 -+#define _RT3883_GPIO_MODE_LNA_G(_x) ((_x) << RT3883_GPIO_MODE_LNA_G_SHIFT) -+#define RT3883_GPIO_MODE_LNA_G_GPIO 0x3 -+#define RT3883_GPIO_MODE_LNA_G _RT3883_GPIO_MODE_LNA_G(RT3883_GPIO_MODE_LNA_G_MASK) -+ -+#define RT3883_GPIO_I2C_SD 1 -+#define RT3883_GPIO_I2C_SCLK 2 -+#define RT3883_GPIO_SPI_CS0 3 -+#define RT3883_GPIO_SPI_CLK 4 -+#define RT3883_GPIO_SPI_MOSI 5 -+#define RT3883_GPIO_SPI_MISO 6 -+#define RT3883_GPIO_7 7 -+#define RT3883_GPIO_10 10 -+#define RT3883_GPIO_11 11 -+#define RT3883_GPIO_14 14 -+#define RT3883_GPIO_UART1_TXD 15 -+#define RT3883_GPIO_UART1_RXD 16 -+#define RT3883_GPIO_JTAG_TDO 17 -+#define RT3883_GPIO_JTAG_TDI 18 -+#define RT3883_GPIO_JTAG_TMS 19 -+#define RT3883_GPIO_JTAG_TCLK 20 -+#define RT3883_GPIO_JTAG_TRST_N 21 -+#define RT3883_GPIO_MDIO_MDC 22 -+#define RT3883_GPIO_MDIO_MDIO 23 -+#define RT3883_GPIO_LNA_PE_A0 32 -+#define RT3883_GPIO_LNA_PE_A1 33 -+#define RT3883_GPIO_LNA_PE_A2 34 -+#define RT3883_GPIO_LNA_PE_G0 35 -+#define RT3883_GPIO_LNA_PE_G1 36 -+#define RT3883_GPIO_LNA_PE_G2 37 -+#define RT3883_GPIO_PCI_AD0 40 -+#define RT3883_GPIO_PCI_AD31 71 -+#define RT3883_GPIO_GE2_TXD0 72 -+#define RT3883_GPIO_GE2_TXD1 73 -+#define RT3883_GPIO_GE2_TXD2 74 -+#define RT3883_GPIO_GE2_TXD3 75 -+#define RT3883_GPIO_GE2_TXEN 76 -+#define RT3883_GPIO_GE2_TXCLK 77 -+#define RT3883_GPIO_GE2_RXD0 78 -+#define RT3883_GPIO_GE2_RXD1 79 -+#define RT3883_GPIO_GE2_RXD2 80 -+#define RT3883_GPIO_GE2_RXD3 81 -+#define RT3883_GPIO_GE2_RXDV 82 -+#define RT3883_GPIO_GE2_RXCLK 83 -+#define RT3883_GPIO_GE1_TXD0 84 -+#define RT3883_GPIO_GE1_TXD1 85 -+#define RT3883_GPIO_GE1_TXD2 86 -+#define RT3883_GPIO_GE1_TXD3 87 -+#define RT3883_GPIO_GE1_TXEN 88 -+#define RT3883_GPIO_GE1_TXCLK 89 -+#define RT3883_GPIO_GE1_RXD0 90 -+#define RT3883_GPIO_GE1_RXD1 91 -+#define RT3883_GPIO_GE1_RXD2 92 -+#define RT3883_GPIO_GE1_RXD3 93 -+#define RT3883_GPIO_GE1_RXDV 94 -+#define RT3883_GPIO_GE1_RXCLK 95 -+ -+#define RT3883_RSTCTRL_PCIE_PCI_PDM BIT(27) -+#define RT3883_RSTCTRL_FLASH BIT(26) -+#define RT3883_RSTCTRL_UDEV BIT(25) -+#define RT3883_RSTCTRL_PCI BIT(24) -+#define RT3883_RSTCTRL_PCIE BIT(23) -+#define RT3883_RSTCTRL_UHST BIT(22) -+#define RT3883_RSTCTRL_FE BIT(21) -+#define RT3883_RSTCTRL_WLAN BIT(20) -+#define RT3883_RSTCTRL_UART1 BIT(29) -+#define RT3883_RSTCTRL_SPI BIT(18) -+#define RT3883_RSTCTRL_I2S BIT(17) -+#define RT3883_RSTCTRL_I2C BIT(16) -+#define RT3883_RSTCTRL_NAND BIT(15) -+#define RT3883_RSTCTRL_DMA BIT(14) -+#define RT3883_RSTCTRL_PIO BIT(13) -+#define RT3883_RSTCTRL_UART BIT(12) -+#define RT3883_RSTCTRL_PCM BIT(11) -+#define RT3883_RSTCTRL_MC BIT(10) -+#define RT3883_RSTCTRL_INTC BIT(9) -+#define RT3883_RSTCTRL_TIMER BIT(8) -+#define RT3883_RSTCTRL_SYS BIT(0) -+ -+#define RT3883_INTC_INT_SYSCTL BIT(0) -+#define RT3883_INTC_INT_TIMER0 BIT(1) -+#define RT3883_INTC_INT_TIMER1 BIT(2) -+#define RT3883_INTC_INT_IA BIT(3) -+#define RT3883_INTC_INT_PCM BIT(4) -+#define RT3883_INTC_INT_UART0 BIT(5) -+#define RT3883_INTC_INT_PIO BIT(6) -+#define RT3883_INTC_INT_DMA BIT(7) -+#define RT3883_INTC_INT_NAND BIT(8) -+#define RT3883_INTC_INT_PERFC BIT(9) -+#define RT3883_INTC_INT_I2S BIT(10) -+#define RT3883_INTC_INT_UART1 BIT(12) -+#define RT3883_INTC_INT_UHST BIT(18) -+#define RT3883_INTC_INT_UDEV BIT(19) -+ -+/* FLASH/SRAM/Codec Controller registers */ -+#define RT3883_FSCC_REG_FLASH_CFG0 0x00 -+#define RT3883_FSCC_REG_FLASH_CFG1 0x04 -+#define RT3883_FSCC_REG_CODEC_CFG0 0x40 -+#define RT3883_FSCC_REG_CODEC_CFG1 0x44 -+ -+#define RT3883_FLASH_CFG_WIDTH_SHIFT 26 -+#define RT3883_FLASH_CFG_WIDTH_MASK 0x3 -+#define RT3883_FLASH_CFG_WIDTH_8BIT 0x0 -+#define RT3883_FLASH_CFG_WIDTH_16BIT 0x1 -+#define RT3883_FLASH_CFG_WIDTH_32BIT 0x2 -+ -+#endif /* _RT3883_REGS_H_ */ ---- a/arch/mips/ralink/Kconfig -+++ b/arch/mips/ralink/Kconfig -@@ -15,6 +15,11 @@ choice - select USB_ARCH_HAS_OHCI - select USB_ARCH_HAS_EHCI - -+ config SOC_RT3883 -+ bool "RT3883" -+ select USB_ARCH_HAS_OHCI -+ select USB_ARCH_HAS_EHCI -+ - endchoice - - choice ---- a/arch/mips/ralink/Makefile -+++ b/arch/mips/ralink/Makefile -@@ -10,6 +10,7 @@ obj-y := prom.o of.o reset.o clk.o irq.o - - obj-$(CONFIG_SOC_RT288X) += rt288x.o - obj-$(CONFIG_SOC_RT305X) += rt305x.o -+obj-$(CONFIG_SOC_RT3883) += rt3883.o - - obj-$(CONFIG_EARLY_PRINTK) += early_printk.o - ---- a/arch/mips/ralink/Platform -+++ b/arch/mips/ralink/Platform -@@ -13,3 +13,8 @@ load-$(CONFIG_SOC_RT288X) += 0xffffffff8 - # Ralink RT305x - # - load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 -+ -+# -+# Ralink RT3883 -+# -+load-$(CONFIG_SOC_RT3883) += 0xffffffff80000000 ---- /dev/null -+++ b/arch/mips/ralink/rt3883.c -@@ -0,0 +1,242 @@ -+/* -+ * 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. -+ * -+ * Parts of this file are based on Ralink's 2.6.21 BSP -+ * -+ * Copyright (C) 2008 Imre Kaloz -+ * Copyright (C) 2008-2011 Gabor Juhos -+ * Copyright (C) 2013 John Crispin -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "common.h" -+ -+static struct ralink_pinmux_grp mode_mux[] = { -+ { -+ .name = "i2c", -+ .mask = RT3883_GPIO_MODE_I2C, -+ .gpio_first = RT3883_GPIO_I2C_SD, -+ .gpio_last = RT3883_GPIO_I2C_SCLK, -+ }, { -+ .name = "spi", -+ .mask = RT3883_GPIO_MODE_SPI, -+ .gpio_first = RT3883_GPIO_SPI_CS0, -+ .gpio_last = RT3883_GPIO_SPI_MISO, -+ }, { -+ .name = "uartlite", -+ .mask = RT3883_GPIO_MODE_UART1, -+ .gpio_first = RT3883_GPIO_UART1_TXD, -+ .gpio_last = RT3883_GPIO_UART1_RXD, -+ }, { -+ .name = "jtag", -+ .mask = RT3883_GPIO_MODE_JTAG, -+ .gpio_first = RT3883_GPIO_JTAG_TDO, -+ .gpio_last = RT3883_GPIO_JTAG_TCLK, -+ }, { -+ .name = "mdio", -+ .mask = RT3883_GPIO_MODE_MDIO, -+ .gpio_first = RT3883_GPIO_MDIO_MDC, -+ .gpio_last = RT3883_GPIO_MDIO_MDIO, -+ }, { -+ .name = "ge1", -+ .mask = RT3883_GPIO_MODE_GE1, -+ .gpio_first = RT3883_GPIO_GE1_TXD0, -+ .gpio_last = RT3883_GPIO_GE1_RXCLK, -+ }, { -+ .name = "ge2", -+ .mask = RT3883_GPIO_MODE_GE2, -+ .gpio_first = RT3883_GPIO_GE2_TXD0, -+ .gpio_last = RT3883_GPIO_GE2_RXCLK, -+ }, { -+ .name = "pci", -+ .mask = RT3883_GPIO_MODE_PCI, -+ .gpio_first = RT3883_GPIO_PCI_AD0, -+ .gpio_last = RT3883_GPIO_PCI_AD31, -+ }, { -+ .name = "lna a", -+ .mask = RT3883_GPIO_MODE_LNA_A, -+ .gpio_first = RT3883_GPIO_LNA_PE_A0, -+ .gpio_last = RT3883_GPIO_LNA_PE_A2, -+ }, { -+ .name = "lna g", -+ .mask = RT3883_GPIO_MODE_LNA_G, -+ .gpio_first = RT3883_GPIO_LNA_PE_G0, -+ .gpio_last = RT3883_GPIO_LNA_PE_G2, -+ }, {0} -+}; -+ -+static struct ralink_pinmux_grp uart_mux[] = { -+ { -+ .name = "uartf", -+ .mask = RT3883_GPIO_MODE_UARTF, -+ .gpio_first = RT3883_GPIO_7, -+ .gpio_last = RT3883_GPIO_14, -+ }, { -+ .name = "pcm uartf", -+ .mask = RT3883_GPIO_MODE_PCM_UARTF, -+ .gpio_first = RT3883_GPIO_7, -+ .gpio_last = RT3883_GPIO_14, -+ }, { -+ .name = "pcm i2s", -+ .mask = RT3883_GPIO_MODE_PCM_I2S, -+ .gpio_first = RT3883_GPIO_7, -+ .gpio_last = RT3883_GPIO_14, -+ }, { -+ .name = "i2s uartf", -+ .mask = RT3883_GPIO_MODE_I2S_UARTF, -+ .gpio_first = RT3883_GPIO_7, -+ .gpio_last = RT3883_GPIO_14, -+ }, { -+ .name = "pcm gpio", -+ .mask = RT3883_GPIO_MODE_PCM_GPIO, -+ .gpio_first = RT3883_GPIO_11, -+ .gpio_last = RT3883_GPIO_14, -+ }, { -+ .name = "gpio uartf", -+ .mask = RT3883_GPIO_MODE_GPIO_UARTF, -+ .gpio_first = RT3883_GPIO_7, -+ .gpio_last = RT3883_GPIO_10, -+ }, { -+ .name = "gpio i2s", -+ .mask = RT3883_GPIO_MODE_GPIO_I2S, -+ .gpio_first = RT3883_GPIO_7, -+ .gpio_last = RT3883_GPIO_10, -+ }, { -+ .name = "gpio", -+ .mask = RT3883_GPIO_MODE_GPIO, -+ }, {0} -+}; -+ -+static struct ralink_pinmux_grp pci_mux[] = { -+ { -+ .name = "pci-dev", -+ .mask = 0, -+ .gpio_first = RT3883_GPIO_PCI_AD0, -+ .gpio_last = RT3883_GPIO_PCI_AD31, -+ }, { -+ .name = "pci-host2", -+ .mask = 1, -+ .gpio_first = RT3883_GPIO_PCI_AD0, -+ .gpio_last = RT3883_GPIO_PCI_AD31, -+ }, { -+ .name = "pci-host1", -+ .mask = 2, -+ .gpio_first = RT3883_GPIO_PCI_AD0, -+ .gpio_last = RT3883_GPIO_PCI_AD31, -+ }, { -+ .name = "pci-fnc", -+ .mask = 3, -+ .gpio_first = RT3883_GPIO_PCI_AD0, -+ .gpio_last = RT3883_GPIO_PCI_AD31, -+ }, { -+ .name = "pci-gpio", -+ .mask = 7, -+ .gpio_first = RT3883_GPIO_PCI_AD0, -+ .gpio_last = RT3883_GPIO_PCI_AD31, -+ }, {0} -+}; -+ -+static void rt3883_wdt_reset(void) -+{ -+ u32 t; -+ -+ /* enable WDT reset output on GPIO 2 */ -+ t = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG1); -+ t |= RT3883_SYSCFG1_GPIO2_AS_WDT_OUT; -+ rt_sysc_w32(t, RT3883_SYSC_REG_SYSCFG1); -+} -+ -+struct ralink_pinmux rt_gpio_pinmux = { -+ .mode = mode_mux, -+ .uart = uart_mux, -+ .uart_shift = RT3883_GPIO_MODE_UART0_SHIFT, -+ .uart_mask = RT3883_GPIO_MODE_GPIO, -+ .wdt_reset = rt3883_wdt_reset, -+ .pci = pci_mux, -+ .pci_shift = RT3883_GPIO_MODE_PCI_SHIFT, -+ .pci_mask = RT3883_GPIO_MODE_PCI_MASK, -+}; -+ -+void __init ralink_clk_init(void) -+{ -+ unsigned long cpu_rate, sys_rate; -+ u32 syscfg0; -+ u32 clksel; -+ u32 ddr2; -+ -+ syscfg0 = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG0); -+ clksel = ((syscfg0 >> RT3883_SYSCFG0_CPUCLK_SHIFT) & -+ RT3883_SYSCFG0_CPUCLK_MASK); -+ ddr2 = syscfg0 & RT3883_SYSCFG0_DRAM_TYPE_DDR2; -+ -+ switch (clksel) { -+ case RT3883_SYSCFG0_CPUCLK_250: -+ cpu_rate = 250000000; -+ sys_rate = (ddr2) ? 125000000 : 83000000; -+ break; -+ case RT3883_SYSCFG0_CPUCLK_384: -+ cpu_rate = 384000000; -+ sys_rate = (ddr2) ? 128000000 : 96000000; -+ break; -+ case RT3883_SYSCFG0_CPUCLK_480: -+ cpu_rate = 480000000; -+ sys_rate = (ddr2) ? 160000000 : 120000000; -+ break; -+ case RT3883_SYSCFG0_CPUCLK_500: -+ cpu_rate = 500000000; -+ sys_rate = (ddr2) ? 166000000 : 125000000; -+ break; -+ } -+ -+ ralink_clk_add("cpu", cpu_rate); -+ ralink_clk_add("10000100.timer", sys_rate); -+ ralink_clk_add("10000120.watchdog", sys_rate); -+ ralink_clk_add("10000500.uart", 40000000); -+ ralink_clk_add("10000b00.spi", sys_rate); -+ ralink_clk_add("10000c00.uartlite", 40000000); -+ ralink_clk_add("10100000.ethernet", sys_rate); -+} -+ -+void __init ralink_of_remap(void) -+{ -+ rt_sysc_membase = plat_of_remap_node("ralink,rt3883-sysc"); -+ rt_memc_membase = plat_of_remap_node("ralink,rt3883-memc"); -+ -+ if (!rt_sysc_membase || !rt_memc_membase) -+ panic("Failed to remap core resources"); -+} -+ -+void prom_soc_init(struct ralink_soc_info *soc_info) -+{ -+ void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT3883_SYSC_BASE); -+ const char *name; -+ u32 n0; -+ u32 n1; -+ u32 id; -+ -+ n0 = __raw_readl(sysc + RT3883_SYSC_REG_CHIPID0_3); -+ n1 = __raw_readl(sysc + RT3883_SYSC_REG_CHIPID4_7); -+ id = __raw_readl(sysc + RT3883_SYSC_REG_REVID); -+ -+ if (n0 == RT3883_CHIP_NAME0 && n1 == RT3883_CHIP_NAME1) { -+ soc_info->compatible = "ralink,rt3883-soc"; -+ name = "RT3883"; -+ } else { -+ panic("rt3883: unknown SoC, n0:%08x n1:%08x", n0, n1); -+ } -+ -+ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN, -+ "Ralink %s ver:%u eco:%u", -+ name, -+ (id >> RT3883_REVID_VER_ID_SHIFT) & RT3883_REVID_VER_ID_MASK, -+ (id & RT3883_REVID_ECO_ID_MASK)); -+} diff --git a/target/linux/ramips/patches-3.8/0113-MIPS-ralink-adds-support-for-MT7620-SoC-family.patch b/target/linux/ramips/patches-3.8/0113-MIPS-ralink-adds-support-for-MT7620-SoC-family.patch deleted file mode 100644 index d52803413c..0000000000 --- a/target/linux/ramips/patches-3.8/0113-MIPS-ralink-adds-support-for-MT7620-SoC-family.patch +++ /dev/null @@ -1,351 +0,0 @@ -From a8d7045a9530d0a9e0c65c0f81852bd57ebde53c Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 21 Mar 2013 17:49:02 +0100 -Subject: [PATCH 113/137] MIPS: ralink: adds support for MT7620 SoC family - -Add support code for mt7620 SOC. - -The code detects the SoC and registers the clk / pinmux settings. - -Signed-off-by: John Crispin -Acked-by: Gabor Juhos -Patchwork: http://patchwork.linux-mips.org/patch/5177/ ---- - arch/mips/include/asm/mach-ralink/mt7620.h | 76 ++++++++++ - arch/mips/ralink/Kconfig | 3 + - arch/mips/ralink/Makefile | 1 + - arch/mips/ralink/Platform | 5 + - arch/mips/ralink/mt7620.c | 214 ++++++++++++++++++++++++++++ - 5 files changed, 299 insertions(+) - create mode 100644 arch/mips/include/asm/mach-ralink/mt7620.h - create mode 100644 arch/mips/ralink/mt7620.c - ---- /dev/null -+++ b/arch/mips/include/asm/mach-ralink/mt7620.h -@@ -0,0 +1,76 @@ -+/* -+ * 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. -+ * -+ * Parts of this file are based on Ralink's 2.6.21 BSP -+ * -+ * Copyright (C) 2008-2011 Gabor Juhos -+ * Copyright (C) 2008 Imre Kaloz -+ * Copyright (C) 2013 John Crispin -+ */ -+ -+#ifndef _MT7620_REGS_H_ -+#define _MT7620_REGS_H_ -+ -+#define MT7620_SYSC_BASE 0x10000000 -+ -+#define SYSC_REG_CHIP_NAME0 0x00 -+#define SYSC_REG_CHIP_NAME1 0x04 -+#define SYSC_REG_CHIP_REV 0x0c -+#define SYSC_REG_SYSTEM_CONFIG0 0x10 -+#define SYSC_REG_SYSTEM_CONFIG1 0x14 -+#define SYSC_REG_CPLL_CONFIG0 0x54 -+#define SYSC_REG_CPLL_CONFIG1 0x58 -+ -+#define MT7620N_CHIP_NAME0 0x33365452 -+#define MT7620N_CHIP_NAME1 0x20203235 -+ -+#define MT7620A_CHIP_NAME0 0x3637544d -+#define MT7620A_CHIP_NAME1 0x20203032 -+ -+#define CHIP_REV_PKG_MASK 0x1 -+#define CHIP_REV_PKG_SHIFT 16 -+#define CHIP_REV_VER_MASK 0xf -+#define CHIP_REV_VER_SHIFT 8 -+#define CHIP_REV_ECO_MASK 0xf -+ -+#define CPLL_SW_CONFIG_SHIFT 31 -+#define CPLL_SW_CONFIG_MASK 0x1 -+#define CPLL_CPU_CLK_SHIFT 24 -+#define CPLL_CPU_CLK_MASK 0x1 -+#define CPLL_MULT_RATIO_SHIFT 16 -+#define CPLL_MULT_RATIO 0x7 -+#define CPLL_DIV_RATIO_SHIFT 10 -+#define CPLL_DIV_RATIO 0x3 -+ -+#define SYSCFG0_DRAM_TYPE_MASK 0x3 -+#define SYSCFG0_DRAM_TYPE_SHIFT 4 -+#define SYSCFG0_DRAM_TYPE_SDRAM 0 -+#define SYSCFG0_DRAM_TYPE_DDR1 1 -+#define SYSCFG0_DRAM_TYPE_DDR2 2 -+ -+#define MT7620_GPIO_MODE_I2C BIT(0) -+#define MT7620_GPIO_MODE_UART0_SHIFT 2 -+#define MT7620_GPIO_MODE_UART0_MASK 0x7 -+#define MT7620_GPIO_MODE_UART0(x) ((x) << MT7620_GPIO_MODE_UART0_SHIFT) -+#define MT7620_GPIO_MODE_UARTF 0x0 -+#define MT7620_GPIO_MODE_PCM_UARTF 0x1 -+#define MT7620_GPIO_MODE_PCM_I2S 0x2 -+#define MT7620_GPIO_MODE_I2S_UARTF 0x3 -+#define MT7620_GPIO_MODE_PCM_GPIO 0x4 -+#define MT7620_GPIO_MODE_GPIO_UARTF 0x5 -+#define MT7620_GPIO_MODE_GPIO_I2S 0x6 -+#define MT7620_GPIO_MODE_GPIO 0x7 -+#define MT7620_GPIO_MODE_UART1 BIT(5) -+#define MT7620_GPIO_MODE_MDIO BIT(8) -+#define MT7620_GPIO_MODE_RGMII1 BIT(9) -+#define MT7620_GPIO_MODE_RGMII2 BIT(10) -+#define MT7620_GPIO_MODE_SPI BIT(11) -+#define MT7620_GPIO_MODE_SPI_REF_CLK BIT(12) -+#define MT7620_GPIO_MODE_WLED BIT(13) -+#define MT7620_GPIO_MODE_JTAG BIT(15) -+#define MT7620_GPIO_MODE_EPHY BIT(15) -+#define MT7620_GPIO_MODE_WDT BIT(22) -+ -+#endif ---- a/arch/mips/ralink/Kconfig -+++ b/arch/mips/ralink/Kconfig -@@ -20,6 +20,9 @@ choice - select USB_ARCH_HAS_OHCI - select USB_ARCH_HAS_EHCI - -+ config SOC_MT7620 -+ bool "MT7620" -+ - endchoice - - choice ---- a/arch/mips/ralink/Makefile -+++ b/arch/mips/ralink/Makefile -@@ -11,6 +11,7 @@ obj-y := prom.o of.o reset.o clk.o irq.o - obj-$(CONFIG_SOC_RT288X) += rt288x.o - obj-$(CONFIG_SOC_RT305X) += rt305x.o - obj-$(CONFIG_SOC_RT3883) += rt3883.o -+obj-$(CONFIG_SOC_MT7620) += mt7620.o - - obj-$(CONFIG_EARLY_PRINTK) += early_printk.o - ---- a/arch/mips/ralink/Platform -+++ b/arch/mips/ralink/Platform -@@ -18,3 +18,8 @@ load-$(CONFIG_SOC_RT305X) += 0xffffffff8 - # Ralink RT3883 - # - load-$(CONFIG_SOC_RT3883) += 0xffffffff80000000 -+ -+# -+# Ralink MT7620 -+# -+load-$(CONFIG_SOC_MT7620) += 0xffffffff80000000 ---- /dev/null -+++ b/arch/mips/ralink/mt7620.c -@@ -0,0 +1,214 @@ -+/* -+ * 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. -+ * -+ * Parts of this file are based on Ralink's 2.6.21 BSP -+ * -+ * Copyright (C) 2008-2011 Gabor Juhos -+ * Copyright (C) 2008 Imre Kaloz -+ * Copyright (C) 2013 John Crispin -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+#include "common.h" -+ -+/* does the board have sdram or ddram */ -+static int dram_type; -+ -+/* the pll dividers */ -+static u32 mt7620_clk_divider[] = { 2, 3, 4, 8 }; -+ -+static struct ralink_pinmux_grp mode_mux[] = { -+ { -+ .name = "i2c", -+ .mask = MT7620_GPIO_MODE_I2C, -+ .gpio_first = 1, -+ .gpio_last = 2, -+ }, { -+ .name = "spi", -+ .mask = MT7620_GPIO_MODE_SPI, -+ .gpio_first = 3, -+ .gpio_last = 6, -+ }, { -+ .name = "uartlite", -+ .mask = MT7620_GPIO_MODE_UART1, -+ .gpio_first = 15, -+ .gpio_last = 16, -+ }, { -+ .name = "wdt", -+ .mask = MT7620_GPIO_MODE_WDT, -+ .gpio_first = 17, -+ .gpio_last = 17, -+ }, { -+ .name = "mdio", -+ .mask = MT7620_GPIO_MODE_MDIO, -+ .gpio_first = 22, -+ .gpio_last = 23, -+ }, { -+ .name = "rgmii1", -+ .mask = MT7620_GPIO_MODE_RGMII1, -+ .gpio_first = 24, -+ .gpio_last = 35, -+ }, { -+ .name = "spi refclk", -+ .mask = MT7620_GPIO_MODE_SPI_REF_CLK, -+ .gpio_first = 37, -+ .gpio_last = 39, -+ }, { -+ .name = "jtag", -+ .mask = MT7620_GPIO_MODE_JTAG, -+ .gpio_first = 40, -+ .gpio_last = 44, -+ }, { -+ /* shared lines with jtag */ -+ .name = "ephy", -+ .mask = MT7620_GPIO_MODE_EPHY, -+ .gpio_first = 40, -+ .gpio_last = 44, -+ }, { -+ .name = "nand", -+ .mask = MT7620_GPIO_MODE_JTAG, -+ .gpio_first = 45, -+ .gpio_last = 59, -+ }, { -+ .name = "rgmii2", -+ .mask = MT7620_GPIO_MODE_RGMII2, -+ .gpio_first = 60, -+ .gpio_last = 71, -+ }, { -+ .name = "wled", -+ .mask = MT7620_GPIO_MODE_WLED, -+ .gpio_first = 72, -+ .gpio_last = 72, -+ }, {0} -+}; -+ -+static struct ralink_pinmux_grp uart_mux[] = { -+ { -+ .name = "uartf", -+ .mask = MT7620_GPIO_MODE_UARTF, -+ .gpio_first = 7, -+ .gpio_last = 14, -+ }, { -+ .name = "pcm uartf", -+ .mask = MT7620_GPIO_MODE_PCM_UARTF, -+ .gpio_first = 7, -+ .gpio_last = 14, -+ }, { -+ .name = "pcm i2s", -+ .mask = MT7620_GPIO_MODE_PCM_I2S, -+ .gpio_first = 7, -+ .gpio_last = 14, -+ }, { -+ .name = "i2s uartf", -+ .mask = MT7620_GPIO_MODE_I2S_UARTF, -+ .gpio_first = 7, -+ .gpio_last = 14, -+ }, { -+ .name = "pcm gpio", -+ .mask = MT7620_GPIO_MODE_PCM_GPIO, -+ .gpio_first = 11, -+ .gpio_last = 14, -+ }, { -+ .name = "gpio uartf", -+ .mask = MT7620_GPIO_MODE_GPIO_UARTF, -+ .gpio_first = 7, -+ .gpio_last = 10, -+ }, { -+ .name = "gpio i2s", -+ .mask = MT7620_GPIO_MODE_GPIO_I2S, -+ .gpio_first = 7, -+ .gpio_last = 10, -+ }, { -+ .name = "gpio", -+ .mask = MT7620_GPIO_MODE_GPIO, -+ }, {0} -+}; -+ -+struct ralink_pinmux rt_gpio_pinmux = { -+ .mode = mode_mux, -+ .uart = uart_mux, -+ .uart_shift = MT7620_GPIO_MODE_UART0_SHIFT, -+ .uart_mask = MT7620_GPIO_MODE_GPIO, -+}; -+ -+void __init ralink_clk_init(void) -+{ -+ unsigned long cpu_rate, sys_rate; -+ u32 c0 = rt_sysc_r32(SYSC_REG_CPLL_CONFIG0); -+ u32 c1 = rt_sysc_r32(SYSC_REG_CPLL_CONFIG1); -+ u32 swconfig = (c0 >> CPLL_SW_CONFIG_SHIFT) & CPLL_SW_CONFIG_MASK; -+ u32 cpu_clk = (c1 >> CPLL_CPU_CLK_SHIFT) & CPLL_CPU_CLK_MASK; -+ -+ if (cpu_clk) { -+ cpu_rate = 480000000; -+ } else if (!swconfig) { -+ cpu_rate = 600000000; -+ } else { -+ u32 m = (c0 >> CPLL_MULT_RATIO_SHIFT) & CPLL_MULT_RATIO; -+ u32 d = (c0 >> CPLL_DIV_RATIO_SHIFT) & CPLL_DIV_RATIO; -+ -+ cpu_rate = ((40 * (m + 24)) / mt7620_clk_divider[d]) * 1000000; -+ } -+ -+ if (dram_type == SYSCFG0_DRAM_TYPE_SDRAM) -+ sys_rate = cpu_rate / 4; -+ else -+ sys_rate = cpu_rate / 3; -+ -+ ralink_clk_add("cpu", cpu_rate); -+ ralink_clk_add("10000100.timer", 40000000); -+ ralink_clk_add("10000500.uart", 40000000); -+ ralink_clk_add("10000c00.uartlite", 40000000); -+} -+ -+void __init ralink_of_remap(void) -+{ -+ rt_sysc_membase = plat_of_remap_node("ralink,mt7620a-sysc"); -+ rt_memc_membase = plat_of_remap_node("ralink,mt7620a-memc"); -+ -+ if (!rt_sysc_membase || !rt_memc_membase) -+ panic("Failed to remap core resources"); -+} -+ -+void prom_soc_init(struct ralink_soc_info *soc_info) -+{ -+ void __iomem *sysc = (void __iomem *) KSEG1ADDR(MT7620_SYSC_BASE); -+ unsigned char *name = NULL; -+ u32 n0; -+ u32 n1; -+ u32 rev; -+ u32 cfg0; -+ -+ n0 = __raw_readl(sysc + SYSC_REG_CHIP_NAME0); -+ n1 = __raw_readl(sysc + SYSC_REG_CHIP_NAME1); -+ -+ if (n0 == MT7620N_CHIP_NAME0 && n1 == MT7620N_CHIP_NAME1) { -+ name = "MT7620N"; -+ soc_info->compatible = "ralink,mt7620n-soc"; -+ } else if (n0 == MT7620A_CHIP_NAME0 && n1 == MT7620A_CHIP_NAME1) { -+ name = "MT7620A"; -+ soc_info->compatible = "ralink,mt7620a-soc"; -+ } else { -+ panic("mt7620: unknown SoC, n0:%08x n1:%08x\n", n0, n1); -+ } -+ -+ rev = __raw_readl(sysc + SYSC_REG_CHIP_REV); -+ -+ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN, -+ "Ralink %s ver:%u eco:%u", -+ name, -+ (rev >> CHIP_REV_VER_SHIFT) & CHIP_REV_VER_MASK, -+ (rev & CHIP_REV_ECO_MASK)); -+ -+ cfg0 = __raw_readl(sysc + SYSC_REG_SYSTEM_CONFIG0); -+ dram_type = (cfg0 >> SYSCFG0_DRAM_TYPE_SHIFT) & SYSCFG0_DRAM_TYPE_MASK; -+} diff --git a/target/linux/ramips/patches-3.8/0114-MIPS-ralink-add-cpu-feature-overrides.h.patch b/target/linux/ramips/patches-3.8/0114-MIPS-ralink-add-cpu-feature-overrides.h.patch deleted file mode 100644 index dc5dab98a1..0000000000 --- a/target/linux/ramips/patches-3.8/0114-MIPS-ralink-add-cpu-feature-overrides.h.patch +++ /dev/null @@ -1,218 +0,0 @@ -From 33c525913af22d1b799a7218ee48579c22a50cf8 Mon Sep 17 00:00:00 2001 -From: Gabor Juhos -Date: Wed, 10 Apr 2013 09:19:07 +0200 -Subject: [PATCH 114/137] MIPS: ralink: add cpu-feature-overrides.h - -Add cpu-feature-overrides.h for RT288x, RT305x and RT3883. - -Signed-off-by: Gabor Juhos -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5175/ ---- - .../asm/mach-ralink/rt288x/cpu-feature-overrides.h | 56 ++++++++++++++++++++ - .../asm/mach-ralink/rt305x/cpu-feature-overrides.h | 56 ++++++++++++++++++++ - .../asm/mach-ralink/rt3883/cpu-feature-overrides.h | 55 +++++++++++++++++++ - arch/mips/ralink/Platform | 3 ++ - 4 files changed, 170 insertions(+) - create mode 100644 arch/mips/include/asm/mach-ralink/rt288x/cpu-feature-overrides.h - create mode 100644 arch/mips/include/asm/mach-ralink/rt305x/cpu-feature-overrides.h - create mode 100644 arch/mips/include/asm/mach-ralink/rt3883/cpu-feature-overrides.h - ---- /dev/null -+++ b/arch/mips/include/asm/mach-ralink/rt288x/cpu-feature-overrides.h -@@ -0,0 +1,56 @@ -+/* -+ * Ralink RT288x specific CPU feature overrides -+ * -+ * Copyright (C) 2008-2009 Gabor Juhos -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * This file was derived from: include/asm-mips/cpu-features.h -+ * Copyright (C) 2003, 2004 Ralf Baechle -+ * Copyright (C) 2004 Maciej W. Rozycki -+ * -+ * 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 _RT288X_CPU_FEATURE_OVERRIDES_H -+#define _RT288X_CPU_FEATURE_OVERRIDES_H -+ -+#define cpu_has_tlb 1 -+#define cpu_has_4kex 1 -+#define cpu_has_3k_cache 0 -+#define cpu_has_4k_cache 1 -+#define cpu_has_tx39_cache 0 -+#define cpu_has_sb1_cache 0 -+#define cpu_has_fpu 0 -+#define cpu_has_32fpr 0 -+#define cpu_has_counter 1 -+#define cpu_has_watch 1 -+#define cpu_has_divec 1 -+ -+#define cpu_has_prefetch 1 -+#define cpu_has_ejtag 1 -+#define cpu_has_llsc 1 -+ -+#define cpu_has_mips16 1 -+#define cpu_has_mdmx 0 -+#define cpu_has_mips3d 0 -+#define cpu_has_smartmips 0 -+ -+#define cpu_has_mips32r1 1 -+#define cpu_has_mips32r2 1 -+#define cpu_has_mips64r1 0 -+#define cpu_has_mips64r2 0 -+ -+#define cpu_has_dsp 0 -+#define cpu_has_mipsmt 0 -+ -+#define cpu_has_64bits 0 -+#define cpu_has_64bit_zero_reg 0 -+#define cpu_has_64bit_gp_regs 0 -+#define cpu_has_64bit_addresses 0 -+ -+#define cpu_dcache_line_size() 16 -+#define cpu_icache_line_size() 16 -+ -+#endif /* _RT288X_CPU_FEATURE_OVERRIDES_H */ ---- /dev/null -+++ b/arch/mips/include/asm/mach-ralink/rt305x/cpu-feature-overrides.h -@@ -0,0 +1,56 @@ -+/* -+ * Ralink RT305x specific CPU feature overrides -+ * -+ * Copyright (C) 2008-2009 Gabor Juhos -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * This file was derived from: include/asm-mips/cpu-features.h -+ * Copyright (C) 2003, 2004 Ralf Baechle -+ * Copyright (C) 2004 Maciej W. Rozycki -+ * -+ * 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 _RT305X_CPU_FEATURE_OVERRIDES_H -+#define _RT305X_CPU_FEATURE_OVERRIDES_H -+ -+#define cpu_has_tlb 1 -+#define cpu_has_4kex 1 -+#define cpu_has_3k_cache 0 -+#define cpu_has_4k_cache 1 -+#define cpu_has_tx39_cache 0 -+#define cpu_has_sb1_cache 0 -+#define cpu_has_fpu 0 -+#define cpu_has_32fpr 0 -+#define cpu_has_counter 1 -+#define cpu_has_watch 1 -+#define cpu_has_divec 1 -+ -+#define cpu_has_prefetch 1 -+#define cpu_has_ejtag 1 -+#define cpu_has_llsc 1 -+ -+#define cpu_has_mips16 1 -+#define cpu_has_mdmx 0 -+#define cpu_has_mips3d 0 -+#define cpu_has_smartmips 0 -+ -+#define cpu_has_mips32r1 1 -+#define cpu_has_mips32r2 1 -+#define cpu_has_mips64r1 0 -+#define cpu_has_mips64r2 0 -+ -+#define cpu_has_dsp 1 -+#define cpu_has_mipsmt 0 -+ -+#define cpu_has_64bits 0 -+#define cpu_has_64bit_zero_reg 0 -+#define cpu_has_64bit_gp_regs 0 -+#define cpu_has_64bit_addresses 0 -+ -+#define cpu_dcache_line_size() 32 -+#define cpu_icache_line_size() 32 -+ -+#endif /* _RT305X_CPU_FEATURE_OVERRIDES_H */ ---- /dev/null -+++ b/arch/mips/include/asm/mach-ralink/rt3883/cpu-feature-overrides.h -@@ -0,0 +1,55 @@ -+/* -+ * Ralink RT3662/RT3883 specific CPU feature overrides -+ * -+ * Copyright (C) 2011-2013 Gabor Juhos -+ * -+ * This file was derived from: include/asm-mips/cpu-features.h -+ * Copyright (C) 2003, 2004 Ralf Baechle -+ * Copyright (C) 2004 Maciej W. Rozycki -+ * -+ * 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 _RT3883_CPU_FEATURE_OVERRIDES_H -+#define _RT3883_CPU_FEATURE_OVERRIDES_H -+ -+#define cpu_has_tlb 1 -+#define cpu_has_4kex 1 -+#define cpu_has_3k_cache 0 -+#define cpu_has_4k_cache 1 -+#define cpu_has_tx39_cache 0 -+#define cpu_has_sb1_cache 0 -+#define cpu_has_fpu 0 -+#define cpu_has_32fpr 0 -+#define cpu_has_counter 1 -+#define cpu_has_watch 1 -+#define cpu_has_divec 1 -+ -+#define cpu_has_prefetch 1 -+#define cpu_has_ejtag 1 -+#define cpu_has_llsc 1 -+ -+#define cpu_has_mips16 1 -+#define cpu_has_mdmx 0 -+#define cpu_has_mips3d 0 -+#define cpu_has_smartmips 0 -+ -+#define cpu_has_mips32r1 1 -+#define cpu_has_mips32r2 1 -+#define cpu_has_mips64r1 0 -+#define cpu_has_mips64r2 0 -+ -+#define cpu_has_dsp 1 -+#define cpu_has_mipsmt 0 -+ -+#define cpu_has_64bits 0 -+#define cpu_has_64bit_zero_reg 0 -+#define cpu_has_64bit_gp_regs 0 -+#define cpu_has_64bit_addresses 0 -+ -+#define cpu_dcache_line_size() 32 -+#define cpu_icache_line_size() 32 -+ -+#endif /* _RT3883_CPU_FEATURE_OVERRIDES_H */ ---- a/arch/mips/ralink/Platform -+++ b/arch/mips/ralink/Platform -@@ -8,16 +8,19 @@ cflags-$(CONFIG_RALINK) += -I$(srctree) - # Ralink RT288x - # - load-$(CONFIG_SOC_RT288X) += 0xffffffff88000000 -+cflags-$(CONFIG_SOC_RT288X) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt288x - - # - # Ralink RT305x - # - load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 -+cflags-$(CONFIG_SOC_RT305X) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt305x - - # - # Ralink RT3883 - # - load-$(CONFIG_SOC_RT3883) += 0xffffffff80000000 -+cflags-$(CONFIG_SOC_RT3883) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt3883 - - # - # Ralink MT7620 diff --git a/target/linux/ramips/patches-3.8/0115-DT-add-vendor-prefixes-for-Ralink.patch b/target/linux/ramips/patches-3.8/0115-DT-add-vendor-prefixes-for-Ralink.patch deleted file mode 100644 index a9a99b4cae..0000000000 --- a/target/linux/ramips/patches-3.8/0115-DT-add-vendor-prefixes-for-Ralink.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 9377ecb9f1fb5da25a0fbd324e7add7644b1d43d Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sat, 13 Apr 2013 10:11:51 +0200 -Subject: [PATCH 115/137] DT: add vendor prefixes for Ralink - -Signed-off-by: John Crispin -Acked-by: Grant Likely ---- - Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + - 1 file changed, 1 insertion(+) - ---- a/Documentation/devicetree/bindings/vendor-prefixes.txt -+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt -@@ -40,6 +40,7 @@ onnn ON Semiconductor Corp. - picochip Picochip Ltd - powervr PowerVR (deprecated, use img) - qcom Qualcomm, Inc. -+ralink Mediatek/Ralink Technology Corp. - ramtron Ramtron International - realtek Realtek Semiconductor Corp. - samsung Samsung Semiconductor diff --git a/target/linux/ramips/patches-3.8/0116-DT-add-documentation-for-the-Ralink-MIPS-SoCs.patch b/target/linux/ramips/patches-3.8/0116-DT-add-documentation-for-the-Ralink-MIPS-SoCs.patch deleted file mode 100644 index f9463c4cb0..0000000000 --- a/target/linux/ramips/patches-3.8/0116-DT-add-documentation-for-the-Ralink-MIPS-SoCs.patch +++ /dev/null @@ -1,38 +0,0 @@ -From fed1ff0d85b481bb3dbebf31e0720d65ce4170c9 Mon Sep 17 00:00:00 2001 -From: Gabor Juhos -Date: Sat, 13 Apr 2013 09:02:40 +0200 -Subject: [PATCH 116/137] DT: add documentation for the Ralink MIPS SoCs - -This patch adds binding documentation for the -compatible values of the Ralink MIPS SoCs. - -Signed-off-by: Gabor Juhos -Signed-off-by: John Crispin -Acked-by: Grant Likely -Patchwork: http://patchwork.linux-mips.org/patch/5187/ ---- - Documentation/devicetree/bindings/mips/ralink.txt | 18 ++++++++++++++++++ - 1 file changed, 18 insertions(+) - create mode 100644 Documentation/devicetree/bindings/mips/ralink.txt - ---- /dev/null -+++ b/Documentation/devicetree/bindings/mips/ralink.txt -@@ -0,0 +1,18 @@ -+Ralink MIPS SoC device tree bindings -+ -+1. SoCs -+ -+Each device tree must specify a compatible value for the Ralink SoC -+it uses in the compatible property of the root node. The compatible -+value must be one of the following values: -+ -+ ralink,rt2880-soc -+ ralink,rt3050-soc -+ ralink,rt3052-soc -+ ralink,rt3350-soc -+ ralink,rt3352-soc -+ ralink,rt3883-soc -+ ralink,rt5350-soc -+ ralink,mt7620a-soc -+ ralink,mt7620n-soc -+ diff --git a/target/linux/ramips/patches-3.8/0117-DT-MIPS-ralink-clean-up-RT3050-dtsi-and-dts-file.patch b/target/linux/ramips/patches-3.8/0117-DT-MIPS-ralink-clean-up-RT3050-dtsi-and-dts-file.patch deleted file mode 100644 index 1328b432a8..0000000000 --- a/target/linux/ramips/patches-3.8/0117-DT-MIPS-ralink-clean-up-RT3050-dtsi-and-dts-file.patch +++ /dev/null @@ -1,117 +0,0 @@ -From 7a30e00a278fe94ac8e42d0967ffde99d1ab74ee Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 21 Mar 2013 17:47:07 +0100 -Subject: [PATCH 117/137] DT: MIPS: ralink: clean up RT3050 dtsi and dts file - -* remove nodes for cores whose drivers are not upstream yet -* add compat string for an additional soc -* fix a whitespace error - -Signed-off-by: John Crispin -Acked-by: Grant Likely -Patchwork: http://patchwork.linux-mips.org/patch/5186/ ---- - arch/mips/ralink/dts/rt3050.dtsi | 52 ++-------------------------------- - arch/mips/ralink/dts/rt3052_eval.dts | 10 ++----- - 2 files changed, 4 insertions(+), 58 deletions(-) - ---- a/arch/mips/ralink/dts/rt3050.dtsi -+++ b/arch/mips/ralink/dts/rt3050.dtsi -@@ -1,7 +1,7 @@ - / { - #address-cells = <1>; - #size-cells = <1>; -- compatible = "ralink,rt3050-soc", "ralink,rt3052-soc"; -+ compatible = "ralink,rt3050-soc", "ralink,rt3052-soc", "ralink,rt3350-soc"; - - cpus { - cpu@0 { -@@ -9,10 +9,6 @@ - }; - }; - -- chosen { -- bootargs = "console=ttyS0,57600 init=/init"; -- }; -- - cpuintc: cpuintc@0 { - #address-cells = <0>; - #interrupt-cells = <1>; -@@ -23,7 +19,7 @@ - palmbus@10000000 { - compatible = "palmbus"; - reg = <0x10000000 0x200000>; -- ranges = <0x0 0x10000000 0x1FFFFF>; -+ ranges = <0x0 0x10000000 0x1FFFFF>; - - #address-cells = <1>; - #size-cells = <1>; -@@ -33,11 +29,6 @@ - reg = <0x0 0x100>; - }; - -- timer@100 { -- compatible = "ralink,rt3052-wdt", "ralink,rt2880-wdt"; -- reg = <0x100 0x100>; -- }; -- - intc: intc@200 { - compatible = "ralink,rt3052-intc", "ralink,rt2880-intc"; - reg = <0x200 0x100>; -@@ -54,45 +45,6 @@ - reg = <0x300 0x100>; - }; - -- gpio0: gpio@600 { -- compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; -- reg = <0x600 0x34>; -- -- gpio-controller; -- #gpio-cells = <2>; -- -- ralink,ngpio = <24>; -- ralink,regs = [ 00 04 08 0c -- 20 24 28 2c -- 30 34 ]; -- }; -- -- gpio1: gpio@638 { -- compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; -- reg = <0x638 0x24>; -- -- gpio-controller; -- #gpio-cells = <2>; -- -- ralink,ngpio = <16>; -- ralink,regs = [ 00 04 08 0c -- 10 14 18 1c -- 20 24 ]; -- }; -- -- gpio2: gpio@660 { -- compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; -- reg = <0x660 0x24>; -- -- gpio-controller; -- #gpio-cells = <2>; -- -- ralink,ngpio = <12>; -- ralink,regs = [ 00 04 08 0c -- 10 14 18 1c -- 20 24 ]; -- }; -- - uartlite@c00 { - compatible = "ralink,rt3052-uart", "ralink,rt2880-uart", "ns16550a"; - reg = <0xc00 0x100>; ---- a/arch/mips/ralink/dts/rt3052_eval.dts -+++ b/arch/mips/ralink/dts/rt3052_eval.dts -@@ -3,8 +3,6 @@ - /include/ "rt3050.dtsi" - - / { -- #address-cells = <1>; -- #size-cells = <1>; - compatible = "ralink,rt3052-eval-board", "ralink,rt3052-soc"; - model = "Ralink RT3052 evaluation board"; - diff --git a/target/linux/ramips/patches-3.8/0118-DT-MIPS-ralink-add-RT2880-dts-files.patch b/target/linux/ramips/patches-3.8/0118-DT-MIPS-ralink-add-RT2880-dts-files.patch deleted file mode 100644 index 8ad9b18eb8..0000000000 --- a/target/linux/ramips/patches-3.8/0118-DT-MIPS-ralink-add-RT2880-dts-files.patch +++ /dev/null @@ -1,147 +0,0 @@ -From b39e659770cb71939765de8c9e73c0a0cfa832db Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Fri, 12 Apr 2013 06:27:37 +0000 -Subject: [PATCH 118/137] DT: MIPS: ralink: add RT2880 dts files - -Add a dtsi file for RT2880 SoC and a sample dts file. - -Signed-off-by: John Crispin -Acked-by: Grant Likely -Patchwork: http://patchwork.linux-mips.org/patch/5188/ ---- - arch/mips/ralink/Kconfig | 4 +++ - arch/mips/ralink/dts/Makefile | 1 + - arch/mips/ralink/dts/rt2880.dtsi | 58 ++++++++++++++++++++++++++++++++++ - arch/mips/ralink/dts/rt2880_eval.dts | 46 +++++++++++++++++++++++++++ - 4 files changed, 109 insertions(+) - create mode 100644 arch/mips/ralink/dts/rt2880.dtsi - create mode 100644 arch/mips/ralink/dts/rt2880_eval.dts - ---- a/arch/mips/ralink/Kconfig -+++ b/arch/mips/ralink/Kconfig -@@ -34,6 +34,10 @@ choice - config DTB_RT_NONE - bool "None" - -+ config DTB_RT2880_EVAL -+ bool "RT2880 eval kit" -+ depends on SOC_RT288X -+ - config DTB_RT305X_EVAL - bool "RT305x eval kit" - depends on SOC_RT305X ---- a/arch/mips/ralink/dts/Makefile -+++ b/arch/mips/ralink/dts/Makefile -@@ -1 +1,2 @@ -+obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o - obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o ---- /dev/null -+++ b/arch/mips/ralink/dts/rt2880.dtsi -@@ -0,0 +1,58 @@ -+/ { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "ralink,rt2880-soc"; -+ -+ cpus { -+ cpu@0 { -+ compatible = "mips,mips4KEc"; -+ }; -+ }; -+ -+ cpuintc: cpuintc@0 { -+ #address-cells = <0>; -+ #interrupt-cells = <1>; -+ interrupt-controller; -+ compatible = "mti,cpu-interrupt-controller"; -+ }; -+ -+ palmbus@300000 { -+ compatible = "palmbus"; -+ reg = <0x300000 0x200000>; -+ ranges = <0x0 0x300000 0x1FFFFF>; -+ -+ #address-cells = <1>; -+ #size-cells = <1>; -+ -+ sysc@0 { -+ compatible = "ralink,rt2880-sysc"; -+ reg = <0x0 0x100>; -+ }; -+ -+ intc: intc@200 { -+ compatible = "ralink,rt2880-intc"; -+ reg = <0x200 0x100>; -+ -+ interrupt-controller; -+ #interrupt-cells = <1>; -+ -+ interrupt-parent = <&cpuintc>; -+ interrupts = <2>; -+ }; -+ -+ memc@300 { -+ compatible = "ralink,rt2880-memc"; -+ reg = <0x300 0x100>; -+ }; -+ -+ uartlite@c00 { -+ compatible = "ralink,rt2880-uart", "ns16550a"; -+ reg = <0xc00 0x100>; -+ -+ interrupt-parent = <&intc>; -+ interrupts = <8>; -+ -+ reg-shift = <2>; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/mips/ralink/dts/rt2880_eval.dts -@@ -0,0 +1,46 @@ -+/dts-v1/; -+ -+/include/ "rt2880.dtsi" -+ -+/ { -+ compatible = "ralink,rt2880-eval-board", "ralink,rt2880-soc"; -+ model = "Ralink RT2880 evaluation board"; -+ -+ memory@0 { -+ reg = <0x8000000 0x2000000>; -+ }; -+ -+ chosen { -+ bootargs = "console=ttyS0,57600"; -+ }; -+ -+ cfi@1f000000 { -+ compatible = "cfi-flash"; -+ reg = <0x1f000000 0x400000>; -+ -+ bank-width = <2>; -+ device-width = <2>; -+ #address-cells = <1>; -+ #size-cells = <1>; -+ -+ partition@0 { -+ label = "uboot"; -+ reg = <0x0 0x30000>; -+ read-only; -+ }; -+ partition@30000 { -+ label = "uboot-env"; -+ reg = <0x30000 0x10000>; -+ read-only; -+ }; -+ partition@40000 { -+ label = "calibration"; -+ reg = <0x40000 0x10000>; -+ read-only; -+ }; -+ partition@50000 { -+ label = "linux"; -+ reg = <0x50000 0x3b0000>; -+ }; -+ }; -+}; diff --git a/target/linux/ramips/patches-3.8/0119-DT-MIPS-ralink-add-RT3883-dts-files.patch b/target/linux/ramips/patches-3.8/0119-DT-MIPS-ralink-add-RT3883-dts-files.patch deleted file mode 100644 index 90b50eda72..0000000000 --- a/target/linux/ramips/patches-3.8/0119-DT-MIPS-ralink-add-RT3883-dts-files.patch +++ /dev/null @@ -1,118 +0,0 @@ -From 8b02459b5aa171dc8726698c4b19341a4e441bb8 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Fri, 12 Apr 2013 06:27:39 +0000 -Subject: [PATCH 119/137] DT: MIPS: ralink: add RT3883 dts files - -Add a dtsi file for RT3883 SoC and a sample dts file. - -Signed-off-by: John Crispin -Acked-by: Grant Likely -Patchwork: http://patchwork.linux-mips.org/patch/5189/ ---- - arch/mips/ralink/Kconfig | 4 +++ - arch/mips/ralink/dts/Makefile | 1 + - arch/mips/ralink/dts/rt3883.dtsi | 58 ++++++++++++++++++++++++++++++++++ - arch/mips/ralink/dts/rt3883_eval.dts | 16 ++++++++++ - 4 files changed, 79 insertions(+) - create mode 100644 arch/mips/ralink/dts/rt3883.dtsi - create mode 100644 arch/mips/ralink/dts/rt3883_eval.dts - ---- a/arch/mips/ralink/Kconfig -+++ b/arch/mips/ralink/Kconfig -@@ -42,6 +42,10 @@ choice - bool "RT305x eval kit" - depends on SOC_RT305X - -+ config DTB_RT3883_EVAL -+ bool "RT3883 eval kit" -+ depends on SOC_RT3883 -+ - endchoice - - endif ---- a/arch/mips/ralink/dts/Makefile -+++ b/arch/mips/ralink/dts/Makefile -@@ -1,2 +1,3 @@ - obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o - obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o -+obj-$(CONFIG_DTB_RT3883_EVAL) := rt3883_eval.dtb.o ---- /dev/null -+++ b/arch/mips/ralink/dts/rt3883.dtsi -@@ -0,0 +1,58 @@ -+/ { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "ralink,rt3883-soc"; -+ -+ cpus { -+ cpu@0 { -+ compatible = "mips,mips74Kc"; -+ }; -+ }; -+ -+ cpuintc: cpuintc@0 { -+ #address-cells = <0>; -+ #interrupt-cells = <1>; -+ interrupt-controller; -+ compatible = "mti,cpu-interrupt-controller"; -+ }; -+ -+ palmbus@10000000 { -+ compatible = "palmbus"; -+ reg = <0x10000000 0x200000>; -+ ranges = <0x0 0x10000000 0x1FFFFF>; -+ -+ #address-cells = <1>; -+ #size-cells = <1>; -+ -+ sysc@0 { -+ compatible = "ralink,rt3883-sysc", "ralink,rt3050-sysc"; -+ reg = <0x0 0x100>; -+ }; -+ -+ intc: intc@200 { -+ compatible = "ralink,rt3883-intc", "ralink,rt2880-intc"; -+ reg = <0x200 0x100>; -+ -+ interrupt-controller; -+ #interrupt-cells = <1>; -+ -+ interrupt-parent = <&cpuintc>; -+ interrupts = <2>; -+ }; -+ -+ memc@300 { -+ compatible = "ralink,rt3883-memc", "ralink,rt3050-memc"; -+ reg = <0x300 0x100>; -+ }; -+ -+ uartlite@c00 { -+ compatible = "ralink,rt3883-uart", "ralink,rt2880-uart", "ns16550a"; -+ reg = <0xc00 0x100>; -+ -+ interrupt-parent = <&intc>; -+ interrupts = <12>; -+ -+ reg-shift = <2>; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/mips/ralink/dts/rt3883_eval.dts -@@ -0,0 +1,16 @@ -+/dts-v1/; -+ -+/include/ "rt3883.dtsi" -+ -+/ { -+ compatible = "ralink,rt3883-eval-board", "ralink,rt3883-soc"; -+ model = "Ralink RT3883 evaluation board"; -+ -+ memory@0 { -+ reg = <0x0 0x2000000>; -+ }; -+ -+ chosen { -+ bootargs = "console=ttyS0,57600"; -+ }; -+}; diff --git a/target/linux/ramips/patches-3.8/0120-DT-MIPS-ralink-add-MT7620A-dts-files.patch b/target/linux/ramips/patches-3.8/0120-DT-MIPS-ralink-add-MT7620A-dts-files.patch deleted file mode 100644 index 82a3086c17..0000000000 --- a/target/linux/ramips/patches-3.8/0120-DT-MIPS-ralink-add-MT7620A-dts-files.patch +++ /dev/null @@ -1,119 +0,0 @@ -From 07741f61fc94fad3c3d21fa1a2ad6f01455cc1dd Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Fri, 12 Apr 2013 06:27:41 +0000 -Subject: [PATCH 120/137] DT: MIPS: ralink: add MT7620A dts files - -Add a dtsi file for MT7620A SoC and a sample dts file. - -Signed-off-by: John Crispin -Acked-by: Grant Likely -Patchwork: http://patchwork.linux-mips.org/patch/5190/ ---- - arch/mips/ralink/Kconfig | 4 +++ - arch/mips/ralink/dts/Makefile | 1 + - arch/mips/ralink/dts/mt7620a.dtsi | 58 +++++++++++++++++++++++++++++++++ - arch/mips/ralink/dts/mt7620a_eval.dts | 16 +++++++++ - 4 files changed, 79 insertions(+) - create mode 100644 arch/mips/ralink/dts/mt7620a.dtsi - create mode 100644 arch/mips/ralink/dts/mt7620a_eval.dts - ---- a/arch/mips/ralink/Kconfig -+++ b/arch/mips/ralink/Kconfig -@@ -46,6 +46,10 @@ choice - bool "RT3883 eval kit" - depends on SOC_RT3883 - -+ config DTB_MT7620A_EVAL -+ bool "MT7620A eval kit" -+ depends on SOC_MT7620 -+ - endchoice - - endif ---- a/arch/mips/ralink/dts/Makefile -+++ b/arch/mips/ralink/dts/Makefile -@@ -1,3 +1,4 @@ - obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o - obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o - obj-$(CONFIG_DTB_RT3883_EVAL) := rt3883_eval.dtb.o -+obj-$(CONFIG_DTB_MT7620A_EVAL) := mt7620a_eval.dtb.o ---- /dev/null -+++ b/arch/mips/ralink/dts/mt7620a.dtsi -@@ -0,0 +1,58 @@ -+/ { -+ #address-cells = <1>; -+ #size-cells = <1>; -+ compatible = "ralink,mtk7620a-soc"; -+ -+ cpus { -+ cpu@0 { -+ compatible = "mips,mips24KEc"; -+ }; -+ }; -+ -+ cpuintc: cpuintc@0 { -+ #address-cells = <0>; -+ #interrupt-cells = <1>; -+ interrupt-controller; -+ compatible = "mti,cpu-interrupt-controller"; -+ }; -+ -+ palmbus@10000000 { -+ compatible = "palmbus"; -+ reg = <0x10000000 0x200000>; -+ ranges = <0x0 0x10000000 0x1FFFFF>; -+ -+ #address-cells = <1>; -+ #size-cells = <1>; -+ -+ sysc@0 { -+ compatible = "ralink,mt7620a-sysc"; -+ reg = <0x0 0x100>; -+ }; -+ -+ intc: intc@200 { -+ compatible = "ralink,mt7620a-intc", "ralink,rt2880-intc"; -+ reg = <0x200 0x100>; -+ -+ interrupt-controller; -+ #interrupt-cells = <1>; -+ -+ interrupt-parent = <&cpuintc>; -+ interrupts = <2>; -+ }; -+ -+ memc@300 { -+ compatible = "ralink,mt7620a-memc", "ralink,rt3050-memc"; -+ reg = <0x300 0x100>; -+ }; -+ -+ uartlite@c00 { -+ compatible = "ralink,mt7620a-uart", "ralink,rt2880-uart", "ns16550a"; -+ reg = <0xc00 0x100>; -+ -+ interrupt-parent = <&intc>; -+ interrupts = <12>; -+ -+ reg-shift = <2>; -+ }; -+ }; -+}; ---- /dev/null -+++ b/arch/mips/ralink/dts/mt7620a_eval.dts -@@ -0,0 +1,16 @@ -+/dts-v1/; -+ -+/include/ "mt7620a.dtsi" -+ -+/ { -+ compatible = "ralink,mt7620a-eval-board", "ralink,mt7620a-soc"; -+ model = "Ralink MT7620A evaluation board"; -+ -+ memory@0 { -+ reg = <0x0 0x2000000>; -+ }; -+ -+ chosen { -+ bootargs = "console=ttyS0,57600"; -+ }; -+}; diff --git a/target/linux/ramips/patches-3.8/0121-MIPS-add-detect_memory_region.patch b/target/linux/ramips/patches-3.8/0121-MIPS-add-detect_memory_region.patch deleted file mode 100644 index 8736e246b6..0000000000 --- a/target/linux/ramips/patches-3.8/0121-MIPS-add-detect_memory_region.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 9041c96ab5bd29d85ca95cffa44c755f68ae6bb1 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sat, 13 Apr 2013 13:15:47 +0200 -Subject: [PATCH 121/137] MIPS: add detect_memory_region() - -Add a generic way of detecting the available RAM. This function is based on the -implementation already used by ath79. - -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5178/ ---- - arch/mips/include/asm/bootinfo.h | 1 + - arch/mips/kernel/setup.c | 20 ++++++++++++++++++++ - 2 files changed, 21 insertions(+) - ---- a/arch/mips/include/asm/bootinfo.h -+++ b/arch/mips/include/asm/bootinfo.h -@@ -104,6 +104,7 @@ struct boot_mem_map { - extern struct boot_mem_map boot_mem_map; - - extern void add_memory_region(phys_t start, phys_t size, long type); -+extern void detect_memory_region(phys_t start, phys_t sz_min, phys_t sz_max); - - extern void prom_init(void); - extern void prom_free_prom_memory(void); ---- a/arch/mips/kernel/setup.c -+++ b/arch/mips/kernel/setup.c -@@ -23,6 +23,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -122,6 +123,25 @@ void __init add_memory_region(phys_t sta - boot_mem_map.nr_map++; - } - -+void __init detect_memory_region(phys_t start, phys_t sz_min, phys_t sz_max) -+{ -+ phys_t size; -+ -+ for (size = sz_min; size < sz_max; size <<= 1) { -+ if (!memcmp(detect_memory_region, -+ detect_memory_region + size, 1024)) -+ break; -+ } -+ -+ pr_debug("Memory: %lluMB of RAM detected at 0x%llx (min: %lluMB, max: %lluMB)\n", -+ ((unsigned long long) size) / SZ_1M, -+ (unsigned long long) start, -+ ((unsigned long long) sz_min) / SZ_1M, -+ ((unsigned long long) sz_max) / SZ_1M); -+ -+ add_memory_region(start, size, BOOT_MEM_RAM); -+} -+ - static void __init print_memory_map(void) - { - int i; diff --git a/target/linux/ramips/patches-3.8/0122-MIPS-ralink-add-memory-definition-to-struct-ralink_s.patch b/target/linux/ramips/patches-3.8/0122-MIPS-ralink-add-memory-definition-to-struct-ralink_s.patch deleted file mode 100644 index 3454a5ba56..0000000000 --- a/target/linux/ramips/patches-3.8/0122-MIPS-ralink-add-memory-definition-to-struct-ralink_s.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 5155790ed1f270379ea98325f01e1c72a36a37d0 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sat, 13 Apr 2013 15:10:14 +0200 -Subject: [PATCH 122/137] MIPS: ralink: add memory definition to struct - ralink_soc_info - -Depending on the actual SoC we have a different base address as well as minimum -and maximum size for RAM. Add these fields to the per SoC structure. - -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5179/ ---- - arch/mips/ralink/common.h | 5 +++++ - 1 file changed, 5 insertions(+) - ---- a/arch/mips/ralink/common.h -+++ b/arch/mips/ralink/common.h -@@ -33,6 +33,11 @@ extern struct ralink_pinmux rt_gpio_pinm - struct ralink_soc_info { - unsigned char sys_type[RAMIPS_SYS_TYPE_LEN]; - unsigned char *compatible; -+ -+ unsigned long mem_base; -+ unsigned long mem_size; -+ unsigned long mem_size_min; -+ unsigned long mem_size_max; - }; - extern struct ralink_soc_info soc_info; - diff --git a/target/linux/ramips/patches-3.8/0123-MIPS-ralink-add-memory-definition-for-RT305x.patch b/target/linux/ramips/patches-3.8/0123-MIPS-ralink-add-memory-definition-for-RT305x.patch deleted file mode 100644 index bf6d7f388d..0000000000 --- a/target/linux/ramips/patches-3.8/0123-MIPS-ralink-add-memory-definition-for-RT305x.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 016f1f659cf70cc78e72e12a2130d8f3e1a6e0d3 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sat, 13 Apr 2013 15:13:40 +0200 -Subject: [PATCH 123/137] MIPS: ralink: add memory definition for RT305x - -Populate struct soc_info with the data that describes our RAM window. - -As memory detection fails on RT5350 we read the amount of available memory -from the system controller. - -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5180/ ---- - arch/mips/include/asm/mach-ralink/rt305x.h | 6 ++++ - arch/mips/ralink/rt305x.c | 45 ++++++++++++++++++++++++++++ - 2 files changed, 51 insertions(+) - ---- a/arch/mips/include/asm/mach-ralink/rt305x.h -+++ b/arch/mips/include/asm/mach-ralink/rt305x.h -@@ -157,4 +157,10 @@ static inline int soc_is_rt5350(void) - #define RT3352_RSTCTRL_UDEV BIT(25) - #define RT3352_SYSCFG1_USB0_HOST_MODE BIT(10) - -+#define RT305X_SDRAM_BASE 0x00000000 -+#define RT305X_MEM_SIZE_MIN 2 -+#define RT305X_MEM_SIZE_MAX 64 -+#define RT3352_MEM_SIZE_MIN 2 -+#define RT3352_MEM_SIZE_MAX 256 -+ - #endif ---- a/arch/mips/ralink/rt305x.c -+++ b/arch/mips/ralink/rt305x.c -@@ -122,6 +122,40 @@ struct ralink_pinmux rt_gpio_pinmux = { - .wdt_reset = rt305x_wdt_reset, - }; - -+static unsigned long rt5350_get_mem_size(void) -+{ -+ void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT305X_SYSC_BASE); -+ unsigned long ret; -+ u32 t; -+ -+ t = __raw_readl(sysc + SYSC_REG_SYSTEM_CONFIG); -+ t = (t >> RT5350_SYSCFG0_DRAM_SIZE_SHIFT) & -+ RT5350_SYSCFG0_DRAM_SIZE_MASK; -+ -+ switch (t) { -+ case RT5350_SYSCFG0_DRAM_SIZE_2M: -+ ret = 2; -+ break; -+ case RT5350_SYSCFG0_DRAM_SIZE_8M: -+ ret = 8; -+ break; -+ case RT5350_SYSCFG0_DRAM_SIZE_16M: -+ ret = 16; -+ break; -+ case RT5350_SYSCFG0_DRAM_SIZE_32M: -+ ret = 32; -+ break; -+ case RT5350_SYSCFG0_DRAM_SIZE_64M: -+ ret = 64; -+ break; -+ default: -+ panic("rt5350: invalid DRAM size: %u", t); -+ break; -+ } -+ -+ return ret * 1024 * 1024; -+} -+ - void __init ralink_clk_init(void) - { - unsigned long cpu_rate, sys_rate, wdt_rate, uart_rate; -@@ -252,4 +286,15 @@ void prom_soc_init(struct ralink_soc_inf - name, - (id >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK, - (id & CHIP_ID_REV_MASK)); -+ -+ soc_info->mem_base = RT305X_SDRAM_BASE; -+ if (soc_is_rt5350()) { -+ soc_info->mem_size = rt5350_get_mem_size(); -+ } else if (soc_is_rt305x() || soc_is_rt3350()) { -+ soc_info->mem_size_min = RT305X_MEM_SIZE_MIN; -+ soc_info->mem_size_max = RT305X_MEM_SIZE_MAX; -+ } else if (soc_is_rt3352()) { -+ soc_info->mem_size_min = RT3352_MEM_SIZE_MIN; -+ soc_info->mem_size_max = RT3352_MEM_SIZE_MAX; -+ } - } diff --git a/target/linux/ramips/patches-3.8/0124-MIPS-ralink-add-memory-definition-for-RT2880.patch b/target/linux/ramips/patches-3.8/0124-MIPS-ralink-add-memory-definition-for-RT2880.patch deleted file mode 100644 index 975fb48a4b..0000000000 --- a/target/linux/ramips/patches-3.8/0124-MIPS-ralink-add-memory-definition-for-RT2880.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 0151f5f0dbf43b6b3718b0d1d403c87429ac0313 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sat, 13 Apr 2013 15:37:37 +0200 -Subject: [PATCH 124/137] MIPS: ralink: add memory definition for RT2880 - -Populate struct soc_info with the data that describes our RAM window. - -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5181/ ---- - arch/mips/include/asm/mach-ralink/rt288x.h | 4 ++++ - arch/mips/ralink/rt288x.c | 4 ++++ - 2 files changed, 8 insertions(+) - ---- a/arch/mips/include/asm/mach-ralink/rt288x.h -+++ b/arch/mips/include/asm/mach-ralink/rt288x.h -@@ -46,4 +46,8 @@ - - #define CLKCFG_SRAM_CS_N_WDT BIT(9) - -+#define RT2880_SDRAM_BASE 0x08000000 -+#define RT2880_MEM_SIZE_MIN 2 -+#define RT2880_MEM_SIZE_MAX 128 -+ - #endif ---- a/arch/mips/ralink/rt288x.c -+++ b/arch/mips/ralink/rt288x.c -@@ -136,4 +136,8 @@ void prom_soc_init(struct ralink_soc_inf - name, - (id >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK, - (id & CHIP_ID_REV_MASK)); -+ -+ soc_info->mem_base = RT2880_SDRAM_BASE; -+ soc_info->mem_size_min = RT2880_MEM_SIZE_MIN; -+ soc_info->mem_size_max = RT2880_MEM_SIZE_MAX; - } diff --git a/target/linux/ramips/patches-3.8/0125-MIPS-ralink-add-memory-definition-for-RT3883.patch b/target/linux/ramips/patches-3.8/0125-MIPS-ralink-add-memory-definition-for-RT3883.patch deleted file mode 100644 index 2c015ad24d..0000000000 --- a/target/linux/ramips/patches-3.8/0125-MIPS-ralink-add-memory-definition-for-RT3883.patch +++ /dev/null @@ -1,36 +0,0 @@ -From de85c6c3e2d5ed9c721a282d91af504a845e1fad Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sat, 13 Apr 2013 20:23:19 +0200 -Subject: [PATCH 125/137] MIPS: ralink: add memory definition for RT3883 - -Populate struct soc_info with the data that describes our RAM window. - -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5182/ ---- - arch/mips/include/asm/mach-ralink/rt3883.h | 4 ++++ - arch/mips/ralink/rt3883.c | 4 ++++ - 2 files changed, 8 insertions(+) - ---- a/arch/mips/include/asm/mach-ralink/rt3883.h -+++ b/arch/mips/include/asm/mach-ralink/rt3883.h -@@ -245,4 +245,8 @@ - #define RT3883_FLASH_CFG_WIDTH_16BIT 0x1 - #define RT3883_FLASH_CFG_WIDTH_32BIT 0x2 - -+#define RT3883_SDRAM_BASE 0x00000000 -+#define RT3883_MEM_SIZE_MIN 2 -+#define RT3883_MEM_SIZE_MAX 256 -+ - #endif /* _RT3883_REGS_H_ */ ---- a/arch/mips/ralink/rt3883.c -+++ b/arch/mips/ralink/rt3883.c -@@ -239,4 +239,8 @@ void prom_soc_init(struct ralink_soc_inf - name, - (id >> RT3883_REVID_VER_ID_SHIFT) & RT3883_REVID_VER_ID_MASK, - (id & RT3883_REVID_ECO_ID_MASK)); -+ -+ soc_info->mem_base = RT3883_SDRAM_BASE; -+ soc_info->mem_size_min = RT3883_MEM_SIZE_MIN; -+ soc_info->mem_size_max = RT3883_MEM_SIZE_MAX; - } diff --git a/target/linux/ramips/patches-3.8/0126-MIPS-ralink-add-memory-definition-for-MT7620.patch b/target/linux/ramips/patches-3.8/0126-MIPS-ralink-add-memory-definition-for-MT7620.patch deleted file mode 100644 index b36ab33071..0000000000 --- a/target/linux/ramips/patches-3.8/0126-MIPS-ralink-add-memory-definition-for-MT7620.patch +++ /dev/null @@ -1,58 +0,0 @@ -From c1d35c42d697e9c28c817921a79c5f814529a4c6 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sun, 14 Apr 2013 09:55:29 +0200 -Subject: [PATCH 126/137] MIPS: ralink: add memory definition for MT7620 - -Populate struct soc_info with the data that describes our RAM window. - -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5183/ ---- - arch/mips/include/asm/mach-ralink/mt7620.h | 8 ++++++++ - arch/mips/ralink/mt7620.c | 20 ++++++++++++++++++++ - 2 files changed, 28 insertions(+) - ---- a/arch/mips/include/asm/mach-ralink/mt7620.h -+++ b/arch/mips/include/asm/mach-ralink/mt7620.h -@@ -50,6 +50,14 @@ - #define SYSCFG0_DRAM_TYPE_DDR1 1 - #define SYSCFG0_DRAM_TYPE_DDR2 2 - -+#define MT7620_DRAM_BASE 0x0 -+#define MT7620_SDRAM_SIZE_MIN 2 -+#define MT7620_SDRAM_SIZE_MAX 64 -+#define MT7620_DDR1_SIZE_MIN 32 -+#define MT7620_DDR1_SIZE_MAX 128 -+#define MT7620_DDR2_SIZE_MIN 32 -+#define MT7620_DDR2_SIZE_MAX 256 -+ - #define MT7620_GPIO_MODE_I2C BIT(0) - #define MT7620_GPIO_MODE_UART0_SHIFT 2 - #define MT7620_GPIO_MODE_UART0_MASK 0x7 ---- a/arch/mips/ralink/mt7620.c -+++ b/arch/mips/ralink/mt7620.c -@@ -211,4 +211,24 @@ void prom_soc_init(struct ralink_soc_inf - - cfg0 = __raw_readl(sysc + SYSC_REG_SYSTEM_CONFIG0); - dram_type = (cfg0 >> SYSCFG0_DRAM_TYPE_SHIFT) & SYSCFG0_DRAM_TYPE_MASK; -+ -+ switch (dram_type) { -+ case SYSCFG0_DRAM_TYPE_SDRAM: -+ soc_info->mem_size_min = MT7620_SDRAM_SIZE_MIN; -+ soc_info->mem_size_max = MT7620_SDRAM_SIZE_MAX; -+ break; -+ -+ case SYSCFG0_DRAM_TYPE_DDR1: -+ soc_info->mem_size_min = MT7620_DDR1_SIZE_MIN; -+ soc_info->mem_size_max = MT7620_DDR1_SIZE_MAX; -+ break; -+ -+ case SYSCFG0_DRAM_TYPE_DDR2: -+ soc_info->mem_size_min = MT7620_DDR2_SIZE_MIN; -+ soc_info->mem_size_max = MT7620_DDR2_SIZE_MAX; -+ break; -+ default: -+ BUG(); -+ } -+ soc_info->mem_base = MT7620_DRAM_BASE; - } diff --git a/target/linux/ramips/patches-3.8/0127-MIPS-ralink-make-use-of-the-new-memory-detection-cod.patch b/target/linux/ramips/patches-3.8/0127-MIPS-ralink-make-use-of-the-new-memory-detection-cod.patch deleted file mode 100644 index 59bdaddbe5..0000000000 --- a/target/linux/ramips/patches-3.8/0127-MIPS-ralink-make-use-of-the-new-memory-detection-cod.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 1618a00f709817cbcdebf038d0b5e251c8d67237 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sat, 13 Apr 2013 15:15:51 +0200 -Subject: [PATCH 127/137] MIPS: ralink: make use of the new memory detection - code - -Call detect_memory_region() from plat_mem_setup() unless the size was already -read from the system controller. - -Signed-off-by: John Crispin -Patchwork: http://patchwork.linux-mips.org/patch/5184/ ---- - arch/mips/ralink/of.c | 9 +++++++++ - 1 file changed, 9 insertions(+) - ---- a/arch/mips/ralink/of.c -+++ b/arch/mips/ralink/of.c -@@ -11,6 +11,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -85,6 +86,14 @@ void __init plat_mem_setup(void) - * parsed resulting in our memory appearing - */ - __dt_setup_arch(&__dtb_start); -+ -+ if (soc_info.mem_size) -+ add_memory_region(soc_info.mem_base, soc_info.mem_size, -+ BOOT_MEM_RAM); -+ else -+ detect_memory_region(soc_info.mem_base, -+ soc_info.mem_size_min * SZ_1M, -+ soc_info.mem_size_max * SZ_1M); - } - - static int __init plat_of_setup(void) diff --git a/target/linux/ramips/patches-3.8/0128-MIPS-ralink-add-pinmux-driver.patch b/target/linux/ramips/patches-3.8/0128-MIPS-ralink-add-pinmux-driver.patch deleted file mode 100644 index f28115cd18..0000000000 --- a/target/linux/ramips/patches-3.8/0128-MIPS-ralink-add-pinmux-driver.patch +++ /dev/null @@ -1,129 +0,0 @@ -From 5a2079532dfaf5762f658370ee7a0afb686f066e Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Mon, 22 Apr 2013 23:11:42 +0200 -Subject: [PATCH 128/137] MIPS: ralink: add pinmux driver - -Add code to setup the pinmux on ralonk SoC. The SoC has a single 32 bit register -for this functionality with simple on/off bits. Building a full featured pinctrl -driver would be overkill. - -Signed-off-by: John Crispin ---- - arch/mips/ralink/Makefile | 2 +- - arch/mips/ralink/common.h | 2 ++ - arch/mips/ralink/of.c | 2 ++ - arch/mips/ralink/pinmux.c | 76 +++++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 81 insertions(+), 1 deletion(-) - create mode 100644 arch/mips/ralink/pinmux.c - ---- a/arch/mips/ralink/Makefile -+++ b/arch/mips/ralink/Makefile -@@ -6,7 +6,7 @@ - # Copyright (C) 2009-2011 Gabor Juhos - # Copyright (C) 2013 John Crispin - --obj-y := prom.o of.o reset.o clk.o irq.o -+obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o - - obj-$(CONFIG_SOC_RT288X) += rt288x.o - obj-$(CONFIG_SOC_RT305X) += rt305x.o ---- a/arch/mips/ralink/common.h -+++ b/arch/mips/ralink/common.h -@@ -50,4 +50,6 @@ extern void prom_soc_init(struct ralink_ - - __iomem void *plat_of_remap_node(const char *node); - -+void ralink_pinmux(void); -+ - #endif /* _RALINK_COMMON_H__ */ ---- a/arch/mips/ralink/of.c -+++ b/arch/mips/ralink/of.c -@@ -110,6 +110,8 @@ static int __init plat_of_setup(void) - if (of_platform_populate(NULL, of_ids, NULL, NULL)) - panic("failed to populate DT\n"); - -+ ralink_pinmux(); -+ - return 0; - } - ---- /dev/null -+++ b/arch/mips/ralink/pinmux.c -@@ -0,0 +1,77 @@ -+/* -+ * 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. -+ * -+ * Copyright (C) 2013 John Crispin -+ */ -+ -+#include -+#include -+ -+#include -+ -+#include "common.h" -+ -+#define SYSC_REG_GPIO_MODE 0x60 -+ -+static int ralink_mux_mask(const char *name, struct ralink_pinmux_grp *grps, u32* mask) -+{ -+ for (; grps && grps->name; grps++) -+ if (!strcmp(grps->name, name)) { -+ *mask = grps->mask; -+ return 0; -+ } -+ -+ return -1; -+} -+ -+void ralink_pinmux(void) -+{ -+ const __be32 *wdt; -+ struct device_node *np; -+ struct property *prop; -+ const char *uart, *pin; -+ u32 mode = 0; -+ int m; -+ -+ np = of_find_compatible_node(NULL, NULL, "ralink,rt3050-sysc"); -+ if (!np) -+ return; -+ -+ of_property_for_each_string(np, "ralink,gpiomux", prop, pin) { -+ if (!ralink_mux_mask(pin, rt_gpio_pinmux.mode, &m)) { -+ mode |= m; -+ pr_debug("pinmux: registered gpiomux \"%s\"\n", pin); -+ } else { -+ pr_err("pinmux: failed to load \"%s\"\n", pin); -+ } -+ } -+ -+ of_property_for_each_string(np, "ralink,pinmux", prop, pin) { -+ if (!ralink_mux_mask(pin, rt_gpio_pinmux.mode, &m)) { -+ mode &= ~m; -+ pr_debug("pinmux: registered pinmux \"%s\"\n", pin); -+ } else { -+ pr_err("pinmux: failed to load group \"%s\"\n", pin); -+ } -+ } -+ -+ of_property_read_string(np, "ralink,uartmux", &uart); -+ if (uart) { -+ mode &= ~(rt_gpio_pinmux.uart_mask << rt_gpio_pinmux.uart_shift); -+ if (ralink_mux_mask(uart, rt_gpio_pinmux.uart, &m)) { -+ pr_err("pinmux: failed to load uartmux \"%s\"\n", uart); -+ mode |= rt_gpio_pinmux.uart_mask << rt_gpio_pinmux.uart_shift; -+ } else { -+ mode |= m << rt_gpio_pinmux.uart_shift; -+ pr_debug("pinmux: registered uartmux \"%s\"\n", uart); -+ } -+ } -+ -+ wdt = of_get_property(np, "ralink,wdtmux", NULL); -+ if (wdt && *wdt && rt_gpio_pinmux.wdt_reset) -+ rt_gpio_pinmux.wdt_reset(); -+ -+ rt_sysc_w32(mode, SYSC_REG_GPIO_MODE); -+} diff --git a/target/linux/ramips/patches-3.8/0129-MIPS-ralink-add-support-for-periodic-timer-irq.patch b/target/linux/ramips/patches-3.8/0129-MIPS-ralink-add-support-for-periodic-timer-irq.patch deleted file mode 100644 index 6664e11e45..0000000000 --- a/target/linux/ramips/patches-3.8/0129-MIPS-ralink-add-support-for-periodic-timer-irq.patch +++ /dev/null @@ -1,220 +0,0 @@ -From 1f307fd0fdca585d5c7c32963e8a8a6f38d8a78c Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Sat, 23 Mar 2013 19:44:41 +0100 -Subject: [PATCH 129/137] MIPS: ralink: add support for periodic timer irq - -Adds a driver for the periodic timer found on Ralink SoC. - -Signed-off-by: John Crispin ---- - arch/mips/ralink/Makefile | 2 +- - arch/mips/ralink/timer.c | 192 +++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 193 insertions(+), 1 deletion(-) - create mode 100644 arch/mips/ralink/timer.c - ---- a/arch/mips/ralink/Makefile -+++ b/arch/mips/ralink/Makefile -@@ -6,7 +6,7 @@ - # Copyright (C) 2009-2011 Gabor Juhos - # Copyright (C) 2013 John Crispin - --obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o -+obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o timer.o - - obj-$(CONFIG_SOC_RT288X) += rt288x.o - obj-$(CONFIG_SOC_RT305X) += rt305x.o ---- /dev/null -+++ b/arch/mips/ralink/timer.c -@@ -0,0 +1,192 @@ -+/* -+ * 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. -+ * -+ * Copyright (C) 2013 John Crispin -+*/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#define TIMER_REG_TMRSTAT 0x00 -+#define TIMER_REG_TMR0LOAD 0x10 -+#define TIMER_REG_TMR0CTL 0x18 -+ -+#define TMRSTAT_TMR0INT BIT(0) -+ -+#define TMR0CTL_ENABLE BIT(7) -+#define TMR0CTL_MODE_PERIODIC BIT(4) -+#define TMR0CTL_PRESCALER 1 -+#define TMR0CTL_PRESCALE_VAL (0xf - TMR0CTL_PRESCALER) -+#define TMR0CTL_PRESCALE_DIV (65536 / BIT(TMR0CTL_PRESCALER)) -+ -+struct rt_timer { -+ struct device *dev; -+ void __iomem *membase; -+ int irq; -+ unsigned long timer_freq; -+ unsigned long timer_div; -+}; -+ -+static inline void rt_timer_w32(struct rt_timer *rt, u8 reg, u32 val) -+{ -+ __raw_writel(val, rt->membase + reg); -+} -+ -+static inline u32 rt_timer_r32(struct rt_timer *rt, u8 reg) -+{ -+ return __raw_readl(rt->membase + reg); -+} -+ -+static irqreturn_t rt_timer_irq(int irq, void *_rt) -+{ -+ struct rt_timer *rt = (struct rt_timer *) _rt; -+ -+ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); -+ rt_timer_w32(rt, TIMER_REG_TMRSTAT, TMRSTAT_TMR0INT); -+ -+ return IRQ_HANDLED; -+} -+ -+ -+static int rt_timer_request(struct rt_timer *rt) -+{ -+ int err = request_irq(rt->irq, rt_timer_irq, IRQF_DISABLED, -+ dev_name(rt->dev), rt); -+ if (err) { -+ dev_err(rt->dev, "failed to request irq\n"); -+ } else { -+ u32 t = TMR0CTL_MODE_PERIODIC | TMR0CTL_PRESCALE_VAL; -+ rt_timer_w32(rt, TIMER_REG_TMR0CTL, t); -+ } -+ return err; -+} -+ -+static void rt_timer_free(struct rt_timer *rt) -+{ -+ free_irq(rt->irq, rt); -+} -+ -+static int rt_timer_config(struct rt_timer *rt, unsigned long divisor) -+{ -+ if (rt->timer_freq < divisor) -+ rt->timer_div = rt->timer_freq; -+ else -+ rt->timer_div = divisor; -+ -+ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); -+ -+ return 0; -+} -+ -+static int rt_timer_enable(struct rt_timer *rt) -+{ -+ u32 t; -+ -+ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); -+ -+ t = rt_timer_r32(rt, TIMER_REG_TMR0CTL); -+ t |= TMR0CTL_ENABLE; -+ rt_timer_w32(rt, TIMER_REG_TMR0CTL, t); -+ -+ return 0; -+} -+ -+static void rt_timer_disable(struct rt_timer *rt) -+{ -+ u32 t; -+ -+ t = rt_timer_r32(rt, TIMER_REG_TMR0CTL); -+ t &= ~TMR0CTL_ENABLE; -+ rt_timer_w32(rt, TIMER_REG_TMR0CTL, t); -+} -+ -+static int rt_timer_probe(struct platform_device *pdev) -+{ -+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ struct rt_timer *rt; -+ struct clk *clk; -+ -+ if (!res) { -+ dev_err(&pdev->dev, "no memory resource found\n"); -+ return -EINVAL; -+ } -+ -+ rt = devm_kzalloc(&pdev->dev, sizeof(*rt), GFP_KERNEL); -+ if (!rt) { -+ dev_err(&pdev->dev, "failed to allocate memory\n"); -+ return -ENOMEM; -+ } -+ -+ rt->irq = platform_get_irq(pdev, 0); -+ if (!rt->irq) { -+ dev_err(&pdev->dev, "failed to load irq\n"); -+ return -ENOENT; -+ } -+ -+ rt->membase = devm_request_and_ioremap(&pdev->dev, res); -+ if (!rt->membase) { -+ dev_err(&pdev->dev, "failed to ioremap\n"); -+ return -ENOMEM; -+ } -+ -+ clk = devm_clk_get(&pdev->dev, NULL); -+ if (IS_ERR(clk)) { -+ dev_err(&pdev->dev, "failed get clock rate\n"); -+ return PTR_ERR(clk); -+ } -+ -+ rt->timer_freq = clk_get_rate(clk) / TMR0CTL_PRESCALE_DIV; -+ if (!rt->timer_freq) -+ return -EINVAL; -+ -+ rt->dev = &pdev->dev; -+ platform_set_drvdata(pdev, rt); -+ -+ rt_timer_request(rt); -+ rt_timer_config(rt, 2); -+ rt_timer_enable(rt); -+ -+ dev_info(&pdev->dev, "maximum frequncy is %luHz\n", rt->timer_freq); -+ -+ return 0; -+} -+ -+static int rt_timer_remove(struct platform_device *pdev) -+{ -+ struct rt_timer *rt = platform_get_drvdata(pdev); -+ -+ rt_timer_disable(rt); -+ rt_timer_free(rt); -+ -+ return 0; -+} -+ -+static const struct of_device_id rt_timer_match[] = { -+ { .compatible = "ralink,rt2880-timer" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, rt_timer_match); -+ -+static struct platform_driver rt_timer_driver = { -+ .probe = rt_timer_probe, -+ .remove = rt_timer_remove, -+ .driver = { -+ .name = "rt-timer", -+ .owner = THIS_MODULE, -+ .of_match_table = rt_timer_match -+ }, -+}; -+ -+module_platform_driver(rt_timer_driver); -+ -+MODULE_DESCRIPTION("Ralink RT2880 timer"); -+MODULE_AUTHOR("John Crispin -Date: Tue, 22 Jan 2013 18:24:34 +0100 -Subject: [PATCH 130/137] GPIO: MIPS: ralink: adds ralink gpio support - -Add gpio driver for Ralink SoC. This driver makes the gpio core on -RT2880, RT305x, rt3352, rt3662, rt3883, rt5350 and mt7620 work. - -Signed-off-by: John Crispin ---- - arch/mips/Kconfig | 1 + - arch/mips/include/asm/mach-ralink/gpio.h | 24 ++++ - drivers/gpio/Kconfig | 6 + - drivers/gpio/Makefile | 1 + - drivers/gpio/gpio-ralink.c | 176 ++++++++++++++++++++++++++++++ - 5 files changed, 208 insertions(+) - create mode 100644 arch/mips/include/asm/mach-ralink/gpio.h - create mode 100644 drivers/gpio/gpio-ralink.c - ---- a/arch/mips/Kconfig -+++ b/arch/mips/Kconfig -@@ -449,6 +449,7 @@ config RALINK - select SYS_HAS_EARLY_PRINTK - select HAVE_MACH_CLKDEV - select CLKDEV_LOOKUP -+ select ARCH_REQUIRE_GPIOLIB - - config SGI_IP22 - bool "SGI IP22 (Indy/Indigo2)" ---- /dev/null -+++ b/arch/mips/include/asm/mach-ralink/gpio.h -@@ -0,0 +1,24 @@ -+/* -+ * Ralink SoC GPIO API support -+ * -+ * Copyright (C) 2008-2009 Gabor Juhos -+ * Copyright (C) 2008 Imre Kaloz -+ * -+ * 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 __ASM_MACH_RALINK_GPIO_H -+#define __ASM_MACH_RALINK_GPIO_H -+ -+#define ARCH_NR_GPIOS 128 -+#include -+ -+#define gpio_get_value __gpio_get_value -+#define gpio_set_value __gpio_set_value -+#define gpio_cansleep __gpio_cansleep -+#define gpio_to_irq __gpio_to_irq -+ -+#endif /* __ASM_MACH_RALINK_GPIO_H */ ---- a/drivers/gpio/Kconfig -+++ b/drivers/gpio/Kconfig -@@ -201,6 +201,12 @@ config GPIO_PXA - help - Say yes here to support the PXA GPIO device - -+config GPIO_RALINK -+ bool "Ralink GPIO Support" -+ depends on RALINK -+ help -+ Say yes here to support the Ralink SoC GPIO device -+ - config GPIO_SPEAR_SPICS - bool "ST SPEAr13xx SPI Chip Select as GPIO support" - depends on PLAT_SPEAR ---- a/drivers/gpio/Makefile -+++ b/drivers/gpio/Makefile -@@ -54,6 +54,7 @@ obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf85 - obj-$(CONFIG_GPIO_PCH) += gpio-pch.o - obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o - obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o -+obj-$(CONFIG_GPIO_RALINK) += gpio-ralink.o - obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o - obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o - obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o ---- /dev/null -+++ b/drivers/gpio/gpio-ralink.c -@@ -0,0 +1,182 @@ -+/* -+ * 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. -+ * -+ * Copyright (C) 2009-2011 Gabor Juhos -+ * Copyright (C) 2013 John Crispin -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+ -+enum ralink_gpio_reg { -+ GPIO_REG_INT = 0, -+ GPIO_REG_EDGE, -+ GPIO_REG_RENA, -+ GPIO_REG_FENA, -+ GPIO_REG_DATA, -+ GPIO_REG_DIR, -+ GPIO_REG_POL, -+ GPIO_REG_SET, -+ GPIO_REG_RESET, -+ GPIO_REG_TOGGLE, -+ GPIO_REG_MAX -+}; -+ -+struct ralink_gpio_chip { -+ struct gpio_chip chip; -+ u8 regs[GPIO_REG_MAX]; -+ -+ spinlock_t lock; -+ void __iomem *membase; -+}; -+ -+static inline struct ralink_gpio_chip *to_ralink_gpio(struct gpio_chip *chip) -+{ -+ struct ralink_gpio_chip *rg; -+ -+ rg = container_of(chip, struct ralink_gpio_chip, chip); -+ return rg; -+} -+ -+static inline void rt_gpio_w32(struct ralink_gpio_chip *rg, u8 reg, u32 val) -+{ -+ iowrite32(val, rg->membase + rg->regs[reg]); -+} -+ -+static inline u32 rt_gpio_r32(struct ralink_gpio_chip *rg, u8 reg) -+{ -+ return ioread32(rg->membase + rg->regs[reg]); -+} -+ -+static void ralink_gpio_set(struct gpio_chip *chip, unsigned offset, int value) -+{ -+ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); -+ -+ rt_gpio_w32(rg, (value) ? GPIO_REG_SET : GPIO_REG_RESET, BIT(offset)); -+} -+ -+static int ralink_gpio_get(struct gpio_chip *chip, unsigned offset) -+{ -+ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); -+ -+ return !!(rt_gpio_r32(rg, GPIO_REG_DATA) & BIT(offset)); -+} -+ -+static int ralink_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -+{ -+ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); -+ unsigned long flags; -+ u32 t; -+ -+ spin_lock_irqsave(&rg->lock, flags); -+ t = rt_gpio_r32(rg, GPIO_REG_DIR); -+ t &= ~BIT(offset); -+ rt_gpio_w32(rg, GPIO_REG_DIR, t); -+ spin_unlock_irqrestore(&rg->lock, flags); -+ -+ return 0; -+} -+ -+static int ralink_gpio_direction_output(struct gpio_chip *chip, -+ unsigned offset, int value) -+{ -+ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); -+ unsigned long flags; -+ u32 t; -+ -+ spin_lock_irqsave(&rg->lock, flags); -+ ralink_gpio_set(chip, offset, value); -+ t = rt_gpio_r32(rg, GPIO_REG_DIR); -+ t |= BIT(offset); -+ rt_gpio_w32(rg, GPIO_REG_DIR, t); -+ spin_unlock_irqrestore(&rg->lock, flags); -+ -+ return 0; -+} -+ -+static int ralink_gpio_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ struct ralink_gpio_chip *gc; -+ const __be32 *ngpio; -+ const __be32 *gpiobase; -+ -+ if (!res) { -+ dev_err(&pdev->dev, "failed to find resource\n"); -+ return -ENOMEM; -+ } -+ -+ gc = devm_kzalloc(&pdev->dev, -+ sizeof(struct ralink_gpio_chip), GFP_KERNEL); -+ if (!gc) -+ return -ENOMEM; -+ -+ gc->membase = devm_request_and_ioremap(&pdev->dev, res); -+ if (!gc->membase) { -+ dev_err(&pdev->dev, "cannot remap I/O memory region\n"); -+ return -ENOMEM; -+ } -+ -+ if (of_property_read_u8_array(np, "ralink,register-map", -+ gc->regs, GPIO_REG_MAX)) { -+ dev_err(&pdev->dev, "failed to read register definition\n"); -+ return -EINVAL; -+ } -+ -+ ngpio = of_get_property(np, "ralink,num-gpios", NULL); -+ if (!ngpio) { -+ dev_err(&pdev->dev, "failed to read number of pins\n"); -+ return -EINVAL; -+ } -+ -+ gpiobase = of_get_property(np, "ralink,gpio-base", NULL); -+ if (gpiobase) -+ gc->chip.base = be32_to_cpu(*gpiobase); -+ else -+ gc->chip.base = -1; -+ -+ gc->chip.label = dev_name(&pdev->dev); -+ gc->chip.of_node = np; -+ gc->chip.ngpio = be32_to_cpu(*ngpio); -+ gc->chip.direction_input = ralink_gpio_direction_input; -+ gc->chip.direction_output = ralink_gpio_direction_output; -+ gc->chip.get = ralink_gpio_get; -+ gc->chip.set = ralink_gpio_set; -+ -+ spin_lock_init(&gc->lock); -+ -+ /* set polarity to low for all lines */ -+ rt_gpio_w32(gc, GPIO_REG_POL, 0); -+ -+ dev_info(&pdev->dev, "registering %d gpios\n", gc->chip.ngpio); -+ -+ return gpiochip_add(&gc->chip); -+} -+ -+static const struct of_device_id ralink_gpio_match[] = { -+ { .compatible = "ralink,rt2880-gpio" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, ralink_gpio_match); -+ -+static struct platform_driver ralink_gpio_driver = { -+ .probe = ralink_gpio_probe, -+ .driver = { -+ .name = "rt2880_gpio", -+ .owner = THIS_MODULE, -+ .of_match_table = ralink_gpio_match, -+ }, -+}; -+ -+static int __init ralink_gpio_init(void) -+{ -+ return platform_driver_register(&ralink_gpio_driver); -+} -+ -+subsys_initcall(ralink_gpio_init); diff --git a/target/linux/ramips/patches-3.8/0131-SPI-ralink-add-Ralink-SoC-spi-driver.patch b/target/linux/ramips/patches-3.8/0131-SPI-ralink-add-Ralink-SoC-spi-driver.patch deleted file mode 100644 index 8217b48318..0000000000 --- a/target/linux/ramips/patches-3.8/0131-SPI-ralink-add-Ralink-SoC-spi-driver.patch +++ /dev/null @@ -1,515 +0,0 @@ -From 32c1cff4c75925a0bbd305e85ed4adb30140cd42 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Mon, 22 Apr 2013 23:16:18 +0200 -Subject: [PATCH 131/137] SPI: ralink: add Ralink SoC spi driver - -Add the driver needed to make SPI work on Ralink SoC. - -Signed-off-by: John Crispin ---- - drivers/spi/Kconfig | 6 + - drivers/spi/Makefile | 1 + - drivers/spi/spi-ralink.c | 472 ++++++++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 479 insertions(+) - create mode 100644 drivers/spi/spi-ralink.c - ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -324,6 +324,12 @@ config SPI_RSPI - help - SPI driver for Renesas RSPI blocks. - -+config SPI_RALINK -+ tristate "Ralink RT288x/RT305x/RT3662 SPI Controller" -+ depends on (SOC_RT288X || SOC_RT305X || SOC_RT3883) -+ help -+ This selects a driver for the Ralink RT288x/RT305x SPI Controller. -+ - config SPI_S3C24XX - tristate "Samsung S3C24XX series SPI" - depends on ARCH_S3C24XX && EXPERIMENTAL ---- a/drivers/spi/Makefile -+++ b/drivers/spi/Makefile -@@ -51,6 +51,7 @@ obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx. - obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx.o - obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o - obj-$(CONFIG_SPI_RSPI) += spi-rspi.o -+obj-$(CONFIG_SPI_RALINK) += spi-ralink.o - obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o - spi-s3c24xx-hw-y := spi-s3c24xx.o - spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o ---- /dev/null -+++ b/drivers/spi/spi-ralink.c -@@ -0,0 +1,472 @@ -+/* -+ * spi-ralink.c -- Ralink RT288x/RT305x SPI controller driver -+ * -+ * Copyright (C) 2011 Sergiy -+ * Copyright (C) 2011-2013 Gabor Juhos -+ * -+ * Some parts are based on spi-orion.c: -+ * Author: Shadi Ammouri -+ * Copyright (C) 2007-2008 Marvell Ltd. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DRIVER_NAME "spi-ralink" -+#define RALINK_NUM_CHIPSELECTS 1 /* only one slave is supported*/ -+#define RALINK_SPI_WAIT_RDY_MAX_LOOP 2000 /* in usec */ -+ -+#define RAMIPS_SPI_STAT 0x00 -+#define RAMIPS_SPI_CFG 0x10 -+#define RAMIPS_SPI_CTL 0x14 -+#define RAMIPS_SPI_DATA 0x20 -+ -+/* SPISTAT register bit field */ -+#define SPISTAT_BUSY BIT(0) -+ -+/* SPICFG register bit field */ -+#define SPICFG_LSBFIRST 0 -+#define SPICFG_MSBFIRST BIT(8) -+#define SPICFG_SPICLKPOL BIT(6) -+#define SPICFG_RXCLKEDGE_FALLING BIT(5) -+#define SPICFG_TXCLKEDGE_FALLING BIT(4) -+#define SPICFG_SPICLK_PRESCALE_MASK 0x7 -+#define SPICFG_SPICLK_DIV2 0 -+#define SPICFG_SPICLK_DIV4 1 -+#define SPICFG_SPICLK_DIV8 2 -+#define SPICFG_SPICLK_DIV16 3 -+#define SPICFG_SPICLK_DIV32 4 -+#define SPICFG_SPICLK_DIV64 5 -+#define SPICFG_SPICLK_DIV128 6 -+#define SPICFG_SPICLK_DISABLE 7 -+ -+/* SPICTL register bit field */ -+#define SPICTL_HIZSDO BIT(3) -+#define SPICTL_STARTWR BIT(2) -+#define SPICTL_STARTRD BIT(1) -+#define SPICTL_SPIENA BIT(0) -+ -+#ifdef DEBUG -+#define spi_debug(args...) printk(args) -+#else -+#define spi_debug(args...) -+#endif -+ -+struct ralink_spi { -+ struct spi_master *master; -+ void __iomem *base; -+ unsigned int sys_freq; -+ unsigned int speed; -+ struct clk *clk; -+}; -+ -+static inline struct ralink_spi *spidev_to_ralink_spi(struct spi_device *spi) -+{ -+ return spi_master_get_devdata(spi->master); -+} -+ -+static inline u32 ralink_spi_read(struct ralink_spi *rs, u32 reg) -+{ -+ return ioread32(rs->base + reg); -+} -+ -+static inline void ralink_spi_write(struct ralink_spi *rs, u32 reg, u32 val) -+{ -+ iowrite32(val, rs->base + reg); -+} -+ -+static inline void ralink_spi_setbits(struct ralink_spi *rs, u32 reg, u32 mask) -+{ -+ void __iomem *addr = rs->base + reg; -+ u32 val; -+ -+ val = ioread32(addr); -+ val |= mask; -+ iowrite32(val, addr); -+} -+ -+static inline void ralink_spi_clrbits(struct ralink_spi *rs, u32 reg, u32 mask) -+{ -+ void __iomem *addr = rs->base + reg; -+ u32 val; -+ -+ val = ioread32(addr); -+ val &= ~mask; -+ iowrite32(val, addr); -+} -+ -+static int ralink_spi_baudrate_set(struct spi_device *spi, unsigned int speed) -+{ -+ struct ralink_spi *rs = spidev_to_ralink_spi(spi); -+ u32 rate; -+ u32 prescale; -+ u32 reg; -+ -+ spi_debug("%s: speed:%u\n", __func__, speed); -+ -+ /* -+ * the supported rates are: 2, 4, 8, ... 128 -+ * round up as we look for equal or less speed -+ */ -+ rate = DIV_ROUND_UP(rs->sys_freq, speed); -+ spi_debug("%s: rate-1:%u\n", __func__, rate); -+ rate = roundup_pow_of_two(rate); -+ spi_debug("%s: rate-2:%u\n", __func__, rate); -+ -+ /* check if requested speed is too small */ -+ if (rate > 128) -+ return -EINVAL; -+ -+ if (rate < 2) -+ rate = 2; -+ -+ /* Convert the rate to SPI clock divisor value. */ -+ prescale = ilog2(rate/2); -+ spi_debug("%s: prescale:%u\n", __func__, prescale); -+ -+ reg = ralink_spi_read(rs, RAMIPS_SPI_CFG); -+ reg = ((reg & ~SPICFG_SPICLK_PRESCALE_MASK) | prescale); -+ ralink_spi_write(rs, RAMIPS_SPI_CFG, reg); -+ rs->speed = speed; -+ return 0; -+} -+ -+/* -+ * called only when no transfer is active on the bus -+ */ -+static int -+ralink_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) -+{ -+ struct ralink_spi *rs = spidev_to_ralink_spi(spi); -+ unsigned int speed = spi->max_speed_hz; -+ int rc; -+ unsigned int bits_per_word = 8; -+ -+ if ((t != NULL) && t->speed_hz) -+ speed = t->speed_hz; -+ -+ if ((t != NULL) && t->bits_per_word) -+ bits_per_word = t->bits_per_word; -+ -+ if (rs->speed != speed) { -+ spi_debug("%s: speed_hz:%u\n", __func__, speed); -+ rc = ralink_spi_baudrate_set(spi, speed); -+ if (rc) -+ return rc; -+ } -+ -+ if (bits_per_word != 8) { -+ spi_debug("%s: bad bits_per_word: %u\n", __func__, -+ bits_per_word); -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static void ralink_spi_set_cs(struct ralink_spi *rs, int enable) -+{ -+ if (enable) -+ ralink_spi_clrbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA); -+ else -+ ralink_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA); -+} -+ -+static inline int ralink_spi_wait_till_ready(struct ralink_spi *rs) -+{ -+ int i; -+ -+ for (i = 0; i < RALINK_SPI_WAIT_RDY_MAX_LOOP; i++) { -+ u32 status; -+ -+ status = ralink_spi_read(rs, RAMIPS_SPI_STAT); -+ if ((status & SPISTAT_BUSY) == 0) -+ return 0; -+ -+ udelay(1); -+ } -+ -+ return -ETIMEDOUT; -+} -+ -+static unsigned int -+ralink_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer) -+{ -+ struct ralink_spi *rs = spidev_to_ralink_spi(spi); -+ unsigned count = 0; -+ u8 *rx = xfer->rx_buf; -+ const u8 *tx = xfer->tx_buf; -+ int err; -+ -+ spi_debug("%s(%d): %s %s\n", __func__, xfer->len, -+ (tx != NULL) ? "tx" : " ", -+ (rx != NULL) ? "rx" : " "); -+ -+ if (tx) { -+ for (count = 0; count < xfer->len; count++) { -+ ralink_spi_write(rs, RAMIPS_SPI_DATA, tx[count]); -+ ralink_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTWR); -+ err = ralink_spi_wait_till_ready(rs); -+ if (err) { -+ dev_err(&spi->dev, "TX failed, err=%d\n", err); -+ goto out; -+ } -+ } -+ } -+ -+ if (rx) { -+ for (count = 0; count < xfer->len; count++) { -+ ralink_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTRD); -+ err = ralink_spi_wait_till_ready(rs); -+ if (err) { -+ dev_err(&spi->dev, "RX failed, err=%d\n", err); -+ goto out; -+ } -+ rx[count] = (u8) ralink_spi_read(rs, RAMIPS_SPI_DATA); -+ } -+ } -+ -+out: -+ return count; -+} -+ -+static int ralink_spi_transfer_one_message(struct spi_master *master, -+ struct spi_message *m) -+{ -+ struct ralink_spi *rs = spi_master_get_devdata(master); -+ struct spi_device *spi = m->spi; -+ struct spi_transfer *t = NULL; -+ int par_override = 0; -+ int status = 0; -+ int cs_active = 0; -+ -+ /* Load defaults */ -+ status = ralink_spi_setup_transfer(spi, NULL); -+ if (status < 0) -+ goto msg_done; -+ -+ list_for_each_entry(t, &m->transfers, transfer_list) { -+ unsigned int bits_per_word = spi->bits_per_word; -+ -+ if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { -+ dev_err(&spi->dev, -+ "message rejected: invalid transfer data buffers\n"); -+ status = -EIO; -+ goto msg_done; -+ } -+ -+ if (t->bits_per_word) -+ bits_per_word = t->bits_per_word; -+ -+ if (bits_per_word != 8) { -+ dev_err(&spi->dev, -+ "message rejected: invalid transfer bits_per_word (%d bits)\n", -+ bits_per_word); -+ status = -EIO; -+ goto msg_done; -+ } -+ -+ if (t->speed_hz && t->speed_hz < (rs->sys_freq / 128)) { -+ dev_err(&spi->dev, -+ "message rejected: device min speed (%d Hz) exceeds required transfer speed (%d Hz)\n", -+ (rs->sys_freq / 128), t->speed_hz); -+ status = -EIO; -+ goto msg_done; -+ } -+ -+ if (par_override || t->speed_hz || t->bits_per_word) { -+ par_override = 1; -+ status = ralink_spi_setup_transfer(spi, t); -+ if (status < 0) -+ goto msg_done; -+ if (!t->speed_hz && !t->bits_per_word) -+ par_override = 0; -+ } -+ -+ if (!cs_active) { -+ ralink_spi_set_cs(rs, 1); -+ cs_active = 1; -+ } -+ -+ if (t->len) -+ m->actual_length += ralink_spi_write_read(spi, t); -+ -+ if (t->delay_usecs) -+ udelay(t->delay_usecs); -+ -+ if (t->cs_change) { -+ ralink_spi_set_cs(rs, 0); -+ cs_active = 0; -+ } -+ } -+ -+msg_done: -+ if (cs_active) -+ ralink_spi_set_cs(rs, 0); -+ -+ m->status = status; -+ spi_finalize_current_message(master); -+ -+ return 0; -+} -+ -+static int ralink_spi_setup(struct spi_device *spi) -+{ -+ struct ralink_spi *rs = spidev_to_ralink_spi(spi); -+ -+ if ((spi->max_speed_hz == 0) || -+ (spi->max_speed_hz > (rs->sys_freq / 2))) -+ spi->max_speed_hz = (rs->sys_freq / 2); -+ -+ if (spi->max_speed_hz < (rs->sys_freq / 128)) { -+ dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n", -+ spi->max_speed_hz); -+ return -EINVAL; -+ } -+ -+ if (spi->bits_per_word != 0 && spi->bits_per_word != 8) { -+ dev_err(&spi->dev, -+ "setup: requested bits per words - os wrong %d bpw\n", -+ spi->bits_per_word); -+ return -EINVAL; -+ } -+ -+ if (spi->bits_per_word == 0) -+ spi->bits_per_word = 8; -+ -+ /* -+ * baudrate & width will be set ralink_spi_setup_transfer -+ */ -+ return 0; -+} -+ -+static void ralink_spi_reset(struct ralink_spi *rs) -+{ -+ ralink_spi_write(rs, RAMIPS_SPI_CFG, -+ SPICFG_MSBFIRST | SPICFG_TXCLKEDGE_FALLING | -+ SPICFG_SPICLK_DIV16 | SPICFG_SPICLKPOL); -+ ralink_spi_write(rs, RAMIPS_SPI_CTL, SPICTL_HIZSDO | SPICTL_SPIENA); -+} -+ -+static int ralink_spi_probe(struct platform_device *pdev) -+{ -+ struct spi_master *master; -+ struct ralink_spi *rs; -+ struct resource *r; -+ int status = 0; -+ -+ master = spi_alloc_master(&pdev->dev, sizeof(*rs)); -+ if (master == NULL) { -+ dev_dbg(&pdev->dev, "master allocation failed\n"); -+ return -ENOMEM; -+ } -+ -+ //if (pdev->id != -1) -+ master->bus_num = 0; -+ -+ /* we support only mode 0, and no options */ -+ master->mode_bits = 0; -+ -+ master->setup = ralink_spi_setup; -+ master->transfer_one_message = ralink_spi_transfer_one_message; -+ master->num_chipselect = RALINK_NUM_CHIPSELECTS; -+ master->dev.of_node = pdev->dev.of_node; -+ -+ dev_set_drvdata(&pdev->dev, master); -+ -+ rs = spi_master_get_devdata(master); -+ rs->master = master; -+ -+ rs->clk = clk_get(&pdev->dev, NULL); -+ if (IS_ERR(rs->clk)) { -+ status = PTR_ERR(rs->clk); -+ dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n", -+ status); -+ goto out_put_master; -+ } -+ -+ status = clk_enable(rs->clk); -+ if (status) -+ goto out_put_clk; -+ -+ rs->sys_freq = clk_get_rate(rs->clk); -+ spi_debug("%s: sys_freq: %u\n", __func__, rs->sys_freq); -+ -+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (r == NULL) { -+ status = -ENODEV; -+ goto out_disable_clk; -+ } -+ -+ rs->base = devm_request_and_ioremap(&pdev->dev, r); -+ if (!rs->base) { -+ status = -EADDRNOTAVAIL; -+ goto out_disable_clk; -+ } -+ -+ ralink_spi_reset(rs); -+ -+ status = spi_register_master(master); -+ if (status) -+ goto out_disable_clk; -+ -+ return 0; -+ -+out_disable_clk: -+ clk_disable(rs->clk); -+out_put_clk: -+ clk_put(rs->clk); -+out_put_master: -+ spi_master_put(master); -+ return status; -+} -+ -+static int ralink_spi_remove(struct platform_device *pdev) -+{ -+ struct spi_master *master; -+ struct ralink_spi *rs; -+ -+ master = dev_get_drvdata(&pdev->dev); -+ rs = spi_master_get_devdata(master); -+ -+ clk_disable(rs->clk); -+ clk_put(rs->clk); -+ spi_unregister_master(master); -+ -+ return 0; -+} -+ -+MODULE_ALIAS("platform:" DRIVER_NAME); -+ -+static const struct of_device_id ralink_spi_match[] = { -+ { .compatible = "ralink,rt2880-spi" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, ralink_spi_match); -+ -+static struct platform_driver ralink_spi_driver = { -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = ralink_spi_match, -+ }, -+ .probe = ralink_spi_probe, -+ .remove = ralink_spi_remove, -+}; -+ -+module_platform_driver(ralink_spi_driver); -+ -+MODULE_DESCRIPTION("Ralink SPI driver"); -+MODULE_AUTHOR("Sergiy "); -+MODULE_AUTHOR("Gabor Juhos "); -+MODULE_LICENSE("GPL"); diff --git a/target/linux/ramips/patches-3.8/0132-serial-of-allow-au1x00-and-rt288x-to-load-from-OF.patch b/target/linux/ramips/patches-3.8/0132-serial-of-allow-au1x00-and-rt288x-to-load-from-OF.patch deleted file mode 100644 index bff0799ed2..0000000000 --- a/target/linux/ramips/patches-3.8/0132-serial-of-allow-au1x00-and-rt288x-to-load-from-OF.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 15bcdbd78abacbe0986a1904d2e2b5dcfe780b5b Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Tue, 22 Jan 2013 16:01:07 +0100 -Subject: [PATCH 132/137] serial: of: allow au1x00 and rt288x to load from OF - -In order to make serial_8250 loadable via OF on Au1x00 and Ralink WiSoC we need -to default the iotype to UPIO_AU. - -Signed-off-by: John Crispin ---- - drivers/tty/serial/of_serial.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - ---- a/drivers/tty/serial/of_serial.c -+++ b/drivers/tty/serial/of_serial.c -@@ -97,7 +97,10 @@ static int of_platform_serial_setup(stru - port->regshift = prop; - - port->irq = irq_of_parse_and_map(np, 0); -- port->iotype = UPIO_MEM; -+ if (of_device_is_compatible(np, "ralink,rt2880-uart")) -+ port->iotype = UPIO_AU; -+ else -+ port->iotype = UPIO_MEM; - if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { - switch (prop) { - case 1: diff --git a/target/linux/ramips/patches-3.8/0133-serial-ralink-adds-mt7620-serial.patch b/target/linux/ramips/patches-3.8/0133-serial-ralink-adds-mt7620-serial.patch deleted file mode 100644 index 1fce223ddb..0000000000 --- a/target/linux/ramips/patches-3.8/0133-serial-ralink-adds-mt7620-serial.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 6471ee7bbf3f8b70267ba1dc93f067e18803c246 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Fri, 15 Mar 2013 18:16:01 +0100 -Subject: [PATCH 133/137] serial: ralink: adds mt7620 serial - -Add the config symbol for Mediatek7620 SoC to SERIAL_8250_RT288X - -Signed-off-by: John Crispin ---- - drivers/tty/serial/8250/Kconfig | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/tty/serial/8250/Kconfig -+++ b/drivers/tty/serial/8250/Kconfig -@@ -280,7 +280,7 @@ config SERIAL_8250_EM - - config SERIAL_8250_RT288X - bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support" -- depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883) -+ depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620) - help - If you have a Ralink RT288x/RT305x SoC based board and want to use the - serial port, say Y to this option. The driver can handle up to 2 serial diff --git a/target/linux/ramips/patches-3.8/0134-PCI-MIPS-adds-rt2880-pci-support.patch b/target/linux/ramips/patches-3.8/0134-PCI-MIPS-adds-rt2880-pci-support.patch deleted file mode 100644 index 70758f1422..0000000000 --- a/target/linux/ramips/patches-3.8/0134-PCI-MIPS-adds-rt2880-pci-support.patch +++ /dev/null @@ -1,319 +0,0 @@ -From 55e9ae6a23cb799b7c1d402e1cfda11a6bd1e86e Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 21 Mar 2013 18:27:29 +0100 -Subject: [PATCH 134/137] PCI: MIPS: adds rt2880 pci support - -Add support for the pci found on the rt2880 SoC. - -Signed-off-by: John Crispin ---- - arch/mips/pci/Makefile | 1 + - arch/mips/pci/pci-rt2880.c | 281 ++++++++++++++++++++++++++++++++++++++++++++ - arch/mips/ralink/Kconfig | 1 + - 3 files changed, 283 insertions(+) - create mode 100644 arch/mips/pci/pci-rt2880.c - ---- a/arch/mips/pci/Makefile -+++ b/arch/mips/pci/Makefile -@@ -42,6 +42,7 @@ obj-$(CONFIG_SIBYTE_BCM1x80) += pci-bcm1 - obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o - obj-$(CONFIG_LANTIQ) += fixup-lantiq.o - obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o -+obj-$(CONFIG_SOC_RT2880) += pci-rt2880.o - obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o - obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o - obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o ---- /dev/null -+++ b/arch/mips/pci/pci-rt2880.c -@@ -0,0 +1,281 @@ -+/* -+ * Ralink RT288x SoC PCI register definitions -+ * -+ * Copyright (C) 2009 John Crispin -+ * Copyright (C) 2009 Gabor Juhos -+ * -+ * Parts of this file are based on Ralink's 2.6.21 BSP -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+ -+#define RT2880_PCI_BASE 0x00440000 -+#define RT288X_CPU_IRQ_PCI 4 -+ -+#define RT2880_PCI_MEM_BASE 0x20000000 -+#define RT2880_PCI_MEM_SIZE 0x10000000 -+#define RT2880_PCI_IO_BASE 0x00460000 -+#define RT2880_PCI_IO_SIZE 0x00010000 -+ -+#define RT2880_PCI_REG_PCICFG_ADDR 0x00 -+#define RT2880_PCI_REG_PCIMSK_ADDR 0x0c -+#define RT2880_PCI_REG_BAR0SETUP_ADDR 0x10 -+#define RT2880_PCI_REG_IMBASEBAR0_ADDR 0x18 -+#define RT2880_PCI_REG_CONFIG_ADDR 0x20 -+#define RT2880_PCI_REG_CONFIG_DATA 0x24 -+#define RT2880_PCI_REG_MEMBASE 0x28 -+#define RT2880_PCI_REG_IOBASE 0x2c -+#define RT2880_PCI_REG_ID 0x30 -+#define RT2880_PCI_REG_CLASS 0x34 -+#define RT2880_PCI_REG_SUBID 0x38 -+#define RT2880_PCI_REG_ARBCTL 0x80 -+ -+static void __iomem *rt2880_pci_base; -+static DEFINE_SPINLOCK(rt2880_pci_lock); -+ -+static u32 rt2880_pci_reg_read(u32 reg) -+{ -+ return readl(rt2880_pci_base + reg); -+} -+ -+static void rt2880_pci_reg_write(u32 val, u32 reg) -+{ -+ writel(val, rt2880_pci_base + reg); -+} -+ -+static inline u32 rt2880_pci_get_cfgaddr(unsigned int bus, unsigned int slot, -+ unsigned int func, unsigned int where) -+{ -+ return ((bus << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | -+ 0x80000000); -+} -+ -+static int rt2880_pci_config_read(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 *val) -+{ -+ unsigned long flags; -+ u32 address; -+ u32 data; -+ -+ address = rt2880_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), -+ PCI_FUNC(devfn), where); -+ -+ spin_lock_irqsave(&rt2880_pci_lock, flags); -+ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); -+ data = rt2880_pci_reg_read(RT2880_PCI_REG_CONFIG_DATA); -+ spin_unlock_irqrestore(&rt2880_pci_lock, flags); -+ -+ switch (size) { -+ case 1: -+ *val = (data >> ((where & 3) << 3)) & 0xff; -+ break; -+ case 2: -+ *val = (data >> ((where & 3) << 3)) & 0xffff; -+ break; -+ case 4: -+ *val = data; -+ break; -+ } -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+ -+static int rt2880_pci_config_write(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 val) -+{ -+ unsigned long flags; -+ u32 address; -+ u32 data; -+ -+ address = rt2880_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), -+ PCI_FUNC(devfn), where); -+ -+ spin_lock_irqsave(&rt2880_pci_lock, flags); -+ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); -+ data = rt2880_pci_reg_read(RT2880_PCI_REG_CONFIG_DATA); -+ -+ switch (size) { -+ case 1: -+ data = (data & ~(0xff << ((where & 3) << 3))) | -+ (val << ((where & 3) << 3)); -+ break; -+ case 2: -+ data = (data & ~(0xffff << ((where & 3) << 3))) | -+ (val << ((where & 3) << 3)); -+ break; -+ case 4: -+ data = val; -+ break; -+ } -+ -+ rt2880_pci_reg_write(data, RT2880_PCI_REG_CONFIG_DATA); -+ spin_unlock_irqrestore(&rt2880_pci_lock, flags); -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+ -+static struct pci_ops rt2880_pci_ops = { -+ .read = rt2880_pci_config_read, -+ .write = rt2880_pci_config_write, -+}; -+ -+static struct resource rt2880_pci_mem_resource = { -+ .name = "PCI MEM space", -+ .start = RT2880_PCI_MEM_BASE, -+ .end = RT2880_PCI_MEM_BASE + RT2880_PCI_MEM_SIZE - 1, -+ .flags = IORESOURCE_MEM, -+}; -+ -+static struct resource rt2880_pci_io_resource = { -+ .name = "PCI IO space", -+ .start = RT2880_PCI_IO_BASE, -+ .end = RT2880_PCI_IO_BASE + RT2880_PCI_IO_SIZE - 1, -+ .flags = IORESOURCE_IO, -+}; -+ -+static struct pci_controller rt2880_pci_controller = { -+ .pci_ops = &rt2880_pci_ops, -+ .mem_resource = &rt2880_pci_mem_resource, -+ .io_resource = &rt2880_pci_io_resource, -+}; -+ -+static inline u32 rt2880_pci_read_u32(unsigned long reg) -+{ -+ unsigned long flags; -+ u32 address; -+ u32 ret; -+ -+ address = rt2880_pci_get_cfgaddr(0, 0, 0, reg); -+ -+ spin_lock_irqsave(&rt2880_pci_lock, flags); -+ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); -+ ret = rt2880_pci_reg_read(RT2880_PCI_REG_CONFIG_DATA); -+ spin_unlock_irqrestore(&rt2880_pci_lock, flags); -+ -+ return ret; -+} -+ -+static inline void rt2880_pci_write_u32(unsigned long reg, u32 val) -+{ -+ unsigned long flags; -+ u32 address; -+ -+ address = rt2880_pci_get_cfgaddr(0, 0, 0, reg); -+ -+ spin_lock_irqsave(&rt2880_pci_lock, flags); -+ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); -+ rt2880_pci_reg_write(val, RT2880_PCI_REG_CONFIG_DATA); -+ spin_unlock_irqrestore(&rt2880_pci_lock, flags); -+} -+ -+int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -+{ -+ u16 cmd; -+ int irq = -1; -+ -+ if (dev->bus->number != 0) -+ return irq; -+ -+ switch (PCI_SLOT(dev->devfn)) { -+ case 0x00: -+ rt2880_pci_write_u32(PCI_BASE_ADDRESS_0, 0x08000000); -+ (void) rt2880_pci_read_u32(PCI_BASE_ADDRESS_0); -+ break; -+ case 0x11: -+ irq = RT288X_CPU_IRQ_PCI; -+ break; -+ default: -+ printk("%s:%s[%d] trying to alloc unknown pci irq\n", -+ __FILE__, __func__, __LINE__); -+ BUG(); -+ break; -+ } -+ -+ pci_write_config_byte((struct pci_dev*)dev, PCI_CACHE_LINE_SIZE, 0x14); -+ pci_write_config_byte((struct pci_dev*)dev, PCI_LATENCY_TIMER, 0xFF); -+ pci_read_config_word((struct pci_dev*)dev, PCI_COMMAND, &cmd); -+ cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | -+ PCI_COMMAND_INVALIDATE | PCI_COMMAND_FAST_BACK | -+ PCI_COMMAND_SERR | PCI_COMMAND_WAIT | PCI_COMMAND_PARITY; -+ pci_write_config_word((struct pci_dev*)dev, PCI_COMMAND, cmd); -+ pci_write_config_byte((struct pci_dev*)dev, PCI_INTERRUPT_LINE, -+ dev->irq); -+ return irq; -+} -+ -+static int rt288x_pci_probe(struct platform_device *pdev) -+{ -+ void __iomem *io_map_base; -+ int i; -+ -+ rt2880_pci_base = ioremap_nocache(RT2880_PCI_BASE, PAGE_SIZE); -+ -+ io_map_base = ioremap(RT2880_PCI_IO_BASE, RT2880_PCI_IO_SIZE); -+ rt2880_pci_controller.io_map_base = (unsigned long) io_map_base; -+ set_io_port_base((unsigned long) io_map_base); -+ -+ ioport_resource.start = RT2880_PCI_IO_BASE; -+ ioport_resource.end = RT2880_PCI_IO_BASE + RT2880_PCI_IO_SIZE - 1; -+ -+ rt2880_pci_reg_write(0, RT2880_PCI_REG_PCICFG_ADDR); -+ for(i = 0; i < 0xfffff; i++) {} -+ -+ rt2880_pci_reg_write(0x79, RT2880_PCI_REG_ARBCTL); -+ rt2880_pci_reg_write(0x07FF0001, RT2880_PCI_REG_BAR0SETUP_ADDR); -+ rt2880_pci_reg_write(RT2880_PCI_MEM_BASE, RT2880_PCI_REG_MEMBASE); -+ rt2880_pci_reg_write(RT2880_PCI_IO_BASE, RT2880_PCI_REG_IOBASE); -+ rt2880_pci_reg_write(0x08000000, RT2880_PCI_REG_IMBASEBAR0_ADDR); -+ rt2880_pci_reg_write(0x08021814, RT2880_PCI_REG_ID); -+ rt2880_pci_reg_write(0x00800001, RT2880_PCI_REG_CLASS); -+ rt2880_pci_reg_write(0x28801814, RT2880_PCI_REG_SUBID); -+ rt2880_pci_reg_write(0x000c0000, RT2880_PCI_REG_PCIMSK_ADDR); -+ -+ rt2880_pci_write_u32(PCI_BASE_ADDRESS_0, 0x08000000); -+ (void) rt2880_pci_read_u32(PCI_BASE_ADDRESS_0); -+ -+ register_pci_controller(&rt2880_pci_controller); -+ return 0; -+} -+ -+int pcibios_plat_dev_init(struct pci_dev *dev) -+{ -+ return 0; -+} -+ -+static const struct of_device_id rt288x_pci_match[] = { -+ { .compatible = "ralink,rt288x-pci" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, rt288x_pci_match); -+ -+static struct platform_driver rt288x_pci_driver = { -+ .probe = rt288x_pci_probe, -+ .driver = { -+ .name = "rt288x-pci", -+ .owner = THIS_MODULE, -+ .of_match_table = rt288x_pci_match, -+ }, -+}; -+ -+int __init pcibios_init(void) -+{ -+ int ret = platform_driver_register(&rt288x_pci_driver); -+ if (ret) -+ pr_info("rt288x-pci: Error registering platform driver!"); -+ return ret; -+} -+ -+arch_initcall(pcibios_init); ---- a/arch/mips/ralink/Kconfig -+++ b/arch/mips/ralink/Kconfig -@@ -8,6 +8,7 @@ choice - - config SOC_RT288X - bool "RT288x" -+ select HW_HAS_PCI - - config SOC_RT305X - bool "RT305x" diff --git a/target/linux/ramips/patches-3.8/0135-PCI-MIPS-adds-rt3883-pci-support.patch b/target/linux/ramips/patches-3.8/0135-PCI-MIPS-adds-rt3883-pci-support.patch deleted file mode 100644 index 9c4ca82404..0000000000 --- a/target/linux/ramips/patches-3.8/0135-PCI-MIPS-adds-rt3883-pci-support.patch +++ /dev/null @@ -1,678 +0,0 @@ -From 2a5dccdb00d85a6ad6111d7a2b13f9f4fae35838 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Thu, 21 Mar 2013 17:34:08 +0100 -Subject: [PATCH 135/137] PCI: MIPS: adds rt3883 pci support - -Add support for the pcie found on the rt3883 SoC. - -Signed-off-by: John Crispin ---- - arch/mips/pci/Makefile | 1 + - arch/mips/pci/pci-rt3883.c | 640 ++++++++++++++++++++++++++++++++++++++++++++ - arch/mips/ralink/Kconfig | 1 + - 3 files changed, 642 insertions(+) - create mode 100644 arch/mips/pci/pci-rt3883.c - ---- a/arch/mips/pci/Makefile -+++ b/arch/mips/pci/Makefile -@@ -43,6 +43,7 @@ obj-$(CONFIG_SNI_RM) += fixup-sni.o ops - obj-$(CONFIG_LANTIQ) += fixup-lantiq.o - obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o - obj-$(CONFIG_SOC_RT2880) += pci-rt2880.o -+obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o - obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o - obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o - obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o ---- /dev/null -+++ b/arch/mips/pci/pci-rt3883.c -@@ -0,0 +1,640 @@ -+/* -+ * Ralink RT3662/RT3883 SoC PCI support -+ * -+ * Copyright (C) 2011-2013 Gabor Juhos -+ * -+ * Parts of this file are based on Ralink's 2.6.21 BSP -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+#define RT3883_MEMORY_BASE 0x00000000 -+#define RT3883_MEMORY_SIZE 0x02000000 -+ -+#define RT3883_PCI_REG_PCICFG 0x00 -+#define RT3883_PCICFG_P2P_BR_DEVNUM_M 0xf -+#define RT3883_PCICFG_P2P_BR_DEVNUM_S 16 -+#define RT3883_PCICFG_PCIRST BIT(1) -+#define RT3883_PCI_REG_PCIRAW 0x04 -+#define RT3883_PCI_REG_PCIINT 0x08 -+#define RT3883_PCI_REG_PCIENA 0x0c -+ -+#define RT3883_PCI_REG_CFGADDR 0x20 -+#define RT3883_PCI_REG_CFGDATA 0x24 -+#define RT3883_PCI_REG_MEMBASE 0x28 -+#define RT3883_PCI_REG_IOBASE 0x2c -+#define RT3883_PCI_REG_ARBCTL 0x80 -+ -+#define RT3883_PCI_REG_BASE(_x) (0x1000 + (_x) * 0x1000) -+#define RT3883_PCI_REG_BAR0SETUP(_x) (RT3883_PCI_REG_BASE((_x)) + 0x10) -+#define RT3883_PCI_REG_IMBASEBAR0(_x) (RT3883_PCI_REG_BASE((_x)) + 0x18) -+#define RT3883_PCI_REG_ID(_x) (RT3883_PCI_REG_BASE((_x)) + 0x30) -+#define RT3883_PCI_REG_CLASS(_x) (RT3883_PCI_REG_BASE((_x)) + 0x34) -+#define RT3883_PCI_REG_SUBID(_x) (RT3883_PCI_REG_BASE((_x)) + 0x38) -+#define RT3883_PCI_REG_STATUS(_x) (RT3883_PCI_REG_BASE((_x)) + 0x50) -+ -+#define RT3883_PCI_MODE_NONE 0 -+#define RT3883_PCI_MODE_PCI BIT(0) -+#define RT3883_PCI_MODE_PCIE BIT(1) -+#define RT3883_PCI_MODE_BOTH (RT3883_PCI_MODE_PCI | RT3883_PCI_MODE_PCIE) -+ -+#define RT3883_PCI_IRQ_COUNT 32 -+ -+#define RT3883_P2P_BR_DEVNUM 1 -+ -+struct rt3883_pci_controller { -+ void __iomem *base; -+ spinlock_t lock; -+ -+ struct irq_domain *irq_domain; -+ -+ struct pci_controller pci_controller; -+ struct resource io_res; -+ struct resource mem_res; -+ -+ bool pcie_ready; -+ unsigned char p2p_devnum; -+}; -+ -+static inline struct rt3883_pci_controller * -+pci_bus_to_rt3883_controller(struct pci_bus *bus) -+{ -+ struct pci_controller *hose; -+ -+ hose = (struct pci_controller *) bus->sysdata; -+ return container_of(hose, struct rt3883_pci_controller, pci_controller); -+} -+ -+static inline u32 rt3883_pci_r32(struct rt3883_pci_controller *rpc, -+ unsigned reg) -+{ -+ return ioread32(rpc->base + reg); -+} -+ -+static inline void rt3883_pci_w32(struct rt3883_pci_controller *rpc, -+ u32 val, unsigned reg) -+{ -+ iowrite32(val, rpc->base + reg); -+} -+ -+static inline u32 rt3883_pci_get_cfgaddr(unsigned int bus, unsigned int slot, -+ unsigned int func, unsigned int where) -+{ -+ return ((bus << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | -+ 0x80000000); -+} -+ -+static u32 rt3883_pci_read_cfg32(struct rt3883_pci_controller *rpc, -+ unsigned bus, unsigned slot, -+ unsigned func, unsigned reg) -+{ -+ unsigned long flags; -+ u32 address; -+ u32 ret; -+ -+ address = rt3883_pci_get_cfgaddr(bus, slot, func, reg); -+ -+ spin_lock_irqsave(&rpc->lock, flags); -+ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); -+ ret = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA); -+ spin_unlock_irqrestore(&rpc->lock, flags); -+ -+ return ret; -+} -+ -+static void rt3883_pci_write_cfg32(struct rt3883_pci_controller *rpc, -+ unsigned bus, unsigned slot, -+ unsigned func, unsigned reg, u32 val) -+{ -+ unsigned long flags; -+ u32 address; -+ -+ address = rt3883_pci_get_cfgaddr(bus, slot, func, reg); -+ -+ spin_lock_irqsave(&rpc->lock, flags); -+ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); -+ rt3883_pci_w32(rpc, val, RT3883_PCI_REG_CFGDATA); -+ spin_unlock_irqrestore(&rpc->lock, flags); -+} -+ -+static void rt3883_pci_irq_handler(unsigned int irq, struct irq_desc *desc) -+{ -+ struct rt3883_pci_controller *rpc; -+ u32 pending; -+ -+ rpc = irq_get_handler_data(irq); -+ -+ pending = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIINT) & -+ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); -+ -+ if (!pending) { -+ spurious_interrupt(); -+ return; -+ } -+ -+ while (pending) { -+ unsigned bit = __ffs(pending); -+ -+ irq = irq_find_mapping(rpc->irq_domain, bit); -+ generic_handle_irq(irq); -+ -+ pending &= ~BIT(bit); -+ } -+} -+ -+static void rt3883_pci_irq_unmask(struct irq_data *d) -+{ -+ struct rt3883_pci_controller *rpc; -+ u32 t; -+ -+ rpc = irq_data_get_irq_chip_data(d); -+ -+ t = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); -+ rt3883_pci_w32(rpc, t | BIT(d->hwirq), RT3883_PCI_REG_PCIENA); -+ /* flush write */ -+ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); -+} -+ -+static void rt3883_pci_irq_mask(struct irq_data *d) -+{ -+ struct rt3883_pci_controller *rpc; -+ u32 t; -+ -+ rpc = irq_data_get_irq_chip_data(d); -+ -+ t = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); -+ rt3883_pci_w32(rpc, t & ~BIT(d->hwirq), RT3883_PCI_REG_PCIENA); -+ /* flush write */ -+ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); -+} -+ -+static struct irq_chip rt3883_pci_irq_chip = { -+ .name = "RT3883 PCI", -+ .irq_mask = rt3883_pci_irq_mask, -+ .irq_unmask = rt3883_pci_irq_unmask, -+ .irq_mask_ack = rt3883_pci_irq_mask, -+}; -+ -+static int rt3883_pci_irq_map(struct irq_domain *d, unsigned int irq, -+ irq_hw_number_t hw) -+{ -+ irq_set_chip_and_handler(irq, &rt3883_pci_irq_chip, handle_level_irq); -+ irq_set_chip_data(irq, d->host_data); -+ -+ return 0; -+} -+ -+static const struct irq_domain_ops rt3883_pci_irq_domain_ops = { -+ .map = rt3883_pci_irq_map, -+ .xlate = irq_domain_xlate_onecell, -+}; -+ -+static int rt3883_pci_irq_init(struct device *dev, -+ struct rt3883_pci_controller *rpc) -+{ -+ struct device_node *np = dev->of_node; -+ struct device_node *intc_np; -+ int irq; -+ int err; -+ -+ intc_np = of_get_child_by_name(np, "interrupt-controller"); -+ if (!intc_np) { -+ dev_err(dev, "no %s child node found", "interrupt-controller"); -+ return -ENODEV; -+ } -+ -+ irq = irq_of_parse_and_map(intc_np, 0); -+ if (irq == 0) { -+ dev_err(dev, "%s has no IRQ", of_node_full_name(intc_np)); -+ err = -EINVAL; -+ goto err_put_intc; -+ } -+ -+ /* disable all interrupts */ -+ rt3883_pci_w32(rpc, 0, RT3883_PCI_REG_PCIENA); -+ -+ rpc->irq_domain = -+ irq_domain_add_linear(intc_np, RT3883_PCI_IRQ_COUNT, -+ &rt3883_pci_irq_domain_ops, -+ rpc); -+ if (!rpc->irq_domain) { -+ dev_err(dev, "unable to add IRQ domain\n"); -+ err = -ENODEV; -+ goto err_put_intc; -+ } -+ -+ irq_set_handler_data(irq, rpc); -+ irq_set_chained_handler(irq, rt3883_pci_irq_handler); -+ -+ return 0; -+ -+err_put_intc: -+ of_node_put(intc_np); -+ return err; -+} -+ -+static int rt3883_pci_config_read(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 *val) -+{ -+ struct rt3883_pci_controller *rpc; -+ unsigned long flags; -+ u32 address; -+ u32 data; -+ -+ rpc = pci_bus_to_rt3883_controller(bus); -+ -+ if (!rpc->pcie_ready && bus->number == 1) -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ -+ address = rt3883_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), -+ PCI_FUNC(devfn), where); -+ -+ spin_lock_irqsave(&rpc->lock, flags); -+ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); -+ data = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA); -+ spin_unlock_irqrestore(&rpc->lock, flags); -+ -+ switch (size) { -+ case 1: -+ *val = (data >> ((where & 3) << 3)) & 0xff; -+ break; -+ case 2: -+ *val = (data >> ((where & 3) << 3)) & 0xffff; -+ break; -+ case 4: -+ *val = data; -+ break; -+ } -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+ -+static int rt3883_pci_config_write(struct pci_bus *bus, unsigned int devfn, -+ int where, int size, u32 val) -+{ -+ struct rt3883_pci_controller *rpc; -+ unsigned long flags; -+ u32 address; -+ u32 data; -+ -+ rpc = pci_bus_to_rt3883_controller(bus); -+ -+ if (!rpc->pcie_ready && bus->number == 1) -+ return PCIBIOS_DEVICE_NOT_FOUND; -+ -+ address = rt3883_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), -+ PCI_FUNC(devfn), where); -+ -+ spin_lock_irqsave(&rpc->lock, flags); -+ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); -+ data = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA); -+ -+ switch (size) { -+ case 1: -+ data = (data & ~(0xff << ((where & 3) << 3))) | -+ (val << ((where & 3) << 3)); -+ break; -+ case 2: -+ data = (data & ~(0xffff << ((where & 3) << 3))) | -+ (val << ((where & 3) << 3)); -+ break; -+ case 4: -+ data = val; -+ break; -+ } -+ -+ rt3883_pci_w32(rpc, data, RT3883_PCI_REG_CFGDATA); -+ spin_unlock_irqrestore(&rpc->lock, flags); -+ -+ return PCIBIOS_SUCCESSFUL; -+} -+ -+static struct pci_ops rt3883_pci_ops = { -+ .read = rt3883_pci_config_read, -+ .write = rt3883_pci_config_write, -+}; -+ -+static void rt3883_pci_preinit(struct rt3883_pci_controller *rpc, unsigned mode) -+{ -+ u32 syscfg1; -+ u32 rstctrl; -+ u32 clkcfg1; -+ u32 t; -+ -+ rstctrl = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL); -+ syscfg1 = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG1); -+ clkcfg1 = rt_sysc_r32(RT3883_SYSC_REG_CLKCFG1); -+ -+ if (mode & RT3883_PCI_MODE_PCIE) { -+ rstctrl |= RT3883_RSTCTRL_PCIE; -+ rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL); -+ -+ /* setup PCI PAD drive mode */ -+ syscfg1 &= ~(0x30); -+ syscfg1 |= (2 << 4); -+ rt_sysc_w32(syscfg1, RT3883_SYSC_REG_SYSCFG1); -+ -+ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0); -+ t &= ~BIT(31); -+ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0); -+ -+ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN1); -+ t &= 0x80ffffff; -+ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN1); -+ -+ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN1); -+ t |= 0xa << 24; -+ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN1); -+ -+ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0); -+ t |= BIT(31); -+ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0); -+ -+ msleep(50); -+ -+ rstctrl &= ~RT3883_RSTCTRL_PCIE; -+ rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL); -+ } -+ -+ syscfg1 |= (RT3883_SYSCFG1_PCIE_RC_MODE | RT3883_SYSCFG1_PCI_HOST_MODE); -+ -+ clkcfg1 &= ~(RT3883_CLKCFG1_PCI_CLK_EN | RT3883_CLKCFG1_PCIE_CLK_EN); -+ -+ if (mode & RT3883_PCI_MODE_PCI) { -+ clkcfg1 |= RT3883_CLKCFG1_PCI_CLK_EN; -+ rstctrl &= ~RT3883_RSTCTRL_PCI; -+ } -+ -+ if (mode & RT3883_PCI_MODE_PCIE) { -+ clkcfg1 |= RT3883_CLKCFG1_PCIE_CLK_EN; -+ rstctrl &= ~RT3883_RSTCTRL_PCIE; -+ } -+ -+ rt_sysc_w32(syscfg1, RT3883_SYSC_REG_SYSCFG1); -+ rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL); -+ rt_sysc_w32(clkcfg1, RT3883_SYSC_REG_CLKCFG1); -+ -+ msleep(500); -+ -+ /* -+ * setup the device number of the P2P bridge -+ * and de-assert the reset line -+ */ -+ t = (RT3883_P2P_BR_DEVNUM << RT3883_PCICFG_P2P_BR_DEVNUM_S); -+ rt3883_pci_w32(rpc, t, RT3883_PCI_REG_PCICFG); -+ -+ /* flush write */ -+ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCICFG); -+ msleep(500); -+ -+ if (mode & RT3883_PCI_MODE_PCIE) { -+ msleep(500); -+ -+ t = rt3883_pci_r32(rpc, RT3883_PCI_REG_STATUS(1)); -+ -+ rpc->pcie_ready = t & BIT(0); -+ -+ if (!rpc->pcie_ready) { -+ /* reset the PCIe block */ -+ t = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL); -+ t |= RT3883_RSTCTRL_PCIE; -+ rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL); -+ t &= ~RT3883_RSTCTRL_PCIE; -+ rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL); -+ -+ /* turn off PCIe clock */ -+ t = rt_sysc_r32(RT3883_SYSC_REG_CLKCFG1); -+ t &= ~RT3883_CLKCFG1_PCIE_CLK_EN; -+ rt_sysc_w32(t, RT3883_SYSC_REG_CLKCFG1); -+ -+ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0); -+ t &= ~0xf000c080; -+ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0); -+ } -+ } -+ -+ /* enable PCI arbiter */ -+ rt3883_pci_w32(rpc, 0x79, RT3883_PCI_REG_ARBCTL); -+} -+ -+static inline void -+rt3883_dump_pci_config(struct rt3883_pci_controller *rpc, -+ int bus, int slot) -+{ -+ int i; -+ -+ for (i = 0; i < 16; i++) { -+ u32 val; -+ -+ val = rt3883_pci_read_cfg32(rpc, bus, slot, 0, i << 2); -+ pr_info("pci %02x:%02x.0 0x%02x = %08x\n", -+ bus, slot, i << 2, val); -+ } -+} -+ -+static int rt3883_pci_probe(struct platform_device *pdev) -+{ -+ struct rt3883_pci_controller *rpc; -+ struct device *dev = &pdev->dev; -+ struct device_node *np = dev->of_node; -+ struct resource *res; -+ struct device_node *child; -+ u32 val; -+ int err; -+ int mode; -+ -+ rpc = devm_kzalloc(dev, sizeof(*rpc), GFP_KERNEL); -+ if (!rpc) -+ return -ENOMEM; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) -+ return -EINVAL; -+ -+ rpc->base = devm_ioremap_resource(dev, res); -+ if (IS_ERR(rpc->base)) -+ return PTR_ERR(rpc->base); -+ -+ rpc->pci_controller.of_node = of_get_child_by_name(np, "host-bridge"); -+ if (!rpc->pci_controller.of_node) { -+ dev_err(dev, "no %s child node found", "host-bridge"); -+ return -ENODEV; -+ } -+ -+ mode = RT3883_PCI_MODE_NONE; -+ for_each_child_of_node(rpc->pci_controller.of_node, child) { -+ u32 slot; -+ -+ if (!of_device_is_available(child)) -+ continue; -+ -+ if (of_property_read_u32(child, "ralink,pci-slot", -+ &slot)) { -+ dev_err(dev, "no '%s' property found for %s\n", -+ "ralink,pci-slot", -+ of_node_full_name(child)); -+ continue; -+ } -+ -+ switch (slot) { -+ case 1: -+ mode |= RT3883_PCI_MODE_PCIE; -+ break; -+ -+ case 17: -+ case 18: -+ mode |= RT3883_PCI_MODE_PCI; -+ break; -+ } -+ } -+ -+ if (mode == RT3883_PCI_MODE_NONE) { -+ dev_err(dev, "unable to determine PCI mode\n"); -+ err = -EINVAL; -+ goto err_put_hb_node; -+ } -+ -+ dev_info(dev, "mode:%s%s\n", -+ (mode & RT3883_PCI_MODE_PCI) ? " PCI" : "", -+ (mode & RT3883_PCI_MODE_PCIE) ? " PCIe" : ""); -+ -+ rt3883_pci_preinit(rpc, mode); -+ -+ rpc->pci_controller.pci_ops = &rt3883_pci_ops; -+ rpc->pci_controller.io_resource = &rpc->io_res; -+ rpc->pci_controller.mem_resource = &rpc->mem_res; -+ -+ /* Load PCI I/O and memory resources from DT */ -+ pci_load_of_ranges(&rpc->pci_controller, -+ rpc->pci_controller.of_node); -+ -+ rt3883_pci_w32(rpc, rpc->mem_res.start, RT3883_PCI_REG_MEMBASE); -+ rt3883_pci_w32(rpc, rpc->io_res.start, RT3883_PCI_REG_IOBASE); -+ -+ ioport_resource.start = rpc->io_res.start; -+ ioport_resource.end = rpc->io_res.end; -+ -+ /* PCI */ -+ rt3883_pci_w32(rpc, 0x03ff0000, RT3883_PCI_REG_BAR0SETUP(0)); -+ rt3883_pci_w32(rpc, RT3883_MEMORY_BASE, RT3883_PCI_REG_IMBASEBAR0(0)); -+ rt3883_pci_w32(rpc, 0x08021814, RT3883_PCI_REG_ID(0)); -+ rt3883_pci_w32(rpc, 0x00800001, RT3883_PCI_REG_CLASS(0)); -+ rt3883_pci_w32(rpc, 0x28801814, RT3883_PCI_REG_SUBID(0)); -+ -+ /* PCIe */ -+ rt3883_pci_w32(rpc, 0x03ff0000, RT3883_PCI_REG_BAR0SETUP(1)); -+ rt3883_pci_w32(rpc, RT3883_MEMORY_BASE, RT3883_PCI_REG_IMBASEBAR0(1)); -+ rt3883_pci_w32(rpc, 0x08021814, RT3883_PCI_REG_ID(1)); -+ rt3883_pci_w32(rpc, 0x06040001, RT3883_PCI_REG_CLASS(1)); -+ rt3883_pci_w32(rpc, 0x28801814, RT3883_PCI_REG_SUBID(1)); -+ -+ err = rt3883_pci_irq_init(dev, rpc); -+ if (err) -+ goto err_put_hb_node; -+ -+ /* PCIe */ -+ val = rt3883_pci_read_cfg32(rpc, 0, 0x01, 0, PCI_COMMAND); -+ val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; -+ rt3883_pci_write_cfg32(rpc, 0, 0x01, 0, PCI_COMMAND, val); -+ -+ /* PCI */ -+ val = rt3883_pci_read_cfg32(rpc, 0, 0x00, 0, PCI_COMMAND); -+ val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; -+ rt3883_pci_write_cfg32(rpc, 0, 0x00, 0, PCI_COMMAND, val); -+ -+ if (mode == RT3883_PCI_MODE_PCIE) { -+ rt3883_pci_w32(rpc, 0x03ff0001, RT3883_PCI_REG_BAR0SETUP(0)); -+ rt3883_pci_w32(rpc, 0x03ff0001, RT3883_PCI_REG_BAR0SETUP(1)); -+ -+ rt3883_pci_write_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0, -+ PCI_BASE_ADDRESS_0, -+ RT3883_MEMORY_BASE); -+ /* flush write */ -+ rt3883_pci_read_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0, -+ PCI_BASE_ADDRESS_0); -+ } else { -+ rt3883_pci_write_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0, -+ PCI_IO_BASE, 0x00000101); -+ } -+ -+ register_pci_controller(&rpc->pci_controller); -+ -+ return 0; -+ -+err_put_hb_node: -+ of_node_put(rpc->pci_controller.of_node); -+ return err; -+} -+ -+int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -+{ -+ struct rt3883_pci_controller *rpc; -+ struct of_irq dev_irq; -+ int err; -+ int irq; -+ -+ rpc = pci_bus_to_rt3883_controller(dev->bus); -+ err = of_irq_map_pci(dev, &dev_irq); -+ if (err) { -+ pr_err("pci %s: unable to get irq map, err=%d\n", -+ pci_name((struct pci_dev *) dev), err); -+ return 0; -+ } -+ -+ irq = irq_create_of_mapping(dev_irq.controller, -+ dev_irq.specifier, -+ dev_irq.size); -+ -+ if (irq == 0) -+ pr_crit("pci %s: no irq found for pin %u\n", -+ pci_name((struct pci_dev *) dev), pin); -+ else -+ pr_info("pci %s: using irq %d for pin %u\n", -+ pci_name((struct pci_dev *) dev), irq, pin); -+ -+ return irq; -+} -+ -+int pcibios_plat_dev_init(struct pci_dev *dev) -+{ -+ return 0; -+} -+ -+static const struct of_device_id rt3883_pci_ids[] = { -+ { .compatible = "ralink,rt3883-pci" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, rt3883_pci_ids); -+ -+static struct platform_driver rt3883_pci_driver = { -+ .probe = rt3883_pci_probe, -+ .driver = { -+ .name = "rt3883-pci", -+ .owner = THIS_MODULE, -+ .of_match_table = of_match_ptr(rt3883_pci_ids), -+ }, -+}; -+ -+static int __init rt3883_pci_init(void) -+{ -+ return platform_driver_register(&rt3883_pci_driver); -+} -+ -+postcore_initcall(rt3883_pci_init); ---- a/arch/mips/ralink/Kconfig -+++ b/arch/mips/ralink/Kconfig -@@ -20,6 +20,7 @@ choice - bool "RT3883" - select USB_ARCH_HAS_OHCI - select USB_ARCH_HAS_EHCI -+ select HW_HAS_PCI - - config SOC_MT7620 - bool "MT7620" diff --git a/target/linux/ramips/patches-3.8/0136-NET-MIPS-add-ralink-SoC-ethernet-driver.patch b/target/linux/ramips/patches-3.8/0136-NET-MIPS-add-ralink-SoC-ethernet-driver.patch deleted file mode 100644 index 9bb9b5fb2a..0000000000 --- a/target/linux/ramips/patches-3.8/0136-NET-MIPS-add-ralink-SoC-ethernet-driver.patch +++ /dev/null @@ -1,3137 +0,0 @@ -From 34fc7d26c01ba594be347aefcc31f55b36c06a72 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Mon, 22 Apr 2013 23:20:03 +0200 -Subject: [PATCH 136/137] NET: MIPS: add ralink SoC ethernet driver - -Add support for Ralink FE and ESW. - -Signed-off-by: John Crispin ---- - .../include/asm/mach-ralink/rt305x_esw_platform.h | 27 + - arch/mips/ralink/rt305x.c | 1 + - drivers/net/ethernet/Kconfig | 1 + - drivers/net/ethernet/Makefile | 1 + - drivers/net/ethernet/ramips/Kconfig | 18 + - drivers/net/ethernet/ramips/Makefile | 9 + - drivers/net/ethernet/ramips/ramips_debugfs.c | 127 ++ - drivers/net/ethernet/ramips/ramips_esw.c | 1221 +++++++++++++++++++ - drivers/net/ethernet/ramips/ramips_eth.h | 375 ++++++ - drivers/net/ethernet/ramips/ramips_main.c | 1281 ++++++++++++++++++++ - 10 files changed, 3061 insertions(+) - create mode 100644 arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h - create mode 100644 drivers/net/ethernet/ramips/Kconfig - create mode 100644 drivers/net/ethernet/ramips/Makefile - create mode 100644 drivers/net/ethernet/ramips/ramips_debugfs.c - create mode 100644 drivers/net/ethernet/ramips/ramips_esw.c - create mode 100644 drivers/net/ethernet/ramips/ramips_eth.h - create mode 100644 drivers/net/ethernet/ramips/ramips_main.c - ---- /dev/null -+++ b/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h -@@ -0,0 +1,27 @@ -+/* -+ * Ralink RT305x SoC platform device registration -+ * -+ * Copyright (C) 2010 Gabor Juhos -+ * -+ * 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 _RT305X_ESW_PLATFORM_H -+#define _RT305X_ESW_PLATFORM_H -+ -+enum { -+ RT305X_ESW_VLAN_CONFIG_NONE = 0, -+ RT305X_ESW_VLAN_CONFIG_LLLLW, -+ RT305X_ESW_VLAN_CONFIG_WLLLL, -+}; -+ -+struct rt305x_esw_platform_data -+{ -+ u8 vlan_config; -+ u32 reg_initval_fct2; -+ u32 reg_initval_fpa2; -+}; -+ -+#endif /* _RT305X_ESW_PLATFORM_H */ ---- a/arch/mips/ralink/rt305x.c -+++ b/arch/mips/ralink/rt305x.c -@@ -221,6 +221,7 @@ void __init ralink_clk_init(void) - } - - ralink_clk_add("cpu", cpu_rate); -+ ralink_clk_add("sys", sys_rate); - ralink_clk_add("10000b00.spi", sys_rate); - ralink_clk_add("10000100.timer", wdt_rate); - ralink_clk_add("10000120.watchdog", wdt_rate); ---- a/drivers/net/ethernet/Kconfig -+++ b/drivers/net/ethernet/Kconfig -@@ -136,6 +136,7 @@ source "drivers/net/ethernet/packetengin - source "drivers/net/ethernet/pasemi/Kconfig" - source "drivers/net/ethernet/qlogic/Kconfig" - source "drivers/net/ethernet/racal/Kconfig" -+source "drivers/net/ethernet/ramips/Kconfig" - source "drivers/net/ethernet/realtek/Kconfig" - source "drivers/net/ethernet/renesas/Kconfig" - source "drivers/net/ethernet/rdc/Kconfig" ---- a/drivers/net/ethernet/Makefile -+++ b/drivers/net/ethernet/Makefile -@@ -54,6 +54,7 @@ obj-$(CONFIG_NET_PACKET_ENGINE) += packe - obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/ - obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/ - obj-$(CONFIG_NET_VENDOR_RACAL) += racal/ -+obj-$(CONFIG_NET_RAMIPS) += ramips/ - obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/ - obj-$(CONFIG_SH_ETH) += renesas/ - obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ ---- /dev/null -+++ b/drivers/net/ethernet/ramips/Kconfig -@@ -0,0 +1,18 @@ -+config NET_RAMIPS -+ tristate "Ralink RT288X/RT3X5X/RT3662/RT3883 ethernet driver" -+ depends on RALINK -+ select PHYLIB if (SOC_RT288X || SOC_RT3883) -+ select SWCONFIG if SOC_RT305X -+ help -+ This driver supports the etehrnet mac inside the ralink wisocs -+ -+if NET_RAMIPS -+ -+config NET_RAMIPS_DEBUG -+ bool "Enable debug messages in the Ralink ethernet driver" -+ -+config NET_RAMIPS_DEBUG_FS -+ bool "Enable debugfs support for the Ralink ethernet driver" -+ depends on DEBUG_FS -+ -+endif ---- /dev/null -+++ b/drivers/net/ethernet/ramips/Makefile -@@ -0,0 +1,9 @@ -+# -+# Makefile for the Ramips SoCs built-in ethernet macs -+# -+ -+ramips-y += ramips_main.o -+ -+ramips-$(CONFIG_NET_RAMIPS_DEBUG_FS) += ramips_debugfs.o -+ -+obj-$(CONFIG_NET_RAMIPS) += ramips.o ---- /dev/null -+++ b/drivers/net/ethernet/ramips/ramips_debugfs.c -@@ -0,0 +1,127 @@ -+/* -+ * Ralink SoC ethernet driver debugfs code -+ * -+ * Copyright (C) 2011-2012 Gabor Juhos -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include -+#include -+#include -+ -+#include "ramips_eth.h" -+ -+static struct dentry *raeth_debugfs_root; -+ -+static int raeth_debugfs_generic_open(struct inode *inode, struct file *file) -+{ -+ file->private_data = inode->i_private; -+ return 0; -+} -+ -+void raeth_debugfs_update_int_stats(struct raeth_priv *re, u32 status) -+{ -+ re->debug.int_stats.total += !!status; -+ -+ re->debug.int_stats.rx_delayed += !!(status & RAMIPS_RX_DLY_INT); -+ re->debug.int_stats.rx_done0 += !!(status & RAMIPS_RX_DONE_INT0); -+ re->debug.int_stats.rx_coherent += !!(status & RAMIPS_RX_COHERENT); -+ -+ re->debug.int_stats.tx_delayed += !!(status & RAMIPS_TX_DLY_INT); -+ re->debug.int_stats.tx_done0 += !!(status & RAMIPS_TX_DONE_INT0); -+ re->debug.int_stats.tx_done1 += !!(status & RAMIPS_TX_DONE_INT1); -+ re->debug.int_stats.tx_done2 += !!(status & RAMIPS_TX_DONE_INT2); -+ re->debug.int_stats.tx_done3 += !!(status & RAMIPS_TX_DONE_INT3); -+ re->debug.int_stats.tx_coherent += !!(status & RAMIPS_TX_COHERENT); -+ -+ re->debug.int_stats.pse_fq_empty += !!(status & RAMIPS_PSE_FQ_EMPTY); -+ re->debug.int_stats.pse_p0_fc += !!(status & RAMIPS_PSE_P0_FC); -+ re->debug.int_stats.pse_p1_fc += !!(status & RAMIPS_PSE_P1_FC); -+ re->debug.int_stats.pse_p2_fc += !!(status & RAMIPS_PSE_P2_FC); -+ re->debug.int_stats.pse_buf_drop += !!(status & RAMIPS_PSE_BUF_DROP); -+} -+ -+static ssize_t read_file_int_stats(struct file *file, char __user *user_buf, -+ size_t count, loff_t *ppos) -+{ -+#define PR_INT_STAT(_label, _field) \ -+ len += snprintf(buf + len, sizeof(buf) - len, \ -+ "%-18s: %10lu\n", _label, re->debug.int_stats._field); -+ -+ struct raeth_priv *re = file->private_data; -+ char buf[512]; -+ unsigned int len = 0; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&re->page_lock, flags); -+ -+ PR_INT_STAT("RX Delayed", rx_delayed); -+ PR_INT_STAT("RX Done 0", rx_done0); -+ PR_INT_STAT("RX Coherent", rx_coherent); -+ -+ PR_INT_STAT("TX Delayed", tx_delayed); -+ PR_INT_STAT("TX Done 0", tx_done0); -+ PR_INT_STAT("TX Done 1", tx_done1); -+ PR_INT_STAT("TX Done 2", tx_done2); -+ PR_INT_STAT("TX Done 3", tx_done3); -+ PR_INT_STAT("TX Coherent", tx_coherent); -+ -+ PR_INT_STAT("PSE FQ empty", pse_fq_empty); -+ PR_INT_STAT("CDMA Flow control", pse_p0_fc); -+ PR_INT_STAT("GDMA1 Flow control", pse_p1_fc); -+ PR_INT_STAT("GDMA2 Flow control", pse_p2_fc); -+ PR_INT_STAT("PSE discard", pse_buf_drop); -+ -+ len += snprintf(buf + len, sizeof(buf) - len, "\n"); -+ PR_INT_STAT("Total", total); -+ -+ spin_unlock_irqrestore(&re->page_lock, flags); -+ -+ return simple_read_from_buffer(user_buf, count, ppos, buf, len); -+#undef PR_INT_STAT -+} -+ -+static const struct file_operations raeth_fops_int_stats = { -+ .open = raeth_debugfs_generic_open, -+ .read = read_file_int_stats, -+ .owner = THIS_MODULE -+}; -+ -+void raeth_debugfs_exit(struct raeth_priv *re) -+{ -+ debugfs_remove_recursive(re->debug.debugfs_dir); -+} -+ -+int raeth_debugfs_init(struct raeth_priv *re) -+{ -+ re->debug.debugfs_dir = debugfs_create_dir(re->netdev->name, -+ raeth_debugfs_root); -+ if (!re->debug.debugfs_dir) -+ return -ENOMEM; -+ -+ debugfs_create_file("int_stats", S_IRUGO, re->debug.debugfs_dir, -+ re, &raeth_fops_int_stats); -+ -+ return 0; -+} -+ -+int raeth_debugfs_root_init(void) -+{ -+ if (raeth_debugfs_root) -+ return -EBUSY; -+ -+ raeth_debugfs_root = debugfs_create_dir("raeth", NULL); -+ if (!raeth_debugfs_root) -+ return -ENOENT; -+ -+ return 0; -+} -+ -+void raeth_debugfs_root_exit(void) -+{ -+ debugfs_remove(raeth_debugfs_root); -+ raeth_debugfs_root = NULL; -+} ---- /dev/null -+++ b/drivers/net/ethernet/ramips/ramips_esw.c -@@ -0,0 +1,1221 @@ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+ -+/* -+ * HW limitations for this switch: -+ * - No large frame support (PKT_MAX_LEN at most 1536) -+ * - Can't have untagged vlan and tagged vlan on one port at the same time, -+ * though this might be possible using the undocumented PPE. -+ */ -+ -+#define RT305X_ESW_REG_ISR 0x00 -+#define RT305X_ESW_REG_IMR 0x04 -+#define RT305X_ESW_REG_FCT0 0x08 -+#define RT305X_ESW_REG_PFC1 0x14 -+#define RT305X_ESW_REG_ATS 0x24 -+#define RT305X_ESW_REG_ATS0 0x28 -+#define RT305X_ESW_REG_ATS1 0x2c -+#define RT305X_ESW_REG_ATS2 0x30 -+#define RT305X_ESW_REG_PVIDC(_n) (0x40 + 4 * (_n)) -+#define RT305X_ESW_REG_VLANI(_n) (0x50 + 4 * (_n)) -+#define RT305X_ESW_REG_VMSC(_n) (0x70 + 4 * (_n)) -+#define RT305X_ESW_REG_POA 0x80 -+#define RT305X_ESW_REG_FPA 0x84 -+#define RT305X_ESW_REG_SOCPC 0x8c -+#define RT305X_ESW_REG_POC0 0x90 -+#define RT305X_ESW_REG_POC1 0x94 -+#define RT305X_ESW_REG_POC2 0x98 -+#define RT305X_ESW_REG_SGC 0x9c -+#define RT305X_ESW_REG_STRT 0xa0 -+#define RT305X_ESW_REG_PCR0 0xc0 -+#define RT305X_ESW_REG_PCR1 0xc4 -+#define RT305X_ESW_REG_FPA2 0xc8 -+#define RT305X_ESW_REG_FCT2 0xcc -+#define RT305X_ESW_REG_SGC2 0xe4 -+#define RT305X_ESW_REG_P0LED 0xa4 -+#define RT305X_ESW_REG_P1LED 0xa8 -+#define RT305X_ESW_REG_P2LED 0xac -+#define RT305X_ESW_REG_P3LED 0xb0 -+#define RT305X_ESW_REG_P4LED 0xb4 -+#define RT305X_ESW_REG_P0PC 0xe8 -+#define RT305X_ESW_REG_P1PC 0xec -+#define RT305X_ESW_REG_P2PC 0xf0 -+#define RT305X_ESW_REG_P3PC 0xf4 -+#define RT305X_ESW_REG_P4PC 0xf8 -+#define RT305X_ESW_REG_P5PC 0xfc -+ -+#define RT305X_ESW_LED_LINK 0 -+#define RT305X_ESW_LED_100M 1 -+#define RT305X_ESW_LED_DUPLEX 2 -+#define RT305X_ESW_LED_ACTIVITY 3 -+#define RT305X_ESW_LED_COLLISION 4 -+#define RT305X_ESW_LED_LINKACT 5 -+#define RT305X_ESW_LED_DUPLCOLL 6 -+#define RT305X_ESW_LED_10MACT 7 -+#define RT305X_ESW_LED_100MACT 8 -+/* Additional led states not in datasheet: */ -+#define RT305X_ESW_LED_BLINK 10 -+#define RT305X_ESW_LED_ON 12 -+ -+#define RT305X_ESW_LINK_S 25 -+#define RT305X_ESW_DUPLEX_S 9 -+#define RT305X_ESW_SPD_S 0 -+ -+#define RT305X_ESW_PCR0_WT_NWAY_DATA_S 16 -+#define RT305X_ESW_PCR0_WT_PHY_CMD BIT(13) -+#define RT305X_ESW_PCR0_CPU_PHY_REG_S 8 -+ -+#define RT305X_ESW_PCR1_WT_DONE BIT(0) -+ -+#define RT305X_ESW_ATS_TIMEOUT (5 * HZ) -+#define RT305X_ESW_PHY_TIMEOUT (5 * HZ) -+ -+#define RT305X_ESW_PVIDC_PVID_M 0xfff -+#define RT305X_ESW_PVIDC_PVID_S 12 -+ -+#define RT305X_ESW_VLANI_VID_M 0xfff -+#define RT305X_ESW_VLANI_VID_S 12 -+ -+#define RT305X_ESW_VMSC_MSC_M 0xff -+#define RT305X_ESW_VMSC_MSC_S 8 -+ -+#define RT305X_ESW_SOCPC_DISUN2CPU_S 0 -+#define RT305X_ESW_SOCPC_DISMC2CPU_S 8 -+#define RT305X_ESW_SOCPC_DISBC2CPU_S 16 -+#define RT305X_ESW_SOCPC_CRC_PADDING BIT(25) -+ -+#define RT305X_ESW_POC0_EN_BP_S 0 -+#define RT305X_ESW_POC0_EN_FC_S 8 -+#define RT305X_ESW_POC0_DIS_RMC2CPU_S 16 -+#define RT305X_ESW_POC0_DIS_PORT_M 0x7f -+#define RT305X_ESW_POC0_DIS_PORT_S 23 -+ -+#define RT305X_ESW_POC2_UNTAG_EN_M 0xff -+#define RT305X_ESW_POC2_UNTAG_EN_S 0 -+#define RT305X_ESW_POC2_ENAGING_S 8 -+#define RT305X_ESW_POC2_DIS_UC_PAUSE_S 16 -+ -+#define RT305X_ESW_SGC2_DOUBLE_TAG_M 0x7f -+#define RT305X_ESW_SGC2_DOUBLE_TAG_S 0 -+#define RT305X_ESW_SGC2_LAN_PMAP_M 0x3f -+#define RT305X_ESW_SGC2_LAN_PMAP_S 24 -+ -+#define RT305X_ESW_PFC1_EN_VLAN_M 0xff -+#define RT305X_ESW_PFC1_EN_VLAN_S 16 -+#define RT305X_ESW_PFC1_EN_TOS_S 24 -+ -+#define RT305X_ESW_VLAN_NONE 0xfff -+ -+#define RT305X_ESW_POA_LINK_MASK 0x1f -+#define RT305X_ESW_POA_LINK_SHIFT 25 -+ -+#define RT305X_ESW_PORT_ST_CHG BIT(26) -+#define RT305X_ESW_PORT0 0 -+#define RT305X_ESW_PORT1 1 -+#define RT305X_ESW_PORT2 2 -+#define RT305X_ESW_PORT3 3 -+#define RT305X_ESW_PORT4 4 -+#define RT305X_ESW_PORT5 5 -+#define RT305X_ESW_PORT6 6 -+ -+#define RT305X_ESW_PORTS_NONE 0 -+ -+#define RT305X_ESW_PMAP_LLLLLL 0x3f -+#define RT305X_ESW_PMAP_LLLLWL 0x2f -+#define RT305X_ESW_PMAP_WLLLLL 0x3e -+ -+#define RT305X_ESW_PORTS_INTERNAL \ -+ (BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT1) | \ -+ BIT(RT305X_ESW_PORT2) | BIT(RT305X_ESW_PORT3) | \ -+ BIT(RT305X_ESW_PORT4)) -+ -+#define RT305X_ESW_PORTS_NOCPU \ -+ (RT305X_ESW_PORTS_INTERNAL | BIT(RT305X_ESW_PORT5)) -+ -+#define RT305X_ESW_PORTS_CPU BIT(RT305X_ESW_PORT6) -+ -+#define RT305X_ESW_PORTS_ALL \ -+ (RT305X_ESW_PORTS_NOCPU | RT305X_ESW_PORTS_CPU) -+ -+#define RT305X_ESW_NUM_VLANS 16 -+#define RT305X_ESW_NUM_VIDS 4096 -+#define RT305X_ESW_NUM_PORTS 7 -+#define RT305X_ESW_NUM_LANWAN 6 -+#define RT305X_ESW_NUM_LEDS 5 -+ -+enum { -+ /* Global attributes. */ -+ RT305X_ESW_ATTR_ENABLE_VLAN, -+ RT305X_ESW_ATTR_ALT_VLAN_DISABLE, -+ /* Port attributes. */ -+ RT305X_ESW_ATTR_PORT_DISABLE, -+ RT305X_ESW_ATTR_PORT_DOUBLETAG, -+ RT305X_ESW_ATTR_PORT_UNTAG, -+ RT305X_ESW_ATTR_PORT_LED, -+ RT305X_ESW_ATTR_PORT_LAN, -+ RT305X_ESW_ATTR_PORT_RECV_BAD, -+ RT305X_ESW_ATTR_PORT_RECV_GOOD, -+}; -+ -+struct rt305x_esw_port { -+ bool disable; -+ bool doubletag; -+ bool untag; -+ u8 led; -+ u16 pvid; -+}; -+ -+struct rt305x_esw_vlan { -+ u8 ports; -+ u16 vid; -+}; -+ -+struct rt305x_esw { -+ struct device *dev; -+ void __iomem *base; -+ int irq; -+ const struct rt305x_esw_platform_data *pdata; -+ /* Protects against concurrent register rmw operations. */ -+ spinlock_t reg_rw_lock; -+ -+ unsigned char port_map; -+ unsigned int reg_initval_fct2; -+ unsigned int reg_initval_fpa2; -+ -+ -+ struct switch_dev swdev; -+ bool global_vlan_enable; -+ bool alt_vlan_disable; -+ struct rt305x_esw_vlan vlans[RT305X_ESW_NUM_VLANS]; -+ struct rt305x_esw_port ports[RT305X_ESW_NUM_PORTS]; -+ -+}; -+ -+static inline void -+rt305x_esw_wr(struct rt305x_esw *esw, u32 val, unsigned reg) -+{ -+ __raw_writel(val, esw->base + reg); -+} -+ -+static inline u32 -+rt305x_esw_rr(struct rt305x_esw *esw, unsigned reg) -+{ -+ return __raw_readl(esw->base + reg); -+} -+ -+static inline void -+rt305x_esw_rmw_raw(struct rt305x_esw *esw, unsigned reg, unsigned long mask, -+ unsigned long val) -+{ -+ unsigned long t; -+ -+ t = __raw_readl(esw->base + reg) & ~mask; -+ __raw_writel(t | val, esw->base + reg); -+} -+ -+static void -+rt305x_esw_rmw(struct rt305x_esw *esw, unsigned reg, unsigned long mask, -+ unsigned long val) -+{ -+ unsigned long flags; -+ -+ spin_lock_irqsave(&esw->reg_rw_lock, flags); -+ rt305x_esw_rmw_raw(esw, reg, mask, val); -+ spin_unlock_irqrestore(&esw->reg_rw_lock, flags); -+} -+ -+static u32 -+rt305x_mii_write(struct rt305x_esw *esw, u32 phy_addr, u32 phy_register, -+ u32 write_data) -+{ -+ unsigned long t_start = jiffies; -+ int ret = 0; -+ -+ while (1) { -+ if (!(rt305x_esw_rr(esw, RT305X_ESW_REG_PCR1) & -+ RT305X_ESW_PCR1_WT_DONE)) -+ break; -+ if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) { -+ ret = 1; -+ goto out; -+ } -+ } -+ -+ write_data &= 0xffff; -+ rt305x_esw_wr(esw, -+ (write_data << RT305X_ESW_PCR0_WT_NWAY_DATA_S) | -+ (phy_register << RT305X_ESW_PCR0_CPU_PHY_REG_S) | -+ (phy_addr) | RT305X_ESW_PCR0_WT_PHY_CMD, -+ RT305X_ESW_REG_PCR0); -+ -+ t_start = jiffies; -+ while (1) { -+ if (rt305x_esw_rr(esw, RT305X_ESW_REG_PCR1) & -+ RT305X_ESW_PCR1_WT_DONE) -+ break; -+ -+ if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) { -+ ret = 1; -+ break; -+ } -+ } -+out: -+ if (ret) -+ printk(KERN_ERR "ramips_eth: MDIO timeout\n"); -+ return ret; -+} -+ -+static unsigned -+rt305x_esw_get_vlan_id(struct rt305x_esw *esw, unsigned vlan) -+{ -+ unsigned s; -+ unsigned val; -+ -+ s = RT305X_ESW_VLANI_VID_S * (vlan % 2); -+ val = rt305x_esw_rr(esw, RT305X_ESW_REG_VLANI(vlan / 2)); -+ val = (val >> s) & RT305X_ESW_VLANI_VID_M; -+ -+ return val; -+} -+ -+static void -+rt305x_esw_set_vlan_id(struct rt305x_esw *esw, unsigned vlan, unsigned vid) -+{ -+ unsigned s; -+ -+ s = RT305X_ESW_VLANI_VID_S * (vlan % 2); -+ rt305x_esw_rmw(esw, -+ RT305X_ESW_REG_VLANI(vlan / 2), -+ RT305X_ESW_VLANI_VID_M << s, -+ (vid & RT305X_ESW_VLANI_VID_M) << s); -+} -+ -+static unsigned -+rt305x_esw_get_pvid(struct rt305x_esw *esw, unsigned port) -+{ -+ unsigned s, val; -+ -+ s = RT305X_ESW_PVIDC_PVID_S * (port % 2); -+ val = rt305x_esw_rr(esw, RT305X_ESW_REG_PVIDC(port / 2)); -+ return (val >> s) & RT305X_ESW_PVIDC_PVID_M; -+} -+ -+static void -+rt305x_esw_set_pvid(struct rt305x_esw *esw, unsigned port, unsigned pvid) -+{ -+ unsigned s; -+ -+ s = RT305X_ESW_PVIDC_PVID_S * (port % 2); -+ rt305x_esw_rmw(esw, -+ RT305X_ESW_REG_PVIDC(port / 2), -+ RT305X_ESW_PVIDC_PVID_M << s, -+ (pvid & RT305X_ESW_PVIDC_PVID_M) << s); -+} -+ -+static unsigned -+rt305x_esw_get_vmsc(struct rt305x_esw *esw, unsigned vlan) -+{ -+ unsigned s, val; -+ -+ s = RT305X_ESW_VMSC_MSC_S * (vlan % 4); -+ val = rt305x_esw_rr(esw, RT305X_ESW_REG_VMSC(vlan / 4)); -+ val = (val >> s) & RT305X_ESW_VMSC_MSC_M; -+ -+ return val; -+} -+ -+static void -+rt305x_esw_set_vmsc(struct rt305x_esw *esw, unsigned vlan, unsigned msc) -+{ -+ unsigned s; -+ -+ s = RT305X_ESW_VMSC_MSC_S * (vlan % 4); -+ rt305x_esw_rmw(esw, -+ RT305X_ESW_REG_VMSC(vlan / 4), -+ RT305X_ESW_VMSC_MSC_M << s, -+ (msc & RT305X_ESW_VMSC_MSC_M) << s); -+} -+ -+static unsigned -+rt305x_esw_get_port_disable(struct rt305x_esw *esw) -+{ -+ unsigned reg; -+ reg = rt305x_esw_rr(esw, RT305X_ESW_REG_POC0); -+ return (reg >> RT305X_ESW_POC0_DIS_PORT_S) & -+ RT305X_ESW_POC0_DIS_PORT_M; -+} -+ -+static void -+rt305x_esw_set_port_disable(struct rt305x_esw *esw, unsigned disable_mask) -+{ -+ unsigned old_mask; -+ unsigned enable_mask; -+ unsigned changed; -+ int i; -+ -+ old_mask = rt305x_esw_get_port_disable(esw); -+ changed = old_mask ^ disable_mask; -+ enable_mask = old_mask & disable_mask; -+ -+ /* enable before writing to MII */ -+ rt305x_esw_rmw(esw, RT305X_ESW_REG_POC0, -+ (RT305X_ESW_POC0_DIS_PORT_M << -+ RT305X_ESW_POC0_DIS_PORT_S), -+ enable_mask << RT305X_ESW_POC0_DIS_PORT_S); -+ -+ for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) { -+ if (!(changed & (1 << i))) -+ continue; -+ if (disable_mask & (1 << i)) { -+ /* disable */ -+ rt305x_mii_write(esw, i, MII_BMCR, -+ BMCR_PDOWN); -+ } else { -+ /* enable */ -+ rt305x_mii_write(esw, i, MII_BMCR, -+ BMCR_FULLDPLX | -+ BMCR_ANENABLE | -+ BMCR_ANRESTART | -+ BMCR_SPEED100); -+ } -+ } -+ -+ /* disable after writing to MII */ -+ rt305x_esw_rmw(esw, RT305X_ESW_REG_POC0, -+ (RT305X_ESW_POC0_DIS_PORT_M << -+ RT305X_ESW_POC0_DIS_PORT_S), -+ disable_mask << RT305X_ESW_POC0_DIS_PORT_S); -+} -+ -+static int -+rt305x_esw_apply_config(struct switch_dev *dev); -+ -+static void -+rt305x_esw_hw_init(struct rt305x_esw *esw) -+{ -+ int i; -+ u8 port_disable = 0; -+ u8 port_map = RT305X_ESW_PMAP_LLLLLL; -+ -+ /* vodoo from original driver */ -+ rt305x_esw_wr(esw, 0xC8A07850, RT305X_ESW_REG_FCT0); -+ rt305x_esw_wr(esw, 0x00000000, RT305X_ESW_REG_SGC2); -+ /* Port priority 1 for all ports, vlan enabled. */ -+ rt305x_esw_wr(esw, 0x00005555 | -+ (RT305X_ESW_PORTS_ALL << RT305X_ESW_PFC1_EN_VLAN_S), -+ RT305X_ESW_REG_PFC1); -+ -+ /* Enable Back Pressure, and Flow Control */ -+ rt305x_esw_wr(esw, -+ ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_BP_S) | -+ (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_FC_S)), -+ RT305X_ESW_REG_POC0); -+ -+ /* Enable Aging, and VLAN TAG removal */ -+ rt305x_esw_wr(esw, -+ ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC2_ENAGING_S) | -+ (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC2_UNTAG_EN_S)), -+ RT305X_ESW_REG_POC2); -+ -+ if (esw->reg_initval_fct2) -+ rt305x_esw_wr(esw, esw->reg_initval_fct2, RT305X_ESW_REG_FCT2); -+ else -+ rt305x_esw_wr(esw, esw->pdata->reg_initval_fct2, RT305X_ESW_REG_FCT2); -+ -+ /* -+ * 300s aging timer, max packet len 1536, broadcast storm prevention -+ * disabled, disable collision abort, mac xor48 hash, 10 packet back -+ * pressure jam, GMII disable was_transmit, back pressure disabled, -+ * 30ms led flash, unmatched IGMP as broadcast, rmc tb fault to all -+ * ports. -+ */ -+ rt305x_esw_wr(esw, 0x0008a301, RT305X_ESW_REG_SGC); -+ -+ /* Setup SoC Port control register */ -+ rt305x_esw_wr(esw, -+ (RT305X_ESW_SOCPC_CRC_PADDING | -+ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISUN2CPU_S) | -+ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISMC2CPU_S) | -+ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISBC2CPU_S)), -+ RT305X_ESW_REG_SOCPC); -+ -+ if (esw->reg_initval_fpa2) -+ rt305x_esw_wr(esw, esw->reg_initval_fpa2, RT305X_ESW_REG_FPA2); -+ else -+ rt305x_esw_wr(esw, esw->pdata->reg_initval_fpa2, RT305X_ESW_REG_FPA2); -+ rt305x_esw_wr(esw, 0x00000000, RT305X_ESW_REG_FPA); -+ -+ /* Force Link/Activity on ports */ -+ rt305x_esw_wr(esw, 0x00000005, RT305X_ESW_REG_P0LED); -+ rt305x_esw_wr(esw, 0x00000005, RT305X_ESW_REG_P1LED); -+ rt305x_esw_wr(esw, 0x00000005, RT305X_ESW_REG_P2LED); -+ rt305x_esw_wr(esw, 0x00000005, RT305X_ESW_REG_P3LED); -+ rt305x_esw_wr(esw, 0x00000005, RT305X_ESW_REG_P4LED); -+ -+ /* Copy disabled port configuration from bootloader setup */ -+ port_disable = rt305x_esw_get_port_disable(esw); -+ for (i = 0; i < 6; i++) -+ esw->ports[i].disable = (port_disable & (1 << i)) != 0; -+ -+ rt305x_mii_write(esw, 0, 31, 0x8000); -+ for (i = 0; i < 5; i++) { -+ if (esw->ports[i].disable) { -+ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN); -+ } else { -+ rt305x_mii_write(esw, i, MII_BMCR, -+ BMCR_FULLDPLX | -+ BMCR_ANENABLE | -+ BMCR_SPEED100); -+ } -+ /* TX10 waveform coefficient */ -+ rt305x_mii_write(esw, i, 26, 0x1601); -+ /* TX100/TX10 AD/DA current bias */ -+ rt305x_mii_write(esw, i, 29, 0x7058); -+ /* TX100 slew rate control */ -+ rt305x_mii_write(esw, i, 30, 0x0018); -+ } -+ -+ /* PHY IOT */ -+ /* select global register */ -+ rt305x_mii_write(esw, 0, 31, 0x0); -+ /* tune TP_IDL tail and head waveform */ -+ rt305x_mii_write(esw, 0, 22, 0x052f); -+ /* set TX10 signal amplitude threshold to minimum */ -+ rt305x_mii_write(esw, 0, 17, 0x0fe0); -+ /* set squelch amplitude to higher threshold */ -+ rt305x_mii_write(esw, 0, 18, 0x40ba); -+ /* longer TP_IDL tail length */ -+ rt305x_mii_write(esw, 0, 14, 0x65); -+ /* select local register */ -+ rt305x_mii_write(esw, 0, 31, 0x8000); -+ -+ if (esw->port_map) -+ port_map = esw->port_map; -+ else -+ port_map = RT305X_ESW_PMAP_LLLLLL; -+ -+ /* -+ * Unused HW feature, but still nice to be consistent here... -+ * This is also exported to userspace ('lan' attribute) so it's -+ * conveniently usable to decide which ports go into the wan vlan by -+ * default. -+ */ -+ rt305x_esw_rmw(esw, RT305X_ESW_REG_SGC2, -+ RT305X_ESW_SGC2_LAN_PMAP_M << RT305X_ESW_SGC2_LAN_PMAP_S, -+ port_map << RT305X_ESW_SGC2_LAN_PMAP_S); -+ -+ /* make the switch leds blink */ -+ for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) -+ esw->ports[i].led = 0x05; -+ -+ /* Apply the empty config. */ -+ rt305x_esw_apply_config(&esw->swdev); -+} -+ -+static irqreturn_t -+rt305x_esw_interrupt(int irq, void *_esw) -+{ -+ struct rt305x_esw *esw = (struct rt305x_esw *) _esw; -+ u32 status; -+ -+ status = rt305x_esw_rr(esw, RT305X_ESW_REG_ISR); -+ if (status & RT305X_ESW_PORT_ST_CHG) { -+ u32 link = rt305x_esw_rr(esw, RT305X_ESW_REG_POA); -+ link >>= RT305X_ESW_POA_LINK_SHIFT; -+ link &= RT305X_ESW_POA_LINK_MASK; -+ dev_info(esw->dev, "link changed 0x%02X\n", link); -+ } -+ rt305x_esw_wr(esw, RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_ISR); -+ -+ return IRQ_HANDLED; -+} -+ -+static void -+rt305x_esw_request_irq(struct rt305x_esw *esw) -+{ -+ /* Only unmask the port change interrupt */ -+ rt305x_esw_wr(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR); -+ -+ /* request the irq handler */ -+ request_irq(esw->irq, rt305x_esw_interrupt, 0, "esw", esw); -+} -+ -+static int -+rt305x_esw_apply_config(struct switch_dev *dev) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ int i; -+ u8 disable = 0; -+ u8 doubletag = 0; -+ u8 en_vlan = 0; -+ u8 untag = 0; -+ -+ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { -+ u32 vid, vmsc; -+ if (esw->global_vlan_enable) { -+ vid = esw->vlans[i].vid; -+ vmsc = esw->vlans[i].ports; -+ } else { -+ vid = RT305X_ESW_VLAN_NONE; -+ vmsc = RT305X_ESW_PORTS_NONE; -+ } -+ rt305x_esw_set_vlan_id(esw, i, vid); -+ rt305x_esw_set_vmsc(esw, i, vmsc); -+ } -+ -+ for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) { -+ u32 pvid; -+ disable |= esw->ports[i].disable << i; -+ if (esw->global_vlan_enable) { -+ doubletag |= esw->ports[i].doubletag << i; -+ en_vlan |= 1 << i; -+ untag |= esw->ports[i].untag << i; -+ pvid = esw->ports[i].pvid; -+ } else { -+ int x = esw->alt_vlan_disable ? 0 : 1; -+ doubletag |= x << i; -+ en_vlan |= x << i; -+ untag |= x << i; -+ pvid = 0; -+ } -+ rt305x_esw_set_pvid(esw, i, pvid); -+ if (i < RT305X_ESW_NUM_LEDS) -+ rt305x_esw_wr(esw, esw->ports[i].led, -+ RT305X_ESW_REG_P0LED + 4*i); -+ } -+ -+ rt305x_esw_set_port_disable(esw, disable); -+ rt305x_esw_rmw(esw, RT305X_ESW_REG_SGC2, -+ (RT305X_ESW_SGC2_DOUBLE_TAG_M << -+ RT305X_ESW_SGC2_DOUBLE_TAG_S), -+ doubletag << RT305X_ESW_SGC2_DOUBLE_TAG_S); -+ rt305x_esw_rmw(esw, RT305X_ESW_REG_PFC1, -+ RT305X_ESW_PFC1_EN_VLAN_M << RT305X_ESW_PFC1_EN_VLAN_S, -+ en_vlan << RT305X_ESW_PFC1_EN_VLAN_S); -+ rt305x_esw_rmw(esw, RT305X_ESW_REG_POC2, -+ RT305X_ESW_POC2_UNTAG_EN_M << RT305X_ESW_POC2_UNTAG_EN_S, -+ untag << RT305X_ESW_POC2_UNTAG_EN_S); -+ -+ if (!esw->global_vlan_enable) { -+ /* -+ * Still need to put all ports into vlan 0 or they'll be -+ * isolated. -+ * NOTE: vlan 0 is special, no vlan tag is prepended -+ */ -+ rt305x_esw_set_vlan_id(esw, 0, 0); -+ rt305x_esw_set_vmsc(esw, 0, RT305X_ESW_PORTS_ALL); -+ } -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_reset_switch(struct switch_dev *dev) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ esw->global_vlan_enable = 0; -+ memset(esw->ports, 0, sizeof(esw->ports)); -+ memset(esw->vlans, 0, sizeof(esw->vlans)); -+ rt305x_esw_hw_init(esw); -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_get_vlan_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ -+ val->value.i = esw->global_vlan_enable; -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_set_vlan_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ -+ esw->global_vlan_enable = val->value.i != 0; -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_get_alt_vlan_disable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ -+ val->value.i = esw->alt_vlan_disable; -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_set_alt_vlan_disable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ -+ esw->alt_vlan_disable = val->value.i != 0; -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_get_port_link(struct switch_dev *dev, -+ int port, -+ struct switch_port_link *link) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ u32 speed, poa; -+ -+ if (port < 0 || port >= RT305X_ESW_NUM_PORTS) -+ return -EINVAL; -+ -+ poa = rt305x_esw_rr(esw, RT305X_ESW_REG_POA) >> port; -+ -+ link->link = (poa >> RT305X_ESW_LINK_S) & 1; -+ link->duplex = (poa >> RT305X_ESW_DUPLEX_S) & 1; -+ if (port < RT305X_ESW_NUM_LEDS) { -+ speed = (poa >> RT305X_ESW_SPD_S) & 1; -+ } else { -+ if (port == RT305X_ESW_NUM_PORTS - 1) -+ poa >>= 1; -+ speed = (poa >> RT305X_ESW_SPD_S) & 3; -+ } -+ switch (speed) { -+ case 0: -+ link->speed = SWITCH_PORT_SPEED_10; -+ break; -+ case 1: -+ link->speed = SWITCH_PORT_SPEED_100; -+ break; -+ case 2: -+ case 3: /* forced gige speed can be 2 or 3 */ -+ link->speed = SWITCH_PORT_SPEED_1000; -+ break; -+ default: -+ link->speed = SWITCH_PORT_SPEED_UNKNOWN; -+ break; -+ } -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_get_port_bool(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ int idx = val->port_vlan; -+ u32 x, reg, shift; -+ -+ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS) -+ return -EINVAL; -+ -+ switch (attr->id) { -+ case RT305X_ESW_ATTR_PORT_DISABLE: -+ reg = RT305X_ESW_REG_POC0; -+ shift = RT305X_ESW_POC0_DIS_PORT_S; -+ break; -+ case RT305X_ESW_ATTR_PORT_DOUBLETAG: -+ reg = RT305X_ESW_REG_SGC2; -+ shift = RT305X_ESW_SGC2_DOUBLE_TAG_S; -+ break; -+ case RT305X_ESW_ATTR_PORT_UNTAG: -+ reg = RT305X_ESW_REG_POC2; -+ shift = RT305X_ESW_POC2_UNTAG_EN_S; -+ break; -+ case RT305X_ESW_ATTR_PORT_LAN: -+ reg = RT305X_ESW_REG_SGC2; -+ shift = RT305X_ESW_SGC2_LAN_PMAP_S; -+ if (idx >= RT305X_ESW_NUM_LANWAN) -+ return -EINVAL; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ x = rt305x_esw_rr(esw, reg); -+ val->value.i = (x >> (idx + shift)) & 1; -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_set_port_bool(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ int idx = val->port_vlan; -+ -+ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS || -+ val->value.i < 0 || val->value.i > 1) -+ return -EINVAL; -+ -+ switch (attr->id) { -+ case RT305X_ESW_ATTR_PORT_DISABLE: -+ esw->ports[idx].disable = val->value.i; -+ break; -+ case RT305X_ESW_ATTR_PORT_DOUBLETAG: -+ esw->ports[idx].doubletag = val->value.i; -+ break; -+ case RT305X_ESW_ATTR_PORT_UNTAG: -+ esw->ports[idx].untag = val->value.i; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_get_port_recv_badgood(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ int idx = val->port_vlan; -+ int shift = attr->id == RT305X_ESW_ATTR_PORT_RECV_GOOD ? 0 : 16; -+ u32 reg; -+ -+ if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN) -+ return -EINVAL; -+ -+ reg = rt305x_esw_rr(esw, RT305X_ESW_REG_P0PC + 4*idx); -+ val->value.i = (reg >> shift) & 0xffff; -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_get_port_led(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ int idx = val->port_vlan; -+ -+ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS || -+ idx >= RT305X_ESW_NUM_LEDS) -+ return -EINVAL; -+ -+ val->value.i = rt305x_esw_rr(esw, RT305X_ESW_REG_P0LED + 4*idx); -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_set_port_led(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ int idx = val->port_vlan; -+ -+ if (idx < 0 || idx >= RT305X_ESW_NUM_LEDS) -+ return -EINVAL; -+ -+ esw->ports[idx].led = val->value.i; -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_get_port_pvid(struct switch_dev *dev, int port, int *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ -+ if (port >= RT305X_ESW_NUM_PORTS) -+ return -EINVAL; -+ -+ *val = rt305x_esw_get_pvid(esw, port); -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_set_port_pvid(struct switch_dev *dev, int port, int val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ -+ if (port >= RT305X_ESW_NUM_PORTS) -+ return -EINVAL; -+ -+ esw->ports[port].pvid = val; -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ u32 vmsc, poc2; -+ int vlan_idx = -1; -+ int i; -+ -+ val->len = 0; -+ -+ if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS) -+ return -EINVAL; -+ -+ /* valid vlan? */ -+ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { -+ if (rt305x_esw_get_vlan_id(esw, i) == val->port_vlan && -+ rt305x_esw_get_vmsc(esw, i) != RT305X_ESW_PORTS_NONE) { -+ vlan_idx = i; -+ break; -+ } -+ } -+ -+ if (vlan_idx == -1) -+ return -EINVAL; -+ -+ vmsc = rt305x_esw_get_vmsc(esw, vlan_idx); -+ poc2 = rt305x_esw_rr(esw, RT305X_ESW_REG_POC2); -+ -+ for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) { -+ struct switch_port *p; -+ int port_mask = 1 << i; -+ -+ if (!(vmsc & port_mask)) -+ continue; -+ -+ p = &val->value.ports[val->len++]; -+ p->id = i; -+ if (poc2 & (port_mask << RT305X_ESW_POC2_UNTAG_EN_S)) -+ p->flags = 0; -+ else -+ p->flags = 1 << SWITCH_PORT_FLAG_TAGGED; -+ } -+ -+ return 0; -+} -+ -+static int -+rt305x_esw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) -+{ -+ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); -+ int ports; -+ int vlan_idx = -1; -+ int i; -+ -+ if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS || -+ val->len > RT305X_ESW_NUM_PORTS) -+ return -EINVAL; -+ -+ /* one of the already defined vlans? */ -+ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { -+ if (esw->vlans[i].vid == val->port_vlan && -+ esw->vlans[i].ports != RT305X_ESW_PORTS_NONE) { -+ vlan_idx = i; -+ break; -+ } -+ } -+ -+ /* select a free slot */ -+ for (i = 0; vlan_idx == -1 && i < RT305X_ESW_NUM_VLANS; i++) { -+ if (esw->vlans[i].ports == RT305X_ESW_PORTS_NONE) -+ vlan_idx = i; -+ } -+ -+ /* bail if all slots are in use */ -+ if (vlan_idx == -1) -+ return -EINVAL; -+ -+ ports = RT305X_ESW_PORTS_NONE; -+ for (i = 0; i < val->len; i++) { -+ struct switch_port *p = &val->value.ports[i]; -+ int port_mask = 1 << p->id; -+ bool untagged = !(p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)); -+ -+ if (p->id >= RT305X_ESW_NUM_PORTS) -+ return -EINVAL; -+ -+ ports |= port_mask; -+ esw->ports[p->id].untag = untagged; -+ } -+ esw->vlans[vlan_idx].ports = ports; -+ if (ports == RT305X_ESW_PORTS_NONE) -+ esw->vlans[vlan_idx].vid = RT305X_ESW_VLAN_NONE; -+ else -+ esw->vlans[vlan_idx].vid = val->port_vlan; -+ -+ return 0; -+} -+ -+static const struct switch_attr rt305x_esw_global[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_vlan", -+ .description = "VLAN mode (1:enabled)", -+ .max = 1, -+ .id = RT305X_ESW_ATTR_ENABLE_VLAN, -+ .get = rt305x_esw_get_vlan_enable, -+ .set = rt305x_esw_set_vlan_enable, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "alternate_vlan_disable", -+ .description = "Use en_vlan instead of doubletag to disable" -+ " VLAN mode", -+ .max = 1, -+ .id = RT305X_ESW_ATTR_ALT_VLAN_DISABLE, -+ .get = rt305x_esw_get_alt_vlan_disable, -+ .set = rt305x_esw_set_alt_vlan_disable, -+ }, -+}; -+ -+static const struct switch_attr rt305x_esw_port[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "disable", -+ .description = "Port state (1:disabled)", -+ .max = 1, -+ .id = RT305X_ESW_ATTR_PORT_DISABLE, -+ .get = rt305x_esw_get_port_bool, -+ .set = rt305x_esw_set_port_bool, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "doubletag", -+ .description = "Double tagging for incoming vlan packets " -+ "(1:enabled)", -+ .max = 1, -+ .id = RT305X_ESW_ATTR_PORT_DOUBLETAG, -+ .get = rt305x_esw_get_port_bool, -+ .set = rt305x_esw_set_port_bool, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "untag", -+ .description = "Untag (1:strip outgoing vlan tag)", -+ .max = 1, -+ .id = RT305X_ESW_ATTR_PORT_UNTAG, -+ .get = rt305x_esw_get_port_bool, -+ .set = rt305x_esw_set_port_bool, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "led", -+ .description = "LED mode (0:link, 1:100m, 2:duplex, 3:activity," -+ " 4:collision, 5:linkact, 6:duplcoll, 7:10mact," -+ " 8:100mact, 10:blink, 12:on)", -+ .max = 15, -+ .id = RT305X_ESW_ATTR_PORT_LED, -+ .get = rt305x_esw_get_port_led, -+ .set = rt305x_esw_set_port_led, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "lan", -+ .description = "HW port group (0:wan, 1:lan)", -+ .max = 1, -+ .id = RT305X_ESW_ATTR_PORT_LAN, -+ .get = rt305x_esw_get_port_bool, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "recv_bad", -+ .description = "Receive bad packet counter", -+ .id = RT305X_ESW_ATTR_PORT_RECV_BAD, -+ .get = rt305x_esw_get_port_recv_badgood, -+ }, -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "recv_good", -+ .description = "Receive good packet counter", -+ .id = RT305X_ESW_ATTR_PORT_RECV_GOOD, -+ .get = rt305x_esw_get_port_recv_badgood, -+ }, -+}; -+ -+static const struct switch_attr rt305x_esw_vlan[] = { -+}; -+ -+static const struct switch_dev_ops rt305x_esw_ops = { -+ .attr_global = { -+ .attr = rt305x_esw_global, -+ .n_attr = ARRAY_SIZE(rt305x_esw_global), -+ }, -+ .attr_port = { -+ .attr = rt305x_esw_port, -+ .n_attr = ARRAY_SIZE(rt305x_esw_port), -+ }, -+ .attr_vlan = { -+ .attr = rt305x_esw_vlan, -+ .n_attr = ARRAY_SIZE(rt305x_esw_vlan), -+ }, -+ .get_vlan_ports = rt305x_esw_get_vlan_ports, -+ .set_vlan_ports = rt305x_esw_set_vlan_ports, -+ .get_port_pvid = rt305x_esw_get_port_pvid, -+ .set_port_pvid = rt305x_esw_set_port_pvid, -+ .get_port_link = rt305x_esw_get_port_link, -+ .apply_config = rt305x_esw_apply_config, -+ .reset_switch = rt305x_esw_reset_switch, -+}; -+ -+static struct rt305x_esw_platform_data rt3050_esw_data = { -+ /* All ports are LAN ports. */ -+ .vlan_config = RT305X_ESW_VLAN_CONFIG_NONE, -+ .reg_initval_fct2 = 0x00d6500c, -+ /* -+ * ext phy base addr 31, enable port 5 polling, rx/tx clock skew 1, -+ * turbo mii off, rgmi 3.3v off -+ * port5: disabled -+ * port6: enabled, gige, full-duplex, rx/tx-flow-control -+ */ -+ .reg_initval_fpa2 = 0x3f502b28, -+}; -+ -+static const struct of_device_id ralink_esw_match[] = { -+ { .compatible = "ralink,rt3050-esw", .data = &rt3050_esw_data }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, ralink_esw_match); -+ -+static int -+rt305x_esw_probe(struct platform_device *pdev) -+{ -+ struct device_node *np = pdev->dev.of_node; -+ const struct rt305x_esw_platform_data *pdata; -+ const __be32 *port_map, *reg_init; -+ struct rt305x_esw *esw; -+ struct switch_dev *swdev; -+ struct resource *res, *irq; -+ int err; -+ -+ pdata = pdev->dev.platform_data; -+ if (!pdata) { -+ const struct of_device_id *match; -+ match = of_match_device(ralink_esw_match, &pdev->dev); -+ if (match) -+ pdata = (struct rt305x_esw_platform_data *) match->data; -+ } -+ if (!pdata) -+ return -EINVAL; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(&pdev->dev, "no memory resource found\n"); -+ return -ENOMEM; -+ } -+ -+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); -+ if (!irq) { -+ dev_err(&pdev->dev, "no irq resource found\n"); -+ return -ENOMEM; -+ } -+ -+ esw = kzalloc(sizeof(struct rt305x_esw), GFP_KERNEL); -+ if (!esw) { -+ dev_err(&pdev->dev, "no memory for private data\n"); -+ return -ENOMEM; -+ } -+ -+ esw->dev = &pdev->dev; -+ esw->irq = irq->start; -+ esw->base = ioremap(res->start, resource_size(res)); -+ if (!esw->base) { -+ dev_err(&pdev->dev, "ioremap failed\n"); -+ err = -ENOMEM; -+ goto free_esw; -+ } -+ -+ port_map = of_get_property(np, "ralink,portmap", NULL); -+ if (port_map) -+ esw->port_map = be32_to_cpu(*port_map); -+ -+ reg_init = of_get_property(np, "ralink,fct2", NULL); -+ if (reg_init) -+ esw->reg_initval_fct2 = be32_to_cpu(*reg_init); -+ -+ reg_init = of_get_property(np, "ralink,fpa2", NULL); -+ if (reg_init) -+ esw->reg_initval_fpa2 = be32_to_cpu(*reg_init); -+ -+ swdev = &esw->swdev; -+ swdev->of_node = pdev->dev.of_node; -+ swdev->name = "rt305x-esw"; -+ swdev->alias = "rt305x"; -+ swdev->cpu_port = RT305X_ESW_PORT6; -+ swdev->ports = RT305X_ESW_NUM_PORTS; -+ swdev->vlans = RT305X_ESW_NUM_VIDS; -+ swdev->ops = &rt305x_esw_ops; -+ -+ err = register_switch(swdev, NULL); -+ if (err < 0) { -+ dev_err(&pdev->dev, "register_switch failed\n"); -+ goto unmap_base; -+ } -+ -+ platform_set_drvdata(pdev, esw); -+ -+ esw->pdata = pdata; -+ spin_lock_init(&esw->reg_rw_lock); -+ rt305x_esw_hw_init(esw); -+ rt305x_esw_request_irq(esw); -+ -+ return 0; -+ -+unmap_base: -+ iounmap(esw->base); -+free_esw: -+ kfree(esw); -+ return err; -+} -+ -+static int -+rt305x_esw_remove(struct platform_device *pdev) -+{ -+ struct rt305x_esw *esw; -+ -+ esw = platform_get_drvdata(pdev); -+ if (esw) { -+ unregister_switch(&esw->swdev); -+ platform_set_drvdata(pdev, NULL); -+ iounmap(esw->base); -+ kfree(esw); -+ } -+ -+ return 0; -+} -+ -+static struct platform_driver rt305x_esw_driver = { -+ .probe = rt305x_esw_probe, -+ .remove = rt305x_esw_remove, -+ .driver = { -+ .name = "rt305x-esw", -+ .owner = THIS_MODULE, -+ .of_match_table = ralink_esw_match, -+ }, -+}; -+ -+static int __init -+rt305x_esw_init(void) -+{ -+ return platform_driver_register(&rt305x_esw_driver); -+} -+ -+static void -+rt305x_esw_exit(void) -+{ -+ platform_driver_unregister(&rt305x_esw_driver); -+} ---- /dev/null -+++ b/drivers/net/ethernet/ramips/ramips_eth.h -@@ -0,0 +1,375 @@ -+/* -+ * 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 -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * based on Ralink SDK3.3 -+ * Copyright (C) 2009 John Crispin -+ */ -+ -+#ifndef RAMIPS_ETH_H -+#define RAMIPS_ETH_H -+ -+#include -+#include -+#include -+#include -+ -+#define NUM_RX_DESC 256 -+#define NUM_TX_DESC 256 -+ -+#define RAMIPS_DELAY_EN_INT 0x80 -+#define RAMIPS_DELAY_MAX_INT 0x04 -+#define RAMIPS_DELAY_MAX_TOUT 0x04 -+#define RAMIPS_DELAY_CHAN (((RAMIPS_DELAY_EN_INT | RAMIPS_DELAY_MAX_INT) << 8) | RAMIPS_DELAY_MAX_TOUT) -+#define RAMIPS_DELAY_INIT ((RAMIPS_DELAY_CHAN << 16) | RAMIPS_DELAY_CHAN) -+#define RAMIPS_PSE_FQFC_CFG_INIT 0x80504000 -+ -+/* interrupt bits */ -+#define RAMIPS_CNT_PPE_AF BIT(31) -+#define RAMIPS_CNT_GDM_AF BIT(29) -+#define RAMIPS_PSE_P2_FC BIT(26) -+#define RAMIPS_PSE_BUF_DROP BIT(24) -+#define RAMIPS_GDM_OTHER_DROP BIT(23) -+#define RAMIPS_PSE_P1_FC BIT(22) -+#define RAMIPS_PSE_P0_FC BIT(21) -+#define RAMIPS_PSE_FQ_EMPTY BIT(20) -+#define RAMIPS_GE1_STA_CHG BIT(18) -+#define RAMIPS_TX_COHERENT BIT(17) -+#define RAMIPS_RX_COHERENT BIT(16) -+#define RAMIPS_TX_DONE_INT3 BIT(11) -+#define RAMIPS_TX_DONE_INT2 BIT(10) -+#define RAMIPS_TX_DONE_INT1 BIT(9) -+#define RAMIPS_TX_DONE_INT0 BIT(8) -+#define RAMIPS_RX_DONE_INT0 BIT(2) -+#define RAMIPS_TX_DLY_INT BIT(1) -+#define RAMIPS_RX_DLY_INT BIT(0) -+ -+#define RT5350_RX_DLY_INT BIT(30) -+#define RT5350_TX_DLY_INT BIT(28) -+ -+/* registers */ -+#define RAMIPS_FE_OFFSET 0x0000 -+#define RAMIPS_GDMA_OFFSET 0x0020 -+#define RAMIPS_PSE_OFFSET 0x0040 -+#define RAMIPS_GDMA2_OFFSET 0x0060 -+#define RAMIPS_CDMA_OFFSET 0x0080 -+#define RAMIPS_PDMA_OFFSET 0x0100 -+#define RAMIPS_PPE_OFFSET 0x0200 -+#define RAMIPS_CMTABLE_OFFSET 0x0400 -+#define RAMIPS_POLICYTABLE_OFFSET 0x1000 -+ -+#define RT5350_PDMA_OFFSET 0x0800 -+#define RT5350_SDM_OFFSET 0x0c00 -+ -+#define RAMIPS_MDIO_ACCESS (RAMIPS_FE_OFFSET + 0x00) -+#define RAMIPS_MDIO_CFG (RAMIPS_FE_OFFSET + 0x04) -+#define RAMIPS_FE_GLO_CFG (RAMIPS_FE_OFFSET + 0x08) -+#define RAMIPS_FE_RST_GL (RAMIPS_FE_OFFSET + 0x0C) -+#define RAMIPS_FE_INT_STATUS (RAMIPS_FE_OFFSET + 0x10) -+#define RAMIPS_FE_INT_ENABLE (RAMIPS_FE_OFFSET + 0x14) -+#define RAMIPS_MDIO_CFG2 (RAMIPS_FE_OFFSET + 0x18) -+#define RAMIPS_FOC_TS_T (RAMIPS_FE_OFFSET + 0x1C) -+ -+#define RAMIPS_GDMA1_FWD_CFG (RAMIPS_GDMA_OFFSET + 0x00) -+#define RAMIPS_GDMA1_SCH_CFG (RAMIPS_GDMA_OFFSET + 0x04) -+#define RAMIPS_GDMA1_SHPR_CFG (RAMIPS_GDMA_OFFSET + 0x08) -+#define RAMIPS_GDMA1_MAC_ADRL (RAMIPS_GDMA_OFFSET + 0x0C) -+#define RAMIPS_GDMA1_MAC_ADRH (RAMIPS_GDMA_OFFSET + 0x10) -+ -+#define RAMIPS_GDMA2_FWD_CFG (RAMIPS_GDMA2_OFFSET + 0x00) -+#define RAMIPS_GDMA2_SCH_CFG (RAMIPS_GDMA2_OFFSET + 0x04) -+#define RAMIPS_GDMA2_SHPR_CFG (RAMIPS_GDMA2_OFFSET + 0x08) -+#define RAMIPS_GDMA2_MAC_ADRL (RAMIPS_GDMA2_OFFSET + 0x0C) -+#define RAMIPS_GDMA2_MAC_ADRH (RAMIPS_GDMA2_OFFSET + 0x10) -+ -+#define RAMIPS_PSE_FQ_CFG (RAMIPS_PSE_OFFSET + 0x00) -+#define RAMIPS_CDMA_FC_CFG (RAMIPS_PSE_OFFSET + 0x04) -+#define RAMIPS_GDMA1_FC_CFG (RAMIPS_PSE_OFFSET + 0x08) -+#define RAMIPS_GDMA2_FC_CFG (RAMIPS_PSE_OFFSET + 0x0C) -+ -+#define RAMIPS_CDMA_CSG_CFG (RAMIPS_CDMA_OFFSET + 0x00) -+#define RAMIPS_CDMA_SCH_CFG (RAMIPS_CDMA_OFFSET + 0x04) -+ -+#define RT5350_TX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x00) -+#define RT5350_TX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x04) -+#define RT5350_TX_CTX_IDX0 (RT5350_PDMA_OFFSET + 0x08) -+#define RT5350_TX_DTX_IDX0 (RT5350_PDMA_OFFSET + 0x0C) -+#define RT5350_TX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x10) -+#define RT5350_TX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x14) -+#define RT5350_TX_CTX_IDX1 (RT5350_PDMA_OFFSET + 0x18) -+#define RT5350_TX_DTX_IDX1 (RT5350_PDMA_OFFSET + 0x1C) -+#define RT5350_TX_BASE_PTR2 (RT5350_PDMA_OFFSET + 0x20) -+#define RT5350_TX_MAX_CNT2 (RT5350_PDMA_OFFSET + 0x24) -+#define RT5350_TX_CTX_IDX2 (RT5350_PDMA_OFFSET + 0x28) -+#define RT5350_TX_DTX_IDX2 (RT5350_PDMA_OFFSET + 0x2C) -+#define RT5350_TX_BASE_PTR3 (RT5350_PDMA_OFFSET + 0x30) -+#define RT5350_TX_MAX_CNT3 (RT5350_PDMA_OFFSET + 0x34) -+#define RT5350_TX_CTX_IDX3 (RT5350_PDMA_OFFSET + 0x38) -+#define RT5350_TX_DTX_IDX3 (RT5350_PDMA_OFFSET + 0x3C) -+#define RT5350_RX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x100) -+#define RT5350_RX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x104) -+#define RT5350_RX_CALC_IDX0 (RT5350_PDMA_OFFSET + 0x108) -+#define RT5350_RX_DRX_IDX0 (RT5350_PDMA_OFFSET + 0x10C) -+#define RT5350_RX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x110) -+#define RT5350_RX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x114) -+#define RT5350_RX_CALC_IDX1 (RT5350_PDMA_OFFSET + 0x118) -+#define RT5350_RX_DRX_IDX1 (RT5350_PDMA_OFFSET + 0x11C) -+#define RT5350_PDMA_GLO_CFG (RT5350_PDMA_OFFSET + 0x204) -+#define RT5350_PDMA_RST_CFG (RT5350_PDMA_OFFSET + 0x208) -+#define RT5350_DLY_INT_CFG (RT5350_PDMA_OFFSET + 0x20c) -+#define RT5350_FE_INT_STATUS (RT5350_PDMA_OFFSET + 0x220) -+#define RT5350_FE_INT_ENABLE (RT5350_PDMA_OFFSET + 0x228) -+#define RT5350_PDMA_SCH_CFG (RT5350_PDMA_OFFSET + 0x280) -+ -+ -+#define RAMIPS_PDMA_GLO_CFG (RAMIPS_PDMA_OFFSET + 0x00) -+#define RAMIPS_PDMA_RST_CFG (RAMIPS_PDMA_OFFSET + 0x04) -+#define RAMIPS_PDMA_SCH_CFG (RAMIPS_PDMA_OFFSET + 0x08) -+#define RAMIPS_DLY_INT_CFG (RAMIPS_PDMA_OFFSET + 0x0C) -+#define RAMIPS_TX_BASE_PTR0 (RAMIPS_PDMA_OFFSET + 0x10) -+#define RAMIPS_TX_MAX_CNT0 (RAMIPS_PDMA_OFFSET + 0x14) -+#define RAMIPS_TX_CTX_IDX0 (RAMIPS_PDMA_OFFSET + 0x18) -+#define RAMIPS_TX_DTX_IDX0 (RAMIPS_PDMA_OFFSET + 0x1C) -+#define RAMIPS_TX_BASE_PTR1 (RAMIPS_PDMA_OFFSET + 0x20) -+#define RAMIPS_TX_MAX_CNT1 (RAMIPS_PDMA_OFFSET + 0x24) -+#define RAMIPS_TX_CTX_IDX1 (RAMIPS_PDMA_OFFSET + 0x28) -+#define RAMIPS_TX_DTX_IDX1 (RAMIPS_PDMA_OFFSET + 0x2C) -+#define RAMIPS_RX_BASE_PTR0 (RAMIPS_PDMA_OFFSET + 0x30) -+#define RAMIPS_RX_MAX_CNT0 (RAMIPS_PDMA_OFFSET + 0x34) -+#define RAMIPS_RX_CALC_IDX0 (RAMIPS_PDMA_OFFSET + 0x38) -+#define RAMIPS_RX_DRX_IDX0 (RAMIPS_PDMA_OFFSET + 0x3C) -+#define RAMIPS_TX_BASE_PTR2 (RAMIPS_PDMA_OFFSET + 0x40) -+#define RAMIPS_TX_MAX_CNT2 (RAMIPS_PDMA_OFFSET + 0x44) -+#define RAMIPS_TX_CTX_IDX2 (RAMIPS_PDMA_OFFSET + 0x48) -+#define RAMIPS_TX_DTX_IDX2 (RAMIPS_PDMA_OFFSET + 0x4C) -+#define RAMIPS_TX_BASE_PTR3 (RAMIPS_PDMA_OFFSET + 0x50) -+#define RAMIPS_TX_MAX_CNT3 (RAMIPS_PDMA_OFFSET + 0x54) -+#define RAMIPS_TX_CTX_IDX3 (RAMIPS_PDMA_OFFSET + 0x58) -+#define RAMIPS_TX_DTX_IDX3 (RAMIPS_PDMA_OFFSET + 0x5C) -+#define RAMIPS_RX_BASE_PTR1 (RAMIPS_PDMA_OFFSET + 0x60) -+#define RAMIPS_RX_MAX_CNT1 (RAMIPS_PDMA_OFFSET + 0x64) -+#define RAMIPS_RX_CALC_IDX1 (RAMIPS_PDMA_OFFSET + 0x68) -+#define RAMIPS_RX_DRX_IDX1 (RAMIPS_PDMA_OFFSET + 0x6C) -+ -+#define RT5350_SDM_CFG (RT5350_SDM_OFFSET + 0x00) //Switch DMA configuration -+#define RT5350_SDM_RRING (RT5350_SDM_OFFSET + 0x04) //Switch DMA Rx Ring -+#define RT5350_SDM_TRING (RT5350_SDM_OFFSET + 0x08) //Switch DMA Tx Ring -+#define RT5350_SDM_MAC_ADRL (RT5350_SDM_OFFSET + 0x0C) //Switch MAC address LSB -+#define RT5350_SDM_MAC_ADRH (RT5350_SDM_OFFSET + 0x10) //Switch MAC Address MSB -+#define RT5350_SDM_TPCNT (RT5350_SDM_OFFSET + 0x100) //Switch DMA Tx packet count -+#define RT5350_SDM_TBCNT (RT5350_SDM_OFFSET + 0x104) //Switch DMA Tx byte count -+#define RT5350_SDM_RPCNT (RT5350_SDM_OFFSET + 0x108) //Switch DMA rx packet count -+#define RT5350_SDM_RBCNT (RT5350_SDM_OFFSET + 0x10C) //Switch DMA rx byte count -+#define RT5350_SDM_CS_ERR (RT5350_SDM_OFFSET + 0x110) //Switch DMA rx checksum error count -+ -+#define RT5350_SDM_ICS_EN BIT(16) -+#define RT5350_SDM_TCS_EN BIT(17) -+#define RT5350_SDM_UCS_EN BIT(18) -+ -+ -+/* MDIO_CFG register bits */ -+#define RAMIPS_MDIO_CFG_AUTO_POLL_EN BIT(29) -+#define RAMIPS_MDIO_CFG_GP1_BP_EN BIT(16) -+#define RAMIPS_MDIO_CFG_GP1_FRC_EN BIT(15) -+#define RAMIPS_MDIO_CFG_GP1_SPEED_10 (0 << 13) -+#define RAMIPS_MDIO_CFG_GP1_SPEED_100 (1 << 13) -+#define RAMIPS_MDIO_CFG_GP1_SPEED_1000 (2 << 13) -+#define RAMIPS_MDIO_CFG_GP1_DUPLEX BIT(12) -+#define RAMIPS_MDIO_CFG_GP1_FC_TX BIT(11) -+#define RAMIPS_MDIO_CFG_GP1_FC_RX BIT(10) -+#define RAMIPS_MDIO_CFG_GP1_LNK_DWN BIT(9) -+#define RAMIPS_MDIO_CFG_GP1_AN_FAIL BIT(8) -+#define RAMIPS_MDIO_CFG_MDC_CLK_DIV_1 (0 << 6) -+#define RAMIPS_MDIO_CFG_MDC_CLK_DIV_2 (1 << 6) -+#define RAMIPS_MDIO_CFG_MDC_CLK_DIV_4 (2 << 6) -+#define RAMIPS_MDIO_CFG_MDC_CLK_DIV_8 (3 << 6) -+#define RAMIPS_MDIO_CFG_TURBO_MII_FREQ BIT(5) -+#define RAMIPS_MDIO_CFG_TURBO_MII_MODE BIT(4) -+#define RAMIPS_MDIO_CFG_RX_CLK_SKEW_0 (0 << 2) -+#define RAMIPS_MDIO_CFG_RX_CLK_SKEW_200 (1 << 2) -+#define RAMIPS_MDIO_CFG_RX_CLK_SKEW_400 (2 << 2) -+#define RAMIPS_MDIO_CFG_RX_CLK_SKEW_INV (3 << 2) -+#define RAMIPS_MDIO_CFG_TX_CLK_SKEW_0 0 -+#define RAMIPS_MDIO_CFG_TX_CLK_SKEW_200 1 -+#define RAMIPS_MDIO_CFG_TX_CLK_SKEW_400 2 -+#define RAMIPS_MDIO_CFG_TX_CLK_SKEW_INV 3 -+ -+/* uni-cast port */ -+#define RAMIPS_GDM1_ICS_EN BIT(22) -+#define RAMIPS_GDM1_TCS_EN BIT(21) -+#define RAMIPS_GDM1_UCS_EN BIT(20) -+#define RAMIPS_GDM1_JMB_EN BIT(19) -+#define RAMIPS_GDM1_STRPCRC BIT(16) -+#define RAMIPS_GDM1_UFRC_P_CPU (0 << 12) -+#define RAMIPS_GDM1_UFRC_P_GDMA1 (1 << 12) -+#define RAMIPS_GDM1_UFRC_P_PPE (6 << 12) -+ -+/* checksums */ -+#define RAMIPS_ICS_GEN_EN BIT(2) -+#define RAMIPS_UCS_GEN_EN BIT(1) -+#define RAMIPS_TCS_GEN_EN BIT(0) -+ -+/* dma ring */ -+#define RAMIPS_PST_DRX_IDX0 BIT(16) -+#define RAMIPS_PST_DTX_IDX3 BIT(3) -+#define RAMIPS_PST_DTX_IDX2 BIT(2) -+#define RAMIPS_PST_DTX_IDX1 BIT(1) -+#define RAMIPS_PST_DTX_IDX0 BIT(0) -+ -+#define RAMIPS_TX_WB_DDONE BIT(6) -+#define RAMIPS_RX_DMA_BUSY BIT(3) -+#define RAMIPS_TX_DMA_BUSY BIT(1) -+#define RAMIPS_RX_DMA_EN BIT(2) -+#define RAMIPS_TX_DMA_EN BIT(0) -+ -+#define RAMIPS_PDMA_SIZE_4DWORDS (0 << 4) -+#define RAMIPS_PDMA_SIZE_8DWORDS (1 << 4) -+#define RAMIPS_PDMA_SIZE_16DWORDS (2 << 4) -+ -+#define RAMIPS_US_CYC_CNT_MASK 0xff -+#define RAMIPS_US_CYC_CNT_SHIFT 0x8 -+#define RAMIPS_US_CYC_CNT_DIVISOR 1000000 -+ -+#define RX_DMA_PLEN0(_x) (((_x) >> 16) & 0x3fff) -+#define RX_DMA_LSO BIT(30) -+#define RX_DMA_DONE BIT(31) -+ -+struct ramips_rx_dma { -+ unsigned int rxd1; -+ unsigned int rxd2; -+ unsigned int rxd3; -+ unsigned int rxd4; -+} __packed __aligned(4); -+ -+#define TX_DMA_PLEN0_MASK ((0x3fff) << 16) -+#define TX_DMA_PLEN0(_x) (((_x) & 0x3fff) << 16) -+#define TX_DMA_LSO BIT(30) -+#define TX_DMA_DONE BIT(31) -+#define TX_DMA_QN(_x) ((_x) << 16) -+#define TX_DMA_PN(_x) ((_x) << 24) -+#define TX_DMA_QN_MASK TX_DMA_QN(0x7) -+#define TX_DMA_PN_MASK TX_DMA_PN(0x7) -+ -+struct ramips_tx_dma { -+ unsigned int txd1; -+ unsigned int txd2; -+ unsigned int txd3; -+ unsigned int txd4; -+} __packed __aligned(4); -+ -+struct raeth_tx_info { -+ struct ramips_tx_dma *tx_desc; -+ struct sk_buff *tx_skb; -+}; -+ -+struct raeth_rx_info { -+ struct ramips_rx_dma *rx_desc; -+ struct sk_buff *rx_skb; -+ dma_addr_t rx_dma; -+ unsigned int pad; -+}; -+ -+struct raeth_int_stats { -+ unsigned long rx_delayed; -+ unsigned long tx_delayed; -+ unsigned long rx_done0; -+ unsigned long tx_done0; -+ unsigned long tx_done1; -+ unsigned long tx_done2; -+ unsigned long tx_done3; -+ unsigned long rx_coherent; -+ unsigned long tx_coherent; -+ -+ unsigned long pse_fq_empty; -+ unsigned long pse_p0_fc; -+ unsigned long pse_p1_fc; -+ unsigned long pse_p2_fc; -+ unsigned long pse_buf_drop; -+ -+ unsigned long total; -+}; -+ -+struct raeth_debug { -+ struct dentry *debugfs_dir; -+ -+ struct raeth_int_stats int_stats; -+}; -+ -+struct raeth_priv -+{ -+ struct device_node *of_node; -+ -+ struct raeth_rx_info *rx_info; -+ dma_addr_t rx_desc_dma; -+ struct tasklet_struct rx_tasklet; -+ struct ramips_rx_dma *rx; -+ -+ struct raeth_tx_info *tx_info; -+ dma_addr_t tx_desc_dma; -+ struct tasklet_struct tx_housekeeping_tasklet; -+ struct ramips_tx_dma *tx; -+ -+ unsigned int skb_free_idx; -+ -+ spinlock_t page_lock; -+ struct net_device *netdev; -+ struct device *parent; -+ -+ int link; -+ int speed; -+ int duplex; -+ int tx_fc; -+ int rx_fc; -+ -+ struct mii_bus *mii_bus; -+ int mii_irq[PHY_MAX_ADDR]; -+ struct phy_device *phy_dev; -+ spinlock_t phy_lock; -+ unsigned long sys_freq; -+ -+ unsigned char mac[6]; -+ void (*reset_fe)(void); -+ int min_pkt_len; -+ -+ u32 phy_mask; -+ phy_interface_t phy_if_mode; -+ -+#ifdef CONFIG_NET_RAMIPS_DEBUG_FS -+ struct raeth_debug debug; -+#endif -+}; -+ -+struct ramips_soc_data -+{ -+ unsigned char mac[6]; -+ void (*reset_fe)(void); -+ int min_pkt_len; -+}; -+ -+ -+#ifdef CONFIG_NET_RAMIPS_DEBUG_FS -+int raeth_debugfs_root_init(void); -+void raeth_debugfs_root_exit(void); -+int raeth_debugfs_init(struct raeth_priv *re); -+void raeth_debugfs_exit(struct raeth_priv *re); -+void raeth_debugfs_update_int_stats(struct raeth_priv *re, u32 status); -+#else -+static inline int raeth_debugfs_root_init(void) { return 0; } -+static inline void raeth_debugfs_root_exit(void) {} -+static inline int raeth_debugfs_init(struct raeth_priv *re) { return 0; } -+static inline void raeth_debugfs_exit(struct raeth_priv *re) {} -+static inline void raeth_debugfs_update_int_stats(struct raeth_priv *re, -+ u32 status) {} -+#endif /* CONFIG_NET_RAMIPS_DEBUG_FS */ -+ -+#endif /* RAMIPS_ETH_H */ ---- /dev/null -+++ b/drivers/net/ethernet/ramips/ramips_main.c -@@ -0,0 +1,1281 @@ -+/* -+ * 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 -+ * -+ * This program is distributed in the hope that it will be useful, -+ * but WITHOUT ANY WARRANTY; without even the implied warranty of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ * GNU General Public License for more details. -+ * -+ * You should have received a copy of the GNU General Public License -+ * along with this program; if not, write to the Free Software -+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. -+ * -+ * Copyright (C) 2009 John Crispin -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "ramips_eth.h" -+ -+#define TX_TIMEOUT (20 * HZ / 100) -+#define MAX_RX_LENGTH 1600 -+ -+#ifdef CONFIG_SOC_RT305X -+#include -+#include "ramips_esw.c" -+#else -+#include -+static inline int rt305x_esw_init(void) { return 0; } -+static inline void rt305x_esw_exit(void) { } -+static inline int soc_is_rt5350(void) { return 0; } -+#endif -+ -+#define phys_to_bus(a) (a & 0x1FFFFFFF) -+ -+#ifdef CONFIG_NET_RAMIPS_DEBUG -+#define RADEBUG(fmt, args...) printk(KERN_DEBUG fmt, ## args) -+#else -+#define RADEBUG(fmt, args...) do {} while (0) -+#endif -+ -+#define RX_DLY_INT ((soc_is_rt5350())?(RT5350_RX_DLY_INT):(RAMIPS_RX_DLY_INT)) -+#define TX_DLY_INT ((soc_is_rt5350())?(RT5350_TX_DLY_INT):(RAMIPS_TX_DLY_INT)) -+ -+enum raeth_reg { -+ RAETH_REG_PDMA_GLO_CFG = 0, -+ RAETH_REG_PDMA_RST_CFG, -+ RAETH_REG_DLY_INT_CFG, -+ RAETH_REG_TX_BASE_PTR0, -+ RAETH_REG_TX_MAX_CNT0, -+ RAETH_REG_TX_CTX_IDX0, -+ RAETH_REG_RX_BASE_PTR0, -+ RAETH_REG_RX_MAX_CNT0, -+ RAETH_REG_RX_CALC_IDX0, -+ RAETH_REG_FE_INT_ENABLE, -+ RAETH_REG_FE_INT_STATUS, -+ RAETH_REG_COUNT -+}; -+ -+static const u32 ramips_reg_table[RAETH_REG_COUNT] = { -+ [RAETH_REG_PDMA_GLO_CFG] = RAMIPS_PDMA_GLO_CFG, -+ [RAETH_REG_PDMA_RST_CFG] = RAMIPS_PDMA_RST_CFG, -+ [RAETH_REG_DLY_INT_CFG] = RAMIPS_DLY_INT_CFG, -+ [RAETH_REG_TX_BASE_PTR0] = RAMIPS_TX_BASE_PTR0, -+ [RAETH_REG_TX_MAX_CNT0] = RAMIPS_TX_MAX_CNT0, -+ [RAETH_REG_TX_CTX_IDX0] = RAMIPS_TX_CTX_IDX0, -+ [RAETH_REG_RX_BASE_PTR0] = RAMIPS_RX_BASE_PTR0, -+ [RAETH_REG_RX_MAX_CNT0] = RAMIPS_RX_MAX_CNT0, -+ [RAETH_REG_RX_CALC_IDX0] = RAMIPS_RX_CALC_IDX0, -+ [RAETH_REG_FE_INT_ENABLE] = RAMIPS_FE_INT_ENABLE, -+ [RAETH_REG_FE_INT_STATUS] = RAMIPS_FE_INT_STATUS, -+}; -+ -+static const u32 rt5350_reg_table[RAETH_REG_COUNT] = { -+ [RAETH_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG, -+ [RAETH_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG, -+ [RAETH_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG, -+ [RAETH_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0, -+ [RAETH_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0, -+ [RAETH_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0, -+ [RAETH_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0, -+ [RAETH_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0, -+ [RAETH_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0, -+ [RAETH_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE, -+ [RAETH_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS, -+}; -+ -+static struct net_device * ramips_dev; -+static void __iomem *ramips_fe_base = 0; -+ -+static inline u32 get_reg_offset(enum raeth_reg reg) -+{ -+ const u32 *table; -+ -+ if (soc_is_rt5350()) -+ table = rt5350_reg_table; -+ else -+ table = ramips_reg_table; -+ -+ return table[reg]; -+} -+ -+static inline void -+ramips_fe_wr(u32 val, unsigned reg) -+{ -+ __raw_writel(val, ramips_fe_base + reg); -+} -+ -+static inline u32 -+ramips_fe_rr(unsigned reg) -+{ -+ return __raw_readl(ramips_fe_base + reg); -+} -+ -+static inline void -+ramips_fe_twr(u32 val, enum raeth_reg reg) -+{ -+ ramips_fe_wr(val, get_reg_offset(reg)); -+} -+ -+static inline u32 -+ramips_fe_trr(enum raeth_reg reg) -+{ -+ return ramips_fe_rr(get_reg_offset(reg)); -+} -+ -+static inline void -+ramips_fe_int_disable(u32 mask) -+{ -+ ramips_fe_twr(ramips_fe_trr(RAETH_REG_FE_INT_ENABLE) & ~mask, -+ RAETH_REG_FE_INT_ENABLE); -+ /* flush write */ -+ ramips_fe_trr(RAETH_REG_FE_INT_ENABLE); -+} -+ -+static inline void -+ramips_fe_int_enable(u32 mask) -+{ -+ ramips_fe_twr(ramips_fe_trr(RAETH_REG_FE_INT_ENABLE) | mask, -+ RAETH_REG_FE_INT_ENABLE); -+ /* flush write */ -+ ramips_fe_trr(RAETH_REG_FE_INT_ENABLE); -+} -+ -+static inline void -+ramips_hw_set_macaddr(unsigned char *mac) -+{ -+ if (soc_is_rt5350()) { -+ ramips_fe_wr((mac[0] << 8) | mac[1], RT5350_SDM_MAC_ADRH); -+ ramips_fe_wr((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], -+ RT5350_SDM_MAC_ADRL); -+ } else { -+ ramips_fe_wr((mac[0] << 8) | mac[1], RAMIPS_GDMA1_MAC_ADRH); -+ ramips_fe_wr((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], -+ RAMIPS_GDMA1_MAC_ADRL); -+ } -+} -+ -+static struct sk_buff * -+ramips_alloc_skb(struct raeth_priv *re) -+{ -+ struct sk_buff *skb; -+ -+ skb = netdev_alloc_skb(re->netdev, MAX_RX_LENGTH + NET_IP_ALIGN); -+ if (!skb) -+ return NULL; -+ -+ skb_reserve(skb, NET_IP_ALIGN); -+ -+ return skb; -+} -+ -+static void -+ramips_ring_setup(struct raeth_priv *re) -+{ -+ int len; -+ int i; -+ -+ memset(re->tx_info, 0, NUM_TX_DESC * sizeof(struct raeth_tx_info)); -+ -+ len = NUM_TX_DESC * sizeof(struct ramips_tx_dma); -+ memset(re->tx, 0, len); -+ -+ for (i = 0; i < NUM_TX_DESC; i++) { -+ struct raeth_tx_info *txi; -+ struct ramips_tx_dma *txd; -+ -+ txd = &re->tx[i]; -+ txd->txd4 = TX_DMA_QN(3) | TX_DMA_PN(1); -+ txd->txd2 = TX_DMA_LSO | TX_DMA_DONE; -+ -+ txi = &re->tx_info[i]; -+ txi->tx_desc = txd; -+ if (txi->tx_skb != NULL) { -+ netdev_warn(re->netdev, -+ "dirty skb for TX desc %d\n", i); -+ txi->tx_skb = NULL; -+ } -+ } -+ -+ len = NUM_RX_DESC * sizeof(struct ramips_rx_dma); -+ memset(re->rx, 0, len); -+ -+ for (i = 0; i < NUM_RX_DESC; i++) { -+ struct raeth_rx_info *rxi; -+ struct ramips_rx_dma *rxd; -+ dma_addr_t dma_addr; -+ -+ rxd = &re->rx[i]; -+ rxi = &re->rx_info[i]; -+ BUG_ON(rxi->rx_skb == NULL); -+ dma_addr = dma_map_single(&re->netdev->dev, rxi->rx_skb->data, -+ MAX_RX_LENGTH, DMA_FROM_DEVICE); -+ rxi->rx_dma = dma_addr; -+ rxi->rx_desc = rxd; -+ -+ rxd->rxd1 = (unsigned int) dma_addr; -+ rxd->rxd2 = RX_DMA_LSO; -+ } -+ -+ /* flush descriptors */ -+ wmb(); -+} -+ -+static void -+ramips_ring_cleanup(struct raeth_priv *re) -+{ -+ int i; -+ -+ for (i = 0; i < NUM_RX_DESC; i++) { -+ struct raeth_rx_info *rxi; -+ -+ rxi = &re->rx_info[i]; -+ if (rxi->rx_skb) -+ dma_unmap_single(&re->netdev->dev, rxi->rx_dma, -+ MAX_RX_LENGTH, DMA_FROM_DEVICE); -+ } -+ -+ for (i = 0; i < NUM_TX_DESC; i++) { -+ struct raeth_tx_info *txi; -+ -+ txi = &re->tx_info[i]; -+ if (txi->tx_skb) { -+ dev_kfree_skb_any(txi->tx_skb); -+ txi->tx_skb = NULL; -+ } -+ } -+ -+ netdev_reset_queue(re->netdev); -+} -+ -+#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT3883) -+ -+#define RAMIPS_MDIO_RETRY 1000 -+ -+static unsigned char *ramips_speed_str(struct raeth_priv *re) -+{ -+ switch (re->speed) { -+ case SPEED_1000: -+ return "1000"; -+ case SPEED_100: -+ return "100"; -+ case SPEED_10: -+ return "10"; -+ } -+ -+ return "?"; -+} -+ -+static void ramips_link_adjust(struct raeth_priv *re) -+{ -+ u32 mdio_cfg; -+ -+ if (!re->link) { -+ netif_carrier_off(re->netdev); -+ netdev_info(re->netdev, "link down\n"); -+ return; -+ } -+ -+ mdio_cfg = RAMIPS_MDIO_CFG_TX_CLK_SKEW_200 | -+ RAMIPS_MDIO_CFG_RX_CLK_SKEW_200 | -+ RAMIPS_MDIO_CFG_GP1_FRC_EN; -+ -+ if (re->duplex == DUPLEX_FULL) -+ mdio_cfg |= RAMIPS_MDIO_CFG_GP1_DUPLEX; -+ -+ if (re->tx_fc) -+ mdio_cfg |= RAMIPS_MDIO_CFG_GP1_FC_TX; -+ -+ if (re->rx_fc) -+ mdio_cfg |= RAMIPS_MDIO_CFG_GP1_FC_RX; -+ -+ switch (re->speed) { -+ case SPEED_10: -+ mdio_cfg |= RAMIPS_MDIO_CFG_GP1_SPEED_10; -+ break; -+ case SPEED_100: -+ mdio_cfg |= RAMIPS_MDIO_CFG_GP1_SPEED_100; -+ break; -+ case SPEED_1000: -+ mdio_cfg |= RAMIPS_MDIO_CFG_GP1_SPEED_1000; -+ break; -+ default: -+ BUG(); -+ } -+ -+ ramips_fe_wr(mdio_cfg, RAMIPS_MDIO_CFG); -+ -+ netif_carrier_on(re->netdev); -+ netdev_info(re->netdev, "link up (%sMbps/%s duplex)\n", -+ ramips_speed_str(re), -+ (DUPLEX_FULL == re->duplex) ? "Full" : "Half"); -+} -+ -+static int -+ramips_mdio_wait_ready(struct raeth_priv *re) -+{ -+ int retries; -+ -+ retries = RAMIPS_MDIO_RETRY; -+ while (1) { -+ u32 t; -+ -+ t = ramips_fe_rr(RAMIPS_MDIO_ACCESS); -+ if ((t & (0x1 << 31)) == 0) -+ return 0; -+ -+ if (retries-- == 0) -+ break; -+ -+ udelay(1); -+ } -+ -+ dev_err(re->parent, "MDIO operation timed out\n"); -+ return -ETIMEDOUT; -+} -+ -+static int -+ramips_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) -+{ -+ struct raeth_priv *re = bus->priv; -+ int err; -+ u32 t; -+ -+ err = ramips_mdio_wait_ready(re); -+ if (err) -+ return 0xffff; -+ -+ t = (phy_addr << 24) | (phy_reg << 16); -+ ramips_fe_wr(t, RAMIPS_MDIO_ACCESS); -+ t |= (1 << 31); -+ ramips_fe_wr(t, RAMIPS_MDIO_ACCESS); -+ -+ err = ramips_mdio_wait_ready(re); -+ if (err) -+ return 0xffff; -+ -+ RADEBUG("%s: addr=%04x, reg=%04x, value=%04x\n", __func__, -+ phy_addr, phy_reg, ramips_fe_rr(RAMIPS_MDIO_ACCESS) & 0xffff); -+ -+ return ramips_fe_rr(RAMIPS_MDIO_ACCESS) & 0xffff; -+} -+ -+static int -+ramips_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val) -+{ -+ struct raeth_priv *re = bus->priv; -+ int err; -+ u32 t; -+ -+ RADEBUG("%s: addr=%04x, reg=%04x, value=%04x\n", __func__, -+ phy_addr, phy_reg, ramips_fe_rr(RAMIPS_MDIO_ACCESS) & 0xffff); -+ -+ err = ramips_mdio_wait_ready(re); -+ if (err) -+ return err; -+ -+ t = (1 << 30) | (phy_addr << 24) | (phy_reg << 16) | val; -+ ramips_fe_wr(t, RAMIPS_MDIO_ACCESS); -+ t |= (1 << 31); -+ ramips_fe_wr(t, RAMIPS_MDIO_ACCESS); -+ -+ return ramips_mdio_wait_ready(re); -+} -+ -+static int -+ramips_mdio_reset(struct mii_bus *bus) -+{ -+ /* TODO */ -+ return 0; -+} -+ -+static int -+ramips_mdio_init(struct raeth_priv *re) -+{ -+ struct device_node *mii_np; -+ int err; -+ -+ mii_np = of_get_child_by_name(re->of_node, "mdio-bus"); -+ if (!mii_np) { -+ dev_err(re->parent, "no %s child node found", "mdio-bus"); -+ return -ENODEV; -+ } -+ -+ if (!of_device_is_available(mii_np)) { -+ err = 0; -+ goto err_put_node; -+ } -+ -+ re->mii_bus = mdiobus_alloc(); -+ if (re->mii_bus == NULL) { -+ err = -ENOMEM; -+ goto err_put_node; -+ } -+ -+ re->mii_bus->name = "ramips_mdio"; -+ re->mii_bus->read = ramips_mdio_read; -+ re->mii_bus->write = ramips_mdio_write; -+ re->mii_bus->reset = ramips_mdio_reset; -+ re->mii_bus->irq = re->mii_irq; -+ re->mii_bus->priv = re; -+ re->mii_bus->parent = re->parent; -+ -+ snprintf(re->mii_bus->id, MII_BUS_ID_SIZE, "%s", "ramips_mdio"); -+ err = of_mdiobus_register(re->mii_bus, mii_np); -+ if (err) -+ goto err_free_bus; -+ -+ return 0; -+ -+err_free_bus: -+ kfree(re->mii_bus); -+err_put_node: -+ of_node_put(mii_np); -+ re->mii_bus = NULL; -+ return err; -+} -+ -+static void -+ramips_mdio_cleanup(struct raeth_priv *re) -+{ -+ if (!re->mii_bus) -+ return; -+ -+ mdiobus_unregister(re->mii_bus); -+ of_node_put(re->mii_bus->dev.of_node); -+ kfree(re->mii_bus); -+} -+ -+static void -+ramips_phy_link_adjust(struct net_device *dev) -+{ -+ struct raeth_priv *re = netdev_priv(dev); -+ struct phy_device *phydev = re->phy_dev; -+ unsigned long flags; -+ int status_change = 0; -+ -+ spin_lock_irqsave(&re->phy_lock, flags); -+ -+ if (phydev->link) -+ if (re->duplex != phydev->duplex || -+ re->speed != phydev->speed) -+ status_change = 1; -+ -+ if (phydev->link != re->link) -+ status_change = 1; -+ -+ re->link = phydev->link; -+ re->duplex = phydev->duplex; -+ re->speed = phydev->speed; -+ -+ if (status_change) -+ ramips_link_adjust(re); -+ -+ spin_unlock_irqrestore(&re->phy_lock, flags); -+} -+ -+static int -+ramips_phy_connect_by_node(struct raeth_priv *re, struct device_node *phy_node) -+{ -+ struct phy_device *phydev; -+ int phy_mode; -+ -+ phy_mode = of_get_phy_mode(re->of_node); -+ if (phy_mode < 0) { -+ dev_err(re->parent, "incorrect phy-mode\n"); -+ return -EINVAL; -+ } -+ -+ phydev = of_phy_connect(re->netdev, phy_node, ramips_phy_link_adjust, -+ 0, phy_mode); -+ if (IS_ERR(phydev)) { -+ dev_err(re->parent, "could not connect to PHY\n"); -+ return PTR_ERR(re->phy_dev); -+ } -+ -+ phydev->supported &= PHY_GBIT_FEATURES; -+ phydev->advertising = phydev->supported; -+ -+ dev_info(re->parent, -+ "connected to PHY at %s [uid=%08x, driver=%s]\n", -+ dev_name(&phydev->dev), phydev->phy_id, -+ phydev->drv->name); -+ -+ re->phy_dev = phydev; -+ re->link = 0; -+ re->speed = 0; -+ re->duplex = -1; -+ re->rx_fc = 0; -+ re->tx_fc = 0; -+ -+ return 0; -+} -+ -+static int -+ramips_phy_connect_fixed(struct raeth_priv *re, const __be32 *link, int size) -+{ -+ if (size != (4 * sizeof(*link))) { -+ dev_err(re->parent, "invalid fixed-link property\n"); -+ return -EINVAL; -+ } -+ -+ re->speed = be32_to_cpup(link++); -+ re->duplex = be32_to_cpup(link++); -+ re->tx_fc = be32_to_cpup(link++); -+ re->rx_fc = be32_to_cpup(link++); -+ -+ switch (re->speed) { -+ case SPEED_10: -+ case SPEED_100: -+ case SPEED_1000: -+ break; -+ default: -+ dev_err(re->parent, "invalid link speed: %d\n", re->speed); -+ return -EINVAL; -+ } -+ -+ dev_info(re->parent, "using fixed link parameters\n"); -+ return 0; -+} -+ -+static int -+ramips_phy_connect(struct raeth_priv *re) -+{ -+ struct device_node *phy_node; -+ const __be32 *p32; -+ int size; -+ -+ phy_node = of_parse_phandle(re->of_node, "phy-handle", 0); -+ if (phy_node) -+ return ramips_phy_connect_by_node(re, phy_node); -+ -+ p32 = of_get_property(re->of_node, "ralink,fixed-link", &size); -+ if (p32) -+ return ramips_phy_connect_fixed(re, p32, size); -+ -+ dev_err(re->parent, "unable to get connection type\n"); -+ return -EINVAL; -+} -+ -+static void -+ramips_phy_disconnect(struct raeth_priv *re) -+{ -+ if (re->phy_dev) -+ phy_disconnect(re->phy_dev); -+} -+ -+static void -+ramips_phy_start(struct raeth_priv *re) -+{ -+ unsigned long flags; -+ -+ if (re->phy_dev) { -+ phy_start(re->phy_dev); -+ } else { -+ spin_lock_irqsave(&re->phy_lock, flags); -+ re->link = 1; -+ ramips_link_adjust(re); -+ spin_unlock_irqrestore(&re->phy_lock, flags); -+ } -+} -+ -+static void -+ramips_phy_stop(struct raeth_priv *re) -+{ -+ unsigned long flags; -+ -+ if (re->phy_dev) -+ phy_stop(re->phy_dev); -+ -+ spin_lock_irqsave(&re->phy_lock, flags); -+ re->link = 0; -+ ramips_link_adjust(re); -+ spin_unlock_irqrestore(&re->phy_lock, flags); -+} -+#else -+static inline int -+ramips_mdio_init(struct raeth_priv *re) -+{ -+ return 0; -+} -+ -+static inline void -+ramips_mdio_cleanup(struct raeth_priv *re) -+{ -+} -+ -+static inline int -+ramips_phy_connect(struct raeth_priv *re) -+{ -+ return 0; -+} -+ -+static inline void -+ramips_phy_disconnect(struct raeth_priv *re) -+{ -+} -+ -+static inline void -+ramips_phy_start(struct raeth_priv *re) -+{ -+} -+ -+static inline void -+ramips_phy_stop(struct raeth_priv *re) -+{ -+} -+#endif /* CONFIG_SOC_RT288X || CONFIG_SOC_RT3883 */ -+ -+static void -+ramips_ring_free(struct raeth_priv *re) -+{ -+ int len; -+ int i; -+ -+ if (re->rx_info) { -+ for (i = 0; i < NUM_RX_DESC; i++) { -+ struct raeth_rx_info *rxi; -+ -+ rxi = &re->rx_info[i]; -+ if (rxi->rx_skb) -+ dev_kfree_skb_any(rxi->rx_skb); -+ } -+ kfree(re->rx_info); -+ } -+ -+ if (re->rx) { -+ len = NUM_RX_DESC * sizeof(struct ramips_rx_dma); -+ dma_free_coherent(&re->netdev->dev, len, re->rx, -+ re->rx_desc_dma); -+ } -+ -+ if (re->tx) { -+ len = NUM_TX_DESC * sizeof(struct ramips_tx_dma); -+ dma_free_coherent(&re->netdev->dev, len, re->tx, -+ re->tx_desc_dma); -+ } -+ -+ kfree(re->tx_info); -+} -+ -+static int -+ramips_ring_alloc(struct raeth_priv *re) -+{ -+ int len; -+ int err = -ENOMEM; -+ int i; -+ -+ re->tx_info = kzalloc(NUM_TX_DESC * sizeof(struct raeth_tx_info), -+ GFP_ATOMIC); -+ if (!re->tx_info) -+ goto err_cleanup; -+ -+ re->rx_info = kzalloc(NUM_RX_DESC * sizeof(struct raeth_rx_info), -+ GFP_ATOMIC); -+ if (!re->rx_info) -+ goto err_cleanup; -+ -+ /* allocate tx ring */ -+ len = NUM_TX_DESC * sizeof(struct ramips_tx_dma); -+ re->tx = dma_alloc_coherent(&re->netdev->dev, len, -+ &re->tx_desc_dma, GFP_ATOMIC); -+ if (!re->tx) -+ goto err_cleanup; -+ -+ /* allocate rx ring */ -+ len = NUM_RX_DESC * sizeof(struct ramips_rx_dma); -+ re->rx = dma_alloc_coherent(&re->netdev->dev, len, -+ &re->rx_desc_dma, GFP_ATOMIC); -+ if (!re->rx) -+ goto err_cleanup; -+ -+ for (i = 0; i < NUM_RX_DESC; i++) { -+ struct sk_buff *skb; -+ -+ skb = ramips_alloc_skb(re); -+ if (!skb) -+ goto err_cleanup; -+ -+ re->rx_info[i].rx_skb = skb; -+ } -+ -+ return 0; -+ -+err_cleanup: -+ ramips_ring_free(re); -+ return err; -+} -+ -+static void -+ramips_setup_dma(struct raeth_priv *re) -+{ -+ ramips_fe_twr(re->tx_desc_dma, RAETH_REG_TX_BASE_PTR0); -+ ramips_fe_twr(NUM_TX_DESC, RAETH_REG_TX_MAX_CNT0); -+ ramips_fe_twr(0, RAETH_REG_TX_CTX_IDX0); -+ ramips_fe_twr(RAMIPS_PST_DTX_IDX0, RAETH_REG_PDMA_RST_CFG); -+ -+ ramips_fe_twr(re->rx_desc_dma, RAETH_REG_RX_BASE_PTR0); -+ ramips_fe_twr(NUM_RX_DESC, RAETH_REG_RX_MAX_CNT0); -+ ramips_fe_twr((NUM_RX_DESC - 1), RAETH_REG_RX_CALC_IDX0); -+ ramips_fe_twr(RAMIPS_PST_DRX_IDX0, RAETH_REG_PDMA_RST_CFG); -+} -+ -+static int -+ramips_eth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) -+{ -+ struct raeth_priv *re = netdev_priv(dev); -+ struct raeth_tx_info *txi, *txi_next; -+ struct ramips_tx_dma *txd, *txd_next; -+ unsigned long tx; -+ unsigned int tx_next; -+ dma_addr_t mapped_addr; -+ -+ if (re->min_pkt_len) { -+ if (skb->len < re->min_pkt_len) { -+ if (skb_padto(skb, re->min_pkt_len)) { -+ printk(KERN_ERR -+ "ramips_eth: skb_padto failed\n"); -+ kfree_skb(skb); -+ return 0; -+ } -+ skb_put(skb, re->min_pkt_len - skb->len); -+ } -+ } -+ -+ dev->trans_start = jiffies; -+ mapped_addr = dma_map_single(&re->netdev->dev, skb->data, skb->len, -+ DMA_TO_DEVICE); -+ -+ spin_lock(&re->page_lock); -+ tx = ramips_fe_trr(RAETH_REG_TX_CTX_IDX0); -+ tx_next = (tx + 1) % NUM_TX_DESC; -+ -+ txi = &re->tx_info[tx]; -+ txd = txi->tx_desc; -+ txi_next = &re->tx_info[tx_next]; -+ txd_next = txi_next->tx_desc; -+ -+ if ((txi->tx_skb) || (txi_next->tx_skb) || -+ !(txd->txd2 & TX_DMA_DONE) || -+ !(txd_next->txd2 & TX_DMA_DONE)) -+ goto out; -+ -+ txi->tx_skb = skb; -+ -+ txd->txd1 = (unsigned int) mapped_addr; -+ wmb(); -+ txd->txd2 = TX_DMA_LSO | TX_DMA_PLEN0(skb->len); -+ dev->stats.tx_packets++; -+ dev->stats.tx_bytes += skb->len; -+ ramips_fe_twr(tx_next, RAETH_REG_TX_CTX_IDX0); -+ netdev_sent_queue(dev, skb->len); -+ spin_unlock(&re->page_lock); -+ return NETDEV_TX_OK; -+ -+ out: -+ spin_unlock(&re->page_lock); -+ dev->stats.tx_dropped++; -+ kfree_skb(skb); -+ return NETDEV_TX_OK; -+} -+ -+static void -+ramips_eth_rx_hw(unsigned long ptr) -+{ -+ struct net_device *dev = (struct net_device *) ptr; -+ struct raeth_priv *re = netdev_priv(dev); -+ int rx; -+ int max_rx = 16; -+ -+ rx = ramips_fe_trr(RAETH_REG_RX_CALC_IDX0); -+ -+ while (max_rx) { -+ struct raeth_rx_info *rxi; -+ struct ramips_rx_dma *rxd; -+ struct sk_buff *rx_skb, *new_skb; -+ int pktlen; -+ -+ rx = (rx + 1) % NUM_RX_DESC; -+ -+ rxi = &re->rx_info[rx]; -+ rxd = rxi->rx_desc; -+ if (!(rxd->rxd2 & RX_DMA_DONE)) -+ break; -+ -+ rx_skb = rxi->rx_skb; -+ pktlen = RX_DMA_PLEN0(rxd->rxd2); -+ -+ new_skb = ramips_alloc_skb(re); -+ /* Reuse the buffer on allocation failures */ -+ if (new_skb) { -+ dma_addr_t dma_addr; -+ -+ dma_unmap_single(&re->netdev->dev, rxi->rx_dma, -+ MAX_RX_LENGTH, DMA_FROM_DEVICE); -+ -+ skb_put(rx_skb, pktlen); -+ rx_skb->dev = dev; -+ rx_skb->protocol = eth_type_trans(rx_skb, dev); -+ rx_skb->ip_summed = CHECKSUM_NONE; -+ dev->stats.rx_packets++; -+ dev->stats.rx_bytes += pktlen; -+ netif_rx(rx_skb); -+ -+ rxi->rx_skb = new_skb; -+ -+ dma_addr = dma_map_single(&re->netdev->dev, -+ new_skb->data, -+ MAX_RX_LENGTH, -+ DMA_FROM_DEVICE); -+ rxi->rx_dma = dma_addr; -+ rxd->rxd1 = (unsigned int) dma_addr; -+ wmb(); -+ } else { -+ dev->stats.rx_dropped++; -+ } -+ -+ rxd->rxd2 = RX_DMA_LSO; -+ ramips_fe_twr(rx, RAETH_REG_RX_CALC_IDX0); -+ max_rx--; -+ } -+ -+ if (max_rx == 0) -+ tasklet_schedule(&re->rx_tasklet); -+ else -+ ramips_fe_int_enable(RX_DLY_INT); -+} -+ -+static void -+ramips_eth_tx_housekeeping(unsigned long ptr) -+{ -+ struct net_device *dev = (struct net_device*)ptr; -+ struct raeth_priv *re = netdev_priv(dev); -+ unsigned int bytes_compl = 0, pkts_compl = 0; -+ -+ spin_lock(&re->page_lock); -+ while (1) { -+ struct raeth_tx_info *txi; -+ struct ramips_tx_dma *txd; -+ -+ txi = &re->tx_info[re->skb_free_idx]; -+ txd = txi->tx_desc; -+ -+ if (!(txd->txd2 & TX_DMA_DONE) || !(txi->tx_skb)) -+ break; -+ -+ pkts_compl++; -+ bytes_compl += txi->tx_skb->len; -+ -+ dev_kfree_skb_irq(txi->tx_skb); -+ txi->tx_skb = NULL; -+ re->skb_free_idx++; -+ if (re->skb_free_idx >= NUM_TX_DESC) -+ re->skb_free_idx = 0; -+ } -+ netdev_completed_queue(dev, pkts_compl, bytes_compl); -+ spin_unlock(&re->page_lock); -+ -+ ramips_fe_int_enable(TX_DLY_INT); -+} -+ -+static void -+ramips_eth_timeout(struct net_device *dev) -+{ -+ struct raeth_priv *re = netdev_priv(dev); -+ -+ tasklet_schedule(&re->tx_housekeeping_tasklet); -+} -+ -+static irqreturn_t -+ramips_eth_irq(int irq, void *dev) -+{ -+ struct raeth_priv *re = netdev_priv(dev); -+ unsigned int status; -+ -+ status = ramips_fe_trr(RAETH_REG_FE_INT_STATUS); -+ status &= ramips_fe_trr(RAETH_REG_FE_INT_ENABLE); -+ -+ if (!status) -+ return IRQ_NONE; -+ -+ ramips_fe_twr(status, RAETH_REG_FE_INT_STATUS); -+ -+ if (status & RX_DLY_INT) { -+ ramips_fe_int_disable(RX_DLY_INT); -+ tasklet_schedule(&re->rx_tasklet); -+ } -+ -+ if (status & TX_DLY_INT) { -+ ramips_fe_int_disable(TX_DLY_INT); -+ tasklet_schedule(&re->tx_housekeeping_tasklet); -+ } -+ -+ raeth_debugfs_update_int_stats(re, status); -+ -+ return IRQ_HANDLED; -+} -+ -+static int -+ramips_eth_hw_init(struct net_device *dev) -+{ -+ struct raeth_priv *re = netdev_priv(dev); -+ int err; -+ -+ err = request_irq(dev->irq, ramips_eth_irq, IRQF_DISABLED, -+ dev_name(re->parent), dev); -+ if (err) -+ return err; -+ -+ err = ramips_ring_alloc(re); -+ if (err) -+ goto err_free_irq; -+ -+ ramips_ring_setup(re); -+ ramips_hw_set_macaddr(dev->dev_addr); -+ -+ ramips_setup_dma(re); -+ ramips_fe_wr((ramips_fe_rr(RAMIPS_FE_GLO_CFG) & -+ ~(RAMIPS_US_CYC_CNT_MASK << RAMIPS_US_CYC_CNT_SHIFT)) | -+ ((re->sys_freq / RAMIPS_US_CYC_CNT_DIVISOR) << RAMIPS_US_CYC_CNT_SHIFT), -+ RAMIPS_FE_GLO_CFG); -+ -+ tasklet_init(&re->tx_housekeeping_tasklet, ramips_eth_tx_housekeeping, -+ (unsigned long)dev); -+ tasklet_init(&re->rx_tasklet, ramips_eth_rx_hw, (unsigned long)dev); -+ -+ -+ ramips_fe_twr(RAMIPS_DELAY_INIT, RAETH_REG_DLY_INT_CFG); -+ ramips_fe_twr(TX_DLY_INT | RX_DLY_INT, RAETH_REG_FE_INT_ENABLE); -+ if (soc_is_rt5350()) { -+ ramips_fe_wr(ramips_fe_rr(RT5350_SDM_CFG) & -+ ~(RT5350_SDM_ICS_EN | RT5350_SDM_TCS_EN | RT5350_SDM_UCS_EN | 0xffff), -+ RT5350_SDM_CFG); -+ } else { -+ ramips_fe_wr(ramips_fe_rr(RAMIPS_GDMA1_FWD_CFG) & -+ ~(RAMIPS_GDM1_ICS_EN | RAMIPS_GDM1_TCS_EN | RAMIPS_GDM1_UCS_EN | 0xffff), -+ RAMIPS_GDMA1_FWD_CFG); -+ ramips_fe_wr(ramips_fe_rr(RAMIPS_CDMA_CSG_CFG) & -+ ~(RAMIPS_ICS_GEN_EN | RAMIPS_TCS_GEN_EN | RAMIPS_UCS_GEN_EN), -+ RAMIPS_CDMA_CSG_CFG); -+ ramips_fe_wr(RAMIPS_PSE_FQFC_CFG_INIT, RAMIPS_PSE_FQ_CFG); -+ } -+ ramips_fe_wr(1, RAMIPS_FE_RST_GL); -+ ramips_fe_wr(0, RAMIPS_FE_RST_GL); -+ -+ return 0; -+ -+err_free_irq: -+ free_irq(dev->irq, dev); -+ return err; -+} -+ -+static int -+ramips_eth_open(struct net_device *dev) -+{ -+ struct raeth_priv *re = netdev_priv(dev); -+ -+ ramips_fe_twr((ramips_fe_trr(RAETH_REG_PDMA_GLO_CFG) & 0xff) | -+ (RAMIPS_TX_WB_DDONE | RAMIPS_RX_DMA_EN | -+ RAMIPS_TX_DMA_EN | RAMIPS_PDMA_SIZE_4DWORDS), -+ RAETH_REG_PDMA_GLO_CFG); -+ ramips_phy_start(re); -+ netif_start_queue(dev); -+ return 0; -+} -+ -+static int -+ramips_eth_stop(struct net_device *dev) -+{ -+ struct raeth_priv *re = netdev_priv(dev); -+ -+ ramips_fe_twr(ramips_fe_trr(RAETH_REG_PDMA_GLO_CFG) & -+ ~(RAMIPS_TX_WB_DDONE | RAMIPS_RX_DMA_EN | RAMIPS_TX_DMA_EN), -+ RAETH_REG_PDMA_GLO_CFG); -+ -+ netif_stop_queue(dev); -+ ramips_phy_stop(re); -+ RADEBUG("ramips_eth: stopped\n"); -+ return 0; -+} -+ -+static int __init -+ramips_eth_probe(struct net_device *dev) -+{ -+ struct raeth_priv *re = netdev_priv(dev); -+ int err; -+ -+ BUG_ON(!re->reset_fe); -+ re->reset_fe(); -+ net_srandom(jiffies); -+ memcpy(dev->dev_addr, re->mac, ETH_ALEN); -+ of_get_mac_address_mtd(re->of_node, dev->dev_addr); -+ ether_setup(dev); -+ dev->mtu = 1500; -+ dev->watchdog_timeo = TX_TIMEOUT; -+ spin_lock_init(&re->page_lock); -+ spin_lock_init(&re->phy_lock); -+ -+ err = ramips_mdio_init(re); -+ if (err) -+ return err; -+ -+ err = ramips_phy_connect(re); -+ if (err) -+ goto err_mdio_cleanup; -+ -+ err = raeth_debugfs_init(re); -+ if (err) -+ goto err_phy_disconnect; -+ -+ err = ramips_eth_hw_init(dev); -+ if (err) -+ goto err_debugfs; -+ -+ return 0; -+ -+err_debugfs: -+ raeth_debugfs_exit(re); -+err_phy_disconnect: -+ ramips_phy_disconnect(re); -+err_mdio_cleanup: -+ ramips_mdio_cleanup(re); -+ return err; -+} -+ -+static void -+ramips_eth_uninit(struct net_device *dev) -+{ -+ struct raeth_priv *re = netdev_priv(dev); -+ -+ raeth_debugfs_exit(re); -+ ramips_phy_disconnect(re); -+ ramips_mdio_cleanup(re); -+ ramips_fe_twr(0, RAETH_REG_FE_INT_ENABLE); -+ free_irq(dev->irq, dev); -+ tasklet_kill(&re->tx_housekeeping_tasklet); -+ tasklet_kill(&re->rx_tasklet); -+ ramips_ring_cleanup(re); -+ ramips_ring_free(re); -+} -+ -+static const struct net_device_ops ramips_eth_netdev_ops = { -+ .ndo_init = ramips_eth_probe, -+ .ndo_uninit = ramips_eth_uninit, -+ .ndo_open = ramips_eth_open, -+ .ndo_stop = ramips_eth_stop, -+ .ndo_start_xmit = ramips_eth_hard_start_xmit, -+ .ndo_tx_timeout = ramips_eth_timeout, -+ .ndo_change_mtu = eth_change_mtu, -+ .ndo_set_mac_address = eth_mac_addr, -+ .ndo_validate_addr = eth_validate_addr, -+}; -+ -+#ifdef CONFIG_SOC_RT305X -+static void rt305x_fe_reset(void) -+{ -+#define RT305X_RESET_FE BIT(21) -+#define RT305X_RESET_ESW BIT(23) -+#define SYSC_REG_RESET_CTRL 0x034 -+ u32 reset_bits = RT305X_RESET_FE; -+ -+ if (soc_is_rt5350()) -+ reset_bits |= RT305X_RESET_ESW; -+ rt_sysc_w32(reset_bits, SYSC_REG_RESET_CTRL); -+ rt_sysc_w32(0, SYSC_REG_RESET_CTRL); -+} -+ -+struct ramips_soc_data rt3050_data = { -+ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, -+ .reset_fe = rt305x_fe_reset, -+ .min_pkt_len = 64, -+}; -+ -+static const struct of_device_id ralink_eth_match[] = { -+ { .compatible = "ralink,rt3050-eth", .data = &rt3050_data }, -+ {}, -+}; -+#else -+static void rt3883_fe_reset(void) -+{ -+#define RT3883_SYSC_REG_RSTCTRL 0x34 -+#define RT3883_RSTCTRL_FE BIT(21) -+ u32 t; -+ -+ t = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL); -+ t |= RT3883_RSTCTRL_FE; -+ rt_sysc_w32(t , RT3883_SYSC_REG_RSTCTRL); -+ -+ t &= ~RT3883_RSTCTRL_FE; -+ rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL); -+} -+ -+struct ramips_soc_data rt3883_data = { -+ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, -+ .reset_fe = rt3883_fe_reset, -+ .min_pkt_len = 64, -+}; -+ -+static const struct of_device_id ralink_eth_match[] = { -+ { .compatible = "ralink,rt3883-eth", .data = &rt3883_data }, -+ {}, -+}; -+#endif -+MODULE_DEVICE_TABLE(of, ralink_eth_match); -+ -+static int -+ramips_eth_plat_probe(struct platform_device *plat) -+{ -+ struct raeth_priv *re; -+ struct resource *res; -+ struct clk *clk; -+ int err; -+ const struct of_device_id *match; -+ const struct ramips_soc_data *soc = NULL; -+ -+ match = of_match_device(ralink_eth_match, &plat->dev); -+ if (match) -+ soc = (const struct ramips_soc_data *) match->data; -+ -+ if (!soc) { -+ dev_err(&plat->dev, "no platform data specified\n"); -+ return -EINVAL; -+ } -+ -+ res = platform_get_resource(plat, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(&plat->dev, "no memory resource found\n"); -+ return -ENXIO; -+ } -+ -+ ramips_fe_base = ioremap_nocache(res->start, res->end - res->start + 1); -+ if (!ramips_fe_base) -+ return -ENOMEM; -+ -+ ramips_dev = alloc_etherdev(sizeof(struct raeth_priv)); -+ if (!ramips_dev) { -+ dev_err(&plat->dev, "alloc_etherdev failed\n"); -+ err = -ENOMEM; -+ goto err_unmap; -+ } -+ -+ strcpy(ramips_dev->name, "eth%d"); -+ ramips_dev->irq = platform_get_irq(plat, 0); -+ if (ramips_dev->irq < 0) { -+ dev_err(&plat->dev, "no IRQ resource found\n"); -+ err = -ENXIO; -+ goto err_free_dev; -+ } -+ ramips_dev->addr_len = ETH_ALEN; -+ ramips_dev->base_addr = (unsigned long)ramips_fe_base; -+ ramips_dev->netdev_ops = &ramips_eth_netdev_ops; -+ -+ re = netdev_priv(ramips_dev); -+ -+ clk = clk_get(&plat->dev, NULL); -+ if (IS_ERR(clk)) -+ panic("unable to get SYS clock, err=%ld", PTR_ERR(clk)); -+ re->sys_freq = clk_get_rate(clk); -+ -+ re->netdev = ramips_dev; -+ re->of_node = plat->dev.of_node; -+ re->parent = &plat->dev; -+ memcpy(re->mac, soc->mac, 6); -+ re->reset_fe = soc->reset_fe; -+ re->min_pkt_len = soc->min_pkt_len; -+ -+ err = register_netdev(ramips_dev); -+ if (err) { -+ dev_err(&plat->dev, "error bringing up device\n"); -+ goto err_free_dev; -+ } -+ -+ netdev_info(ramips_dev, "done loading\n"); -+ return 0; -+ -+ err_free_dev: -+ kfree(ramips_dev); -+ err_unmap: -+ iounmap(ramips_fe_base); -+ return err; -+} -+ -+static int -+ramips_eth_plat_remove(struct platform_device *plat) -+{ -+ unregister_netdev(ramips_dev); -+ free_netdev(ramips_dev); -+ RADEBUG("ramips_eth: unloaded\n"); -+ return 0; -+} -+ -+ -+ -+static struct platform_driver ramips_eth_driver = { -+ .probe = ramips_eth_plat_probe, -+ .remove = ramips_eth_plat_remove, -+ .driver = { -+ .name = "ramips_eth", -+ .owner = THIS_MODULE, -+ .of_match_table = ralink_eth_match -+ }, -+}; -+ -+static int __init -+ramips_eth_init(void) -+{ -+ int ret; -+ -+ ret = raeth_debugfs_root_init(); -+ if (ret) -+ goto err_out; -+ -+ ret = rt305x_esw_init(); -+ if (ret) -+ goto err_debugfs_exit; -+ -+ ret = platform_driver_register(&ramips_eth_driver); -+ if (ret) { -+ printk(KERN_ERR -+ "ramips_eth: Error registering platfom driver!\n"); -+ goto esw_cleanup; -+ } -+ -+ return 0; -+ -+esw_cleanup: -+ rt305x_esw_exit(); -+err_debugfs_exit: -+ raeth_debugfs_root_exit(); -+err_out: -+ return ret; -+} -+ -+static void __exit -+ramips_eth_cleanup(void) -+{ -+ platform_driver_unregister(&ramips_eth_driver); -+ rt305x_esw_exit(); -+ raeth_debugfs_root_exit(); -+} -+ -+module_init(ramips_eth_init); -+module_exit(ramips_eth_cleanup); -+ -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("John Crispin "); -+MODULE_DESCRIPTION("ethernet driver for ramips boards"); diff --git a/target/linux/ramips/patches-3.8/0137-watchdog-adds-ralink-wdt.patch b/target/linux/ramips/patches-3.8/0137-watchdog-adds-ralink-wdt.patch deleted file mode 100644 index 3b7246ffdb..0000000000 --- a/target/linux/ramips/patches-3.8/0137-watchdog-adds-ralink-wdt.patch +++ /dev/null @@ -1,395 +0,0 @@ -From 14c1b064274d28cf88113a685c58374a515f3018 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Mon, 22 Apr 2013 23:23:07 +0200 -Subject: [PATCH 137/137] watchdog: adds ralink wdt - -Adds the watchdog driver for ralink SoC. - -Signed-off-by: John Crispin ---- - drivers/watchdog/Kconfig | 6 + - drivers/watchdog/Makefile | 1 + - drivers/watchdog/ralink_wdt.c | 352 +++++++++++++++++++++++++++++++++++++++++ - 3 files changed, 359 insertions(+) - create mode 100644 drivers/watchdog/ralink_wdt.c - ---- a/drivers/watchdog/Kconfig -+++ b/drivers/watchdog/Kconfig -@@ -1077,6 +1077,12 @@ config LANTIQ_WDT - help - Hardware driver for the Lantiq SoC Watchdog Timer. - -+config RALINK_WDT -+ tristate "Ralink SoC watchdog" -+ depends on RALINK -+ help -+ Hardware driver for the Ralink SoC Watchdog Timer. -+ - # PARISC Architecture - - # POWERPC Architecture ---- a/drivers/watchdog/Makefile -+++ b/drivers/watchdog/Makefile -@@ -132,6 +132,7 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o - obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o - octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o - obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o -+obj-$(CONFIG_RALINK_WDT) += ralink_wdt.o - - # PARISC Architecture - ---- /dev/null -+++ b/drivers/watchdog/ralink_wdt.c -@@ -0,0 +1,352 @@ -+/* -+ * Ralink RT288X/RT305X built-in hardware watchdog timer -+ * -+ * Copyright (C) 2011 Gabor Juhos -+ * -+ * This driver was based on: drivers/watchdog/ixp4xx_wdt.c -+ * Author: Deepak Saxena -+ * Copyright 2004 (c) MontaVista, Software, Inc. -+ * -+ * which again was based on sa1100 driver, -+ * Copyright (C) 2000 Oleg Drokin -+ * -+ * parts of the driver are based on Ralink's 2.6.21 BSP -+ * -+ * This program is free software; you can redistribute it and/or modify it -+ * under the terms of the GNU General Public License version 2 as published -+ * by the Free Software Foundation. -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define DRIVER_NAME "ralink-wdt" -+ -+#define RALINK_WDT_TIMEOUT 30 /* seconds */ -+#define RALINK_WDT_PRESCALE 65536 -+ -+#define TIMER_REG_TMR1LOAD 0x00 -+#define TIMER_REG_TMR1CTL 0x08 -+ -+#define TMRSTAT_TMR1RST BIT(5) -+ -+#define TMR1CTL_ENABLE BIT(7) -+#define TMR1CTL_MODE_SHIFT 4 -+#define TMR1CTL_MODE_MASK 0x3 -+#define TMR1CTL_MODE_FREE_RUNNING 0x0 -+#define TMR1CTL_MODE_PERIODIC 0x1 -+#define TMR1CTL_MODE_TIMEOUT 0x2 -+#define TMR1CTL_MODE_WDT 0x3 -+#define TMR1CTL_PRESCALE_MASK 0xf -+#define TMR1CTL_PRESCALE_65536 0xf -+ -+static int nowayout = WATCHDOG_NOWAYOUT; -+module_param(nowayout, int, 0); -+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " -+ "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -+ -+static int ralink_wdt_timeout = RALINK_WDT_TIMEOUT; -+module_param_named(timeout, ralink_wdt_timeout, int, 0); -+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, 0 means use maximum " -+ "(default=" __MODULE_STRING(RALINK_WDT_TIMEOUT) "s)"); -+ -+static unsigned long ralink_wdt_flags; -+ -+#define WDT_FLAGS_BUSY 0 -+#define WDT_FLAGS_EXPECT_CLOSE 1 -+ -+static struct clk *ralink_wdt_clk; -+static unsigned long ralink_wdt_freq; -+static int ralink_wdt_max_timeout; -+static void __iomem *ralink_wdt_base; -+ -+static inline void rt_wdt_w32(unsigned reg, u32 val) -+{ -+ __raw_writel(val, ralink_wdt_base + reg); -+} -+ -+static inline u32 rt_wdt_r32(unsigned reg) -+{ -+ return __raw_readl(ralink_wdt_base + reg); -+} -+ -+static inline void ralink_wdt_keepalive(void) -+{ -+ rt_wdt_w32(TIMER_REG_TMR1LOAD, ralink_wdt_timeout * ralink_wdt_freq); -+} -+ -+static inline void ralink_wdt_enable(void) -+{ -+ u32 t; -+ -+ ralink_wdt_keepalive(); -+ -+ t = rt_wdt_r32(TIMER_REG_TMR1CTL); -+ t |= TMR1CTL_ENABLE; -+ rt_wdt_w32(TIMER_REG_TMR1CTL, t); -+} -+ -+static inline void ralink_wdt_disable(void) -+{ -+ u32 t; -+ -+ ralink_wdt_keepalive(); -+ -+ t = rt_wdt_r32(TIMER_REG_TMR1CTL); -+ t &= ~TMR1CTL_ENABLE; -+ rt_wdt_w32(TIMER_REG_TMR1CTL, t); -+} -+ -+static int ralink_wdt_set_timeout(int val) -+{ -+ if (val < 1 || val > ralink_wdt_max_timeout) { -+ pr_warn(DRIVER_NAME -+ ": timeout value %d must be 0 < timeout <= %d, using %d\n", -+ val, ralink_wdt_max_timeout, ralink_wdt_timeout); -+ return -EINVAL; -+ } -+ -+ ralink_wdt_timeout = val; -+ ralink_wdt_keepalive(); -+ -+ return 0; -+} -+ -+static int ralink_wdt_open(struct inode *inode, struct file *file) -+{ -+ u32 t; -+ -+ if (test_and_set_bit(WDT_FLAGS_BUSY, &ralink_wdt_flags)) -+ return -EBUSY; -+ -+ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags); -+ -+ t = rt_wdt_r32(TIMER_REG_TMR1CTL); -+ t &= ~(TMR1CTL_MODE_MASK << TMR1CTL_MODE_SHIFT | -+ TMR1CTL_PRESCALE_MASK); -+ t |= (TMR1CTL_MODE_WDT << TMR1CTL_MODE_SHIFT | -+ TMR1CTL_PRESCALE_65536); -+ rt_wdt_w32(TIMER_REG_TMR1CTL, t); -+ -+ ralink_wdt_enable(); -+ -+ return nonseekable_open(inode, file); -+} -+ -+static int ralink_wdt_release(struct inode *inode, struct file *file) -+{ -+ if (test_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags)) -+ ralink_wdt_disable(); -+ else { -+ pr_crit(DRIVER_NAME ": device closed unexpectedly, " -+ "watchdog timer will not stop!\n"); -+ ralink_wdt_keepalive(); -+ } -+ -+ clear_bit(WDT_FLAGS_BUSY, &ralink_wdt_flags); -+ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags); -+ -+ return 0; -+} -+ -+static ssize_t rt_wdt_w32ite(struct file *file, const char *data, -+ size_t len, loff_t *ppos) -+{ -+ if (len) { -+ if (!nowayout) { -+ size_t i; -+ -+ clear_bit(WDT_FLAGS_EXPECT_CLOSE, &ralink_wdt_flags); -+ -+ for (i = 0; i != len; i++) { -+ char c; -+ -+ if (get_user(c, data + i)) -+ return -EFAULT; -+ -+ if (c == 'V') -+ set_bit(WDT_FLAGS_EXPECT_CLOSE, -+ &ralink_wdt_flags); -+ } -+ } -+ -+ ralink_wdt_keepalive(); -+ } -+ -+ return len; -+} -+ -+static const struct watchdog_info ralink_wdt_info = { -+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | -+ WDIOF_MAGICCLOSE, -+ .firmware_version = 0, -+ .identity = "RALINK watchdog", -+}; -+ -+static long ralink_wdt_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ void __user *argp = (void __user *)arg; -+ int __user *p = argp; -+ int err; -+ int t; -+ -+ switch (cmd) { -+ case WDIOC_GETSUPPORT: -+ err = copy_to_user(argp, &ralink_wdt_info, -+ sizeof(ralink_wdt_info)) ? -EFAULT : 0; -+ break; -+ -+ case WDIOC_GETSTATUS: -+ err = put_user(0, p); -+ break; -+ -+ case WDIOC_KEEPALIVE: -+ ralink_wdt_keepalive(); -+ err = 0; -+ break; -+ -+ case WDIOC_SETTIMEOUT: -+ err = get_user(t, p); -+ if (err) -+ break; -+ -+ err = ralink_wdt_set_timeout(t); -+ if (err) -+ break; -+ -+ /* fallthrough */ -+ case WDIOC_GETTIMEOUT: -+ err = put_user(ralink_wdt_timeout, p); -+ break; -+ -+ default: -+ err = -ENOTTY; -+ break; -+ } -+ -+ return err; -+} -+ -+static const struct file_operations ralink_wdt_fops = { -+ .owner = THIS_MODULE, -+ .llseek = no_llseek, -+ .write = rt_wdt_w32ite, -+ .unlocked_ioctl = ralink_wdt_ioctl, -+ .open = ralink_wdt_open, -+ .release = ralink_wdt_release, -+}; -+ -+static struct miscdevice ralink_wdt_miscdev = { -+ .minor = WATCHDOG_MINOR, -+ .name = "watchdog", -+ .fops = &ralink_wdt_fops, -+}; -+ -+static int ralink_wdt_probe(struct platform_device *pdev) -+{ -+ struct resource *res; -+ int err; -+ -+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!res) { -+ dev_err(&pdev->dev, "no memory resource found\n"); -+ return -EINVAL; -+ } -+ -+ ralink_wdt_base = ioremap(res->start, resource_size(res)); -+ if (!ralink_wdt_base) -+ return -ENOMEM; -+ -+ ralink_wdt_clk = clk_get(&pdev->dev, NULL); -+ if (IS_ERR(ralink_wdt_clk)) { -+ err = PTR_ERR(ralink_wdt_clk); -+ goto err_unmap; -+ } -+ -+ err = clk_enable(ralink_wdt_clk); -+ if (err) -+ goto err_clk_put; -+ -+ ralink_wdt_freq = clk_get_rate(ralink_wdt_clk) / RALINK_WDT_PRESCALE; -+ if (!ralink_wdt_freq) { -+ err = -EINVAL; -+ goto err_clk_disable; -+ } -+ -+ ralink_wdt_max_timeout = (0xfffful / ralink_wdt_freq); -+ if (ralink_wdt_timeout < 1 || -+ ralink_wdt_timeout > ralink_wdt_max_timeout) { -+ ralink_wdt_timeout = ralink_wdt_max_timeout; -+ dev_info(&pdev->dev, -+ "timeout value must be 0 < timeout <= %d, using %d\n", -+ ralink_wdt_max_timeout, ralink_wdt_timeout); -+ } -+ -+ err = misc_register(&ralink_wdt_miscdev); -+ if (err) { -+ dev_err(&pdev->dev, -+ "unable to register misc device, err=%d\n", err); -+ goto err_clk_disable; -+ } -+ -+ return 0; -+ -+err_clk_disable: -+ clk_disable(ralink_wdt_clk); -+err_clk_put: -+ clk_put(ralink_wdt_clk); -+err_unmap: -+ iounmap(ralink_wdt_base); -+ return err; -+} -+ -+static int ralink_wdt_remove(struct platform_device *pdev) -+{ -+ misc_deregister(&ralink_wdt_miscdev); -+ clk_disable(ralink_wdt_clk); -+ clk_put(ralink_wdt_clk); -+ iounmap(ralink_wdt_base); -+ return 0; -+} -+ -+static void ralink_wdt_shutdown(struct platform_device *pdev) -+{ -+ ralink_wdt_disable(); -+} -+ -+static const struct of_device_id ralink_wdt_match[] = { -+ { .compatible = "ralink,rt2880-wdt" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, ralink_wdt_match); -+ -+static struct platform_driver ralink_wdt_driver = { -+ .probe = ralink_wdt_probe, -+ .remove = ralink_wdt_remove, -+ .shutdown = ralink_wdt_shutdown, -+ .driver = { -+ .name = DRIVER_NAME, -+ .owner = THIS_MODULE, -+ .of_match_table = ralink_wdt_match, -+ }, -+}; -+ -+module_platform_driver(ralink_wdt_driver); -+ -+MODULE_DESCRIPTION("MediaTek/Ralink RT288X/RT305X hardware watchdog driver"); -+MODULE_AUTHOR("Gabor Juhos -Date: Sun, 17 Mar 2013 09:29:15 +0100 -Subject: [PATCH 201/208] owrt: OF: NET: add of_get_mac_address_mtd() - -Many embedded devices have information such as mac addresses stored inside mtd -devices. This patch allows us to add a property inside a node describing a -network interface. The new property points at a mtd partition with an offset -where the mac address can be found. - -Signed-off-by: John Crispin ---- - drivers/of/of_net.c | 37 +++++++++++++++++++++++++++++++++++++ - include/linux/of_net.h | 1 + - 2 files changed, 38 insertions(+) - ---- a/drivers/of/of_net.c -+++ b/drivers/of/of_net.c -@@ -10,6 +10,7 @@ - #include - #include - #include -+#include - - /** - * It maps 'enum phy_interface_t' found in include/linux/phy.h -@@ -92,3 +93,39 @@ const void *of_get_mac_address(struct de - return NULL; - } - EXPORT_SYMBOL(of_get_mac_address); -+ -+int of_get_mac_address_mtd(struct device_node *np, void *mac) -+{ -+ struct device_node *mtd_np = NULL; -+ size_t retlen; -+ int size, ret; -+ struct mtd_info *mtd; -+ const char *part; -+ const __be32 *list; -+ phandle phandle; -+ -+ list = of_get_property(np, "mtd-mac-address", &size); -+ if (!list || (size != (2 * sizeof(*list)))) -+ return -ENOENT; -+ -+ phandle = be32_to_cpup(list++); -+ if (phandle) -+ mtd_np = of_find_node_by_phandle(phandle); -+ -+ if (!mtd_np) -+ return -ENOENT; -+ -+ part = of_get_property(mtd_np, "label", NULL); -+ if (!part) -+ part = mtd_np->name; -+ -+ mtd = get_mtd_device_nm(part); -+ if (IS_ERR(mtd)) -+ return PTR_ERR(mtd); -+ -+ ret = mtd_read(mtd, be32_to_cpup(list), 6, &retlen, (u_char *) mac); -+ put_mtd_device(mtd); -+ -+ return ret; -+} -+EXPORT_SYMBOL_GPL(of_get_mac_address_mtd); ---- a/include/linux/of_net.h -+++ b/include/linux/of_net.h -@@ -11,6 +11,7 @@ - #include - extern const int of_get_phy_mode(struct device_node *np); - extern const void *of_get_mac_address(struct device_node *np); -+extern int of_get_mac_address_mtd(struct device_node *np, void *mac); - #endif - - #endif /* __LINUX_OF_NET_H */ diff --git a/target/linux/ramips/patches-3.8/0203-owrt-OF-USB-add-OF-binding-for-ehci-and-ohci-platfor.patch b/target/linux/ramips/patches-3.8/0203-owrt-OF-USB-add-OF-binding-for-ehci-and-ohci-platfor.patch deleted file mode 100644 index 13a45b2626..0000000000 --- a/target/linux/ramips/patches-3.8/0203-owrt-OF-USB-add-OF-binding-for-ehci-and-ohci-platfor.patch +++ /dev/null @@ -1,59 +0,0 @@ -From 3c99a50d689cb4811b13b9810d18c9316587216f Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Mon, 18 Mar 2013 20:51:21 +0100 -Subject: [PATCH 203/208] owrt: OF: USB: add OF binding for ehci and ohci - platform driver - -Make ohci-platform and ehci-platform loadable from OF. - -Signed-off-by: John Crispin ---- - drivers/usb/host/ehci-platform.c | 7 +++++++ - drivers/usb/host/ohci-platform.c | 7 +++++++ - 2 files changed, 14 insertions(+) - ---- a/drivers/usb/host/ehci-platform.c -+++ b/drivers/usb/host/ehci-platform.c -@@ -183,6 +183,12 @@ static int ehci_platform_resume(struct d - #define ehci_platform_resume NULL - #endif /* CONFIG_PM */ - -+static const struct of_device_id ehci_match_table[] = { -+ { .compatible = "ehci-platform" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, ehci_match_table); -+ - static const struct platform_device_id ehci_platform_table[] = { - { "ehci-platform", 0 }, - { } -@@ -203,6 +209,7 @@ static struct platform_driver ehci_platf - .owner = THIS_MODULE, - .name = "ehci-platform", - .pm = &ehci_platform_pm_ops, -+ .of_match_table = ehci_match_table, - } - }; - ---- a/drivers/usb/host/ohci-platform.c -+++ b/drivers/usb/host/ohci-platform.c -@@ -200,6 +200,12 @@ static int ohci_platform_resume(struct d - #define ohci_platform_resume NULL - #endif /* CONFIG_PM */ - -+static const struct of_device_id ohci_match_table[] = { -+ { .compatible = "ohci-platform" }, -+ {}, -+}; -+MODULE_DEVICE_TABLE(of, ohci_match_table); -+ - static const struct platform_device_id ohci_platform_table[] = { - { "ohci-platform", 0 }, - { } -@@ -220,5 +226,6 @@ static struct platform_driver ohci_platf - .owner = THIS_MODULE, - .name = "ohci-platform", - .pm = &ohci_platform_pm_ops, -+ .of_match_table = ohci_match_table, - } - }; diff --git a/target/linux/ramips/patches-3.8/0204-owrt-MIPS-ralink-add-usb-platform-support.patch b/target/linux/ramips/patches-3.8/0204-owrt-MIPS-ralink-add-usb-platform-support.patch deleted file mode 100644 index 0fedc91b77..0000000000 --- a/target/linux/ramips/patches-3.8/0204-owrt-MIPS-ralink-add-usb-platform-support.patch +++ /dev/null @@ -1,325 +0,0 @@ -From d7e679017ec92824145b275572f6ef83d461f076 Mon Sep 17 00:00:00 2001 -From: John Crispin -Date: Tue, 19 Mar 2013 09:26:22 +0100 -Subject: [PATCH 204/208] owrt: MIPS: ralink: add usb platform support - -Add code to load the platform ehci/ohci driver on Ralink SoC. For the usb core -to work we need to populate the platform_data during boot, prior to the usb -driver being loaded. - -Signed-off-by: John Crispin ---- - arch/mips/ralink/Makefile | 4 +- - arch/mips/ralink/common.h | 1 + - arch/mips/ralink/mt7620.c | 5 ++ - arch/mips/ralink/of.c | 1 + - arch/mips/ralink/rt305x-usb.c | 120 +++++++++++++++++++++++++++++++++++++++++ - arch/mips/ralink/rt3883-usb.c | 118 ++++++++++++++++++++++++++++++++++++++++ - 6 files changed, 247 insertions(+), 2 deletions(-) - create mode 100644 arch/mips/ralink/rt305x-usb.c - create mode 100644 arch/mips/ralink/rt3883-usb.c - ---- a/arch/mips/ralink/Makefile -+++ b/arch/mips/ralink/Makefile -@@ -9,8 +9,8 @@ - obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o timer.o - - obj-$(CONFIG_SOC_RT288X) += rt288x.o --obj-$(CONFIG_SOC_RT305X) += rt305x.o --obj-$(CONFIG_SOC_RT3883) += rt3883.o -+obj-$(CONFIG_SOC_RT305X) += rt305x.o rt305x-usb.o -+obj-$(CONFIG_SOC_RT3883) += rt3883.o rt3883-usb.o - obj-$(CONFIG_SOC_MT7620) += mt7620.o - - obj-$(CONFIG_EARLY_PRINTK) += early_printk.o ---- a/arch/mips/ralink/common.h -+++ b/arch/mips/ralink/common.h -@@ -51,5 +51,6 @@ extern void prom_soc_init(struct ralink_ - __iomem void *plat_of_remap_node(const char *node); - - void ralink_pinmux(void); -+void ralink_usb_platform(void); - - #endif /* _RALINK_COMMON_H__ */ ---- a/arch/mips/ralink/mt7620.c -+++ b/arch/mips/ralink/mt7620.c -@@ -140,6 +140,11 @@ struct ralink_pinmux rt_gpio_pinmux = { - .uart_mask = MT7620_GPIO_MODE_GPIO, - }; - -+void ralink_usb_platform(void) -+{ -+ -+} -+ - void __init ralink_clk_init(void) - { - unsigned long cpu_rate, sys_rate; ---- a/arch/mips/ralink/of.c -+++ b/arch/mips/ralink/of.c -@@ -111,6 +111,7 @@ static int __init plat_of_setup(void) - panic("failed to populate DT\n"); - - ralink_pinmux(); -+ ralink_usb_platform(); - - return 0; - } ---- /dev/null -+++ b/arch/mips/ralink/rt305x-usb.c -@@ -0,0 +1,120 @@ -+/* -+ * 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. -+ * -+ * Copyright (C) 2008-2011 Gabor Juhos -+ * Copyright (C) 2013 John Crispin -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+static atomic_t rt3352_usb_pwr_ref = ATOMIC_INIT(0); -+ -+static int rt3352_usb_power_on(struct platform_device *pdev) -+{ -+ -+ if (atomic_inc_return(&rt3352_usb_pwr_ref) == 1) { -+ u32 t; -+ -+ t = rt_sysc_r32(RT3352_SYSC_REG_USB_PS); -+ -+ /* enable clock for port0's and port1's phys */ -+ t = rt_sysc_r32(RT3352_SYSC_REG_CLKCFG1); -+ t |= RT3352_CLKCFG1_UPHY0_CLK_EN | RT3352_CLKCFG1_UPHY1_CLK_EN; -+ rt_sysc_w32(t, RT3352_SYSC_REG_CLKCFG1); -+ mdelay(500); -+ -+ /* pull USBHOST and USBDEV out from reset */ -+ t = rt_sysc_r32(RT3352_SYSC_REG_RSTCTRL); -+ t &= ~(RT3352_RSTCTRL_UHST | RT3352_RSTCTRL_UDEV); -+ rt_sysc_w32(t, RT3352_SYSC_REG_RSTCTRL); -+ mdelay(500); -+ -+ /* enable host mode */ -+ t = rt_sysc_r32(RT3352_SYSC_REG_SYSCFG1); -+ t |= RT3352_SYSCFG1_USB0_HOST_MODE; -+ rt_sysc_w32(t, RT3352_SYSC_REG_SYSCFG1); -+ -+ t = rt_sysc_r32(RT3352_SYSC_REG_USB_PS); -+ } -+ -+ return 0; -+} -+ -+static void rt3352_usb_power_off(struct platform_device *pdev) -+{ -+ if (atomic_dec_return(&rt3352_usb_pwr_ref) == 0) { -+ u32 t; -+ -+ /* put USBHOST and USBDEV into reset */ -+ t = rt_sysc_r32(RT3352_SYSC_REG_RSTCTRL); -+ t |= RT3352_RSTCTRL_UHST | RT3352_RSTCTRL_UDEV; -+ rt_sysc_w32(t, RT3352_SYSC_REG_RSTCTRL); -+ udelay(10000); -+ -+ /* disable clock for port0's and port1's phys*/ -+ t = rt_sysc_r32(RT3352_SYSC_REG_CLKCFG1); -+ t &= ~(RT3352_CLKCFG1_UPHY0_CLK_EN | RT3352_CLKCFG1_UPHY1_CLK_EN); -+ rt_sysc_w32(t, RT3352_SYSC_REG_CLKCFG1); -+ udelay(10000); -+ } -+} -+ -+static struct usb_ehci_pdata rt3352_ehci_data = { -+ .power_on = rt3352_usb_power_on, -+ .power_off = rt3352_usb_power_off, -+}; -+ -+static struct usb_ohci_pdata rt3352_ohci_data = { -+ .power_on = rt3352_usb_power_on, -+ .power_off = rt3352_usb_power_off, -+}; -+ -+static void ralink_add_usb(char *name, void *pdata, u64 *mask) -+{ -+ struct device_node *node; -+ struct platform_device *pdev; -+ -+ node = of_find_compatible_node(NULL, NULL, name); -+ if (!node) -+ return; -+ -+ pdev = of_find_device_by_node(node); -+ if (!pdev) -+ goto error_out; -+ -+ if (pdata) -+ pdev->dev.platform_data = pdata; -+ if (mask) { -+ pdev->dev.dma_mask = mask; -+ pdev->dev.coherent_dma_mask = *mask; -+ } -+ -+error_out: -+ of_node_put(node); -+} -+ -+static u64 rt3352_ohci_dmamask = DMA_BIT_MASK(32); -+static u64 rt3352_ehci_dmamask = DMA_BIT_MASK(32); -+ -+void ralink_usb_platform(void) -+{ -+ if (soc_is_rt3352() || soc_is_rt5350()) { -+ ralink_add_usb("ohci-platform", -+ &rt3352_ohci_data, &rt3352_ohci_dmamask); -+ ralink_add_usb("ehci-platform", -+ &rt3352_ehci_data, &rt3352_ehci_dmamask); -+ } -+} ---- /dev/null -+++ b/arch/mips/ralink/rt3883-usb.c -@@ -0,0 +1,118 @@ -+/* -+ * 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. -+ * -+ * Copyright (C) 2008-2011 Gabor Juhos -+ * Copyright (C) 2013 John Crispin -+ */ -+ -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+ -+static atomic_t rt3883_usb_pwr_ref = ATOMIC_INIT(0); -+ -+static int rt3883_usb_power_on(struct platform_device *pdev) -+{ -+ if (atomic_inc_return(&rt3883_usb_pwr_ref) == 1) { -+ u32 t; -+ -+ t = rt_sysc_r32(RT3883_SYSC_REG_USB_PS); -+ -+ /* enable clock for port0's and port1's phys */ -+ t = rt_sysc_r32(RT3883_SYSC_REG_CLKCFG1); -+ t |= RT3883_CLKCFG1_UPHY0_CLK_EN | RT3883_CLKCFG1_UPHY1_CLK_EN; -+ rt_sysc_w32(t, RT3883_SYSC_REG_CLKCFG1); -+ mdelay(500); -+ -+ /* pull USBHOST and USBDEV out from reset */ -+ t = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL); -+ t &= ~(RT3883_RSTCTRL_UHST | RT3883_RSTCTRL_UDEV); -+ rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL); -+ mdelay(500); -+ -+ /* enable host mode */ -+ t = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG1); -+ t |= RT3883_SYSCFG1_USB0_HOST_MODE; -+ rt_sysc_w32(t, RT3883_SYSC_REG_SYSCFG1); -+ -+ t = rt_sysc_r32(RT3883_SYSC_REG_USB_PS); -+ } -+ -+ return 0; -+} -+ -+static void rt3883_usb_power_off(struct platform_device *pdev) -+{ -+ if (atomic_dec_return(&rt3883_usb_pwr_ref) == 0) { -+ u32 t; -+ -+ /* put USBHOST and USBDEV into reset */ -+ t = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL); -+ t |= RT3883_RSTCTRL_UHST | RT3883_RSTCTRL_UDEV; -+ rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL); -+ udelay(10000); -+ -+ /* disable clock for port0's and port1's phys*/ -+ t = rt_sysc_r32(RT3883_SYSC_REG_CLKCFG1); -+ t &= ~(RT3883_CLKCFG1_UPHY0_CLK_EN | -+ RT3883_CLKCFG1_UPHY1_CLK_EN); -+ rt_sysc_w32(t, RT3883_SYSC_REG_CLKCFG1); -+ udelay(10000); -+ } -+} -+ -+static struct usb_ohci_pdata rt3883_ohci_data = { -+ .power_on = rt3883_usb_power_on, -+ .power_off = rt3883_usb_power_off, -+}; -+ -+static struct usb_ehci_pdata rt3883_ehci_data = { -+ .power_on = rt3883_usb_power_on, -+ .power_off = rt3883_usb_power_off, -+}; -+ -+static void ralink_add_usb(char *name, void *pdata, u64 *mask) -+{ -+ struct device_node *node; -+ struct platform_device *pdev; -+ -+ node = of_find_compatible_node(NULL, NULL, name); -+ if (!node) -+ return; -+ -+ pdev = of_find_device_by_node(node); -+ if (!pdev) -+ goto error_out; -+ -+ if (pdata) -+ pdev->dev.platform_data = pdata; -+ if (mask) { -+ pdev->dev.dma_mask = mask; -+ pdev->dev.coherent_dma_mask = *mask; -+ } -+ -+error_out: -+ of_node_put(node); -+} -+ -+static u64 rt3883_ohci_dmamask = DMA_BIT_MASK(32); -+static u64 rt3883_ehci_dmamask = DMA_BIT_MASK(32); -+ -+void ralink_usb_platform(void) -+{ -+ ralink_add_usb("ohci-platform", -+ &rt3883_ohci_data, &rt3883_ohci_dmamask); -+ ralink_add_usb("ehci-platform", -+ &rt3883_ehci_data, &rt3883_ehci_dmamask); -+} ---- a/arch/mips/ralink/rt288x.c -+++ b/arch/mips/ralink/rt288x.c -@@ -74,6 +74,11 @@ struct ralink_pinmux rt_gpio_pinmux = { - .wdt_reset = rt288x_wdt_reset, - }; - -+void ralink_usb_platform(void) -+{ -+ -+} -+ - void __init ralink_clk_init(void) - { - unsigned long cpu_rate; diff --git a/target/linux/ramips/patches-3.9/0100-MIPS-move-mips_-set-get-_machine_name-to-a-more-gene.patch b/target/linux/ramips/patches-3.9/0100-MIPS-move-mips_-set-get-_machine_name-to-a-more-gene.patch new file mode 100644 index 0000000000..89f40bf2e1 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0100-MIPS-move-mips_-set-get-_machine_name-to-a-more-gene.patch @@ -0,0 +1,167 @@ +From 67d6534cdc4f90e6998a79ac57d5318412f18486 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 11 Apr 2013 05:34:59 +0000 +Subject: [PATCH 100/164] MIPS: move mips_{set,get}_machine_name() to a more + generic place + +Previously this functionality was only available to users of the mips_machine +api. Moving the code to prom.c allows us to also add a OF wrapper. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5164/ +--- + arch/mips/include/asm/mips_machine.h | 4 ---- + arch/mips/include/asm/prom.h | 3 +++ + arch/mips/kernel/mips_machine.c | 21 --------------------- + arch/mips/kernel/proc.c | 2 +- + arch/mips/kernel/prom.c | 31 +++++++++++++++++++++++++++++++ + 5 files changed, 35 insertions(+), 26 deletions(-) + +diff --git a/arch/mips/include/asm/mips_machine.h b/arch/mips/include/asm/mips_machine.h +index 363bb35..9d00aeb 100644 +--- a/arch/mips/include/asm/mips_machine.h ++++ b/arch/mips/include/asm/mips_machine.h +@@ -42,13 +42,9 @@ extern long __mips_machines_end; + #ifdef CONFIG_MIPS_MACHINE + int mips_machtype_setup(char *id) __init; + void mips_machine_setup(void) __init; +-void mips_set_machine_name(const char *name) __init; +-char *mips_get_machine_name(void); + #else + static inline int mips_machtype_setup(char *id) { return 1; } + static inline void mips_machine_setup(void) { } +-static inline void mips_set_machine_name(const char *name) { } +-static inline char *mips_get_machine_name(void) { return NULL; } + #endif /* CONFIG_MIPS_MACHINE */ + + #endif /* __ASM_MIPS_MACHINE_H */ +diff --git a/arch/mips/include/asm/prom.h b/arch/mips/include/asm/prom.h +index 8808bf5..1e7e096 100644 +--- a/arch/mips/include/asm/prom.h ++++ b/arch/mips/include/asm/prom.h +@@ -48,4 +48,7 @@ extern void __dt_setup_arch(struct boot_param_header *bph); + static inline void device_tree_init(void) { } + #endif /* CONFIG_OF */ + ++extern char *mips_get_machine_name(void); ++extern void mips_set_machine_name(const char *name); ++ + #endif /* __ASM_PROM_H */ +diff --git a/arch/mips/kernel/mips_machine.c b/arch/mips/kernel/mips_machine.c +index 411a058..6dc5866 100644 +--- a/arch/mips/kernel/mips_machine.c ++++ b/arch/mips/kernel/mips_machine.c +@@ -13,7 +13,6 @@ + #include + + static struct mips_machine *mips_machine __initdata; +-static char *mips_machine_name = "Unknown"; + + #define for_each_machine(mach) \ + for ((mach) = (struct mips_machine *)&__mips_machines_start; \ +@@ -21,25 +20,6 @@ static char *mips_machine_name = "Unknown"; + (unsigned long)(mach) < (unsigned long)&__mips_machines_end; \ + (mach)++) + +-__init void mips_set_machine_name(const char *name) +-{ +- char *p; +- +- if (name == NULL) +- return; +- +- p = kstrdup(name, GFP_KERNEL); +- if (!p) +- pr_err("MIPS: no memory for machine_name\n"); +- +- mips_machine_name = p; +-} +- +-char *mips_get_machine_name(void) +-{ +- return mips_machine_name; +-} +- + __init int mips_machtype_setup(char *id) + { + struct mips_machine *mach; +@@ -79,7 +59,6 @@ __init void mips_machine_setup(void) + return; + + mips_set_machine_name(mips_machine->mach_name); +- pr_info("MIPS: machine is %s\n", mips_machine_name); + + if (mips_machine->mach_setup) + mips_machine->mach_setup(); +diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c +index 7a54f74..1dd137b 100644 +--- a/arch/mips/kernel/proc.c ++++ b/arch/mips/kernel/proc.c +@@ -12,7 +12,7 @@ + #include + #include + #include +-#include ++#include + + unsigned int vced_count, vcei_count; + +diff --git a/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c +index 028f6f8..b68e53b 100644 +--- a/arch/mips/kernel/prom.c ++++ b/arch/mips/kernel/prom.c +@@ -23,6 +23,22 @@ + #include + #include + ++static char mips_machine_name[64] = "Unknown"; ++ ++__init void mips_set_machine_name(const char *name) ++{ ++ if (name == NULL) ++ return; ++ ++ strncpy(mips_machine_name, name, sizeof(mips_machine_name)); ++ pr_info("MIPS: machine is %s\n", mips_get_machine_name()); ++} ++ ++char *mips_get_machine_name(void) ++{ ++ return mips_machine_name; ++} ++ + int __init early_init_dt_scan_memory_arch(unsigned long node, + const char *uname, int depth, + void *data) +@@ -50,6 +66,18 @@ void __init early_init_dt_setup_initrd_arch(unsigned long start, + } + #endif + ++int __init early_init_dt_scan_model(unsigned long node, const char *uname, ++ int depth, void *data) ++{ ++ if (!depth) { ++ char *model = of_get_flat_dt_prop(node, "model", NULL); ++ ++ if (model) ++ mips_set_machine_name(model); ++ } ++ return 0; ++} ++ + void __init early_init_devtree(void *params) + { + /* Setup flat device-tree pointer */ +@@ -65,6 +93,9 @@ void __init early_init_devtree(void *params) + /* Scan memory nodes */ + of_scan_flat_dt(early_init_dt_scan_root, NULL); + of_scan_flat_dt(early_init_dt_scan_memory_arch, NULL); ++ ++ /* try to load the mips machine name */ ++ of_scan_flat_dt(early_init_dt_scan_model, NULL); + } + + void __init __dt_setup_arch(struct boot_param_header *bph) +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0101-MIPS-ralink-add-PCI-IRQ-handling.patch b/target/linux/ramips/patches-3.9/0101-MIPS-ralink-add-PCI-IRQ-handling.patch new file mode 100644 index 0000000000..6e4e671e5b --- /dev/null +++ b/target/linux/ramips/patches-3.9/0101-MIPS-ralink-add-PCI-IRQ-handling.patch @@ -0,0 +1,40 @@ +From ca19f83f8551e6dd19073f04ad91639e98d6e22e Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Wed, 10 Apr 2013 09:07:27 +0200 +Subject: [PATCH 101/164] MIPS: ralink: add PCI IRQ handling + +The Ralink IRQ code was not handling the PCI IRQ yet. Add this functionaility +to make PCI work on rt3883. + +Signed-off-by: John Crispin +Signed-off-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5165/ +--- + arch/mips/ralink/irq.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c +index 6d054c5..d9807d0 100644 +--- a/arch/mips/ralink/irq.c ++++ b/arch/mips/ralink/irq.c +@@ -31,6 +31,7 @@ + #define INTC_INT_GLOBAL BIT(31) + + #define RALINK_CPU_IRQ_INTC (MIPS_CPU_IRQ_BASE + 2) ++#define RALINK_CPU_IRQ_PCI (MIPS_CPU_IRQ_BASE + 4) + #define RALINK_CPU_IRQ_FE (MIPS_CPU_IRQ_BASE + 5) + #define RALINK_CPU_IRQ_WIFI (MIPS_CPU_IRQ_BASE + 6) + #define RALINK_CPU_IRQ_COUNTER (MIPS_CPU_IRQ_BASE + 7) +@@ -104,6 +105,9 @@ asmlinkage void plat_irq_dispatch(void) + else if (pending & STATUSF_IP6) + do_IRQ(RALINK_CPU_IRQ_WIFI); + ++ else if (pending & STATUSF_IP4) ++ do_IRQ(RALINK_CPU_IRQ_PCI); ++ + else if (pending & STATUSF_IP2) + do_IRQ(RALINK_CPU_IRQ_INTC); + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0102-MIPS-ralink-add-RT3352-register-defines.patch b/target/linux/ramips/patches-3.9/0102-MIPS-ralink-add-RT3352-register-defines.patch new file mode 100644 index 0000000000..726745cd7c --- /dev/null +++ b/target/linux/ramips/patches-3.9/0102-MIPS-ralink-add-RT3352-register-defines.patch @@ -0,0 +1,40 @@ +From 48cf6bc7019d418e18831214731a55ec7320abb3 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 21 Mar 2013 19:01:49 +0100 +Subject: [PATCH 102/164] MIPS: ralink: add RT3352 register defines + +Add a few missing defines that are needed to make USB and clock detection work +on the RT3352. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5166/ +--- + arch/mips/include/asm/mach-ralink/rt305x.h | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/rt305x.h b/arch/mips/include/asm/mach-ralink/rt305x.h +index 7d344f2..e36c3c5 100644 +--- a/arch/mips/include/asm/mach-ralink/rt305x.h ++++ b/arch/mips/include/asm/mach-ralink/rt305x.h +@@ -136,4 +136,17 @@ static inline int soc_is_rt5350(void) + #define RT305X_GPIO_MODE_SDRAM BIT(8) + #define RT305X_GPIO_MODE_RGMII BIT(9) + ++#define RT3352_SYSC_REG_SYSCFG0 0x010 ++#define RT3352_SYSC_REG_SYSCFG1 0x014 ++#define RT3352_SYSC_REG_CLKCFG1 0x030 ++#define RT3352_SYSC_REG_RSTCTRL 0x034 ++#define RT3352_SYSC_REG_USB_PS 0x05c ++ ++#define RT3352_CLKCFG0_XTAL_SEL BIT(20) ++#define RT3352_CLKCFG1_UPHY0_CLK_EN BIT(18) ++#define RT3352_CLKCFG1_UPHY1_CLK_EN BIT(20) ++#define RT3352_RSTCTRL_UHST BIT(22) ++#define RT3352_RSTCTRL_UDEV BIT(25) ++#define RT3352_SYSCFG1_USB0_HOST_MODE BIT(10) ++ + #endif +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0103-MIPS-ralink-fix-RT305x-clock-setup.patch b/target/linux/ramips/patches-3.9/0103-MIPS-ralink-fix-RT305x-clock-setup.patch new file mode 100644 index 0000000000..735ed8dd73 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0103-MIPS-ralink-fix-RT305x-clock-setup.patch @@ -0,0 +1,52 @@ +From 93ded6b41dfdf71f2d2b2cf96e26f5784f373f5c Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 22 Mar 2013 19:25:59 +0100 +Subject: [PATCH 103/164] MIPS: ralink: fix RT305x clock setup + +Add a few missing clocks. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5167/ +--- + arch/mips/ralink/rt305x.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index 0a4bbdc..5d49a54 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -124,6 +124,8 @@ struct ralink_pinmux gpio_pinmux = { + void __init ralink_clk_init(void) + { + unsigned long cpu_rate, sys_rate, wdt_rate, uart_rate; ++ unsigned long wmac_rate = 40000000; ++ + u32 t = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG); + + if (soc_is_rt305x() || soc_is_rt3350()) { +@@ -176,11 +178,21 @@ void __init ralink_clk_init(void) + BUG(); + } + ++ if (soc_is_rt3352() || soc_is_rt5350()) { ++ u32 val = rt_sysc_r32(RT3352_SYSC_REG_SYSCFG0); ++ ++ if (!(val & RT3352_CLKCFG0_XTAL_SEL)) ++ wmac_rate = 20000000; ++ } ++ + ralink_clk_add("cpu", cpu_rate); + ralink_clk_add("10000b00.spi", sys_rate); + ralink_clk_add("10000100.timer", wdt_rate); ++ ralink_clk_add("10000120.watchdog", wdt_rate); + ralink_clk_add("10000500.uart", uart_rate); + ralink_clk_add("10000c00.uartlite", uart_rate); ++ ralink_clk_add("10100000.ethernet", sys_rate); ++ ralink_clk_add("10180000.wmac", wmac_rate); + } + + void __init ralink_of_remap(void) +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0104-MIPS-ralink-add-missing-comment-in-irq-driver.patch b/target/linux/ramips/patches-3.9/0104-MIPS-ralink-add-missing-comment-in-irq-driver.patch new file mode 100644 index 0000000000..e96a13e48a --- /dev/null +++ b/target/linux/ramips/patches-3.9/0104-MIPS-ralink-add-missing-comment-in-irq-driver.patch @@ -0,0 +1,29 @@ +From cd202a8165e847e1c7bf8454982149730376f27a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 16 Mar 2013 16:28:54 +0100 +Subject: [PATCH 104/164] MIPS: ralink: add missing comment in irq driver + +Trivial patch that adds a comment that makes the code more readable. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5168/ +--- + arch/mips/ralink/irq.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/mips/ralink/irq.c b/arch/mips/ralink/irq.c +index d9807d0..320b1f1 100644 +--- a/arch/mips/ralink/irq.c ++++ b/arch/mips/ralink/irq.c +@@ -166,6 +166,7 @@ static int __init intc_of_init(struct device_node *node, + irq_set_chained_handler(irq, ralink_intc_irq_handler); + irq_set_handler_data(irq, domain); + ++ /* tell the kernel which irq is used for performance monitoring */ + cp0_perfcount_irq = irq_create_mapping(domain, 9); + + return 0; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0105-MIPS-ralink-add-RT5350-sdram-register-defines.patch b/target/linux/ramips/patches-3.9/0105-MIPS-ralink-add-RT5350-sdram-register-defines.patch new file mode 100644 index 0000000000..49860a56c6 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0105-MIPS-ralink-add-RT5350-sdram-register-defines.patch @@ -0,0 +1,37 @@ +From 4c77c43dbef08096dd3798d4e421495b2c048285 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 25 Mar 2013 11:19:58 +0100 +Subject: [PATCH 105/164] MIPS: ralink: add RT5350 sdram register defines + +Add a few missing defines that are needed to make memory detection work on the +RT5350. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5169/ +--- + arch/mips/include/asm/mach-ralink/rt305x.h | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/rt305x.h b/arch/mips/include/asm/mach-ralink/rt305x.h +index e36c3c5..80cda8a 100644 +--- a/arch/mips/include/asm/mach-ralink/rt305x.h ++++ b/arch/mips/include/asm/mach-ralink/rt305x.h +@@ -97,6 +97,14 @@ static inline int soc_is_rt5350(void) + #define RT5350_SYSCFG0_CPUCLK_320 0x2 + #define RT5350_SYSCFG0_CPUCLK_300 0x3 + ++#define RT5350_SYSCFG0_DRAM_SIZE_SHIFT 12 ++#define RT5350_SYSCFG0_DRAM_SIZE_MASK 7 ++#define RT5350_SYSCFG0_DRAM_SIZE_2M 0 ++#define RT5350_SYSCFG0_DRAM_SIZE_8M 1 ++#define RT5350_SYSCFG0_DRAM_SIZE_16M 2 ++#define RT5350_SYSCFG0_DRAM_SIZE_32M 3 ++#define RT5350_SYSCFG0_DRAM_SIZE_64M 4 ++ + /* multi function gpio pins */ + #define RT305X_GPIO_I2C_SD 1 + #define RT305X_GPIO_I2C_SCLK 2 +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0106-MIPS-ralink-make-early_printk-work-on-RT2880.patch b/target/linux/ramips/patches-3.9/0106-MIPS-ralink-make-early_printk-work-on-RT2880.patch new file mode 100644 index 0000000000..f734032091 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0106-MIPS-ralink-make-early_printk-work-on-RT2880.patch @@ -0,0 +1,33 @@ +From 3d38304ce93752b9f196b22b7abeb8296fff7ba7 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 9 Apr 2013 18:31:15 +0200 +Subject: [PATCH 106/164] MIPS: ralink: make early_printk work on RT2880 + +RT2880 has a different location for the early serial port. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5170/ +--- + arch/mips/ralink/early_printk.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/arch/mips/ralink/early_printk.c b/arch/mips/ralink/early_printk.c +index c4ae47e..b46d041 100644 +--- a/arch/mips/ralink/early_printk.c ++++ b/arch/mips/ralink/early_printk.c +@@ -11,7 +11,11 @@ + + #include + ++#ifdef CONFIG_SOC_RT288X ++#define EARLY_UART_BASE 0x300c00 ++#else + #define EARLY_UART_BASE 0x10000c00 ++#endif + + #define UART_REG_RX 0x00 + #define UART_REG_TX 0x04 +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0107-MIPS-ralink-rename-gpio_pinmux-to-rt_gpio_pinmux.patch b/target/linux/ramips/patches-3.9/0107-MIPS-ralink-rename-gpio_pinmux-to-rt_gpio_pinmux.patch new file mode 100644 index 0000000000..877ca66c67 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0107-MIPS-ralink-rename-gpio_pinmux-to-rt_gpio_pinmux.patch @@ -0,0 +1,43 @@ +From 5dad33f0c1dd0b1605df6571e3493799106f36ee Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 22:12:09 +0200 +Subject: [PATCH 107/164] MIPS: ralink: rename gpio_pinmux to rt_gpio_pinmux + +Add proper namespacing to the variable. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5171/ +--- + arch/mips/ralink/common.h | 2 +- + arch/mips/ralink/rt305x.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h +index 3009903..f4b19c6 100644 +--- a/arch/mips/ralink/common.h ++++ b/arch/mips/ralink/common.h +@@ -24,7 +24,7 @@ struct ralink_pinmux { + int uart_shift; + void (*wdt_reset)(void); + }; +-extern struct ralink_pinmux gpio_pinmux; ++extern struct ralink_pinmux rt_gpio_pinmux; + + struct ralink_soc_info { + unsigned char sys_type[RAMIPS_SYS_TYPE_LEN]; +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index 5d49a54..f1a6c33 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -114,7 +114,7 @@ void rt305x_wdt_reset(void) + rt_sysc_w32(t, SYSC_REG_SYSTEM_CONFIG); + } + +-struct ralink_pinmux gpio_pinmux = { ++struct ralink_pinmux rt_gpio_pinmux = { + .mode = mode_mux, + .uart = uart_mux, + .uart_shift = RT305X_GPIO_MODE_UART0_SHIFT, +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0108-MIPS-ralink-make-the-RT305x-pinmuxing-structure-stat.patch b/target/linux/ramips/patches-3.9/0108-MIPS-ralink-make-the-RT305x-pinmuxing-structure-stat.patch new file mode 100644 index 0000000000..cb18cfe309 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0108-MIPS-ralink-make-the-RT305x-pinmuxing-structure-stat.patch @@ -0,0 +1,49 @@ +From 7a77fb07f96b8c7fa446ca1b62eddf7213362a70 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 22:16:12 +0200 +Subject: [PATCH 108/164] MIPS: ralink: make the RT305x pinmuxing structure + static + +These structures are exported via struct ralink_pinmux rt_gpio_pinmux and can +hence be static. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5172/ +--- + arch/mips/ralink/rt305x.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index f1a6c33..5b42078 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -22,7 +22,7 @@ + + enum rt305x_soc_type rt305x_soc; + +-struct ralink_pinmux_grp mode_mux[] = { ++static struct ralink_pinmux_grp mode_mux[] = { + { + .name = "i2c", + .mask = RT305X_GPIO_MODE_I2C, +@@ -61,7 +61,7 @@ struct ralink_pinmux_grp mode_mux[] = { + }, {0} + }; + +-struct ralink_pinmux_grp uart_mux[] = { ++static struct ralink_pinmux_grp uart_mux[] = { + { + .name = "uartf", + .mask = RT305X_GPIO_MODE_UARTF, +@@ -103,7 +103,7 @@ struct ralink_pinmux_grp uart_mux[] = { + }, {0} + }; + +-void rt305x_wdt_reset(void) ++static void rt305x_wdt_reset(void) + { + u32 t; + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0109-MIPS-ralink-add-pci-group-to-struct-ralink_pinmux.patch b/target/linux/ramips/patches-3.9/0109-MIPS-ralink-add-pci-group-to-struct-ralink_pinmux.patch new file mode 100644 index 0000000000..be32230ad5 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0109-MIPS-ralink-add-pci-group-to-struct-ralink_pinmux.patch @@ -0,0 +1,31 @@ +From b9ed5175f31c55122690a3e9af1cea2d1937768d Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Fri, 12 Apr 2013 12:40:23 +0200 +Subject: [PATCH 109/164] MIPS: ralink: add pci group to struct ralink_pinmux + +This will be used for RT3662/RT3883. + +Signed-off-by: Gabor Juhos +Acked-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5173/ +--- + arch/mips/ralink/common.h | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h +index f4b19c6..bebd149 100644 +--- a/arch/mips/ralink/common.h ++++ b/arch/mips/ralink/common.h +@@ -23,6 +23,9 @@ struct ralink_pinmux { + struct ralink_pinmux_grp *uart; + int uart_shift; + void (*wdt_reset)(void); ++ struct ralink_pinmux_grp *pci; ++ int pci_shift; ++ u32 pci_mask; + }; + extern struct ralink_pinmux rt_gpio_pinmux; + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0110-MIPS-ralink-add-uart-mask-to-struct-ralink_pinmux.patch b/target/linux/ramips/patches-3.9/0110-MIPS-ralink-add-uart-mask-to-struct-ralink_pinmux.patch new file mode 100644 index 0000000000..904b11d8ee --- /dev/null +++ b/target/linux/ramips/patches-3.9/0110-MIPS-ralink-add-uart-mask-to-struct-ralink_pinmux.patch @@ -0,0 +1,56 @@ +From f4c0850f31389bbb6b8d806be7786efc62af1e84 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 12:45:27 +0200 +Subject: [PATCH 110/164] MIPS: ralink: add uart mask to struct ralink_pinmux + +Add a field for the uart muxing mask and set it inside the rt305x setup code. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5744/ +--- + arch/mips/ralink/common.h | 1 + + arch/mips/ralink/rt305x.c | 5 +++-- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h +index bebd149..299119b 100644 +--- a/arch/mips/ralink/common.h ++++ b/arch/mips/ralink/common.h +@@ -22,6 +22,7 @@ struct ralink_pinmux { + struct ralink_pinmux_grp *mode; + struct ralink_pinmux_grp *uart; + int uart_shift; ++ u32 uart_mask; + void (*wdt_reset)(void); + struct ralink_pinmux_grp *pci; + int pci_shift; +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index 5b42078..6aa3cb1 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -91,12 +91,12 @@ static struct ralink_pinmux_grp uart_mux[] = { + .name = "gpio uartf", + .mask = RT305X_GPIO_MODE_GPIO_UARTF, + .gpio_first = RT305X_GPIO_7, +- .gpio_last = RT305X_GPIO_14, ++ .gpio_last = RT305X_GPIO_10, + }, { + .name = "gpio i2s", + .mask = RT305X_GPIO_MODE_GPIO_I2S, + .gpio_first = RT305X_GPIO_7, +- .gpio_last = RT305X_GPIO_14, ++ .gpio_last = RT305X_GPIO_10, + }, { + .name = "gpio", + .mask = RT305X_GPIO_MODE_GPIO, +@@ -118,6 +118,7 @@ struct ralink_pinmux rt_gpio_pinmux = { + .mode = mode_mux, + .uart = uart_mux, + .uart_shift = RT305X_GPIO_MODE_UART0_SHIFT, ++ .uart_mask = RT305X_GPIO_MODE_UART0_MASK, + .wdt_reset = rt305x_wdt_reset, + }; + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0111-MIPS-ralink-adds-support-for-RT2880-SoC-family.patch b/target/linux/ramips/patches-3.9/0111-MIPS-ralink-adds-support-for-RT2880-SoC-family.patch new file mode 100644 index 0000000000..4f608f2928 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0111-MIPS-ralink-adds-support-for-RT2880-SoC-family.patch @@ -0,0 +1,281 @@ +From 9a63b5b2e6684a80053b866de0c83adf284be857 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 27 Jan 2013 09:17:20 +0100 +Subject: [PATCH 111/164] MIPS: ralink: adds support for RT2880 SoC family + +Add support code for rt2880 SOC. + +The code detects the SoC and registers the clk / pinmux settings. + +Signed-off-by: John Crispin +Signed-off-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5176/ +--- + arch/mips/Kconfig | 2 +- + arch/mips/include/asm/mach-ralink/rt288x.h | 49 ++++++++++ + arch/mips/ralink/Kconfig | 3 + + arch/mips/ralink/Makefile | 1 + + arch/mips/ralink/Platform | 5 + + arch/mips/ralink/rt288x.c | 139 ++++++++++++++++++++++++++++ + 6 files changed, 198 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/include/asm/mach-ralink/rt288x.h + create mode 100644 arch/mips/ralink/rt288x.c + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index 51244bf..e4da4f8 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -1152,7 +1152,7 @@ config BOOT_ELF32 + + config MIPS_L1_CACHE_SHIFT + int +- default "4" if MACH_DECSTATION || MIKROTIK_RB532 || PMC_MSP4200_EVAL ++ default "4" if MACH_DECSTATION || MIKROTIK_RB532 || PMC_MSP4200_EVAL || SOC_RT288X + default "6" if MIPS_CPU_SCACHE + default "7" if SGI_IP22 || SGI_IP27 || SGI_IP28 || SNI_RM || CPU_CAVIUM_OCTEON + default "5" +diff --git a/arch/mips/include/asm/mach-ralink/rt288x.h b/arch/mips/include/asm/mach-ralink/rt288x.h +new file mode 100644 +index 0000000..ad8b42d +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt288x.h +@@ -0,0 +1,49 @@ ++/* ++ * 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. ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * Copyright (C) 2008-2011 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#ifndef _RT288X_REGS_H_ ++#define _RT288X_REGS_H_ ++ ++#define RT2880_SYSC_BASE 0x00300000 ++ ++#define SYSC_REG_CHIP_NAME0 0x00 ++#define SYSC_REG_CHIP_NAME1 0x04 ++#define SYSC_REG_CHIP_ID 0x0c ++#define SYSC_REG_SYSTEM_CONFIG 0x10 ++#define SYSC_REG_CLKCFG 0x30 ++ ++#define RT2880_CHIP_NAME0 0x38325452 ++#define RT2880_CHIP_NAME1 0x20203038 ++ ++#define CHIP_ID_ID_MASK 0xff ++#define CHIP_ID_ID_SHIFT 8 ++#define CHIP_ID_REV_MASK 0xff ++ ++#define SYSTEM_CONFIG_CPUCLK_SHIFT 20 ++#define SYSTEM_CONFIG_CPUCLK_MASK 0x3 ++#define SYSTEM_CONFIG_CPUCLK_250 0x0 ++#define SYSTEM_CONFIG_CPUCLK_266 0x1 ++#define SYSTEM_CONFIG_CPUCLK_280 0x2 ++#define SYSTEM_CONFIG_CPUCLK_300 0x3 ++ ++#define RT2880_GPIO_MODE_I2C BIT(0) ++#define RT2880_GPIO_MODE_UART0 BIT(1) ++#define RT2880_GPIO_MODE_SPI BIT(2) ++#define RT2880_GPIO_MODE_UART1 BIT(3) ++#define RT2880_GPIO_MODE_JTAG BIT(4) ++#define RT2880_GPIO_MODE_MDIO BIT(5) ++#define RT2880_GPIO_MODE_SDRAM BIT(6) ++#define RT2880_GPIO_MODE_PCI BIT(7) ++ ++#define CLKCFG_SRAM_CS_N_WDT BIT(9) ++ ++#endif +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index a0b0197..6723b94 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -6,6 +6,9 @@ choice + help + Select Ralink MIPS SoC type. + ++ config SOC_RT288X ++ bool "RT288x" ++ + config SOC_RT305X + bool "RT305x" + select USB_ARCH_HAS_HCD +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index 939757f..6d826f2 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -8,6 +8,7 @@ + + obj-y := prom.o of.o reset.o clk.o irq.o + ++obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o + + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +diff --git a/arch/mips/ralink/Platform b/arch/mips/ralink/Platform +index 6babd65..3f49e51 100644 +--- a/arch/mips/ralink/Platform ++++ b/arch/mips/ralink/Platform +@@ -5,6 +5,11 @@ core-$(CONFIG_RALINK) += arch/mips/ralink/ + cflags-$(CONFIG_RALINK) += -I$(srctree)/arch/mips/include/asm/mach-ralink + + # ++# Ralink RT288x ++# ++load-$(CONFIG_SOC_RT288X) += 0xffffffff88000000 ++ ++# + # Ralink RT305x + # + load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 +diff --git a/arch/mips/ralink/rt288x.c b/arch/mips/ralink/rt288x.c +new file mode 100644 +index 0000000..1e0788e +--- /dev/null ++++ b/arch/mips/ralink/rt288x.c +@@ -0,0 +1,139 @@ ++/* ++ * 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. ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * Copyright (C) 2008-2011 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "common.h" ++ ++static struct ralink_pinmux_grp mode_mux[] = { ++ { ++ .name = "i2c", ++ .mask = RT2880_GPIO_MODE_I2C, ++ .gpio_first = 1, ++ .gpio_last = 2, ++ }, { ++ .name = "spi", ++ .mask = RT2880_GPIO_MODE_SPI, ++ .gpio_first = 3, ++ .gpio_last = 6, ++ }, { ++ .name = "uartlite", ++ .mask = RT2880_GPIO_MODE_UART0, ++ .gpio_first = 7, ++ .gpio_last = 14, ++ }, { ++ .name = "jtag", ++ .mask = RT2880_GPIO_MODE_JTAG, ++ .gpio_first = 17, ++ .gpio_last = 21, ++ }, { ++ .name = "mdio", ++ .mask = RT2880_GPIO_MODE_MDIO, ++ .gpio_first = 22, ++ .gpio_last = 23, ++ }, { ++ .name = "sdram", ++ .mask = RT2880_GPIO_MODE_SDRAM, ++ .gpio_first = 24, ++ .gpio_last = 39, ++ }, { ++ .name = "pci", ++ .mask = RT2880_GPIO_MODE_PCI, ++ .gpio_first = 40, ++ .gpio_last = 71, ++ }, {0} ++}; ++ ++static void rt288x_wdt_reset(void) ++{ ++ u32 t; ++ ++ /* enable WDT reset output on pin SRAM_CS_N */ ++ t = rt_sysc_r32(SYSC_REG_CLKCFG); ++ t |= CLKCFG_SRAM_CS_N_WDT; ++ rt_sysc_w32(t, SYSC_REG_CLKCFG); ++} ++ ++struct ralink_pinmux rt_gpio_pinmux = { ++ .mode = mode_mux, ++ .wdt_reset = rt288x_wdt_reset, ++}; ++ ++void __init ralink_clk_init(void) ++{ ++ unsigned long cpu_rate; ++ u32 t = rt_sysc_r32(SYSC_REG_SYSTEM_CONFIG); ++ t = ((t >> SYSTEM_CONFIG_CPUCLK_SHIFT) & SYSTEM_CONFIG_CPUCLK_MASK); ++ ++ switch (t) { ++ case SYSTEM_CONFIG_CPUCLK_250: ++ cpu_rate = 250000000; ++ break; ++ case SYSTEM_CONFIG_CPUCLK_266: ++ cpu_rate = 266666667; ++ break; ++ case SYSTEM_CONFIG_CPUCLK_280: ++ cpu_rate = 280000000; ++ break; ++ case SYSTEM_CONFIG_CPUCLK_300: ++ cpu_rate = 300000000; ++ break; ++ } ++ ++ ralink_clk_add("cpu", cpu_rate); ++ ralink_clk_add("300100.timer", cpu_rate / 2); ++ ralink_clk_add("300120.watchdog", cpu_rate / 2); ++ ralink_clk_add("300500.uart", cpu_rate / 2); ++ ralink_clk_add("300c00.uartlite", cpu_rate / 2); ++ ralink_clk_add("400000.ethernet", cpu_rate / 2); ++} ++ ++void __init ralink_of_remap(void) ++{ ++ rt_sysc_membase = plat_of_remap_node("ralink,rt2880-sysc"); ++ rt_memc_membase = plat_of_remap_node("ralink,rt2880-memc"); ++ ++ if (!rt_sysc_membase || !rt_memc_membase) ++ panic("Failed to remap core resources"); ++} ++ ++void prom_soc_init(struct ralink_soc_info *soc_info) ++{ ++ void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT2880_SYSC_BASE); ++ const char *name; ++ u32 n0; ++ u32 n1; ++ u32 id; ++ ++ n0 = __raw_readl(sysc + SYSC_REG_CHIP_NAME0); ++ n1 = __raw_readl(sysc + SYSC_REG_CHIP_NAME1); ++ id = __raw_readl(sysc + SYSC_REG_CHIP_ID); ++ ++ if (n0 == RT2880_CHIP_NAME0 && n1 == RT2880_CHIP_NAME1) { ++ soc_info->compatible = "ralink,r2880-soc"; ++ name = "RT2880"; ++ } else { ++ panic("rt288x: unknown SoC, n0:%08x n1:%08x", n0, n1); ++ } ++ ++ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN, ++ "Ralink %s id:%u rev:%u", ++ name, ++ (id >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK, ++ (id & CHIP_ID_REV_MASK)); ++} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0112-MIPS-ralink-adds-support-for-RT3883-SoC-family.patch b/target/linux/ramips/patches-3.9/0112-MIPS-ralink-adds-support-for-RT3883-SoC-family.patch new file mode 100644 index 0000000000..47b13d8142 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0112-MIPS-ralink-adds-support-for-RT3883-SoC-family.patch @@ -0,0 +1,567 @@ +From b91aa2fcc93a92d851baa0745790a999e1f31592 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 27 Jan 2013 09:39:02 +0100 +Subject: [PATCH 112/164] MIPS: ralink: adds support for RT3883 SoC family + +Add support code for rt3883 SOC. + +The code detects the SoC and registers the clk / pinmux settings. + +Signed-off-by: John Crispin +Signed-off-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5185/ +--- + arch/mips/include/asm/mach-ralink/rt3883.h | 247 ++++++++++++++++++++++++++++ + arch/mips/ralink/Kconfig | 5 + + arch/mips/ralink/Makefile | 1 + + arch/mips/ralink/Platform | 5 + + arch/mips/ralink/rt3883.c | 242 +++++++++++++++++++++++++++ + 5 files changed, 500 insertions(+) + create mode 100644 arch/mips/include/asm/mach-ralink/rt3883.h + create mode 100644 arch/mips/ralink/rt3883.c + +diff --git a/arch/mips/include/asm/mach-ralink/rt3883.h b/arch/mips/include/asm/mach-ralink/rt3883.h +new file mode 100644 +index 0000000..b91c6c1 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt3883.h +@@ -0,0 +1,247 @@ ++/* ++ * Ralink RT3662/RT3883 SoC register definitions ++ * ++ * Copyright (C) 2011-2012 Gabor Juhos ++ * ++ * 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 _RT3883_REGS_H_ ++#define _RT3883_REGS_H_ ++ ++#include ++ ++#define RT3883_SDRAM_BASE 0x00000000 ++#define RT3883_SYSC_BASE 0x10000000 ++#define RT3883_TIMER_BASE 0x10000100 ++#define RT3883_INTC_BASE 0x10000200 ++#define RT3883_MEMC_BASE 0x10000300 ++#define RT3883_UART0_BASE 0x10000500 ++#define RT3883_PIO_BASE 0x10000600 ++#define RT3883_FSCC_BASE 0x10000700 ++#define RT3883_NANDC_BASE 0x10000810 ++#define RT3883_I2C_BASE 0x10000900 ++#define RT3883_I2S_BASE 0x10000a00 ++#define RT3883_SPI_BASE 0x10000b00 ++#define RT3883_UART1_BASE 0x10000c00 ++#define RT3883_PCM_BASE 0x10002000 ++#define RT3883_GDMA_BASE 0x10002800 ++#define RT3883_CODEC1_BASE 0x10003000 ++#define RT3883_CODEC2_BASE 0x10003800 ++#define RT3883_FE_BASE 0x10100000 ++#define RT3883_ROM_BASE 0x10118000 ++#define RT3883_USBDEV_BASE 0x10112000 ++#define RT3883_PCI_BASE 0x10140000 ++#define RT3883_WLAN_BASE 0x10180000 ++#define RT3883_USBHOST_BASE 0x101c0000 ++#define RT3883_BOOT_BASE 0x1c000000 ++#define RT3883_SRAM_BASE 0x1e000000 ++#define RT3883_PCIMEM_BASE 0x20000000 ++ ++#define RT3883_EHCI_BASE (RT3883_USBHOST_BASE) ++#define RT3883_OHCI_BASE (RT3883_USBHOST_BASE + 0x1000) ++ ++#define RT3883_SYSC_SIZE 0x100 ++#define RT3883_TIMER_SIZE 0x100 ++#define RT3883_INTC_SIZE 0x100 ++#define RT3883_MEMC_SIZE 0x100 ++#define RT3883_UART0_SIZE 0x100 ++#define RT3883_UART1_SIZE 0x100 ++#define RT3883_PIO_SIZE 0x100 ++#define RT3883_FSCC_SIZE 0x100 ++#define RT3883_NANDC_SIZE 0x0f0 ++#define RT3883_I2C_SIZE 0x100 ++#define RT3883_I2S_SIZE 0x100 ++#define RT3883_SPI_SIZE 0x100 ++#define RT3883_PCM_SIZE 0x800 ++#define RT3883_GDMA_SIZE 0x800 ++#define RT3883_CODEC1_SIZE 0x800 ++#define RT3883_CODEC2_SIZE 0x800 ++#define RT3883_FE_SIZE 0x10000 ++#define RT3883_ROM_SIZE 0x4000 ++#define RT3883_USBDEV_SIZE 0x4000 ++#define RT3883_PCI_SIZE 0x40000 ++#define RT3883_WLAN_SIZE 0x40000 ++#define RT3883_USBHOST_SIZE 0x40000 ++#define RT3883_BOOT_SIZE (32 * 1024 * 1024) ++#define RT3883_SRAM_SIZE (32 * 1024 * 1024) ++ ++/* SYSC registers */ ++#define RT3883_SYSC_REG_CHIPID0_3 0x00 /* Chip ID 0 */ ++#define RT3883_SYSC_REG_CHIPID4_7 0x04 /* Chip ID 1 */ ++#define RT3883_SYSC_REG_REVID 0x0c /* Chip Revision Identification */ ++#define RT3883_SYSC_REG_SYSCFG0 0x10 /* System Configuration 0 */ ++#define RT3883_SYSC_REG_SYSCFG1 0x14 /* System Configuration 1 */ ++#define RT3883_SYSC_REG_CLKCFG0 0x2c /* Clock Configuration 0 */ ++#define RT3883_SYSC_REG_CLKCFG1 0x30 /* Clock Configuration 1 */ ++#define RT3883_SYSC_REG_RSTCTRL 0x34 /* Reset Control*/ ++#define RT3883_SYSC_REG_RSTSTAT 0x38 /* Reset Status*/ ++#define RT3883_SYSC_REG_USB_PS 0x5c /* USB Power saving control */ ++#define RT3883_SYSC_REG_GPIO_MODE 0x60 /* GPIO Purpose Select */ ++#define RT3883_SYSC_REG_PCIE_CLK_GEN0 0x7c ++#define RT3883_SYSC_REG_PCIE_CLK_GEN1 0x80 ++#define RT3883_SYSC_REG_PCIE_CLK_GEN2 0x84 ++#define RT3883_SYSC_REG_PMU 0x88 ++#define RT3883_SYSC_REG_PMU1 0x8c ++ ++#define RT3883_CHIP_NAME0 0x38335452 ++#define RT3883_CHIP_NAME1 0x20203338 ++ ++#define RT3883_REVID_VER_ID_MASK 0x0f ++#define RT3883_REVID_VER_ID_SHIFT 8 ++#define RT3883_REVID_ECO_ID_MASK 0x0f ++ ++#define RT3883_SYSCFG0_DRAM_TYPE_DDR2 BIT(17) ++#define RT3883_SYSCFG0_CPUCLK_SHIFT 8 ++#define RT3883_SYSCFG0_CPUCLK_MASK 0x3 ++#define RT3883_SYSCFG0_CPUCLK_250 0x0 ++#define RT3883_SYSCFG0_CPUCLK_384 0x1 ++#define RT3883_SYSCFG0_CPUCLK_480 0x2 ++#define RT3883_SYSCFG0_CPUCLK_500 0x3 ++ ++#define RT3883_SYSCFG1_USB0_HOST_MODE BIT(10) ++#define RT3883_SYSCFG1_PCIE_RC_MODE BIT(8) ++#define RT3883_SYSCFG1_PCI_HOST_MODE BIT(7) ++#define RT3883_SYSCFG1_PCI_66M_MODE BIT(6) ++#define RT3883_SYSCFG1_GPIO2_AS_WDT_OUT BIT(2) ++ ++#define RT3883_CLKCFG1_PCIE_CLK_EN BIT(21) ++#define RT3883_CLKCFG1_UPHY1_CLK_EN BIT(20) ++#define RT3883_CLKCFG1_PCI_CLK_EN BIT(19) ++#define RT3883_CLKCFG1_UPHY0_CLK_EN BIT(18) ++ ++#define RT3883_GPIO_MODE_I2C BIT(0) ++#define RT3883_GPIO_MODE_SPI BIT(1) ++#define RT3883_GPIO_MODE_UART0_SHIFT 2 ++#define RT3883_GPIO_MODE_UART0_MASK 0x7 ++#define RT3883_GPIO_MODE_UART0(x) ((x) << RT3883_GPIO_MODE_UART0_SHIFT) ++#define RT3883_GPIO_MODE_UARTF 0x0 ++#define RT3883_GPIO_MODE_PCM_UARTF 0x1 ++#define RT3883_GPIO_MODE_PCM_I2S 0x2 ++#define RT3883_GPIO_MODE_I2S_UARTF 0x3 ++#define RT3883_GPIO_MODE_PCM_GPIO 0x4 ++#define RT3883_GPIO_MODE_GPIO_UARTF 0x5 ++#define RT3883_GPIO_MODE_GPIO_I2S 0x6 ++#define RT3883_GPIO_MODE_GPIO 0x7 ++#define RT3883_GPIO_MODE_UART1 BIT(5) ++#define RT3883_GPIO_MODE_JTAG BIT(6) ++#define RT3883_GPIO_MODE_MDIO BIT(7) ++#define RT3883_GPIO_MODE_GE1 BIT(9) ++#define RT3883_GPIO_MODE_GE2 BIT(10) ++#define RT3883_GPIO_MODE_PCI_SHIFT 11 ++#define RT3883_GPIO_MODE_PCI_MASK 0x7 ++#define RT3883_GPIO_MODE_PCI (RT3883_GPIO_MODE_PCI_MASK << RT3883_GPIO_MODE_PCI_SHIFT) ++#define RT3883_GPIO_MODE_LNA_A_SHIFT 16 ++#define RT3883_GPIO_MODE_LNA_A_MASK 0x3 ++#define _RT3883_GPIO_MODE_LNA_A(_x) ((_x) << RT3883_GPIO_MODE_LNA_A_SHIFT) ++#define RT3883_GPIO_MODE_LNA_A_GPIO 0x3 ++#define RT3883_GPIO_MODE_LNA_A _RT3883_GPIO_MODE_LNA_A(RT3883_GPIO_MODE_LNA_A_MASK) ++#define RT3883_GPIO_MODE_LNA_G_SHIFT 18 ++#define RT3883_GPIO_MODE_LNA_G_MASK 0x3 ++#define _RT3883_GPIO_MODE_LNA_G(_x) ((_x) << RT3883_GPIO_MODE_LNA_G_SHIFT) ++#define RT3883_GPIO_MODE_LNA_G_GPIO 0x3 ++#define RT3883_GPIO_MODE_LNA_G _RT3883_GPIO_MODE_LNA_G(RT3883_GPIO_MODE_LNA_G_MASK) ++ ++#define RT3883_GPIO_I2C_SD 1 ++#define RT3883_GPIO_I2C_SCLK 2 ++#define RT3883_GPIO_SPI_CS0 3 ++#define RT3883_GPIO_SPI_CLK 4 ++#define RT3883_GPIO_SPI_MOSI 5 ++#define RT3883_GPIO_SPI_MISO 6 ++#define RT3883_GPIO_7 7 ++#define RT3883_GPIO_10 10 ++#define RT3883_GPIO_14 14 ++#define RT3883_GPIO_UART1_TXD 15 ++#define RT3883_GPIO_UART1_RXD 16 ++#define RT3883_GPIO_JTAG_TDO 17 ++#define RT3883_GPIO_JTAG_TDI 18 ++#define RT3883_GPIO_JTAG_TMS 19 ++#define RT3883_GPIO_JTAG_TCLK 20 ++#define RT3883_GPIO_JTAG_TRST_N 21 ++#define RT3883_GPIO_MDIO_MDC 22 ++#define RT3883_GPIO_MDIO_MDIO 23 ++#define RT3883_GPIO_LNA_PE_A0 32 ++#define RT3883_GPIO_LNA_PE_A1 33 ++#define RT3883_GPIO_LNA_PE_A2 34 ++#define RT3883_GPIO_LNA_PE_G0 35 ++#define RT3883_GPIO_LNA_PE_G1 36 ++#define RT3883_GPIO_LNA_PE_G2 37 ++#define RT3883_GPIO_PCI_AD0 40 ++#define RT3883_GPIO_PCI_AD31 71 ++#define RT3883_GPIO_GE2_TXD0 72 ++#define RT3883_GPIO_GE2_TXD1 73 ++#define RT3883_GPIO_GE2_TXD2 74 ++#define RT3883_GPIO_GE2_TXD3 75 ++#define RT3883_GPIO_GE2_TXEN 76 ++#define RT3883_GPIO_GE2_TXCLK 77 ++#define RT3883_GPIO_GE2_RXD0 78 ++#define RT3883_GPIO_GE2_RXD1 79 ++#define RT3883_GPIO_GE2_RXD2 80 ++#define RT3883_GPIO_GE2_RXD3 81 ++#define RT3883_GPIO_GE2_RXDV 82 ++#define RT3883_GPIO_GE2_RXCLK 83 ++#define RT3883_GPIO_GE1_TXD0 84 ++#define RT3883_GPIO_GE1_TXD1 85 ++#define RT3883_GPIO_GE1_TXD2 86 ++#define RT3883_GPIO_GE1_TXD3 87 ++#define RT3883_GPIO_GE1_TXEN 88 ++#define RT3883_GPIO_GE1_TXCLK 89 ++#define RT3883_GPIO_GE1_RXD0 90 ++#define RT3883_GPIO_GE1_RXD1 91 ++#define RT3883_GPIO_GE1_RXD2 92 ++#define RT3883_GPIO_GE1_RXD3 93 ++#define RT3883_GPIO_GE1_RXDV 94 ++#define RT3883_GPIO_GE1_RXCLK 95 ++ ++#define RT3883_RSTCTRL_PCIE_PCI_PDM BIT(27) ++#define RT3883_RSTCTRL_FLASH BIT(26) ++#define RT3883_RSTCTRL_UDEV BIT(25) ++#define RT3883_RSTCTRL_PCI BIT(24) ++#define RT3883_RSTCTRL_PCIE BIT(23) ++#define RT3883_RSTCTRL_UHST BIT(22) ++#define RT3883_RSTCTRL_FE BIT(21) ++#define RT3883_RSTCTRL_WLAN BIT(20) ++#define RT3883_RSTCTRL_UART1 BIT(29) ++#define RT3883_RSTCTRL_SPI BIT(18) ++#define RT3883_RSTCTRL_I2S BIT(17) ++#define RT3883_RSTCTRL_I2C BIT(16) ++#define RT3883_RSTCTRL_NAND BIT(15) ++#define RT3883_RSTCTRL_DMA BIT(14) ++#define RT3883_RSTCTRL_PIO BIT(13) ++#define RT3883_RSTCTRL_UART BIT(12) ++#define RT3883_RSTCTRL_PCM BIT(11) ++#define RT3883_RSTCTRL_MC BIT(10) ++#define RT3883_RSTCTRL_INTC BIT(9) ++#define RT3883_RSTCTRL_TIMER BIT(8) ++#define RT3883_RSTCTRL_SYS BIT(0) ++ ++#define RT3883_INTC_INT_SYSCTL BIT(0) ++#define RT3883_INTC_INT_TIMER0 BIT(1) ++#define RT3883_INTC_INT_TIMER1 BIT(2) ++#define RT3883_INTC_INT_IA BIT(3) ++#define RT3883_INTC_INT_PCM BIT(4) ++#define RT3883_INTC_INT_UART0 BIT(5) ++#define RT3883_INTC_INT_PIO BIT(6) ++#define RT3883_INTC_INT_DMA BIT(7) ++#define RT3883_INTC_INT_NAND BIT(8) ++#define RT3883_INTC_INT_PERFC BIT(9) ++#define RT3883_INTC_INT_I2S BIT(10) ++#define RT3883_INTC_INT_UART1 BIT(12) ++#define RT3883_INTC_INT_UHST BIT(18) ++#define RT3883_INTC_INT_UDEV BIT(19) ++ ++/* FLASH/SRAM/Codec Controller registers */ ++#define RT3883_FSCC_REG_FLASH_CFG0 0x00 ++#define RT3883_FSCC_REG_FLASH_CFG1 0x04 ++#define RT3883_FSCC_REG_CODEC_CFG0 0x40 ++#define RT3883_FSCC_REG_CODEC_CFG1 0x44 ++ ++#define RT3883_FLASH_CFG_WIDTH_SHIFT 26 ++#define RT3883_FLASH_CFG_WIDTH_MASK 0x3 ++#define RT3883_FLASH_CFG_WIDTH_8BIT 0x0 ++#define RT3883_FLASH_CFG_WIDTH_16BIT 0x1 ++#define RT3883_FLASH_CFG_WIDTH_32BIT 0x2 ++ ++#endif /* _RT3883_REGS_H_ */ +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 6723b94..ce57d3e 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -15,6 +15,11 @@ choice + select USB_ARCH_HAS_OHCI + select USB_ARCH_HAS_EHCI + ++ config SOC_RT3883 ++ bool "RT3883" ++ select USB_ARCH_HAS_OHCI ++ select USB_ARCH_HAS_EHCI ++ + endchoice + + choice +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index 6d826f2..ba9669c 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -10,6 +10,7 @@ obj-y := prom.o of.o reset.o clk.o irq.o + + obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o ++obj-$(CONFIG_SOC_RT3883) += rt3883.o + + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + +diff --git a/arch/mips/ralink/Platform b/arch/mips/ralink/Platform +index 3f49e51..f67c08d 100644 +--- a/arch/mips/ralink/Platform ++++ b/arch/mips/ralink/Platform +@@ -13,3 +13,8 @@ load-$(CONFIG_SOC_RT288X) += 0xffffffff88000000 + # Ralink RT305x + # + load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 ++ ++# ++# Ralink RT3883 ++# ++load-$(CONFIG_SOC_RT3883) += 0xffffffff80000000 +diff --git a/arch/mips/ralink/rt3883.c b/arch/mips/ralink/rt3883.c +new file mode 100644 +index 0000000..2d90aa9 +--- /dev/null ++++ b/arch/mips/ralink/rt3883.c +@@ -0,0 +1,242 @@ ++/* ++ * 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. ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * Copyright (C) 2008 Imre Kaloz ++ * Copyright (C) 2008-2011 Gabor Juhos ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "common.h" ++ ++static struct ralink_pinmux_grp mode_mux[] = { ++ { ++ .name = "i2c", ++ .mask = RT3883_GPIO_MODE_I2C, ++ .gpio_first = RT3883_GPIO_I2C_SD, ++ .gpio_last = RT3883_GPIO_I2C_SCLK, ++ }, { ++ .name = "spi", ++ .mask = RT3883_GPIO_MODE_SPI, ++ .gpio_first = RT3883_GPIO_SPI_CS0, ++ .gpio_last = RT3883_GPIO_SPI_MISO, ++ }, { ++ .name = "uartlite", ++ .mask = RT3883_GPIO_MODE_UART1, ++ .gpio_first = RT3883_GPIO_UART1_TXD, ++ .gpio_last = RT3883_GPIO_UART1_RXD, ++ }, { ++ .name = "jtag", ++ .mask = RT3883_GPIO_MODE_JTAG, ++ .gpio_first = RT3883_GPIO_JTAG_TDO, ++ .gpio_last = RT3883_GPIO_JTAG_TCLK, ++ }, { ++ .name = "mdio", ++ .mask = RT3883_GPIO_MODE_MDIO, ++ .gpio_first = RT3883_GPIO_MDIO_MDC, ++ .gpio_last = RT3883_GPIO_MDIO_MDIO, ++ }, { ++ .name = "ge1", ++ .mask = RT3883_GPIO_MODE_GE1, ++ .gpio_first = RT3883_GPIO_GE1_TXD0, ++ .gpio_last = RT3883_GPIO_GE1_RXCLK, ++ }, { ++ .name = "ge2", ++ .mask = RT3883_GPIO_MODE_GE2, ++ .gpio_first = RT3883_GPIO_GE2_TXD0, ++ .gpio_last = RT3883_GPIO_GE2_RXCLK, ++ }, { ++ .name = "pci", ++ .mask = RT3883_GPIO_MODE_PCI, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, { ++ .name = "lna a", ++ .mask = RT3883_GPIO_MODE_LNA_A, ++ .gpio_first = RT3883_GPIO_LNA_PE_A0, ++ .gpio_last = RT3883_GPIO_LNA_PE_A2, ++ }, { ++ .name = "lna g", ++ .mask = RT3883_GPIO_MODE_LNA_G, ++ .gpio_first = RT3883_GPIO_LNA_PE_G0, ++ .gpio_last = RT3883_GPIO_LNA_PE_G2, ++ }, {0} ++}; ++ ++static struct ralink_pinmux_grp uart_mux[] = { ++ { ++ .name = "uartf", ++ .mask = RT3883_GPIO_MODE_UARTF, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_14, ++ }, { ++ .name = "pcm uartf", ++ .mask = RT3883_GPIO_MODE_PCM_UARTF, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_14, ++ }, { ++ .name = "pcm i2s", ++ .mask = RT3883_GPIO_MODE_PCM_I2S, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_14, ++ }, { ++ .name = "i2s uartf", ++ .mask = RT3883_GPIO_MODE_I2S_UARTF, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_14, ++ }, { ++ .name = "pcm gpio", ++ .mask = RT3883_GPIO_MODE_PCM_GPIO, ++ .gpio_first = RT3883_GPIO_11, ++ .gpio_last = RT3883_GPIO_14, ++ }, { ++ .name = "gpio uartf", ++ .mask = RT3883_GPIO_MODE_GPIO_UARTF, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_10, ++ }, { ++ .name = "gpio i2s", ++ .mask = RT3883_GPIO_MODE_GPIO_I2S, ++ .gpio_first = RT3883_GPIO_7, ++ .gpio_last = RT3883_GPIO_10, ++ }, { ++ .name = "gpio", ++ .mask = RT3883_GPIO_MODE_GPIO, ++ }, {0} ++}; ++ ++static struct ralink_pinmux_grp pci_mux[] = { ++ { ++ .name = "pci-dev", ++ .mask = 0, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, { ++ .name = "pci-host2", ++ .mask = 1, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, { ++ .name = "pci-host1", ++ .mask = 2, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, { ++ .name = "pci-fnc", ++ .mask = 3, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, { ++ .name = "pci-gpio", ++ .mask = 7, ++ .gpio_first = RT3883_GPIO_PCI_AD0, ++ .gpio_last = RT3883_GPIO_PCI_AD31, ++ }, {0} ++}; ++ ++static void rt3883_wdt_reset(void) ++{ ++ u32 t; ++ ++ /* enable WDT reset output on GPIO 2 */ ++ t = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG1); ++ t |= RT3883_SYSCFG1_GPIO2_AS_WDT_OUT; ++ rt_sysc_w32(t, RT3883_SYSC_REG_SYSCFG1); ++} ++ ++struct ralink_pinmux rt_gpio_pinmux = { ++ .mode = mode_mux, ++ .uart = uart_mux, ++ .uart_shift = RT3883_GPIO_MODE_UART0_SHIFT, ++ .uart_mask = RT3883_GPIO_MODE_GPIO, ++ .wdt_reset = rt3883_wdt_reset, ++ .pci = pci_mux, ++ .pci_shift = RT3883_GPIO_MODE_PCI_SHIFT, ++ .pci_mask = RT3883_GPIO_MODE_PCI_MASK, ++}; ++ ++void __init ralink_clk_init(void) ++{ ++ unsigned long cpu_rate, sys_rate; ++ u32 syscfg0; ++ u32 clksel; ++ u32 ddr2; ++ ++ syscfg0 = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG0); ++ clksel = ((syscfg0 >> RT3883_SYSCFG0_CPUCLK_SHIFT) & ++ RT3883_SYSCFG0_CPUCLK_MASK); ++ ddr2 = syscfg0 & RT3883_SYSCFG0_DRAM_TYPE_DDR2; ++ ++ switch (clksel) { ++ case RT3883_SYSCFG0_CPUCLK_250: ++ cpu_rate = 250000000; ++ sys_rate = (ddr2) ? 125000000 : 83000000; ++ break; ++ case RT3883_SYSCFG0_CPUCLK_384: ++ cpu_rate = 384000000; ++ sys_rate = (ddr2) ? 128000000 : 96000000; ++ break; ++ case RT3883_SYSCFG0_CPUCLK_480: ++ cpu_rate = 480000000; ++ sys_rate = (ddr2) ? 160000000 : 120000000; ++ break; ++ case RT3883_SYSCFG0_CPUCLK_500: ++ cpu_rate = 500000000; ++ sys_rate = (ddr2) ? 166000000 : 125000000; ++ break; ++ } ++ ++ ralink_clk_add("cpu", cpu_rate); ++ ralink_clk_add("10000100.timer", sys_rate); ++ ralink_clk_add("10000120.watchdog", sys_rate); ++ ralink_clk_add("10000500.uart", 40000000); ++ ralink_clk_add("10000b00.spi", sys_rate); ++ ralink_clk_add("10000c00.uartlite", 40000000); ++ ralink_clk_add("10100000.ethernet", sys_rate); ++} ++ ++void __init ralink_of_remap(void) ++{ ++ rt_sysc_membase = plat_of_remap_node("ralink,rt3883-sysc"); ++ rt_memc_membase = plat_of_remap_node("ralink,rt3883-memc"); ++ ++ if (!rt_sysc_membase || !rt_memc_membase) ++ panic("Failed to remap core resources"); ++} ++ ++void prom_soc_init(struct ralink_soc_info *soc_info) ++{ ++ void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT3883_SYSC_BASE); ++ const char *name; ++ u32 n0; ++ u32 n1; ++ u32 id; ++ ++ n0 = __raw_readl(sysc + RT3883_SYSC_REG_CHIPID0_3); ++ n1 = __raw_readl(sysc + RT3883_SYSC_REG_CHIPID4_7); ++ id = __raw_readl(sysc + RT3883_SYSC_REG_REVID); ++ ++ if (n0 == RT3883_CHIP_NAME0 && n1 == RT3883_CHIP_NAME1) { ++ soc_info->compatible = "ralink,rt3883-soc"; ++ name = "RT3883"; ++ } else { ++ panic("rt3883: unknown SoC, n0:%08x n1:%08x", n0, n1); ++ } ++ ++ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN, ++ "Ralink %s ver:%u eco:%u", ++ name, ++ (id >> RT3883_REVID_VER_ID_SHIFT) & RT3883_REVID_VER_ID_MASK, ++ (id & RT3883_REVID_ECO_ID_MASK)); ++} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0113-MIPS-ralink-adds-support-for-MT7620-SoC-family.patch b/target/linux/ramips/patches-3.9/0113-MIPS-ralink-adds-support-for-MT7620-SoC-family.patch new file mode 100644 index 0000000000..cd6a97b9f6 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0113-MIPS-ralink-adds-support-for-MT7620-SoC-family.patch @@ -0,0 +1,366 @@ +From 878887d15bdee87366b7d3952d1c74c4b68a0782 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 21 Mar 2013 17:49:02 +0100 +Subject: [PATCH 113/164] MIPS: ralink: adds support for MT7620 SoC family + +Add support code for mt7620 SOC. + +The code detects the SoC and registers the clk / pinmux settings. + +Signed-off-by: John Crispin +Acked-by: Gabor Juhos +Patchwork: http://patchwork.linux-mips.org/patch/5177/ +--- + arch/mips/include/asm/mach-ralink/mt7620.h | 76 ++++++++++ + arch/mips/ralink/Kconfig | 3 + + arch/mips/ralink/Makefile | 1 + + arch/mips/ralink/Platform | 5 + + arch/mips/ralink/mt7620.c | 214 ++++++++++++++++++++++++++++ + 5 files changed, 299 insertions(+) + create mode 100644 arch/mips/include/asm/mach-ralink/mt7620.h + create mode 100644 arch/mips/ralink/mt7620.c + +diff --git a/arch/mips/include/asm/mach-ralink/mt7620.h b/arch/mips/include/asm/mach-ralink/mt7620.h +new file mode 100644 +index 0000000..b272649 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/mt7620.h +@@ -0,0 +1,76 @@ ++/* ++ * 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. ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * Copyright (C) 2008-2011 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#ifndef _MT7620_REGS_H_ ++#define _MT7620_REGS_H_ ++ ++#define MT7620_SYSC_BASE 0x10000000 ++ ++#define SYSC_REG_CHIP_NAME0 0x00 ++#define SYSC_REG_CHIP_NAME1 0x04 ++#define SYSC_REG_CHIP_REV 0x0c ++#define SYSC_REG_SYSTEM_CONFIG0 0x10 ++#define SYSC_REG_SYSTEM_CONFIG1 0x14 ++#define SYSC_REG_CPLL_CONFIG0 0x54 ++#define SYSC_REG_CPLL_CONFIG1 0x58 ++ ++#define MT7620N_CHIP_NAME0 0x33365452 ++#define MT7620N_CHIP_NAME1 0x20203235 ++ ++#define MT7620A_CHIP_NAME0 0x3637544d ++#define MT7620A_CHIP_NAME1 0x20203032 ++ ++#define CHIP_REV_PKG_MASK 0x1 ++#define CHIP_REV_PKG_SHIFT 16 ++#define CHIP_REV_VER_MASK 0xf ++#define CHIP_REV_VER_SHIFT 8 ++#define CHIP_REV_ECO_MASK 0xf ++ ++#define CPLL_SW_CONFIG_SHIFT 31 ++#define CPLL_SW_CONFIG_MASK 0x1 ++#define CPLL_CPU_CLK_SHIFT 24 ++#define CPLL_CPU_CLK_MASK 0x1 ++#define CPLL_MULT_RATIO_SHIFT 16 ++#define CPLL_MULT_RATIO 0x7 ++#define CPLL_DIV_RATIO_SHIFT 10 ++#define CPLL_DIV_RATIO 0x3 ++ ++#define SYSCFG0_DRAM_TYPE_MASK 0x3 ++#define SYSCFG0_DRAM_TYPE_SHIFT 4 ++#define SYSCFG0_DRAM_TYPE_SDRAM 0 ++#define SYSCFG0_DRAM_TYPE_DDR1 1 ++#define SYSCFG0_DRAM_TYPE_DDR2 2 ++ ++#define MT7620_GPIO_MODE_I2C BIT(0) ++#define MT7620_GPIO_MODE_UART0_SHIFT 2 ++#define MT7620_GPIO_MODE_UART0_MASK 0x7 ++#define MT7620_GPIO_MODE_UART0(x) ((x) << MT7620_GPIO_MODE_UART0_SHIFT) ++#define MT7620_GPIO_MODE_UARTF 0x0 ++#define MT7620_GPIO_MODE_PCM_UARTF 0x1 ++#define MT7620_GPIO_MODE_PCM_I2S 0x2 ++#define MT7620_GPIO_MODE_I2S_UARTF 0x3 ++#define MT7620_GPIO_MODE_PCM_GPIO 0x4 ++#define MT7620_GPIO_MODE_GPIO_UARTF 0x5 ++#define MT7620_GPIO_MODE_GPIO_I2S 0x6 ++#define MT7620_GPIO_MODE_GPIO 0x7 ++#define MT7620_GPIO_MODE_UART1 BIT(5) ++#define MT7620_GPIO_MODE_MDIO BIT(8) ++#define MT7620_GPIO_MODE_RGMII1 BIT(9) ++#define MT7620_GPIO_MODE_RGMII2 BIT(10) ++#define MT7620_GPIO_MODE_SPI BIT(11) ++#define MT7620_GPIO_MODE_SPI_REF_CLK BIT(12) ++#define MT7620_GPIO_MODE_WLED BIT(13) ++#define MT7620_GPIO_MODE_JTAG BIT(15) ++#define MT7620_GPIO_MODE_EPHY BIT(15) ++#define MT7620_GPIO_MODE_WDT BIT(22) ++ ++#endif +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index ce57d3e..86f6c77 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -20,6 +20,9 @@ choice + select USB_ARCH_HAS_OHCI + select USB_ARCH_HAS_EHCI + ++ config SOC_MT7620 ++ bool "MT7620" ++ + endchoice + + choice +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index ba9669c..38cf1a8 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -11,6 +11,7 @@ obj-y := prom.o of.o reset.o clk.o irq.o + obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o + obj-$(CONFIG_SOC_RT3883) += rt3883.o ++obj-$(CONFIG_SOC_MT7620) += mt7620.o + + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + +diff --git a/arch/mips/ralink/Platform b/arch/mips/ralink/Platform +index f67c08d..b2cbf16 100644 +--- a/arch/mips/ralink/Platform ++++ b/arch/mips/ralink/Platform +@@ -18,3 +18,8 @@ load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 + # Ralink RT3883 + # + load-$(CONFIG_SOC_RT3883) += 0xffffffff80000000 ++ ++# ++# Ralink MT7620 ++# ++load-$(CONFIG_SOC_MT7620) += 0xffffffff80000000 +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +new file mode 100644 +index 0000000..eb00ab8 +--- /dev/null ++++ b/arch/mips/ralink/mt7620.c +@@ -0,0 +1,214 @@ ++/* ++ * 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. ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * Copyright (C) 2008-2011 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "common.h" ++ ++/* does the board have sdram or ddram */ ++static int dram_type; ++ ++/* the pll dividers */ ++static u32 mt7620_clk_divider[] = { 2, 3, 4, 8 }; ++ ++static struct ralink_pinmux_grp mode_mux[] = { ++ { ++ .name = "i2c", ++ .mask = MT7620_GPIO_MODE_I2C, ++ .gpio_first = 1, ++ .gpio_last = 2, ++ }, { ++ .name = "spi", ++ .mask = MT7620_GPIO_MODE_SPI, ++ .gpio_first = 3, ++ .gpio_last = 6, ++ }, { ++ .name = "uartlite", ++ .mask = MT7620_GPIO_MODE_UART1, ++ .gpio_first = 15, ++ .gpio_last = 16, ++ }, { ++ .name = "wdt", ++ .mask = MT7620_GPIO_MODE_WDT, ++ .gpio_first = 17, ++ .gpio_last = 17, ++ }, { ++ .name = "mdio", ++ .mask = MT7620_GPIO_MODE_MDIO, ++ .gpio_first = 22, ++ .gpio_last = 23, ++ }, { ++ .name = "rgmii1", ++ .mask = MT7620_GPIO_MODE_RGMII1, ++ .gpio_first = 24, ++ .gpio_last = 35, ++ }, { ++ .name = "spi refclk", ++ .mask = MT7620_GPIO_MODE_SPI_REF_CLK, ++ .gpio_first = 37, ++ .gpio_last = 39, ++ }, { ++ .name = "jtag", ++ .mask = MT7620_GPIO_MODE_JTAG, ++ .gpio_first = 40, ++ .gpio_last = 44, ++ }, { ++ /* shared lines with jtag */ ++ .name = "ephy", ++ .mask = MT7620_GPIO_MODE_EPHY, ++ .gpio_first = 40, ++ .gpio_last = 44, ++ }, { ++ .name = "nand", ++ .mask = MT7620_GPIO_MODE_JTAG, ++ .gpio_first = 45, ++ .gpio_last = 59, ++ }, { ++ .name = "rgmii2", ++ .mask = MT7620_GPIO_MODE_RGMII2, ++ .gpio_first = 60, ++ .gpio_last = 71, ++ }, { ++ .name = "wled", ++ .mask = MT7620_GPIO_MODE_WLED, ++ .gpio_first = 72, ++ .gpio_last = 72, ++ }, {0} ++}; ++ ++static struct ralink_pinmux_grp uart_mux[] = { ++ { ++ .name = "uartf", ++ .mask = MT7620_GPIO_MODE_UARTF, ++ .gpio_first = 7, ++ .gpio_last = 14, ++ }, { ++ .name = "pcm uartf", ++ .mask = MT7620_GPIO_MODE_PCM_UARTF, ++ .gpio_first = 7, ++ .gpio_last = 14, ++ }, { ++ .name = "pcm i2s", ++ .mask = MT7620_GPIO_MODE_PCM_I2S, ++ .gpio_first = 7, ++ .gpio_last = 14, ++ }, { ++ .name = "i2s uartf", ++ .mask = MT7620_GPIO_MODE_I2S_UARTF, ++ .gpio_first = 7, ++ .gpio_last = 14, ++ }, { ++ .name = "pcm gpio", ++ .mask = MT7620_GPIO_MODE_PCM_GPIO, ++ .gpio_first = 11, ++ .gpio_last = 14, ++ }, { ++ .name = "gpio uartf", ++ .mask = MT7620_GPIO_MODE_GPIO_UARTF, ++ .gpio_first = 7, ++ .gpio_last = 10, ++ }, { ++ .name = "gpio i2s", ++ .mask = MT7620_GPIO_MODE_GPIO_I2S, ++ .gpio_first = 7, ++ .gpio_last = 10, ++ }, { ++ .name = "gpio", ++ .mask = MT7620_GPIO_MODE_GPIO, ++ }, {0} ++}; ++ ++struct ralink_pinmux rt_gpio_pinmux = { ++ .mode = mode_mux, ++ .uart = uart_mux, ++ .uart_shift = MT7620_GPIO_MODE_UART0_SHIFT, ++ .uart_mask = MT7620_GPIO_MODE_GPIO, ++}; ++ ++void __init ralink_clk_init(void) ++{ ++ unsigned long cpu_rate, sys_rate; ++ u32 c0 = rt_sysc_r32(SYSC_REG_CPLL_CONFIG0); ++ u32 c1 = rt_sysc_r32(SYSC_REG_CPLL_CONFIG1); ++ u32 swconfig = (c0 >> CPLL_SW_CONFIG_SHIFT) & CPLL_SW_CONFIG_MASK; ++ u32 cpu_clk = (c1 >> CPLL_CPU_CLK_SHIFT) & CPLL_CPU_CLK_MASK; ++ ++ if (cpu_clk) { ++ cpu_rate = 480000000; ++ } else if (!swconfig) { ++ cpu_rate = 600000000; ++ } else { ++ u32 m = (c0 >> CPLL_MULT_RATIO_SHIFT) & CPLL_MULT_RATIO; ++ u32 d = (c0 >> CPLL_DIV_RATIO_SHIFT) & CPLL_DIV_RATIO; ++ ++ cpu_rate = ((40 * (m + 24)) / mt7620_clk_divider[d]) * 1000000; ++ } ++ ++ if (dram_type == SYSCFG0_DRAM_TYPE_SDRAM) ++ sys_rate = cpu_rate / 4; ++ else ++ sys_rate = cpu_rate / 3; ++ ++ ralink_clk_add("cpu", cpu_rate); ++ ralink_clk_add("10000100.timer", 40000000); ++ ralink_clk_add("10000500.uart", 40000000); ++ ralink_clk_add("10000c00.uartlite", 40000000); ++} ++ ++void __init ralink_of_remap(void) ++{ ++ rt_sysc_membase = plat_of_remap_node("ralink,mt7620a-sysc"); ++ rt_memc_membase = plat_of_remap_node("ralink,mt7620a-memc"); ++ ++ if (!rt_sysc_membase || !rt_memc_membase) ++ panic("Failed to remap core resources"); ++} ++ ++void prom_soc_init(struct ralink_soc_info *soc_info) ++{ ++ void __iomem *sysc = (void __iomem *) KSEG1ADDR(MT7620_SYSC_BASE); ++ unsigned char *name = NULL; ++ u32 n0; ++ u32 n1; ++ u32 rev; ++ u32 cfg0; ++ ++ n0 = __raw_readl(sysc + SYSC_REG_CHIP_NAME0); ++ n1 = __raw_readl(sysc + SYSC_REG_CHIP_NAME1); ++ ++ if (n0 == MT7620N_CHIP_NAME0 && n1 == MT7620N_CHIP_NAME1) { ++ name = "MT7620N"; ++ soc_info->compatible = "ralink,mt7620n-soc"; ++ } else if (n0 == MT7620A_CHIP_NAME0 && n1 == MT7620A_CHIP_NAME1) { ++ name = "MT7620A"; ++ soc_info->compatible = "ralink,mt7620a-soc"; ++ } else { ++ panic("mt7620: unknown SoC, n0:%08x n1:%08x\n", n0, n1); ++ } ++ ++ rev = __raw_readl(sysc + SYSC_REG_CHIP_REV); ++ ++ snprintf(soc_info->sys_type, RAMIPS_SYS_TYPE_LEN, ++ "Ralink %s ver:%u eco:%u", ++ name, ++ (rev >> CHIP_REV_VER_SHIFT) & CHIP_REV_VER_MASK, ++ (rev & CHIP_REV_ECO_MASK)); ++ ++ cfg0 = __raw_readl(sysc + SYSC_REG_SYSTEM_CONFIG0); ++ dram_type = (cfg0 >> SYSCFG0_DRAM_TYPE_SHIFT) & SYSCFG0_DRAM_TYPE_MASK; ++} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0114-MIPS-ralink-add-cpu-feature-overrides.h.patch b/target/linux/ramips/patches-3.9/0114-MIPS-ralink-add-cpu-feature-overrides.h.patch new file mode 100644 index 0000000000..d02e2407e2 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0114-MIPS-ralink-add-cpu-feature-overrides.h.patch @@ -0,0 +1,232 @@ +From f5d5b8b69f5b11fdd810a1a56eff91077ad5ba84 Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Wed, 10 Apr 2013 09:19:07 +0200 +Subject: [PATCH 114/164] MIPS: ralink: add cpu-feature-overrides.h + +Add cpu-feature-overrides.h for RT288x, RT305x and RT3883. + +Signed-off-by: Gabor Juhos +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5175/ +--- + .../asm/mach-ralink/rt288x/cpu-feature-overrides.h | 56 ++++++++++++++++++++ + .../asm/mach-ralink/rt305x/cpu-feature-overrides.h | 56 ++++++++++++++++++++ + .../asm/mach-ralink/rt3883/cpu-feature-overrides.h | 55 +++++++++++++++++++ + arch/mips/ralink/Platform | 3 ++ + 4 files changed, 170 insertions(+) + create mode 100644 arch/mips/include/asm/mach-ralink/rt288x/cpu-feature-overrides.h + create mode 100644 arch/mips/include/asm/mach-ralink/rt305x/cpu-feature-overrides.h + create mode 100644 arch/mips/include/asm/mach-ralink/rt3883/cpu-feature-overrides.h + +diff --git a/arch/mips/include/asm/mach-ralink/rt288x/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ralink/rt288x/cpu-feature-overrides.h +new file mode 100644 +index 0000000..72fc106 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt288x/cpu-feature-overrides.h +@@ -0,0 +1,56 @@ ++/* ++ * Ralink RT288x specific CPU feature overrides ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * This file was derived from: include/asm-mips/cpu-features.h ++ * Copyright (C) 2003, 2004 Ralf Baechle ++ * Copyright (C) 2004 Maciej W. Rozycki ++ * ++ * 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 _RT288X_CPU_FEATURE_OVERRIDES_H ++#define _RT288X_CPU_FEATURE_OVERRIDES_H ++ ++#define cpu_has_tlb 1 ++#define cpu_has_4kex 1 ++#define cpu_has_3k_cache 0 ++#define cpu_has_4k_cache 1 ++#define cpu_has_tx39_cache 0 ++#define cpu_has_sb1_cache 0 ++#define cpu_has_fpu 0 ++#define cpu_has_32fpr 0 ++#define cpu_has_counter 1 ++#define cpu_has_watch 1 ++#define cpu_has_divec 1 ++ ++#define cpu_has_prefetch 1 ++#define cpu_has_ejtag 1 ++#define cpu_has_llsc 1 ++ ++#define cpu_has_mips16 1 ++#define cpu_has_mdmx 0 ++#define cpu_has_mips3d 0 ++#define cpu_has_smartmips 0 ++ ++#define cpu_has_mips32r1 1 ++#define cpu_has_mips32r2 1 ++#define cpu_has_mips64r1 0 ++#define cpu_has_mips64r2 0 ++ ++#define cpu_has_dsp 0 ++#define cpu_has_mipsmt 0 ++ ++#define cpu_has_64bits 0 ++#define cpu_has_64bit_zero_reg 0 ++#define cpu_has_64bit_gp_regs 0 ++#define cpu_has_64bit_addresses 0 ++ ++#define cpu_dcache_line_size() 16 ++#define cpu_icache_line_size() 16 ++ ++#endif /* _RT288X_CPU_FEATURE_OVERRIDES_H */ +diff --git a/arch/mips/include/asm/mach-ralink/rt305x/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ralink/rt305x/cpu-feature-overrides.h +new file mode 100644 +index 0000000..917c286 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt305x/cpu-feature-overrides.h +@@ -0,0 +1,56 @@ ++/* ++ * Ralink RT305x specific CPU feature overrides ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * This file was derived from: include/asm-mips/cpu-features.h ++ * Copyright (C) 2003, 2004 Ralf Baechle ++ * Copyright (C) 2004 Maciej W. Rozycki ++ * ++ * 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 _RT305X_CPU_FEATURE_OVERRIDES_H ++#define _RT305X_CPU_FEATURE_OVERRIDES_H ++ ++#define cpu_has_tlb 1 ++#define cpu_has_4kex 1 ++#define cpu_has_3k_cache 0 ++#define cpu_has_4k_cache 1 ++#define cpu_has_tx39_cache 0 ++#define cpu_has_sb1_cache 0 ++#define cpu_has_fpu 0 ++#define cpu_has_32fpr 0 ++#define cpu_has_counter 1 ++#define cpu_has_watch 1 ++#define cpu_has_divec 1 ++ ++#define cpu_has_prefetch 1 ++#define cpu_has_ejtag 1 ++#define cpu_has_llsc 1 ++ ++#define cpu_has_mips16 1 ++#define cpu_has_mdmx 0 ++#define cpu_has_mips3d 0 ++#define cpu_has_smartmips 0 ++ ++#define cpu_has_mips32r1 1 ++#define cpu_has_mips32r2 1 ++#define cpu_has_mips64r1 0 ++#define cpu_has_mips64r2 0 ++ ++#define cpu_has_dsp 1 ++#define cpu_has_mipsmt 0 ++ ++#define cpu_has_64bits 0 ++#define cpu_has_64bit_zero_reg 0 ++#define cpu_has_64bit_gp_regs 0 ++#define cpu_has_64bit_addresses 0 ++ ++#define cpu_dcache_line_size() 32 ++#define cpu_icache_line_size() 32 ++ ++#endif /* _RT305X_CPU_FEATURE_OVERRIDES_H */ +diff --git a/arch/mips/include/asm/mach-ralink/rt3883/cpu-feature-overrides.h b/arch/mips/include/asm/mach-ralink/rt3883/cpu-feature-overrides.h +new file mode 100644 +index 0000000..181fbf4 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt3883/cpu-feature-overrides.h +@@ -0,0 +1,55 @@ ++/* ++ * Ralink RT3662/RT3883 specific CPU feature overrides ++ * ++ * Copyright (C) 2011-2013 Gabor Juhos ++ * ++ * This file was derived from: include/asm-mips/cpu-features.h ++ * Copyright (C) 2003, 2004 Ralf Baechle ++ * Copyright (C) 2004 Maciej W. Rozycki ++ * ++ * 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 _RT3883_CPU_FEATURE_OVERRIDES_H ++#define _RT3883_CPU_FEATURE_OVERRIDES_H ++ ++#define cpu_has_tlb 1 ++#define cpu_has_4kex 1 ++#define cpu_has_3k_cache 0 ++#define cpu_has_4k_cache 1 ++#define cpu_has_tx39_cache 0 ++#define cpu_has_sb1_cache 0 ++#define cpu_has_fpu 0 ++#define cpu_has_32fpr 0 ++#define cpu_has_counter 1 ++#define cpu_has_watch 1 ++#define cpu_has_divec 1 ++ ++#define cpu_has_prefetch 1 ++#define cpu_has_ejtag 1 ++#define cpu_has_llsc 1 ++ ++#define cpu_has_mips16 1 ++#define cpu_has_mdmx 0 ++#define cpu_has_mips3d 0 ++#define cpu_has_smartmips 0 ++ ++#define cpu_has_mips32r1 1 ++#define cpu_has_mips32r2 1 ++#define cpu_has_mips64r1 0 ++#define cpu_has_mips64r2 0 ++ ++#define cpu_has_dsp 1 ++#define cpu_has_mipsmt 0 ++ ++#define cpu_has_64bits 0 ++#define cpu_has_64bit_zero_reg 0 ++#define cpu_has_64bit_gp_regs 0 ++#define cpu_has_64bit_addresses 0 ++ ++#define cpu_dcache_line_size() 32 ++#define cpu_icache_line_size() 32 ++ ++#endif /* _RT3883_CPU_FEATURE_OVERRIDES_H */ +diff --git a/arch/mips/ralink/Platform b/arch/mips/ralink/Platform +index b2cbf16..cda4b66 100644 +--- a/arch/mips/ralink/Platform ++++ b/arch/mips/ralink/Platform +@@ -8,16 +8,19 @@ cflags-$(CONFIG_RALINK) += -I$(srctree)/arch/mips/include/asm/mach-ralink + # Ralink RT288x + # + load-$(CONFIG_SOC_RT288X) += 0xffffffff88000000 ++cflags-$(CONFIG_SOC_RT288X) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt288x + + # + # Ralink RT305x + # + load-$(CONFIG_SOC_RT305X) += 0xffffffff80000000 ++cflags-$(CONFIG_SOC_RT305X) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt305x + + # + # Ralink RT3883 + # + load-$(CONFIG_SOC_RT3883) += 0xffffffff80000000 ++cflags-$(CONFIG_SOC_RT3883) += -I$(srctree)/arch/mips/include/asm/mach-ralink/rt3883 + + # + # Ralink MT7620 +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0115-DT-add-vendor-prefixes-for-Ralink.patch b/target/linux/ramips/patches-3.9/0115-DT-add-vendor-prefixes-for-Ralink.patch new file mode 100644 index 0000000000..cbae1080a8 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0115-DT-add-vendor-prefixes-for-Ralink.patch @@ -0,0 +1,26 @@ +From 94299fd468be1bcac63eb159a7f280f3bc0351ec Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 10:11:51 +0200 +Subject: [PATCH 115/164] DT: add vendor prefixes for Ralink + +Signed-off-by: John Crispin +Acked-by: Grant Likely +--- + Documentation/devicetree/bindings/vendor-prefixes.txt | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt +index 19e1ef7..6527412 100644 +--- a/Documentation/devicetree/bindings/vendor-prefixes.txt ++++ b/Documentation/devicetree/bindings/vendor-prefixes.txt +@@ -41,6 +41,7 @@ onnn ON Semiconductor Corp. + picochip Picochip Ltd + powervr PowerVR (deprecated, use img) + qcom Qualcomm, Inc. ++ralink Mediatek/Ralink Technology Corp. + ramtron Ramtron International + realtek Realtek Semiconductor Corp. + renesas Renesas Electronics Corporation +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0116-DT-add-documentation-for-the-Ralink-MIPS-SoCs.patch b/target/linux/ramips/patches-3.9/0116-DT-add-documentation-for-the-Ralink-MIPS-SoCs.patch new file mode 100644 index 0000000000..b029e45307 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0116-DT-add-documentation-for-the-Ralink-MIPS-SoCs.patch @@ -0,0 +1,44 @@ +From f8ad9ce9fceec8ffa986ac44c52ead3f6adade1e Mon Sep 17 00:00:00 2001 +From: Gabor Juhos +Date: Sat, 13 Apr 2013 09:02:40 +0200 +Subject: [PATCH 116/164] DT: add documentation for the Ralink MIPS SoCs + +This patch adds binding documentation for the +compatible values of the Ralink MIPS SoCs. + +Signed-off-by: Gabor Juhos +Signed-off-by: John Crispin +Acked-by: Grant Likely +Patchwork: http://patchwork.linux-mips.org/patch/5187/ +--- + Documentation/devicetree/bindings/mips/ralink.txt | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mips/ralink.txt + +diff --git a/Documentation/devicetree/bindings/mips/ralink.txt b/Documentation/devicetree/bindings/mips/ralink.txt +new file mode 100644 +index 0000000..59b6a35 +--- /dev/null ++++ b/Documentation/devicetree/bindings/mips/ralink.txt +@@ -0,0 +1,18 @@ ++Ralink MIPS SoC device tree bindings ++ ++1. SoCs ++ ++Each device tree must specify a compatible value for the Ralink SoC ++it uses in the compatible property of the root node. The compatible ++value must be one of the following values: ++ ++ ralink,rt2880-soc ++ ralink,rt3050-soc ++ ralink,rt3052-soc ++ ralink,rt3350-soc ++ ralink,rt3352-soc ++ ralink,rt3883-soc ++ ralink,rt5350-soc ++ ralink,mt7620a-soc ++ ralink,mt7620n-soc ++ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0117-DT-MIPS-ralink-clean-up-RT3050-dtsi-and-dts-file.patch b/target/linux/ramips/patches-3.9/0117-DT-MIPS-ralink-clean-up-RT3050-dtsi-and-dts-file.patch new file mode 100644 index 0000000000..e21565d668 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0117-DT-MIPS-ralink-clean-up-RT3050-dtsi-and-dts-file.patch @@ -0,0 +1,139 @@ +From 0f26ec753f34e16e00d25c576a190765aedd62c9 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 21 Mar 2013 17:47:07 +0100 +Subject: [PATCH 117/164] DT: MIPS: ralink: clean up RT3050 dtsi and dts file + +* remove nodes for cores whose drivers are not upstream yet +* add compat string for an additional soc +* fix a whitespace error + +Signed-off-by: John Crispin +Acked-by: Grant Likely +Patchwork: http://patchwork.linux-mips.org/patch/5186/ +--- + arch/mips/ralink/dts/rt3050.dtsi | 52 ++-------------------------------- + arch/mips/ralink/dts/rt3052_eval.dts | 10 ++----- + 2 files changed, 4 insertions(+), 58 deletions(-) + +diff --git a/arch/mips/ralink/dts/rt3050.dtsi b/arch/mips/ralink/dts/rt3050.dtsi +index 069d066..ef7da1e 100644 +--- a/arch/mips/ralink/dts/rt3050.dtsi ++++ b/arch/mips/ralink/dts/rt3050.dtsi +@@ -1,7 +1,7 @@ + / { + #address-cells = <1>; + #size-cells = <1>; +- compatible = "ralink,rt3050-soc", "ralink,rt3052-soc"; ++ compatible = "ralink,rt3050-soc", "ralink,rt3052-soc", "ralink,rt3350-soc"; + + cpus { + cpu@0 { +@@ -9,10 +9,6 @@ + }; + }; + +- chosen { +- bootargs = "console=ttyS0,57600 init=/init"; +- }; +- + cpuintc: cpuintc@0 { + #address-cells = <0>; + #interrupt-cells = <1>; +@@ -23,7 +19,7 @@ + palmbus@10000000 { + compatible = "palmbus"; + reg = <0x10000000 0x200000>; +- ranges = <0x0 0x10000000 0x1FFFFF>; ++ ranges = <0x0 0x10000000 0x1FFFFF>; + + #address-cells = <1>; + #size-cells = <1>; +@@ -33,11 +29,6 @@ + reg = <0x0 0x100>; + }; + +- timer@100 { +- compatible = "ralink,rt3052-wdt", "ralink,rt2880-wdt"; +- reg = <0x100 0x100>; +- }; +- + intc: intc@200 { + compatible = "ralink,rt3052-intc", "ralink,rt2880-intc"; + reg = <0x200 0x100>; +@@ -54,45 +45,6 @@ + reg = <0x300 0x100>; + }; + +- gpio0: gpio@600 { +- compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; +- reg = <0x600 0x34>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- ralink,ngpio = <24>; +- ralink,regs = [ 00 04 08 0c +- 20 24 28 2c +- 30 34 ]; +- }; +- +- gpio1: gpio@638 { +- compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; +- reg = <0x638 0x24>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- ralink,ngpio = <16>; +- ralink,regs = [ 00 04 08 0c +- 10 14 18 1c +- 20 24 ]; +- }; +- +- gpio2: gpio@660 { +- compatible = "ralink,rt3052-gpio", "ralink,rt2880-gpio"; +- reg = <0x660 0x24>; +- +- gpio-controller; +- #gpio-cells = <2>; +- +- ralink,ngpio = <12>; +- ralink,regs = [ 00 04 08 0c +- 10 14 18 1c +- 20 24 ]; +- }; +- + uartlite@c00 { + compatible = "ralink,rt3052-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0xc00 0x100>; +diff --git a/arch/mips/ralink/dts/rt3052_eval.dts b/arch/mips/ralink/dts/rt3052_eval.dts +index 148a590..df17f5f 100644 +--- a/arch/mips/ralink/dts/rt3052_eval.dts ++++ b/arch/mips/ralink/dts/rt3052_eval.dts +@@ -3,8 +3,6 @@ + /include/ "rt3050.dtsi" + + / { +- #address-cells = <1>; +- #size-cells = <1>; + compatible = "ralink,rt3052-eval-board", "ralink,rt3052-soc"; + model = "Ralink RT3052 evaluation board"; + +@@ -12,12 +10,8 @@ + reg = <0x0 0x2000000>; + }; + +- palmbus@10000000 { +- sysc@0 { +- ralink,pinmmux = "uartlite", "spi"; +- ralink,uartmux = "gpio"; +- ralink,wdtmux = <0>; +- }; ++ chosen { ++ bootargs = "console=ttyS0,57600"; + }; + + cfi@1f000000 { +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0118-DT-MIPS-ralink-add-RT2880-dts-files.patch b/target/linux/ramips/patches-3.9/0118-DT-MIPS-ralink-add-RT2880-dts-files.patch new file mode 100644 index 0000000000..b47dec8c95 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0118-DT-MIPS-ralink-add-RT2880-dts-files.patch @@ -0,0 +1,160 @@ +From f88ca014e92cf209c0e5b6f68b45e3ca0ada6c45 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 06:27:37 +0000 +Subject: [PATCH 118/164] DT: MIPS: ralink: add RT2880 dts files + +Add a dtsi file for RT2880 SoC and a sample dts file. + +Signed-off-by: John Crispin +Acked-by: Grant Likely +Patchwork: http://patchwork.linux-mips.org/patch/5188/ +--- + arch/mips/ralink/Kconfig | 4 +++ + arch/mips/ralink/dts/Makefile | 1 + + arch/mips/ralink/dts/rt2880.dtsi | 58 ++++++++++++++++++++++++++++++++++ + arch/mips/ralink/dts/rt2880_eval.dts | 46 +++++++++++++++++++++++++++ + 4 files changed, 109 insertions(+) + create mode 100644 arch/mips/ralink/dts/rt2880.dtsi + create mode 100644 arch/mips/ralink/dts/rt2880_eval.dts + +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 86f6c77..2f6fbb8 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -34,6 +34,10 @@ choice + config DTB_RT_NONE + bool "None" + ++ config DTB_RT2880_EVAL ++ bool "RT2880 eval kit" ++ depends on SOC_RT288X ++ + config DTB_RT305X_EVAL + bool "RT305x eval kit" + depends on SOC_RT305X +diff --git a/arch/mips/ralink/dts/Makefile b/arch/mips/ralink/dts/Makefile +index 1a69fb3..f635a01 100644 +--- a/arch/mips/ralink/dts/Makefile ++++ b/arch/mips/ralink/dts/Makefile +@@ -1 +1,2 @@ ++obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o + obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o +diff --git a/arch/mips/ralink/dts/rt2880.dtsi b/arch/mips/ralink/dts/rt2880.dtsi +new file mode 100644 +index 0000000..182afde +--- /dev/null ++++ b/arch/mips/ralink/dts/rt2880.dtsi +@@ -0,0 +1,58 @@ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "ralink,rt2880-soc"; ++ ++ cpus { ++ cpu@0 { ++ compatible = "mips,mips4KEc"; ++ }; ++ }; ++ ++ cpuintc: cpuintc@0 { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ compatible = "mti,cpu-interrupt-controller"; ++ }; ++ ++ palmbus@300000 { ++ compatible = "palmbus"; ++ reg = <0x300000 0x200000>; ++ ranges = <0x0 0x300000 0x1FFFFF>; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ sysc@0 { ++ compatible = "ralink,rt2880-sysc"; ++ reg = <0x0 0x100>; ++ }; ++ ++ intc: intc@200 { ++ compatible = "ralink,rt2880-intc"; ++ reg = <0x200 0x100>; ++ ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <2>; ++ }; ++ ++ memc@300 { ++ compatible = "ralink,rt2880-memc"; ++ reg = <0x300 0x100>; ++ }; ++ ++ uartlite@c00 { ++ compatible = "ralink,rt2880-uart", "ns16550a"; ++ reg = <0xc00 0x100>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <8>; ++ ++ reg-shift = <2>; ++ }; ++ }; ++}; +diff --git a/arch/mips/ralink/dts/rt2880_eval.dts b/arch/mips/ralink/dts/rt2880_eval.dts +new file mode 100644 +index 0000000..322d700 +--- /dev/null ++++ b/arch/mips/ralink/dts/rt2880_eval.dts +@@ -0,0 +1,46 @@ ++/dts-v1/; ++ ++/include/ "rt2880.dtsi" ++ ++/ { ++ compatible = "ralink,rt2880-eval-board", "ralink,rt2880-soc"; ++ model = "Ralink RT2880 evaluation board"; ++ ++ memory@0 { ++ reg = <0x8000000 0x2000000>; ++ }; ++ ++ chosen { ++ bootargs = "console=ttyS0,57600"; ++ }; ++ ++ cfi@1f000000 { ++ compatible = "cfi-flash"; ++ reg = <0x1f000000 0x400000>; ++ ++ bank-width = <2>; ++ device-width = <2>; ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ partition@0 { ++ label = "uboot"; ++ reg = <0x0 0x30000>; ++ read-only; ++ }; ++ partition@30000 { ++ label = "uboot-env"; ++ reg = <0x30000 0x10000>; ++ read-only; ++ }; ++ partition@40000 { ++ label = "calibration"; ++ reg = <0x40000 0x10000>; ++ read-only; ++ }; ++ partition@50000 { ++ label = "linux"; ++ reg = <0x50000 0x3b0000>; ++ }; ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0119-DT-MIPS-ralink-add-RT3883-dts-files.patch b/target/linux/ramips/patches-3.9/0119-DT-MIPS-ralink-add-RT3883-dts-files.patch new file mode 100644 index 0000000000..334ddee079 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0119-DT-MIPS-ralink-add-RT3883-dts-files.patch @@ -0,0 +1,131 @@ +From 20c94bd844c5743d58ad8d8df81460e049f9df4b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 06:27:39 +0000 +Subject: [PATCH 119/164] DT: MIPS: ralink: add RT3883 dts files + +Add a dtsi file for RT3883 SoC and a sample dts file. + +Signed-off-by: John Crispin +Acked-by: Grant Likely +Patchwork: http://patchwork.linux-mips.org/patch/5189/ +--- + arch/mips/ralink/Kconfig | 4 +++ + arch/mips/ralink/dts/Makefile | 1 + + arch/mips/ralink/dts/rt3883.dtsi | 58 ++++++++++++++++++++++++++++++++++ + arch/mips/ralink/dts/rt3883_eval.dts | 16 ++++++++++ + 4 files changed, 79 insertions(+) + create mode 100644 arch/mips/ralink/dts/rt3883.dtsi + create mode 100644 arch/mips/ralink/dts/rt3883_eval.dts + +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 2f6fbb8..493411f 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -42,6 +42,10 @@ choice + bool "RT305x eval kit" + depends on SOC_RT305X + ++ config DTB_RT3883_EVAL ++ bool "RT3883 eval kit" ++ depends on SOC_RT3883 ++ + endchoice + + endif +diff --git a/arch/mips/ralink/dts/Makefile b/arch/mips/ralink/dts/Makefile +index f635a01..040a986 100644 +--- a/arch/mips/ralink/dts/Makefile ++++ b/arch/mips/ralink/dts/Makefile +@@ -1,2 +1,3 @@ + obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o + obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o ++obj-$(CONFIG_DTB_RT3883_EVAL) := rt3883_eval.dtb.o +diff --git a/arch/mips/ralink/dts/rt3883.dtsi b/arch/mips/ralink/dts/rt3883.dtsi +new file mode 100644 +index 0000000..3b131dd +--- /dev/null ++++ b/arch/mips/ralink/dts/rt3883.dtsi +@@ -0,0 +1,58 @@ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "ralink,rt3883-soc"; ++ ++ cpus { ++ cpu@0 { ++ compatible = "mips,mips74Kc"; ++ }; ++ }; ++ ++ cpuintc: cpuintc@0 { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ compatible = "mti,cpu-interrupt-controller"; ++ }; ++ ++ palmbus@10000000 { ++ compatible = "palmbus"; ++ reg = <0x10000000 0x200000>; ++ ranges = <0x0 0x10000000 0x1FFFFF>; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ sysc@0 { ++ compatible = "ralink,rt3883-sysc", "ralink,rt3050-sysc"; ++ reg = <0x0 0x100>; ++ }; ++ ++ intc: intc@200 { ++ compatible = "ralink,rt3883-intc", "ralink,rt2880-intc"; ++ reg = <0x200 0x100>; ++ ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <2>; ++ }; ++ ++ memc@300 { ++ compatible = "ralink,rt3883-memc", "ralink,rt3050-memc"; ++ reg = <0x300 0x100>; ++ }; ++ ++ uartlite@c00 { ++ compatible = "ralink,rt3883-uart", "ralink,rt2880-uart", "ns16550a"; ++ reg = <0xc00 0x100>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <12>; ++ ++ reg-shift = <2>; ++ }; ++ }; ++}; +diff --git a/arch/mips/ralink/dts/rt3883_eval.dts b/arch/mips/ralink/dts/rt3883_eval.dts +new file mode 100644 +index 0000000..2fa6b33 +--- /dev/null ++++ b/arch/mips/ralink/dts/rt3883_eval.dts +@@ -0,0 +1,16 @@ ++/dts-v1/; ++ ++/include/ "rt3883.dtsi" ++ ++/ { ++ compatible = "ralink,rt3883-eval-board", "ralink,rt3883-soc"; ++ model = "Ralink RT3883 evaluation board"; ++ ++ memory@0 { ++ reg = <0x0 0x2000000>; ++ }; ++ ++ chosen { ++ bootargs = "console=ttyS0,57600"; ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0120-DT-MIPS-ralink-add-MT7620A-dts-files.patch b/target/linux/ramips/patches-3.9/0120-DT-MIPS-ralink-add-MT7620A-dts-files.patch new file mode 100644 index 0000000000..d96e10de9f --- /dev/null +++ b/target/linux/ramips/patches-3.9/0120-DT-MIPS-ralink-add-MT7620A-dts-files.patch @@ -0,0 +1,132 @@ +From 248c7a2b678eee7da39363b1f097ea7eedceb435 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Apr 2013 06:27:41 +0000 +Subject: [PATCH 120/164] DT: MIPS: ralink: add MT7620A dts files + +Add a dtsi file for MT7620A SoC and a sample dts file. + +Signed-off-by: John Crispin +Acked-by: Grant Likely +Patchwork: http://patchwork.linux-mips.org/patch/5190/ +--- + arch/mips/ralink/Kconfig | 4 +++ + arch/mips/ralink/dts/Makefile | 1 + + arch/mips/ralink/dts/mt7620a.dtsi | 58 +++++++++++++++++++++++++++++++++ + arch/mips/ralink/dts/mt7620a_eval.dts | 16 +++++++++ + 4 files changed, 79 insertions(+) + create mode 100644 arch/mips/ralink/dts/mt7620a.dtsi + create mode 100644 arch/mips/ralink/dts/mt7620a_eval.dts + +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 493411f..026e823 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -46,6 +46,10 @@ choice + bool "RT3883 eval kit" + depends on SOC_RT3883 + ++ config DTB_MT7620A_EVAL ++ bool "MT7620A eval kit" ++ depends on SOC_MT7620 ++ + endchoice + + endif +diff --git a/arch/mips/ralink/dts/Makefile b/arch/mips/ralink/dts/Makefile +index 040a986..18194fa 100644 +--- a/arch/mips/ralink/dts/Makefile ++++ b/arch/mips/ralink/dts/Makefile +@@ -1,3 +1,4 @@ + obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o + obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o + obj-$(CONFIG_DTB_RT3883_EVAL) := rt3883_eval.dtb.o ++obj-$(CONFIG_DTB_MT7620A_EVAL) := mt7620a_eval.dtb.o +diff --git a/arch/mips/ralink/dts/mt7620a.dtsi b/arch/mips/ralink/dts/mt7620a.dtsi +new file mode 100644 +index 0000000..08bf24f +--- /dev/null ++++ b/arch/mips/ralink/dts/mt7620a.dtsi +@@ -0,0 +1,58 @@ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "ralink,mtk7620a-soc"; ++ ++ cpus { ++ cpu@0 { ++ compatible = "mips,mips24KEc"; ++ }; ++ }; ++ ++ cpuintc: cpuintc@0 { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ compatible = "mti,cpu-interrupt-controller"; ++ }; ++ ++ palmbus@10000000 { ++ compatible = "palmbus"; ++ reg = <0x10000000 0x200000>; ++ ranges = <0x0 0x10000000 0x1FFFFF>; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ sysc@0 { ++ compatible = "ralink,mt7620a-sysc"; ++ reg = <0x0 0x100>; ++ }; ++ ++ intc: intc@200 { ++ compatible = "ralink,mt7620a-intc", "ralink,rt2880-intc"; ++ reg = <0x200 0x100>; ++ ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <2>; ++ }; ++ ++ memc@300 { ++ compatible = "ralink,mt7620a-memc", "ralink,rt3050-memc"; ++ reg = <0x300 0x100>; ++ }; ++ ++ uartlite@c00 { ++ compatible = "ralink,mt7620a-uart", "ralink,rt2880-uart", "ns16550a"; ++ reg = <0xc00 0x100>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <12>; ++ ++ reg-shift = <2>; ++ }; ++ }; ++}; +diff --git a/arch/mips/ralink/dts/mt7620a_eval.dts b/arch/mips/ralink/dts/mt7620a_eval.dts +new file mode 100644 +index 0000000..35eb874 +--- /dev/null ++++ b/arch/mips/ralink/dts/mt7620a_eval.dts +@@ -0,0 +1,16 @@ ++/dts-v1/; ++ ++/include/ "mt7620a.dtsi" ++ ++/ { ++ compatible = "ralink,mt7620a-eval-board", "ralink,mt7620a-soc"; ++ model = "Ralink MT7620A evaluation board"; ++ ++ memory@0 { ++ reg = <0x0 0x2000000>; ++ }; ++ ++ chosen { ++ bootargs = "console=ttyS0,57600"; ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0121-MIPS-add-detect_memory_region.patch b/target/linux/ramips/patches-3.9/0121-MIPS-add-detect_memory_region.patch new file mode 100644 index 0000000000..90ca369901 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0121-MIPS-add-detect_memory_region.patch @@ -0,0 +1,68 @@ +From 2ccca2ac0b54eb21890412c888cf5a5bda656bba Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 13:15:47 +0200 +Subject: [PATCH 121/164] MIPS: add detect_memory_region() + +Add a generic way of detecting the available RAM. This function is based on the +implementation already used by ath79. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5178/ +--- + arch/mips/include/asm/bootinfo.h | 1 + + arch/mips/kernel/setup.c | 20 ++++++++++++++++++++ + 2 files changed, 21 insertions(+) + +diff --git a/arch/mips/include/asm/bootinfo.h b/arch/mips/include/asm/bootinfo.h +index b71dd5b..4d2cdea 100644 +--- a/arch/mips/include/asm/bootinfo.h ++++ b/arch/mips/include/asm/bootinfo.h +@@ -104,6 +104,7 @@ struct boot_mem_map { + extern struct boot_mem_map boot_mem_map; + + extern void add_memory_region(phys_t start, phys_t size, long type); ++extern void detect_memory_region(phys_t start, phys_t sz_min, phys_t sz_max); + + extern void prom_init(void); + extern void prom_free_prom_memory(void); +diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c +index 4c774d5..7325793 100644 +--- a/arch/mips/kernel/setup.c ++++ b/arch/mips/kernel/setup.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -122,6 +123,25 @@ void __init add_memory_region(phys_t start, phys_t size, long type) + boot_mem_map.nr_map++; + } + ++void __init detect_memory_region(phys_t start, phys_t sz_min, phys_t sz_max) ++{ ++ phys_t size; ++ ++ for (size = sz_min; size < sz_max; size <<= 1) { ++ if (!memcmp(detect_memory_region, ++ detect_memory_region + size, 1024)) ++ break; ++ } ++ ++ pr_debug("Memory: %lluMB of RAM detected at 0x%llx (min: %lluMB, max: %lluMB)\n", ++ ((unsigned long long) size) / SZ_1M, ++ (unsigned long long) start, ++ ((unsigned long long) sz_min) / SZ_1M, ++ ((unsigned long long) sz_max) / SZ_1M); ++ ++ add_memory_region(start, size, BOOT_MEM_RAM); ++} ++ + static void __init print_memory_map(void) + { + int i; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0122-MIPS-ralink-add-memory-definition-to-struct-ralink_s.patch b/target/linux/ramips/patches-3.9/0122-MIPS-ralink-add-memory-definition-to-struct-ralink_s.patch new file mode 100644 index 0000000000..25e0711eb1 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0122-MIPS-ralink-add-memory-definition-to-struct-ralink_s.patch @@ -0,0 +1,34 @@ +From 438579e0fd477cec8584bdf012ea51cf4ad8e05c Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 15:10:14 +0200 +Subject: [PATCH 122/164] MIPS: ralink: add memory definition to struct + ralink_soc_info + +Depending on the actual SoC we have a different base address as well as minimum +and maximum size for RAM. Add these fields to the per SoC structure. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5179/ +--- + arch/mips/ralink/common.h | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h +index 299119b..83144c3 100644 +--- a/arch/mips/ralink/common.h ++++ b/arch/mips/ralink/common.h +@@ -33,6 +33,11 @@ extern struct ralink_pinmux rt_gpio_pinmux; + struct ralink_soc_info { + unsigned char sys_type[RAMIPS_SYS_TYPE_LEN]; + unsigned char *compatible; ++ ++ unsigned long mem_base; ++ unsigned long mem_size; ++ unsigned long mem_size_min; ++ unsigned long mem_size_max; + }; + extern struct ralink_soc_info soc_info; + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0123-MIPS-ralink-add-memory-definition-for-RT305x.patch b/target/linux/ramips/patches-3.9/0123-MIPS-ralink-add-memory-definition-for-RT305x.patch new file mode 100644 index 0000000000..0921629c22 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0123-MIPS-ralink-add-memory-definition-for-RT305x.patch @@ -0,0 +1,96 @@ +From 1a255c5e67baad1735f985ad818d2b970fcc6808 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 15:13:40 +0200 +Subject: [PATCH 123/164] MIPS: ralink: add memory definition for RT305x + +Populate struct soc_info with the data that describes our RAM window. + +As memory detection fails on RT5350 we read the amount of available memory +from the system controller. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5180/ +--- + arch/mips/include/asm/mach-ralink/rt305x.h | 6 ++++ + arch/mips/ralink/rt305x.c | 45 ++++++++++++++++++++++++++++ + 2 files changed, 51 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/rt305x.h b/arch/mips/include/asm/mach-ralink/rt305x.h +index 80cda8a..069bf37 100644 +--- a/arch/mips/include/asm/mach-ralink/rt305x.h ++++ b/arch/mips/include/asm/mach-ralink/rt305x.h +@@ -157,4 +157,10 @@ static inline int soc_is_rt5350(void) + #define RT3352_RSTCTRL_UDEV BIT(25) + #define RT3352_SYSCFG1_USB0_HOST_MODE BIT(10) + ++#define RT305X_SDRAM_BASE 0x00000000 ++#define RT305X_MEM_SIZE_MIN 2 ++#define RT305X_MEM_SIZE_MAX 64 ++#define RT3352_MEM_SIZE_MIN 2 ++#define RT3352_MEM_SIZE_MAX 256 ++ + #endif +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index 6aa3cb1..ca7ee3a 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -122,6 +122,40 @@ struct ralink_pinmux rt_gpio_pinmux = { + .wdt_reset = rt305x_wdt_reset, + }; + ++static unsigned long rt5350_get_mem_size(void) ++{ ++ void __iomem *sysc = (void __iomem *) KSEG1ADDR(RT305X_SYSC_BASE); ++ unsigned long ret; ++ u32 t; ++ ++ t = __raw_readl(sysc + SYSC_REG_SYSTEM_CONFIG); ++ t = (t >> RT5350_SYSCFG0_DRAM_SIZE_SHIFT) & ++ RT5350_SYSCFG0_DRAM_SIZE_MASK; ++ ++ switch (t) { ++ case RT5350_SYSCFG0_DRAM_SIZE_2M: ++ ret = 2; ++ break; ++ case RT5350_SYSCFG0_DRAM_SIZE_8M: ++ ret = 8; ++ break; ++ case RT5350_SYSCFG0_DRAM_SIZE_16M: ++ ret = 16; ++ break; ++ case RT5350_SYSCFG0_DRAM_SIZE_32M: ++ ret = 32; ++ break; ++ case RT5350_SYSCFG0_DRAM_SIZE_64M: ++ ret = 64; ++ break; ++ default: ++ panic("rt5350: invalid DRAM size: %u", t); ++ break; ++ } ++ ++ return ret; ++} ++ + void __init ralink_clk_init(void) + { + unsigned long cpu_rate, sys_rate, wdt_rate, uart_rate; +@@ -252,4 +286,15 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + name, + (id >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK, + (id & CHIP_ID_REV_MASK)); ++ ++ soc_info->mem_base = RT305X_SDRAM_BASE; ++ if (soc_is_rt5350()) { ++ soc_info->mem_size = rt5350_get_mem_size(); ++ } else if (soc_is_rt305x() || soc_is_rt3350()) { ++ soc_info->mem_size_min = RT305X_MEM_SIZE_MIN; ++ soc_info->mem_size_max = RT305X_MEM_SIZE_MAX; ++ } else if (soc_is_rt3352()) { ++ soc_info->mem_size_min = RT3352_MEM_SIZE_MIN; ++ soc_info->mem_size_max = RT3352_MEM_SIZE_MAX; ++ } + } +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0124-MIPS-ralink-add-memory-definition-for-RT2880.patch b/target/linux/ramips/patches-3.9/0124-MIPS-ralink-add-memory-definition-for-RT2880.patch new file mode 100644 index 0000000000..43adf86a39 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0124-MIPS-ralink-add-memory-definition-for-RT2880.patch @@ -0,0 +1,43 @@ +From 45529406ea5b481da5986e0a14ae26a8c103691b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 15:37:37 +0200 +Subject: [PATCH 124/164] MIPS: ralink: add memory definition for RT2880 + +Populate struct soc_info with the data that describes our RAM window. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5181/ +--- + arch/mips/include/asm/mach-ralink/rt288x.h | 4 ++++ + arch/mips/ralink/rt288x.c | 4 ++++ + 2 files changed, 8 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/rt288x.h b/arch/mips/include/asm/mach-ralink/rt288x.h +index ad8b42d..03ad716 100644 +--- a/arch/mips/include/asm/mach-ralink/rt288x.h ++++ b/arch/mips/include/asm/mach-ralink/rt288x.h +@@ -46,4 +46,8 @@ + + #define CLKCFG_SRAM_CS_N_WDT BIT(9) + ++#define RT2880_SDRAM_BASE 0x08000000 ++#define RT2880_MEM_SIZE_MIN 2 ++#define RT2880_MEM_SIZE_MAX 128 ++ + #endif +diff --git a/arch/mips/ralink/rt288x.c b/arch/mips/ralink/rt288x.c +index 1e0788e..f87de1a 100644 +--- a/arch/mips/ralink/rt288x.c ++++ b/arch/mips/ralink/rt288x.c +@@ -136,4 +136,8 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + name, + (id >> CHIP_ID_ID_SHIFT) & CHIP_ID_ID_MASK, + (id & CHIP_ID_REV_MASK)); ++ ++ soc_info->mem_base = RT2880_SDRAM_BASE; ++ soc_info->mem_size_min = RT2880_MEM_SIZE_MIN; ++ soc_info->mem_size_max = RT2880_MEM_SIZE_MAX; + } +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0125-MIPS-ralink-add-memory-definition-for-RT3883.patch b/target/linux/ramips/patches-3.9/0125-MIPS-ralink-add-memory-definition-for-RT3883.patch new file mode 100644 index 0000000000..ab25311075 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0125-MIPS-ralink-add-memory-definition-for-RT3883.patch @@ -0,0 +1,51 @@ +From d771f446558ef408ca97c391887ae843dd23c358 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 20:23:19 +0200 +Subject: [PATCH 125/164] MIPS: ralink: add memory definition for RT3883 + +Populate struct soc_info with the data that describes our RAM window. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5182/ +--- + arch/mips/include/asm/mach-ralink/rt3883.h | 5 +++++ + arch/mips/ralink/rt3883.c | 4 ++++ + 2 files changed, 9 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/rt3883.h b/arch/mips/include/asm/mach-ralink/rt3883.h +index b91c6c1..058382f 100644 +--- a/arch/mips/include/asm/mach-ralink/rt3883.h ++++ b/arch/mips/include/asm/mach-ralink/rt3883.h +@@ -152,6 +152,7 @@ + #define RT3883_GPIO_SPI_MISO 6 + #define RT3883_GPIO_7 7 + #define RT3883_GPIO_10 10 ++#define RT3883_GPIO_11 11 + #define RT3883_GPIO_14 14 + #define RT3883_GPIO_UART1_TXD 15 + #define RT3883_GPIO_UART1_RXD 16 +@@ -244,4 +245,8 @@ + #define RT3883_FLASH_CFG_WIDTH_16BIT 0x1 + #define RT3883_FLASH_CFG_WIDTH_32BIT 0x2 + ++#define RT3883_SDRAM_BASE 0x00000000 ++#define RT3883_MEM_SIZE_MIN 2 ++#define RT3883_MEM_SIZE_MAX 256 ++ + #endif /* _RT3883_REGS_H_ */ +diff --git a/arch/mips/ralink/rt3883.c b/arch/mips/ralink/rt3883.c +index 2d90aa9..afbf2ce 100644 +--- a/arch/mips/ralink/rt3883.c ++++ b/arch/mips/ralink/rt3883.c +@@ -239,4 +239,8 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + name, + (id >> RT3883_REVID_VER_ID_SHIFT) & RT3883_REVID_VER_ID_MASK, + (id & RT3883_REVID_ECO_ID_MASK)); ++ ++ soc_info->mem_base = RT3883_SDRAM_BASE; ++ soc_info->mem_size_min = RT3883_MEM_SIZE_MIN; ++ soc_info->mem_size_max = RT3883_MEM_SIZE_MAX; + } +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0126-MIPS-ralink-add-memory-definition-for-MT7620.patch b/target/linux/ramips/patches-3.9/0126-MIPS-ralink-add-memory-definition-for-MT7620.patch new file mode 100644 index 0000000000..001483873d --- /dev/null +++ b/target/linux/ramips/patches-3.9/0126-MIPS-ralink-add-memory-definition-for-MT7620.patch @@ -0,0 +1,65 @@ +From 0c6c7304e33f3decff3293739076f29314ce535e Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 14 Apr 2013 09:55:29 +0200 +Subject: [PATCH 126/164] MIPS: ralink: add memory definition for MT7620 + +Populate struct soc_info with the data that describes our RAM window. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5183/ +--- + arch/mips/include/asm/mach-ralink/mt7620.h | 8 ++++++++ + arch/mips/ralink/mt7620.c | 20 ++++++++++++++++++++ + 2 files changed, 28 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/mt7620.h b/arch/mips/include/asm/mach-ralink/mt7620.h +index b272649..9809972 100644 +--- a/arch/mips/include/asm/mach-ralink/mt7620.h ++++ b/arch/mips/include/asm/mach-ralink/mt7620.h +@@ -50,6 +50,14 @@ + #define SYSCFG0_DRAM_TYPE_DDR1 1 + #define SYSCFG0_DRAM_TYPE_DDR2 2 + ++#define MT7620_DRAM_BASE 0x0 ++#define MT7620_SDRAM_SIZE_MIN 2 ++#define MT7620_SDRAM_SIZE_MAX 64 ++#define MT7620_DDR1_SIZE_MIN 32 ++#define MT7620_DDR1_SIZE_MAX 128 ++#define MT7620_DDR2_SIZE_MIN 32 ++#define MT7620_DDR2_SIZE_MAX 256 ++ + #define MT7620_GPIO_MODE_I2C BIT(0) + #define MT7620_GPIO_MODE_UART0_SHIFT 2 + #define MT7620_GPIO_MODE_UART0_MASK 0x7 +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index eb00ab8..98ddb93 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -211,4 +211,24 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + + cfg0 = __raw_readl(sysc + SYSC_REG_SYSTEM_CONFIG0); + dram_type = (cfg0 >> SYSCFG0_DRAM_TYPE_SHIFT) & SYSCFG0_DRAM_TYPE_MASK; ++ ++ switch (dram_type) { ++ case SYSCFG0_DRAM_TYPE_SDRAM: ++ soc_info->mem_size_min = MT7620_SDRAM_SIZE_MIN; ++ soc_info->mem_size_max = MT7620_SDRAM_SIZE_MAX; ++ break; ++ ++ case SYSCFG0_DRAM_TYPE_DDR1: ++ soc_info->mem_size_min = MT7620_DDR1_SIZE_MIN; ++ soc_info->mem_size_max = MT7620_DDR1_SIZE_MAX; ++ break; ++ ++ case SYSCFG0_DRAM_TYPE_DDR2: ++ soc_info->mem_size_min = MT7620_DDR2_SIZE_MIN; ++ soc_info->mem_size_max = MT7620_DDR2_SIZE_MAX; ++ break; ++ default: ++ BUG(); ++ } ++ soc_info->mem_base = MT7620_DRAM_BASE; + } +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0127-MIPS-ralink-make-use-of-the-new-memory-detection-cod.patch b/target/linux/ramips/patches-3.9/0127-MIPS-ralink-make-use-of-the-new-memory-detection-cod.patch new file mode 100644 index 0000000000..a1eae9007f --- /dev/null +++ b/target/linux/ramips/patches-3.9/0127-MIPS-ralink-make-use-of-the-new-memory-detection-cod.patch @@ -0,0 +1,45 @@ +From 550797902b0285736caceaf3c01506077d5440ea Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Apr 2013 15:15:51 +0200 +Subject: [PATCH 127/164] MIPS: ralink: make use of the new memory detection + code + +Call detect_memory_region() from plat_mem_setup() unless the size was already +read from the system controller. + +Signed-off-by: John Crispin +Patchwork: http://patchwork.linux-mips.org/patch/5184/ +--- + arch/mips/ralink/of.c | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c +index 4165e70..fb15695 100644 +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -85,6 +86,14 @@ void __init plat_mem_setup(void) + * parsed resulting in our memory appearing + */ + __dt_setup_arch(&__dtb_start); ++ ++ if (soc_info.mem_size) ++ add_memory_region(soc_info.mem_base, soc_info.mem_size, ++ BOOT_MEM_RAM); ++ else ++ detect_memory_region(soc_info.mem_base, ++ soc_info.mem_size_min * SZ_1M, ++ soc_info.mem_size_max * SZ_1M); + } + + static int __init plat_of_setup(void) +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0128-MIPS-ralink-upstream-v3.10.patch b/target/linux/ramips/patches-3.9/0128-MIPS-ralink-upstream-v3.10.patch new file mode 100644 index 0000000000..105ef3eee8 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0128-MIPS-ralink-upstream-v3.10.patch @@ -0,0 +1,23 @@ +From b3c76fc6b7be5aa2514a268e405e9b6e1c3ebcf0 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 30 May 2013 16:16:13 +0200 +Subject: [PATCH 128/164] MIPS: ralink: upstream v3.10 + +patches prior to this were sent upstream and accepted for v3.10 + +Signed-off-by: John Crispin +--- + dummy | 1 + + 1 file changed, 1 insertion(+) + create mode 100644 dummy + +diff --git a/dummy b/dummy +new file mode 100644 +index 0000000..421376d +--- /dev/null ++++ b/dummy +@@ -0,0 +1 @@ ++dummy +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0129-MIPS-ralink-add-pinmux-driver.patch b/target/linux/ramips/patches-3.9/0129-MIPS-ralink-add-pinmux-driver.patch new file mode 100644 index 0000000000..c51a4f4215 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0129-MIPS-ralink-add-pinmux-driver.patch @@ -0,0 +1,141 @@ +From 596c13f9c14edfe1ed71fe64274825c1978c964c Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Apr 2013 23:11:42 +0200 +Subject: [PATCH 129/164] MIPS: ralink: add pinmux driver + +Add code to setup the pinmux on ralonk SoC. The SoC has a single 32 bit register +for this functionality with simple on/off bits. Building a full featured pinctrl +driver would be overkill. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Makefile | 2 +- + arch/mips/ralink/common.h | 2 ++ + arch/mips/ralink/of.c | 2 ++ + arch/mips/ralink/pinmux.c | 77 +++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 82 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/ralink/pinmux.c + +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index 38cf1a8..341b4de 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -6,7 +6,7 @@ + # Copyright (C) 2009-2011 Gabor Juhos + # Copyright (C) 2013 John Crispin + +-obj-y := prom.o of.o reset.o clk.o irq.o ++obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o + + obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o +diff --git a/arch/mips/ralink/common.h b/arch/mips/ralink/common.h +index 83144c3..f113fd6 100644 +--- a/arch/mips/ralink/common.h ++++ b/arch/mips/ralink/common.h +@@ -50,4 +50,6 @@ extern void prom_soc_init(struct ralink_soc_info *soc_info); + + __iomem void *plat_of_remap_node(const char *node); + ++void ralink_pinmux(void); ++ + #endif /* _RALINK_COMMON_H__ */ +diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c +index fb15695..f916774 100644 +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -110,6 +110,8 @@ static int __init plat_of_setup(void) + if (of_platform_populate(NULL, of_ids, NULL, NULL)) + panic("failed to populate DT\n"); + ++ ralink_pinmux(); ++ + return 0; + } + +diff --git a/arch/mips/ralink/pinmux.c b/arch/mips/ralink/pinmux.c +new file mode 100644 +index 0000000..1720216 +--- /dev/null ++++ b/arch/mips/ralink/pinmux.c +@@ -0,0 +1,77 @@ ++/* ++ * 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. ++ * ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "common.h" ++ ++#define SYSC_REG_GPIO_MODE 0x60 ++ ++static int ralink_mux_mask(const char *name, struct ralink_pinmux_grp *grps, u32* mask) ++{ ++ for (; grps && grps->name; grps++) ++ if (!strcmp(grps->name, name)) { ++ *mask = grps->mask; ++ return 0; ++ } ++ ++ return -1; ++} ++ ++void ralink_pinmux(void) ++{ ++ const __be32 *wdt; ++ struct device_node *np; ++ struct property *prop; ++ const char *uart, *pin; ++ u32 mode = 0; ++ int m; ++ ++ np = of_find_compatible_node(NULL, NULL, "ralink,rt3050-sysc"); ++ if (!np) ++ return; ++ ++ of_property_for_each_string(np, "ralink,gpiomux", prop, pin) { ++ if (!ralink_mux_mask(pin, rt_gpio_pinmux.mode, &m)) { ++ mode |= m; ++ pr_debug("pinmux: registered gpiomux \"%s\"\n", pin); ++ } else { ++ pr_err("pinmux: failed to load \"%s\"\n", pin); ++ } ++ } ++ ++ of_property_for_each_string(np, "ralink,pinmux", prop, pin) { ++ if (!ralink_mux_mask(pin, rt_gpio_pinmux.mode, &m)) { ++ mode &= ~m; ++ pr_debug("pinmux: registered pinmux \"%s\"\n", pin); ++ } else { ++ pr_err("pinmux: failed to load group \"%s\"\n", pin); ++ } ++ } ++ ++ of_property_read_string(np, "ralink,uartmux", &uart); ++ if (uart) { ++ mode &= ~(rt_gpio_pinmux.uart_mask << rt_gpio_pinmux.uart_shift); ++ if (ralink_mux_mask(uart, rt_gpio_pinmux.uart, &m)) { ++ pr_err("pinmux: failed to load uartmux \"%s\"\n", uart); ++ mode |= rt_gpio_pinmux.uart_mask << rt_gpio_pinmux.uart_shift; ++ } else { ++ mode |= m << rt_gpio_pinmux.uart_shift; ++ pr_debug("pinmux: registered uartmux \"%s\"\n", uart); ++ } ++ } ++ ++ wdt = of_get_property(np, "ralink,wdtmux", NULL); ++ if (wdt && *wdt && rt_gpio_pinmux.wdt_reset) ++ rt_gpio_pinmux.wdt_reset(); ++ ++ rt_sysc_w32(mode, SYSC_REG_GPIO_MODE); ++} +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0130-MIPS-ralink-add-support-for-periodic-timer-irq.patch b/target/linux/ramips/patches-3.9/0130-MIPS-ralink-add-support-for-periodic-timer-irq.patch new file mode 100644 index 0000000000..d2b318712c --- /dev/null +++ b/target/linux/ramips/patches-3.9/0130-MIPS-ralink-add-support-for-periodic-timer-irq.patch @@ -0,0 +1,228 @@ +From f8496dd6c8fbcfd159390c31791fe2a86e48acb9 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 23 Mar 2013 19:44:41 +0100 +Subject: [PATCH 130/164] MIPS: ralink: add support for periodic timer irq + +Adds a driver for the periodic timer found on Ralink SoC. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Makefile | 2 +- + arch/mips/ralink/timer.c | 192 +++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 193 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/ralink/timer.c + +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index 341b4de..cae7d88 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -6,7 +6,7 @@ + # Copyright (C) 2009-2011 Gabor Juhos + # Copyright (C) 2013 John Crispin + +-obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o ++obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o timer.o + + obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o +diff --git a/arch/mips/ralink/timer.c b/arch/mips/ralink/timer.c +new file mode 100644 +index 0000000..0a6856c +--- /dev/null ++++ b/arch/mips/ralink/timer.c +@@ -0,0 +1,192 @@ ++/* ++ * 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. ++ * ++ * Copyright (C) 2013 John Crispin ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define TIMER_REG_TMRSTAT 0x00 ++#define TIMER_REG_TMR0LOAD 0x10 ++#define TIMER_REG_TMR0CTL 0x18 ++ ++#define TMRSTAT_TMR0INT BIT(0) ++ ++#define TMR0CTL_ENABLE BIT(7) ++#define TMR0CTL_MODE_PERIODIC BIT(4) ++#define TMR0CTL_PRESCALER 1 ++#define TMR0CTL_PRESCALE_VAL (0xf - TMR0CTL_PRESCALER) ++#define TMR0CTL_PRESCALE_DIV (65536 / BIT(TMR0CTL_PRESCALER)) ++ ++struct rt_timer { ++ struct device *dev; ++ void __iomem *membase; ++ int irq; ++ unsigned long timer_freq; ++ unsigned long timer_div; ++}; ++ ++static inline void rt_timer_w32(struct rt_timer *rt, u8 reg, u32 val) ++{ ++ __raw_writel(val, rt->membase + reg); ++} ++ ++static inline u32 rt_timer_r32(struct rt_timer *rt, u8 reg) ++{ ++ return __raw_readl(rt->membase + reg); ++} ++ ++static irqreturn_t rt_timer_irq(int irq, void *_rt) ++{ ++ struct rt_timer *rt = (struct rt_timer *) _rt; ++ ++ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); ++ rt_timer_w32(rt, TIMER_REG_TMRSTAT, TMRSTAT_TMR0INT); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++static int rt_timer_request(struct rt_timer *rt) ++{ ++ int err = request_irq(rt->irq, rt_timer_irq, IRQF_DISABLED, ++ dev_name(rt->dev), rt); ++ if (err) { ++ dev_err(rt->dev, "failed to request irq\n"); ++ } else { ++ u32 t = TMR0CTL_MODE_PERIODIC | TMR0CTL_PRESCALE_VAL; ++ rt_timer_w32(rt, TIMER_REG_TMR0CTL, t); ++ } ++ return err; ++} ++ ++static void rt_timer_free(struct rt_timer *rt) ++{ ++ free_irq(rt->irq, rt); ++} ++ ++static int rt_timer_config(struct rt_timer *rt, unsigned long divisor) ++{ ++ if (rt->timer_freq < divisor) ++ rt->timer_div = rt->timer_freq; ++ else ++ rt->timer_div = divisor; ++ ++ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); ++ ++ return 0; ++} ++ ++static int rt_timer_enable(struct rt_timer *rt) ++{ ++ u32 t; ++ ++ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); ++ ++ t = rt_timer_r32(rt, TIMER_REG_TMR0CTL); ++ t |= TMR0CTL_ENABLE; ++ rt_timer_w32(rt, TIMER_REG_TMR0CTL, t); ++ ++ return 0; ++} ++ ++static void rt_timer_disable(struct rt_timer *rt) ++{ ++ u32 t; ++ ++ t = rt_timer_r32(rt, TIMER_REG_TMR0CTL); ++ t &= ~TMR0CTL_ENABLE; ++ rt_timer_w32(rt, TIMER_REG_TMR0CTL, t); ++} ++ ++static int rt_timer_probe(struct platform_device *pdev) ++{ ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ struct rt_timer *rt; ++ struct clk *clk; ++ ++ if (!res) { ++ dev_err(&pdev->dev, "no memory resource found\n"); ++ return -EINVAL; ++ } ++ ++ rt = devm_kzalloc(&pdev->dev, sizeof(*rt), GFP_KERNEL); ++ if (!rt) { ++ dev_err(&pdev->dev, "failed to allocate memory\n"); ++ return -ENOMEM; ++ } ++ ++ rt->irq = platform_get_irq(pdev, 0); ++ if (!rt->irq) { ++ dev_err(&pdev->dev, "failed to load irq\n"); ++ return -ENOENT; ++ } ++ ++ rt->membase = devm_request_and_ioremap(&pdev->dev, res); ++ if (!rt->membase) { ++ dev_err(&pdev->dev, "failed to ioremap\n"); ++ return -ENOMEM; ++ } ++ ++ clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "failed get clock rate\n"); ++ return PTR_ERR(clk); ++ } ++ ++ rt->timer_freq = clk_get_rate(clk) / TMR0CTL_PRESCALE_DIV; ++ if (!rt->timer_freq) ++ return -EINVAL; ++ ++ rt->dev = &pdev->dev; ++ platform_set_drvdata(pdev, rt); ++ ++ rt_timer_request(rt); ++ rt_timer_config(rt, 2); ++ rt_timer_enable(rt); ++ ++ dev_info(&pdev->dev, "maximum frequncy is %luHz\n", rt->timer_freq); ++ ++ return 0; ++} ++ ++static int rt_timer_remove(struct platform_device *pdev) ++{ ++ struct rt_timer *rt = platform_get_drvdata(pdev); ++ ++ rt_timer_disable(rt); ++ rt_timer_free(rt); ++ ++ return 0; ++} ++ ++static const struct of_device_id rt_timer_match[] = { ++ { .compatible = "ralink,rt2880-timer" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt_timer_match); ++ ++static struct platform_driver rt_timer_driver = { ++ .probe = rt_timer_probe, ++ .remove = rt_timer_remove, ++ .driver = { ++ .name = "rt-timer", ++ .owner = THIS_MODULE, ++ .of_match_table = rt_timer_match ++ }, ++}; ++ ++module_platform_driver(rt_timer_driver); ++ ++MODULE_DESCRIPTION("Ralink RT2880 timer"); ++MODULE_AUTHOR("John Crispin +Date: Sun, 19 May 2013 00:42:23 +0200 +Subject: [PATCH 131/164] MIPS: ralink: add rt_sysc_m32 helper + +Signed-off-by: John Crispin +--- + arch/mips/include/asm/mach-ralink/ralink_regs.h | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/ralink_regs.h b/arch/mips/include/asm/mach-ralink/ralink_regs.h +index 5a508f9..bd93014 100644 +--- a/arch/mips/include/asm/mach-ralink/ralink_regs.h ++++ b/arch/mips/include/asm/mach-ralink/ralink_regs.h +@@ -26,6 +26,13 @@ static inline u32 rt_sysc_r32(unsigned reg) + return __raw_readl(rt_sysc_membase + reg); + } + ++static inline void rt_sysc_m32(u32 clr, u32 set, unsigned reg) ++{ ++ u32 val = rt_sysc_r32(reg) & ~clr; ++ ++ __raw_writel(val | set, rt_sysc_membase + reg); ++} ++ + static inline void rt_memc_w32(u32 val, unsigned reg) + { + __raw_writel(val, rt_memc_membase + reg); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0132-MIPS-ralink-make-mt7620-ram-detect-verbose.patch b/target/linux/ramips/patches-3.9/0132-MIPS-ralink-make-mt7620-ram-detect-verbose.patch new file mode 100644 index 0000000000..e98a254dd4 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0132-MIPS-ralink-make-mt7620-ram-detect-verbose.patch @@ -0,0 +1,39 @@ +From eb6f94f9d53a0eecf0ff8edd5318ee2dd5403a01 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 20 May 2013 20:30:11 +0200 +Subject: [PATCH 132/164] MIPS: ralink: make mt7620 ram detect verbose + +Make the code print which of SDRAM, DDR1 or DDR2 was detected. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/mt7620.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index 98ddb93..28350d0 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -214,16 +214,19 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + + switch (dram_type) { + case SYSCFG0_DRAM_TYPE_SDRAM: ++ pr_info("Board has SDRAM\n"); + soc_info->mem_size_min = MT7620_SDRAM_SIZE_MIN; + soc_info->mem_size_max = MT7620_SDRAM_SIZE_MAX; + break; + + case SYSCFG0_DRAM_TYPE_DDR1: ++ pr_info("Board has DDR1\n"); + soc_info->mem_size_min = MT7620_DDR1_SIZE_MIN; + soc_info->mem_size_max = MT7620_DDR1_SIZE_MAX; + break; + + case SYSCFG0_DRAM_TYPE_DDR2: ++ pr_info("Board has DDR2\n"); + soc_info->mem_size_min = MT7620_DDR2_SIZE_MIN; + soc_info->mem_size_max = MT7620_DDR2_SIZE_MAX; + break; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0133-MIPS-ralink-add-verbose-pmu-info.patch b/target/linux/ramips/patches-3.9/0133-MIPS-ralink-add-verbose-pmu-info.patch new file mode 100644 index 0000000000..5b9ac944a7 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0133-MIPS-ralink-add-verbose-pmu-info.patch @@ -0,0 +1,64 @@ +From c7150f86dfb0fe2613af3c5bd1c0c587130b9460 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 20 May 2013 20:57:09 +0200 +Subject: [PATCH 133/164] MIPS: ralink: add verbose pmu info + +Print the PMU and LDO settings on boot. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/mt7620.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index 28350d0..69729a5 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -20,6 +20,22 @@ + + #include "common.h" + ++/* analog */ ++#define PMU0_CFG 0x88 ++#define PMU_SW_SET BIT(28) ++#define A_DCDC_EN BIT(24) ++#define A_SSC_PERI BIT(19) ++#define A_SSC_GEN BIT(18) ++#define A_SSC_M 0x3 ++#define A_SSC_S 16 ++#define A_DLY_M 0x7 ++#define A_DLY_S 8 ++#define A_VTUNE_M 0xff ++ ++/* digital */ ++#define PMU1_CFG 0x8C ++#define DIG_SW_SEL BIT(25) ++ + /* does the board have sdram or ddram */ + static int dram_type; + +@@ -187,6 +203,8 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + u32 n1; + u32 rev; + u32 cfg0; ++ u32 pmu0; ++ u32 pmu1; + + n0 = __raw_readl(sysc + SYSC_REG_CHIP_NAME0); + n1 = __raw_readl(sysc + SYSC_REG_CHIP_NAME1); +@@ -234,4 +252,12 @@ void prom_soc_init(struct ralink_soc_info *soc_info) + BUG(); + } + soc_info->mem_base = MT7620_DRAM_BASE; ++ ++ pmu0 = __raw_readl(sysc + PMU0_CFG); ++ pmu1 = __raw_readl(sysc + PMU1_CFG); ++ ++ pr_info("Analog PMU set to %s control\n", ++ (pmu0 & PMU_SW_SET) ? ("sw") : ("hw")); ++ pr_info("Digital PMU set to %s control\n", ++ (pmu1 & DIG_SW_SEL) ? ("sw") : ("hw")); + } +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0134-MIPS-ralink-adds-a-bootrom-dumper-module.patch b/target/linux/ramips/patches-3.9/0134-MIPS-ralink-adds-a-bootrom-dumper-module.patch new file mode 100644 index 0000000000..602cf40a54 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0134-MIPS-ralink-adds-a-bootrom-dumper-module.patch @@ -0,0 +1,83 @@ +From 9dc3dba1bd621fe0e502d1d6efdfd64a0259a342 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 21 May 2013 15:50:31 +0200 +Subject: [PATCH 134/164] MIPS: ralink: adds a bootrom dumper module + +This patch adds a trivial driver that allows userland to extract the bootrom of +a SoC via debugfs. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Makefile | 2 ++ + arch/mips/ralink/bootrom.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 50 insertions(+) + create mode 100644 arch/mips/ralink/bootrom.c + +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index cae7d88..5fa6129 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -15,4 +15,6 @@ obj-$(CONFIG_SOC_MT7620) += mt7620.o + + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + ++obj-$(CONFIG_DEBUG_FS) += bootrom.o ++ + obj-y += dts/ +diff --git a/arch/mips/ralink/bootrom.c b/arch/mips/ralink/bootrom.c +new file mode 100644 +index 0000000..f926f6f +--- /dev/null ++++ b/arch/mips/ralink/bootrom.c +@@ -0,0 +1,48 @@ ++/* ++ * 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. ++ * ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++ ++#define BOOTROM_OFFSET 0x10118000 ++#define BOOTROM_SIZE 0x8000 ++ ++static void __iomem *membase = (void __iomem*) KSEG1ADDR(BOOTROM_OFFSET); ++ ++static int bootrom_show(struct seq_file *s, void *unused) ++{ ++ seq_write(s, membase, BOOTROM_SIZE); ++ ++ return 0; ++} ++ ++static int bootrom_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, bootrom_show, NULL); ++} ++ ++static const struct file_operations bootrom_file_ops = { ++ .open = bootrom_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int bootrom_setup(void) ++{ ++ if (!debugfs_create_file("bootrom", 0444, ++ NULL, NULL, &bootrom_file_ops)) { ++ pr_err("Failed to create bootrom debugfs file\n"); ++ ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++postcore_initcall(bootrom_setup); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0135-MIPS-ralink-add-missing-SZ_1M-multiplier.patch b/target/linux/ramips/patches-3.9/0135-MIPS-ralink-add-missing-SZ_1M-multiplier.patch new file mode 100644 index 0000000000..2d5c71f539 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0135-MIPS-ralink-add-missing-SZ_1M-multiplier.patch @@ -0,0 +1,29 @@ +From 4a8fcf59741a8b71f97823c31d461fa6d9089cb0 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 21 May 2013 17:15:54 +0200 +Subject: [PATCH 135/164] MIPS: ralink: add missing SZ_1M multiplier + +On RT5350 the memory size is set to Bytes and not MegaBytes due to a missing +multiplier. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/of.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c +index f916774..b25c1f2 100644 +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -88,7 +88,7 @@ void __init plat_mem_setup(void) + __dt_setup_arch(&__dtb_start); + + if (soc_info.mem_size) +- add_memory_region(soc_info.mem_base, soc_info.mem_size, ++ add_memory_region(soc_info.mem_base, soc_info.mem_size * SZ_1M, + BOOT_MEM_RAM); + else + detect_memory_region(soc_info.mem_base, +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0136-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch b/target/linux/ramips/patches-3.9/0136-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch new file mode 100644 index 0000000000..21ab341513 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0136-MIPS-use-set_mode-to-enable-disable-the-cevt-r4k-irq.patch @@ -0,0 +1,78 @@ +From 4977695198a8daf06746cb6c499d754040b6d0ac Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 3 May 2013 00:04:58 +0200 +Subject: [PATCH 136/164] MIPS: use set_mode() to enable/disable the cevt-r4k + irq + +Signed-off-by: John Crispin +--- + arch/mips/kernel/cevt-r4k.c | 39 ++++++++++++++++++++++++++------------- + 1 file changed, 26 insertions(+), 13 deletions(-) + +diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c +index 07b847d..d5d94e5 100644 +--- a/arch/mips/kernel/cevt-r4k.c ++++ b/arch/mips/kernel/cevt-r4k.c +@@ -39,12 +39,6 @@ static int mips_next_event(unsigned long delta, + + #endif /* CONFIG_MIPS_MT_SMTC */ + +-void mips_set_clock_mode(enum clock_event_mode mode, +- struct clock_event_device *evt) +-{ +- /* Nothing to do ... */ +-} +- + DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device); + int cp0_timer_irq_installed; + +@@ -89,6 +83,32 @@ struct irqaction c0_compare_irqaction = { + .name = "timer", + }; + ++void mips_set_clock_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ switch (mode) { ++ case CLOCK_EVT_MODE_ONESHOT: ++ if (cp0_timer_irq_installed) ++ break; ++ ++ cp0_timer_irq_installed = 1; ++ ++ setup_irq(evt->irq, &c0_compare_irqaction); ++ break; ++ ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ if (!cp0_timer_irq_installed) ++ break; ++ ++ cp0_timer_irq_installed = 0; ++ free_irq(evt->irq, &c0_compare_irqaction); ++ break; ++ ++ default: ++ pr_err("Unhandeled mips clock_mode\n"); ++ break; ++ } ++} + + void mips_event_handler(struct clock_event_device *dev) + { +@@ -208,13 +228,6 @@ int __cpuinit r4k_clockevent_init(void) + + clockevents_register_device(cd); + +- if (cp0_timer_irq_installed) +- return 0; +- +- cp0_timer_irq_installed = 1; +- +- setup_irq(irq, &c0_compare_irqaction); +- + return 0; + } + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0137-MIPS-ralink-add-illegal-access-driver.patch b/target/linux/ramips/patches-3.9/0137-MIPS-ralink-add-illegal-access-driver.patch new file mode 100644 index 0000000000..670b20052a --- /dev/null +++ b/target/linux/ramips/patches-3.9/0137-MIPS-ralink-add-illegal-access-driver.patch @@ -0,0 +1,121 @@ +From 0402961539c5a98f3e9eac439e03f89498d110a1 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 16 May 2013 23:28:23 +0200 +Subject: [PATCH 137/164] MIPS: ralink: add illegal access driver + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Makefile | 2 +- + arch/mips/ralink/ill_acc.c | 87 ++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 88 insertions(+), 1 deletion(-) + create mode 100644 arch/mips/ralink/ill_acc.c + +diff --git a/arch/mips/ralink/Makefile b/arch/mips/ralink/Makefile +index 5fa6129..55a5bfc 100644 +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -6,7 +6,7 @@ + # Copyright (C) 2009-2011 Gabor Juhos + # Copyright (C) 2013 John Crispin + +-obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o timer.o ++obj-y := prom.o of.o reset.o clk.o irq.o pinmux.o timer.o ill_acc.o + + obj-$(CONFIG_SOC_RT288X) += rt288x.o + obj-$(CONFIG_SOC_RT305X) += rt305x.o +diff --git a/arch/mips/ralink/ill_acc.c b/arch/mips/ralink/ill_acc.c +new file mode 100644 +index 0000000..4a3f696 +--- /dev/null ++++ b/arch/mips/ralink/ill_acc.c +@@ -0,0 +1,87 @@ ++/* ++ * 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. ++ * ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++ ++#include ++ ++#define REG_ILL_ACC_ADDR 0x10 ++#define REG_ILL_ACC_TYPE 0x14 ++ ++#define ILL_INT_STATUS BIT(31) ++#define ILL_ACC_WRITE BIT(30) ++#define ILL_ACC_LEN_M 0xff ++#define ILL_ACC_OFF_M 0xf ++#define ILL_ACC_OFF_S 16 ++#define ILL_ACC_ID_M 0x7 ++#define ILL_ACC_ID_S 8 ++ ++#define DRV_NAME "ill_acc" ++ ++static const char *ill_acc_ids[] = { ++ "cpu", "dma", "ppe", "pdma rx","pdma tx", "pci/e", "wmac", "usb", ++}; ++ ++static irqreturn_t ill_acc_irq_handler(int irq, void *_priv) ++{ ++ struct device *dev = (struct device *) _priv; ++ u32 addr = rt_memc_r32(REG_ILL_ACC_ADDR); ++ u32 type = rt_memc_r32(REG_ILL_ACC_TYPE); ++ ++ dev_err(dev, "illegal %s access from %s - addr:0x%08x offset:%d len:%d\n", ++ (type & ILL_ACC_WRITE) ? ("write") : ("read"), ++ ill_acc_ids[(type >> ILL_ACC_ID_S) & ILL_ACC_ID_M], ++ addr, (type >> ILL_ACC_OFF_S) & ILL_ACC_OFF_M, ++ type & ILL_ACC_LEN_M); ++ ++ rt_memc_w32(REG_ILL_ACC_TYPE, REG_ILL_ACC_TYPE); ++ ++ return IRQ_HANDLED; ++} ++ ++static int __init ill_acc_of_setup(void) ++{ ++ struct platform_device *pdev; ++ struct device_node *np; ++ int irq; ++ ++ /* somehow this driver breaks on RT5350 */ ++ if (of_machine_is_compatible("ralink,rt5350-soc")) ++ return -EINVAL; ++ ++ np = of_find_compatible_node(NULL, NULL, "ralink,rt3050-memc"); ++ if (!np) ++ return -EINVAL; ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev) { ++ pr_err("%s: failed to lookup pdev\n", np->name); ++ return -EINVAL; ++ } ++ ++ irq = irq_of_parse_and_map(np, 0); ++ if (!irq) { ++ dev_err(&pdev->dev, "failed to get irq\n"); ++ return -EINVAL; ++ } ++ ++ if (request_irq(irq, ill_acc_irq_handler, 0, "ill_acc", &pdev->dev)) { ++ dev_err(&pdev->dev, "failed to request irq\n"); ++ return -EINVAL; ++ } ++ ++ rt_memc_w32(ILL_INT_STATUS, REG_ILL_ACC_TYPE); ++ ++ dev_info(&pdev->dev, "irq registered\n"); ++ ++ return 0; ++} ++ ++arch_initcall(ill_acc_of_setup); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0138-MIPS-ralink-workaround-DTB-memory-issue.patch b/target/linux/ramips/patches-3.9/0138-MIPS-ralink-workaround-DTB-memory-issue.patch new file mode 100644 index 0000000000..dc113bd143 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0138-MIPS-ralink-workaround-DTB-memory-issue.patch @@ -0,0 +1,29 @@ +From bcffa7a81e00b8f67c459a2f5526edc0a5077794 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 23 May 2013 18:50:56 +0200 +Subject: [PATCH 138/164] MIPS: ralink: workaround DTB memory issue + +If the DTB is too big a bug happens on boot when init ram is freed. +This is a temporary fix until the real cause is found. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/of.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c +index b25c1f2..8efb02b 100644 +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -74,7 +74,7 @@ void __init device_tree_init(void) + unflatten_device_tree(); + + /* free the space reserved for the dt blob */ +- free_bootmem(base, size); ++ //free_bootmem(base, size); + } + + void __init plat_mem_setup(void) +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0139-MIPS-ralink-add-spi-clock-definition-to-mt7620a.patch b/target/linux/ramips/patches-3.9/0139-MIPS-ralink-add-spi-clock-definition-to-mt7620a.patch new file mode 100644 index 0000000000..c520b66e45 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0139-MIPS-ralink-add-spi-clock-definition-to-mt7620a.patch @@ -0,0 +1,25 @@ +From 17d26ed75f0981cdc497f9062aec5f66d3c44d88 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 23 May 2013 18:46:25 +0200 +Subject: [PATCH 139/164] MIPS: ralink: add spi clock definition to mt7620a + +Signed-off-by: John Crispin +--- + arch/mips/ralink/mt7620.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index 69729a5..08c96db6 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -183,6 +183,7 @@ void __init ralink_clk_init(void) + ralink_clk_add("cpu", cpu_rate); + ralink_clk_add("10000100.timer", 40000000); + ralink_clk_add("10000500.uart", 40000000); ++ ralink_clk_add("10000b00.spi", 40000000); + ralink_clk_add("10000c00.uartlite", 40000000); + } + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0140-MIPS-ralink-DTS-file-updates.patch b/target/linux/ramips/patches-3.9/0140-MIPS-ralink-DTS-file-updates.patch new file mode 100644 index 0000000000..b85ff9cee6 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0140-MIPS-ralink-DTS-file-updates.patch @@ -0,0 +1,1006 @@ +From cee339922876e924295c27e274923d1b381f5057 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 30 Apr 2013 17:27:46 +0200 +Subject: [PATCH 140/164] MIPS: ralink DTS file updates + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Kconfig | 8 + + arch/mips/ralink/dts/Makefile | 2 + + arch/mips/ralink/dts/mt7620a.dtsi | 238 ++++++++++++++++++++++++- + arch/mips/ralink/dts/mt7620a_eval.dts | 111 ++++++++++++ + arch/mips/ralink/dts/mt7620a_mt7610e_eval.dts | 99 ++++++++++ + arch/mips/ralink/dts/rt2880.dtsi | 17 ++ + arch/mips/ralink/dts/rt2880_eval.dts | 6 + + arch/mips/ralink/dts/rt3050.dtsi | 31 +++- + arch/mips/ralink/dts/rt3052_eval.dts | 19 +- + arch/mips/ralink/dts/rt5350.dtsi | 227 +++++++++++++++++++++++ + arch/mips/ralink/dts/rt5350_eval.dts | 69 +++++++ + 11 files changed, 824 insertions(+), 3 deletions(-) + create mode 100644 arch/mips/ralink/dts/mt7620a_mt7610e_eval.dts + create mode 100644 arch/mips/ralink/dts/rt5350.dtsi + create mode 100644 arch/mips/ralink/dts/rt5350_eval.dts + +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 026e823..38540a4 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -42,6 +42,10 @@ choice + bool "RT305x eval kit" + depends on SOC_RT305X + ++ config DTB_RT5350_EVAL ++ bool "RT5350 eval kit" ++ depends on SOC_RT305X ++ + config DTB_RT3883_EVAL + bool "RT3883 eval kit" + depends on SOC_RT3883 +@@ -50,6 +54,10 @@ choice + bool "MT7620A eval kit" + depends on SOC_MT7620 + ++ config DTB_MT7620A_MT7610E_EVAL ++ bool "MT7620A + MT7610E eval kit" ++ depends on SOC_MT7620 ++ + endchoice + + endif +diff --git a/arch/mips/ralink/dts/Makefile b/arch/mips/ralink/dts/Makefile +index 18194fa..0bd12b5 100644 +--- a/arch/mips/ralink/dts/Makefile ++++ b/arch/mips/ralink/dts/Makefile +@@ -1,4 +1,6 @@ + obj-$(CONFIG_DTB_RT2880_EVAL) := rt2880_eval.dtb.o + obj-$(CONFIG_DTB_RT305X_EVAL) := rt3052_eval.dtb.o ++obj-$(CONFIG_DTB_RT5350_EVAL) := rt5350_eval.dtb.o + obj-$(CONFIG_DTB_RT3883_EVAL) := rt3883_eval.dtb.o + obj-$(CONFIG_DTB_MT7620A_EVAL) := mt7620a_eval.dtb.o ++obj-$(CONFIG_DTB_MT7620A_MT7610E_EVAL) := mt7620a_mt7610e_eval.dtb.o +diff --git a/arch/mips/ralink/dts/mt7620a.dtsi b/arch/mips/ralink/dts/mt7620a.dtsi +index 08bf24f..104abfb 100644 +--- a/arch/mips/ralink/dts/mt7620a.dtsi ++++ b/arch/mips/ralink/dts/mt7620a.dtsi +@@ -25,14 +25,36 @@ + #size-cells = <1>; + + sysc@0 { +- compatible = "ralink,mt7620a-sysc"; ++ compatible = "ralink,mt7620a-sysc", "ralink,rt3050-sysc"; + reg = <0x0 0x100>; + }; + ++ timer@100 { ++ compatible = "ralink,mt7620a-timer", "ralink,rt2880-timer"; ++ reg = <0x100 0x20>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <1>; ++ }; ++ ++ watchdog@120 { ++ compatible = "ralink,mt7620a-wdt", "ralink,rt2880-wdt"; ++ reg = <0x120 0x10>; ++ ++ resets = <&rstctrl 8>; ++ reset-names = "wdt"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <1>; ++ }; ++ + intc: intc@200 { + compatible = "ralink,mt7620a-intc", "ralink,rt2880-intc"; + reg = <0x200 0x100>; + ++ resets = <&rstctrl 19>; ++ reset-names = "intc"; ++ + interrupt-controller; + #interrupt-cells = <1>; + +@@ -43,16 +65,230 @@ + memc@300 { + compatible = "ralink,mt7620a-memc", "ralink,rt3050-memc"; + reg = <0x300 0x100>; ++ ++ resets = <&rstctrl 20>; ++ reset-names = "mc"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <3>; ++ }; ++ ++ uart@500 { ++ compatible = "ralink,mt7620a-uart", "ralink,rt2880-uart", "ns16550a"; ++ reg = <0x500 0x100>; ++ ++ resets = <&rstctrl 12>; ++ reset-names = "uart"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <5>; ++ ++ reg-shift = <2>; ++ ++ status = "disabled"; ++ }; ++ ++ gpio0: gpio@600 { ++ compatible = "ralink,mt7620a-gpio", "ralink,rt2880-gpio"; ++ reg = <0x600 0x34>; ++ ++ resets = <&rstctrl 13>; ++ reset-names = "pio"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <6>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ ralink,gpio-base = <0>; ++ ralink,num-gpios = <24>; ++ ralink,register-map = [ 00 04 08 0c ++ 20 24 28 2c ++ 30 34 ]; ++ ++ status = "disabled"; ++ }; ++ ++ gpio1: gpio@638 { ++ compatible = "ralink,mt7620a-gpio", "ralink,rt2880-gpio"; ++ reg = <0x638 0x24>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <6>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ ralink,gpio-base = <24>; ++ ralink,num-gpios = <16>; ++ ralink,register-map = [ 00 04 08 0c ++ 10 14 18 1c ++ 20 24 ]; ++ ++ status = "disabled"; ++ }; ++ ++ gpio2: gpio@660 { ++ compatible = "ralink,mt7620a-gpio", "ralink,rt2880-gpio"; ++ reg = <0x660 0x24>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <6>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ ralink,gpio-base = <40>; ++ ralink,num-gpios = <32>; ++ ralink,register-map = [ 00 04 08 0c ++ 10 14 18 1c ++ 20 24 ]; ++ ++ status = "disabled"; ++ }; ++ ++ i2c@900 { ++ compatible = "link,mt7620a-i2c", "ralink,rt2880-i2c"; ++ reg = <0x900 0x100>; ++ ++ resets = <&rstctrl 16>; ++ reset-names = "i2c"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ spi@b00 { ++ compatible = "ralink,mt7620a-spi", "ralink,rt2880-spi"; ++ reg = <0xb00 0x100>; ++ ++ resets = <&rstctrl 18>; ++ reset-names = "spi"; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ status = "disabled"; + }; + + uartlite@c00 { + compatible = "ralink,mt7620a-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0xc00 0x100>; + ++ resets = <&rstctrl 19>; ++ reset-names = "uartl"; ++ + interrupt-parent = <&intc>; + interrupts = <12>; + + reg-shift = <2>; + }; ++ ++ systick@d00 { ++ compatible = "ralink,mt7620a-systick", "ralink,cevt-systick"; ++ reg = <0xd00 0x10>; ++ ++ resets = <&rstctrl 28>; ++ reset-names = "intc"; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <7>; ++ }; ++ ++ gdma@2800 { ++ compatible = "ralink,mt7620a-gdma", "ralink,rt2880-gdma"; ++ reg = <0x2800 0x800>; ++ ++ resets = <&rstctrl 14>; ++ reset-names = "dma"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <7>; ++ }; ++ }; ++ ++ rstctrl: rstctrl { ++ compatible = "ralink,mt7620a-reset", "ralink,rt2880-reset"; ++ #reset-cells = <1>; ++ }; ++ ++ ubsphy { ++ compatible = "ralink,mt7620a-usbphy"; ++ ++ resets = <&rstctrl 22 &rstctrl 25>; ++ reset-names = "host", "device"; ++ }; ++ ++ ethernet@10100000 { ++ compatible = "ralink,mt7620a-eth"; ++ reg = <0x10100000 10000>; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <5>; ++ ++ status = "disabled"; ++ ++ mdio-bus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ }; ++ ++ gsw@10110000 { ++ compatible = "ralink,mt7620a-gsw"; ++ reg = <0x10110000 8000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <17>; ++ ++ status = "disabled"; ++ }; ++ ++ sdhci@10130000 { ++ compatible = "ralink,mt7620a-sdhci"; ++ reg = <0x10130000 4000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <14>; ++ ++ status = "disabled"; ++ }; ++ ++ ehci@101c0000 { ++ compatible = "ralink,rt3xxx-ehci"; ++ reg = <0x101c0000 0x1000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <18>; ++ }; ++ ++ ohci@101c1000 { ++ compatible = "ralink,rt3xxx-ohci"; ++ reg = <0x101c1000 0x1000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <18>; ++ }; ++ ++ pcie@10140000 { ++ compatible = "ralink,mt7620a-pci"; ++ reg = <0x10140000 0x100 ++ 0x10142000 0x100>; ++ ++ resets = <&rstctrl 26>; ++ reset-names = "pcie0"; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <4>; ++ ++ status = "disabled"; + }; + }; +diff --git a/arch/mips/ralink/dts/mt7620a_eval.dts b/arch/mips/ralink/dts/mt7620a_eval.dts +index 35eb874..b56f449 100644 +--- a/arch/mips/ralink/dts/mt7620a_eval.dts ++++ b/arch/mips/ralink/dts/mt7620a_eval.dts +@@ -13,4 +13,115 @@ + chosen { + bootargs = "console=ttyS0,57600"; + }; ++ ++ palmbus@10000000 { ++ sysc@0 { ++ ralink,pinmux = "spi", "uartlite", "mdio", "wled", "ephy", "rgmii1", "rgmii2"; ++ ralink,gpiomux = "i2c", "jtag"; ++ ralink,uartmux = "gpio"; ++ ralink,wdtmux = <1>; ++ }; ++ ++ gpio0: gpio@600 { ++ status = "okay"; ++ }; ++ ++ spi@b00 { ++ status = "okay"; ++ ++ m25p80@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "en25q64"; ++ reg = <0 0>; ++ linux,modalias = "m25p80", "en25q64"; ++ spi-max-frequency = <10000000>; ++ ++ partition@0 { ++ label = "u-boot"; ++ reg = <0x0 0x30000>; ++ read-only; ++ }; ++ ++ partition@30000 { ++ label = "u-boot-env"; ++ reg = <0x30000 0x10000>; ++ read-only; ++ }; ++ ++ factory: partition@40000 { ++ label = "factory"; ++ reg = <0x40000 0x10000>; ++ read-only; ++ }; ++ ++ partition@50000 { ++ label = "firmware"; ++ reg = <0x50000 0x7b0000>; ++ }; ++ }; ++ }; ++ }; ++ ++ ethernet@10100000 { ++ status = "okay"; ++ ++ port@4 { ++ compatible = "lantiq,mt7620a-gsw-port", "ralink,eth-port"; ++ reg = <4>; ++ phy-mode = "rgmii"; ++ phy-handle = <&phy4>; ++ }; ++ ++ port@5 { ++ compatible = "lantiq,mt7620a-gsw-port", "ralink,eth-port"; ++ reg = <5>; ++ phy-mode = "rgmii"; ++ phy-handle = <&phy5>; ++ }; ++ ++ mdio-bus { ++ status = "okay"; ++ ++ phy4: ethernet-phy@4 { ++ reg = <4>; ++ phy-mode = "rgmii"; ++ }; ++ ++ phy5: ethernet-phy@5 { ++ reg = <5>; ++ phy-mode = "rgmii"; ++ }; ++ }; ++ }; ++ ++ gsw@10110000 { ++ status = "okay"; ++ ralink,port4 = "gmac"; ++ }; ++ ++ sdhci@10130000 { ++ status = "okay"; ++ }; ++ ++ pcie@10140000 { ++ status = "okay"; ++ }; ++ ++ gpio-keys-polled { ++ compatible = "gpio-keys"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ poll-interval = <20>; ++ s2 { ++ label = "S2"; ++ gpios = <&gpio0 1 1>; ++ linux,code = <0x100>; ++ }; ++ s3 { ++ label = "S3"; ++ gpios = <&gpio0 2 1>; ++ linux,code = <0x101>; ++ }; ++ }; + }; +diff --git a/arch/mips/ralink/dts/mt7620a_mt7610e_eval.dts b/arch/mips/ralink/dts/mt7620a_mt7610e_eval.dts +new file mode 100644 +index 0000000..0d7755b +--- /dev/null ++++ b/arch/mips/ralink/dts/mt7620a_mt7610e_eval.dts +@@ -0,0 +1,99 @@ ++/dts-v1/; ++ ++/include/ "mt7620a.dtsi" ++ ++/ { ++ compatible = "ralink,mt7620a-eval-board", "ralink,mt7620a-soc"; ++ model = "Ralink MT7620A evaluation board"; ++ ++ memory@0 { ++ reg = <0x0 0x2000000>; ++ }; ++ ++ chosen { ++ bootargs = "console=ttyS0,57600"; ++ }; ++ ++ palmbus@10000000 { ++ sysc@0 { ++ ralink,pinmux = "spi", "uartlite", "mdio", "wled", "ephy", "rgmii1", "rgmii2"; ++ ralink,gpiomux = "i2c", "jtag"; ++ ralink,uartmux = "gpio"; ++ ralink,wdtmux = <1>; ++ }; ++ ++ gpio0: gpio@600 { ++ status = "okay"; ++ }; ++ ++ spi@b00 { ++ status = "okay"; ++ ++ m25p80@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "en25q64"; ++ reg = <0 0>; ++ linux,modalias = "m25p80", "en25q64"; ++ spi-max-frequency = <10000000>; ++ ++ partition@0 { ++ label = "u-boot"; ++ reg = <0x0 0x30000>; ++ read-only; ++ }; ++ ++ partition@30000 { ++ label = "u-boot-env"; ++ reg = <0x30000 0x10000>; ++ read-only; ++ }; ++ ++ factory: partition@40000 { ++ label = "factory"; ++ reg = <0x40000 0x10000>; ++ read-only; ++ }; ++ ++ partition@50000 { ++ label = "firmware"; ++ reg = <0x50000 0x7b0000>; ++ }; ++ }; ++ }; ++ }; ++ ++ ethernet@10100000 { ++ status = "okay"; ++ }; ++ ++ gsw@10110000 { ++ status = "okay"; ++ ralink,port4 = "ephy"; ++ }; ++ ++ sdhci@10130000 { ++ status = "okay"; ++ }; ++ ++ pcie@10140000 { ++ status = "okay"; ++ }; ++ ++ gpio-keys-polled { ++ compatible = "gpio-keys"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ poll-interval = <20>; ++ wps { ++ label = "wps"; ++ gpios = <&gpio0 12 1>; ++ linux,code = <0x100>; ++ }; ++ reset { ++ label = "reset"; ++ gpios = <&gpio0 13 1>; ++ linux,code = <0x101>; ++ }; ++ }; ++}; +diff --git a/arch/mips/ralink/dts/rt2880.dtsi b/arch/mips/ralink/dts/rt2880.dtsi +index 182afde..2a34b8d 100644 +--- a/arch/mips/ralink/dts/rt2880.dtsi ++++ b/arch/mips/ralink/dts/rt2880.dtsi +@@ -55,4 +55,21 @@ + reg-shift = <2>; + }; + }; ++ ++ ethernet@400000 { ++ compatible = "ralink,rt2880-eth"; ++ reg = <0x00400000 10000>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <5>; ++ ++ status = "disabled"; ++ ++ mdio-bus { ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ }; + }; +diff --git a/arch/mips/ralink/dts/rt2880_eval.dts b/arch/mips/ralink/dts/rt2880_eval.dts +index 322d700..58a1edf 100644 +--- a/arch/mips/ralink/dts/rt2880_eval.dts ++++ b/arch/mips/ralink/dts/rt2880_eval.dts +@@ -43,4 +43,10 @@ + reg = <0x50000 0x3b0000>; + }; + }; ++ ++ ethernet@400000 { ++ status = "okay"; ++ ++ ralink,fixed-link = <1000 1 1 1>; ++ }; + }; +diff --git a/arch/mips/ralink/dts/rt3050.dtsi b/arch/mips/ralink/dts/rt3050.dtsi +index ef7da1e..b1ac940 100644 +--- a/arch/mips/ralink/dts/rt3050.dtsi ++++ b/arch/mips/ralink/dts/rt3050.dtsi +@@ -1,7 +1,7 @@ + / { + #address-cells = <1>; + #size-cells = <1>; +- compatible = "ralink,rt3050-soc", "ralink,rt3052-soc", "ralink,rt3350-soc"; ++ compatible = "ralink,rt3050-soc", "ralink,rt3052-soc"; + + cpus { + cpu@0 { +@@ -45,6 +45,15 @@ + reg = <0x300 0x100>; + }; + ++ i2c@900 { ++ compatible = "link,rt3052-i2c", "ralink,rt2880-i2c"; ++ reg = <0x900 0x100>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ + uartlite@c00 { + compatible = "ralink,rt3052-uart", "ralink,rt2880-uart", "ns16550a"; + reg = <0xc00 0x100>; +@@ -55,4 +64,24 @@ + reg-shift = <2>; + }; + }; ++ ++ ethernet@10100000 { ++ compatible = "ralink,rt3050-eth"; ++ reg = <0x10100000 10000>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <5>; ++ ++ status = "disabled"; ++ }; ++ ++ esw@10110000 { ++ compatible = "ralink,rt3050-esw"; ++ reg = <0x10110000 8000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <17>; ++ ++ status = "disabled"; ++ }; + }; +diff --git a/arch/mips/ralink/dts/rt3052_eval.dts b/arch/mips/ralink/dts/rt3052_eval.dts +index df17f5f..df02957 100644 +--- a/arch/mips/ralink/dts/rt3052_eval.dts ++++ b/arch/mips/ralink/dts/rt3052_eval.dts +@@ -3,7 +3,7 @@ + /include/ "rt3050.dtsi" + + / { +- compatible = "ralink,rt3052-eval-board", "ralink,rt3052-soc"; ++ compatible = "ralink,rt3052-eval-board", "ralink,rt3052-soc", "ralink,rt5350-soc"; + model = "Ralink RT3052 evaluation board"; + + memory@0 { +@@ -14,6 +14,14 @@ + bootargs = "console=ttyS0,57600"; + }; + ++ palmbus@10000000 { ++ sysc@0 { ++ ralink,pinmux = "i2c", "spi", "uartlite", "jtag", "mdio", "sdram", "rgmii"; ++ ralink,uartmux = "gpio"; ++ ralink,wdtmux = <1>; ++ }; ++ }; ++ + cfi@1f000000 { + compatible = "cfi-flash"; + reg = <0x1f000000 0x800000>; +@@ -43,4 +51,13 @@ + reg = <0x50000 0x7b0000>; + }; + }; ++ ++ ethernet@10100000 { ++ status = "okay"; ++ }; ++ ++ esw@10110000 { ++ status = "okay"; ++ ralink,portmap = <0x2f>; ++ }; + }; +diff --git a/arch/mips/ralink/dts/rt5350.dtsi b/arch/mips/ralink/dts/rt5350.dtsi +new file mode 100644 +index 0000000..3d6b3bc +--- /dev/null ++++ b/arch/mips/ralink/dts/rt5350.dtsi +@@ -0,0 +1,227 @@ ++/ { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "ralink,rt5350-soc"; ++ ++ cpus { ++ cpu@0 { ++ compatible = "mips,mips24KEc"; ++ }; ++ }; ++ ++ cpuintc: cpuintc@0 { ++ #address-cells = <0>; ++ #interrupt-cells = <1>; ++ interrupt-controller; ++ compatible = "mti,cpu-interrupt-controller"; ++ }; ++ ++ palmbus@10000000 { ++ compatible = "palmbus"; ++ reg = <0x10000000 0x200000>; ++ ranges = <0x0 0x10000000 0x1FFFFF>; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ sysc@0 { ++ compatible = "ralink,rt5350-sysc", "ralink,rt3050-sysc"; ++ reg = <0x0 0x100>; ++ }; ++ ++ timer@100 { ++ compatible = "ralink,rt5350-timer", "ralink,rt2880-timer"; ++ reg = <0x100 0x20>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <1>; ++ }; ++ ++ watchdog@120 { ++ compatible = "ralink,rt5350-wdt", "ralink,rt2880-wdt"; ++ reg = <0x120 0x10>; ++ ++ resets = <&rstctrl 8>; ++ reset-names = "wdt"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <1>; ++ }; ++ ++ intc: intc@200 { ++ compatible = "ralink,rt5350-intc", "ralink,rt2880-intc"; ++ reg = <0x200 0x100>; ++ ++ resets = <&rstctrl 19>; ++ reset-names = "intc"; ++ ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <2>; ++ }; ++ ++ memc@300 { ++ compatible = "ralink,rt5350-memc", "ralink,rt3050-memc"; ++ reg = <0x300 0x100>; ++ ++ resets = <&rstctrl 20>; ++ reset-names = "mc"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <3>; ++ }; ++ ++ uart@500 { ++ compatible = "ralink,rt5350-uart", "ralink,rt2880-uart", "ns16550a"; ++ reg = <0x500 0x100>; ++ ++ resets = <&rstctrl 12>; ++ reset-names = "uart"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <5>; ++ ++ reg-shift = <2>; ++ ++ status = "disabled"; ++ }; ++ ++ gpio0: gpio@600 { ++ compatible = "ralink,rt5350-gpio", "ralink,rt2880-gpio"; ++ reg = <0x600 0x34>; ++ ++ resets = <&rstctrl 13>; ++ reset-names = "pio"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <6>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ ralink,gpio-base = <0>; ++ ralink,num-gpios = <24>; ++ ralink,register-map = [ 00 04 08 0c ++ 20 24 28 2c ++ 30 34 ]; ++ ++ status = "disabled"; ++ }; ++ ++ gpio1: gpio@638 { ++ compatible = "ralink,rt5350-gpio", "ralink,rt2880-gpio"; ++ reg = <0x638 0x24>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <6>; ++ ++ gpio-controller; ++ #gpio-cells = <2>; ++ ++ ralink,gpio-base = <24>; ++ ralink,num-gpios = <16>; ++ ralink,register-map = [ 00 04 08 0c ++ 10 14 18 1c ++ 20 24 ]; ++ ++ status = "disabled"; ++ }; ++ ++ i2c@900 { ++ compatible = "link,rt5350-i2c", "ralink,rt2880-i2c"; ++ reg = <0x900 0x100>; ++ ++ resets = <&rstctrl 16>; ++ reset-names = "i2c"; ++ ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ status = "disabled"; ++ }; ++ ++ spi@b00 { ++ compatible = "ralink,rt5350-spi", "ralink,rt2880-spi"; ++ reg = <0xb00 0x100>; ++ ++ resets = <&rstctrl 18>; ++ reset-names = "spi"; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ++ status = "disabled"; ++ }; ++ ++ uartlite@c00 { ++ compatible = "ralink,rt5350-uart", "ralink,rt2880-uart", "ns16550a"; ++ reg = <0xc00 0x100>; ++ ++ resets = <&rstctrl 19>; ++ reset-names = "uartl"; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <12>; ++ ++ reg-shift = <2>; ++ }; ++ ++ systick@d00 { ++ compatible = "ralink,rt5350-systick", "ralink,cevt-systick"; ++ reg = <0xd00 0x10>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <7>; ++ }; ++ }; ++ ++ rstctrl: rstctrl { ++ compatible = "ralink,rt5350-reset", "ralink,rt2880-reset"; ++ #reset-cells = <1>; ++ }; ++ ++ ubsphy { ++ compatible = "ralink,rt3xxx-usbphy"; ++ ++ resets = <&rstctrl 22 &rstctrl 25>; ++ reset-names = "host", "device"; ++ }; ++ ++ ethernet@10100000 { ++ compatible = "ralink,rt5350-eth"; ++ reg = <0x10100000 10000>; ++ ++ interrupt-parent = <&cpuintc>; ++ interrupts = <5>; ++ ++ status = "disabled"; ++ }; ++ ++ esw@10110000 { ++ compatible = "ralink,rt3050-esw"; ++ reg = <0x10110000 8000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <17>; ++ ++ status = "disabled"; ++ }; ++ ++ ehci@101c0000 { ++ compatible = "ralink,rt3xxx-ehci"; ++ reg = <0x101c0000 0x1000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <18>; ++ }; ++ ++ ohci@101c1000 { ++ compatible = "ralink,rt3xxx-ohci"; ++ reg = <0x101c1000 0x1000>; ++ ++ interrupt-parent = <&intc>; ++ interrupts = <18>; ++ }; ++}; +diff --git a/arch/mips/ralink/dts/rt5350_eval.dts b/arch/mips/ralink/dts/rt5350_eval.dts +new file mode 100644 +index 0000000..ab92043 +--- /dev/null ++++ b/arch/mips/ralink/dts/rt5350_eval.dts +@@ -0,0 +1,69 @@ ++/dts-v1/; ++ ++/include/ "rt5350.dtsi" ++ ++/ { ++ compatible = "ralink,rt5350-eval-board", "ralink,rt5350-soc"; ++ model = "Ralink RT5350 evaluation board"; ++ ++ chosen { ++ bootargs = "console=ttyS0,57600"; ++ }; ++ ++ palmbus@10000000 { ++ sysc@0 { ++ ralink,pinmux = "i2c", "spi", "uartlite", "jtag", "mdio", "sdram", "rgmii"; ++ ralink,uartmux = "gpio"; ++ ralink,wdtmux = <1>; ++ }; ++ ++ gpio0: gpio@600 { ++ status = "okay"; ++ }; ++ ++ spi@b00 { ++ status = "okay"; ++ ++ m25p80@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "en25q64"; ++ reg = <0 0>; ++ linux,modalias = "m25p80", "mx25l3205d"; ++ spi-max-frequency = <10000000>; ++ ++ partition@0 { ++ label = "u-boot"; ++ reg = <0x0 0x30000>; ++ read-only; ++ }; ++ ++ partition@30000 { ++ label = "u-boot-env"; ++ reg = <0x30000 0x10000>; ++ read-only; ++ }; ++ ++ factory: partition@40000 { ++ label = "factory"; ++ reg = <0x40000 0x10000>; ++ read-only; ++ }; ++ ++ partition@50000 { ++ label = "firmware"; ++ reg = <0x50000 0x3b0000>; ++ }; ++ }; ++ }; ++ }; ++ ++ ethernet@10100000 { ++ status = "okay"; ++ }; ++ ++ esw@10110000 { ++ status = "okay"; ++ ralink,portmap = <0x2f>; ++ }; ++}; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0141-clocksource-make-clocksource_of_init-pass-a-device_n.patch b/target/linux/ramips/patches-3.9/0141-clocksource-make-clocksource_of_init-pass-a-device_n.patch new file mode 100644 index 0000000000..6cfa91a049 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0141-clocksource-make-clocksource_of_init-pass-a-device_n.patch @@ -0,0 +1,131 @@ +From ec2ed8cdbe8b3d24261f0d88eb039e9d71e5d588 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 23 May 2013 16:58:12 +0200 +Subject: [PATCH 141/164] clocksource: make clocksource_of_init() pass a + device_node pointer + +If we look at the clocksources that are OF enabled we will notice, that they +all do a of_find_matching_node() when being called. This patch changes +clocksource_of_init() to always pass the struct device_node pointer to the +init function. + +Signed-off-by: John Crispin +--- + drivers/clocksource/bcm2835_timer.c | 12 +----------- + drivers/clocksource/clksrc-of.c | 4 ++-- + drivers/clocksource/tegra20_timer.c | 14 +------------- + drivers/clocksource/vt8500_timer.c | 14 +------------- + 4 files changed, 5 insertions(+), 39 deletions(-) + +diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c +index 50c68fe..766611d 100644 +--- a/drivers/clocksource/bcm2835_timer.c ++++ b/drivers/clocksource/bcm2835_timer.c +@@ -95,23 +95,13 @@ static irqreturn_t bcm2835_time_interrupt(int irq, void *dev_id) + } + } + +-static struct of_device_id bcm2835_time_match[] __initconst = { +- { .compatible = "brcm,bcm2835-system-timer" }, +- {} +-}; +- +-static void __init bcm2835_timer_init(void) ++static void __init bcm2835_timer_init(struct device_node *node) + { +- struct device_node *node; + void __iomem *base; + u32 freq; + int irq; + struct bcm2835_timer *timer; + +- node = of_find_matching_node(NULL, bcm2835_time_match); +- if (!node) +- panic("No bcm2835 timer node"); +- + base = of_iomap(node, 0); + if (!base) + panic("Can't remap registers"); +diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c +index bdabdaa..3ef11fb 100644 +--- a/drivers/clocksource/clksrc-of.c ++++ b/drivers/clocksource/clksrc-of.c +@@ -26,10 +26,10 @@ void __init clocksource_of_init(void) + { + struct device_node *np; + const struct of_device_id *match; +- void (*init_func)(void); ++ void (*init_func)(struct device_node *); + + for_each_matching_node_and_match(np, __clksrc_of_table, &match) { + init_func = match->data; +- init_func(); ++ init_func(np); + } + } +diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c +index 0bde03f..e698d8e 100644 +--- a/drivers/clocksource/tegra20_timer.c ++++ b/drivers/clocksource/tegra20_timer.c +@@ -154,29 +154,17 @@ static struct irqaction tegra_timer_irq = { + .dev_id = &tegra_clockevent, + }; + +-static const struct of_device_id timer_match[] __initconst = { +- { .compatible = "nvidia,tegra20-timer" }, +- {} +-}; +- + static const struct of_device_id rtc_match[] __initconst = { + { .compatible = "nvidia,tegra20-rtc" }, + {} + }; + +-static void __init tegra20_init_timer(void) ++static void __init tegra20_init_timer(struct device_node *np) + { +- struct device_node *np; + struct clk *clk; + unsigned long rate; + int ret; + +- np = of_find_matching_node(NULL, timer_match); +- if (!np) { +- pr_err("Failed to find timer DT node\n"); +- BUG(); +- } +- + timer_reg_base = of_iomap(np, 0); + if (!timer_reg_base) { + pr_err("Can't map timer registers\n"); +diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c +index 8efc86b..2422552 100644 +--- a/drivers/clocksource/vt8500_timer.c ++++ b/drivers/clocksource/vt8500_timer.c +@@ -129,22 +129,10 @@ static struct irqaction irq = { + .dev_id = &clockevent, + }; + +-static struct of_device_id vt8500_timer_ids[] = { +- { .compatible = "via,vt8500-timer" }, +- { } +-}; +- +-static void __init vt8500_timer_init(void) ++static void __init vt8500_timer_init(struct device_node *np) + { +- struct device_node *np; + int timer_irq; + +- np = of_find_matching_node(NULL, vt8500_timer_ids); +- if (!np) { +- pr_err("%s: Timer description missing from Device Tree\n", +- __func__); +- return; +- } + regbase = of_iomap(np, 0); + if (!regbase) { + pr_err("%s: Missing iobase description in Device Tree\n", +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0142-clocksource-MIPS-ralink-add-support-for-systick-time.patch b/target/linux/ramips/patches-3.9/0142-clocksource-MIPS-ralink-add-support-for-systick-time.patch new file mode 100644 index 0000000000..a17c160355 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0142-clocksource-MIPS-ralink-add-support-for-systick-time.patch @@ -0,0 +1,259 @@ +From bc350a91da176ae8573c0755c66f5ee1911495bc Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 30 Apr 2013 12:35:50 +0200 +Subject: [PATCH 142/164] clocksource: MIPS: ralink: add support for systick + timer found on newer ralink SoC + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Kconfig | 2 + + arch/mips/ralink/clk.c | 1 + + drivers/clocksource/Kconfig | 6 ++ + drivers/clocksource/Makefile | 1 + + drivers/clocksource/cevt-rt3352.c | 162 +++++++++++++++++++++++++++++++++++++ + include/linux/clocksource.h | 1 + + 6 files changed, 173 insertions(+) + create mode 100644 drivers/clocksource/cevt-rt3352.c + +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 38540a4..3fe032c 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -14,6 +14,7 @@ choice + select USB_ARCH_HAS_HCD + select USB_ARCH_HAS_OHCI + select USB_ARCH_HAS_EHCI ++ select CLKEVT_RT3352 + + config SOC_RT3883 + bool "RT3883" +@@ -22,6 +23,7 @@ choice + + config SOC_MT7620 + bool "MT7620" ++ select CLKEVT_RT3352 + + endchoice + +diff --git a/arch/mips/ralink/clk.c b/arch/mips/ralink/clk.c +index 8dfa22f..bba0cdf 100644 +--- a/arch/mips/ralink/clk.c ++++ b/arch/mips/ralink/clk.c +@@ -69,4 +69,5 @@ void __init plat_time_init(void) + pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000); + mips_hpt_frequency = clk_get_rate(clk) / 2; + clk_put(clk); ++ clocksource_of_init(); + } +diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig +index e507ab7..1216c16 100644 +--- a/drivers/clocksource/Kconfig ++++ b/drivers/clocksource/Kconfig +@@ -7,6 +7,12 @@ config CLKSRC_I8253 + config CLKEVT_I8253 + bool + ++config CLKEVT_RT3352 ++ bool ++ depends on MIPS && RALINK ++ select CLKSRC_OF ++ select CLKSRC_MMIO ++ + config I8253_LOCK + bool + +diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile +index 4d8283a..db47a4e 100644 +--- a/drivers/clocksource/Makefile ++++ b/drivers/clocksource/Makefile +@@ -10,6 +10,7 @@ obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o + obj-$(CONFIG_EM_TIMER_STI) += em_sti.o + obj-$(CONFIG_CLKBLD_I8253) += i8253.o + obj-$(CONFIG_CLKSRC_MMIO) += mmio.o ++obj-$(CONFIG_CLKEVT_RT3352) += cevt-rt3352.o + obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o + obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o + obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o +diff --git a/drivers/clocksource/cevt-rt3352.c b/drivers/clocksource/cevt-rt3352.c +new file mode 100644 +index 0000000..bd50edd +--- /dev/null ++++ b/drivers/clocksource/cevt-rt3352.c +@@ -0,0 +1,162 @@ ++/* ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file "COPYING" in the main directory of this archive ++ * for more details. ++ * ++ * Copyright (C) 2013 by John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define SYSTICK_FREQ (50 * 1000) ++ ++#define SYSTICK_CONFIG 0x00 ++#define SYSTICK_COMPARE 0x04 ++#define SYSTICK_COUNT 0x08 ++ ++/* route systick irq to mips irq 7 instead of the r4k-timer */ ++#define CFG_EXT_STK_EN 0x2 ++/* enable the counter */ ++#define CFG_CNT_EN 0x1 ++ ++struct systick_device { ++ void __iomem *membase; ++ struct clock_event_device dev; ++ int irq_requested; ++ int freq_scale; ++}; ++ ++static void systick_set_clock_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt); ++ ++static int systick_next_event(unsigned long delta, ++ struct clock_event_device *evt) ++{ ++ struct systick_device *sdev = container_of(evt, struct systick_device, dev); ++ u32 count; ++ ++ count = ioread32(sdev->membase + SYSTICK_COUNT); ++ count = (count + delta) % SYSTICK_FREQ; ++ iowrite32(count + delta, sdev->membase + SYSTICK_COMPARE); ++ ++ return 0; ++} ++ ++static void systick_event_handler(struct clock_event_device *dev) ++{ ++ /* noting to do here */ ++} ++ ++static irqreturn_t systick_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *dev = (struct clock_event_device *) dev_id; ++ ++ dev->event_handler(dev); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct systick_device systick = { ++ .dev = { ++ /* cevt-r4k uses 300, make sure systick gets used if available */ ++ .rating = 310, ++ .features = CLOCK_EVT_FEAT_ONESHOT, ++ .set_next_event = systick_next_event, ++ .set_mode = systick_set_clock_mode, ++ .event_handler = systick_event_handler, ++ }, ++}; ++ ++static struct irqaction systick_irqaction = { ++ .handler = systick_interrupt, ++ .flags = IRQF_PERCPU | IRQF_TIMER, ++ .dev_id = &systick.dev, ++}; ++ ++/* ugly hack */ ++#ifdef CONFIG_SOC_MT7620 ++ ++#define CLK_LUT_CFG 0x40 ++#define SLEEP_EN BIT(31) ++ ++static inline void mt7620_freq_scaling(struct systick_device *sdev, int status) ++{ ++ if (sdev->freq_scale == status) ++ return; ++ ++ sdev->freq_scale = status; ++ ++ pr_info("%s: %s autosleep mode\n", systick.dev.name, (status) ? ("enable") : ("disable")); ++ if (status) ++ rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) | SLEEP_EN, CLK_LUT_CFG); ++ else ++ rt_sysc_w32(rt_sysc_r32(CLK_LUT_CFG) & ~SLEEP_EN, CLK_LUT_CFG); ++} ++#else ++static inline void mt7620_freq_scaling(struct systick_device *sdev, int status) {} ++#endif ++ ++static void systick_set_clock_mode(enum clock_event_mode mode, ++ struct clock_event_device *evt) ++{ ++ struct systick_device *sdev = container_of(evt, struct systick_device, dev); ++ ++ switch (mode) { ++ case CLOCK_EVT_MODE_ONESHOT: ++ if (!sdev->irq_requested) ++ setup_irq(systick.dev.irq, &systick_irqaction); ++ mt7620_freq_scaling(sdev, 1); ++ sdev->irq_requested = 1; ++ iowrite32(CFG_EXT_STK_EN | CFG_CNT_EN, systick.membase + SYSTICK_CONFIG); ++ break; ++ ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ if (sdev->irq_requested) ++ free_irq(systick.dev.irq, &systick_irqaction); ++ mt7620_freq_scaling(sdev, 0); ++ sdev->irq_requested = 0; ++ iowrite32(0, systick.membase + SYSTICK_CONFIG); ++ break; ++ ++ default: ++ pr_err("%s: Unhandeled mips clock_mode\n", systick.dev.name); ++ break; ++ } ++} ++ ++static void __init ralink_systick_init(struct device_node *np) ++{ ++ systick.membase = of_iomap(np, 0); ++ if (!systick.membase) { ++ pr_err("%s: of_iomap failed", np->name); ++ return; ++ } ++ ++ clocksource_mmio_init(systick.membase + SYSTICK_COUNT, np->name, ++ SYSTICK_FREQ, 301, 16, clocksource_mmio_readl_up); ++ ++ systick_irqaction.name = np->name; ++ systick.dev.name = np->name; ++ clockevent_set_clock(&systick.dev, SYSTICK_FREQ); ++ systick.dev.max_delta_ns = clockevent_delta2ns(0x7fff, &systick.dev); ++ systick.dev.min_delta_ns = clockevent_delta2ns(0x3, &systick.dev); ++ systick.dev.irq = irq_of_parse_and_map(np, 0); ++ if (!systick.dev.irq) ++ panic("%s: request_irq failed", np->name); ++ ++ clockevents_register_device(&systick.dev); ++ ++ pr_info("%s: runing - mult: %d, shift: %d\n", np->name, systick.dev.mult, systick.dev.shift); ++} ++ ++CLOCKSOURCE_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init); +diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h +index 27cfda4..08ed5e1 100644 +--- a/include/linux/clocksource.h ++++ b/include/linux/clocksource.h +@@ -340,6 +340,7 @@ extern void clocksource_of_init(void); + __used __section(__clksrc_of_table) \ + = { .compatible = compat, .data = fn }; + #else ++static inline void clocksource_of_init(void) {} + #define CLOCKSOURCE_OF_DECLARE(name, compat, fn) + #endif + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0143-GPIO-MIPS-ralink-adds-ralink-gpio-support.patch b/target/linux/ramips/patches-3.9/0143-GPIO-MIPS-ralink-adds-ralink-gpio-support.patch new file mode 100644 index 0000000000..a6c36f09ea --- /dev/null +++ b/target/linux/ramips/patches-3.9/0143-GPIO-MIPS-ralink-adds-ralink-gpio-support.patch @@ -0,0 +1,425 @@ +From 82a24aa01752a87c571e47323f0e141c818e531b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 22 Jan 2013 18:24:34 +0100 +Subject: [PATCH 143/164] GPIO: MIPS: ralink: adds ralink gpio support + +Add gpio driver for Ralink SoC. This driver makes the gpio core on +RT2880, RT305x, rt3352, rt3662, rt3883, rt5350 and mt7620 work. + +Signed-off-by: John Crispin +--- + arch/mips/Kconfig | 1 + + arch/mips/include/asm/mach-ralink/gpio.h | 24 +++ + drivers/gpio/Kconfig | 6 + + drivers/gpio/Makefile | 1 + + drivers/gpio/gpio-ralink.c | 326 ++++++++++++++++++++++++++++++ + 5 files changed, 358 insertions(+) + create mode 100644 arch/mips/include/asm/mach-ralink/gpio.h + create mode 100644 drivers/gpio/gpio-ralink.c + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index e4da4f8..b237c50 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -443,6 +443,7 @@ config RALINK + select SYS_HAS_EARLY_PRINTK + select HAVE_MACH_CLKDEV + select CLKDEV_LOOKUP ++ select ARCH_REQUIRE_GPIOLIB + + config SGI_IP22 + bool "SGI IP22 (Indy/Indigo2)" +diff --git a/arch/mips/include/asm/mach-ralink/gpio.h b/arch/mips/include/asm/mach-ralink/gpio.h +new file mode 100644 +index 0000000..f68ee16 +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/gpio.h +@@ -0,0 +1,24 @@ ++/* ++ * Ralink SoC GPIO API support ++ * ++ * Copyright (C) 2008-2009 Gabor Juhos ++ * Copyright (C) 2008 Imre Kaloz ++ * ++ * 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 __ASM_MACH_RALINK_GPIO_H ++#define __ASM_MACH_RALINK_GPIO_H ++ ++#define ARCH_NR_GPIOS 128 ++#include ++ ++#define gpio_get_value __gpio_get_value ++#define gpio_set_value __gpio_set_value ++#define gpio_cansleep __gpio_cansleep ++#define gpio_to_irq __gpio_to_irq ++ ++#endif /* __ASM_MACH_RALINK_GPIO_H */ +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +index 93aaadf..29add97 100644 +--- a/drivers/gpio/Kconfig ++++ b/drivers/gpio/Kconfig +@@ -204,6 +204,12 @@ config GPIO_PXA + help + Say yes here to support the PXA GPIO device + ++config GPIO_RALINK ++ bool "Ralink GPIO Support" ++ depends on RALINK ++ help ++ Say yes here to support the Ralink SoC GPIO device ++ + config GPIO_SPEAR_SPICS + bool "ST SPEAr13xx SPI Chip Select as GPIO support" + depends on PLAT_SPEAR +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +index 22e07bc..f7b6603 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -55,6 +55,7 @@ obj-$(CONFIG_GPIO_PCF857X) += gpio-pcf857x.o + obj-$(CONFIG_GPIO_PCH) += gpio-pch.o + obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o + obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o ++obj-$(CONFIG_GPIO_RALINK) += gpio-ralink.o + obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o + obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o + obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o +diff --git a/drivers/gpio/gpio-ralink.c b/drivers/gpio/gpio-ralink.c +new file mode 100644 +index 0000000..12984f1 +--- /dev/null ++++ b/drivers/gpio/gpio-ralink.c +@@ -0,0 +1,326 @@ ++/* ++ * 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. ++ * ++ * Copyright (C) 2009-2011 Gabor Juhos ++ * Copyright (C) 2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++enum ralink_gpio_reg { ++ GPIO_REG_INT = 0, ++ GPIO_REG_EDGE, ++ GPIO_REG_RENA, ++ GPIO_REG_FENA, ++ GPIO_REG_DATA, ++ GPIO_REG_DIR, ++ GPIO_REG_POL, ++ GPIO_REG_SET, ++ GPIO_REG_RESET, ++ GPIO_REG_TOGGLE, ++ GPIO_REG_MAX ++}; ++ ++struct ralink_gpio_chip { ++ struct gpio_chip chip; ++ u8 regs[GPIO_REG_MAX]; ++ ++ spinlock_t lock; ++ void __iomem *membase; ++ struct irq_domain *domain; ++ int irq; ++ ++ u32 rising; ++ u32 falling; ++}; ++ ++#define MAP_MAX 4 ++static struct irq_domain *irq_map[MAP_MAX]; ++static int irq_map_count; ++static atomic_t irq_refcount = ATOMIC_INIT(0); ++ ++static inline struct ralink_gpio_chip *to_ralink_gpio(struct gpio_chip *chip) ++{ ++ struct ralink_gpio_chip *rg; ++ ++ rg = container_of(chip, struct ralink_gpio_chip, chip); ++ ++ return rg; ++} ++ ++static inline void rt_gpio_w32(struct ralink_gpio_chip *rg, u8 reg, u32 val) ++{ ++ iowrite32(val, rg->membase + rg->regs[reg]); ++} ++ ++static inline u32 rt_gpio_r32(struct ralink_gpio_chip *rg, u8 reg) ++{ ++ return ioread32(rg->membase + rg->regs[reg]); ++} ++ ++static void ralink_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ ++ rt_gpio_w32(rg, (value) ? GPIO_REG_SET : GPIO_REG_RESET, BIT(offset)); ++} ++ ++static int ralink_gpio_get(struct gpio_chip *chip, unsigned offset) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ ++ return !!(rt_gpio_r32(rg, GPIO_REG_DATA) & BIT(offset)); ++} ++ ++static int ralink_gpio_direction_input(struct gpio_chip *chip, unsigned offset) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ unsigned long flags; ++ u32 t; ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ t = rt_gpio_r32(rg, GPIO_REG_DIR); ++ t &= ~BIT(offset); ++ rt_gpio_w32(rg, GPIO_REG_DIR, t); ++ spin_unlock_irqrestore(&rg->lock, flags); ++ ++ return 0; ++} ++ ++static int ralink_gpio_direction_output(struct gpio_chip *chip, ++ unsigned offset, int value) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ unsigned long flags; ++ u32 t; ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ ralink_gpio_set(chip, offset, value); ++ t = rt_gpio_r32(rg, GPIO_REG_DIR); ++ t |= BIT(offset); ++ rt_gpio_w32(rg, GPIO_REG_DIR, t); ++ spin_unlock_irqrestore(&rg->lock, flags); ++ ++ return 0; ++} ++ ++static int ralink_gpio_to_irq(struct gpio_chip *chip, unsigned pin) ++{ ++ struct ralink_gpio_chip *rg = to_ralink_gpio(chip); ++ ++ if (rg->irq < 1) ++ return -1; ++ ++ ralink_gpio_direction_input(chip, pin); ++ ++ return irq_create_mapping(rg->domain, pin); ++} ++ ++static void ralink_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) ++{ ++ int i; ++ ++ for (i = 0; i < irq_map_count; i++) { ++ struct irq_domain *domain = irq_map[i]; ++ struct ralink_gpio_chip *rg = (struct ralink_gpio_chip *) domain->host_data; ++ unsigned long pending = rt_gpio_r32(rg, GPIO_REG_INT); ++ int bit; ++ ++ for_each_set_bit(bit, &pending, rg->chip.ngpio) { ++ u32 map = irq_find_mapping(domain, bit); ++ generic_handle_irq(map); ++ rt_gpio_w32(rg, GPIO_REG_INT, BIT(bit)); ++ } ++ } ++} ++ ++static void ralink_gpio_irq_unmask(struct irq_data *d) ++{ ++ struct ralink_gpio_chip *rg = (struct ralink_gpio_chip *) d->domain->host_data; ++ u32 val = rt_gpio_r32(rg, GPIO_REG_RENA); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ rt_gpio_w32(rg, GPIO_REG_RENA, val | (BIT(d->hwirq) & rg->rising)); ++ rt_gpio_w32(rg, GPIO_REG_FENA, val | (BIT(d->hwirq) & rg->falling)); ++ spin_unlock_irqrestore(&rg->lock, flags); ++} ++ ++static void ralink_gpio_irq_mask(struct irq_data *d) ++{ ++ struct ralink_gpio_chip *rg = (struct ralink_gpio_chip *) d->domain->host_data; ++ u32 val = rt_gpio_r32(rg, GPIO_REG_RENA); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&rg->lock, flags); ++ rt_gpio_w32(rg, GPIO_REG_FENA, val & ~BIT(d->hwirq)); ++ rt_gpio_w32(rg, GPIO_REG_RENA, val & ~BIT(d->hwirq)); ++ spin_unlock_irqrestore(&rg->lock, flags); ++} ++ ++static int ralink_gpio_irq_type(struct irq_data *d, unsigned int type) ++{ ++ struct ralink_gpio_chip *rg = (struct ralink_gpio_chip *) d->domain->host_data; ++ u32 mask = BIT(d->hwirq); ++ ++ if (type == IRQ_TYPE_PROBE) { ++ if ((rg->rising | rg->falling) & mask) ++ return 0; ++ ++ type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_RISING; ++ } ++ ++ if (type & IRQ_TYPE_EDGE_RISING) ++ rg->rising |= mask; ++ else ++ rg->rising &= mask; ++ ++ if (type & IRQ_TYPE_EDGE_RISING) ++ rg->falling |= mask; ++ else ++ rg->falling &= mask; ++ ++ return 0; ++} ++ ++static struct irq_chip ralink_gpio_irq_chip = { ++ .name = "GPIO", ++ .irq_unmask = ralink_gpio_irq_unmask, ++ .irq_mask = ralink_gpio_irq_mask, ++ .irq_mask_ack = ralink_gpio_irq_mask, ++ .irq_set_type = ralink_gpio_irq_type, ++}; ++ ++static int gpio_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) ++{ ++ irq_set_chip_and_handler(irq, &ralink_gpio_irq_chip, handle_level_irq); ++ irq_set_handler_data(irq, d); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops irq_domain_ops = { ++ .xlate = irq_domain_xlate_onecell, ++ .map = gpio_map, ++}; ++ ++static void ralink_gpio_irq_init(struct device_node *np, struct ralink_gpio_chip *rg) ++{ ++ if (irq_map_count >= MAP_MAX) ++ return; ++ ++ rg->irq = irq_of_parse_and_map(np, 0); ++ if (!rg->irq) ++ return; ++ ++ rg->domain = irq_domain_add_linear(np, rg->chip.ngpio, &irq_domain_ops, rg); ++ if (!rg->domain) { ++ dev_err(rg->chip.dev, "irq_domain_add_linear failed\n"); ++ return; ++ } ++ ++ irq_map[irq_map_count++] = rg->domain; ++ ++ rt_gpio_w32(rg, GPIO_REG_RENA, 0x0); ++ rt_gpio_w32(rg, GPIO_REG_FENA, 0x0); ++ ++ if (!atomic_read(&irq_refcount)) ++ irq_set_chained_handler(rg->irq, ralink_gpio_irq_handler); ++ atomic_inc(&irq_refcount); ++ ++ dev_info(rg->chip.dev, "registering %d irq handlers\n", rg->chip.ngpio); ++} ++ ++static int ralink_gpio_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ struct ralink_gpio_chip *rg; ++ const __be32 *ngpio, *gpiobase; ++ ++ if (!res) { ++ dev_err(&pdev->dev, "failed to find resource\n"); ++ return -ENOMEM; ++ } ++ ++ rg = devm_kzalloc(&pdev->dev, ++ sizeof(struct ralink_gpio_chip), GFP_KERNEL); ++ if (!rg) ++ return -ENOMEM; ++ ++ rg->membase = devm_request_and_ioremap(&pdev->dev, res); ++ if (!rg->membase) { ++ dev_err(&pdev->dev, "cannot remap I/O memory region\n"); ++ return -ENOMEM; ++ } ++ ++ if (of_property_read_u8_array(np, "ralink,register-map", ++ rg->regs, GPIO_REG_MAX)) { ++ dev_err(&pdev->dev, "failed to read register definition\n"); ++ return -EINVAL; ++ } ++ ++ ngpio = of_get_property(np, "ralink,num-gpios", NULL); ++ if (!ngpio) { ++ dev_err(&pdev->dev, "failed to read number of pins\n"); ++ return -EINVAL; ++ } ++ ++ gpiobase = of_get_property(np, "ralink,gpio-base", NULL); ++ if (gpiobase) ++ rg->chip.base = be32_to_cpu(*gpiobase); ++ else ++ rg->chip.base = -1; ++ ++ spin_lock_init(&rg->lock); ++ ++ rg->chip.dev = &pdev->dev; ++ rg->chip.label = dev_name(&pdev->dev); ++ rg->chip.of_node = np; ++ rg->chip.ngpio = be32_to_cpu(*ngpio); ++ rg->chip.direction_input = ralink_gpio_direction_input; ++ rg->chip.direction_output = ralink_gpio_direction_output; ++ rg->chip.get = ralink_gpio_get; ++ rg->chip.set = ralink_gpio_set; ++ rg->chip.to_irq = ralink_gpio_to_irq; ++ ++ /* set polarity to low for all lines */ ++ rt_gpio_w32(rg, GPIO_REG_POL, 0); ++ ++ dev_info(&pdev->dev, "registering %d gpios\n", rg->chip.ngpio); ++ ++ ralink_gpio_irq_init(np, rg); ++ ++ return gpiochip_add(&rg->chip); ++} ++ ++static const struct of_device_id ralink_gpio_match[] = { ++ { .compatible = "ralink,rt2880-gpio" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ralink_gpio_match); ++ ++static struct platform_driver ralink_gpio_driver = { ++ .probe = ralink_gpio_probe, ++ .driver = { ++ .name = "rt2880_gpio", ++ .owner = THIS_MODULE, ++ .of_match_table = ralink_gpio_match, ++ }, ++}; ++ ++static int __init ralink_gpio_init(void) ++{ ++ return platform_driver_register(&ralink_gpio_driver); ++} ++ ++subsys_initcall(ralink_gpio_init); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0144-SPI-ralink-add-Ralink-SoC-spi-driver.patch b/target/linux/ramips/patches-3.9/0144-SPI-ralink-add-Ralink-SoC-spi-driver.patch new file mode 100644 index 0000000000..07a49c9f6e --- /dev/null +++ b/target/linux/ramips/patches-3.9/0144-SPI-ralink-add-Ralink-SoC-spi-driver.patch @@ -0,0 +1,528 @@ +From 14620ea8ee17ba9642fbe42d73ff8ebfb42d6cf7 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Apr 2013 23:16:18 +0200 +Subject: [PATCH 144/164] SPI: ralink: add Ralink SoC spi driver + +Add the driver needed to make SPI work on Ralink SoC. + +Signed-off-by: John Crispin +--- + drivers/spi/Kconfig | 6 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-ralink.c | 475 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 482 insertions(+) + create mode 100644 drivers/spi/spi-ralink.c + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 2be0de9..a7de751 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -327,6 +327,12 @@ config SPI_RSPI + help + SPI driver for Renesas RSPI blocks. + ++config SPI_RALINK ++ tristate "Ralink RT288x/RT305x/RT3662 SPI Controller" ++ depends on (SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620) ++ help ++ This selects a driver for the Ralink RT288x/RT305x SPI Controller. ++ + config SPI_S3C24XX + tristate "Samsung S3C24XX series SPI" + depends on ARCH_S3C24XX +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index e53c309..a4b3c5b 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -53,6 +53,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o + obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o + obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o + obj-$(CONFIG_SPI_RSPI) += spi-rspi.o ++obj-$(CONFIG_SPI_RALINK) += spi-ralink.o + obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o + spi-s3c24xx-hw-y := spi-s3c24xx.o + spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o +diff --git a/drivers/spi/spi-ralink.c b/drivers/spi/spi-ralink.c +new file mode 100644 +index 0000000..b07cbaa +--- /dev/null ++++ b/drivers/spi/spi-ralink.c +@@ -0,0 +1,475 @@ ++/* ++ * spi-ralink.c -- Ralink RT288x/RT305x SPI controller driver ++ * ++ * Copyright (C) 2011 Sergiy ++ * Copyright (C) 2011-2013 Gabor Juhos ++ * ++ * Some parts are based on spi-orion.c: ++ * Author: Shadi Ammouri ++ * Copyright (C) 2007-2008 Marvell Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_NAME "spi-ralink" ++#define RALINK_NUM_CHIPSELECTS 1 /* only one slave is supported*/ ++#define RALINK_SPI_WAIT_RDY_MAX_LOOP 2000 /* in usec */ ++ ++#define RAMIPS_SPI_STAT 0x00 ++#define RAMIPS_SPI_CFG 0x10 ++#define RAMIPS_SPI_CTL 0x14 ++#define RAMIPS_SPI_DATA 0x20 ++ ++/* SPISTAT register bit field */ ++#define SPISTAT_BUSY BIT(0) ++ ++/* SPICFG register bit field */ ++#define SPICFG_LSBFIRST 0 ++#define SPICFG_MSBFIRST BIT(8) ++#define SPICFG_SPICLKPOL BIT(6) ++#define SPICFG_RXCLKEDGE_FALLING BIT(5) ++#define SPICFG_TXCLKEDGE_FALLING BIT(4) ++#define SPICFG_SPICLK_PRESCALE_MASK 0x7 ++#define SPICFG_SPICLK_DIV2 0 ++#define SPICFG_SPICLK_DIV4 1 ++#define SPICFG_SPICLK_DIV8 2 ++#define SPICFG_SPICLK_DIV16 3 ++#define SPICFG_SPICLK_DIV32 4 ++#define SPICFG_SPICLK_DIV64 5 ++#define SPICFG_SPICLK_DIV128 6 ++#define SPICFG_SPICLK_DISABLE 7 ++ ++/* SPICTL register bit field */ ++#define SPICTL_HIZSDO BIT(3) ++#define SPICTL_STARTWR BIT(2) ++#define SPICTL_STARTRD BIT(1) ++#define SPICTL_SPIENA BIT(0) ++ ++#ifdef DEBUG ++#define spi_debug(args...) printk(args) ++#else ++#define spi_debug(args...) ++#endif ++ ++struct ralink_spi { ++ struct spi_master *master; ++ void __iomem *base; ++ unsigned int sys_freq; ++ unsigned int speed; ++ struct clk *clk; ++}; ++ ++static inline struct ralink_spi *spidev_to_ralink_spi(struct spi_device *spi) ++{ ++ return spi_master_get_devdata(spi->master); ++} ++ ++static inline u32 ralink_spi_read(struct ralink_spi *rs, u32 reg) ++{ ++ return ioread32(rs->base + reg); ++} ++ ++static inline void ralink_spi_write(struct ralink_spi *rs, u32 reg, u32 val) ++{ ++ iowrite32(val, rs->base + reg); ++} ++ ++static inline void ralink_spi_setbits(struct ralink_spi *rs, u32 reg, u32 mask) ++{ ++ void __iomem *addr = rs->base + reg; ++ u32 val; ++ ++ val = ioread32(addr); ++ val |= mask; ++ iowrite32(val, addr); ++} ++ ++static inline void ralink_spi_clrbits(struct ralink_spi *rs, u32 reg, u32 mask) ++{ ++ void __iomem *addr = rs->base + reg; ++ u32 val; ++ ++ val = ioread32(addr); ++ val &= ~mask; ++ iowrite32(val, addr); ++} ++ ++static int ralink_spi_baudrate_set(struct spi_device *spi, unsigned int speed) ++{ ++ struct ralink_spi *rs = spidev_to_ralink_spi(spi); ++ u32 rate; ++ u32 prescale; ++ u32 reg; ++ ++ spi_debug("%s: speed:%u\n", __func__, speed); ++ ++ /* ++ * the supported rates are: 2, 4, 8, ... 128 ++ * round up as we look for equal or less speed ++ */ ++ rate = DIV_ROUND_UP(rs->sys_freq, speed); ++ spi_debug("%s: rate-1:%u\n", __func__, rate); ++ rate = roundup_pow_of_two(rate); ++ spi_debug("%s: rate-2:%u\n", __func__, rate); ++ ++ /* check if requested speed is too small */ ++ if (rate > 128) ++ return -EINVAL; ++ ++ if (rate < 2) ++ rate = 2; ++ ++ /* Convert the rate to SPI clock divisor value. */ ++ prescale = ilog2(rate/2); ++ spi_debug("%s: prescale:%u\n", __func__, prescale); ++ ++ reg = ralink_spi_read(rs, RAMIPS_SPI_CFG); ++ reg = ((reg & ~SPICFG_SPICLK_PRESCALE_MASK) | prescale); ++ ralink_spi_write(rs, RAMIPS_SPI_CFG, reg); ++ rs->speed = speed; ++ return 0; ++} ++ ++/* ++ * called only when no transfer is active on the bus ++ */ ++static int ++ralink_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) ++{ ++ struct ralink_spi *rs = spidev_to_ralink_spi(spi); ++ unsigned int speed = spi->max_speed_hz; ++ int rc; ++ unsigned int bits_per_word = 8; ++ ++ if ((t != NULL) && t->speed_hz) ++ speed = t->speed_hz; ++ ++ if ((t != NULL) && t->bits_per_word) ++ bits_per_word = t->bits_per_word; ++ ++ if (rs->speed != speed) { ++ spi_debug("%s: speed_hz:%u\n", __func__, speed); ++ rc = ralink_spi_baudrate_set(spi, speed); ++ if (rc) ++ return rc; ++ } ++ ++ if (bits_per_word != 8) { ++ spi_debug("%s: bad bits_per_word: %u\n", __func__, ++ bits_per_word); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void ralink_spi_set_cs(struct ralink_spi *rs, int enable) ++{ ++ if (enable) ++ ralink_spi_clrbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA); ++ else ++ ralink_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA); ++} ++ ++static inline int ralink_spi_wait_till_ready(struct ralink_spi *rs) ++{ ++ int i; ++ ++ for (i = 0; i < RALINK_SPI_WAIT_RDY_MAX_LOOP; i++) { ++ u32 status; ++ ++ status = ralink_spi_read(rs, RAMIPS_SPI_STAT); ++ if ((status & SPISTAT_BUSY) == 0) ++ return 0; ++ ++ udelay(1); ++ } ++ ++ return -ETIMEDOUT; ++} ++ ++static unsigned int ++ralink_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer) ++{ ++ struct ralink_spi *rs = spidev_to_ralink_spi(spi); ++ unsigned count = 0; ++ u8 *rx = xfer->rx_buf; ++ const u8 *tx = xfer->tx_buf; ++ int err; ++ ++ spi_debug("%s(%d): %s %s\n", __func__, xfer->len, ++ (tx != NULL) ? "tx" : " ", ++ (rx != NULL) ? "rx" : " "); ++ ++ if (tx) { ++ for (count = 0; count < xfer->len; count++) { ++ ralink_spi_write(rs, RAMIPS_SPI_DATA, tx[count]); ++ ralink_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTWR); ++ err = ralink_spi_wait_till_ready(rs); ++ if (err) { ++ dev_err(&spi->dev, "TX failed, err=%d\n", err); ++ goto out; ++ } ++ } ++ } ++ ++ if (rx) { ++ for (count = 0; count < xfer->len; count++) { ++ ralink_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTRD); ++ err = ralink_spi_wait_till_ready(rs); ++ if (err) { ++ dev_err(&spi->dev, "RX failed, err=%d\n", err); ++ goto out; ++ } ++ rx[count] = (u8) ralink_spi_read(rs, RAMIPS_SPI_DATA); ++ } ++ } ++ ++out: ++ return count; ++} ++ ++static int ralink_spi_transfer_one_message(struct spi_master *master, ++ struct spi_message *m) ++{ ++ struct ralink_spi *rs = spi_master_get_devdata(master); ++ struct spi_device *spi = m->spi; ++ struct spi_transfer *t = NULL; ++ int par_override = 0; ++ int status = 0; ++ int cs_active = 0; ++ ++ /* Load defaults */ ++ status = ralink_spi_setup_transfer(spi, NULL); ++ if (status < 0) ++ goto msg_done; ++ ++ list_for_each_entry(t, &m->transfers, transfer_list) { ++ unsigned int bits_per_word = spi->bits_per_word; ++ ++ if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { ++ dev_err(&spi->dev, ++ "message rejected: invalid transfer data buffers\n"); ++ status = -EIO; ++ goto msg_done; ++ } ++ ++ if (t->bits_per_word) ++ bits_per_word = t->bits_per_word; ++ ++ if (bits_per_word != 8) { ++ dev_err(&spi->dev, ++ "message rejected: invalid transfer bits_per_word (%d bits)\n", ++ bits_per_word); ++ status = -EIO; ++ goto msg_done; ++ } ++ ++ if (t->speed_hz && t->speed_hz < (rs->sys_freq / 128)) { ++ dev_err(&spi->dev, ++ "message rejected: device min speed (%d Hz) exceeds required transfer speed (%d Hz)\n", ++ (rs->sys_freq / 128), t->speed_hz); ++ status = -EIO; ++ goto msg_done; ++ } ++ ++ if (par_override || t->speed_hz || t->bits_per_word) { ++ par_override = 1; ++ status = ralink_spi_setup_transfer(spi, t); ++ if (status < 0) ++ goto msg_done; ++ if (!t->speed_hz && !t->bits_per_word) ++ par_override = 0; ++ } ++ ++ if (!cs_active) { ++ ralink_spi_set_cs(rs, 1); ++ cs_active = 1; ++ } ++ ++ if (t->len) ++ m->actual_length += ralink_spi_write_read(spi, t); ++ ++ if (t->delay_usecs) ++ udelay(t->delay_usecs); ++ ++ if (t->cs_change) { ++ ralink_spi_set_cs(rs, 0); ++ cs_active = 0; ++ } ++ } ++ ++msg_done: ++ if (cs_active) ++ ralink_spi_set_cs(rs, 0); ++ ++ m->status = status; ++ spi_finalize_current_message(master); ++ ++ return 0; ++} ++ ++static int ralink_spi_setup(struct spi_device *spi) ++{ ++ struct ralink_spi *rs = spidev_to_ralink_spi(spi); ++ ++ if ((spi->max_speed_hz == 0) || ++ (spi->max_speed_hz > (rs->sys_freq / 2))) ++ spi->max_speed_hz = (rs->sys_freq / 2); ++ ++ if (spi->max_speed_hz < (rs->sys_freq / 128)) { ++ dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n", ++ spi->max_speed_hz); ++ return -EINVAL; ++ } ++ ++ if (spi->bits_per_word != 0 && spi->bits_per_word != 8) { ++ dev_err(&spi->dev, ++ "setup: requested bits per words - os wrong %d bpw\n", ++ spi->bits_per_word); ++ return -EINVAL; ++ } ++ ++ if (spi->bits_per_word == 0) ++ spi->bits_per_word = 8; ++ ++ /* ++ * baudrate & width will be set ralink_spi_setup_transfer ++ */ ++ return 0; ++} ++ ++static void ralink_spi_reset(struct ralink_spi *rs) ++{ ++ ralink_spi_write(rs, RAMIPS_SPI_CFG, ++ SPICFG_MSBFIRST | SPICFG_TXCLKEDGE_FALLING | ++ SPICFG_SPICLK_DIV16 | SPICFG_SPICLKPOL); ++ ralink_spi_write(rs, RAMIPS_SPI_CTL, SPICTL_HIZSDO | SPICTL_SPIENA); ++} ++ ++static int ralink_spi_probe(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct ralink_spi *rs; ++ struct resource *r; ++ int status = 0; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*rs)); ++ if (master == NULL) { ++ dev_dbg(&pdev->dev, "master allocation failed\n"); ++ return -ENOMEM; ++ } ++ ++ //if (pdev->id != -1) ++ master->bus_num = 0; ++ ++ /* we support only mode 0, and no options */ ++ master->mode_bits = 0; ++ ++ master->setup = ralink_spi_setup; ++ master->transfer_one_message = ralink_spi_transfer_one_message; ++ master->num_chipselect = RALINK_NUM_CHIPSELECTS; ++ master->dev.of_node = pdev->dev.of_node; ++ ++ dev_set_drvdata(&pdev->dev, master); ++ ++ rs = spi_master_get_devdata(master); ++ rs->master = master; ++ ++ rs->clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(rs->clk)) { ++ status = PTR_ERR(rs->clk); ++ dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n", ++ status); ++ goto out_put_master; ++ } ++ ++ status = clk_enable(rs->clk); ++ if (status) ++ goto out_put_clk; ++ ++ rs->sys_freq = clk_get_rate(rs->clk); ++ spi_debug("%s: sys_freq: %u\n", __func__, rs->sys_freq); ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (r == NULL) { ++ status = -ENODEV; ++ goto out_disable_clk; ++ } ++ ++ rs->base = devm_request_and_ioremap(&pdev->dev, r); ++ if (!rs->base) { ++ status = -EADDRNOTAVAIL; ++ goto out_disable_clk; ++ } ++ ++ device_reset(&pdev->dev); ++ ++ ralink_spi_reset(rs); ++ ++ status = spi_register_master(master); ++ if (status) ++ goto out_disable_clk; ++ ++ return 0; ++ ++out_disable_clk: ++ clk_disable(rs->clk); ++out_put_clk: ++ clk_put(rs->clk); ++out_put_master: ++ spi_master_put(master); ++ return status; ++} ++ ++static int ralink_spi_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct ralink_spi *rs; ++ ++ master = dev_get_drvdata(&pdev->dev); ++ rs = spi_master_get_devdata(master); ++ ++ clk_disable(rs->clk); ++ clk_put(rs->clk); ++ spi_unregister_master(master); ++ ++ return 0; ++} ++ ++MODULE_ALIAS("platform:" DRIVER_NAME); ++ ++static const struct of_device_id ralink_spi_match[] = { ++ { .compatible = "ralink,rt2880-spi" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ralink_spi_match); ++ ++static struct platform_driver ralink_spi_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = ralink_spi_match, ++ }, ++ .probe = ralink_spi_probe, ++ .remove = ralink_spi_remove, ++}; ++ ++module_platform_driver(ralink_spi_driver); ++ ++MODULE_DESCRIPTION("Ralink SPI driver"); ++MODULE_AUTHOR("Sergiy "); ++MODULE_AUTHOR("Gabor Juhos "); ++MODULE_LICENSE("GPL"); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0145-serial-of-allow-au1x00-and-rt288x-to-load-from-OF.patch b/target/linux/ramips/patches-3.9/0145-serial-of-allow-au1x00-and-rt288x-to-load-from-OF.patch new file mode 100644 index 0000000000..9be464c8dd --- /dev/null +++ b/target/linux/ramips/patches-3.9/0145-serial-of-allow-au1x00-and-rt288x-to-load-from-OF.patch @@ -0,0 +1,32 @@ +From cf52f453acdd9599a1be0fcf0e941bad62046cd4 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 22 Jan 2013 16:01:07 +0100 +Subject: [PATCH 145/164] serial: of: allow au1x00 and rt288x to load from OF + +In order to make serial_8250 loadable via OF on Au1x00 and Ralink WiSoC we need +to default the iotype to UPIO_AU. + +Signed-off-by: John Crispin +--- + drivers/tty/serial/of_serial.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/drivers/tty/serial/of_serial.c b/drivers/tty/serial/of_serial.c +index b025d54..42f8550 100644 +--- a/drivers/tty/serial/of_serial.c ++++ b/drivers/tty/serial/of_serial.c +@@ -98,7 +98,10 @@ static int of_platform_serial_setup(struct platform_device *ofdev, + port->regshift = prop; + + port->irq = irq_of_parse_and_map(np, 0); +- port->iotype = UPIO_MEM; ++ if (of_device_is_compatible(np, "ralink,rt2880-uart")) ++ port->iotype = UPIO_AU; ++ else ++ port->iotype = UPIO_MEM; + if (of_property_read_u32(np, "reg-io-width", &prop) == 0) { + switch (prop) { + case 1: +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0146-serial-ralink-adds-mt7620-serial.patch b/target/linux/ramips/patches-3.9/0146-serial-ralink-adds-mt7620-serial.patch new file mode 100644 index 0000000000..31fb18e296 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0146-serial-ralink-adds-mt7620-serial.patch @@ -0,0 +1,28 @@ +From 070a93b895d420757fd66215f0dba81f4b7b36c0 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 15 Mar 2013 18:16:01 +0100 +Subject: [PATCH 146/164] serial: ralink: adds mt7620 serial + +Add the config symbol for Mediatek7620 SoC to SERIAL_8250_RT288X + +Signed-off-by: John Crispin +--- + drivers/tty/serial/8250/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig +index 80fe91e..f6d6820 100644 +--- a/drivers/tty/serial/8250/Kconfig ++++ b/drivers/tty/serial/8250/Kconfig +@@ -296,7 +296,7 @@ config SERIAL_8250_EM + + config SERIAL_8250_RT288X + bool "Ralink RT288x/RT305x/RT3662/RT3883 serial port support" +- depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883) ++ depends on SERIAL_8250 && (SOC_RT288X || SOC_RT305X || SOC_RT3883 || SOC_MT7620) + help + If you have a Ralink RT288x/RT305x SoC based board and want to use the + serial port, say Y to this option. The driver can handle up to 2 serial +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0147-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch b/target/linux/ramips/patches-3.9/0147-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch new file mode 100644 index 0000000000..2689bb2882 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0147-mmc-MIPS-ralink-add-sdhci-for-mt7620a-SoC.patch @@ -0,0 +1,3446 @@ +From 5c328efd4f012365c9a08f7f2b23e293f639c43f Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 2 May 2013 14:59:01 +0200 +Subject: [PATCH 147/164] mmc: MIPS: ralink: add sdhci for mt7620a SoC + +Signed-off-by: John Crispin +--- + drivers/mmc/host/Kconfig | 11 + + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/mt6575_sd.h | 1068 ++++++++++++++++++ + drivers/mmc/host/sdhci-mt7620.c | 2314 +++++++++++++++++++++++++++++++++++++++ + 4 files changed, 3394 insertions(+) + create mode 100644 drivers/mmc/host/mt6575_sd.h + create mode 100644 drivers/mmc/host/sdhci-mt7620.c + +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index d88219e..aa47e64 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -249,6 +249,17 @@ config MMC_SDHCI_BCM2835 + + If unsure, say N. + ++config MMC_SDHCI_MT7620 ++ tristate "SDHCI platform support for the MT7620 SD/MMC Controller" ++ depends on SOC_MT7620 ++ depends on MMC_SDHCI_PLTFM ++ select MMC_SDHCI_IO_ACCESSORS ++ help ++ This selects the BCM2835 SD/MMC controller. If you have a BCM2835 ++ platform with SD or MMC devices, say Y or M here. ++ ++ If unsure, say N. ++ + config MMC_OMAP + tristate "TI OMAP Multimedia Card Interface support" + depends on ARCH_OMAP +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index c380e3c..3b81db4 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -60,6 +60,7 @@ obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o + obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o + obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o + obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o ++obj-$(CONFIG_MMC_SDHCI_MT7620) += sdhci-mt7620.o + + ifeq ($(CONFIG_CB710_DEBUG),y) + CFLAGS-cb710-mmc += -DDEBUG +diff --git a/drivers/mmc/host/mt6575_sd.h b/drivers/mmc/host/mt6575_sd.h +new file mode 100644 +index 0000000..406382c +--- /dev/null ++++ b/drivers/mmc/host/mt6575_sd.h +@@ -0,0 +1,1068 @@ ++/* Copyright Statement: ++ * ++ * This software/firmware and related documentation ("MediaTek Software") are ++ * protected under relevant copyright laws. The information contained herein ++ * is confidential and proprietary to MediaTek Inc. and/or its licensors. ++ * Without the prior written permission of MediaTek inc. and/or its licensors, ++ * any reproduction, modification, use or disclosure of MediaTek Software, ++ * and information contained herein, in whole or in part, shall be strictly prohibited. ++ */ ++/* MediaTek Inc. (C) 2010. All rights reserved. ++ * ++ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES ++ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") ++ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON ++ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. ++ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE ++ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR ++ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH ++ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES ++ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES ++ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK ++ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR ++ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND ++ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE, ++ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE, ++ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO ++ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. ++ * ++ * The following software/firmware and/or related documentation ("MediaTek Software") ++ * have been modified by MediaTek Inc. All revisions are subject to any receiver's ++ * applicable license agreements with MediaTek Inc. ++ */ ++ ++#ifndef MT6575_SD_H ++#define MT6575_SD_H ++ ++#include ++#include ++ ++// #include /* --- by chhung */ ++ ++typedef void (*sdio_irq_handler_t)(void*); /* external irq handler */ ++typedef void (*pm_callback_t)(pm_message_t state, void *data); ++ ++#define MSDC_CD_PIN_EN (1 << 0) /* card detection pin is wired */ ++#define MSDC_WP_PIN_EN (1 << 1) /* write protection pin is wired */ ++#define MSDC_RST_PIN_EN (1 << 2) /* emmc reset pin is wired */ ++#define MSDC_SDIO_IRQ (1 << 3) /* use internal sdio irq (bus) */ ++#define MSDC_EXT_SDIO_IRQ (1 << 4) /* use external sdio irq */ ++#define MSDC_REMOVABLE (1 << 5) /* removable slot */ ++#define MSDC_SYS_SUSPEND (1 << 6) /* suspended by system */ ++#define MSDC_HIGHSPEED (1 << 7) /* high-speed mode support */ ++#define MSDC_UHS1 (1 << 8) /* uhs-1 mode support */ ++#define MSDC_DDR (1 << 9) /* ddr mode support */ ++#define MSDC_SPE (1 << 10) /* special support */ ++#define MSDC_INTERNAL_CLK (1 << 11) /* Force Internal clock */ ++#define MSDC_TABDRV (1 << 12) /* TABLET */ ++ ++ ++#define MSDC_SMPL_RISING (0) ++#define MSDC_SMPL_FALLING (1) ++ ++#define MSDC_CMD_PIN (0) ++#define MSDC_DAT_PIN (1) ++#define MSDC_CD_PIN (2) ++#define MSDC_WP_PIN (3) ++#define MSDC_RST_PIN (4) ++ ++enum { ++ MSDC_CLKSRC_26MHZ = 0, ++ MSDC_CLKSRC_197MHZ = 1, ++ MSDC_CLKSRC_208MHZ = 2 ++}; ++ ++struct msdc_hw { ++ unsigned char clk_src; /* host clock source */ ++ unsigned char cmd_edge; /* command latch edge */ ++ unsigned char data_edge; /* data latch edge */ ++ unsigned char clk_drv; /* clock pad driving */ ++ unsigned char cmd_drv; /* command pad driving */ ++ unsigned char dat_drv; /* data pad driving */ ++ unsigned long flags; /* hardware capability flags */ ++ unsigned long data_pins; /* data pins */ ++ unsigned long data_offset; /* data address offset */ ++ ++ /* config gpio pull mode */ ++ void (*config_gpio_pin)(int type, int pull); ++ ++ /* external power control for card */ ++ void (*ext_power_on)(void); ++ void (*ext_power_off)(void); ++ ++ /* external sdio irq operations */ ++ void (*request_sdio_eirq)(sdio_irq_handler_t sdio_irq_handler, void *data); ++ void (*enable_sdio_eirq)(void); ++ void (*disable_sdio_eirq)(void); ++ ++ /* external cd irq operations */ ++ void (*request_cd_eirq)(sdio_irq_handler_t cd_irq_handler, void *data); ++ void (*enable_cd_eirq)(void); ++ void (*disable_cd_eirq)(void); ++ int (*get_cd_status)(void); ++ ++ /* power management callback for external module */ ++ void (*register_pm)(pm_callback_t pm_cb, void *data); ++}; ++ ++extern struct msdc_hw msdc0_hw; ++extern struct msdc_hw msdc1_hw; ++extern struct msdc_hw msdc2_hw; ++extern struct msdc_hw msdc3_hw; ++ ++ ++/*--------------------------------------------------------------------------*/ ++/* Common Macro */ ++/*--------------------------------------------------------------------------*/ ++#define REG_ADDR(x) ((volatile u32*)(base + OFFSET_##x)) ++ ++/*--------------------------------------------------------------------------*/ ++/* Common Definition */ ++/*--------------------------------------------------------------------------*/ ++#define MSDC_FIFO_SZ (128) ++#define MSDC_FIFO_THD (64) // (128) ++#define MSDC_NUM (4) ++ ++#define MSDC_MS (0) ++#define MSDC_SDMMC (1) ++ ++#define MSDC_MODE_UNKNOWN (0) ++#define MSDC_MODE_PIO (1) ++#define MSDC_MODE_DMA_BASIC (2) ++#define MSDC_MODE_DMA_DESC (3) ++#define MSDC_MODE_DMA_ENHANCED (4) ++#define MSDC_MODE_MMC_STREAM (5) ++ ++#define MSDC_BUS_1BITS (0) ++#define MSDC_BUS_4BITS (1) ++#define MSDC_BUS_8BITS (2) ++ ++#define MSDC_BRUST_8B (3) ++#define MSDC_BRUST_16B (4) ++#define MSDC_BRUST_32B (5) ++#define MSDC_BRUST_64B (6) ++ ++#define MSDC_PIN_PULL_NONE (0) ++#define MSDC_PIN_PULL_DOWN (1) ++#define MSDC_PIN_PULL_UP (2) ++#define MSDC_PIN_KEEP (3) ++ ++#define MSDC_MAX_SCLK (48000000) /* +/- by chhung */ ++#define MSDC_MIN_SCLK (260000) ++ ++#define MSDC_AUTOCMD12 (0x0001) ++#define MSDC_AUTOCMD23 (0x0002) ++#define MSDC_AUTOCMD19 (0x0003) ++ ++#define MSDC_EMMC_BOOTMODE0 (0) /* Pull low CMD mode */ ++#define MSDC_EMMC_BOOTMODE1 (1) /* Reset CMD mode */ ++ ++enum { ++ RESP_NONE = 0, ++ RESP_R1, ++ RESP_R2, ++ RESP_R3, ++ RESP_R4, ++ RESP_R5, ++ RESP_R6, ++ RESP_R7, ++ RESP_R1B ++}; ++ ++/*--------------------------------------------------------------------------*/ ++/* Register Offset */ ++/*--------------------------------------------------------------------------*/ ++#define OFFSET_MSDC_CFG (0x0) ++#define OFFSET_MSDC_IOCON (0x04) ++#define OFFSET_MSDC_PS (0x08) ++#define OFFSET_MSDC_INT (0x0c) ++#define OFFSET_MSDC_INTEN (0x10) ++#define OFFSET_MSDC_FIFOCS (0x14) ++#define OFFSET_MSDC_TXDATA (0x18) ++#define OFFSET_MSDC_RXDATA (0x1c) ++#define OFFSET_SDC_CFG (0x30) ++#define OFFSET_SDC_CMD (0x34) ++#define OFFSET_SDC_ARG (0x38) ++#define OFFSET_SDC_STS (0x3c) ++#define OFFSET_SDC_RESP0 (0x40) ++#define OFFSET_SDC_RESP1 (0x44) ++#define OFFSET_SDC_RESP2 (0x48) ++#define OFFSET_SDC_RESP3 (0x4c) ++#define OFFSET_SDC_BLK_NUM (0x50) ++#define OFFSET_SDC_CSTS (0x58) ++#define OFFSET_SDC_CSTS_EN (0x5c) ++#define OFFSET_SDC_DCRC_STS (0x60) ++#define OFFSET_EMMC_CFG0 (0x70) ++#define OFFSET_EMMC_CFG1 (0x74) ++#define OFFSET_EMMC_STS (0x78) ++#define OFFSET_EMMC_IOCON (0x7c) ++#define OFFSET_SDC_ACMD_RESP (0x80) ++#define OFFSET_SDC_ACMD19_TRG (0x84) ++#define OFFSET_SDC_ACMD19_STS (0x88) ++#define OFFSET_MSDC_DMA_SA (0x90) ++#define OFFSET_MSDC_DMA_CA (0x94) ++#define OFFSET_MSDC_DMA_CTRL (0x98) ++#define OFFSET_MSDC_DMA_CFG (0x9c) ++#define OFFSET_MSDC_DBG_SEL (0xa0) ++#define OFFSET_MSDC_DBG_OUT (0xa4) ++#define OFFSET_MSDC_PATCH_BIT (0xb0) ++#define OFFSET_MSDC_PATCH_BIT1 (0xb4) ++#define OFFSET_MSDC_PAD_CTL0 (0xe0) ++#define OFFSET_MSDC_PAD_CTL1 (0xe4) ++#define OFFSET_MSDC_PAD_CTL2 (0xe8) ++#define OFFSET_MSDC_PAD_TUNE (0xec) ++#define OFFSET_MSDC_DAT_RDDLY0 (0xf0) ++#define OFFSET_MSDC_DAT_RDDLY1 (0xf4) ++#define OFFSET_MSDC_HW_DBG (0xf8) ++#define OFFSET_MSDC_VERSION (0x100) ++#define OFFSET_MSDC_ECO_VER (0x104) ++ ++/*--------------------------------------------------------------------------*/ ++/* Register Address */ ++/*--------------------------------------------------------------------------*/ ++ ++/* common register */ ++#define MSDC_CFG REG_ADDR(MSDC_CFG) ++#define MSDC_IOCON REG_ADDR(MSDC_IOCON) ++#define MSDC_PS REG_ADDR(MSDC_PS) ++#define MSDC_INT REG_ADDR(MSDC_INT) ++#define MSDC_INTEN REG_ADDR(MSDC_INTEN) ++#define MSDC_FIFOCS REG_ADDR(MSDC_FIFOCS) ++#define MSDC_TXDATA REG_ADDR(MSDC_TXDATA) ++#define MSDC_RXDATA REG_ADDR(MSDC_RXDATA) ++#define MSDC_PATCH_BIT0 REG_ADDR(MSDC_PATCH_BIT) ++ ++/* sdmmc register */ ++#define SDC_CFG REG_ADDR(SDC_CFG) ++#define SDC_CMD REG_ADDR(SDC_CMD) ++#define SDC_ARG REG_ADDR(SDC_ARG) ++#define SDC_STS REG_ADDR(SDC_STS) ++#define SDC_RESP0 REG_ADDR(SDC_RESP0) ++#define SDC_RESP1 REG_ADDR(SDC_RESP1) ++#define SDC_RESP2 REG_ADDR(SDC_RESP2) ++#define SDC_RESP3 REG_ADDR(SDC_RESP3) ++#define SDC_BLK_NUM REG_ADDR(SDC_BLK_NUM) ++#define SDC_CSTS REG_ADDR(SDC_CSTS) ++#define SDC_CSTS_EN REG_ADDR(SDC_CSTS_EN) ++#define SDC_DCRC_STS REG_ADDR(SDC_DCRC_STS) ++ ++/* emmc register*/ ++#define EMMC_CFG0 REG_ADDR(EMMC_CFG0) ++#define EMMC_CFG1 REG_ADDR(EMMC_CFG1) ++#define EMMC_STS REG_ADDR(EMMC_STS) ++#define EMMC_IOCON REG_ADDR(EMMC_IOCON) ++ ++/* auto command register */ ++#define SDC_ACMD_RESP REG_ADDR(SDC_ACMD_RESP) ++#define SDC_ACMD19_TRG REG_ADDR(SDC_ACMD19_TRG) ++#define SDC_ACMD19_STS REG_ADDR(SDC_ACMD19_STS) ++ ++/* dma register */ ++#define MSDC_DMA_SA REG_ADDR(MSDC_DMA_SA) ++#define MSDC_DMA_CA REG_ADDR(MSDC_DMA_CA) ++#define MSDC_DMA_CTRL REG_ADDR(MSDC_DMA_CTRL) ++#define MSDC_DMA_CFG REG_ADDR(MSDC_DMA_CFG) ++ ++/* pad ctrl register */ ++#define MSDC_PAD_CTL0 REG_ADDR(MSDC_PAD_CTL0) ++#define MSDC_PAD_CTL1 REG_ADDR(MSDC_PAD_CTL1) ++#define MSDC_PAD_CTL2 REG_ADDR(MSDC_PAD_CTL2) ++ ++/* data read delay */ ++#define MSDC_DAT_RDDLY0 REG_ADDR(MSDC_DAT_RDDLY0) ++#define MSDC_DAT_RDDLY1 REG_ADDR(MSDC_DAT_RDDLY1) ++ ++/* debug register */ ++#define MSDC_DBG_SEL REG_ADDR(MSDC_DBG_SEL) ++#define MSDC_DBG_OUT REG_ADDR(MSDC_DBG_OUT) ++ ++/* misc register */ ++#define MSDC_PATCH_BIT REG_ADDR(MSDC_PATCH_BIT) ++#define MSDC_PATCH_BIT1 REG_ADDR(MSDC_PATCH_BIT1) ++#define MSDC_PAD_TUNE REG_ADDR(MSDC_PAD_TUNE) ++#define MSDC_HW_DBG REG_ADDR(MSDC_HW_DBG) ++#define MSDC_VERSION REG_ADDR(MSDC_VERSION) ++#define MSDC_ECO_VER REG_ADDR(MSDC_ECO_VER) /* ECO Version */ ++ ++/*--------------------------------------------------------------------------*/ ++/* Register Mask */ ++/*--------------------------------------------------------------------------*/ ++ ++/* MSDC_CFG mask */ ++#define MSDC_CFG_MODE (0x1 << 0) /* RW */ ++#define MSDC_CFG_CKPDN (0x1 << 1) /* RW */ ++#define MSDC_CFG_RST (0x1 << 2) /* RW */ ++#define MSDC_CFG_PIO (0x1 << 3) /* RW */ ++#define MSDC_CFG_CKDRVEN (0x1 << 4) /* RW */ ++#define MSDC_CFG_BV18SDT (0x1 << 5) /* RW */ ++#define MSDC_CFG_BV18PSS (0x1 << 6) /* R */ ++#define MSDC_CFG_CKSTB (0x1 << 7) /* R */ ++#define MSDC_CFG_CKDIV (0xff << 8) /* RW */ ++#define MSDC_CFG_CKMOD (0x3 << 16) /* RW */ ++ ++/* MSDC_IOCON mask */ ++#define MSDC_IOCON_SDR104CKS (0x1 << 0) /* RW */ ++#define MSDC_IOCON_RSPL (0x1 << 1) /* RW */ ++#define MSDC_IOCON_DSPL (0x1 << 2) /* RW */ ++#define MSDC_IOCON_DDLSEL (0x1 << 3) /* RW */ ++#define MSDC_IOCON_DDR50CKD (0x1 << 4) /* RW */ ++#define MSDC_IOCON_DSPLSEL (0x1 << 5) /* RW */ ++#define MSDC_IOCON_D0SPL (0x1 << 16) /* RW */ ++#define MSDC_IOCON_D1SPL (0x1 << 17) /* RW */ ++#define MSDC_IOCON_D2SPL (0x1 << 18) /* RW */ ++#define MSDC_IOCON_D3SPL (0x1 << 19) /* RW */ ++#define MSDC_IOCON_D4SPL (0x1 << 20) /* RW */ ++#define MSDC_IOCON_D5SPL (0x1 << 21) /* RW */ ++#define MSDC_IOCON_D6SPL (0x1 << 22) /* RW */ ++#define MSDC_IOCON_D7SPL (0x1 << 23) /* RW */ ++#define MSDC_IOCON_RISCSZ (0x3 << 24) /* RW */ ++ ++/* MSDC_PS mask */ ++#define MSDC_PS_CDEN (0x1 << 0) /* RW */ ++#define MSDC_PS_CDSTS (0x1 << 1) /* R */ ++#define MSDC_PS_CDDEBOUNCE (0xf << 12) /* RW */ ++#define MSDC_PS_DAT (0xff << 16) /* R */ ++#define MSDC_PS_CMD (0x1 << 24) /* R */ ++#define MSDC_PS_WP (0x1UL<< 31) /* R */ ++ ++/* MSDC_INT mask */ ++#define MSDC_INT_MMCIRQ (0x1 << 0) /* W1C */ ++#define MSDC_INT_CDSC (0x1 << 1) /* W1C */ ++#define MSDC_INT_ACMDRDY (0x1 << 3) /* W1C */ ++#define MSDC_INT_ACMDTMO (0x1 << 4) /* W1C */ ++#define MSDC_INT_ACMDCRCERR (0x1 << 5) /* W1C */ ++#define MSDC_INT_DMAQ_EMPTY (0x1 << 6) /* W1C */ ++#define MSDC_INT_SDIOIRQ (0x1 << 7) /* W1C */ ++#define MSDC_INT_CMDRDY (0x1 << 8) /* W1C */ ++#define MSDC_INT_CMDTMO (0x1 << 9) /* W1C */ ++#define MSDC_INT_RSPCRCERR (0x1 << 10) /* W1C */ ++#define MSDC_INT_CSTA (0x1 << 11) /* R */ ++#define MSDC_INT_XFER_COMPL (0x1 << 12) /* W1C */ ++#define MSDC_INT_DXFER_DONE (0x1 << 13) /* W1C */ ++#define MSDC_INT_DATTMO (0x1 << 14) /* W1C */ ++#define MSDC_INT_DATCRCERR (0x1 << 15) /* W1C */ ++#define MSDC_INT_ACMD19_DONE (0x1 << 16) /* W1C */ ++ ++/* MSDC_INTEN mask */ ++#define MSDC_INTEN_MMCIRQ (0x1 << 0) /* RW */ ++#define MSDC_INTEN_CDSC (0x1 << 1) /* RW */ ++#define MSDC_INTEN_ACMDRDY (0x1 << 3) /* RW */ ++#define MSDC_INTEN_ACMDTMO (0x1 << 4) /* RW */ ++#define MSDC_INTEN_ACMDCRCERR (0x1 << 5) /* RW */ ++#define MSDC_INTEN_DMAQ_EMPTY (0x1 << 6) /* RW */ ++#define MSDC_INTEN_SDIOIRQ (0x1 << 7) /* RW */ ++#define MSDC_INTEN_CMDRDY (0x1 << 8) /* RW */ ++#define MSDC_INTEN_CMDTMO (0x1 << 9) /* RW */ ++#define MSDC_INTEN_RSPCRCERR (0x1 << 10) /* RW */ ++#define MSDC_INTEN_CSTA (0x1 << 11) /* RW */ ++#define MSDC_INTEN_XFER_COMPL (0x1 << 12) /* RW */ ++#define MSDC_INTEN_DXFER_DONE (0x1 << 13) /* RW */ ++#define MSDC_INTEN_DATTMO (0x1 << 14) /* RW */ ++#define MSDC_INTEN_DATCRCERR (0x1 << 15) /* RW */ ++#define MSDC_INTEN_ACMD19_DONE (0x1 << 16) /* RW */ ++ ++/* MSDC_FIFOCS mask */ ++#define MSDC_FIFOCS_RXCNT (0xff << 0) /* R */ ++#define MSDC_FIFOCS_TXCNT (0xff << 16) /* R */ ++#define MSDC_FIFOCS_CLR (0x1UL<< 31) /* RW */ ++ ++/* SDC_CFG mask */ ++#define SDC_CFG_SDIOINTWKUP (0x1 << 0) /* RW */ ++#define SDC_CFG_INSWKUP (0x1 << 1) /* RW */ ++#define SDC_CFG_BUSWIDTH (0x3 << 16) /* RW */ ++#define SDC_CFG_SDIO (0x1 << 19) /* RW */ ++#define SDC_CFG_SDIOIDE (0x1 << 20) /* RW */ ++#define SDC_CFG_INTATGAP (0x1 << 21) /* RW */ ++#define SDC_CFG_DTOC (0xffUL << 24) /* RW */ ++ ++/* SDC_CMD mask */ ++#define SDC_CMD_OPC (0x3f << 0) /* RW */ ++#define SDC_CMD_BRK (0x1 << 6) /* RW */ ++#define SDC_CMD_RSPTYP (0x7 << 7) /* RW */ ++#define SDC_CMD_DTYP (0x3 << 11) /* RW */ ++#define SDC_CMD_DTYP (0x3 << 11) /* RW */ ++#define SDC_CMD_RW (0x1 << 13) /* RW */ ++#define SDC_CMD_STOP (0x1 << 14) /* RW */ ++#define SDC_CMD_GOIRQ (0x1 << 15) /* RW */ ++#define SDC_CMD_BLKLEN (0xfff<< 16) /* RW */ ++#define SDC_CMD_AUTOCMD (0x3 << 28) /* RW */ ++#define SDC_CMD_VOLSWTH (0x1 << 30) /* RW */ ++ ++/* SDC_STS mask */ ++#define SDC_STS_SDCBUSY (0x1 << 0) /* RW */ ++#define SDC_STS_CMDBUSY (0x1 << 1) /* RW */ ++#define SDC_STS_SWR_COMPL (0x1 << 31) /* RW */ ++ ++/* SDC_DCRC_STS mask */ ++#define SDC_DCRC_STS_NEG (0xf << 8) /* RO */ ++#define SDC_DCRC_STS_POS (0xff << 0) /* RO */ ++ ++/* EMMC_CFG0 mask */ ++#define EMMC_CFG0_BOOTSTART (0x1 << 0) /* W */ ++#define EMMC_CFG0_BOOTSTOP (0x1 << 1) /* W */ ++#define EMMC_CFG0_BOOTMODE (0x1 << 2) /* RW */ ++#define EMMC_CFG0_BOOTACKDIS (0x1 << 3) /* RW */ ++#define EMMC_CFG0_BOOTWDLY (0x7 << 12) /* RW */ ++#define EMMC_CFG0_BOOTSUPP (0x1 << 15) /* RW */ ++ ++/* EMMC_CFG1 mask */ ++#define EMMC_CFG1_BOOTDATTMC (0xfffff << 0) /* RW */ ++#define EMMC_CFG1_BOOTACKTMC (0xfffUL << 20) /* RW */ ++ ++/* EMMC_STS mask */ ++#define EMMC_STS_BOOTCRCERR (0x1 << 0) /* W1C */ ++#define EMMC_STS_BOOTACKERR (0x1 << 1) /* W1C */ ++#define EMMC_STS_BOOTDATTMO (0x1 << 2) /* W1C */ ++#define EMMC_STS_BOOTACKTMO (0x1 << 3) /* W1C */ ++#define EMMC_STS_BOOTUPSTATE (0x1 << 4) /* R */ ++#define EMMC_STS_BOOTACKRCV (0x1 << 5) /* W1C */ ++#define EMMC_STS_BOOTDATRCV (0x1 << 6) /* R */ ++ ++/* EMMC_IOCON mask */ ++#define EMMC_IOCON_BOOTRST (0x1 << 0) /* RW */ ++ ++/* SDC_ACMD19_TRG mask */ ++#define SDC_ACMD19_TRG_TUNESEL (0xf << 0) /* RW */ ++ ++/* MSDC_DMA_CTRL mask */ ++#define MSDC_DMA_CTRL_START (0x1 << 0) /* W */ ++#define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */ ++#define MSDC_DMA_CTRL_RESUME (0x1 << 2) /* W */ ++#define MSDC_DMA_CTRL_MODE (0x1 << 8) /* RW */ ++#define MSDC_DMA_CTRL_LASTBUF (0x1 << 10) /* RW */ ++#define MSDC_DMA_CTRL_BRUSTSZ (0x7 << 12) /* RW */ ++#define MSDC_DMA_CTRL_XFERSZ (0xffffUL << 16)/* RW */ ++ ++/* MSDC_DMA_CFG mask */ ++#define MSDC_DMA_CFG_STS (0x1 << 0) /* R */ ++#define MSDC_DMA_CFG_DECSEN (0x1 << 1) /* RW */ ++#define MSDC_DMA_CFG_BDCSERR (0x1 << 4) /* R */ ++#define MSDC_DMA_CFG_GPDCSERR (0x1 << 5) /* R */ ++ ++/* MSDC_PATCH_BIT mask */ ++#define MSDC_PATCH_BIT_WFLSMODE (0x1 << 0) /* RW */ ++#define MSDC_PATCH_BIT_ODDSUPP (0x1 << 1) /* RW */ ++#define MSDC_PATCH_BIT_CKGEN_CK (0x1 << 6) /* E2: Fixed to 1 */ ++#define MSDC_PATCH_BIT_IODSSEL (0x1 << 16) /* RW */ ++#define MSDC_PATCH_BIT_IOINTSEL (0x1 << 17) /* RW */ ++#define MSDC_PATCH_BIT_BUSYDLY (0xf << 18) /* RW */ ++#define MSDC_PATCH_BIT_WDOD (0xf << 22) /* RW */ ++#define MSDC_PATCH_BIT_IDRTSEL (0x1 << 26) /* RW */ ++#define MSDC_PATCH_BIT_CMDFSEL (0x1 << 27) /* RW */ ++#define MSDC_PATCH_BIT_INTDLSEL (0x1 << 28) /* RW */ ++#define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */ ++#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */ ++ ++/* MSDC_PATCH_BIT1 mask */ ++#define MSDC_PATCH_BIT1_WRDAT_CRCS (0x7 << 3) ++#define MSDC_PATCH_BIT1_CMD_RSP (0x7 << 0) ++ ++/* MSDC_PAD_CTL0 mask */ ++#define MSDC_PAD_CTL0_CLKDRVN (0x7 << 0) /* RW */ ++#define MSDC_PAD_CTL0_CLKDRVP (0x7 << 4) /* RW */ ++#define MSDC_PAD_CTL0_CLKSR (0x1 << 8) /* RW */ ++#define MSDC_PAD_CTL0_CLKPD (0x1 << 16) /* RW */ ++#define MSDC_PAD_CTL0_CLKPU (0x1 << 17) /* RW */ ++#define MSDC_PAD_CTL0_CLKSMT (0x1 << 18) /* RW */ ++#define MSDC_PAD_CTL0_CLKIES (0x1 << 19) /* RW */ ++#define MSDC_PAD_CTL0_CLKTDSEL (0xf << 20) /* RW */ ++#define MSDC_PAD_CTL0_CLKRDSEL (0xffUL<< 24) /* RW */ ++ ++/* MSDC_PAD_CTL1 mask */ ++#define MSDC_PAD_CTL1_CMDDRVN (0x7 << 0) /* RW */ ++#define MSDC_PAD_CTL1_CMDDRVP (0x7 << 4) /* RW */ ++#define MSDC_PAD_CTL1_CMDSR (0x1 << 8) /* RW */ ++#define MSDC_PAD_CTL1_CMDPD (0x1 << 16) /* RW */ ++#define MSDC_PAD_CTL1_CMDPU (0x1 << 17) /* RW */ ++#define MSDC_PAD_CTL1_CMDSMT (0x1 << 18) /* RW */ ++#define MSDC_PAD_CTL1_CMDIES (0x1 << 19) /* RW */ ++#define MSDC_PAD_CTL1_CMDTDSEL (0xf << 20) /* RW */ ++#define MSDC_PAD_CTL1_CMDRDSEL (0xffUL<< 24) /* RW */ ++ ++/* MSDC_PAD_CTL2 mask */ ++#define MSDC_PAD_CTL2_DATDRVN (0x7 << 0) /* RW */ ++#define MSDC_PAD_CTL2_DATDRVP (0x7 << 4) /* RW */ ++#define MSDC_PAD_CTL2_DATSR (0x1 << 8) /* RW */ ++#define MSDC_PAD_CTL2_DATPD (0x1 << 16) /* RW */ ++#define MSDC_PAD_CTL2_DATPU (0x1 << 17) /* RW */ ++#define MSDC_PAD_CTL2_DATIES (0x1 << 19) /* RW */ ++#define MSDC_PAD_CTL2_DATSMT (0x1 << 18) /* RW */ ++#define MSDC_PAD_CTL2_DATTDSEL (0xf << 20) /* RW */ ++#define MSDC_PAD_CTL2_DATRDSEL (0xffUL<< 24) /* RW */ ++ ++/* MSDC_PAD_TUNE mask */ ++#define MSDC_PAD_TUNE_DATWRDLY (0x1F << 0) /* RW */ ++#define MSDC_PAD_TUNE_DATRRDLY (0x1F << 8) /* RW */ ++#define MSDC_PAD_TUNE_CMDRDLY (0x1F << 16) /* RW */ ++#define MSDC_PAD_TUNE_CMDRRDLY (0x1FUL << 22) /* RW */ ++#define MSDC_PAD_TUNE_CLKTXDLY (0x1FUL << 27) /* RW */ ++ ++/* MSDC_DAT_RDDLY0/1 mask */ ++#define MSDC_DAT_RDDLY0_D0 (0x1F << 0) /* RW */ ++#define MSDC_DAT_RDDLY0_D1 (0x1F << 8) /* RW */ ++#define MSDC_DAT_RDDLY0_D2 (0x1F << 16) /* RW */ ++#define MSDC_DAT_RDDLY0_D3 (0x1F << 24) /* RW */ ++ ++#define MSDC_DAT_RDDLY1_D4 (0x1F << 0) /* RW */ ++#define MSDC_DAT_RDDLY1_D5 (0x1F << 8) /* RW */ ++#define MSDC_DAT_RDDLY1_D6 (0x1F << 16) /* RW */ ++#define MSDC_DAT_RDDLY1_D7 (0x1F << 24) /* RW */ ++ ++#define MSDC_CKGEN_MSDC_DLY_SEL (0x1F<<10) ++#define MSDC_INT_DAT_LATCH_CK_SEL (0x7<<7) ++#define MSDC_CKGEN_MSDC_CK_SEL (0x1<<6) ++#define CARD_READY_FOR_DATA (1<<8) ++#define CARD_CURRENT_STATE(x) ((x&0x00001E00)>>9) ++ ++/*--------------------------------------------------------------------------*/ ++/* Descriptor Structure */ ++/*--------------------------------------------------------------------------*/ ++typedef struct { ++ u32 hwo:1; /* could be changed by hw */ ++ u32 bdp:1; ++ u32 rsv0:6; ++ u32 chksum:8; ++ u32 intr:1; ++ u32 rsv1:15; ++ void *next; ++ void *ptr; ++ u32 buflen:16; ++ u32 extlen:8; ++ u32 rsv2:8; ++ u32 arg; ++ u32 blknum; ++ u32 cmd; ++} gpd_t; ++ ++typedef struct { ++ u32 eol:1; ++ u32 rsv0:7; ++ u32 chksum:8; ++ u32 rsv1:1; ++ u32 blkpad:1; ++ u32 dwpad:1; ++ u32 rsv2:13; ++ void *next; ++ void *ptr; ++ u32 buflen:16; ++ u32 rsv3:16; ++} bd_t; ++ ++/*--------------------------------------------------------------------------*/ ++/* Register Debugging Structure */ ++/*--------------------------------------------------------------------------*/ ++ ++typedef struct { ++ u32 msdc:1; ++ u32 ckpwn:1; ++ u32 rst:1; ++ u32 pio:1; ++ u32 ckdrven:1; ++ u32 start18v:1; ++ u32 pass18v:1; ++ u32 ckstb:1; ++ u32 ckdiv:8; ++ u32 ckmod:2; ++ u32 pad:14; ++} msdc_cfg_reg; ++typedef struct { ++ u32 sdr104cksel:1; ++ u32 rsmpl:1; ++ u32 dsmpl:1; ++ u32 ddlysel:1; ++ u32 ddr50ckd:1; ++ u32 dsplsel:1; ++ u32 pad1:10; ++ u32 d0spl:1; ++ u32 d1spl:1; ++ u32 d2spl:1; ++ u32 d3spl:1; ++ u32 d4spl:1; ++ u32 d5spl:1; ++ u32 d6spl:1; ++ u32 d7spl:1; ++ u32 riscsz:1; ++ u32 pad2:7; ++} msdc_iocon_reg; ++typedef struct { ++ u32 cden:1; ++ u32 cdsts:1; ++ u32 pad1:10; ++ u32 cddebounce:4; ++ u32 dat:8; ++ u32 cmd:1; ++ u32 pad2:6; ++ u32 wp:1; ++} msdc_ps_reg; ++typedef struct { ++ u32 mmcirq:1; ++ u32 cdsc:1; ++ u32 pad1:1; ++ u32 atocmdrdy:1; ++ u32 atocmdtmo:1; ++ u32 atocmdcrc:1; ++ u32 dmaqempty:1; ++ u32 sdioirq:1; ++ u32 cmdrdy:1; ++ u32 cmdtmo:1; ++ u32 rspcrc:1; ++ u32 csta:1; ++ u32 xfercomp:1; ++ u32 dxferdone:1; ++ u32 dattmo:1; ++ u32 datcrc:1; ++ u32 atocmd19done:1; ++ u32 pad2:15; ++} msdc_int_reg; ++typedef struct { ++ u32 mmcirq:1; ++ u32 cdsc:1; ++ u32 pad1:1; ++ u32 atocmdrdy:1; ++ u32 atocmdtmo:1; ++ u32 atocmdcrc:1; ++ u32 dmaqempty:1; ++ u32 sdioirq:1; ++ u32 cmdrdy:1; ++ u32 cmdtmo:1; ++ u32 rspcrc:1; ++ u32 csta:1; ++ u32 xfercomp:1; ++ u32 dxferdone:1; ++ u32 dattmo:1; ++ u32 datcrc:1; ++ u32 atocmd19done:1; ++ u32 pad2:15; ++} msdc_inten_reg; ++typedef struct { ++ u32 rxcnt:8; ++ u32 pad1:8; ++ u32 txcnt:8; ++ u32 pad2:7; ++ u32 clr:1; ++} msdc_fifocs_reg; ++typedef struct { ++ u32 val; ++} msdc_txdat_reg; ++typedef struct { ++ u32 val; ++} msdc_rxdat_reg; ++typedef struct { ++ u32 sdiowkup:1; ++ u32 inswkup:1; ++ u32 pad1:14; ++ u32 buswidth:2; ++ u32 pad2:1; ++ u32 sdio:1; ++ u32 sdioide:1; ++ u32 intblkgap:1; ++ u32 pad4:2; ++ u32 dtoc:8; ++} sdc_cfg_reg; ++typedef struct { ++ u32 cmd:6; ++ u32 brk:1; ++ u32 rsptyp:3; ++ u32 pad1:1; ++ u32 dtype:2; ++ u32 rw:1; ++ u32 stop:1; ++ u32 goirq:1; ++ u32 blklen:12; ++ u32 atocmd:2; ++ u32 volswth:1; ++ u32 pad2:1; ++} sdc_cmd_reg; ++typedef struct { ++ u32 arg; ++} sdc_arg_reg; ++typedef struct { ++ u32 sdcbusy:1; ++ u32 cmdbusy:1; ++ u32 pad:29; ++ u32 swrcmpl:1; ++} sdc_sts_reg; ++typedef struct { ++ u32 val; ++} sdc_resp0_reg; ++typedef struct { ++ u32 val; ++} sdc_resp1_reg; ++typedef struct { ++ u32 val; ++} sdc_resp2_reg; ++typedef struct { ++ u32 val; ++} sdc_resp3_reg; ++typedef struct { ++ u32 num; ++} sdc_blknum_reg; ++typedef struct { ++ u32 sts; ++} sdc_csts_reg; ++typedef struct { ++ u32 sts; ++} sdc_cstsen_reg; ++typedef struct { ++ u32 datcrcsts:8; ++ u32 ddrcrcsts:4; ++ u32 pad:20; ++} sdc_datcrcsts_reg; ++typedef struct { ++ u32 bootstart:1; ++ u32 bootstop:1; ++ u32 bootmode:1; ++ u32 pad1:9; ++ u32 bootwaidly:3; ++ u32 bootsupp:1; ++ u32 pad2:16; ++} emmc_cfg0_reg; ++typedef struct { ++ u32 bootcrctmc:16; ++ u32 pad:4; ++ u32 bootacktmc:12; ++} emmc_cfg1_reg; ++typedef struct { ++ u32 bootcrcerr:1; ++ u32 bootackerr:1; ++ u32 bootdattmo:1; ++ u32 bootacktmo:1; ++ u32 bootupstate:1; ++ u32 bootackrcv:1; ++ u32 bootdatrcv:1; ++ u32 pad:25; ++} emmc_sts_reg; ++typedef struct { ++ u32 bootrst:1; ++ u32 pad:31; ++} emmc_iocon_reg; ++typedef struct { ++ u32 val; ++} msdc_acmd_resp_reg; ++typedef struct { ++ u32 tunesel:4; ++ u32 pad:28; ++} msdc_acmd19_trg_reg; ++typedef struct { ++ u32 val; ++} msdc_acmd19_sts_reg; ++typedef struct { ++ u32 addr; ++} msdc_dma_sa_reg; ++typedef struct { ++ u32 addr; ++} msdc_dma_ca_reg; ++typedef struct { ++ u32 start:1; ++ u32 stop:1; ++ u32 resume:1; ++ u32 pad1:5; ++ u32 mode:1; ++ u32 pad2:1; ++ u32 lastbuf:1; ++ u32 pad3:1; ++ u32 brustsz:3; ++ u32 pad4:1; ++ u32 xfersz:16; ++} msdc_dma_ctrl_reg; ++typedef struct { ++ u32 status:1; ++ u32 decsen:1; ++ u32 pad1:2; ++ u32 bdcsen:1; ++ u32 gpdcsen:1; ++ u32 pad2:26; ++} msdc_dma_cfg_reg; ++typedef struct { ++ u32 sel:16; ++ u32 pad2:16; ++} msdc_dbg_sel_reg; ++typedef struct { ++ u32 val; ++} msdc_dbg_out_reg; ++typedef struct { ++ u32 clkdrvn:3; ++ u32 rsv0:1; ++ u32 clkdrvp:3; ++ u32 rsv1:1; ++ u32 clksr:1; ++ u32 rsv2:7; ++ u32 clkpd:1; ++ u32 clkpu:1; ++ u32 clksmt:1; ++ u32 clkies:1; ++ u32 clktdsel:4; ++ u32 clkrdsel:8; ++} msdc_pad_ctl0_reg; ++typedef struct { ++ u32 cmddrvn:3; ++ u32 rsv0:1; ++ u32 cmddrvp:3; ++ u32 rsv1:1; ++ u32 cmdsr:1; ++ u32 rsv2:7; ++ u32 cmdpd:1; ++ u32 cmdpu:1; ++ u32 cmdsmt:1; ++ u32 cmdies:1; ++ u32 cmdtdsel:4; ++ u32 cmdrdsel:8; ++} msdc_pad_ctl1_reg; ++typedef struct { ++ u32 datdrvn:3; ++ u32 rsv0:1; ++ u32 datdrvp:3; ++ u32 rsv1:1; ++ u32 datsr:1; ++ u32 rsv2:7; ++ u32 datpd:1; ++ u32 datpu:1; ++ u32 datsmt:1; ++ u32 daties:1; ++ u32 dattdsel:4; ++ u32 datrdsel:8; ++} msdc_pad_ctl2_reg; ++typedef struct { ++ u32 wrrxdly:3; ++ u32 pad1:5; ++ u32 rdrxdly:8; ++ u32 pad2:16; ++} msdc_pad_tune_reg; ++typedef struct { ++ u32 dat0:5; ++ u32 rsv0:3; ++ u32 dat1:5; ++ u32 rsv1:3; ++ u32 dat2:5; ++ u32 rsv2:3; ++ u32 dat3:5; ++ u32 rsv3:3; ++} msdc_dat_rddly0; ++typedef struct { ++ u32 dat4:5; ++ u32 rsv4:3; ++ u32 dat5:5; ++ u32 rsv5:3; ++ u32 dat6:5; ++ u32 rsv6:3; ++ u32 dat7:5; ++ u32 rsv7:3; ++} msdc_dat_rddly1; ++typedef struct { ++ u32 dbg0sel:8; ++ u32 dbg1sel:6; ++ u32 pad1:2; ++ u32 dbg2sel:6; ++ u32 pad2:2; ++ u32 dbg3sel:6; ++ u32 pad3:2; ++} msdc_hw_dbg_reg; ++typedef struct { ++ u32 val; ++} msdc_version_reg; ++typedef struct { ++ u32 val; ++} msdc_eco_ver_reg; ++ ++struct msdc_regs { ++ msdc_cfg_reg msdc_cfg; /* base+0x00h */ ++ msdc_iocon_reg msdc_iocon; /* base+0x04h */ ++ msdc_ps_reg msdc_ps; /* base+0x08h */ ++ msdc_int_reg msdc_int; /* base+0x0ch */ ++ msdc_inten_reg msdc_inten; /* base+0x10h */ ++ msdc_fifocs_reg msdc_fifocs; /* base+0x14h */ ++ msdc_txdat_reg msdc_txdat; /* base+0x18h */ ++ msdc_rxdat_reg msdc_rxdat; /* base+0x1ch */ ++ u32 rsv1[4]; ++ sdc_cfg_reg sdc_cfg; /* base+0x30h */ ++ sdc_cmd_reg sdc_cmd; /* base+0x34h */ ++ sdc_arg_reg sdc_arg; /* base+0x38h */ ++ sdc_sts_reg sdc_sts; /* base+0x3ch */ ++ sdc_resp0_reg sdc_resp0; /* base+0x40h */ ++ sdc_resp1_reg sdc_resp1; /* base+0x44h */ ++ sdc_resp2_reg sdc_resp2; /* base+0x48h */ ++ sdc_resp3_reg sdc_resp3; /* base+0x4ch */ ++ sdc_blknum_reg sdc_blknum; /* base+0x50h */ ++ u32 rsv2[1]; ++ sdc_csts_reg sdc_csts; /* base+0x58h */ ++ sdc_cstsen_reg sdc_cstsen; /* base+0x5ch */ ++ sdc_datcrcsts_reg sdc_dcrcsta; /* base+0x60h */ ++ u32 rsv3[3]; ++ emmc_cfg0_reg emmc_cfg0; /* base+0x70h */ ++ emmc_cfg1_reg emmc_cfg1; /* base+0x74h */ ++ emmc_sts_reg emmc_sts; /* base+0x78h */ ++ emmc_iocon_reg emmc_iocon; /* base+0x7ch */ ++ msdc_acmd_resp_reg acmd_resp; /* base+0x80h */ ++ msdc_acmd19_trg_reg acmd19_trg; /* base+0x84h */ ++ msdc_acmd19_sts_reg acmd19_sts; /* base+0x88h */ ++ u32 rsv4[1]; ++ msdc_dma_sa_reg dma_sa; /* base+0x90h */ ++ msdc_dma_ca_reg dma_ca; /* base+0x94h */ ++ msdc_dma_ctrl_reg dma_ctrl; /* base+0x98h */ ++ msdc_dma_cfg_reg dma_cfg; /* base+0x9ch */ ++ msdc_dbg_sel_reg dbg_sel; /* base+0xa0h */ ++ msdc_dbg_out_reg dbg_out; /* base+0xa4h */ ++ u32 rsv5[2]; ++ u32 patch0; /* base+0xb0h */ ++ u32 patch1; /* base+0xb4h */ ++ u32 rsv6[10]; ++ msdc_pad_ctl0_reg pad_ctl0; /* base+0xe0h */ ++ msdc_pad_ctl1_reg pad_ctl1; /* base+0xe4h */ ++ msdc_pad_ctl2_reg pad_ctl2; /* base+0xe8h */ ++ msdc_pad_tune_reg pad_tune; /* base+0xech */ ++ msdc_dat_rddly0 dat_rddly0; /* base+0xf0h */ ++ msdc_dat_rddly1 dat_rddly1; /* base+0xf4h */ ++ msdc_hw_dbg_reg hw_dbg; /* base+0xf8h */ ++ u32 rsv7[1]; ++ msdc_version_reg version; /* base+0x100h */ ++ msdc_eco_ver_reg eco_ver; /* base+0x104h */ ++}; ++ ++struct scatterlist_ex { ++ u32 cmd; ++ u32 arg; ++ u32 sglen; ++ struct scatterlist *sg; ++}; ++ ++#define DMA_FLAG_NONE (0x00000000) ++#define DMA_FLAG_EN_CHKSUM (0x00000001) ++#define DMA_FLAG_PAD_BLOCK (0x00000002) ++#define DMA_FLAG_PAD_DWORD (0x00000004) ++ ++struct msdc_dma { ++ u32 flags; /* flags */ ++ u32 xfersz; /* xfer size in bytes */ ++ u32 sglen; /* size of scatter list */ ++ u32 blklen; /* block size */ ++ struct scatterlist *sg; /* I/O scatter list */ ++ struct scatterlist_ex *esg; /* extended I/O scatter list */ ++ u8 mode; /* dma mode */ ++ u8 burstsz; /* burst size */ ++ u8 intr; /* dma done interrupt */ ++ u8 padding; /* padding */ ++ u32 cmd; /* enhanced mode command */ ++ u32 arg; /* enhanced mode arg */ ++ u32 rsp; /* enhanced mode command response */ ++ u32 autorsp; /* auto command response */ ++ ++ gpd_t *gpd; /* pointer to gpd array */ ++ bd_t *bd; /* pointer to bd array */ ++ dma_addr_t gpd_addr; /* the physical address of gpd array */ ++ dma_addr_t bd_addr; /* the physical address of bd array */ ++ u32 used_gpd; /* the number of used gpd elements */ ++ u32 used_bd; /* the number of used bd elements */ ++}; ++ ++struct msdc_host ++{ ++ struct msdc_hw *hw; ++ ++ struct mmc_host *mmc; /* mmc structure */ ++ struct mmc_command *cmd; ++ struct mmc_data *data; ++ struct mmc_request *mrq; ++ int cmd_rsp; ++ int cmd_rsp_done; ++ int cmd_r1b_done; ++ ++ int error; ++ spinlock_t lock; /* mutex */ ++ struct semaphore sem; ++ ++ u32 blksz; /* host block size */ ++ u32 base; /* host base address */ ++ int id; /* host id */ ++ int pwr_ref; /* core power reference count */ ++ ++ u32 xfer_size; /* total transferred size */ ++ ++ struct msdc_dma dma; /* dma channel */ ++ u32 dma_addr; /* dma transfer address */ ++ u32 dma_left_size; /* dma transfer left size */ ++ u32 dma_xfer_size; /* dma transfer size in bytes */ ++ int dma_xfer; /* dma transfer mode */ ++ ++ u32 timeout_ns; /* data timeout ns */ ++ u32 timeout_clks; /* data timeout clks */ ++ ++ atomic_t abort; /* abort transfer */ ++ ++ int irq; /* host interrupt */ ++ ++ struct tasklet_struct card_tasklet; ++ ++ struct completion cmd_done; ++ struct completion xfer_done; ++ struct pm_message pm_state; ++ ++ u32 mclk; /* mmc subsystem clock */ ++ u32 hclk; /* host clock speed */ ++ u32 sclk; /* SD/MS clock speed */ ++ u8 core_clkon; /* Host core clock on ? */ ++ u8 card_clkon; /* Card clock on ? */ ++ u8 core_power; /* core power */ ++ u8 power_mode; /* host power mode */ ++ u8 card_inserted; /* card inserted ? */ ++ u8 suspend; /* host suspended ? */ ++ u8 reserved; ++ u8 app_cmd; /* for app command */ ++ u32 app_cmd_arg; ++ u64 starttime; ++}; ++ ++static inline unsigned int uffs(unsigned int x) ++{ ++ unsigned int r = 1; ++ ++ if (!x) ++ return 0; ++ if (!(x & 0xffff)) { ++ x >>= 16; ++ r += 16; ++ } ++ if (!(x & 0xff)) { ++ x >>= 8; ++ r += 8; ++ } ++ if (!(x & 0xf)) { ++ x >>= 4; ++ r += 4; ++ } ++ if (!(x & 3)) { ++ x >>= 2; ++ r += 2; ++ } ++ if (!(x & 1)) { ++ x >>= 1; ++ r += 1; ++ } ++ return r; ++} ++#define sdr_read8(reg) __raw_readb(reg) ++#define sdr_read16(reg) __raw_readw(reg) ++#define sdr_read32(reg) __raw_readl(reg) ++#define sdr_write8(reg,val) __raw_writeb(val,reg) ++#define sdr_write16(reg,val) __raw_writew(val,reg) ++#define sdr_write32(reg,val) __raw_writel(val,reg) ++ ++#define sdr_set_bits(reg,bs) ((*(volatile u32*)(reg)) |= (u32)(bs)) ++#define sdr_clr_bits(reg,bs) ((*(volatile u32*)(reg)) &= ~((u32)(bs))) ++ ++#define sdr_set_field(reg,field,val) \ ++ do { \ ++ volatile unsigned int tv = sdr_read32(reg); \ ++ tv &= ~(field); \ ++ tv |= ((val) << (uffs((unsigned int)field) - 1)); \ ++ sdr_write32(reg,tv); \ ++ } while(0) ++#define sdr_get_field(reg,field,val) \ ++ do { \ ++ volatile unsigned int tv = sdr_read32(reg); \ ++ val = ((tv & (field)) >> (uffs((unsigned int)field) - 1)); \ ++ } while(0) ++ ++#endif ++ +diff --git a/drivers/mmc/host/sdhci-mt7620.c b/drivers/mmc/host/sdhci-mt7620.c +new file mode 100644 +index 0000000..a3cb5e4 +--- /dev/null ++++ b/drivers/mmc/host/sdhci-mt7620.c +@@ -0,0 +1,2314 @@ ++/* Copyright Statement: ++ * ++ * This software/firmware and related documentation ("MediaTek Software") are ++ * protected under relevant copyright laws. The information contained herein ++ * is confidential and proprietary to MediaTek Inc. and/or its licensors. ++ * Without the prior written permission of MediaTek inc. and/or its licensors, ++ * any reproduction, modification, use or disclosure of MediaTek Software, ++ * and information contained herein, in whole or in part, shall be strictly prohibited. ++ * ++ * MediaTek Inc. (C) 2010. All rights reserved. ++ * ++ * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES ++ * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") ++ * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON ++ * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. ++ * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE ++ * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR ++ * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH ++ * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES ++ * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES ++ * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK ++ * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR ++ * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND ++ * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE, ++ * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE, ++ * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO ++ * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. ++ * ++ * The following software/firmware and/or related documentation ("MediaTek Software") ++ * have been modified by MediaTek Inc. All revisions are subject to any receiver's ++ * applicable license agreements with MediaTek Inc. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define MSDC_SMPL_FALLING (1) ++#define MSDC_CD_PIN_EN (1 << 0) /* card detection pin is wired */ ++#define MSDC_WP_PIN_EN (1 << 1) /* write protection pin is wired */ ++#define MSDC_REMOVABLE (1 << 5) /* removable slot */ ++#define MSDC_SYS_SUSPEND (1 << 6) /* suspended by system */ ++#define MSDC_HIGHSPEED (1 << 7) ++ ++#define IRQ_SDC 22 ++ ++#include ++ ++#include "mt6575_sd.h" ++ ++#define DRV_NAME "mtk-sd" ++ ++#define HOST_MAX_NUM (1) /* +/- by chhung */ ++ ++#define HOST_MAX_MCLK (48000000) /* +/- by chhung */ ++#define HOST_MIN_MCLK (260000) ++ ++#define HOST_MAX_BLKSZ (2048) ++ ++#define MSDC_OCR_AVAIL (MMC_VDD_28_29 | MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33) ++ ++#define GPIO_PULL_DOWN (0) ++#define GPIO_PULL_UP (1) ++ ++#define DEFAULT_DEBOUNCE (8) /* 8 cycles */ ++#define DEFAULT_DTOC (40) /* data timeout counter. 65536x40 sclk. */ ++ ++#define CMD_TIMEOUT (HZ/10) /* 100ms */ ++#define DAT_TIMEOUT (HZ/2 * 5) /* 500ms x5 */ ++ ++#define MAX_DMA_CNT (64 * 1024 - 512) /* a single transaction for WIFI may be 50K*/ ++ ++#define MAX_GPD_NUM (1 + 1) /* one null gpd */ ++#define MAX_BD_NUM (1024) ++#define MAX_BD_PER_GPD (MAX_BD_NUM) ++ ++#define MAX_HW_SGMTS (MAX_BD_NUM) ++#define MAX_PHY_SGMTS (MAX_BD_NUM) ++#define MAX_SGMT_SZ (MAX_DMA_CNT) ++#define MAX_REQ_SZ (MAX_SGMT_SZ * 8) ++ ++#ifdef MT6575_SD_DEBUG ++static struct msdc_regs *msdc_reg[HOST_MAX_NUM]; ++#endif ++ ++//================================= ++#define PERI_MSDC0_PDN (15) ++//#define PERI_MSDC1_PDN (16) ++//#define PERI_MSDC2_PDN (17) ++//#define PERI_MSDC3_PDN (18) ++ ++struct msdc_host *msdc_6575_host[] = {NULL,NULL,NULL,NULL}; ++ ++struct msdc_hw msdc0_hw = { ++ .clk_src = 0, ++ .cmd_edge = MSDC_SMPL_FALLING, ++ .data_edge = MSDC_SMPL_FALLING, ++ .clk_drv = 4, ++ .cmd_drv = 4, ++ .dat_drv = 4, ++ .data_pins = 4, ++ .data_offset = 0, ++ .flags = MSDC_SYS_SUSPEND | MSDC_WP_PIN_EN | MSDC_CD_PIN_EN | MSDC_REMOVABLE | MSDC_HIGHSPEED, ++}; ++ ++static struct resource mtk_sd_resources[] = { ++ [0] = { ++ .start = 0xb0130000, ++ .end = 0xb0133fff, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_SDC, /*FIXME*/ ++ .end = IRQ_SDC, /*FIXME*/ ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct platform_device mtk_sd_device = { ++ .name = "mtk-sd", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(mtk_sd_resources), ++ .resource = mtk_sd_resources, ++}; ++/* end of +++ */ ++ ++static int msdc_rsp[] = { ++ 0, /* RESP_NONE */ ++ 1, /* RESP_R1 */ ++ 2, /* RESP_R2 */ ++ 3, /* RESP_R3 */ ++ 4, /* RESP_R4 */ ++ 1, /* RESP_R5 */ ++ 1, /* RESP_R6 */ ++ 1, /* RESP_R7 */ ++ 7, /* RESP_R1b */ ++}; ++ ++/* For Inhanced DMA */ ++#define msdc_init_gpd_ex(gpd,extlen,cmd,arg,blknum) \ ++ do { \ ++ ((gpd_t*)gpd)->extlen = extlen; \ ++ ((gpd_t*)gpd)->cmd = cmd; \ ++ ((gpd_t*)gpd)->arg = arg; \ ++ ((gpd_t*)gpd)->blknum = blknum; \ ++ }while(0) ++ ++#define msdc_init_bd(bd, blkpad, dwpad, dptr, dlen) \ ++ do { \ ++ BUG_ON(dlen > 0xFFFFUL); \ ++ ((bd_t*)bd)->blkpad = blkpad; \ ++ ((bd_t*)bd)->dwpad = dwpad; \ ++ ((bd_t*)bd)->ptr = (void*)dptr; \ ++ ((bd_t*)bd)->buflen = dlen; \ ++ }while(0) ++ ++#define msdc_txfifocnt() ((sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_TXCNT) >> 16) ++#define msdc_rxfifocnt() ((sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_RXCNT) >> 0) ++#define msdc_fifo_write32(v) sdr_write32(MSDC_TXDATA, (v)) ++#define msdc_fifo_write8(v) sdr_write8(MSDC_TXDATA, (v)) ++#define msdc_fifo_read32() sdr_read32(MSDC_RXDATA) ++#define msdc_fifo_read8() sdr_read8(MSDC_RXDATA) ++ ++ ++#define msdc_dma_on() sdr_clr_bits(MSDC_CFG, MSDC_CFG_PIO) ++#define msdc_dma_off() sdr_set_bits(MSDC_CFG, MSDC_CFG_PIO) ++ ++#define msdc_retry(expr,retry,cnt) \ ++ do { \ ++ int backup = cnt; \ ++ while (retry) { \ ++ if (!(expr)) break; \ ++ if (cnt-- == 0) { \ ++ retry--; mdelay(1); cnt = backup; \ ++ } \ ++ } \ ++ WARN_ON(retry == 0); \ ++ } while(0) ++ ++#if 0 /* +/- chhung */ ++#define msdc_reset() \ ++ do { \ ++ int retry = 3, cnt = 1000; \ ++ sdr_set_bits(MSDC_CFG, MSDC_CFG_RST); \ ++ dsb(); \ ++ msdc_retry(sdr_read32(MSDC_CFG) & MSDC_CFG_RST, retry, cnt); \ ++ } while(0) ++#else ++#define msdc_reset() \ ++ do { \ ++ int retry = 3, cnt = 1000; \ ++ sdr_set_bits(MSDC_CFG, MSDC_CFG_RST); \ ++ msdc_retry(sdr_read32(MSDC_CFG) & MSDC_CFG_RST, retry, cnt); \ ++ } while(0) ++#endif /* end of +/- */ ++ ++#define msdc_clr_int() \ ++ do { \ ++ volatile u32 val = sdr_read32(MSDC_INT); \ ++ sdr_write32(MSDC_INT, val); \ ++ } while(0) ++ ++#define msdc_clr_fifo() \ ++ do { \ ++ int retry = 3, cnt = 1000; \ ++ sdr_set_bits(MSDC_FIFOCS, MSDC_FIFOCS_CLR); \ ++ msdc_retry(sdr_read32(MSDC_FIFOCS) & MSDC_FIFOCS_CLR, retry, cnt); \ ++ } while(0) ++ ++#define msdc_irq_save(val) \ ++ do { \ ++ val = sdr_read32(MSDC_INTEN); \ ++ sdr_clr_bits(MSDC_INTEN, val); \ ++ } while(0) ++ ++#define msdc_irq_restore(val) \ ++ do { \ ++ sdr_set_bits(MSDC_INTEN, val); \ ++ } while(0) ++ ++/* clock source for host: global */ ++static u32 hclks[] = {48000000}; /* +/- by chhung */ ++ ++//============================================ ++// the power for msdc host controller: global ++// always keep the VMC on. ++//============================================ ++#define msdc_vcore_on(host) \ ++ do { \ ++ printk("[+]VMC ref. count<%d>\n", ++host->pwr_ref); \ ++ (void)hwPowerOn(MT65XX_POWER_LDO_VMC, VOL_3300, "SD"); \ ++ } while (0) ++#define msdc_vcore_off(host) \ ++ do { \ ++ printk("[-]VMC ref. count<%d>\n", --host->pwr_ref); \ ++ (void)hwPowerDown(MT65XX_POWER_LDO_VMC, "SD"); \ ++ } while (0) ++ ++//==================================== ++// the vdd output for card: global ++// always keep the VMCH on. ++//==================================== ++#define msdc_vdd_on(host) \ ++ do { \ ++ (void)hwPowerOn(MT65XX_POWER_LDO_VMCH, VOL_3300, "SD"); \ ++ } while (0) ++#define msdc_vdd_off(host) \ ++ do { \ ++ (void)hwPowerDown(MT65XX_POWER_LDO_VMCH, "SD"); \ ++ } while (0) ++ ++#define sdc_is_busy() (sdr_read32(SDC_STS) & SDC_STS_SDCBUSY) ++#define sdc_is_cmd_busy() (sdr_read32(SDC_STS) & SDC_STS_CMDBUSY) ++ ++#define sdc_send_cmd(cmd,arg) \ ++ do { \ ++ sdr_write32(SDC_ARG, (arg)); \ ++ sdr_write32(SDC_CMD, (cmd)); \ ++ } while(0) ++ ++// can modify to read h/w register. ++//#define is_card_present(h) ((sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 0 : 1); ++#define is_card_present(h) (((struct msdc_host*)(h))->card_inserted) ++ ++/* +++ chhung */ ++#ifndef __ASSEMBLY__ ++#define PHYSADDR(a) (((unsigned long)(a)) & 0x1fffffff) ++#else ++#define PHYSADDR(a) ((a) & 0x1fffffff) ++#endif ++/* end of +++ */ ++static unsigned int msdc_do_command(struct msdc_host *host, ++ struct mmc_command *cmd, ++ int tune, ++ unsigned long timeout); ++ ++static int msdc_tune_cmdrsp(struct msdc_host*host,struct mmc_command *cmd); ++ ++#ifdef MT6575_SD_DEBUG ++static void msdc_dump_card_status(struct msdc_host *host, u32 status) ++{ ++ static char *state[] = { ++ "Idle", /* 0 */ ++ "Ready", /* 1 */ ++ "Ident", /* 2 */ ++ "Stby", /* 3 */ ++ "Tran", /* 4 */ ++ "Data", /* 5 */ ++ "Rcv", /* 6 */ ++ "Prg", /* 7 */ ++ "Dis", /* 8 */ ++ "Reserved", /* 9 */ ++ "Reserved", /* 10 */ ++ "Reserved", /* 11 */ ++ "Reserved", /* 12 */ ++ "Reserved", /* 13 */ ++ "Reserved", /* 14 */ ++ "I/O mode", /* 15 */ ++ }; ++ if (status & R1_OUT_OF_RANGE) ++ printk("[CARD_STATUS] Out of Range\n"); ++ if (status & R1_ADDRESS_ERROR) ++ printk("[CARD_STATUS] Address Error\n"); ++ if (status & R1_BLOCK_LEN_ERROR) ++ printk("[CARD_STATUS] Block Len Error\n"); ++ if (status & R1_ERASE_SEQ_ERROR) ++ printk("[CARD_STATUS] Erase Seq Error\n"); ++ if (status & R1_ERASE_PARAM) ++ printk("[CARD_STATUS] Erase Param\n"); ++ if (status & R1_WP_VIOLATION) ++ printk("[CARD_STATUS] WP Violation\n"); ++ if (status & R1_CARD_IS_LOCKED) ++ printk("[CARD_STATUS] Card is Locked\n"); ++ if (status & R1_LOCK_UNLOCK_FAILED) ++ printk("[CARD_STATUS] Lock/Unlock Failed\n"); ++ if (status & R1_COM_CRC_ERROR) ++ printk("[CARD_STATUS] Command CRC Error\n"); ++ if (status & R1_ILLEGAL_COMMAND) ++ printk("[CARD_STATUS] Illegal Command\n"); ++ if (status & R1_CARD_ECC_FAILED) ++ printk("[CARD_STATUS] Card ECC Failed\n"); ++ if (status & R1_CC_ERROR) ++ printk("[CARD_STATUS] CC Error\n"); ++ if (status & R1_ERROR) ++ printk("[CARD_STATUS] Error\n"); ++ if (status & R1_UNDERRUN) ++ printk("[CARD_STATUS] Underrun\n"); ++ if (status & R1_OVERRUN) ++ printk("[CARD_STATUS] Overrun\n"); ++ if (status & R1_CID_CSD_OVERWRITE) ++ printk("[CARD_STATUS] CID/CSD Overwrite\n"); ++ if (status & R1_WP_ERASE_SKIP) ++ printk("[CARD_STATUS] WP Eraser Skip\n"); ++ if (status & R1_CARD_ECC_DISABLED) ++ printk("[CARD_STATUS] Card ECC Disabled\n"); ++ if (status & R1_ERASE_RESET) ++ printk("[CARD_STATUS] Erase Reset\n"); ++ if (status & R1_READY_FOR_DATA) ++ printk("[CARD_STATUS] Ready for Data\n"); ++ if (status & R1_SWITCH_ERROR) ++ printk("[CARD_STATUS] Switch error\n"); ++ if (status & R1_APP_CMD) ++ printk("[CARD_STATUS] App Command\n"); ++ ++ printk("[CARD_STATUS] '%s' State\n", state[R1_CURRENT_STATE(status)]); ++} ++ ++static void msdc_dump_ocr_reg(struct msdc_host *host, u32 resp) ++{ ++ if (resp & (1 << 7)) ++ printk("[OCR] Low Voltage Range\n"); ++ if (resp & (1 << 15)) ++ printk("[OCR] 2.7-2.8 volt\n"); ++ if (resp & (1 << 16)) ++ printk("[OCR] 2.8-2.9 volt\n"); ++ if (resp & (1 << 17)) ++ printk("[OCR] 2.9-3.0 volt\n"); ++ if (resp & (1 << 18)) ++ printk("[OCR] 3.0-3.1 volt\n"); ++ if (resp & (1 << 19)) ++ printk("[OCR] 3.1-3.2 volt\n"); ++ if (resp & (1 << 20)) ++ printk("[OCR] 3.2-3.3 volt\n"); ++ if (resp & (1 << 21)) ++ printk("[OCR] 3.3-3.4 volt\n"); ++ if (resp & (1 << 22)) ++ printk("[OCR] 3.4-3.5 volt\n"); ++ if (resp & (1 << 23)) ++ printk("[OCR] 3.5-3.6 volt\n"); ++ if (resp & (1 << 24)) ++ printk("[OCR] Switching to 1.8V Accepted (S18A)\n"); ++ if (resp & (1 << 30)) ++ printk("[OCR] Card Capacity Status (CCS)\n"); ++ if (resp & (1 << 31)) ++ printk("[OCR] Card Power Up Status (Idle)\n"); ++ else ++ printk("[OCR] Card Power Up Status (Busy)\n"); ++} ++ ++static void msdc_dump_rca_resp(struct msdc_host *host, u32 resp) ++{ ++ u32 status = (((resp >> 15) & 0x1) << 23) | ++ (((resp >> 14) & 0x1) << 22) | ++ (((resp >> 13) & 0x1) << 19) | ++ (resp & 0x1fff); ++ ++ printk("[RCA] 0x%.4x\n", resp >> 16); ++ ++ msdc_dump_card_status(host, status); ++} ++ ++static void msdc_dump_io_resp(struct msdc_host *host, u32 resp) ++{ ++ u32 flags = (resp >> 8) & 0xFF; ++ char *state[] = {"DIS", "CMD", "TRN", "RFU"}; ++ ++ if (flags & (1 << 7)) ++ printk("[IO] COM_CRC_ERR\n"); ++ if (flags & (1 << 6)) ++ printk("[IO] Illgal command\n"); ++ if (flags & (1 << 3)) ++ printk("[IO] Error\n"); ++ if (flags & (1 << 2)) ++ printk("[IO] RFU\n"); ++ if (flags & (1 << 1)) ++ printk("[IO] Function number error\n"); ++ if (flags & (1 << 0)) ++ printk("[IO] Out of range\n"); ++ ++ printk("[IO] State: %s, Data:0x%x\n", state[(resp >> 12) & 0x3], resp & 0xFF); ++} ++#endif ++ ++static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks) ++{ ++ u32 base = host->base; ++ u32 timeout, clk_ns; ++ ++ host->timeout_ns = ns; ++ host->timeout_clks = clks; ++ ++ clk_ns = 1000000000UL / host->sclk; ++ timeout = ns / clk_ns + clks; ++ timeout = timeout >> 16; /* in 65536 sclk cycle unit */ ++ timeout = timeout > 1 ? timeout - 1 : 0; ++ timeout = timeout > 255 ? 255 : timeout; ++ ++ sdr_set_field(SDC_CFG, SDC_CFG_DTOC, timeout); ++ ++/* printk("Set read data timeout: %dns %dclks -> %d x 65536 cycles\n", ++ ns, clks, timeout + 1);*/ ++} ++ ++static void msdc_eirq_sdio(void *data) ++{ ++ struct msdc_host *host = (struct msdc_host *)data; ++ ++// printk("SDIO EINT\n"); ++ ++ mmc_signal_sdio_irq(host->mmc); ++} ++ ++static void msdc_eirq_cd(void *data) ++{ ++ struct msdc_host *host = (struct msdc_host *)data; ++ ++// printk("CD EINT\n"); ++ ++ tasklet_hi_schedule(&host->card_tasklet); ++} ++ ++static void msdc_tasklet_card(unsigned long arg) ++{ ++ struct msdc_host *host = (struct msdc_host *)arg; ++ struct msdc_hw *hw = host->hw; ++ u32 base = host->base; ++ u32 inserted; ++ u32 status = 0; ++ ++ spin_lock(&host->lock); ++ ++ if (hw->get_cd_status) { ++ inserted = hw->get_cd_status(); ++ } else { ++ status = sdr_read32(MSDC_PS); ++ inserted = (status & MSDC_PS_CDSTS) ? 0 : 1; ++ } ++ ++ host->card_inserted = inserted; ++ ++ if (!host->suspend) { ++ host->mmc->f_max = HOST_MAX_MCLK; ++ mmc_detect_change(host->mmc, msecs_to_jiffies(20)); ++ } ++ ++// printk("card found<%s>\n", inserted ? "inserted" : "removed"); ++ ++ spin_unlock(&host->lock); ++} ++ ++static void msdc_set_mclk(struct msdc_host *host, int ddr, unsigned int hz) ++{ ++ u32 base = host->base; ++ u32 hclk = host->hclk; ++ u32 mode, flags, div, sclk; ++ ++ if (!hz) { ++// printk("set mclk to 0!!!\n"); ++ msdc_reset(); ++ return; ++ } ++ ++ msdc_irq_save(flags); ++ ++ if (ddr) { ++ mode = 0x2; ++ if (hz >= (hclk >> 2)) { ++ div = 1; ++ sclk = hclk >> 2; ++ } else { ++ div = (hclk + ((hz << 2) - 1)) / (hz << 2); ++ sclk = (hclk >> 2) / div; ++ } ++ } else if (hz >= hclk) { ++ mode = 0x1; ++ div = 0; ++ sclk = hclk; ++ } else { ++ mode = 0x0; ++ if (hz >= (hclk >> 1)) { ++ div = 0; ++ sclk = hclk >> 1; ++ } else { ++ div = (hclk + ((hz << 2) - 1)) / (hz << 2); ++ sclk = (hclk >> 2) / div; ++ } ++ } ++ ++ sdr_set_field(MSDC_CFG, MSDC_CFG_CKMOD, mode); ++ sdr_set_field(MSDC_CFG, MSDC_CFG_CKDIV, div); ++ ++ while (!(sdr_read32(MSDC_CFG) & MSDC_CFG_CKSTB)); ++ ++ host->sclk = sclk; ++ host->mclk = hz; ++ msdc_set_timeout(host, host->timeout_ns, host->timeout_clks); ++ ++/* printk("!!! Set<%dKHz> Source<%dKHz> -> sclk<%dKHz>\n", ++ hz / 1000, hclk / 1000, sclk / 1000); ++*/ ++ msdc_irq_restore(flags); ++} ++ ++static void msdc_abort_data(struct msdc_host *host) ++{ ++ u32 base = host->base; ++ struct mmc_command *stop = host->mrq->stop; ++ ++// printk("Need to Abort. dma<%d>\n", host->dma_xfer); ++ ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ ++ if (stop) { ++// printk("stop when abort CMD<%d>\n", stop->opcode); ++ msdc_do_command(host, stop, 0, CMD_TIMEOUT); ++ } ++} ++ ++static unsigned int msdc_command_start(struct msdc_host *host, ++ struct mmc_command *cmd, int tune, unsigned long timeout) ++{ ++ u32 wints = MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO | ++ MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO | ++ MSDC_INT_ACMD19_DONE; ++ u32 base = host->base; ++ u32 opcode = cmd->opcode; ++ u32 rawcmd; ++ u32 resp; ++ unsigned long tmo; ++ ++ if (opcode == MMC_SEND_OP_COND || opcode == SD_APP_OP_COND) ++ resp = RESP_R3; ++ else if (opcode == MMC_SET_RELATIVE_ADDR || opcode == SD_SEND_RELATIVE_ADDR) ++ resp = (mmc_cmd_type(cmd) == MMC_CMD_BCR) ? RESP_R6 : RESP_R1; ++ else if (opcode == MMC_FAST_IO) ++ resp = RESP_R4; ++ else if (opcode == MMC_GO_IRQ_STATE) ++ resp = RESP_R5; ++ else if (opcode == MMC_SELECT_CARD) ++ resp = (cmd->arg != 0) ? RESP_R1B : RESP_NONE; ++ else if (opcode == SD_IO_RW_DIRECT || opcode == SD_IO_RW_EXTENDED) ++ resp = RESP_R1; ++ else if (opcode == SD_SEND_IF_COND && (mmc_cmd_type(cmd) == MMC_CMD_BCR)) ++ resp = RESP_R1; ++ else { ++ switch (mmc_resp_type(cmd)) { ++ case MMC_RSP_R1: ++ resp = RESP_R1; ++ break; ++ case MMC_RSP_R1B: ++ resp = RESP_R1B; ++ break; ++ case MMC_RSP_R2: ++ resp = RESP_R2; ++ break; ++ case MMC_RSP_R3: ++ resp = RESP_R3; ++ break; ++ case MMC_RSP_NONE: ++ default: ++ resp = RESP_NONE; ++ break; ++ } ++ } ++ ++ cmd->error = 0; ++ rawcmd = opcode | msdc_rsp[resp] << 7 | host->blksz << 16; ++ ++ if (opcode == MMC_READ_MULTIPLE_BLOCK) { ++ rawcmd |= (2 << 11); ++ } else if (opcode == MMC_READ_SINGLE_BLOCK) { ++ rawcmd |= (1 << 11); ++ } else if (opcode == MMC_WRITE_MULTIPLE_BLOCK) { ++ rawcmd |= ((2 << 11) | (1 << 13)); ++ } else if (opcode == MMC_WRITE_BLOCK) { ++ rawcmd |= ((1 << 11) | (1 << 13)); ++ } else if (opcode == SD_IO_RW_EXTENDED) { ++ if (cmd->data->flags & MMC_DATA_WRITE) ++ rawcmd |= (1 << 13); ++ if (cmd->data->blocks > 1) ++ rawcmd |= (2 << 11); ++ else ++ rawcmd |= (1 << 11); ++ } else if (opcode == SD_IO_RW_DIRECT && cmd->flags == (unsigned int)-1) { ++ rawcmd |= (1 << 14); ++ } else if ((opcode == SD_APP_SEND_SCR) || ++ (opcode == SD_APP_SEND_NUM_WR_BLKS) || ++ (opcode == SD_SWITCH && (mmc_cmd_type(cmd) == MMC_CMD_ADTC)) || ++ (opcode == SD_APP_SD_STATUS && (mmc_cmd_type(cmd) == MMC_CMD_ADTC)) || ++ (opcode == MMC_SEND_EXT_CSD && (mmc_cmd_type(cmd) == MMC_CMD_ADTC))) { ++ rawcmd |= (1 << 11); ++ } else if (opcode == MMC_STOP_TRANSMISSION) { ++ rawcmd |= (1 << 14); ++ rawcmd &= ~(0x0FFF << 16); ++ } ++ ++// printk("CMD<%d><0x%.8x> Arg<0x%.8x>\n", opcode , rawcmd, cmd->arg); ++ ++ tmo = jiffies + timeout; ++ ++ if (opcode == MMC_SEND_STATUS) { ++ for (;;) { ++ if (!sdc_is_cmd_busy()) ++ break; ++ ++ if (time_after(jiffies, tmo)) { ++ //printk("XXX cmd_busy timeout: before CMD<%d>\n", opcode); ++ cmd->error = (unsigned int)-ETIMEDOUT; ++ msdc_reset(); ++ goto end; ++ } ++ } ++ } else { ++ for (;;) { ++ if (!sdc_is_busy()) ++ break; ++ if (time_after(jiffies, tmo)) { ++ //printk("XXX sdc_busy timeout: before CMD<%d>\n", opcode); ++ cmd->error = (unsigned int)-ETIMEDOUT; ++ msdc_reset(); ++ goto end; ++ } ++ } ++ } ++ ++ //BUG_ON(in_interrupt()); ++ host->cmd = cmd; ++ host->cmd_rsp = resp; ++ init_completion(&host->cmd_done); ++ sdr_set_bits(MSDC_INTEN, wints); ++ sdc_send_cmd(rawcmd, cmd->arg); ++ ++end: ++ return cmd->error; ++} ++ ++static unsigned int msdc_command_resp(struct msdc_host *host, struct mmc_command *cmd, ++ int tune, unsigned long timeout) ++{ ++ u32 base = host->base; ++ //u32 opcode = cmd->opcode; ++ u32 resp; ++ u32 wints = MSDC_INT_CMDRDY | MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO | ++ MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO | ++ MSDC_INT_ACMD19_DONE; ++ ++ resp = host->cmd_rsp; ++ ++ BUG_ON(in_interrupt()); ++ spin_unlock(&host->lock); ++ if (!wait_for_completion_timeout(&host->cmd_done, 10*timeout)) { ++ //printk("XXX CMD<%d> wait_for_completion timeout ARG<0x%.8x>\n", opcode, cmd->arg); ++ cmd->error = (unsigned int)-ETIMEDOUT; ++ msdc_reset(); ++ } ++ spin_lock(&host->lock); ++ ++ sdr_clr_bits(MSDC_INTEN, wints); ++ host->cmd = NULL; ++ ++ if (!tune) ++ return cmd->error; ++ ++ /* memory card CRC */ ++ if (host->hw->flags & MSDC_REMOVABLE && cmd->error == (unsigned int)(-EIO) ) { ++ if (sdr_read32(SDC_CMD) & 0x1800) { ++ msdc_abort_data(host); ++ } else { ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ } ++ cmd->error = msdc_tune_cmdrsp(host,cmd); ++ } ++ ++ return cmd->error; ++} ++ ++static unsigned int msdc_do_command(struct msdc_host *host, struct mmc_command *cmd, ++ int tune, unsigned long timeout) ++{ ++ if (!msdc_command_start(host, cmd, tune, timeout)) ++ msdc_command_resp(host, cmd, tune, timeout); ++ ++ //printk(" return<%d> resp<0x%.8x>\n", cmd->error, cmd->resp[0]); ++ return cmd->error; ++} ++ ++static int msdc_pio_abort(struct msdc_host *host, struct mmc_data *data, unsigned long tmo) ++{ ++ u32 base = host->base; ++ int ret = 0; ++ ++ if (atomic_read(&host->abort)) ++ ret = 1; ++ ++ if (time_after(jiffies, tmo)) { ++ data->error = (unsigned int)-ETIMEDOUT; ++ //printk("XXX PIO Data Timeout: CMD<%d>\n", host->mrq->cmd->opcode); ++ ret = 1; ++ } ++ ++ if (ret) { ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ //printk("msdc pio find abort\n"); ++ } ++ ++ return ret; ++} ++ ++static int msdc_pio_read(struct msdc_host *host, struct mmc_data *data) ++{ ++ struct scatterlist *sg = data->sg; ++ u32 base = host->base; ++ u32 num = data->sg_len; ++ u32 *ptr; ++ u8 *u8ptr; ++ u32 left; ++ u32 count, size = 0; ++ u32 wints = MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR; ++ unsigned long tmo = jiffies + DAT_TIMEOUT; ++ ++ sdr_set_bits(MSDC_INTEN, wints); ++ while (num) { ++ left = sg_dma_len(sg); ++ ptr = sg_virt(sg); ++ while (left) { ++ if ((left >= MSDC_FIFO_THD) && (msdc_rxfifocnt() >= MSDC_FIFO_THD)) { ++ count = MSDC_FIFO_THD >> 2; ++ do { ++ *ptr++ = msdc_fifo_read32(); ++ } while (--count); ++ left -= MSDC_FIFO_THD; ++ } else if ((left < MSDC_FIFO_THD) && msdc_rxfifocnt() >= left) { ++ while (left > 3) { ++ *ptr++ = msdc_fifo_read32(); ++ left -= 4; ++ } ++ ++ u8ptr = (u8 *)ptr; ++ while(left) { ++ * u8ptr++ = msdc_fifo_read8(); ++ left--; ++ } ++ } ++ ++ if (msdc_pio_abort(host, data, tmo)) ++ goto end; ++ } ++ size += sg_dma_len(sg); ++ sg = sg_next(sg); num--; ++ } ++end: ++ data->bytes_xfered += size; ++ //printk(" PIO Read<%d>bytes\n", size); ++ ++ sdr_clr_bits(MSDC_INTEN, wints); ++ if(data->error) ++ printk("read pio data->error<%d> left<%d> size<%d>\n", data->error, left, size); ++ ++ return data->error; ++} ++ ++static int msdc_pio_write(struct msdc_host* host, struct mmc_data *data) ++{ ++ u32 base = host->base; ++ struct scatterlist *sg = data->sg; ++ u32 num = data->sg_len; ++ u32 *ptr; ++ u8 *u8ptr; ++ u32 left; ++ u32 count, size = 0; ++ u32 wints = MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR; ++ unsigned long tmo = jiffies + DAT_TIMEOUT; ++ ++ sdr_set_bits(MSDC_INTEN, wints); ++ while (num) { ++ left = sg_dma_len(sg); ++ ptr = sg_virt(sg); ++ ++ while (left) { ++ if (left >= MSDC_FIFO_SZ && msdc_txfifocnt() == 0) { ++ count = MSDC_FIFO_SZ >> 2; ++ do { ++ msdc_fifo_write32(*ptr); ptr++; ++ } while (--count); ++ left -= MSDC_FIFO_SZ; ++ } else if (left < MSDC_FIFO_SZ && msdc_txfifocnt() == 0) { ++ while (left > 3) { ++ msdc_fifo_write32(*ptr); ptr++; ++ left -= 4; ++ } ++ ++ u8ptr = (u8*)ptr; ++ while( left) { ++ msdc_fifo_write8(*u8ptr); ++ u8ptr++; ++ left--; ++ } ++ } ++ ++ if (msdc_pio_abort(host, data, tmo)) ++ goto end; ++ } ++ size += sg_dma_len(sg); ++ sg = sg_next(sg); num--; ++ } ++end: ++ data->bytes_xfered += size; ++ //printk(" PIO Write<%d>bytes\n", size); ++ if(data->error) ++ printk("write pio data->error<%d>\n", data->error); ++ ++ sdr_clr_bits(MSDC_INTEN, wints); ++ ++ return data->error; ++} ++ ++static void msdc_dma_start(struct msdc_host *host) ++{ ++ u32 base = host->base; ++ u32 wints = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR; ++ ++ sdr_set_bits(MSDC_INTEN, wints); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1); ++ ++ //printk("DMA start\n"); ++} ++ ++static void msdc_dma_stop(struct msdc_host *host) ++{ ++ u32 base = host->base; ++ u32 wints = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | MSDC_INTEN_DATCRCERR; ++ ++ //printk("DMA status: 0x%.8x\n",sdr_read32(MSDC_DMA_CFG)); ++ ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP, 1); ++ while (sdr_read32(MSDC_DMA_CFG) & MSDC_DMA_CFG_STS); ++ sdr_clr_bits(MSDC_INTEN, wints); /* Not just xfer_comp */ ++ ++ //printk("DMA stop\n"); ++} ++ ++static u8 msdc_dma_calcs(u8 *buf, u32 len) ++{ ++ u32 i, sum = 0; ++ ++ for (i = 0; i < len; i++) ++ sum += buf[i]; ++ ++ return 0xFF - (u8)sum; ++} ++ ++static int msdc_dma_config(struct msdc_host *host, struct msdc_dma *dma) ++{ ++ u32 base = host->base; ++ u32 sglen = dma->sglen; ++ u32 j, num, bdlen; ++ u8 blkpad, dwpad, chksum; ++ struct scatterlist *sg = dma->sg; ++ gpd_t *gpd; ++ bd_t *bd; ++ ++ switch (dma->mode) { ++ case MSDC_MODE_DMA_BASIC: ++ BUG_ON(dma->xfersz > 65535); ++ BUG_ON(dma->sglen != 1); ++ sdr_write32(MSDC_DMA_SA, PHYSADDR(sg_dma_address(sg))); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_LASTBUF, 1); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_XFERSZ, sg_dma_len(sg)); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_BRUSTSZ, dma->burstsz); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, 0); ++ break; ++ ++ case MSDC_MODE_DMA_DESC: ++ blkpad = (dma->flags & DMA_FLAG_PAD_BLOCK) ? 1 : 0; ++ dwpad = (dma->flags & DMA_FLAG_PAD_DWORD) ? 1 : 0; ++ chksum = (dma->flags & DMA_FLAG_EN_CHKSUM) ? 1 : 0; ++ ++ num = (sglen + MAX_BD_PER_GPD - 1) / MAX_BD_PER_GPD; ++ BUG_ON(num !=1 ); ++ ++ gpd = dma->gpd; ++ bd = dma->bd; ++ bdlen = sglen; ++ ++ gpd->hwo = 1; /* hw will clear it */ ++ gpd->bdp = 1; ++ gpd->chksum = 0; /* need to clear first. */ ++ gpd->chksum = (chksum ? msdc_dma_calcs((u8 *)gpd, 16) : 0); ++ ++ for (j = 0; j < bdlen; j++) { ++ msdc_init_bd(&bd[j], blkpad, dwpad, sg_dma_address(sg), sg_dma_len(sg)); ++ if( j == bdlen - 1) ++ bd[j].eol = 1; ++ else ++ bd[j].eol = 0; ++ bd[j].chksum = 0; /* checksume need to clear first */ ++ bd[j].chksum = (chksum ? msdc_dma_calcs((u8 *)(&bd[j]), 16) : 0); ++ sg++; ++ } ++ ++ dma->used_gpd += 2; ++ dma->used_bd += bdlen; ++ ++ sdr_set_field(MSDC_DMA_CFG, MSDC_DMA_CFG_DECSEN, chksum); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_BRUSTSZ, dma->burstsz); ++ sdr_set_field(MSDC_DMA_CTRL, MSDC_DMA_CTRL_MODE, 1); ++ sdr_write32(MSDC_DMA_SA, PHYSADDR((u32)dma->gpd_addr)); ++ break; ++ } ++ ++// printk("DMA_CTRL = 0x%x\n", sdr_read32(MSDC_DMA_CTRL)); ++// printk("DMA_CFG = 0x%x\n", sdr_read32(MSDC_DMA_CFG)); ++// printk("DMA_SA = 0x%x\n", sdr_read32(MSDC_DMA_SA)); ++ ++ return 0; ++} ++ ++static void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma, ++ struct scatterlist *sg, unsigned int sglen) ++{ ++ BUG_ON(sglen > MAX_BD_NUM); ++ ++ dma->sg = sg; ++ dma->flags = DMA_FLAG_EN_CHKSUM; ++ dma->sglen = sglen; ++ dma->xfersz = host->xfer_size; ++ dma->burstsz = MSDC_BRUST_64B; ++ ++ if (sglen == 1 && sg_dma_len(sg) <= MAX_DMA_CNT) ++ dma->mode = MSDC_MODE_DMA_BASIC; ++ else ++ dma->mode = MSDC_MODE_DMA_DESC; ++ ++// printk("DMA mode<%d> sglen<%d> xfersz<%d>\n", dma->mode, dma->sglen, dma->xfersz); ++ ++ msdc_dma_config(host, dma); ++} ++ ++static void msdc_set_blknum(struct msdc_host *host, u32 blknum) ++{ ++ u32 base = host->base; ++ ++ sdr_write32(SDC_BLK_NUM, blknum); ++} ++ ++static int msdc_do_request(struct mmc_host*mmc, struct mmc_request*mrq) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ struct mmc_command *cmd; ++ struct mmc_data *data; ++ u32 base = host->base; ++ unsigned int left=0; ++ int dma = 0, read = 1, dir = DMA_FROM_DEVICE, send_type=0; ++ ++#define SND_DAT 0 ++#define SND_CMD 1 ++ ++ BUG_ON(mmc == NULL); ++ BUG_ON(mrq == NULL); ++ ++ host->error = 0; ++ atomic_set(&host->abort, 0); ++ ++ cmd = mrq->cmd; ++ data = mrq->cmd->data; ++ ++ if (!data) { ++ send_type = SND_CMD; ++ if (msdc_do_command(host, cmd, 1, CMD_TIMEOUT) != 0) ++ goto done; ++ } else { ++ BUG_ON(data->blksz > HOST_MAX_BLKSZ); ++ send_type=SND_DAT; ++ ++ data->error = 0; ++ read = data->flags & MMC_DATA_READ ? 1 : 0; ++ host->data = data; ++ host->xfer_size = data->blocks * data->blksz; ++ host->blksz = data->blksz; ++ ++ host->dma_xfer = dma = ((host->xfer_size >= 512) ? 1 : 0); ++ ++ if (read) ++ if ((host->timeout_ns != data->timeout_ns) || ++ (host->timeout_clks != data->timeout_clks)) ++ msdc_set_timeout(host, data->timeout_ns, data->timeout_clks); ++ ++ msdc_set_blknum(host, data->blocks); ++ ++ if (dma) { ++ msdc_dma_on(); ++ init_completion(&host->xfer_done); ++ ++ if (msdc_command_start(host, cmd, 1, CMD_TIMEOUT) != 0) ++ goto done; ++ ++ dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; ++ dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, dir); ++ msdc_dma_setup(host, &host->dma, data->sg, data->sg_len); ++ ++ if (msdc_command_resp(host, cmd, 1, CMD_TIMEOUT) != 0) ++ goto done; ++ ++ msdc_dma_start(host); ++ ++ spin_unlock(&host->lock); ++ if (!wait_for_completion_timeout(&host->xfer_done, DAT_TIMEOUT)) { ++ /*printk("XXX CMD<%d> wait xfer_done<%d> timeout!!\n", cmd->opcode, data->blocks * data->blksz); ++ printk(" DMA_SA = 0x%x\n", sdr_read32(MSDC_DMA_SA)); ++ printk(" DMA_CA = 0x%x\n", sdr_read32(MSDC_DMA_CA)); ++ printk(" DMA_CTRL = 0x%x\n", sdr_read32(MSDC_DMA_CTRL)); ++ printk(" DMA_CFG = 0x%x\n", sdr_read32(MSDC_DMA_CFG));*/ ++ data->error = (unsigned int)-ETIMEDOUT; ++ ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ } ++ spin_lock(&host->lock); ++ msdc_dma_stop(host); ++ } else { ++ if (msdc_do_command(host, cmd, 1, CMD_TIMEOUT) != 0) ++ goto done; ++ ++ if (read) { ++ if (msdc_pio_read(host, data)) ++ goto done; ++ } else { ++ if (msdc_pio_write(host, data)) ++ goto done; ++ } ++ ++ if (!read) { ++ while (1) { ++ left = msdc_txfifocnt(); ++ if (left == 0) { ++ break; ++ } ++ if (msdc_pio_abort(host, data, jiffies + DAT_TIMEOUT)) { ++ break; ++ /* Fix me: what about if data error, when stop ? how to? */ ++ } ++ } ++ } else { ++ /* Fix me: read case: need to check CRC error */ ++ } ++ ++ /* For write case: SDCBUSY and Xfer_Comp will assert when DAT0 not busy. ++ For read case : SDCBUSY and Xfer_Comp will assert when last byte read out from FIFO. ++ */ ++ ++ /* try not to wait xfer_comp interrupt. ++ the next command will check SDC_BUSY. ++ SDC_BUSY means xfer_comp assert ++ */ ++ ++ } // PIO mode ++ ++ /* Last: stop transfer */ ++ if (data->stop){ ++ if (msdc_do_command(host, data->stop, 0, CMD_TIMEOUT) != 0) { ++ goto done; ++ } ++ } ++ } ++ ++done: ++ if (data != NULL) { ++ host->data = NULL; ++ host->dma_xfer = 0; ++ if (dma != 0) { ++ msdc_dma_off(); ++ host->dma.used_bd = 0; ++ host->dma.used_gpd = 0; ++ dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, dir); ++ } ++ host->blksz = 0; ++ ++ // printk("CMD<%d> data<%s %s> blksz<%d> block<%d> error<%d>",cmd->opcode, (dma? "dma":"pio\n"), ++ // (read ? "read ":"write") ,data->blksz, data->blocks, data->error); ++ } ++ ++ if (mrq->cmd->error) host->error = 0x001; ++ if (mrq->data && mrq->data->error) host->error |= 0x010; ++ if (mrq->stop && mrq->stop->error) host->error |= 0x100; ++ ++ //if (host->error) printk("host->error<%d>\n", host->error); ++ ++ return host->error; ++} ++ ++static int msdc_app_cmd(struct mmc_host *mmc, struct msdc_host *host) ++{ ++ struct mmc_command cmd; ++ struct mmc_request mrq; ++ u32 err; ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ cmd.opcode = MMC_APP_CMD; ++#if 0 /* bug: we meet mmc->card is null when ACMD6 */ ++ cmd.arg = mmc->card->rca << 16; ++#else ++ cmd.arg = host->app_cmd_arg; ++#endif ++ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC; ++ ++ memset(&mrq, 0, sizeof(struct mmc_request)); ++ mrq.cmd = &cmd; cmd.mrq = &mrq; ++ cmd.data = NULL; ++ ++ err = msdc_do_command(host, &cmd, 0, CMD_TIMEOUT); ++ return err; ++} ++ ++static int msdc_tune_cmdrsp(struct msdc_host*host, struct mmc_command *cmd) ++{ ++ int result = -1; ++ u32 base = host->base; ++ u32 rsmpl, cur_rsmpl, orig_rsmpl; ++ u32 rrdly, cur_rrdly = 0, orig_rrdly; ++ u32 skip = 1; ++ ++ /* ==== don't support 3.0 now ==== ++ 1: R_SMPL[1] ++ 2: PAD_CMD_RESP_RXDLY[26:22] ++ ==========================*/ ++ ++ // save the previous tune result ++ sdr_get_field(MSDC_IOCON, MSDC_IOCON_RSPL, orig_rsmpl); ++ sdr_get_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY, orig_rrdly); ++ ++ rrdly = 0; ++ do { ++ for (rsmpl = 0; rsmpl < 2; rsmpl++) { ++ /* Lv1: R_SMPL[1] */ ++ cur_rsmpl = (orig_rsmpl + rsmpl) % 2; ++ if (skip == 1) { ++ skip = 0; ++ continue; ++ } ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_RSPL, cur_rsmpl); ++ ++ if (host->app_cmd) { ++ result = msdc_app_cmd(host->mmc, host); ++ if (result) { ++ //printk("TUNE_CMD app_cmd<%d> failed: RESP_RXDLY<%d>,R_SMPL<%d>\n", ++ // host->mrq->cmd->opcode, cur_rrdly, cur_rsmpl); ++ continue; ++ } ++ } ++ result = msdc_do_command(host, cmd, 0, CMD_TIMEOUT); // not tune. ++ //printk("TUNE_CMD<%d> %s PAD_CMD_RESP_RXDLY[26:22]<%d> R_SMPL[1]<%d>\n", cmd->opcode, ++// (result == 0) ? "PASS" : "FAIL", cur_rrdly, cur_rsmpl); ++ ++ if (result == 0) { ++ return 0; ++ } ++ if (result != (unsigned int)(-EIO)) { ++ // printk("TUNE_CMD<%d> Error<%d> not -EIO\n", cmd->opcode, result); ++ return result; ++ } ++ ++ /* should be EIO */ ++ if (sdr_read32(SDC_CMD) & 0x1800) { /* check if has data phase */ ++ msdc_abort_data(host); ++ } ++ } ++ ++ /* Lv2: PAD_CMD_RESP_RXDLY[26:22] */ ++ cur_rrdly = (orig_rrdly + rrdly + 1) % 32; ++ sdr_set_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_CMDRRDLY, cur_rrdly); ++ }while (++rrdly < 32); ++ ++ return result; ++} ++ ++/* Support SD2.0 Only */ ++static int msdc_tune_bread(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ u32 base = host->base; ++ u32 ddr=0; ++ u32 dcrc = 0; ++ u32 rxdly, cur_rxdly0, cur_rxdly1; ++ u32 dsmpl, cur_dsmpl, orig_dsmpl; ++ u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3; ++ u32 cur_dat4, cur_dat5, cur_dat6, cur_dat7; ++ u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3; ++ u32 orig_dat4, orig_dat5, orig_dat6, orig_dat7; ++ int result = -1; ++ u32 skip = 1; ++ ++ sdr_get_field(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl); ++ ++ /* Tune Method 2. */ ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1); ++ ++ rxdly = 0; ++ do { ++ for (dsmpl = 0; dsmpl < 2; dsmpl++) { ++ cur_dsmpl = (orig_dsmpl + dsmpl) % 2; ++ if (skip == 1) { ++ skip = 0; ++ continue; ++ } ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl); ++ ++ if (host->app_cmd) { ++ result = msdc_app_cmd(host->mmc, host); ++ if (result) { ++ //printk("TUNE_BREAD app_cmd<%d> failed\n", host->mrq->cmd->opcode); ++ continue; ++ } ++ } ++ result = msdc_do_request(mmc,mrq); ++ ++ sdr_get_field(SDC_DCRC_STS, SDC_DCRC_STS_POS|SDC_DCRC_STS_NEG, dcrc); /* RO */ ++ if (!ddr) dcrc &= ~SDC_DCRC_STS_NEG; ++ //printk("TUNE_BREAD<%s> dcrc<0x%x> DATRDDLY0/1<0x%x><0x%x> dsmpl<0x%x>\n", ++ // (result == 0 && dcrc == 0) ? "PASS" : "FAIL", dcrc, ++ // sdr_read32(MSDC_DAT_RDDLY0), sdr_read32(MSDC_DAT_RDDLY1), cur_dsmpl); ++ ++ /* Fix me: result is 0, but dcrc is still exist */ ++ if (result == 0 && dcrc == 0) { ++ goto done; ++ } else { ++ /* there is a case: command timeout, and data phase not processed */ ++ if (mrq->data->error != 0 && mrq->data->error != (unsigned int)(-EIO)) { ++ //printk("TUNE_READ: result<0x%x> cmd_error<%d> data_error<%d>\n", ++ // result, mrq->cmd->error, mrq->data->error); ++ goto done; ++ } ++ } ++ } ++ ++ cur_rxdly0 = sdr_read32(MSDC_DAT_RDDLY0); ++ cur_rxdly1 = sdr_read32(MSDC_DAT_RDDLY1); ++ ++ /* E1 ECO. YD: Reverse */ ++ if (sdr_read32(MSDC_ECO_VER) >= 4) { ++ orig_dat0 = (cur_rxdly0 >> 24) & 0x1F; ++ orig_dat1 = (cur_rxdly0 >> 16) & 0x1F; ++ orig_dat2 = (cur_rxdly0 >> 8) & 0x1F; ++ orig_dat3 = (cur_rxdly0 >> 0) & 0x1F; ++ orig_dat4 = (cur_rxdly1 >> 24) & 0x1F; ++ orig_dat5 = (cur_rxdly1 >> 16) & 0x1F; ++ orig_dat6 = (cur_rxdly1 >> 8) & 0x1F; ++ orig_dat7 = (cur_rxdly1 >> 0) & 0x1F; ++ } else { ++ orig_dat0 = (cur_rxdly0 >> 0) & 0x1F; ++ orig_dat1 = (cur_rxdly0 >> 8) & 0x1F; ++ orig_dat2 = (cur_rxdly0 >> 16) & 0x1F; ++ orig_dat3 = (cur_rxdly0 >> 24) & 0x1F; ++ orig_dat4 = (cur_rxdly1 >> 0) & 0x1F; ++ orig_dat5 = (cur_rxdly1 >> 8) & 0x1F; ++ orig_dat6 = (cur_rxdly1 >> 16) & 0x1F; ++ orig_dat7 = (cur_rxdly1 >> 24) & 0x1F; ++ } ++ ++ if (ddr) { ++ cur_dat0 = (dcrc & (1 << 0) || dcrc & (1 << 8)) ? ((orig_dat0 + 1) % 32) : orig_dat0; ++ cur_dat1 = (dcrc & (1 << 1) || dcrc & (1 << 9)) ? ((orig_dat1 + 1) % 32) : orig_dat1; ++ cur_dat2 = (dcrc & (1 << 2) || dcrc & (1 << 10)) ? ((orig_dat2 + 1) % 32) : orig_dat2; ++ cur_dat3 = (dcrc & (1 << 3) || dcrc & (1 << 11)) ? ((orig_dat3 + 1) % 32) : orig_dat3; ++ } else { ++ cur_dat0 = (dcrc & (1 << 0)) ? ((orig_dat0 + 1) % 32) : orig_dat0; ++ cur_dat1 = (dcrc & (1 << 1)) ? ((orig_dat1 + 1) % 32) : orig_dat1; ++ cur_dat2 = (dcrc & (1 << 2)) ? ((orig_dat2 + 1) % 32) : orig_dat2; ++ cur_dat3 = (dcrc & (1 << 3)) ? ((orig_dat3 + 1) % 32) : orig_dat3; ++ } ++ cur_dat4 = (dcrc & (1 << 4)) ? ((orig_dat4 + 1) % 32) : orig_dat4; ++ cur_dat5 = (dcrc & (1 << 5)) ? ((orig_dat5 + 1) % 32) : orig_dat5; ++ cur_dat6 = (dcrc & (1 << 6)) ? ((orig_dat6 + 1) % 32) : orig_dat6; ++ cur_dat7 = (dcrc & (1 << 7)) ? ((orig_dat7 + 1) % 32) : orig_dat7; ++ ++ cur_rxdly0 = (cur_dat0 << 24) | (cur_dat1 << 16) | (cur_dat2 << 8) | (cur_dat3 << 0); ++ cur_rxdly1 = (cur_dat4 << 24) | (cur_dat5 << 16) | (cur_dat6 << 8) | (cur_dat7 << 0); ++ ++ sdr_write32(MSDC_DAT_RDDLY0, cur_rxdly0); ++ sdr_write32(MSDC_DAT_RDDLY1, cur_rxdly1); ++ ++ } while (++rxdly < 32); ++ ++done: ++ return result; ++} ++ ++static int msdc_tune_bwrite(struct mmc_host *mmc,struct mmc_request *mrq) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ u32 base = host->base; ++ ++ u32 wrrdly, cur_wrrdly = 0, orig_wrrdly; ++ u32 dsmpl, cur_dsmpl, orig_dsmpl; ++ u32 rxdly, cur_rxdly0; ++ u32 orig_dat0, orig_dat1, orig_dat2, orig_dat3; ++ u32 cur_dat0, cur_dat1, cur_dat2, cur_dat3; ++ int result = -1; ++ u32 skip = 1; ++ ++ // MSDC_IOCON_DDR50CKD need to check. [Fix me] ++ ++ sdr_get_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, orig_wrrdly); ++ sdr_get_field(MSDC_IOCON, MSDC_IOCON_DSPL, orig_dsmpl ); ++ ++ /* Tune Method 2. just DAT0 */ ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DDLSEL, 1); ++ cur_rxdly0 = sdr_read32(MSDC_DAT_RDDLY0); ++ ++ /* E1 ECO. YD: Reverse */ ++ if (sdr_read32(MSDC_ECO_VER) >= 4) { ++ orig_dat0 = (cur_rxdly0 >> 24) & 0x1F; ++ orig_dat1 = (cur_rxdly0 >> 16) & 0x1F; ++ orig_dat2 = (cur_rxdly0 >> 8) & 0x1F; ++ orig_dat3 = (cur_rxdly0 >> 0) & 0x1F; ++ } else { ++ orig_dat0 = (cur_rxdly0 >> 0) & 0x1F; ++ orig_dat1 = (cur_rxdly0 >> 8) & 0x1F; ++ orig_dat2 = (cur_rxdly0 >> 16) & 0x1F; ++ orig_dat3 = (cur_rxdly0 >> 24) & 0x1F; ++ } ++ ++ rxdly = 0; ++ do { ++ wrrdly = 0; ++ do { ++ for (dsmpl = 0; dsmpl < 2; dsmpl++) { ++ cur_dsmpl = (orig_dsmpl + dsmpl) % 2; ++ if (skip == 1) { ++ skip = 0; ++ continue; ++ } ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, cur_dsmpl); ++ ++ if (host->app_cmd) { ++ result = msdc_app_cmd(host->mmc, host); ++ if (result) { ++ //printk("TUNE_BWRITE app_cmd<%d> failed\n", host->mrq->cmd->opcode); ++ continue; ++ } ++ } ++ result = msdc_do_request(mmc,mrq); ++ ++ //printk("TUNE_BWRITE<%s> DSPL<%d> DATWRDLY<%d> MSDC_DAT_RDDLY0<0x%x>\n", ++ // result == 0 ? "PASS" : "FAIL", ++ // cur_dsmpl, cur_wrrdly, cur_rxdly0); ++ ++ if (result == 0) { ++ goto done; ++ } ++ else { ++ /* there is a case: command timeout, and data phase not processed */ ++ if (mrq->data->error != (unsigned int)(-EIO)) { ++ //printk("TUNE_READ: result<0x%x> cmd_error<%d> data_error<%d>\n", ++ // && result, mrq->cmd->error, mrq->data->error); ++ goto done; ++ } ++ } ++ } ++ cur_wrrdly = (orig_wrrdly + wrrdly + 1) % 32; ++ sdr_set_field(MSDC_PAD_TUNE, MSDC_PAD_TUNE_DATWRDLY, cur_wrrdly); ++ } while (++wrrdly < 32); ++ ++ cur_dat0 = (orig_dat0 + rxdly) % 32; /* only adjust bit-1 for crc */ ++ cur_dat1 = orig_dat1; ++ cur_dat2 = orig_dat2; ++ cur_dat3 = orig_dat3; ++ ++ cur_rxdly0 = (cur_dat0 << 24) | (cur_dat1 << 16) | (cur_dat2 << 8) | (cur_dat3 << 0); ++ sdr_write32(MSDC_DAT_RDDLY0, cur_rxdly0); ++ } while (++rxdly < 32); ++ ++done: ++ return result; ++} ++ ++static int msdc_get_card_status(struct mmc_host *mmc, struct msdc_host *host, u32 *status) ++{ ++ struct mmc_command cmd; ++ struct mmc_request mrq; ++ u32 err; ++ ++ memset(&cmd, 0, sizeof(struct mmc_command)); ++ cmd.opcode = MMC_SEND_STATUS; ++ if (mmc->card) { ++ cmd.arg = mmc->card->rca << 16; ++ } else { ++ //printk("cmd13 mmc card is null\n"); ++ cmd.arg = host->app_cmd_arg; ++ } ++ cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; ++ ++ memset(&mrq, 0, sizeof(struct mmc_request)); ++ mrq.cmd = &cmd; cmd.mrq = &mrq; ++ cmd.data = NULL; ++ ++ err = msdc_do_command(host, &cmd, 1, CMD_TIMEOUT); ++ ++ if (status) ++ *status = cmd.resp[0]; ++ ++ return err; ++} ++ ++static int msdc_check_busy(struct mmc_host *mmc, struct msdc_host *host) ++{ ++ u32 err = 0; ++ u32 status = 0; ++ ++ do { ++ err = msdc_get_card_status(mmc, host, &status); ++ if (err) ++ return err; ++ /* need cmd12? */ ++ //printk("cmd<13> resp<0x%x>\n", status); ++ } while (R1_CURRENT_STATE(status) == 7); ++ ++ return err; ++} ++ ++static int msdc_tune_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ struct mmc_command *cmd; ++ struct mmc_data *data; ++ int ret=0, read; ++ ++ cmd = mrq->cmd; ++ data = mrq->cmd->data; ++ ++ read = data->flags & MMC_DATA_READ ? 1 : 0; ++ ++ if (read) { ++ if (data->error == (unsigned int)(-EIO)) ++ ret = msdc_tune_bread(mmc,mrq); ++ } else { ++ ret = msdc_check_busy(mmc, host); ++ if (ret){ ++ //printk("XXX cmd13 wait program done failed\n"); ++ return ret; ++ } ++ /* CRC and TO */ ++ /* Fix me: don't care card status? */ ++ ret = msdc_tune_bwrite(mmc,mrq); ++ } ++ ++ return ret; ++} ++ ++static void msdc_ops_request(struct mmc_host *mmc,struct mmc_request *mrq) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ ++ if (host->mrq) { ++ //printk("XXX host->mrq<0x%.8x>\n", (int)host->mrq); ++ BUG(); ++ } ++ if (!is_card_present(host) || host->power_mode == MMC_POWER_OFF) { ++ //printk("cmd<%d> card<%d> power<%d>\n", mrq->cmd->opcode, is_card_present(host), host->power_mode); ++ mrq->cmd->error = (unsigned int)-ENOMEDIUM; ++ mrq->done(mrq); ++ return; ++ } ++ spin_lock(&host->lock); ++ ++ host->mrq = mrq; ++ ++ if (msdc_do_request(mmc,mrq)) ++ if(host->hw->flags & MSDC_REMOVABLE && mrq->data && mrq->data->error) ++ msdc_tune_request(mmc,mrq); ++ ++ if (mrq->cmd->opcode == MMC_APP_CMD) { ++ host->app_cmd = 1; ++ host->app_cmd_arg = mrq->cmd->arg; /* save the RCA */ ++ } else { ++ host->app_cmd = 0; ++ } ++ ++ host->mrq = NULL; ++ ++ spin_unlock(&host->lock); ++ ++ mmc_request_done(mmc, mrq); ++} ++ ++/* called by ops.set_ios */ ++static void msdc_set_buswidth(struct msdc_host *host, u32 width) ++{ ++ u32 base = host->base; ++ u32 val = sdr_read32(SDC_CFG); ++ ++ val &= ~SDC_CFG_BUSWIDTH; ++ ++ switch (width) { ++ default: ++ case MMC_BUS_WIDTH_1: ++ width = 1; ++ val |= (MSDC_BUS_1BITS << 16); ++ break; ++ case MMC_BUS_WIDTH_4: ++ val |= (MSDC_BUS_4BITS << 16); ++ break; ++ case MMC_BUS_WIDTH_8: ++ val |= (MSDC_BUS_8BITS << 16); ++ break; ++ } ++ ++ sdr_write32(SDC_CFG, val); ++ ++ //printk("Bus Width = %d\n", width); ++} ++ ++/* ops.set_ios */ ++static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ struct msdc_hw *hw=host->hw; ++ u32 base = host->base; ++ u32 ddr = 0; ++ ++#ifdef MT6575_SD_DEBUG ++ static char *vdd[] = { ++ "1.50v", "1.55v", "1.60v", "1.65v", "1.70v", "1.80v", "1.90v", ++ "2.00v", "2.10v", "2.20v", "2.30v", "2.40v", "2.50v", "2.60v", ++ "2.70v", "2.80v", "2.90v", "3.00v", "3.10v", "3.20v", "3.30v", ++ "3.40v", "3.50v", "3.60v" ++ }; ++ static char *power_mode[] = { ++ "OFF", "UP", "ON" ++ }; ++ static char *bus_mode[] = { ++ "UNKNOWN", "OPENDRAIN", "PUSHPULL" ++ }; ++ static char *timing[] = { ++ "LEGACY", "MMC_HS", "SD_HS" ++ }; ++ ++ /*printk("SET_IOS: CLK(%dkHz), BUS(%s), BW(%u), PWR(%s), VDD(%s), TIMING(%s)\n", ++ ios->clock / 1000, bus_mode[ios->bus_mode], ++ (ios->bus_width == MMC_BUS_WIDTH_4) ? 4 : 1, ++ power_mode[ios->power_mode], vdd[ios->vdd], timing[ios->timing]);*/ ++#endif ++ ++ msdc_set_buswidth(host, ios->bus_width); ++ ++ /* Power control ??? */ ++ switch (ios->power_mode) { ++ case MMC_POWER_OFF: ++ case MMC_POWER_UP: ++ // msdc_set_power_mode(host, ios->power_mode); /* --- by chhung */ ++ break; ++ case MMC_POWER_ON: ++ host->power_mode = MMC_POWER_ON; ++ break; ++ default: ++ break; ++ } ++ ++ /* Clock control */ ++ if (host->mclk != ios->clock) { ++ if(ios->clock > 25000000) { ++ //printk("SD data latch edge<%d>\n", hw->data_edge); ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_RSPL, hw->cmd_edge); ++ sdr_set_field(MSDC_IOCON, MSDC_IOCON_DSPL, hw->data_edge); ++ } else { ++ sdr_write32(MSDC_IOCON, 0x00000000); ++ sdr_write32(MSDC_DAT_RDDLY0, 0x10101010); // for MT7620 E2 and afterward ++ sdr_write32(MSDC_DAT_RDDLY1, 0x00000000); ++ sdr_write32(MSDC_PAD_TUNE, 0x84101010); // for MT7620 E2 and afterward ++ } ++ msdc_set_mclk(host, ddr, ios->clock); ++ } ++} ++ ++/* ops.get_ro */ ++static int msdc_ops_get_ro(struct mmc_host *mmc) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ u32 base = host->base; ++ unsigned long flags; ++ int ro = 0; ++ ++ if (host->hw->flags & MSDC_WP_PIN_EN) { /* set for card */ ++ spin_lock_irqsave(&host->lock, flags); ++ ro = (sdr_read32(MSDC_PS) >> 31); ++ spin_unlock_irqrestore(&host->lock, flags); ++ } ++ return ro; ++} ++ ++/* ops.get_cd */ ++static int msdc_ops_get_cd(struct mmc_host *mmc) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ u32 base = host->base; ++ unsigned long flags; ++ int present = 1; ++ ++ /* for sdio, MSDC_REMOVABLE not set, always return 1 */ ++ if (!(host->hw->flags & MSDC_REMOVABLE)) { ++ /* For sdio, read H/W always get<1>, but may timeout some times */ ++#if 1 ++ host->card_inserted = 1; ++ return 1; ++#else ++ host->card_inserted = (host->pm_state.event == PM_EVENT_USER_RESUME) ? 1 : 0; ++ printk("sdio ops_get_cd<%d>\n", host->card_inserted); ++ return host->card_inserted; ++#endif ++ } ++ ++ /* MSDC_CD_PIN_EN set for card */ ++ if (host->hw->flags & MSDC_CD_PIN_EN) { ++ spin_lock_irqsave(&host->lock, flags); ++#if 0 ++ present = host->card_inserted; /* why not read from H/W: Fix me*/ ++#else ++ present = (sdr_read32(MSDC_PS) & MSDC_PS_CDSTS) ? 0 : 1; ++ host->card_inserted = present; ++#endif ++ spin_unlock_irqrestore(&host->lock, flags); ++ } else { ++ present = 0; /* TODO? Check DAT3 pins for card detection */ ++ } ++ ++ //printk("ops_get_cd return<%d>\n", present); ++ return present; ++} ++ ++/* ops.enable_sdio_irq */ ++static void msdc_ops_enable_sdio_irq(struct mmc_host *mmc, int enable) ++{ ++ struct msdc_host *host = mmc_priv(mmc); ++ struct msdc_hw *hw = host->hw; ++ u32 base = host->base; ++ u32 tmp; ++ ++ if (hw->flags & MSDC_EXT_SDIO_IRQ) { /* yes for sdio */ ++ if (enable) { ++ hw->enable_sdio_eirq(); /* combo_sdio_enable_eirq */ ++ } else { ++ hw->disable_sdio_eirq(); /* combo_sdio_disable_eirq */ ++ } ++ } else { ++ //printk("XXX \n"); /* so never enter here */ ++ tmp = sdr_read32(SDC_CFG); ++ /* FIXME. Need to interrupt gap detection */ ++ if (enable) { ++ tmp |= (SDC_CFG_SDIOIDE | SDC_CFG_SDIOINTWKUP); ++ } else { ++ tmp &= ~(SDC_CFG_SDIOIDE | SDC_CFG_SDIOINTWKUP); ++ } ++ sdr_write32(SDC_CFG, tmp); ++ } ++} ++ ++static struct mmc_host_ops mt_msdc_ops = { ++ .request = msdc_ops_request, ++ .set_ios = msdc_ops_set_ios, ++ .get_ro = msdc_ops_get_ro, ++ .get_cd = msdc_ops_get_cd, ++ .enable_sdio_irq = msdc_ops_enable_sdio_irq, ++}; ++ ++/*--------------------------------------------------------------------------*/ ++/* interrupt handler */ ++/*--------------------------------------------------------------------------*/ ++static irqreturn_t msdc_irq(int irq, void *dev_id) ++{ ++ struct msdc_host *host = (struct msdc_host *)dev_id; ++ struct mmc_data *data = host->data; ++ struct mmc_command *cmd = host->cmd; ++ u32 base = host->base; ++ ++ u32 cmdsts = MSDC_INT_RSPCRCERR | MSDC_INT_CMDTMO | MSDC_INT_CMDRDY | ++ MSDC_INT_ACMDCRCERR | MSDC_INT_ACMDTMO | MSDC_INT_ACMDRDY | ++ MSDC_INT_ACMD19_DONE; ++ u32 datsts = MSDC_INT_DATCRCERR |MSDC_INT_DATTMO; ++ ++ u32 intsts = sdr_read32(MSDC_INT); ++ u32 inten = sdr_read32(MSDC_INTEN); inten &= intsts; ++ ++ sdr_write32(MSDC_INT, intsts); /* clear interrupts */ ++ /* MSG will cause fatal error */ ++ ++ /* card change interrupt */ ++ if (intsts & MSDC_INT_CDSC){ ++ //printk("MSDC_INT_CDSC irq<0x%.8x>\n", intsts); ++ tasklet_hi_schedule(&host->card_tasklet); ++ /* tuning when plug card ? */ ++ } ++ ++ /* sdio interrupt */ ++ if (intsts & MSDC_INT_SDIOIRQ){ ++ //printk("XXX MSDC_INT_SDIOIRQ\n"); /* seems not sdio irq */ ++ //mmc_signal_sdio_irq(host->mmc); ++ } ++ ++ /* transfer complete interrupt */ ++ if (data != NULL) { ++ if (inten & MSDC_INT_XFER_COMPL) { ++ data->bytes_xfered = host->dma.xfersz; ++ complete(&host->xfer_done); ++ } ++ ++ if (intsts & datsts) { ++ /* do basic reset, or stop command will sdc_busy */ ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ atomic_set(&host->abort, 1); /* For PIO mode exit */ ++ ++ if (intsts & MSDC_INT_DATTMO){ ++ //printk("XXX CMD<%d> MSDC_INT_DATTMO\n", host->mrq->cmd->opcode); ++ data->error = (unsigned int)-ETIMEDOUT; ++ } ++ else if (intsts & MSDC_INT_DATCRCERR){ ++ //printk("XXX CMD<%d> MSDC_INT_DATCRCERR, SDC_DCRC_STS<0x%x>\n", host->mrq->cmd->opcode, sdr_read32(SDC_DCRC_STS)); ++ data->error = (unsigned int)-EIO; ++ } ++ ++ //if(sdr_read32(MSDC_INTEN) & MSDC_INT_XFER_COMPL) { ++ if (host->dma_xfer) { ++ complete(&host->xfer_done); /* Read CRC come fast, XFER_COMPL not enabled */ ++ } /* PIO mode can't do complete, because not init */ ++ } ++ } ++ ++ /* command interrupts */ ++ if ((cmd != NULL) && (intsts & cmdsts)) { ++ if ((intsts & MSDC_INT_CMDRDY) || (intsts & MSDC_INT_ACMDRDY) || ++ (intsts & MSDC_INT_ACMD19_DONE)) { ++ u32 *rsp = &cmd->resp[0]; ++ ++ switch (host->cmd_rsp) { ++ case RESP_NONE: ++ break; ++ case RESP_R2: ++ *rsp++ = sdr_read32(SDC_RESP3); *rsp++ = sdr_read32(SDC_RESP2); ++ *rsp++ = sdr_read32(SDC_RESP1); *rsp++ = sdr_read32(SDC_RESP0); ++ break; ++ default: /* Response types 1, 3, 4, 5, 6, 7(1b) */ ++ if ((intsts & MSDC_INT_ACMDRDY) || (intsts & MSDC_INT_ACMD19_DONE)) { ++ *rsp = sdr_read32(SDC_ACMD_RESP); ++ } else { ++ *rsp = sdr_read32(SDC_RESP0); ++ } ++ break; ++ } ++ } else if ((intsts & MSDC_INT_RSPCRCERR) || (intsts & MSDC_INT_ACMDCRCERR)) { ++ if(intsts & MSDC_INT_ACMDCRCERR){ ++ //printk("XXX CMD<%d> MSDC_INT_ACMDCRCERR\n",cmd->opcode); ++ } ++ else { ++ //printk("XXX CMD<%d> MSDC_INT_RSPCRCERR\n",cmd->opcode); ++ } ++ cmd->error = (unsigned int)-EIO; ++ } else if ((intsts & MSDC_INT_CMDTMO) || (intsts & MSDC_INT_ACMDTMO)) { ++ if(intsts & MSDC_INT_ACMDTMO){ ++ //printk("XXX CMD<%d> MSDC_INT_ACMDTMO\n",cmd->opcode); ++ } ++ else { ++ //printk("XXX CMD<%d> MSDC_INT_CMDTMO\n",cmd->opcode); ++ } ++ cmd->error = (unsigned int)-ETIMEDOUT; ++ msdc_reset(); ++ msdc_clr_fifo(); ++ msdc_clr_int(); ++ } ++ complete(&host->cmd_done); ++ } ++ ++ /* mmc irq interrupts */ ++ if (intsts & MSDC_INT_MMCIRQ) { ++ //printk(KERN_INFO "msdc[%d] MMCIRQ: SDC_CSTS=0x%.8x\r\n", host->id, sdr_read32(SDC_CSTS)); ++ } ++ ++#ifdef MT6575_SD_DEBUG ++ { ++ msdc_int_reg *int_reg = (msdc_int_reg*)&intsts; ++ /*printk("IRQ_EVT(0x%x): MMCIRQ(%d) CDSC(%d), ACRDY(%d), ACTMO(%d), ACCRE(%d) AC19DN(%d)\n", ++ intsts, ++ int_reg->mmcirq, ++ int_reg->cdsc, ++ int_reg->atocmdrdy, ++ int_reg->atocmdtmo, ++ int_reg->atocmdcrc, ++ int_reg->atocmd19done); ++ printk("IRQ_EVT(0x%x): SDIO(%d) CMDRDY(%d), CMDTMO(%d), RSPCRC(%d), CSTA(%d)\n", ++ intsts, ++ int_reg->sdioirq, ++ int_reg->cmdrdy, ++ int_reg->cmdtmo, ++ int_reg->rspcrc, ++ int_reg->csta); ++ printk("IRQ_EVT(0x%x): XFCMP(%d) DXDONE(%d), DATTMO(%d), DATCRC(%d), DMAEMP(%d)\n", ++ intsts, ++ int_reg->xfercomp, ++ int_reg->dxferdone, ++ int_reg->dattmo, ++ int_reg->datcrc, ++ int_reg->dmaqempty);*/ ++ ++ } ++#endif ++ ++ return IRQ_HANDLED; ++} ++ ++/*--------------------------------------------------------------------------*/ ++/* platform_driver members */ ++/*--------------------------------------------------------------------------*/ ++/* called by msdc_drv_probe/remove */ ++static void msdc_enable_cd_irq(struct msdc_host *host, int enable) ++{ ++ struct msdc_hw *hw = host->hw; ++ u32 base = host->base; ++ ++ /* for sdio, not set */ ++ if ((hw->flags & MSDC_CD_PIN_EN) == 0) { ++ /* Pull down card detection pin since it is not avaiable */ ++ /* ++ if (hw->config_gpio_pin) ++ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_DOWN); ++ */ ++ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN); ++ sdr_clr_bits(MSDC_INTEN, MSDC_INTEN_CDSC); ++ sdr_clr_bits(SDC_CFG, SDC_CFG_INSWKUP); ++ return; ++ } ++ ++ //printk("CD IRQ Eanable(%d)\n", enable); ++ ++ if (enable) { ++ if (hw->enable_cd_eirq) { /* not set, never enter */ ++ hw->enable_cd_eirq(); ++ } else { ++ /* card detection circuit relies on the core power so that the core power ++ * shouldn't be turned off. Here adds a reference count to keep ++ * the core power alive. ++ */ ++ //msdc_vcore_on(host); //did in msdc_init_hw() ++ ++ if (hw->config_gpio_pin) /* NULL */ ++ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_UP); ++ ++ sdr_set_field(MSDC_PS, MSDC_PS_CDDEBOUNCE, DEFAULT_DEBOUNCE); ++ sdr_set_bits(MSDC_PS, MSDC_PS_CDEN); ++ sdr_set_bits(MSDC_INTEN, MSDC_INTEN_CDSC); ++ sdr_set_bits(SDC_CFG, SDC_CFG_INSWKUP); /* not in document! Fix me */ ++ } ++ } else { ++ if (hw->disable_cd_eirq) { ++ hw->disable_cd_eirq(); ++ } else { ++ if (hw->config_gpio_pin) /* NULL */ ++ hw->config_gpio_pin(MSDC_CD_PIN, GPIO_PULL_DOWN); ++ ++ sdr_clr_bits(SDC_CFG, SDC_CFG_INSWKUP); ++ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN); ++ sdr_clr_bits(MSDC_INTEN, MSDC_INTEN_CDSC); ++ ++ /* Here decreases a reference count to core power since card ++ * detection circuit is shutdown. ++ */ ++ //msdc_vcore_off(host); ++ } ++ } ++} ++ ++/* called by msdc_drv_probe */ ++static void msdc_init_hw(struct msdc_host *host) ++{ ++ u32 base = host->base; ++ struct msdc_hw *hw = host->hw; ++ ++#ifdef MT6575_SD_DEBUG ++ msdc_reg[host->id] = (struct msdc_regs *)host->base; ++#endif ++ ++ /* Power on */ ++#if 0 /* --- chhung */ ++ msdc_vcore_on(host); ++ msdc_pin_reset(host, MSDC_PIN_PULL_UP); ++ msdc_select_clksrc(host, hw->clk_src); ++ enable_clock(PERI_MSDC0_PDN + host->id, "SD"); ++ msdc_vdd_on(host); ++#endif /* end of --- */ ++ /* Configure to MMC/SD mode */ ++ sdr_set_field(MSDC_CFG, MSDC_CFG_MODE, MSDC_SDMMC); ++ ++ /* Reset */ ++ msdc_reset(); ++ msdc_clr_fifo(); ++ ++ /* Disable card detection */ ++ sdr_clr_bits(MSDC_PS, MSDC_PS_CDEN); ++ ++ /* Disable and clear all interrupts */ ++ sdr_clr_bits(MSDC_INTEN, sdr_read32(MSDC_INTEN)); ++ sdr_write32(MSDC_INT, sdr_read32(MSDC_INT)); ++ ++#if 1 ++ /* reset tuning parameter */ ++ sdr_write32(MSDC_PAD_CTL0, 0x00090000); ++ sdr_write32(MSDC_PAD_CTL1, 0x000A0000); ++ sdr_write32(MSDC_PAD_CTL2, 0x000A0000); ++ // sdr_write32(MSDC_PAD_TUNE, 0x00000000); ++ sdr_write32(MSDC_PAD_TUNE, 0x84101010); // for MT7620 E2 and afterward ++ // sdr_write32(MSDC_DAT_RDDLY0, 0x00000000); ++ sdr_write32(MSDC_DAT_RDDLY0, 0x10101010); // for MT7620 E2 and afterward ++ sdr_write32(MSDC_DAT_RDDLY1, 0x00000000); ++ sdr_write32(MSDC_IOCON, 0x00000000); ++#if 0 // use MT7620 default value: 0x403c004f ++ sdr_write32(MSDC_PATCH_BIT0, 0x003C000F); /* bit0 modified: Rx Data Clock Source: 1 -> 2.0*/ ++#endif ++ ++ if (sdr_read32(MSDC_ECO_VER) >= 4) { ++ if (host->id == 1) { ++ sdr_set_field(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_WRDAT_CRCS, 1); ++ sdr_set_field(MSDC_PATCH_BIT1, MSDC_PATCH_BIT1_CMD_RSP, 1); ++ ++ /* internal clock: latch read data */ ++ sdr_set_bits(MSDC_PATCH_BIT0, MSDC_PATCH_BIT_CKGEN_CK); ++ } ++ } ++#endif ++ ++ /* for safety, should clear SDC_CFG.SDIO_INT_DET_EN & set SDC_CFG.SDIO in ++ pre-loader,uboot,kernel drivers. and SDC_CFG.SDIO_INT_DET_EN will be only ++ set when kernel driver wants to use SDIO bus interrupt */ ++ /* Configure to enable SDIO mode. it's must otherwise sdio cmd5 failed */ ++ sdr_set_bits(SDC_CFG, SDC_CFG_SDIO); ++ ++ /* disable detect SDIO device interupt function */ ++ sdr_clr_bits(SDC_CFG, SDC_CFG_SDIOIDE); ++ ++ /* eneable SMT for glitch filter */ ++ sdr_set_bits(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKSMT); ++ sdr_set_bits(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDSMT); ++ sdr_set_bits(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATSMT); ++ ++#if 1 ++ /* set clk, cmd, dat pad driving */ ++ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVN, hw->clk_drv); ++ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVP, hw->clk_drv); ++ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVN, hw->cmd_drv); ++ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVP, hw->cmd_drv); ++ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVN, hw->dat_drv); ++ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVP, hw->dat_drv); ++#else ++ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVN, 0); ++ sdr_set_field(MSDC_PAD_CTL0, MSDC_PAD_CTL0_CLKDRVP, 0); ++ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVN, 0); ++ sdr_set_field(MSDC_PAD_CTL1, MSDC_PAD_CTL1_CMDDRVP, 0); ++ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVN, 0); ++ sdr_set_field(MSDC_PAD_CTL2, MSDC_PAD_CTL2_DATDRVP, 0); ++#endif ++ ++ /* set sampling edge */ ++ ++ /* write crc timeout detection */ ++ sdr_set_field(MSDC_PATCH_BIT0, 1 << 30, 1); ++ ++ /* Configure to default data timeout */ ++ sdr_set_field(SDC_CFG, SDC_CFG_DTOC, DEFAULT_DTOC); ++ ++ msdc_set_buswidth(host, MMC_BUS_WIDTH_1); ++ ++ //printk("init hardware done!\n"); ++} ++ ++/* called by msdc_drv_remove */ ++static void msdc_deinit_hw(struct msdc_host *host) ++{ ++ u32 base = host->base; ++ ++ /* Disable and clear all interrupts */ ++ sdr_clr_bits(MSDC_INTEN, sdr_read32(MSDC_INTEN)); ++ sdr_write32(MSDC_INT, sdr_read32(MSDC_INT)); ++ ++ /* Disable card detection */ ++ msdc_enable_cd_irq(host, 0); ++ // msdc_set_power_mode(host, MMC_POWER_OFF); /* make sure power down */ /* --- by chhung */ ++} ++ ++/* init gpd and bd list in msdc_drv_probe */ ++static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma) ++{ ++ gpd_t *gpd = dma->gpd; ++ bd_t *bd = dma->bd; ++ bd_t *ptr, *prev; ++ ++ /* we just support one gpd */ ++ int bdlen = MAX_BD_PER_GPD; ++ ++ /* init the 2 gpd */ ++ memset(gpd, 0, sizeof(gpd_t) * 2); ++ //gpd->next = (void *)virt_to_phys(gpd + 1); /* pointer to a null gpd, bug! kmalloc <-> virt_to_phys */ ++ //gpd->next = (dma->gpd_addr + 1); /* bug */ ++ gpd->next = (void *)((u32)dma->gpd_addr + sizeof(gpd_t)); ++ ++ //gpd->intr = 0; ++ gpd->bdp = 1; /* hwo, cs, bd pointer */ ++ //gpd->ptr = (void*)virt_to_phys(bd); ++ gpd->ptr = (void *)dma->bd_addr; /* physical address */ ++ ++ memset(bd, 0, sizeof(bd_t) * bdlen); ++ ptr = bd + bdlen - 1; ++ //ptr->eol = 1; /* 0 or 1 [Fix me]*/ ++ //ptr->next = 0; ++ ++ while (ptr != bd) { ++ prev = ptr - 1; ++ prev->next = (void *)(dma->bd_addr + sizeof(bd_t) *(ptr - bd)); ++ ptr = prev; ++ } ++} ++ ++static int msdc_drv_probe(struct platform_device *pdev) ++{ ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ __iomem void *base; ++ struct mmc_host *mmc; ++ struct resource *mem; ++ struct msdc_host *host; ++ struct msdc_hw *hw; ++ int ret, irq; ++ pdev->dev.platform_data = &msdc0_hw; ++ ++ /* Allocate MMC host for this device */ ++ mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev); ++ if (!mmc) return -ENOMEM; ++ ++ hw = (struct msdc_hw*)pdev->dev.platform_data; ++ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irq = platform_get_irq(pdev, 0); ++ ++ //BUG_ON((!hw) || (!mem) || (irq < 0)); /* --- by chhung */ ++ ++ base = devm_request_and_ioremap(&pdev->dev, res); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++/* mem = request_mem_region(mem->start - 0xa0000000, (mem->end - mem->start + 1) - 0xa0000000, dev_name(&pdev->dev)); ++ if (mem == NULL) { ++ mmc_free_host(mmc); ++ return -EBUSY; ++ } ++*/ ++ /* Set host parameters to mmc */ ++ mmc->ops = &mt_msdc_ops; ++ mmc->f_min = HOST_MIN_MCLK; ++ mmc->f_max = HOST_MAX_MCLK; ++ mmc->ocr_avail = MSDC_OCR_AVAIL; ++ ++ /* For sd card: MSDC_SYS_SUSPEND | MSDC_WP_PIN_EN | MSDC_CD_PIN_EN | MSDC_REMOVABLE | MSDC_HIGHSPEED, ++ For sdio : MSDC_EXT_SDIO_IRQ | MSDC_HIGHSPEED */ ++ if (hw->flags & MSDC_HIGHSPEED) { ++ mmc->caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; ++ } ++ if (hw->data_pins == 4) { /* current data_pins are all 4*/ ++ mmc->caps |= MMC_CAP_4_BIT_DATA; ++ } else if (hw->data_pins == 8) { ++ mmc->caps |= MMC_CAP_8_BIT_DATA; ++ } ++ if ((hw->flags & MSDC_SDIO_IRQ) || (hw->flags & MSDC_EXT_SDIO_IRQ)) ++ mmc->caps |= MMC_CAP_SDIO_IRQ; /* yes for sdio */ ++ ++ /* MMC core transfer sizes tunable parameters */ ++ // mmc->max_hw_segs = MAX_HW_SGMTS; ++// mmc->max_phys_segs = MAX_PHY_SGMTS; ++ mmc->max_seg_size = MAX_SGMT_SZ; ++ mmc->max_blk_size = HOST_MAX_BLKSZ; ++ mmc->max_req_size = MAX_REQ_SZ; ++ mmc->max_blk_count = mmc->max_req_size; ++ ++ host = mmc_priv(mmc); ++ host->hw = hw; ++ host->mmc = mmc; ++ host->id = pdev->id; ++ host->error = 0; ++ host->irq = irq; ++ host->base = (unsigned long) base; ++ host->mclk = 0; /* mclk: the request clock of mmc sub-system */ ++ host->hclk = hclks[hw->clk_src]; /* hclk: clock of clock source to msdc controller */ ++ host->sclk = 0; /* sclk: the really clock after divition */ ++ host->pm_state = PMSG_RESUME; ++ host->suspend = 0; ++ host->core_clkon = 0; ++ host->card_clkon = 0; ++ host->core_power = 0; ++ host->power_mode = MMC_POWER_OFF; ++// host->card_inserted = hw->flags & MSDC_REMOVABLE ? 0 : 1; ++ host->timeout_ns = 0; ++ host->timeout_clks = DEFAULT_DTOC * 65536; ++ ++ host->mrq = NULL; ++ //init_MUTEX(&host->sem); /* we don't need to support multiple threads access */ ++ ++ host->dma.used_gpd = 0; ++ host->dma.used_bd = 0; ++ ++ /* using dma_alloc_coherent*/ /* todo: using 1, for all 4 slots */ ++ host->dma.gpd = dma_alloc_coherent(NULL, MAX_GPD_NUM * sizeof(gpd_t), &host->dma.gpd_addr, GFP_KERNEL); ++ host->dma.bd = dma_alloc_coherent(NULL, MAX_BD_NUM * sizeof(bd_t), &host->dma.bd_addr, GFP_KERNEL); ++ BUG_ON((!host->dma.gpd) || (!host->dma.bd)); ++ msdc_init_gpd_bd(host, &host->dma); ++ /*for emmc*/ ++ msdc_6575_host[pdev->id] = host; ++ ++ tasklet_init(&host->card_tasklet, msdc_tasklet_card, (ulong)host); ++ spin_lock_init(&host->lock); ++ msdc_init_hw(host); ++ ++ ret = request_irq((unsigned int)irq, msdc_irq, IRQF_TRIGGER_LOW, dev_name(&pdev->dev), host); ++ if (ret) goto release; ++ // mt65xx_irq_unmask(irq); /* --- by chhung */ ++ ++ if (hw->flags & MSDC_CD_PIN_EN) { /* not set for sdio */ ++ if (hw->request_cd_eirq) { /* not set for MT6575 */ ++ hw->request_cd_eirq(msdc_eirq_cd, (void*)host); /* msdc_eirq_cd will not be used! */ ++ } ++ } ++ ++ if (hw->request_sdio_eirq) /* set to combo_sdio_request_eirq() for WIFI */ ++ hw->request_sdio_eirq(msdc_eirq_sdio, (void*)host); /* msdc_eirq_sdio() will be called when EIRQ */ ++ ++ if (hw->register_pm) {/* yes for sdio */ ++ if(hw->flags & MSDC_SYS_SUSPEND) { /* will not set for WIFI */ ++ //printk("MSDC_SYS_SUSPEND and register_pm both set\n"); ++ } ++ //mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY; /* pm not controlled by system but by client. */ /* --- by chhung */ ++ } ++ ++ platform_set_drvdata(pdev, mmc); ++ ++ ret = mmc_add_host(mmc); ++ if (ret) goto free_irq; ++ ++ /* Config card detection pin and enable interrupts */ ++ if (hw->flags & MSDC_CD_PIN_EN) { /* set for card */ ++ msdc_enable_cd_irq(host, 1); ++ } else { ++ msdc_enable_cd_irq(host, 0); ++ } ++ ++ return 0; ++ ++free_irq: ++ free_irq(irq, host); ++release: ++ platform_set_drvdata(pdev, NULL); ++ msdc_deinit_hw(host); ++ ++ tasklet_kill(&host->card_tasklet); ++ ++/* if (mem) ++ release_mem_region(mem->start, mem->end - mem->start + 1); ++*/ ++ mmc_free_host(mmc); ++ ++ return ret; ++} ++ ++/* 4 device share one driver, using "drvdata" to show difference */ ++static int msdc_drv_remove(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc; ++ struct msdc_host *host; ++ struct resource *mem; ++ ++ ++ mmc = platform_get_drvdata(pdev); ++ BUG_ON(!mmc); ++ ++ host = mmc_priv(mmc); ++ BUG_ON(!host); ++ ++ //printk("removed !!!\n"); ++ ++ platform_set_drvdata(pdev, NULL); ++ mmc_remove_host(host->mmc); ++ msdc_deinit_hw(host); ++ ++ tasklet_kill(&host->card_tasklet); ++ free_irq(host->irq, host); ++ ++ dma_free_coherent(NULL, MAX_GPD_NUM * sizeof(gpd_t), host->dma.gpd, host->dma.gpd_addr); ++ dma_free_coherent(NULL, MAX_BD_NUM * sizeof(bd_t), host->dma.bd, host->dma.bd_addr); ++ ++ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ if (mem) ++ release_mem_region(mem->start, mem->end - mem->start + 1); ++ ++ mmc_free_host(host->mmc); ++ ++ return 0; ++} ++ ++static const struct of_device_id mt7620a_sdhci_match[] = { ++ { .compatible = "ralink,mt7620a-sdhci" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt288x_wdt_match); ++ ++/* Fix me: Power Flow */ ++static struct platform_driver mt_msdc_driver = { ++ .probe = msdc_drv_probe, ++ .remove = msdc_drv_remove, ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ .of_match_table = mt7620a_sdhci_match, ++ ++ }, ++}; ++ ++static int __init mt_msdc_init(void) ++{ ++ int ret; ++/* +++ chhung */ ++ unsigned int reg; ++ ++ mtk_sd_device.dev.platform_data = &msdc0_hw; ++ printk("MTK MSDC device init.\n"); ++ reg = sdr_read32((__iomem void *) 0xb0000060) & ~(0x3<<18); ++ reg |= 0x1 << 18; ++ sdr_write32((__iomem void *) 0xb0000060, reg); ++/* end of +++ */ ++ ret = platform_driver_register(&mt_msdc_driver); ++ if (ret) { ++ printk(KERN_ERR DRV_NAME ": Can't register driver"); ++ return ret; ++ } ++ printk(KERN_INFO DRV_NAME ": MediaTek MT6575 MSDC Driver\n"); ++ ++ //msdc_debug_proc_init(); ++ return 0; ++} ++ ++static void __exit mt_msdc_exit(void) ++{ ++ platform_driver_unregister(&mt_msdc_driver); ++} ++ ++module_init(mt_msdc_init); ++module_exit(mt_msdc_exit); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MediaTek MT6575 SD/MMC Card Driver"); ++MODULE_AUTHOR("Infinity Chen "); ++ ++EXPORT_SYMBOL(msdc_6575_host); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0148-DMA-MIPS-ralink-add-dmaengine-driver.patch b/target/linux/ramips/patches-3.9/0148-DMA-MIPS-ralink-add-dmaengine-driver.patch new file mode 100644 index 0000000000..a56ba188f1 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0148-DMA-MIPS-ralink-add-dmaengine-driver.patch @@ -0,0 +1,341 @@ +From 27eef043b05fb8d9d3178fd3352c530f71901dd4 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 23 May 2013 18:45:29 +0200 +Subject: [PATCH 148/164] DMA: MIPS: ralink: add dmaengine driver + +Signed-off-by: John Crispin +--- + drivers/dma/Kconfig | 7 ++ + drivers/dma/Makefile | 1 + + drivers/dma/ralink_gdma.c | 229 +++++++++++++++++++++++++++++++++++++++++++++ + drivers/dma/ralink_gdma.h | 55 +++++++++++ + 4 files changed, 292 insertions(+) + create mode 100644 drivers/dma/ralink_gdma.c + create mode 100644 drivers/dma/ralink_gdma.h + +diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig +index aeaea32..751cdc4 100644 +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -322,6 +322,13 @@ config MMP_PDMA + help + Support the MMP PDMA engine for PXA and MMP platfrom. + ++config RALINK_GDMA ++ bool "Ralink Generic DMA support" ++ depends on RALINK ++ select DMA_ENGINE ++ help ++ Support the GDMA engine for MIPS based Ralink SoC. ++ + config DMA_ENGINE + bool + +diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile +index 488e3ff..f69e07e 100644 +--- a/drivers/dma/Makefile ++++ b/drivers/dma/Makefile +@@ -37,3 +37,4 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o + obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o + obj-$(CONFIG_DMA_OMAP) += omap-dma.o + obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o ++obj-$(CONFIG_RALINK_GDMA) += ralink_gdma.o +diff --git a/drivers/dma/ralink_gdma.c b/drivers/dma/ralink_gdma.c +new file mode 100644 +index 0000000..be7c317 +--- /dev/null ++++ b/drivers/dma/ralink_gdma.c +@@ -0,0 +1,229 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ralink_gdma.h" ++ ++#define SURFBOARDINT_DMA 10 ++#define MEMCPY_DMA_CH 8 ++#define to_rt2880_dma_chan(chan) \ ++ container_of(chan, struct rt2880_dma_chan, common) ++ ++static dma_cookie_t rt2880_dma_tx_submit(struct dma_async_tx_descriptor *tx) ++{ ++ dma_cookie_t cookie; ++ ++ cookie = tx->chan->cookie; ++ ++ return cookie; ++} ++ ++#define MIN_RTDMA_PKT_LEN 128 ++static struct dma_async_tx_descriptor * ++rt2880_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, ++ size_t len, unsigned long flags) ++{ ++ struct rt2880_dma_chan *rt_chan = to_rt2880_dma_chan(chan); ++ unsigned long mid_offset; ++ ++ spin_lock_bh(&rt_chan->lock); ++ ++ if(len < MIN_RTDMA_PKT_LEN) { ++ memcpy(phys_to_virt(dest), phys_to_virt(src), len); ++ } else { ++ mid_offset = len/2; ++ ++ /* Lower parts are transferred by GDMA. ++ * Upper parts are transferred by CPU. ++ */ ++ RT_DMA_WRITE_REG(RT_DMA_SRC_REG(MEMCPY_DMA_CH), src); ++ RT_DMA_WRITE_REG(RT_DMA_DST_REG(MEMCPY_DMA_CH), dest); ++ RT_DMA_WRITE_REG(RT_DMA_CTRL_REG(MEMCPY_DMA_CH), (mid_offset << 16) | (3 << 3) | (3 << 0)); ++ ++ memcpy(phys_to_virt(dest)+mid_offset, phys_to_virt(src)+mid_offset, len-mid_offset); ++ ++ dma_async_tx_descriptor_init(&rt_chan->txd, chan); ++ ++ while((RT_DMA_READ_REG(RT_DMA_DONEINT) & (0x1<lock); ++ ++ return &rt_chan->txd; ++} ++ ++/** ++ * rt2880_dma_status - poll the status of an XOR transaction ++ * @chan: XOR channel handle ++ * @cookie: XOR transaction identifier ++ * @txstate: XOR transactions state holder (or NULL) ++ */ ++static enum dma_status rt2880_dma_status(struct dma_chan *chan, ++ dma_cookie_t cookie, ++ struct dma_tx_state *txstate) ++{ ++ return 0; ++} ++ ++static irqreturn_t rt2880_dma_interrupt_handler(int irq, void *data) ++{ ++ ++ printk("%s\n",__FUNCTION__); ++ ++ return IRQ_HANDLED; ++} ++ ++static void rt2880_dma_issue_pending(struct dma_chan *chan) ++{ ++} ++ ++static int rt2880_dma_alloc_chan_resources(struct dma_chan *chan) ++{ ++// printk("%s\n",__FUNCTION__); ++ ++ return 0; ++} ++ ++static void rt2880_dma_free_chan_resources(struct dma_chan *chan) ++{ ++// printk("%s\n",__FUNCTION__); ++ ++} ++ ++static int rt2880_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case DMA_TERMINATE_ALL: ++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ break; ++ case DMA_SLAVE_CONFIG: ++ printk("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ break; ++ default: ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static int rt2880_dma_probe(struct platform_device *pdev) ++{ ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ __iomem void *membase; ++ struct dma_device *dma_dev; ++ struct rt2880_dma_chan *rt_chan; ++ int err; ++ int ret; ++ int reg; ++ int irq; ++ ++ membase = devm_request_and_ioremap(&pdev->dev, res); ++ if (IS_ERR(membase)) ++ return PTR_ERR(membase); ++ ++ dma_dev = devm_kzalloc(&pdev->dev, sizeof(*dma_dev), GFP_KERNEL); ++ if (!dma_dev) ++ return -ENOMEM; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (!irq) { ++ dev_err(&pdev->dev, "failed to load irq\n"); ++ return -ENOENT; ++ } ++ ++ ++ INIT_LIST_HEAD(&dma_dev->channels); ++ dma_cap_zero(dma_dev->cap_mask); ++ dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask); ++ dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); ++ dma_dev->device_alloc_chan_resources = rt2880_dma_alloc_chan_resources; ++ dma_dev->device_free_chan_resources = rt2880_dma_free_chan_resources; ++ dma_dev->device_tx_status = rt2880_dma_status; ++ dma_dev->device_issue_pending = rt2880_dma_issue_pending; ++ dma_dev->device_prep_dma_memcpy = rt2880_dma_prep_dma_memcpy; ++ dma_dev->device_control = rt2880_dma_control; ++ dma_dev->dev = &pdev->dev; ++ ++ rt_chan = devm_kzalloc(&pdev->dev, sizeof(*rt_chan), GFP_KERNEL); ++ if (!rt_chan) { ++ return -ENOMEM; ++ } ++ ++ spin_lock_init(&rt_chan->lock); ++ INIT_LIST_HEAD(&rt_chan->chain); ++ INIT_LIST_HEAD(&rt_chan->completed_slots); ++ INIT_LIST_HEAD(&rt_chan->all_slots); ++ rt_chan->common.device = dma_dev; ++ rt_chan->txd.tx_submit = rt2880_dma_tx_submit; ++ ++ list_add_tail(&rt_chan->common.device_node, &dma_dev->channels); ++ ++ err = dma_async_device_register(dma_dev); ++ if (0 != err) { ++ pr_err("ERR_MDMA:device_register failed: %d\n", err); ++ return 1; ++ } ++ ++ ret = request_irq(irq, rt2880_dma_interrupt_handler, 0, dev_name(&pdev->dev), NULL); ++ if(ret){ ++ pr_err("IRQ %d is not free.\n", SURFBOARDINT_DMA); ++ return 1; ++ } ++ ++ //set GDMA register in advance. ++ reg = (32 << 16) | (32 << 8) | (MEMCPY_DMA_CH << 3); ++ RT_DMA_WRITE_REG(RT_DMA_CTRL_REG1(MEMCPY_DMA_CH), reg); ++ ++ dev_info(&pdev->dev, "running\n"); ++ ++ return 0; ++} ++ ++static int rt2880_dma_remove(struct platform_device *dev) ++{ ++ struct dma_device *dma_dev = platform_get_drvdata(dev); ++ ++ printk("%s\n",__FUNCTION__); ++ ++ dma_async_device_unregister(dma_dev); ++ ++ return 0; ++} ++ ++static const struct of_device_id rt2880_dma_match[] = { ++ { .compatible = "ralink,rt2880-gdma" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt2880_wdt_match); ++ ++static struct platform_driver rt2880_dma_driver = { ++ .probe = rt2880_dma_probe, ++ .remove = rt2880_dma_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = RT_DMA_NAME, ++ .of_match_table = rt2880_dma_match, ++ }, ++}; ++ ++static int __init rt2880_dma_init(void) ++{ ++ int rc; ++ ++ rc = platform_driver_register(&rt2880_dma_driver); ++ return rc; ++} ++module_init(rt2880_dma_init); ++ ++MODULE_AUTHOR("Steven Liu "); ++MODULE_AUTHOR("John Crispin "); ++MODULE_DESCRIPTION("DMA engine driver for Ralink DMA engine"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/dma/ralink_gdma.h b/drivers/dma/ralink_gdma.h +new file mode 100644 +index 0000000..73e1948 +--- /dev/null ++++ b/drivers/dma/ralink_gdma.h +@@ -0,0 +1,55 @@ ++/* ++ * Copyright (C) 2007, 2008, Marvell International Ltd. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software Foundation, ++ * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#ifndef RT_DMA_H ++#define RT_DMA_H ++ ++#include ++#include ++#include ++#include ++ ++#define RT_DMA_NAME "rt2880_dma" ++ ++#define RALINK_GDMA_BASE 0xB0002800 ++ ++struct rt2880_dma_chan { ++ int pending; ++ dma_cookie_t completed_cookie; ++ spinlock_t lock; /* protects the descriptor slot pool */ ++ void __iomem *mmr_base; ++ unsigned int idx; ++ enum dma_transaction_type current_type; ++ struct dma_async_tx_descriptor txd; ++ struct list_head chain; ++ struct list_head completed_slots; ++ struct dma_chan common; ++ struct list_head all_slots; ++ int slots_allocated; ++ struct tasklet_struct irq_tasklet; ++}; ++ ++#define RT_DMA_READ_REG(addr) le32_to_cpu(*(volatile u32 *)(addr)) ++#define RT_DMA_WRITE_REG(addr, val) *((volatile uint32_t *)(addr)) = cpu_to_le32(val) ++ ++#define RT_DMA_SRC_REG(ch) (RALINK_GDMA_BASE + ch*16) ++#define RT_DMA_DST_REG(ch) (RT_DMA_SRC_REG(ch) + 4) ++#define RT_DMA_CTRL_REG(ch) (RT_DMA_DST_REG(ch) + 4) ++#define RT_DMA_CTRL_REG1(ch) (RT_DMA_CTRL_REG(ch) + 4) ++#define RT_DMA_DONEINT (RALINK_GDMA_BASE + 0x204) ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0149-PCI-MIPS-adds-rt2880-pci-support.patch b/target/linux/ramips/patches-3.9/0149-PCI-MIPS-adds-rt2880-pci-support.patch new file mode 100644 index 0000000000..da94ee690a --- /dev/null +++ b/target/linux/ramips/patches-3.9/0149-PCI-MIPS-adds-rt2880-pci-support.patch @@ -0,0 +1,329 @@ +From 7cc89e7fc028a8e23b5dc226be5cf94b33764885 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 21 Mar 2013 18:27:29 +0100 +Subject: [PATCH 149/164] PCI: MIPS: adds rt2880 pci support + +Add support for the pci found on the rt2880 SoC. + +Signed-off-by: John Crispin +--- + arch/mips/pci/Makefile | 1 + + arch/mips/pci/pci-rt2880.c | 281 ++++++++++++++++++++++++++++++++++++++++++++ + arch/mips/ralink/Kconfig | 1 + + 3 files changed, 283 insertions(+) + create mode 100644 arch/mips/pci/pci-rt2880.c + +diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile +index 2cb1d31..77974ba 100644 +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -41,6 +41,7 @@ obj-$(CONFIG_SIBYTE_BCM1x80) += pci-bcm1480.o pci-bcm1480ht.o + obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o + obj-$(CONFIG_LANTIQ) += fixup-lantiq.o + obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o ++obj-$(CONFIG_SOC_RT2880) += pci-rt2880.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o + obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o +diff --git a/arch/mips/pci/pci-rt2880.c b/arch/mips/pci/pci-rt2880.c +new file mode 100644 +index 0000000..e2c4730 +--- /dev/null ++++ b/arch/mips/pci/pci-rt2880.c +@@ -0,0 +1,281 @@ ++/* ++ * Ralink RT288x SoC PCI register definitions ++ * ++ * Copyright (C) 2009 John Crispin ++ * Copyright (C) 2009 Gabor Juhos ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define RT2880_PCI_BASE 0x00440000 ++#define RT288X_CPU_IRQ_PCI 4 ++ ++#define RT2880_PCI_MEM_BASE 0x20000000 ++#define RT2880_PCI_MEM_SIZE 0x10000000 ++#define RT2880_PCI_IO_BASE 0x00460000 ++#define RT2880_PCI_IO_SIZE 0x00010000 ++ ++#define RT2880_PCI_REG_PCICFG_ADDR 0x00 ++#define RT2880_PCI_REG_PCIMSK_ADDR 0x0c ++#define RT2880_PCI_REG_BAR0SETUP_ADDR 0x10 ++#define RT2880_PCI_REG_IMBASEBAR0_ADDR 0x18 ++#define RT2880_PCI_REG_CONFIG_ADDR 0x20 ++#define RT2880_PCI_REG_CONFIG_DATA 0x24 ++#define RT2880_PCI_REG_MEMBASE 0x28 ++#define RT2880_PCI_REG_IOBASE 0x2c ++#define RT2880_PCI_REG_ID 0x30 ++#define RT2880_PCI_REG_CLASS 0x34 ++#define RT2880_PCI_REG_SUBID 0x38 ++#define RT2880_PCI_REG_ARBCTL 0x80 ++ ++static void __iomem *rt2880_pci_base; ++static DEFINE_SPINLOCK(rt2880_pci_lock); ++ ++static u32 rt2880_pci_reg_read(u32 reg) ++{ ++ return readl(rt2880_pci_base + reg); ++} ++ ++static void rt2880_pci_reg_write(u32 val, u32 reg) ++{ ++ writel(val, rt2880_pci_base + reg); ++} ++ ++static inline u32 rt2880_pci_get_cfgaddr(unsigned int bus, unsigned int slot, ++ unsigned int func, unsigned int where) ++{ ++ return ((bus << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | ++ 0x80000000); ++} ++ ++static int rt2880_pci_config_read(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *val) ++{ ++ unsigned long flags; ++ u32 address; ++ u32 data; ++ ++ address = rt2880_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), ++ PCI_FUNC(devfn), where); ++ ++ spin_lock_irqsave(&rt2880_pci_lock, flags); ++ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); ++ data = rt2880_pci_reg_read(RT2880_PCI_REG_CONFIG_DATA); ++ spin_unlock_irqrestore(&rt2880_pci_lock, flags); ++ ++ switch (size) { ++ case 1: ++ *val = (data >> ((where & 3) << 3)) & 0xff; ++ break; ++ case 2: ++ *val = (data >> ((where & 3) << 3)) & 0xffff; ++ break; ++ case 4: ++ *val = data; ++ break; ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int rt2880_pci_config_write(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 val) ++{ ++ unsigned long flags; ++ u32 address; ++ u32 data; ++ ++ address = rt2880_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), ++ PCI_FUNC(devfn), where); ++ ++ spin_lock_irqsave(&rt2880_pci_lock, flags); ++ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); ++ data = rt2880_pci_reg_read(RT2880_PCI_REG_CONFIG_DATA); ++ ++ switch (size) { ++ case 1: ++ data = (data & ~(0xff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 2: ++ data = (data & ~(0xffff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 4: ++ data = val; ++ break; ++ } ++ ++ rt2880_pci_reg_write(data, RT2880_PCI_REG_CONFIG_DATA); ++ spin_unlock_irqrestore(&rt2880_pci_lock, flags); ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static struct pci_ops rt2880_pci_ops = { ++ .read = rt2880_pci_config_read, ++ .write = rt2880_pci_config_write, ++}; ++ ++static struct resource rt2880_pci_mem_resource = { ++ .name = "PCI MEM space", ++ .start = RT2880_PCI_MEM_BASE, ++ .end = RT2880_PCI_MEM_BASE + RT2880_PCI_MEM_SIZE - 1, ++ .flags = IORESOURCE_MEM, ++}; ++ ++static struct resource rt2880_pci_io_resource = { ++ .name = "PCI IO space", ++ .start = RT2880_PCI_IO_BASE, ++ .end = RT2880_PCI_IO_BASE + RT2880_PCI_IO_SIZE - 1, ++ .flags = IORESOURCE_IO, ++}; ++ ++static struct pci_controller rt2880_pci_controller = { ++ .pci_ops = &rt2880_pci_ops, ++ .mem_resource = &rt2880_pci_mem_resource, ++ .io_resource = &rt2880_pci_io_resource, ++}; ++ ++static inline u32 rt2880_pci_read_u32(unsigned long reg) ++{ ++ unsigned long flags; ++ u32 address; ++ u32 ret; ++ ++ address = rt2880_pci_get_cfgaddr(0, 0, 0, reg); ++ ++ spin_lock_irqsave(&rt2880_pci_lock, flags); ++ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); ++ ret = rt2880_pci_reg_read(RT2880_PCI_REG_CONFIG_DATA); ++ spin_unlock_irqrestore(&rt2880_pci_lock, flags); ++ ++ return ret; ++} ++ ++static inline void rt2880_pci_write_u32(unsigned long reg, u32 val) ++{ ++ unsigned long flags; ++ u32 address; ++ ++ address = rt2880_pci_get_cfgaddr(0, 0, 0, reg); ++ ++ spin_lock_irqsave(&rt2880_pci_lock, flags); ++ rt2880_pci_reg_write(address, RT2880_PCI_REG_CONFIG_ADDR); ++ rt2880_pci_reg_write(val, RT2880_PCI_REG_CONFIG_DATA); ++ spin_unlock_irqrestore(&rt2880_pci_lock, flags); ++} ++ ++int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ u16 cmd; ++ int irq = -1; ++ ++ if (dev->bus->number != 0) ++ return irq; ++ ++ switch (PCI_SLOT(dev->devfn)) { ++ case 0x00: ++ rt2880_pci_write_u32(PCI_BASE_ADDRESS_0, 0x08000000); ++ (void) rt2880_pci_read_u32(PCI_BASE_ADDRESS_0); ++ break; ++ case 0x11: ++ irq = RT288X_CPU_IRQ_PCI; ++ break; ++ default: ++ printk("%s:%s[%d] trying to alloc unknown pci irq\n", ++ __FILE__, __func__, __LINE__); ++ BUG(); ++ break; ++ } ++ ++ pci_write_config_byte((struct pci_dev*)dev, PCI_CACHE_LINE_SIZE, 0x14); ++ pci_write_config_byte((struct pci_dev*)dev, PCI_LATENCY_TIMER, 0xFF); ++ pci_read_config_word((struct pci_dev*)dev, PCI_COMMAND, &cmd); ++ cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | ++ PCI_COMMAND_INVALIDATE | PCI_COMMAND_FAST_BACK | ++ PCI_COMMAND_SERR | PCI_COMMAND_WAIT | PCI_COMMAND_PARITY; ++ pci_write_config_word((struct pci_dev*)dev, PCI_COMMAND, cmd); ++ pci_write_config_byte((struct pci_dev*)dev, PCI_INTERRUPT_LINE, ++ dev->irq); ++ return irq; ++} ++ ++static int rt288x_pci_probe(struct platform_device *pdev) ++{ ++ void __iomem *io_map_base; ++ int i; ++ ++ rt2880_pci_base = ioremap_nocache(RT2880_PCI_BASE, PAGE_SIZE); ++ ++ io_map_base = ioremap(RT2880_PCI_IO_BASE, RT2880_PCI_IO_SIZE); ++ rt2880_pci_controller.io_map_base = (unsigned long) io_map_base; ++ set_io_port_base((unsigned long) io_map_base); ++ ++ ioport_resource.start = RT2880_PCI_IO_BASE; ++ ioport_resource.end = RT2880_PCI_IO_BASE + RT2880_PCI_IO_SIZE - 1; ++ ++ rt2880_pci_reg_write(0, RT2880_PCI_REG_PCICFG_ADDR); ++ for(i = 0; i < 0xfffff; i++) {} ++ ++ rt2880_pci_reg_write(0x79, RT2880_PCI_REG_ARBCTL); ++ rt2880_pci_reg_write(0x07FF0001, RT2880_PCI_REG_BAR0SETUP_ADDR); ++ rt2880_pci_reg_write(RT2880_PCI_MEM_BASE, RT2880_PCI_REG_MEMBASE); ++ rt2880_pci_reg_write(RT2880_PCI_IO_BASE, RT2880_PCI_REG_IOBASE); ++ rt2880_pci_reg_write(0x08000000, RT2880_PCI_REG_IMBASEBAR0_ADDR); ++ rt2880_pci_reg_write(0x08021814, RT2880_PCI_REG_ID); ++ rt2880_pci_reg_write(0x00800001, RT2880_PCI_REG_CLASS); ++ rt2880_pci_reg_write(0x28801814, RT2880_PCI_REG_SUBID); ++ rt2880_pci_reg_write(0x000c0000, RT2880_PCI_REG_PCIMSK_ADDR); ++ ++ rt2880_pci_write_u32(PCI_BASE_ADDRESS_0, 0x08000000); ++ (void) rt2880_pci_read_u32(PCI_BASE_ADDRESS_0); ++ ++ register_pci_controller(&rt2880_pci_controller); ++ return 0; ++} ++ ++int pcibios_plat_dev_init(struct pci_dev *dev) ++{ ++ return 0; ++} ++ ++static const struct of_device_id rt288x_pci_match[] = { ++ { .compatible = "ralink,rt288x-pci" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt288x_pci_match); ++ ++static struct platform_driver rt288x_pci_driver = { ++ .probe = rt288x_pci_probe, ++ .driver = { ++ .name = "rt288x-pci", ++ .owner = THIS_MODULE, ++ .of_match_table = rt288x_pci_match, ++ }, ++}; ++ ++int __init pcibios_init(void) ++{ ++ int ret = platform_driver_register(&rt288x_pci_driver); ++ if (ret) ++ pr_info("rt288x-pci: Error registering platform driver!"); ++ return ret; ++} ++ ++arch_initcall(pcibios_init); +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 3fe032c..c0ac93a 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -8,6 +8,7 @@ choice + + config SOC_RT288X + bool "RT288x" ++ select HW_HAS_PCI + + config SOC_RT305X + bool "RT305x" +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0150-PCI-MIPS-adds-rt3883-pci-support.patch b/target/linux/ramips/patches-3.9/0150-PCI-MIPS-adds-rt3883-pci-support.patch new file mode 100644 index 0000000000..20ae55875f --- /dev/null +++ b/target/linux/ramips/patches-3.9/0150-PCI-MIPS-adds-rt3883-pci-support.patch @@ -0,0 +1,688 @@ +From 9e3707ddded9895aa47e11b42125a94f04f8bd9d Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 21 Mar 2013 17:34:08 +0100 +Subject: [PATCH 150/164] PCI: MIPS: adds rt3883 pci support + +Add support for the pcie found on the rt3883 SoC. + +Signed-off-by: John Crispin +--- + arch/mips/pci/Makefile | 1 + + arch/mips/pci/pci-rt3883.c | 640 ++++++++++++++++++++++++++++++++++++++++++++ + arch/mips/ralink/Kconfig | 1 + + 3 files changed, 642 insertions(+) + create mode 100644 arch/mips/pci/pci-rt3883.c + +diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile +index 77974ba..3cbfd9b 100644 +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -42,6 +42,7 @@ obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o + obj-$(CONFIG_LANTIQ) += fixup-lantiq.o + obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o + obj-$(CONFIG_SOC_RT2880) += pci-rt2880.o ++obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o + obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o +diff --git a/arch/mips/pci/pci-rt3883.c b/arch/mips/pci/pci-rt3883.c +new file mode 100644 +index 0000000..212c90b +--- /dev/null ++++ b/arch/mips/pci/pci-rt3883.c +@@ -0,0 +1,640 @@ ++/* ++ * Ralink RT3662/RT3883 SoC PCI support ++ * ++ * Copyright (C) 2011-2013 Gabor Juhos ++ * ++ * Parts of this file are based on Ralink's 2.6.21 BSP ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define RT3883_MEMORY_BASE 0x00000000 ++#define RT3883_MEMORY_SIZE 0x02000000 ++ ++#define RT3883_PCI_REG_PCICFG 0x00 ++#define RT3883_PCICFG_P2P_BR_DEVNUM_M 0xf ++#define RT3883_PCICFG_P2P_BR_DEVNUM_S 16 ++#define RT3883_PCICFG_PCIRST BIT(1) ++#define RT3883_PCI_REG_PCIRAW 0x04 ++#define RT3883_PCI_REG_PCIINT 0x08 ++#define RT3883_PCI_REG_PCIENA 0x0c ++ ++#define RT3883_PCI_REG_CFGADDR 0x20 ++#define RT3883_PCI_REG_CFGDATA 0x24 ++#define RT3883_PCI_REG_MEMBASE 0x28 ++#define RT3883_PCI_REG_IOBASE 0x2c ++#define RT3883_PCI_REG_ARBCTL 0x80 ++ ++#define RT3883_PCI_REG_BASE(_x) (0x1000 + (_x) * 0x1000) ++#define RT3883_PCI_REG_BAR0SETUP(_x) (RT3883_PCI_REG_BASE((_x)) + 0x10) ++#define RT3883_PCI_REG_IMBASEBAR0(_x) (RT3883_PCI_REG_BASE((_x)) + 0x18) ++#define RT3883_PCI_REG_ID(_x) (RT3883_PCI_REG_BASE((_x)) + 0x30) ++#define RT3883_PCI_REG_CLASS(_x) (RT3883_PCI_REG_BASE((_x)) + 0x34) ++#define RT3883_PCI_REG_SUBID(_x) (RT3883_PCI_REG_BASE((_x)) + 0x38) ++#define RT3883_PCI_REG_STATUS(_x) (RT3883_PCI_REG_BASE((_x)) + 0x50) ++ ++#define RT3883_PCI_MODE_NONE 0 ++#define RT3883_PCI_MODE_PCI BIT(0) ++#define RT3883_PCI_MODE_PCIE BIT(1) ++#define RT3883_PCI_MODE_BOTH (RT3883_PCI_MODE_PCI | RT3883_PCI_MODE_PCIE) ++ ++#define RT3883_PCI_IRQ_COUNT 32 ++ ++#define RT3883_P2P_BR_DEVNUM 1 ++ ++struct rt3883_pci_controller { ++ void __iomem *base; ++ spinlock_t lock; ++ ++ struct irq_domain *irq_domain; ++ ++ struct pci_controller pci_controller; ++ struct resource io_res; ++ struct resource mem_res; ++ ++ bool pcie_ready; ++ unsigned char p2p_devnum; ++}; ++ ++static inline struct rt3883_pci_controller * ++pci_bus_to_rt3883_controller(struct pci_bus *bus) ++{ ++ struct pci_controller *hose; ++ ++ hose = (struct pci_controller *) bus->sysdata; ++ return container_of(hose, struct rt3883_pci_controller, pci_controller); ++} ++ ++static inline u32 rt3883_pci_r32(struct rt3883_pci_controller *rpc, ++ unsigned reg) ++{ ++ return ioread32(rpc->base + reg); ++} ++ ++static inline void rt3883_pci_w32(struct rt3883_pci_controller *rpc, ++ u32 val, unsigned reg) ++{ ++ iowrite32(val, rpc->base + reg); ++} ++ ++static inline u32 rt3883_pci_get_cfgaddr(unsigned int bus, unsigned int slot, ++ unsigned int func, unsigned int where) ++{ ++ return ((bus << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | ++ 0x80000000); ++} ++ ++static u32 rt3883_pci_read_cfg32(struct rt3883_pci_controller *rpc, ++ unsigned bus, unsigned slot, ++ unsigned func, unsigned reg) ++{ ++ unsigned long flags; ++ u32 address; ++ u32 ret; ++ ++ address = rt3883_pci_get_cfgaddr(bus, slot, func, reg); ++ ++ spin_lock_irqsave(&rpc->lock, flags); ++ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); ++ ret = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA); ++ spin_unlock_irqrestore(&rpc->lock, flags); ++ ++ return ret; ++} ++ ++static void rt3883_pci_write_cfg32(struct rt3883_pci_controller *rpc, ++ unsigned bus, unsigned slot, ++ unsigned func, unsigned reg, u32 val) ++{ ++ unsigned long flags; ++ u32 address; ++ ++ address = rt3883_pci_get_cfgaddr(bus, slot, func, reg); ++ ++ spin_lock_irqsave(&rpc->lock, flags); ++ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); ++ rt3883_pci_w32(rpc, val, RT3883_PCI_REG_CFGDATA); ++ spin_unlock_irqrestore(&rpc->lock, flags); ++} ++ ++static void rt3883_pci_irq_handler(unsigned int irq, struct irq_desc *desc) ++{ ++ struct rt3883_pci_controller *rpc; ++ u32 pending; ++ ++ rpc = irq_get_handler_data(irq); ++ ++ pending = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIINT) & ++ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); ++ ++ if (!pending) { ++ spurious_interrupt(); ++ return; ++ } ++ ++ while (pending) { ++ unsigned bit = __ffs(pending); ++ ++ irq = irq_find_mapping(rpc->irq_domain, bit); ++ generic_handle_irq(irq); ++ ++ pending &= ~BIT(bit); ++ } ++} ++ ++static void rt3883_pci_irq_unmask(struct irq_data *d) ++{ ++ struct rt3883_pci_controller *rpc; ++ u32 t; ++ ++ rpc = irq_data_get_irq_chip_data(d); ++ ++ t = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); ++ rt3883_pci_w32(rpc, t | BIT(d->hwirq), RT3883_PCI_REG_PCIENA); ++ /* flush write */ ++ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); ++} ++ ++static void rt3883_pci_irq_mask(struct irq_data *d) ++{ ++ struct rt3883_pci_controller *rpc; ++ u32 t; ++ ++ rpc = irq_data_get_irq_chip_data(d); ++ ++ t = rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); ++ rt3883_pci_w32(rpc, t & ~BIT(d->hwirq), RT3883_PCI_REG_PCIENA); ++ /* flush write */ ++ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCIENA); ++} ++ ++static struct irq_chip rt3883_pci_irq_chip = { ++ .name = "RT3883 PCI", ++ .irq_mask = rt3883_pci_irq_mask, ++ .irq_unmask = rt3883_pci_irq_unmask, ++ .irq_mask_ack = rt3883_pci_irq_mask, ++}; ++ ++static int rt3883_pci_irq_map(struct irq_domain *d, unsigned int irq, ++ irq_hw_number_t hw) ++{ ++ irq_set_chip_and_handler(irq, &rt3883_pci_irq_chip, handle_level_irq); ++ irq_set_chip_data(irq, d->host_data); ++ ++ return 0; ++} ++ ++static const struct irq_domain_ops rt3883_pci_irq_domain_ops = { ++ .map = rt3883_pci_irq_map, ++ .xlate = irq_domain_xlate_onecell, ++}; ++ ++static int rt3883_pci_irq_init(struct device *dev, ++ struct rt3883_pci_controller *rpc) ++{ ++ struct device_node *np = dev->of_node; ++ struct device_node *intc_np; ++ int irq; ++ int err; ++ ++ intc_np = of_get_child_by_name(np, "interrupt-controller"); ++ if (!intc_np) { ++ dev_err(dev, "no %s child node found", "interrupt-controller"); ++ return -ENODEV; ++ } ++ ++ irq = irq_of_parse_and_map(intc_np, 0); ++ if (irq == 0) { ++ dev_err(dev, "%s has no IRQ", of_node_full_name(intc_np)); ++ err = -EINVAL; ++ goto err_put_intc; ++ } ++ ++ /* disable all interrupts */ ++ rt3883_pci_w32(rpc, 0, RT3883_PCI_REG_PCIENA); ++ ++ rpc->irq_domain = ++ irq_domain_add_linear(intc_np, RT3883_PCI_IRQ_COUNT, ++ &rt3883_pci_irq_domain_ops, ++ rpc); ++ if (!rpc->irq_domain) { ++ dev_err(dev, "unable to add IRQ domain\n"); ++ err = -ENODEV; ++ goto err_put_intc; ++ } ++ ++ irq_set_handler_data(irq, rpc); ++ irq_set_chained_handler(irq, rt3883_pci_irq_handler); ++ ++ return 0; ++ ++err_put_intc: ++ of_node_put(intc_np); ++ return err; ++} ++ ++static int rt3883_pci_config_read(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 *val) ++{ ++ struct rt3883_pci_controller *rpc; ++ unsigned long flags; ++ u32 address; ++ u32 data; ++ ++ rpc = pci_bus_to_rt3883_controller(bus); ++ ++ if (!rpc->pcie_ready && bus->number == 1) ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ ++ address = rt3883_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), ++ PCI_FUNC(devfn), where); ++ ++ spin_lock_irqsave(&rpc->lock, flags); ++ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); ++ data = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA); ++ spin_unlock_irqrestore(&rpc->lock, flags); ++ ++ switch (size) { ++ case 1: ++ *val = (data >> ((where & 3) << 3)) & 0xff; ++ break; ++ case 2: ++ *val = (data >> ((where & 3) << 3)) & 0xffff; ++ break; ++ case 4: ++ *val = data; ++ break; ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int rt3883_pci_config_write(struct pci_bus *bus, unsigned int devfn, ++ int where, int size, u32 val) ++{ ++ struct rt3883_pci_controller *rpc; ++ unsigned long flags; ++ u32 address; ++ u32 data; ++ ++ rpc = pci_bus_to_rt3883_controller(bus); ++ ++ if (!rpc->pcie_ready && bus->number == 1) ++ return PCIBIOS_DEVICE_NOT_FOUND; ++ ++ address = rt3883_pci_get_cfgaddr(bus->number, PCI_SLOT(devfn), ++ PCI_FUNC(devfn), where); ++ ++ spin_lock_irqsave(&rpc->lock, flags); ++ rt3883_pci_w32(rpc, address, RT3883_PCI_REG_CFGADDR); ++ data = rt3883_pci_r32(rpc, RT3883_PCI_REG_CFGDATA); ++ ++ switch (size) { ++ case 1: ++ data = (data & ~(0xff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 2: ++ data = (data & ~(0xffff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 4: ++ data = val; ++ break; ++ } ++ ++ rt3883_pci_w32(rpc, data, RT3883_PCI_REG_CFGDATA); ++ spin_unlock_irqrestore(&rpc->lock, flags); ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static struct pci_ops rt3883_pci_ops = { ++ .read = rt3883_pci_config_read, ++ .write = rt3883_pci_config_write, ++}; ++ ++static void rt3883_pci_preinit(struct rt3883_pci_controller *rpc, unsigned mode) ++{ ++ u32 syscfg1; ++ u32 rstctrl; ++ u32 clkcfg1; ++ u32 t; ++ ++ rstctrl = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL); ++ syscfg1 = rt_sysc_r32(RT3883_SYSC_REG_SYSCFG1); ++ clkcfg1 = rt_sysc_r32(RT3883_SYSC_REG_CLKCFG1); ++ ++ if (mode & RT3883_PCI_MODE_PCIE) { ++ rstctrl |= RT3883_RSTCTRL_PCIE; ++ rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL); ++ ++ /* setup PCI PAD drive mode */ ++ syscfg1 &= ~(0x30); ++ syscfg1 |= (2 << 4); ++ rt_sysc_w32(syscfg1, RT3883_SYSC_REG_SYSCFG1); ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ t &= ~BIT(31); ++ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN1); ++ t &= 0x80ffffff; ++ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN1); ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN1); ++ t |= 0xa << 24; ++ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN1); ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ t |= BIT(31); ++ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ ++ msleep(50); ++ ++ rstctrl &= ~RT3883_RSTCTRL_PCIE; ++ rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL); ++ } ++ ++ syscfg1 |= (RT3883_SYSCFG1_PCIE_RC_MODE | RT3883_SYSCFG1_PCI_HOST_MODE); ++ ++ clkcfg1 &= ~(RT3883_CLKCFG1_PCI_CLK_EN | RT3883_CLKCFG1_PCIE_CLK_EN); ++ ++ if (mode & RT3883_PCI_MODE_PCI) { ++ clkcfg1 |= RT3883_CLKCFG1_PCI_CLK_EN; ++ rstctrl &= ~RT3883_RSTCTRL_PCI; ++ } ++ ++ if (mode & RT3883_PCI_MODE_PCIE) { ++ clkcfg1 |= RT3883_CLKCFG1_PCIE_CLK_EN; ++ rstctrl &= ~RT3883_RSTCTRL_PCIE; ++ } ++ ++ rt_sysc_w32(syscfg1, RT3883_SYSC_REG_SYSCFG1); ++ rt_sysc_w32(rstctrl, RT3883_SYSC_REG_RSTCTRL); ++ rt_sysc_w32(clkcfg1, RT3883_SYSC_REG_CLKCFG1); ++ ++ msleep(500); ++ ++ /* ++ * setup the device number of the P2P bridge ++ * and de-assert the reset line ++ */ ++ t = (RT3883_P2P_BR_DEVNUM << RT3883_PCICFG_P2P_BR_DEVNUM_S); ++ rt3883_pci_w32(rpc, t, RT3883_PCI_REG_PCICFG); ++ ++ /* flush write */ ++ rt3883_pci_r32(rpc, RT3883_PCI_REG_PCICFG); ++ msleep(500); ++ ++ if (mode & RT3883_PCI_MODE_PCIE) { ++ msleep(500); ++ ++ t = rt3883_pci_r32(rpc, RT3883_PCI_REG_STATUS(1)); ++ ++ rpc->pcie_ready = t & BIT(0); ++ ++ if (!rpc->pcie_ready) { ++ /* reset the PCIe block */ ++ t = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL); ++ t |= RT3883_RSTCTRL_PCIE; ++ rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL); ++ t &= ~RT3883_RSTCTRL_PCIE; ++ rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL); ++ ++ /* turn off PCIe clock */ ++ t = rt_sysc_r32(RT3883_SYSC_REG_CLKCFG1); ++ t &= ~RT3883_CLKCFG1_PCIE_CLK_EN; ++ rt_sysc_w32(t, RT3883_SYSC_REG_CLKCFG1); ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ t &= ~0xf000c080; ++ rt_sysc_w32(t, RT3883_SYSC_REG_PCIE_CLK_GEN0); ++ } ++ } ++ ++ /* enable PCI arbiter */ ++ rt3883_pci_w32(rpc, 0x79, RT3883_PCI_REG_ARBCTL); ++} ++ ++static inline void ++rt3883_dump_pci_config(struct rt3883_pci_controller *rpc, ++ int bus, int slot) ++{ ++ int i; ++ ++ for (i = 0; i < 16; i++) { ++ u32 val; ++ ++ val = rt3883_pci_read_cfg32(rpc, bus, slot, 0, i << 2); ++ pr_info("pci %02x:%02x.0 0x%02x = %08x\n", ++ bus, slot, i << 2, val); ++ } ++} ++ ++static int rt3883_pci_probe(struct platform_device *pdev) ++{ ++ struct rt3883_pci_controller *rpc; ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ struct resource *res; ++ struct device_node *child; ++ u32 val; ++ int err; ++ int mode; ++ ++ rpc = devm_kzalloc(dev, sizeof(*rpc), GFP_KERNEL); ++ if (!rpc) ++ return -ENOMEM; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) ++ return -EINVAL; ++ ++ rpc->base = devm_ioremap_resource(dev, res); ++ if (IS_ERR(rpc->base)) ++ return PTR_ERR(rpc->base); ++ ++ rpc->pci_controller.of_node = of_get_child_by_name(np, "host-bridge"); ++ if (!rpc->pci_controller.of_node) { ++ dev_err(dev, "no %s child node found", "host-bridge"); ++ return -ENODEV; ++ } ++ ++ mode = RT3883_PCI_MODE_NONE; ++ for_each_child_of_node(rpc->pci_controller.of_node, child) { ++ u32 slot; ++ ++ if (!of_device_is_available(child)) ++ continue; ++ ++ if (of_property_read_u32(child, "ralink,pci-slot", ++ &slot)) { ++ dev_err(dev, "no '%s' property found for %s\n", ++ "ralink,pci-slot", ++ of_node_full_name(child)); ++ continue; ++ } ++ ++ switch (slot) { ++ case 1: ++ mode |= RT3883_PCI_MODE_PCIE; ++ break; ++ ++ case 17: ++ case 18: ++ mode |= RT3883_PCI_MODE_PCI; ++ break; ++ } ++ } ++ ++ if (mode == RT3883_PCI_MODE_NONE) { ++ dev_err(dev, "unable to determine PCI mode\n"); ++ err = -EINVAL; ++ goto err_put_hb_node; ++ } ++ ++ dev_info(dev, "mode:%s%s\n", ++ (mode & RT3883_PCI_MODE_PCI) ? " PCI" : "", ++ (mode & RT3883_PCI_MODE_PCIE) ? " PCIe" : ""); ++ ++ rt3883_pci_preinit(rpc, mode); ++ ++ rpc->pci_controller.pci_ops = &rt3883_pci_ops; ++ rpc->pci_controller.io_resource = &rpc->io_res; ++ rpc->pci_controller.mem_resource = &rpc->mem_res; ++ ++ /* Load PCI I/O and memory resources from DT */ ++ pci_load_of_ranges(&rpc->pci_controller, ++ rpc->pci_controller.of_node); ++ ++ rt3883_pci_w32(rpc, rpc->mem_res.start, RT3883_PCI_REG_MEMBASE); ++ rt3883_pci_w32(rpc, rpc->io_res.start, RT3883_PCI_REG_IOBASE); ++ ++ ioport_resource.start = rpc->io_res.start; ++ ioport_resource.end = rpc->io_res.end; ++ ++ /* PCI */ ++ rt3883_pci_w32(rpc, 0x03ff0000, RT3883_PCI_REG_BAR0SETUP(0)); ++ rt3883_pci_w32(rpc, RT3883_MEMORY_BASE, RT3883_PCI_REG_IMBASEBAR0(0)); ++ rt3883_pci_w32(rpc, 0x08021814, RT3883_PCI_REG_ID(0)); ++ rt3883_pci_w32(rpc, 0x00800001, RT3883_PCI_REG_CLASS(0)); ++ rt3883_pci_w32(rpc, 0x28801814, RT3883_PCI_REG_SUBID(0)); ++ ++ /* PCIe */ ++ rt3883_pci_w32(rpc, 0x03ff0000, RT3883_PCI_REG_BAR0SETUP(1)); ++ rt3883_pci_w32(rpc, RT3883_MEMORY_BASE, RT3883_PCI_REG_IMBASEBAR0(1)); ++ rt3883_pci_w32(rpc, 0x08021814, RT3883_PCI_REG_ID(1)); ++ rt3883_pci_w32(rpc, 0x06040001, RT3883_PCI_REG_CLASS(1)); ++ rt3883_pci_w32(rpc, 0x28801814, RT3883_PCI_REG_SUBID(1)); ++ ++ err = rt3883_pci_irq_init(dev, rpc); ++ if (err) ++ goto err_put_hb_node; ++ ++ /* PCIe */ ++ val = rt3883_pci_read_cfg32(rpc, 0, 0x01, 0, PCI_COMMAND); ++ val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; ++ rt3883_pci_write_cfg32(rpc, 0, 0x01, 0, PCI_COMMAND, val); ++ ++ /* PCI */ ++ val = rt3883_pci_read_cfg32(rpc, 0, 0x00, 0, PCI_COMMAND); ++ val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; ++ rt3883_pci_write_cfg32(rpc, 0, 0x00, 0, PCI_COMMAND, val); ++ ++ if (mode == RT3883_PCI_MODE_PCIE) { ++ rt3883_pci_w32(rpc, 0x03ff0001, RT3883_PCI_REG_BAR0SETUP(0)); ++ rt3883_pci_w32(rpc, 0x03ff0001, RT3883_PCI_REG_BAR0SETUP(1)); ++ ++ rt3883_pci_write_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0, ++ PCI_BASE_ADDRESS_0, ++ RT3883_MEMORY_BASE); ++ /* flush write */ ++ rt3883_pci_read_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0, ++ PCI_BASE_ADDRESS_0); ++ } else { ++ rt3883_pci_write_cfg32(rpc, 0, RT3883_P2P_BR_DEVNUM, 0, ++ PCI_IO_BASE, 0x00000101); ++ } ++ ++ register_pci_controller(&rpc->pci_controller); ++ ++ return 0; ++ ++err_put_hb_node: ++ of_node_put(rpc->pci_controller.of_node); ++ return err; ++} ++ ++int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ struct rt3883_pci_controller *rpc; ++ struct of_irq dev_irq; ++ int err; ++ int irq; ++ ++ rpc = pci_bus_to_rt3883_controller(dev->bus); ++ err = of_irq_map_pci(dev, &dev_irq); ++ if (err) { ++ pr_err("pci %s: unable to get irq map, err=%d\n", ++ pci_name((struct pci_dev *) dev), err); ++ return 0; ++ } ++ ++ irq = irq_create_of_mapping(dev_irq.controller, ++ dev_irq.specifier, ++ dev_irq.size); ++ ++ if (irq == 0) ++ pr_crit("pci %s: no irq found for pin %u\n", ++ pci_name((struct pci_dev *) dev), pin); ++ else ++ pr_info("pci %s: using irq %d for pin %u\n", ++ pci_name((struct pci_dev *) dev), irq, pin); ++ ++ return irq; ++} ++ ++int pcibios_plat_dev_init(struct pci_dev *dev) ++{ ++ return 0; ++} ++ ++static const struct of_device_id rt3883_pci_ids[] = { ++ { .compatible = "ralink,rt3883-pci" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt3883_pci_ids); ++ ++static struct platform_driver rt3883_pci_driver = { ++ .probe = rt3883_pci_probe, ++ .driver = { ++ .name = "rt3883-pci", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(rt3883_pci_ids), ++ }, ++}; ++ ++static int __init rt3883_pci_init(void) ++{ ++ return platform_driver_register(&rt3883_pci_driver); ++} ++ ++postcore_initcall(rt3883_pci_init); +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index c0ac93a..2fbe93c 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -21,6 +21,7 @@ choice + bool "RT3883" + select USB_ARCH_HAS_OHCI + select USB_ARCH_HAS_EHCI ++ select HW_HAS_PCI + + config SOC_MT7620 + bool "MT7620" +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0151-PCI-MIPS-adds-mt7620a-pcie-driver.patch b/target/linux/ramips/patches-3.9/0151-PCI-MIPS-adds-mt7620a-pcie-driver.patch new file mode 100644 index 0000000000..f5be94e793 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0151-PCI-MIPS-adds-mt7620a-pcie-driver.patch @@ -0,0 +1,409 @@ +From 06276ceb7de13d19c74cff597c5c62fe1f9fb556 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 18 May 2013 22:06:15 +0200 +Subject: [PATCH 151/164] PCI: MIPS: adds mt7620a pcie driver + +Signed-off-by: John Crispin +--- + arch/mips/pci/Makefile | 1 + + arch/mips/pci/pci-mt7620a.c | 363 +++++++++++++++++++++++++++++++++++++++++++ + arch/mips/ralink/Kconfig | 1 + + 3 files changed, 365 insertions(+) + create mode 100644 arch/mips/pci/pci-mt7620a.c + +diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile +index 3cbfd9b..025d3a7 100644 +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -43,6 +43,7 @@ obj-$(CONFIG_LANTIQ) += fixup-lantiq.o + obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o + obj-$(CONFIG_SOC_RT2880) += pci-rt2880.o + obj-$(CONFIG_SOC_RT3883) += pci-rt3883.o ++obj-$(CONFIG_SOC_MT7620) += pci-mt7620a.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o + obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o +diff --git a/arch/mips/pci/pci-mt7620a.c b/arch/mips/pci/pci-mt7620a.c +new file mode 100644 +index 0000000..271763c +--- /dev/null ++++ b/arch/mips/pci/pci-mt7620a.c +@@ -0,0 +1,363 @@ ++/* ++ * Ralink MT7620A SoC PCI support ++ * ++ * Copyright (C) 2007-2013 Bruce Chang ++ * Copyright (C) 2013 John Crispin ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define RALINK_PCI_MM_MAP_BASE 0x20000000 ++#define RALINK_PCI_IO_MAP_BASE 0x10160000 ++ ++#define RALINK_INT_PCIE0 4 ++#define RALINK_SYSTEM_CONTROL_BASE 0xb0000000 ++#define RALINK_SYSCFG1 0x14 ++#define RALINK_CLKCFG1 0x30 ++#define RALINK_GPIOMODE 0x60 ++#define RALINK_PCIE_CLK_GEN 0x7c ++#define RALINK_PCIE_CLK_GEN1 0x80 ++#define PCIEPHY0_CFG 0x90 ++#define PPLL_CFG1 0x9c ++#define PPLL_DRV 0xa0 ++#define RALINK_PCI_HOST_MODE_EN (1<<7) ++#define RALINK_PCIE_RC_MODE_EN (1<<8) ++#define RALINK_PCIE_RST (1<<23) ++#define RALINK_PCI_RST (1<<24) ++#define RALINK_PCI_CLK_EN (1<<19) ++#define RALINK_PCIE_CLK_EN (1<<21) ++#define PCI_SLOTx2 (1<<11) ++#define PCI_SLOTx1 (2<<11) ++#define PDRV_SW_SET (1<<31) ++#define LC_CKDRVPD_ (1<<19) ++ ++#define RALINK_PCI_CONFIG_ADDR 0x20 ++#define RALINK_PCI_CONFIG_DATA_VIRTUAL_REG 0x24 ++#define MEMORY_BASE 0x0 ++#define RALINK_PCIE0_RST (1<<26) ++#define RALINK_PCI_BASE 0xB0140000 ++#define RALINK_PCI_MEMBASE 0x28 ++#define RALINK_PCI_IOBASE 0x2C ++ ++#define RT6855_PCIE0_OFFSET 0x2000 ++ ++#define RALINK_PCI_PCICFG_ADDR 0x00 ++#define RALINK_PCI0_BAR0SETUP_ADDR 0x10 ++#define RALINK_PCI0_IMBASEBAR0_ADDR 0x18 ++#define RALINK_PCI0_ID 0x30 ++#define RALINK_PCI0_CLASS 0x34 ++#define RALINK_PCI0_SUBID 0x38 ++#define RALINK_PCI0_STATUS 0x50 ++#define RALINK_PCI_PCIMSK_ADDR 0x0C ++ ++#define RALINK_PCIE0_CLK_EN (1 << 26) ++ ++#define BUSY 0x80000000 ++#define WAITRETRY_MAX 10 ++#define WRITE_MODE (1UL << 23) ++#define DATA_SHIFT 0 ++#define ADDR_SHIFT 8 ++ ++ ++static void __iomem *bridge_base; ++static void __iomem *pcie_base; ++ ++static struct reset_control *rstpcie0; ++ ++static inline void bridge_w32(u32 val, unsigned reg) ++{ ++ iowrite32(val, bridge_base + reg); ++} ++ ++static inline u32 bridge_r32(unsigned reg) ++{ ++ return ioread32(bridge_base + reg); ++} ++ ++static inline void pcie_w32(u32 val, unsigned reg) ++{ ++ iowrite32(val, pcie_base + reg); ++} ++ ++static inline u32 pcie_r32(unsigned reg) ++{ ++ return ioread32(pcie_base + reg); ++} ++ ++static inline void pcie_m32(u32 clr, u32 set, unsigned reg) ++{ ++ u32 val = pcie_r32(reg); ++ val &= ~clr; ++ val |= set; ++ pcie_w32(val, reg); ++} ++ ++int wait_pciephy_busy(void) ++{ ++ unsigned long reg_value = 0x0, retry = 0; ++ ++ while (1) { ++ //reg_value = rareg(READMODE, PCIEPHY0_CFG, 0); ++ reg_value = pcie_r32(PCIEPHY0_CFG); ++ ++ if (reg_value & BUSY) ++ mdelay(100); ++ else ++ break; ++ if (retry++ > WAITRETRY_MAX){ ++ printk("PCIE-PHY retry failed.\n"); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static void pcie_phy(unsigned long addr, unsigned long val) ++{ ++ wait_pciephy_busy(); ++ pcie_w32(WRITE_MODE | (val << DATA_SHIFT) | (addr << ADDR_SHIFT), PCIEPHY0_CFG); ++ mdelay(1); ++ wait_pciephy_busy(); ++} ++ ++static int pci_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 * val) ++{ ++ unsigned int slot = PCI_SLOT(devfn); ++ u8 func = PCI_FUNC(devfn); ++ u32 address; ++ u32 data; ++ ++ address = (((where & 0xF00) >> 8) << 24) | (bus->number << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | 0x80000000; ++ bridge_w32(address, RALINK_PCI_CONFIG_ADDR); ++ data = bridge_r32(RALINK_PCI_CONFIG_DATA_VIRTUAL_REG); ++ ++ switch (size) { ++ case 1: ++ *val = (data >> ((where & 3) << 3)) & 0xff; ++ break; ++ case 2: ++ *val = (data >> ((where & 3) << 3)) & 0xffff; ++ break; ++ case 4: ++ *val = data; ++ break; ++ } ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++static int pci_config_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) ++{ ++ unsigned int slot = PCI_SLOT(devfn); ++ u8 func = PCI_FUNC(devfn); ++ u32 address; ++ u32 data; ++ ++ address = (((where & 0xF00) >> 8) << 24) | (bus->number << 16) | (slot << 11) | (func << 8) | (where & 0xfc) | 0x80000000; ++ bridge_w32(address, RALINK_PCI_CONFIG_ADDR); ++ data = bridge_r32(RALINK_PCI_CONFIG_DATA_VIRTUAL_REG); ++ ++ switch (size) { ++ case 1: ++ data = (data & ~(0xff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 2: ++ data = (data & ~(0xffff << ((where & 3) << 3))) | ++ (val << ((where & 3) << 3)); ++ break; ++ case 4: ++ data = val; ++ break; ++ } ++ ++ bridge_w32(data, RALINK_PCI_CONFIG_DATA_VIRTUAL_REG); ++ ++ return PCIBIOS_SUCCESSFUL; ++} ++ ++struct pci_ops mt7620a_pci_ops= { ++ .read = pci_config_read, ++ .write = pci_config_write, ++}; ++ ++static struct resource mt7620a_res_pci_mem1 = { ++ .name = "pci memory", ++ .start = RALINK_PCI_MM_MAP_BASE, ++ .end = (u32) ((RALINK_PCI_MM_MAP_BASE + (unsigned char *)0x0fffffff)), ++ .flags = IORESOURCE_MEM, ++}; ++static struct resource mt7620a_res_pci_io1 = { ++ .name = "pci io", ++ .start = RALINK_PCI_IO_MAP_BASE, ++ .end = (u32) ((RALINK_PCI_IO_MAP_BASE + (unsigned char *)0x0ffff)), ++ .flags = IORESOURCE_IO, ++}; ++ ++struct pci_controller mt7620a_controller = { ++ .pci_ops = &mt7620a_pci_ops, ++ .mem_resource = &mt7620a_res_pci_mem1, ++ .io_resource = &mt7620a_res_pci_io1, ++ .mem_offset = 0x00000000UL, ++ .io_offset = 0x00000000UL, ++ .io_map_base = 0xa0000000, ++}; ++ ++static int mt7620a_pci_probe(struct platform_device *pdev) ++{ ++ struct resource *bridge_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ struct resource *pcie_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ ++ rstpcie0 = devm_reset_control_get(&pdev->dev, "pcie0"); ++ if (IS_ERR(rstpcie0)) ++ return PTR_ERR(rstpcie0); ++ ++ bridge_base = devm_request_and_ioremap(&pdev->dev, bridge_res); ++ if (!bridge_base) ++ return -ENOMEM; ++ ++ pcie_base = devm_request_and_ioremap(&pdev->dev, pcie_res); ++ if (!pcie_base) ++ return -ENOMEM; ++ ++ iomem_resource.start = 0; ++ iomem_resource.end= ~0; ++ ioport_resource.start= 0; ++ ioport_resource.end = ~0; ++ ++ /* PCIE: bypass PCIe DLL */ ++ pcie_phy(0x0, 0x80); ++ pcie_phy(0x1, 0x04); ++ /* PCIE: Elastic buffer control */ ++ pcie_phy(0x68, 0xB4); ++ ++ reset_control_assert(rstpcie0); ++ rt_sysc_m32(RALINK_PCIE0_CLK_EN, 0, RALINK_CLKCFG1); ++ rt_sysc_m32(1<<19, 1<<31, PPLL_DRV); ++ rt_sysc_m32(0x3 << 16, 0, RALINK_GPIOMODE); ++ ++ reset_control_deassert(rstpcie0); ++ rt_sysc_m32(0, RALINK_PCIE0_CLK_EN, RALINK_CLKCFG1); ++ ++ mdelay(100); ++ ++ if (!(rt_sysc_r32(PPLL_CFG1) & 1<<23)) { ++ printk("MT7620 PPLL unlock\n"); ++ reset_control_assert(rstpcie0); ++ rt_sysc_m32(BIT(26), 0, RALINK_CLKCFG1); ++ return 0; ++ } ++ rt_sysc_m32((0x1<<18) | (0x1<<17), (0x1 << 19) | (0x1 << 31), PPLL_DRV); ++ ++ mdelay(100); ++ reset_control_assert(rstpcie0); ++ rt_sysc_m32(0x30, 2 << 4, RALINK_SYSCFG1); ++ ++ rt_sysc_m32(~0x7fffffff, 0x80000000, RALINK_PCIE_CLK_GEN); ++ rt_sysc_m32(~0x80ffffff, 0xa << 24, RALINK_PCIE_CLK_GEN1); ++ ++ mdelay(50); ++ reset_control_deassert(rstpcie0); ++ pcie_m32(BIT(1), 0, RALINK_PCI_PCICFG_ADDR); ++ mdelay(100); ++ ++ if (( pcie_r32(RALINK_PCI0_STATUS) & 0x1) == 0) { ++ reset_control_assert(rstpcie0); ++ rt_sysc_m32(RALINK_PCIE0_CLK_EN, 0, RALINK_CLKCFG1); ++ rt_sysc_m32(LC_CKDRVPD_, PDRV_SW_SET, PPLL_DRV); ++ printk("PCIE0 no card, disable it(RST&CLK)\n"); ++ } ++ ++ bridge_w32(0xffffffff, RALINK_PCI_MEMBASE); ++ bridge_w32(RALINK_PCI_IO_MAP_BASE, RALINK_PCI_IOBASE); ++ ++ pcie_w32(0x7FFF0000, RALINK_PCI0_BAR0SETUP_ADDR); ++ pcie_w32(MEMORY_BASE, RALINK_PCI0_IMBASEBAR0_ADDR); ++ pcie_w32(0x08021814, RALINK_PCI0_ID); ++ pcie_w32(0x06040001, RALINK_PCI0_CLASS); ++ pcie_w32(0x28801814, RALINK_PCI0_SUBID); ++ pcie_m32(0, BIT(20), RALINK_PCI_PCIMSK_ADDR); ++ ++ register_pci_controller(&mt7620a_controller); ++ ++ return 0; ++} ++ ++int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ const struct resource *res; ++ u16 cmd; ++ u32 val; ++ int i, irq = 0; ++ ++ if ((dev->bus->number == 0) && (slot == 0)) { ++ pcie_w32(0x7FFF0001, RALINK_PCI0_BAR0SETUP_ADDR); //open 7FFF:2G; ENABLE ++ pci_config_write(dev->bus, 0, PCI_BASE_ADDRESS_0, 4, MEMORY_BASE); ++ pci_config_read(dev->bus, 0, PCI_BASE_ADDRESS_0, 4, &val); ++ } else if ((dev->bus->number == 1) && (slot == 0x0)) { ++ irq = RALINK_INT_PCIE0; ++ } else { ++ printk("bus=0x%x, slot = 0x%x\n", dev->bus->number, slot); ++ return 0; ++ } ++ ++ for (i = 0; i < 6; i++) { ++ res = &dev->resource[i]; ++ } ++ ++ pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x14); //configure cache line size 0x14 ++ pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xFF); //configure latency timer 0x10 ++ pci_read_config_word(dev, PCI_COMMAND, &cmd); ++ ++ // FIXME ++ cmd = cmd | PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY; ++ pci_write_config_word(dev, PCI_COMMAND, cmd); ++ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); ++ //pci_write_config_byte(dev, PCI_INTERRUPT_PIN, dev->irq); ++ ++ return irq; ++} ++ ++int pcibios_plat_dev_init(struct pci_dev *dev) ++{ ++ return 0; ++} ++ ++static const struct of_device_id mt7620a_pci_ids[] = { ++ { .compatible = "ralink,mt7620a-pci" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, mt7620a_pci_ids); ++ ++static struct platform_driver mt7620a_pci_driver = { ++ .probe = mt7620a_pci_probe, ++ .driver = { ++ .name = "mt7620a-pci", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(mt7620a_pci_ids), ++ }, ++}; ++ ++static int __init mt7620a_pci_init(void) ++{ ++ return platform_driver_register(&mt7620a_pci_driver); ++} ++ ++arch_initcall(mt7620a_pci_init); +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index 2fbe93c..c8d5b6c 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -26,6 +26,7 @@ choice + config SOC_MT7620 + bool "MT7620" + select CLKEVT_RT3352 ++ select HW_HAS_PCI + + endchoice + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0152-watchdog-adds-ralink-wdt.patch b/target/linux/ramips/patches-3.9/0152-watchdog-adds-ralink-wdt.patch new file mode 100644 index 0000000000..d70d2b28c0 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0152-watchdog-adds-ralink-wdt.patch @@ -0,0 +1,274 @@ +From 307ae4eb2504dcc9663a0452a42c38669b8ba815 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Apr 2013 23:23:07 +0200 +Subject: [PATCH 152/164] watchdog: adds ralink wdt + +Adds the watchdog driver for ralink SoC. + +Signed-off-by: John Crispin +--- + arch/mips/ralink/mt7620.c | 1 + + drivers/watchdog/Kconfig | 7 ++ + drivers/watchdog/Makefile | 1 + + drivers/watchdog/rt2880_wdt.c | 208 +++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 217 insertions(+) + create mode 100644 drivers/watchdog/rt2880_wdt.c + +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index 08c96db6..4956d96 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -182,6 +182,7 @@ void __init ralink_clk_init(void) + + ralink_clk_add("cpu", cpu_rate); + ralink_clk_add("10000100.timer", 40000000); ++ ralink_clk_add("10000120.watchdog", 40000000); + ralink_clk_add("10000500.uart", 40000000); + ralink_clk_add("10000b00.spi", 40000000); + ralink_clk_add("10000c00.uartlite", 40000000); +diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig +index e89fc31..5e34760 100644 +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -1104,6 +1104,13 @@ config LANTIQ_WDT + help + Hardware driver for the Lantiq SoC Watchdog Timer. + ++config RALINK_WDT ++ tristate "Ralink SoC watchdog" ++ select WATCHDOG_CORE ++ depends on RALINK ++ help ++ Hardware driver for the Ralink SoC Watchdog Timer. ++ + # PARISC Architecture + + # POWERPC Architecture +diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile +index a300b94..2681e3d 100644 +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -134,6 +134,7 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o + obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o + octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o + obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o ++obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o + + # PARISC Architecture + +diff --git a/drivers/watchdog/rt2880_wdt.c b/drivers/watchdog/rt2880_wdt.c +new file mode 100644 +index 0000000..3df65a4 +--- /dev/null ++++ b/drivers/watchdog/rt2880_wdt.c +@@ -0,0 +1,207 @@ ++/* ++ * Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer ++ * ++ * Copyright (C) 2011 Gabor Juhos ++ * Copyright (C) 2013 John Crispin ++ * ++ * This driver was based on: drivers/watchdog/softdog.c ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define SYSC_RSTSTAT 0x38 ++#define WDT_RST_CAUSE BIT(1) ++ ++#define RALINK_WDT_TIMEOUT 30 ++#define RALINK_WDT_PRESCALE 65536 ++ ++#define TIMER_REG_TMR1LOAD 0x00 ++#define TIMER_REG_TMR1CTL 0x08 ++ ++#define TMRSTAT_TMR1RST BIT(5) ++ ++#define TMR1CTL_ENABLE BIT(7) ++#define TMR1CTL_MODE_SHIFT 4 ++#define TMR1CTL_MODE_MASK 0x3 ++#define TMR1CTL_MODE_FREE_RUNNING 0x0 ++#define TMR1CTL_MODE_PERIODIC 0x1 ++#define TMR1CTL_MODE_TIMEOUT 0x2 ++#define TMR1CTL_MODE_WDT 0x3 ++#define TMR1CTL_PRESCALE_MASK 0xf ++#define TMR1CTL_PRESCALE_65536 0xf ++ ++static struct clk *rt288x_wdt_clk; ++static unsigned long rt288x_wdt_freq; ++static void __iomem *rt288x_wdt_base; ++ ++static bool nowayout = WATCHDOG_NOWAYOUT; ++module_param(nowayout, bool, 0); ++MODULE_PARM_DESC(nowayout, ++ "Watchdog cannot be stopped once started (default=" ++ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); ++ ++static inline void rt_wdt_w32(unsigned reg, u32 val) ++{ ++ iowrite32(val, rt288x_wdt_base + reg); ++} ++ ++static inline u32 rt_wdt_r32(unsigned reg) ++{ ++ return ioread32(rt288x_wdt_base + reg); ++} ++ ++static int rt288x_wdt_ping(struct watchdog_device *w) ++{ ++ rt_wdt_w32(TIMER_REG_TMR1LOAD, w->timeout * rt288x_wdt_freq); ++ ++ return 0; ++} ++ ++static int rt288x_wdt_start(struct watchdog_device *w) ++{ ++ u32 t; ++ ++ t = rt_wdt_r32(TIMER_REG_TMR1CTL); ++ t &= ~(TMR1CTL_MODE_MASK << TMR1CTL_MODE_SHIFT | ++ TMR1CTL_PRESCALE_MASK); ++ t |= (TMR1CTL_MODE_WDT << TMR1CTL_MODE_SHIFT | ++ TMR1CTL_PRESCALE_65536); ++ rt_wdt_w32(TIMER_REG_TMR1CTL, t); ++ ++ rt288x_wdt_ping(w); ++ ++ t = rt_wdt_r32(TIMER_REG_TMR1CTL); ++ t |= TMR1CTL_ENABLE; ++ rt_wdt_w32(TIMER_REG_TMR1CTL, t); ++ ++ return 0; ++} ++ ++static int rt288x_wdt_stop(struct watchdog_device *w) ++{ ++ u32 t; ++ ++ rt288x_wdt_ping(w); ++ ++ t = rt_wdt_r32(TIMER_REG_TMR1CTL); ++ t &= ~TMR1CTL_ENABLE; ++ rt_wdt_w32(TIMER_REG_TMR1CTL, t); ++ ++ return 0; ++} ++ ++static int rt288x_wdt_set_timeout(struct watchdog_device *w, unsigned int t) ++{ ++ w->timeout = t; ++ rt288x_wdt_ping(w); ++ ++ return 0; ++} ++ ++static int rt288x_wdt_bootcause(void) ++{ ++ if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE) ++ return WDIOF_CARDRESET; ++ ++ return 0; ++} ++ ++static struct watchdog_info rt288x_wdt_info = { ++ .identity = "Ralink Watchdog", ++ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, ++}; ++ ++static struct watchdog_ops rt288x_wdt_ops = { ++ .owner = THIS_MODULE, ++ .start = rt288x_wdt_start, ++ .stop = rt288x_wdt_stop, ++ .ping = rt288x_wdt_ping, ++ .set_timeout = rt288x_wdt_set_timeout, ++}; ++ ++static struct watchdog_device rt288x_wdt_dev = { ++ .info = &rt288x_wdt_info, ++ .ops = &rt288x_wdt_ops, ++ .min_timeout = 1, ++}; ++ ++static int rt288x_wdt_probe(struct platform_device *pdev) ++{ ++ struct resource *res; ++ int ret; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ rt288x_wdt_base = devm_request_and_ioremap(&pdev->dev, res); ++ if (IS_ERR(rt288x_wdt_base)) ++ return PTR_ERR(rt288x_wdt_base); ++ ++ rt288x_wdt_clk = devm_clk_get(&pdev->dev, NULL); ++ if (IS_ERR(rt288x_wdt_clk)) ++ return PTR_ERR(rt288x_wdt_clk); ++ ++ device_reset(&pdev->dev); ++ ++ rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE; ++ ++ rt288x_wdt_dev.dev = &pdev->dev; ++ rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause(); ++ ++ rt288x_wdt_dev.timeout = rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq); ++ ++ watchdog_set_nowayout(&rt288x_wdt_dev, nowayout); ++ ++ ret = watchdog_register_device(&rt288x_wdt_dev); ++ if (!ret) ++ dev_info(&pdev->dev, "Initialized\n"); ++ ++ return 0; ++} ++ ++static int rt288x_wdt_remove(struct platform_device *pdev) ++{ ++ watchdog_unregister_device(&rt288x_wdt_dev); ++ ++ return 0; ++} ++ ++static void rt288x_wdt_shutdown(struct platform_device *pdev) ++{ ++ rt288x_wdt_stop(&rt288x_wdt_dev); ++} ++ ++static const struct of_device_id rt288x_wdt_match[] = { ++ { .compatible = "ralink,rt2880-wdt" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, rt288x_wdt_match); ++ ++static struct platform_driver rt288x_wdt_driver = { ++ .probe = rt288x_wdt_probe, ++ .remove = rt288x_wdt_remove, ++ .shutdown = rt288x_wdt_shutdown, ++ .driver = { ++ .name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .of_match_table = rt288x_wdt_match, ++ }, ++}; ++ ++module_platform_driver(rt288x_wdt_driver); ++ ++MODULE_DESCRIPTION("MediaTek/Ralink RT288x/RT3xxx hardware watchdog driver"); ++MODULE_AUTHOR("Gabor Juhos +Date: Mon, 29 Apr 2013 14:40:43 +0200 +Subject: [PATCH 153/164] i2c: MIPS: adds ralink I2C driver + +Signed-off-by: John Crispin +--- + .../devicetree/bindings/i2c/i2c-ralink.txt | 27 ++ + drivers/i2c/busses/Kconfig | 4 + + drivers/i2c/busses/Makefile | 1 + + drivers/i2c/busses/i2c-ralink.c | 274 ++++++++++++++++++++ + 4 files changed, 306 insertions(+) + create mode 100644 Documentation/devicetree/bindings/i2c/i2c-ralink.txt + create mode 100644 drivers/i2c/busses/i2c-ralink.c + +diff --git a/Documentation/devicetree/bindings/i2c/i2c-ralink.txt b/Documentation/devicetree/bindings/i2c/i2c-ralink.txt +new file mode 100644 +index 0000000..8fa8ac3 +--- /dev/null ++++ b/Documentation/devicetree/bindings/i2c/i2c-ralink.txt +@@ -0,0 +1,27 @@ ++I2C for Ralink platforms ++ ++Required properties : ++- compatible : Must be "link,rt3052-i2c" ++- reg: physical base address of the controller and length of memory mapped ++ region. ++- #address-cells = <1>; ++- #size-cells = <0>; ++ ++Optional properties: ++- Child nodes conforming to i2c bus binding ++ ++Example : ++ ++palmbus@10000000 { ++ i2c@900 { ++ compatible = "link,rt3052-i2c"; ++ reg = <0x900 0x100>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ ++ hwmon@4b { ++ compatible = "national,lm92"; ++ reg = <0x4b>; ++ }; ++ }; ++}; +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index adfee98..ec925ae 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -628,6 +628,10 @@ config I2C_PXA_SLAVE + is necessary for systems where the PXA may be a target on the + I2C bus. + ++config I2C_RALINK ++ tristate "Ralink I2C Controller" ++ select OF_I2C ++ + config HAVE_S3C2410_I2C + bool + help +diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile +index 8f4fc23..7e39a13 100644 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -62,6 +62,7 @@ obj-$(CONFIG_I2C_PNX) += i2c-pnx.o + obj-$(CONFIG_I2C_PUV3) += i2c-puv3.o + obj-$(CONFIG_I2C_PXA) += i2c-pxa.o + obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o ++obj-$(CONFIG_I2C_RALINK) += i2c-ralink.o + obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o + obj-$(CONFIG_I2C_S6000) += i2c-s6000.o + obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o +diff --git a/drivers/i2c/busses/i2c-ralink.c b/drivers/i2c/busses/i2c-ralink.c +new file mode 100644 +index 0000000..b5abf0f +--- /dev/null ++++ b/drivers/i2c/busses/i2c-ralink.c +@@ -0,0 +1,274 @@ ++/* ++ * drivers/i2c/busses/i2c-ralink.c ++ * ++ * Copyright (C) 2013 Steven Liu ++ * ++ * This software is licensed under the terms of the GNU General Public ++ * License version 2, as published by the Free Software Foundation, and ++ * may be copied, distributed, and modified under those terms. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define REG_CONFIG_REG 0x00 ++#define REG_CLKDIV_REG 0x04 ++#define REG_DEVADDR_REG 0x08 ++#define REG_ADDR_REG 0x0C ++#define REG_DATAOUT_REG 0x10 ++#define REG_DATAIN_REG 0x14 ++#define REG_STATUS_REG 0x18 ++#define REG_STARTXFR_REG 0x1C ++#define REG_BYTECNT_REG 0x20 ++ ++#define I2C_STARTERR BIT(4) ++#define I2C_ACKERR BIT(3) ++#define I2C_DATARDY BIT(2) ++#define I2C_SDOEMPTY BIT(1) ++#define I2C_BUSY BIT(0) ++ ++#define I2C_DEVADLEN_7 (6 << 2) ++#define I2C_ADDRDIS BIT(1) ++ ++#define I2C_RETRY 0x400 ++ ++#define CLKDIV_VALUE 600 ++ ++#define READ_CMD 0x01 ++#define WRITE_CMD 0x00 ++#define READ_BLOCK 64 ++ ++static void __iomem *membase; ++static struct i2c_adapter *adapter; ++ ++static void rt_i2c_w32(u32 val, unsigned reg) ++{ ++ iowrite32(val, membase + reg); ++} ++ ++static u32 rt_i2c_r32(unsigned reg) ++{ ++ return ioread32(membase + reg); ++} ++ ++static inline int rt_i2c_wait_rx_done(void) ++{ ++ int retries = I2C_RETRY; ++ ++ do { ++ if (!retries--) ++ break; ++ } while(!(rt_i2c_r32(REG_STATUS_REG) & I2C_DATARDY)); ++ ++ return (retries < 0); ++} ++ ++static inline int rt_i2c_wait_idle(void) ++{ ++ int retries = I2C_RETRY; ++ ++ do { ++ if (!retries--) ++ break; ++ } while(rt_i2c_r32(REG_STATUS_REG) & I2C_BUSY); ++ ++ return (retries < 0); ++} ++ ++static inline int rt_i2c_wait_tx_done(void) ++{ ++ int retries = I2C_RETRY; ++ ++ do { ++ if (!retries--) ++ break; ++ } while(!(rt_i2c_r32(REG_STATUS_REG) & I2C_SDOEMPTY)); ++ ++ return (retries < 0); ++} ++ ++static int rt_i2c_handle_msg(struct i2c_adapter *a, struct i2c_msg* msg) ++{ ++ int i = 0, j = 0, pos = 0; ++ int nblock = msg->len / READ_BLOCK; ++ int rem = msg->len % READ_BLOCK; ++ ++ if (msg->flags & I2C_M_TEN) { ++ printk("10 bits addr not supported\n"); ++ return -EINVAL; ++ } ++ ++ if (msg->flags & I2C_M_RD) { ++ for (i = 0; i < nblock; i++) { ++ rt_i2c_wait_idle(); ++ rt_i2c_w32(READ_BLOCK - 1, REG_BYTECNT_REG); ++ rt_i2c_w32(READ_CMD, REG_STARTXFR_REG); ++ for (j = 0; j < READ_BLOCK; j++) { ++ if (rt_i2c_wait_rx_done()) ++ return -1; ++ msg->buf[pos++] = rt_i2c_r32(REG_DATAIN_REG); ++ } ++ } ++ ++ rt_i2c_wait_idle(); ++ rt_i2c_w32(rem - 1, REG_BYTECNT_REG); ++ rt_i2c_w32(READ_CMD, REG_STARTXFR_REG); ++ for (i = 0; i < rem; i++) { ++ if (rt_i2c_wait_rx_done()) ++ return -1; ++ msg->buf[pos++] = rt_i2c_r32(REG_DATAIN_REG); ++ } ++ } else { ++ rt_i2c_wait_idle(); ++ rt_i2c_w32(msg->len - 1, REG_BYTECNT_REG); ++ rt_i2c_w32(msg->buf[0], REG_DATAOUT_REG); ++ rt_i2c_w32(WRITE_CMD, REG_STARTXFR_REG); ++ for (i = 1; i < msg->len; i++) { ++ rt_i2c_w32(msg->buf[i], REG_DATAOUT_REG); ++ if (rt_i2c_wait_tx_done()) ++ return -1; ++ } ++ } ++ ++ return msg->len; ++} ++ ++static int rt_i2c_master_xfer(struct i2c_adapter *a, struct i2c_msg *m, int n) ++{ ++ int i = 0; ++ int ret = 0; ++ ++ if (rt_i2c_wait_idle()) { ++ printk("i2c transfer failed\n"); ++ return 0; ++ } ++ ++ device_reset(a->dev.parent); ++ ++ rt_i2c_w32(m->addr, REG_DEVADDR_REG); ++ rt_i2c_w32(I2C_DEVADLEN_7 | I2C_ADDRDIS, REG_CONFIG_REG); ++ rt_i2c_w32(CLKDIV_VALUE, REG_CLKDIV_REG); ++ ++ for (i = 0; i < n && !ret; i++) ++ ret = rt_i2c_handle_msg(a, &m[i]); ++ ++ if (ret) { ++ printk("i2c transfer failed\n"); ++ return 0; ++ } ++ ++ return n; ++} ++ ++static u32 rt_i2c_func(struct i2c_adapter *a) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; ++} ++ ++static const struct i2c_algorithm rt_i2c_algo = { ++ .master_xfer = rt_i2c_master_xfer, ++ .functionality = rt_i2c_func, ++}; ++ ++static int rt_i2c_probe(struct platform_device *pdev) ++{ ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ int ret; ++ ++ if (!res) { ++ dev_err(&pdev->dev, "no memory resource found\n"); ++ return -ENODEV; ++ } ++ ++ adapter = devm_kzalloc(&pdev->dev, sizeof(struct i2c_adapter), GFP_KERNEL); ++ if (!adapter) { ++ dev_err(&pdev->dev, "failed to allocate i2c_adapter\n"); ++ return -ENOMEM; ++ } ++ ++ membase = devm_request_and_ioremap(&pdev->dev, res); ++ if (IS_ERR(membase)) ++ return PTR_ERR(membase); ++ ++ strlcpy(adapter->name, dev_name(&pdev->dev), sizeof(adapter->name)); ++ adapter->owner = THIS_MODULE; ++ adapter->nr = pdev->id; ++ adapter->timeout = HZ; ++ adapter->algo = &rt_i2c_algo; ++ adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; ++ adapter->dev.parent = &pdev->dev; ++ adapter->dev.of_node = pdev->dev.of_node; ++ ++ ret = i2c_add_numbered_adapter(adapter); ++ if (ret) ++ return ret; ++ ++ of_i2c_register_devices(adapter); ++ ++ platform_set_drvdata(pdev, adapter); ++ ++ dev_info(&pdev->dev, "loaded\n"); ++ ++ return 0; ++} ++ ++static int rt_i2c_remove(struct platform_device *pdev) ++{ ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++static const struct of_device_id i2c_rt_dt_ids[] = { ++ { .compatible = "ralink,rt2880-i2c", }, ++ { /* sentinel */ } ++}; ++ ++MODULE_DEVICE_TABLE(of, i2c_rt_dt_ids); ++ ++static struct platform_driver rt_i2c_driver = { ++ .probe = rt_i2c_probe, ++ .remove = rt_i2c_remove, ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "i2c-ralink", ++ .of_match_table = i2c_rt_dt_ids, ++ }, ++}; ++ ++static int __init i2c_rt_init (void) ++{ ++ return platform_driver_register(&rt_i2c_driver); ++} ++subsys_initcall(i2c_rt_init); ++ ++static void __exit i2c_rt_exit (void) ++{ ++ platform_driver_unregister(&rt_i2c_driver); ++} ++ ++module_exit (i2c_rt_exit); ++ ++MODULE_AUTHOR("Steven Liu "); ++MODULE_DESCRIPTION("Ralink I2c host driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:Ralink-I2C"); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0154-reset-Add-reset-controller-API.patch b/target/linux/ramips/patches-3.9/0154-reset-Add-reset-controller-API.patch new file mode 100644 index 0000000000..900f0a2e46 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0154-reset-Add-reset-controller-API.patch @@ -0,0 +1,554 @@ +From d6cfbdfa001891894efe078a49ad82ac8a932dbb Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 20 May 2013 15:42:01 +0200 +Subject: [PATCH 154/164] reset: Add reset controller API + +backport from v3.10-rc1 +61fc41317666be400802ac793f47de816ef7bd57 +6034bb22d8387708075c083385e5d2e1072a4f33 +4e11f848c65b1c87782cb232a6e3b47a9d4c1f98 + +This adds a simple API for devices to request being reset +by separate reset controller hardware and implements the +reset signal device tree binding. + +Signed-off-by: Philipp Zabel +Reviewed-by: Stephen Warren +Reviewed-by: Shawn Guo +Reviewed-by: Marek Vasut +Reviewed-by: Pavel Machek +--- + Documentation/devicetree/bindings/reset/reset.txt | 75 ++++++ + drivers/Kconfig | 2 + + drivers/Makefile | 3 + + drivers/reset/Kconfig | 13 + + drivers/reset/Makefile | 1 + + drivers/reset/core.c | 297 +++++++++++++++++++++ + include/linux/reset-controller.h | 51 ++++ + include/linux/reset.h | 17 ++ + 8 files changed, 459 insertions(+) + create mode 100644 Documentation/devicetree/bindings/reset/reset.txt + create mode 100644 drivers/reset/Kconfig + create mode 100644 drivers/reset/Makefile + create mode 100644 drivers/reset/core.c + create mode 100644 include/linux/reset-controller.h + create mode 100644 include/linux/reset.h + +diff --git a/Documentation/devicetree/bindings/reset/reset.txt b/Documentation/devicetree/bindings/reset/reset.txt +new file mode 100644 +index 0000000..31db6ff +--- /dev/null ++++ b/Documentation/devicetree/bindings/reset/reset.txt +@@ -0,0 +1,75 @@ ++= Reset Signal Device Tree Bindings = ++ ++This binding is intended to represent the hardware reset signals present ++internally in most IC (SoC, FPGA, ...) designs. Reset signals for whole ++standalone chips are most likely better represented as GPIOs, although there ++are likely to be exceptions to this rule. ++ ++Hardware blocks typically receive a reset signal. This signal is generated by ++a reset provider (e.g. power management or clock module) and received by a ++reset consumer (the module being reset, or a module managing when a sub- ++ordinate module is reset). This binding exists to represent the provider and ++consumer, and provide a way to couple the two together. ++ ++A reset signal is represented by the phandle of the provider, plus a reset ++specifier - a list of DT cells that represents the reset signal within the ++provider. The length (number of cells) and semantics of the reset specifier ++are dictated by the binding of the reset provider, although common schemes ++are described below. ++ ++A word on where to place reset signal consumers in device tree: It is possible ++in hardware for a reset signal to affect multiple logically separate HW blocks ++at once. In this case, it would be unwise to represent this reset signal in ++the DT node of each affected HW block, since if activated, an unrelated block ++may be reset. Instead, reset signals should be represented in the DT node ++where it makes most sense to control it; this may be a bus node if all ++children of the bus are affected by the reset signal, or an individual HW ++block node for dedicated reset signals. The intent of this binding is to give ++appropriate software access to the reset signals in order to manage the HW, ++rather than to slavishly enumerate the reset signal that affects each HW ++block. ++ ++= Reset providers = ++ ++Required properties: ++#reset-cells: Number of cells in a reset specifier; Typically 0 for nodes ++ with a single reset output and 1 for nodes with multiple ++ reset outputs. ++ ++For example: ++ ++ rst: reset-controller { ++ #reset-cells = <1>; ++ }; ++ ++= Reset consumers = ++ ++Required properties: ++resets: List of phandle and reset specifier pairs, one pair ++ for each reset signal that affects the device, or that the ++ device manages. Note: if the reset provider specifies '0' for ++ #reset-cells, then only the phandle portion of the pair will ++ appear. ++ ++Optional properties: ++reset-names: List of reset signal name strings sorted in the same order as ++ the resets property. Consumers drivers will use reset-names to ++ match reset signal names with reset specifiers. ++ ++For example: ++ ++ device { ++ resets = <&rst 20>; ++ reset-names = "reset"; ++ }; ++ ++This represents a device with a single reset signal named "reset". ++ ++ bus { ++ resets = <&rst 10> <&rst 11> <&rst 12> <&rst 11>; ++ reset-names = "i2s1", "i2s2", "dma", "mixer"; ++ }; ++ ++This represents a bus that controls the reset signal of each of four sub- ++ordinate devices. Consider for example a bus that fails to operate unless no ++child device has reset asserted. +diff --git a/drivers/Kconfig b/drivers/Kconfig +index 202fa6d..847f8e3 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -162,4 +162,6 @@ source "drivers/irqchip/Kconfig" + + source "drivers/ipack/Kconfig" + ++source "drivers/reset/Kconfig" ++ + endmenu +diff --git a/drivers/Makefile b/drivers/Makefile +index dce39a9..1a64c4c 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -37,6 +37,9 @@ obj-$(CONFIG_XEN) += xen/ + # regulators early, since some subsystems rely on them to initialize + obj-$(CONFIG_REGULATOR) += regulator/ + ++# reset controllers early, since gpu drivers might rely on them to initialize ++obj-$(CONFIG_RESET_CONTROLLER) += reset/ ++ + # tty/ comes before char/ so that the VT console is the boot-time + # default. + obj-y += tty/ +diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig +new file mode 100644 +index 0000000..c9d04f7 +--- /dev/null ++++ b/drivers/reset/Kconfig +@@ -0,0 +1,13 @@ ++config ARCH_HAS_RESET_CONTROLLER ++ bool ++ ++menuconfig RESET_CONTROLLER ++ bool "Reset Controller Support" ++ default y if ARCH_HAS_RESET_CONTROLLER ++ help ++ Generic Reset Controller support. ++ ++ This framework is designed to abstract reset handling of devices ++ via GPIOs or SoC-internal reset controller modules. ++ ++ If unsure, say no. +diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile +new file mode 100644 +index 0000000..1e2d83f +--- /dev/null ++++ b/drivers/reset/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_RESET_CONTROLLER) += core.o +diff --git a/drivers/reset/core.c b/drivers/reset/core.c +new file mode 100644 +index 0000000..d1b6089 +--- /dev/null ++++ b/drivers/reset/core.c +@@ -0,0 +1,297 @@ ++/* ++ * Reset Controller framework ++ * ++ * Copyright 2013 Philipp Zabel, Pengutronix ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static DEFINE_MUTEX(reset_controller_list_mutex); ++static LIST_HEAD(reset_controller_list); ++ ++/** ++ * struct reset_control - a reset control ++ * @rcdev: a pointer to the reset controller device ++ * this reset control belongs to ++ * @id: ID of the reset controller in the reset ++ * controller device ++ */ ++struct reset_control { ++ struct reset_controller_dev *rcdev; ++ struct device *dev; ++ unsigned int id; ++}; ++ ++/** ++ * of_reset_simple_xlate - translate reset_spec to the reset line number ++ * @rcdev: a pointer to the reset controller device ++ * @reset_spec: reset line specifier as found in the device tree ++ * @flags: a flags pointer to fill in (optional) ++ * ++ * This simple translation function should be used for reset controllers ++ * with 1:1 mapping, where reset lines can be indexed by number without gaps. ++ */ ++int of_reset_simple_xlate(struct reset_controller_dev *rcdev, ++ const struct of_phandle_args *reset_spec) ++{ ++ if (WARN_ON(reset_spec->args_count != rcdev->of_reset_n_cells)) ++ return -EINVAL; ++ ++ if (reset_spec->args[0] >= rcdev->nr_resets) ++ return -EINVAL; ++ ++ return reset_spec->args[0]; ++} ++EXPORT_SYMBOL_GPL(of_reset_simple_xlate); ++ ++/** ++ * reset_controller_register - register a reset controller device ++ * @rcdev: a pointer to the initialized reset controller device ++ */ ++int reset_controller_register(struct reset_controller_dev *rcdev) ++{ ++ if (!rcdev->of_xlate) { ++ rcdev->of_reset_n_cells = 1; ++ rcdev->of_xlate = of_reset_simple_xlate; ++ } ++ ++ mutex_lock(&reset_controller_list_mutex); ++ list_add(&rcdev->list, &reset_controller_list); ++ mutex_unlock(&reset_controller_list_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(reset_controller_register); ++ ++/** ++ * reset_controller_unregister - unregister a reset controller device ++ * @rcdev: a pointer to the reset controller device ++ */ ++void reset_controller_unregister(struct reset_controller_dev *rcdev) ++{ ++ mutex_lock(&reset_controller_list_mutex); ++ list_del(&rcdev->list); ++ mutex_unlock(&reset_controller_list_mutex); ++} ++EXPORT_SYMBOL_GPL(reset_controller_unregister); ++ ++/** ++ * reset_control_reset - reset the controlled device ++ * @rstc: reset controller ++ */ ++int reset_control_reset(struct reset_control *rstc) ++{ ++ if (rstc->rcdev->ops->reset) ++ return rstc->rcdev->ops->reset(rstc->rcdev, rstc->id); ++ ++ return -ENOSYS; ++} ++EXPORT_SYMBOL_GPL(reset_control_reset); ++ ++/** ++ * reset_control_assert - asserts the reset line ++ * @rstc: reset controller ++ */ ++int reset_control_assert(struct reset_control *rstc) ++{ ++ if (rstc->rcdev->ops->assert) ++ return rstc->rcdev->ops->assert(rstc->rcdev, rstc->id); ++ ++ return -ENOSYS; ++} ++EXPORT_SYMBOL_GPL(reset_control_assert); ++ ++/** ++ * reset_control_deassert - deasserts the reset line ++ * @rstc: reset controller ++ */ ++int reset_control_deassert(struct reset_control *rstc) ++{ ++ if (rstc->rcdev->ops->deassert) ++ return rstc->rcdev->ops->deassert(rstc->rcdev, rstc->id); ++ ++ return -ENOSYS; ++} ++EXPORT_SYMBOL_GPL(reset_control_deassert); ++ ++/** ++ * reset_control_get - Lookup and obtain a reference to a reset controller. ++ * @dev: device to be reset by the controller ++ * @id: reset line name ++ * ++ * Returns a struct reset_control or IS_ERR() condition containing errno. ++ * ++ * Use of id names is optional. ++ */ ++struct reset_control *reset_control_get(struct device *dev, const char *id) ++{ ++ struct reset_control *rstc = ERR_PTR(-EPROBE_DEFER); ++ struct reset_controller_dev *r, *rcdev; ++ struct of_phandle_args args; ++ int index = 0; ++ int rstc_id; ++ int ret; ++ ++ if (!dev) ++ return ERR_PTR(-EINVAL); ++ ++ if (id) ++ index = of_property_match_string(dev->of_node, ++ "reset-names", id); ++ ret = of_parse_phandle_with_args(dev->of_node, "resets", "#reset-cells", ++ index, &args); ++ if (ret) ++ return ERR_PTR(ret); ++ ++ mutex_lock(&reset_controller_list_mutex); ++ rcdev = NULL; ++ list_for_each_entry(r, &reset_controller_list, list) { ++ if (args.np == r->of_node) { ++ rcdev = r; ++ break; ++ } ++ } ++ of_node_put(args.np); ++ ++ if (!rcdev) { ++ mutex_unlock(&reset_controller_list_mutex); ++ return ERR_PTR(-ENODEV); ++ } ++ ++ rstc_id = rcdev->of_xlate(rcdev, &args); ++ if (rstc_id < 0) { ++ mutex_unlock(&reset_controller_list_mutex); ++ return ERR_PTR(rstc_id); ++ } ++ ++ try_module_get(rcdev->owner); ++ mutex_unlock(&reset_controller_list_mutex); ++ ++ rstc = kzalloc(sizeof(*rstc), GFP_KERNEL); ++ if (!rstc) { ++ module_put(rcdev->owner); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ rstc->dev = dev; ++ rstc->rcdev = rcdev; ++ rstc->id = rstc_id; ++ ++ return rstc; ++} ++EXPORT_SYMBOL_GPL(reset_control_get); ++ ++/** ++ * reset_control_put - free the reset controller ++ * @rstc: reset controller ++ */ ++ ++void reset_control_put(struct reset_control *rstc) ++{ ++ if (IS_ERR(rstc)) ++ return; ++ ++ module_put(rstc->rcdev->owner); ++ kfree(rstc); ++} ++EXPORT_SYMBOL_GPL(reset_control_put); ++ ++static void devm_reset_control_release(struct device *dev, void *res) ++{ ++ reset_control_put(*(struct reset_control **)res); ++} ++ ++/** ++ * devm_reset_control_get - resource managed reset_control_get() ++ * @dev: device to be reset by the controller ++ * @id: reset line name ++ * ++ * Managed reset_control_get(). For reset controllers returned from this ++ * function, reset_control_put() is called automatically on driver detach. ++ * See reset_control_get() for more information. ++ */ ++struct reset_control *devm_reset_control_get(struct device *dev, const char *id) ++{ ++ struct reset_control **ptr, *rstc; ++ ++ ptr = devres_alloc(devm_reset_control_release, sizeof(*ptr), ++ GFP_KERNEL); ++ if (!ptr) ++ return ERR_PTR(-ENOMEM); ++ ++ rstc = reset_control_get(dev, id); ++ if (!IS_ERR(rstc)) { ++ *ptr = rstc; ++ devres_add(dev, ptr); ++ } else { ++ devres_free(ptr); ++ } ++ ++ return rstc; ++} ++EXPORT_SYMBOL_GPL(devm_reset_control_get); ++ ++static int devm_reset_control_match(struct device *dev, void *res, void *data) ++{ ++ struct reset_control **rstc = res; ++ if (WARN_ON(!rstc || !*rstc)) ++ return 0; ++ return *rstc == data; ++} ++ ++/** ++ * devm_reset_control_put - resource managed reset_control_put() ++ * @rstc: reset controller to free ++ * ++ * Deallocate a reset control allocated withd devm_reset_control_get(). ++ * This function will not need to be called normally, as devres will take ++ * care of freeing the resource. ++ */ ++void devm_reset_control_put(struct reset_control *rstc) ++{ ++ int ret; ++ ++ ret = devres_release(rstc->dev, devm_reset_control_release, ++ devm_reset_control_match, rstc); ++ if (ret) ++ WARN_ON(ret); ++} ++EXPORT_SYMBOL_GPL(devm_reset_control_put); ++ ++/** ++ * device_reset - find reset controller associated with the device ++ * and perform reset ++ * @dev: device to be reset by the controller ++ * ++ * Convenience wrapper for reset_control_get() and reset_control_reset(). ++ * This is useful for the common case of devices with single, dedicated reset ++ * lines. ++ */ ++int device_reset(struct device *dev) ++{ ++ struct reset_control *rstc; ++ int ret; ++ ++ rstc = reset_control_get(dev, NULL); ++ if (IS_ERR(rstc)) ++ return PTR_ERR(rstc); ++ ++ ret = reset_control_reset(rstc); ++ ++ reset_control_put(rstc); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(device_reset); +diff --git a/include/linux/reset-controller.h b/include/linux/reset-controller.h +new file mode 100644 +index 0000000..2f61311 +--- /dev/null ++++ b/include/linux/reset-controller.h +@@ -0,0 +1,51 @@ ++#ifndef _LINUX_RESET_CONTROLLER_H_ ++#define _LINUX_RESET_CONTROLLER_H_ ++ ++#include ++ ++struct reset_controller_dev; ++ ++/** ++ * struct reset_control_ops ++ * ++ * @reset: for self-deasserting resets, does all necessary ++ * things to reset the device ++ * @assert: manually assert the reset line, if supported ++ * @deassert: manually deassert the reset line, if supported ++ */ ++struct reset_control_ops { ++ int (*reset)(struct reset_controller_dev *rcdev, unsigned long id); ++ int (*assert)(struct reset_controller_dev *rcdev, unsigned long id); ++ int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id); ++}; ++ ++struct module; ++struct device_node; ++ ++/** ++ * struct reset_controller_dev - reset controller entity that might ++ * provide multiple reset controls ++ * @ops: a pointer to device specific struct reset_control_ops ++ * @owner: kernel module of the reset controller driver ++ * @list: internal list of reset controller devices ++ * @of_node: corresponding device tree node as phandle target ++ * @of_reset_n_cells: number of cells in reset line specifiers ++ * @of_xlate: translation function to translate from specifier as found in the ++ * device tree to id as given to the reset control ops ++ * @nr_resets: number of reset controls in this reset controller device ++ */ ++struct reset_controller_dev { ++ struct reset_control_ops *ops; ++ struct module *owner; ++ struct list_head list; ++ struct device_node *of_node; ++ int of_reset_n_cells; ++ int (*of_xlate)(struct reset_controller_dev *rcdev, ++ const struct of_phandle_args *reset_spec); ++ unsigned int nr_resets; ++}; ++ ++int reset_controller_register(struct reset_controller_dev *rcdev); ++void reset_controller_unregister(struct reset_controller_dev *rcdev); ++ ++#endif +diff --git a/include/linux/reset.h b/include/linux/reset.h +new file mode 100644 +index 0000000..6082247 +--- /dev/null ++++ b/include/linux/reset.h +@@ -0,0 +1,17 @@ ++#ifndef _LINUX_RESET_H_ ++#define _LINUX_RESET_H_ ++ ++struct device; ++struct reset_control; ++ ++int reset_control_reset(struct reset_control *rstc); ++int reset_control_assert(struct reset_control *rstc); ++int reset_control_deassert(struct reset_control *rstc); ++ ++struct reset_control *reset_control_get(struct device *dev, const char *id); ++void reset_control_put(struct reset_control *rstc); ++struct reset_control *devm_reset_control_get(struct device *dev, const char *id); ++ ++int device_reset(struct device *dev); ++ ++#endif +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0155-reset-MIPS-ralink-add-core-device-reset-wrapper.patch b/target/linux/ramips/patches-3.9/0155-reset-MIPS-ralink-add-core-device-reset-wrapper.patch new file mode 100644 index 0000000000..a22cfc8020 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0155-reset-MIPS-ralink-add-core-device-reset-wrapper.patch @@ -0,0 +1,135 @@ +From 4bec674c9f3f0bc137cf5aa4d2cb01e9f8027b3d Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 8 May 2013 22:08:39 +0200 +Subject: [PATCH 155/164] reset: MIPS: ralink: add core/device reset wrapper + +Add a helper for reseting different devices ont he SoC. + +Signed-off-by: John Crispin +--- + arch/mips/Kconfig | 1 + + arch/mips/ralink/of.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++ + arch/mips/ralink/reset.c | 1 + + 3 files changed, 61 insertions(+) + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index b237c50..cfc7153 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -444,6 +444,7 @@ config RALINK + select HAVE_MACH_CLKDEV + select CLKDEV_LOOKUP + select ARCH_REQUIRE_GPIOLIB ++ select ARCH_HAS_RESET_CONTROLLER + + config SGI_IP22 + bool "SGI IP22 (Indy/Indigo2)" +diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c +index 8efb02b..2faf478 100644 +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -14,16 +14,22 @@ + #include + #include + #include ++#include + #include + #include + #include ++#include + + #include + #include + #include + ++#include ++ + #include "common.h" + ++#define SYSC_REG_RESET_CTRL 0x034 ++ + __iomem void *rt_sysc_membase; + __iomem void *rt_memc_membase; + +@@ -96,6 +102,53 @@ void __init plat_mem_setup(void) + soc_info.mem_size_max * SZ_1M); + } + ++static int ralink_assert_device(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ u32 val; ++ ++ if (id < 8) ++ return -1; ++ ++ val = rt_sysc_r32(SYSC_REG_RESET_CTRL); ++ val |= BIT(id); ++ rt_sysc_w32(val, SYSC_REG_RESET_CTRL); ++ ++ return 0; ++} ++ ++static int ralink_deassert_device(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ u32 val; ++ ++ if (id < 8) ++ return -1; ++ ++ val = rt_sysc_r32(SYSC_REG_RESET_CTRL); ++ val &= ~BIT(id); ++ rt_sysc_w32(val, SYSC_REG_RESET_CTRL); ++ ++ return 0; ++} ++ ++static int ralink_reset_device(struct reset_controller_dev *rcdev, unsigned long id) ++{ ++ ralink_assert_device(rcdev, id); ++ return ralink_deassert_device(rcdev, id); ++} ++ ++static struct reset_control_ops reset_ops = { ++ .reset = ralink_reset_device, ++ .assert = ralink_assert_device, ++ .deassert = ralink_deassert_device, ++}; ++ ++static struct reset_controller_dev reset_dev = { ++ .ops = &reset_ops, ++ .owner = THIS_MODULE, ++ .nr_resets = 32, ++ .of_reset_n_cells = 1, ++}; ++ + static int __init plat_of_setup(void) + { + static struct of_device_id of_ids[3]; +@@ -110,6 +163,12 @@ static int __init plat_of_setup(void) + if (of_platform_populate(NULL, of_ids, NULL, NULL)) + panic("failed to populate DT\n"); + ++ reset_dev.of_node = of_find_compatible_node(NULL, NULL, "ralink,rt2880-reset"); ++ if (!reset_dev.of_node) ++ panic("Failed to find reset controller node"); ++ ++ reset_controller_register(&reset_dev); ++ + ralink_pinmux(); + + return 0; +diff --git a/arch/mips/ralink/reset.c b/arch/mips/ralink/reset.c +index 22120e5..6c15f4f 100644 +--- a/arch/mips/ralink/reset.c ++++ b/arch/mips/ralink/reset.c +@@ -10,6 +10,7 @@ + + #include + #include ++#include + + #include + +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0156-NET-add-of_get_mac_address_mtd.patch b/target/linux/ramips/patches-3.9/0156-NET-add-of_get_mac_address_mtd.patch new file mode 100644 index 0000000000..b790e6b21c --- /dev/null +++ b/target/linux/ramips/patches-3.9/0156-NET-add-of_get_mac_address_mtd.patch @@ -0,0 +1,83 @@ +From 14b015461adfb540ee46baf432691cc9eda6c046 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 17 Mar 2013 09:29:15 +0100 +Subject: [PATCH 156/164] NET: add of_get_mac_address_mtd() + +Many embedded devices have information such as mac addresses stored inside mtd +devices. This patch allows us to add a property inside a node describing a +network interface. The new property points at a mtd partition with an offset +where the mac address can be found. + +Signed-off-by: John Crispin +--- + drivers/of/of_net.c | 37 +++++++++++++++++++++++++++++++++++++ + include/linux/of_net.h | 1 + + 2 files changed, 38 insertions(+) + +diff --git a/drivers/of/of_net.c b/drivers/of/of_net.c +index ffab033..15f4a71 100644 +--- a/drivers/of/of_net.c ++++ b/drivers/of/of_net.c +@@ -10,6 +10,7 @@ + #include + #include + #include ++#include + + /** + * It maps 'enum phy_interface_t' found in include/linux/phy.h +@@ -92,3 +93,39 @@ const void *of_get_mac_address(struct device_node *np) + return NULL; + } + EXPORT_SYMBOL(of_get_mac_address); ++ ++int of_get_mac_address_mtd(struct device_node *np, void *mac) ++{ ++ struct device_node *mtd_np = NULL; ++ size_t retlen; ++ int size, ret; ++ struct mtd_info *mtd; ++ const char *part; ++ const __be32 *list; ++ phandle phandle; ++ ++ list = of_get_property(np, "mtd-mac-address", &size); ++ if (!list || (size != (2 * sizeof(*list)))) ++ return -ENOENT; ++ ++ phandle = be32_to_cpup(list++); ++ if (phandle) ++ mtd_np = of_find_node_by_phandle(phandle); ++ ++ if (!mtd_np) ++ return -ENOENT; ++ ++ part = of_get_property(mtd_np, "label", NULL); ++ if (!part) ++ part = mtd_np->name; ++ ++ mtd = get_mtd_device_nm(part); ++ if (IS_ERR(mtd)) ++ return PTR_ERR(mtd); ++ ++ ret = mtd_read(mtd, be32_to_cpup(list), 6, &retlen, (u_char *) mac); ++ put_mtd_device(mtd); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(of_get_mac_address_mtd); +diff --git a/include/linux/of_net.h b/include/linux/of_net.h +index f474641..9d3304f 100644 +--- a/include/linux/of_net.h ++++ b/include/linux/of_net.h +@@ -11,6 +11,7 @@ + #include + extern const int of_get_phy_mode(struct device_node *np); + extern const void *of_get_mac_address(struct device_node *np); ++extern int of_get_mac_address_mtd(struct device_node *np, void *mac); + #endif + + #endif /* __LINUX_OF_NET_H */ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0157-NET-multi-phy-support.patch b/target/linux/ramips/patches-3.9/0157-NET-multi-phy-support.patch new file mode 100644 index 0000000000..62133ef014 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0157-NET-multi-phy-support.patch @@ -0,0 +1,61 @@ +From 611c89720d9992d54da93bd946000c574d23f2b7 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 11 May 2013 23:40:19 +0200 +Subject: [PATCH 157/164] NET: multi phy support + +Signed-off-by: John Crispin +--- + drivers/net/phy/phy.c | 9 ++++++--- + include/linux/phy.h | 2 +- + 2 files changed, 7 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c +index ef9ea92..27f9b45 100644 +--- a/drivers/net/phy/phy.c ++++ b/drivers/net/phy/phy.c +@@ -800,7 +800,8 @@ void phy_state_machine(struct work_struct *work) + * negotiation for now */ + if (!phydev->link) { + phydev->state = PHY_NOLINK; +- netif_carrier_off(phydev->attached_dev); ++ if (!phydev->no_auto_carrier_off) ++ netif_carrier_off(phydev->attached_dev); + phydev->adjust_link(phydev->attached_dev); + break; + } +@@ -891,7 +892,8 @@ void phy_state_machine(struct work_struct *work) + netif_carrier_on(phydev->attached_dev); + } else { + phydev->state = PHY_NOLINK; +- netif_carrier_off(phydev->attached_dev); ++ if (!phydev->no_auto_carrier_off) ++ netif_carrier_off(phydev->attached_dev); + } + + phydev->adjust_link(phydev->attached_dev); +@@ -903,7 +905,8 @@ void phy_state_machine(struct work_struct *work) + case PHY_HALTED: + if (phydev->link) { + phydev->link = 0; +- netif_carrier_off(phydev->attached_dev); ++ if (!phydev->no_auto_carrier_off) ++ netif_carrier_off(phydev->attached_dev); + phydev->adjust_link(phydev->attached_dev); + } + break; +diff --git a/include/linux/phy.h b/include/linux/phy.h +index 33999ad..9c54bc9 100644 +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -298,7 +298,7 @@ struct phy_device { + + struct phy_c45_device_ids c45_ids; + bool is_c45; +- ++ bool no_auto_carrier_off; + enum phy_state state; + + u32 dev_flags; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0159-NET-MIPS-add-ralink-SoC-ethernet-driver.patch b/target/linux/ramips/patches-3.9/0159-NET-MIPS-add-ralink-SoC-ethernet-driver.patch new file mode 100644 index 0000000000..4f894e8953 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0159-NET-MIPS-add-ralink-SoC-ethernet-driver.patch @@ -0,0 +1,4732 @@ +From 12ea1efe1496e936d1cecc4de97ee55321aceee9 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 22 Apr 2013 23:20:03 +0200 +Subject: [PATCH 159/164] NET: MIPS: add ralink SoC ethernet driver + +Add support for Ralink FE and ESW. + +Signed-off-by: John Crispin +--- + .../include/asm/mach-ralink/rt305x_esw_platform.h | 27 + + arch/mips/ralink/rt305x.c | 1 + + drivers/net/ethernet/Kconfig | 1 + + drivers/net/ethernet/Makefile | 1 + + drivers/net/ethernet/ralink/Kconfig | 31 + + drivers/net/ethernet/ralink/Makefile | 18 + + drivers/net/ethernet/ralink/esw_rt3052.c | 1463 ++++++++++++++++++++ + drivers/net/ethernet/ralink/esw_rt3052.h | 32 + + drivers/net/ethernet/ralink/gsw_mt7620a.c | 1027 ++++++++++++++ + drivers/net/ethernet/ralink/gsw_mt7620a.h | 29 + + drivers/net/ethernet/ralink/mdio.c | 245 ++++ + drivers/net/ethernet/ralink/mdio.h | 29 + + drivers/net/ethernet/ralink/mdio_rt2880.c | 163 +++ + drivers/net/ethernet/ralink/mdio_rt2880.h | 25 + + drivers/net/ethernet/ralink/ralink_soc_eth.c | 759 ++++++++++ + drivers/net/ethernet/ralink/ralink_soc_eth.h | 372 +++++ + drivers/net/ethernet/ralink/soc_mt7620.c | 111 ++ + drivers/net/ethernet/ralink/soc_rt2880.c | 51 + + drivers/net/ethernet/ralink/soc_rt305x.c | 102 ++ + drivers/net/ethernet/ralink/soc_rt3883.c | 59 + + 20 files changed, 4546 insertions(+) + create mode 100644 arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h + create mode 100644 drivers/net/ethernet/ralink/Kconfig + create mode 100644 drivers/net/ethernet/ralink/Makefile + create mode 100644 drivers/net/ethernet/ralink/esw_rt3052.c + create mode 100644 drivers/net/ethernet/ralink/esw_rt3052.h + create mode 100644 drivers/net/ethernet/ralink/gsw_mt7620a.c + create mode 100644 drivers/net/ethernet/ralink/gsw_mt7620a.h + create mode 100644 drivers/net/ethernet/ralink/mdio.c + create mode 100644 drivers/net/ethernet/ralink/mdio.h + create mode 100644 drivers/net/ethernet/ralink/mdio_rt2880.c + create mode 100644 drivers/net/ethernet/ralink/mdio_rt2880.h + create mode 100644 drivers/net/ethernet/ralink/ralink_soc_eth.c + create mode 100644 drivers/net/ethernet/ralink/ralink_soc_eth.h + create mode 100644 drivers/net/ethernet/ralink/soc_mt7620.c + create mode 100644 drivers/net/ethernet/ralink/soc_rt2880.c + create mode 100644 drivers/net/ethernet/ralink/soc_rt305x.c + create mode 100644 drivers/net/ethernet/ralink/soc_rt3883.c + +diff --git a/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h b/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h +new file mode 100644 +index 0000000..2098c5c +--- /dev/null ++++ b/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h +@@ -0,0 +1,27 @@ ++/* ++ * Ralink RT305x SoC platform device registration ++ * ++ * Copyright (C) 2010 Gabor Juhos ++ * ++ * 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 _RT305X_ESW_PLATFORM_H ++#define _RT305X_ESW_PLATFORM_H ++ ++enum { ++ RT305X_ESW_VLAN_CONFIG_NONE = 0, ++ RT305X_ESW_VLAN_CONFIG_LLLLW, ++ RT305X_ESW_VLAN_CONFIG_WLLLL, ++}; ++ ++struct rt305x_esw_platform_data ++{ ++ u8 vlan_config; ++ u32 reg_initval_fct2; ++ u32 reg_initval_fpa2; ++}; ++ ++#endif /* _RT305X_ESW_PLATFORM_H */ +diff --git a/arch/mips/ralink/rt305x.c b/arch/mips/ralink/rt305x.c +index ca7ee3a..1a6b458 100644 +--- a/arch/mips/ralink/rt305x.c ++++ b/arch/mips/ralink/rt305x.c +@@ -221,6 +221,7 @@ void __init ralink_clk_init(void) + } + + ralink_clk_add("cpu", cpu_rate); ++ ralink_clk_add("sys", sys_rate); + ralink_clk_add("10000b00.spi", sys_rate); + ralink_clk_add("10000100.timer", wdt_rate); + ralink_clk_add("10000120.watchdog", wdt_rate); +diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig +index ed956e0..0b3caa1 100644 +--- a/drivers/net/ethernet/Kconfig ++++ b/drivers/net/ethernet/Kconfig +@@ -135,6 +135,7 @@ config ETHOC + source "drivers/net/ethernet/packetengines/Kconfig" + source "drivers/net/ethernet/pasemi/Kconfig" + source "drivers/net/ethernet/qlogic/Kconfig" ++source "drivers/net/ethernet/ralink/Kconfig" + source "drivers/net/ethernet/realtek/Kconfig" + source "drivers/net/ethernet/renesas/Kconfig" + source "drivers/net/ethernet/rdc/Kconfig" +diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile +index 8268d85..508c494 100644 +--- a/drivers/net/ethernet/Makefile ++++ b/drivers/net/ethernet/Makefile +@@ -53,6 +53,7 @@ obj-$(CONFIG_ETHOC) += ethoc.o + obj-$(CONFIG_NET_PACKET_ENGINE) += packetengines/ + obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/ + obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/ ++obj-$(CONFIG_NET_RALINK) += ralink/ + obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/ + obj-$(CONFIG_SH_ETH) += renesas/ + obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ +diff --git a/drivers/net/ethernet/ralink/Kconfig b/drivers/net/ethernet/ralink/Kconfig +new file mode 100644 +index 0000000..ca2c9ad +--- /dev/null ++++ b/drivers/net/ethernet/ralink/Kconfig +@@ -0,0 +1,31 @@ ++config NET_RALINK ++ tristate "Ralink RT288X/RT3X5X/RT3662/RT3883/MT7620 ethernet driver" ++ depends on RALINK ++ help ++ This driver supports the ethernet mac inside the ralink wisocs ++ ++if NET_RALINK ++ ++config NET_RALINK_MDIO ++ def_bool NET_RALINK ++ depends on (SOC_RT288X || SOC_RT3883 || SOC_MT7620) ++ select PHYLIB ++ ++config NET_RALINK_MDIO_RT2880 ++ def_bool NET_RALINK ++ depends on (SOC_RT288X || SOC_RT3883) ++ select NET_RALINK_MDIO ++ ++config NET_RALINK_ESW_RT3052 ++ def_bool NET_RALINK ++ depends on SOC_RT305X ++ select PHYLIB ++ select SWCONFIG ++ ++config NET_RALINK_GSW_MT7620 ++ def_bool NET_RALINK ++ depends on SOC_MT7620 ++ select NET_RALINK_MDIO ++ select PHYLIB ++ select SWCONFIG ++endif +diff --git a/drivers/net/ethernet/ralink/Makefile b/drivers/net/ethernet/ralink/Makefile +new file mode 100644 +index 0000000..a38fa21 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/Makefile +@@ -0,0 +1,18 @@ ++# ++# Makefile for the Ralink SoCs built-in ethernet macs ++# ++ ++ralink-eth-y += ralink_soc_eth.o ++ ++ralink-eth-$(CONFIG_NET_RALINK_MDIO) += mdio.o ++ralink-eth-$(CONFIG_NET_RALINK_MDIO_RT2880) += mdio_rt2880.o ++ ++ralink-eth-$(CONFIG_NET_RALINK_ESW_RT3052) += esw_rt3052.o ++ralink-eth-$(CONFIG_NET_RALINK_GSW_MT7620) += gsw_mt7620a.o ++ ++ralink-eth-$(CONFIG_SOC_RT288X) += soc_rt2880.o ++ralink-eth-$(CONFIG_SOC_RT305X) += soc_rt305x.o ++ralink-eth-$(CONFIG_SOC_RT3883) += soc_rt3883.o ++ralink-eth-$(CONFIG_SOC_MT7620) += soc_mt7620.o ++ ++obj-$(CONFIG_NET_RALINK) += ralink-eth.o +diff --git a/drivers/net/ethernet/ralink/esw_rt3052.c b/drivers/net/ethernet/ralink/esw_rt3052.c +new file mode 100644 +index 0000000..b937062 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/esw_rt3052.c +@@ -0,0 +1,1463 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++/* ++ * HW limitations for this switch: ++ * - No large frame support (PKT_MAX_LEN at most 1536) ++ * - Can't have untagged vlan and tagged vlan on one port at the same time, ++ * though this might be possible using the undocumented PPE. ++ */ ++ ++#define RT305X_ESW_REG_ISR 0x00 ++#define RT305X_ESW_REG_IMR 0x04 ++#define RT305X_ESW_REG_FCT0 0x08 ++#define RT305X_ESW_REG_PFC1 0x14 ++#define RT305X_ESW_REG_ATS 0x24 ++#define RT305X_ESW_REG_ATS0 0x28 ++#define RT305X_ESW_REG_ATS1 0x2c ++#define RT305X_ESW_REG_ATS2 0x30 ++#define RT305X_ESW_REG_PVIDC(_n) (0x40 + 4 * (_n)) ++#define RT305X_ESW_REG_VLANI(_n) (0x50 + 4 * (_n)) ++#define RT305X_ESW_REG_VMSC(_n) (0x70 + 4 * (_n)) ++#define RT305X_ESW_REG_POA 0x80 ++#define RT305X_ESW_REG_FPA 0x84 ++#define RT305X_ESW_REG_SOCPC 0x8c ++#define RT305X_ESW_REG_POC0 0x90 ++#define RT305X_ESW_REG_POC1 0x94 ++#define RT305X_ESW_REG_POC2 0x98 ++#define RT305X_ESW_REG_SGC 0x9c ++#define RT305X_ESW_REG_STRT 0xa0 ++#define RT305X_ESW_REG_PCR0 0xc0 ++#define RT305X_ESW_REG_PCR1 0xc4 ++#define RT305X_ESW_REG_FPA2 0xc8 ++#define RT305X_ESW_REG_FCT2 0xcc ++#define RT305X_ESW_REG_SGC2 0xe4 ++#define RT305X_ESW_REG_P0LED 0xa4 ++#define RT305X_ESW_REG_P1LED 0xa8 ++#define RT305X_ESW_REG_P2LED 0xac ++#define RT305X_ESW_REG_P3LED 0xb0 ++#define RT305X_ESW_REG_P4LED 0xb4 ++#define RT305X_ESW_REG_PXPC(_x) (0xe8 + (4 * _x)) ++#define RT305X_ESW_REG_P1PC 0xec ++#define RT305X_ESW_REG_P2PC 0xf0 ++#define RT305X_ESW_REG_P3PC 0xf4 ++#define RT305X_ESW_REG_P4PC 0xf8 ++#define RT305X_ESW_REG_P5PC 0xfc ++ ++#define RT305X_ESW_LED_LINK 0 ++#define RT305X_ESW_LED_100M 1 ++#define RT305X_ESW_LED_DUPLEX 2 ++#define RT305X_ESW_LED_ACTIVITY 3 ++#define RT305X_ESW_LED_COLLISION 4 ++#define RT305X_ESW_LED_LINKACT 5 ++#define RT305X_ESW_LED_DUPLCOLL 6 ++#define RT305X_ESW_LED_10MACT 7 ++#define RT305X_ESW_LED_100MACT 8 ++/* Additional led states not in datasheet: */ ++#define RT305X_ESW_LED_BLINK 10 ++#define RT305X_ESW_LED_ON 12 ++ ++#define RT305X_ESW_LINK_S 25 ++#define RT305X_ESW_DUPLEX_S 9 ++#define RT305X_ESW_SPD_S 0 ++ ++#define RT305X_ESW_PCR0_WT_NWAY_DATA_S 16 ++#define RT305X_ESW_PCR0_WT_PHY_CMD BIT(13) ++#define RT305X_ESW_PCR0_CPU_PHY_REG_S 8 ++ ++#define RT305X_ESW_PCR1_WT_DONE BIT(0) ++ ++#define RT305X_ESW_ATS_TIMEOUT (5 * HZ) ++#define RT305X_ESW_PHY_TIMEOUT (5 * HZ) ++ ++#define RT305X_ESW_PVIDC_PVID_M 0xfff ++#define RT305X_ESW_PVIDC_PVID_S 12 ++ ++#define RT305X_ESW_VLANI_VID_M 0xfff ++#define RT305X_ESW_VLANI_VID_S 12 ++ ++#define RT305X_ESW_VMSC_MSC_M 0xff ++#define RT305X_ESW_VMSC_MSC_S 8 ++ ++#define RT305X_ESW_SOCPC_DISUN2CPU_S 0 ++#define RT305X_ESW_SOCPC_DISMC2CPU_S 8 ++#define RT305X_ESW_SOCPC_DISBC2CPU_S 16 ++#define RT305X_ESW_SOCPC_CRC_PADDING BIT(25) ++ ++#define RT305X_ESW_POC0_EN_BP_S 0 ++#define RT305X_ESW_POC0_EN_FC_S 8 ++#define RT305X_ESW_POC0_DIS_RMC2CPU_S 16 ++#define RT305X_ESW_POC0_DIS_PORT_M 0x7f ++#define RT305X_ESW_POC0_DIS_PORT_S 23 ++ ++#define RT305X_ESW_POC2_UNTAG_EN_M 0xff ++#define RT305X_ESW_POC2_UNTAG_EN_S 0 ++#define RT305X_ESW_POC2_ENAGING_S 8 ++#define RT305X_ESW_POC2_DIS_UC_PAUSE_S 16 ++ ++#define RT305X_ESW_SGC2_DOUBLE_TAG_M 0x7f ++#define RT305X_ESW_SGC2_DOUBLE_TAG_S 0 ++#define RT305X_ESW_SGC2_LAN_PMAP_M 0x3f ++#define RT305X_ESW_SGC2_LAN_PMAP_S 24 ++ ++#define RT305X_ESW_PFC1_EN_VLAN_M 0xff ++#define RT305X_ESW_PFC1_EN_VLAN_S 16 ++#define RT305X_ESW_PFC1_EN_TOS_S 24 ++ ++#define RT305X_ESW_VLAN_NONE 0xfff ++ ++#define RT305X_ESW_GSC_BC_STROM_MASK 0x3 ++#define RT305X_ESW_GSC_BC_STROM_SHIFT 4 ++ ++#define RT305X_ESW_GSC_LED_FREQ_MASK 0x3 ++#define RT305X_ESW_GSC_LED_FREQ_SHIFT 23 ++ ++#define RT305X_ESW_POA_LINK_MASK 0x1f ++#define RT305X_ESW_POA_LINK_SHIFT 25 ++ ++#define RT305X_ESW_PORT_ST_CHG BIT(26) ++#define RT305X_ESW_PORT0 0 ++#define RT305X_ESW_PORT1 1 ++#define RT305X_ESW_PORT2 2 ++#define RT305X_ESW_PORT3 3 ++#define RT305X_ESW_PORT4 4 ++#define RT305X_ESW_PORT5 5 ++#define RT305X_ESW_PORT6 6 ++ ++#define RT305X_ESW_PORTS_NONE 0 ++ ++#define RT305X_ESW_PMAP_LLLLLL 0x3f ++#define RT305X_ESW_PMAP_LLLLWL 0x2f ++#define RT305X_ESW_PMAP_WLLLLL 0x3e ++ ++#define RT305X_ESW_PORTS_INTERNAL \ ++ (BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT1) | \ ++ BIT(RT305X_ESW_PORT2) | BIT(RT305X_ESW_PORT3) | \ ++ BIT(RT305X_ESW_PORT4)) ++ ++#define RT305X_ESW_PORTS_NOCPU \ ++ (RT305X_ESW_PORTS_INTERNAL | BIT(RT305X_ESW_PORT5)) ++ ++#define RT305X_ESW_PORTS_CPU BIT(RT305X_ESW_PORT6) ++ ++#define RT305X_ESW_PORTS_ALL \ ++ (RT305X_ESW_PORTS_NOCPU | RT305X_ESW_PORTS_CPU) ++ ++#define RT305X_ESW_NUM_VLANS 16 ++#define RT305X_ESW_NUM_VIDS 4096 ++#define RT305X_ESW_NUM_PORTS 7 ++#define RT305X_ESW_NUM_LANWAN 6 ++#define RT305X_ESW_NUM_LEDS 5 ++ ++#define RT5350_ESW_REG_PXTPC(_x) (0x150 + (4 * _x)) ++#define RT5350_EWS_REG_LED_POLARITY 0x168 ++#define RT5350_RESET_EPHY BIT(24) ++#define SYSC_REG_RESET_CTRL 0x34 ++ ++enum { ++ /* Global attributes. */ ++ RT305X_ESW_ATTR_ENABLE_VLAN, ++ RT305X_ESW_ATTR_ALT_VLAN_DISABLE, ++ RT305X_ESW_ATTR_BC_STATUS, ++ RT305X_ESW_ATTR_LED_FREQ, ++ /* Port attributes. */ ++ RT305X_ESW_ATTR_PORT_DISABLE, ++ RT305X_ESW_ATTR_PORT_DOUBLETAG, ++ RT305X_ESW_ATTR_PORT_UNTAG, ++ RT305X_ESW_ATTR_PORT_LED, ++ RT305X_ESW_ATTR_PORT_LAN, ++ RT305X_ESW_ATTR_PORT_RECV_BAD, ++ RT305X_ESW_ATTR_PORT_RECV_GOOD, ++ RT5350_ESW_ATTR_PORT_TR_BAD, ++ RT5350_ESW_ATTR_PORT_TR_GOOD, ++}; ++ ++struct esw_port { ++ bool disable; ++ bool doubletag; ++ bool untag; ++ u8 led; ++ u16 pvid; ++}; ++ ++struct esw_vlan { ++ u8 ports; ++ u16 vid; ++}; ++ ++struct rt305x_esw { ++ struct device *dev; ++ void __iomem *base; ++ int irq; ++ const struct rt305x_esw_platform_data *pdata; ++ /* Protects against concurrent register rmw operations. */ ++ spinlock_t reg_rw_lock; ++ ++ unsigned char port_map; ++ unsigned int reg_initval_fct2; ++ unsigned int reg_initval_fpa2; ++ unsigned int reg_led_polarity; ++ ++ ++ struct switch_dev swdev; ++ bool global_vlan_enable; ++ bool alt_vlan_disable; ++ int bc_storm_protect; ++ int led_frequency; ++ struct esw_vlan vlans[RT305X_ESW_NUM_VLANS]; ++ struct esw_port ports[RT305X_ESW_NUM_PORTS]; ++ ++}; ++ ++static inline void esw_w32(struct rt305x_esw *esw, u32 val, unsigned reg) ++{ ++ __raw_writel(val, esw->base + reg); ++} ++ ++static inline u32 esw_r32(struct rt305x_esw *esw, unsigned reg) ++{ ++ return __raw_readl(esw->base + reg); ++} ++ ++static inline void esw_rmw_raw(struct rt305x_esw *esw, unsigned reg, unsigned long mask, ++ unsigned long val) ++{ ++ unsigned long t; ++ ++ t = __raw_readl(esw->base + reg) & ~mask; ++ __raw_writel(t | val, esw->base + reg); ++} ++ ++static void esw_rmw(struct rt305x_esw *esw, unsigned reg, unsigned long mask, ++ unsigned long val) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&esw->reg_rw_lock, flags); ++ esw_rmw_raw(esw, reg, mask, val); ++ spin_unlock_irqrestore(&esw->reg_rw_lock, flags); ++} ++ ++static u32 rt305x_mii_write(struct rt305x_esw *esw, u32 phy_addr, u32 phy_register, ++ u32 write_data) ++{ ++ unsigned long t_start = jiffies; ++ int ret = 0; ++ ++ while (1) { ++ if (!(esw_r32(esw, RT305X_ESW_REG_PCR1) & ++ RT305X_ESW_PCR1_WT_DONE)) ++ break; ++ if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) { ++ ret = 1; ++ goto out; ++ } ++ } ++ ++ write_data &= 0xffff; ++ esw_w32(esw, ++ (write_data << RT305X_ESW_PCR0_WT_NWAY_DATA_S) | ++ (phy_register << RT305X_ESW_PCR0_CPU_PHY_REG_S) | ++ (phy_addr) | RT305X_ESW_PCR0_WT_PHY_CMD, ++ RT305X_ESW_REG_PCR0); ++ ++ t_start = jiffies; ++ while (1) { ++ if (esw_r32(esw, RT305X_ESW_REG_PCR1) & ++ RT305X_ESW_PCR1_WT_DONE) ++ break; ++ ++ if (time_after(jiffies, t_start + RT305X_ESW_PHY_TIMEOUT)) { ++ ret = 1; ++ break; ++ } ++ } ++out: ++ if (ret) ++ printk(KERN_ERR "ramips_eth: MDIO timeout\n"); ++ return ret; ++} ++ ++static unsigned esw_get_vlan_id(struct rt305x_esw *esw, unsigned vlan) ++{ ++ unsigned s; ++ unsigned val; ++ ++ s = RT305X_ESW_VLANI_VID_S * (vlan % 2); ++ val = esw_r32(esw, RT305X_ESW_REG_VLANI(vlan / 2)); ++ val = (val >> s) & RT305X_ESW_VLANI_VID_M; ++ ++ return val; ++} ++ ++static void esw_set_vlan_id(struct rt305x_esw *esw, unsigned vlan, unsigned vid) ++{ ++ unsigned s; ++ ++ s = RT305X_ESW_VLANI_VID_S * (vlan % 2); ++ esw_rmw(esw, ++ RT305X_ESW_REG_VLANI(vlan / 2), ++ RT305X_ESW_VLANI_VID_M << s, ++ (vid & RT305X_ESW_VLANI_VID_M) << s); ++} ++ ++static unsigned esw_get_pvid(struct rt305x_esw *esw, unsigned port) ++{ ++ unsigned s, val; ++ ++ s = RT305X_ESW_PVIDC_PVID_S * (port % 2); ++ val = esw_r32(esw, RT305X_ESW_REG_PVIDC(port / 2)); ++ return (val >> s) & RT305X_ESW_PVIDC_PVID_M; ++} ++ ++static void esw_set_pvid(struct rt305x_esw *esw, unsigned port, unsigned pvid) ++{ ++ unsigned s; ++ ++ s = RT305X_ESW_PVIDC_PVID_S * (port % 2); ++ esw_rmw(esw, ++ RT305X_ESW_REG_PVIDC(port / 2), ++ RT305X_ESW_PVIDC_PVID_M << s, ++ (pvid & RT305X_ESW_PVIDC_PVID_M) << s); ++} ++ ++static unsigned esw_get_vmsc(struct rt305x_esw *esw, unsigned vlan) ++{ ++ unsigned s, val; ++ ++ s = RT305X_ESW_VMSC_MSC_S * (vlan % 4); ++ val = esw_r32(esw, RT305X_ESW_REG_VMSC(vlan / 4)); ++ val = (val >> s) & RT305X_ESW_VMSC_MSC_M; ++ ++ return val; ++} ++ ++static void esw_set_vmsc(struct rt305x_esw *esw, unsigned vlan, unsigned msc) ++{ ++ unsigned s; ++ ++ s = RT305X_ESW_VMSC_MSC_S * (vlan % 4); ++ esw_rmw(esw, ++ RT305X_ESW_REG_VMSC(vlan / 4), ++ RT305X_ESW_VMSC_MSC_M << s, ++ (msc & RT305X_ESW_VMSC_MSC_M) << s); ++} ++ ++static unsigned esw_get_port_disable(struct rt305x_esw *esw) ++{ ++ unsigned reg; ++ reg = esw_r32(esw, RT305X_ESW_REG_POC0); ++ return (reg >> RT305X_ESW_POC0_DIS_PORT_S) & ++ RT305X_ESW_POC0_DIS_PORT_M; ++} ++ ++static void esw_set_port_disable(struct rt305x_esw *esw, unsigned disable_mask) ++{ ++ unsigned old_mask; ++ unsigned enable_mask; ++ unsigned changed; ++ int i; ++ ++ old_mask = esw_get_port_disable(esw); ++ changed = old_mask ^ disable_mask; ++ enable_mask = old_mask & disable_mask; ++ ++ /* enable before writing to MII */ ++ esw_rmw(esw, RT305X_ESW_REG_POC0, ++ (RT305X_ESW_POC0_DIS_PORT_M << ++ RT305X_ESW_POC0_DIS_PORT_S), ++ enable_mask << RT305X_ESW_POC0_DIS_PORT_S); ++ ++ for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) { ++ if (!(changed & (1 << i))) ++ continue; ++ if (disable_mask & (1 << i)) { ++ /* disable */ ++ rt305x_mii_write(esw, i, MII_BMCR, ++ BMCR_PDOWN); ++ } else { ++ /* enable */ ++ rt305x_mii_write(esw, i, MII_BMCR, ++ BMCR_FULLDPLX | ++ BMCR_ANENABLE | ++ BMCR_ANRESTART | ++ BMCR_SPEED100); ++ } ++ } ++ ++ /* disable after writing to MII */ ++ esw_rmw(esw, RT305X_ESW_REG_POC0, ++ (RT305X_ESW_POC0_DIS_PORT_M << ++ RT305X_ESW_POC0_DIS_PORT_S), ++ disable_mask << RT305X_ESW_POC0_DIS_PORT_S); ++} ++ ++static void esw_set_gsc(struct rt305x_esw *esw) ++{ ++ esw_rmw(esw, RT305X_ESW_REG_SGC, ++ RT305X_ESW_GSC_BC_STROM_MASK << RT305X_ESW_GSC_BC_STROM_SHIFT, ++ esw->bc_storm_protect << RT305X_ESW_GSC_BC_STROM_SHIFT); ++ esw_rmw(esw, RT305X_ESW_REG_SGC, ++ RT305X_ESW_GSC_LED_FREQ_MASK << RT305X_ESW_GSC_LED_FREQ_SHIFT, ++ esw->led_frequency << RT305X_ESW_GSC_LED_FREQ_SHIFT); ++} ++ ++static int esw_apply_config(struct switch_dev *dev); ++ ++static void esw_hw_init(struct rt305x_esw *esw) ++{ ++ int i; ++ u8 port_disable = 0; ++ u8 port_map = RT305X_ESW_PMAP_LLLLLL; ++ ++ /* vodoo from original driver */ ++ esw_w32(esw, 0xC8A07850, RT305X_ESW_REG_FCT0); ++ esw_w32(esw, 0x00000000, RT305X_ESW_REG_SGC2); ++ /* Port priority 1 for all ports, vlan enabled. */ ++ esw_w32(esw, 0x00005555 | ++ (RT305X_ESW_PORTS_ALL << RT305X_ESW_PFC1_EN_VLAN_S), ++ RT305X_ESW_REG_PFC1); ++ ++ /* Enable Back Pressure, and Flow Control */ ++ esw_w32(esw, ++ ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_BP_S) | ++ (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_FC_S)), ++ RT305X_ESW_REG_POC0); ++ ++ /* Enable Aging, and VLAN TAG removal */ ++ esw_w32(esw, ++ ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC2_ENAGING_S) | ++ (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC2_UNTAG_EN_S)), ++ RT305X_ESW_REG_POC2); ++ ++ if (esw->reg_initval_fct2) ++ esw_w32(esw, esw->reg_initval_fct2, RT305X_ESW_REG_FCT2); ++ else ++ esw_w32(esw, esw->pdata->reg_initval_fct2, RT305X_ESW_REG_FCT2); ++ ++ /* ++ * 300s aging timer, max packet len 1536, broadcast storm prevention ++ * disabled, disable collision abort, mac xor48 hash, 10 packet back ++ * pressure jam, GMII disable was_transmit, back pressure disabled, ++ * 30ms led flash, unmatched IGMP as broadcast, rmc tb fault to all ++ * ports. ++ */ ++ esw_w32(esw, 0x0008a301, RT305X_ESW_REG_SGC); ++ ++ /* Setup SoC Port control register */ ++ esw_w32(esw, ++ (RT305X_ESW_SOCPC_CRC_PADDING | ++ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISUN2CPU_S) | ++ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISMC2CPU_S) | ++ (RT305X_ESW_PORTS_CPU << RT305X_ESW_SOCPC_DISBC2CPU_S)), ++ RT305X_ESW_REG_SOCPC); ++ ++ if (esw->reg_initval_fpa2) ++ esw_w32(esw, esw->reg_initval_fpa2, RT305X_ESW_REG_FPA2); ++ else ++ esw_w32(esw, esw->pdata->reg_initval_fpa2, RT305X_ESW_REG_FPA2); ++ esw_w32(esw, 0x00000000, RT305X_ESW_REG_FPA); ++ ++ /* Force Link/Activity on ports */ ++ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P0LED); ++ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P1LED); ++ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P2LED); ++ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P3LED); ++ esw_w32(esw, 0x00000005, RT305X_ESW_REG_P4LED); ++ ++ /* Copy disabled port configuration from bootloader setup */ ++ port_disable = esw_get_port_disable(esw); ++ for (i = 0; i < 6; i++) ++ esw->ports[i].disable = (port_disable & (1 << i)) != 0; ++ ++ if (soc_is_rt3352()) { ++ /* reset EPHY */ ++ u32 val = rt_sysc_r32(SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(val | RT5350_RESET_EPHY, SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(val, SYSC_REG_RESET_CTRL); ++ ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ for (i = 0; i < 5; i++) { ++ if (esw->ports[i].disable) { ++ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN); ++ } else { ++ rt305x_mii_write(esw, i, MII_BMCR, ++ BMCR_FULLDPLX | ++ BMCR_ANENABLE | ++ BMCR_SPEED100); ++ } ++ /* TX10 waveform coefficient LSB=0 disable PHY */ ++ rt305x_mii_write(esw, i, 26, 0x1601); ++ /* TX100/TX10 AD/DA current bias */ ++ rt305x_mii_write(esw, i, 29, 0x7016); ++ /* TX100 slew rate control */ ++ rt305x_mii_write(esw, i, 30, 0x0038); ++ } ++ ++ /* select global register */ ++ rt305x_mii_write(esw, 0, 31, 0x0); ++ /* enlarge agcsel threshold 3 and threshold 2 */ ++ rt305x_mii_write(esw, 0, 1, 0x4a40); ++ /* enlarge agcsel threshold 5 and threshold 4 */ ++ rt305x_mii_write(esw, 0, 2, 0x6254); ++ /* enlarge agcsel threshold */ ++ rt305x_mii_write(esw, 0, 3, 0xa17f); ++ rt305x_mii_write(esw, 0,12, 0x7eaa); ++ /* longer TP_IDL tail length */ ++ rt305x_mii_write(esw, 0, 14, 0x65); ++ /* increased squelch pulse count threshold. */ ++ rt305x_mii_write(esw, 0, 16, 0x0684); ++ /* set TX10 signal amplitude threshold to minimum */ ++ rt305x_mii_write(esw, 0, 17, 0x0fe0); ++ /* set squelch amplitude to higher threshold */ ++ rt305x_mii_write(esw, 0, 18, 0x40ba); ++ /* tune TP_IDL tail and head waveform, enable power down slew rate control */ ++ rt305x_mii_write(esw, 0, 22, 0x253f); ++ /* set PLL/Receive bias current are calibrated */ ++ rt305x_mii_write(esw, 0, 27, 0x2fda); ++ /* change PLL/Receive bias current to internal(RT3350) */ ++ rt305x_mii_write(esw, 0, 28, 0xc410); ++ /* change PLL bias current to internal(RT3052_MP3) */ ++ rt305x_mii_write(esw, 0, 29, 0x598b); ++ /* select local register */ ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ } else if (soc_is_rt5350()) { ++ /* reset EPHY */ ++ u32 val = rt_sysc_r32(SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(val | RT5350_RESET_EPHY, SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(val, SYSC_REG_RESET_CTRL); ++ ++ /* set the led polarity */ ++ esw_w32(esw, esw->reg_led_polarity & 0x1F, RT5350_EWS_REG_LED_POLARITY); ++ ++ /* local registers */ ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ for (i = 0; i < 5; i++) { ++ if (esw->ports[i].disable) { ++ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN); ++ } else { ++ rt305x_mii_write(esw, i, MII_BMCR, ++ BMCR_FULLDPLX | ++ BMCR_ANENABLE | ++ BMCR_SPEED100); ++ } ++ /* TX10 waveform coefficient LSB=0 disable PHY */ ++ rt305x_mii_write(esw, i, 26, 0x1601); ++ /* TX100/TX10 AD/DA current bias */ ++ rt305x_mii_write(esw, i, 29, 0x7015); ++ /* TX100 slew rate control */ ++ rt305x_mii_write(esw, i, 30, 0x0038); ++ } ++ ++ /* global registers */ ++ rt305x_mii_write(esw, 0, 31, 0x0); ++ /* enlarge agcsel threshold 3 and threshold 2 */ ++ rt305x_mii_write(esw, 0, 1, 0x4a40); ++ /* enlarge agcsel threshold 5 and threshold 4 */ ++ rt305x_mii_write(esw, 0, 2, 0x6254); ++ /* enlarge agcsel threshold 6 */ ++ rt305x_mii_write(esw, 0, 3, 0xa17f); ++ rt305x_mii_write(esw, 0, 12, 0x7eaa); ++ /* longer TP_IDL tail length */ ++ rt305x_mii_write(esw, 0, 14, 0x65); ++ /* increased squelch pulse count threshold. */ ++ rt305x_mii_write(esw, 0, 16, 0x0684); ++ /* set TX10 signal amplitude threshold to minimum */ ++ rt305x_mii_write(esw, 0, 17, 0x0fe0); ++ /* set squelch amplitude to higher threshold */ ++ rt305x_mii_write(esw, 0, 18, 0x40ba); ++ /* tune TP_IDL tail and head waveform, enable power down slew rate control */ ++ rt305x_mii_write(esw, 0, 22, 0x253f); ++ /* set PLL/Receive bias current are calibrated */ ++ rt305x_mii_write(esw, 0, 27, 0x2fda); ++ /* change PLL/Receive bias current to internal(RT3350) */ ++ rt305x_mii_write(esw, 0, 28, 0xc410); ++ /* change PLL bias current to internal(RT3052_MP3) */ ++ rt305x_mii_write(esw, 0, 29, 0x598b); ++ /* select local register */ ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ } else { ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ for (i = 0; i < 5; i++) { ++ if (esw->ports[i].disable) { ++ rt305x_mii_write(esw, i, MII_BMCR, BMCR_PDOWN); ++ } else { ++ rt305x_mii_write(esw, i, MII_BMCR, ++ BMCR_FULLDPLX | ++ BMCR_ANENABLE | ++ BMCR_SPEED100); ++ } ++ /* TX10 waveform coefficient */ ++ rt305x_mii_write(esw, i, 26, 0x1601); ++ /* TX100/TX10 AD/DA current bias */ ++ rt305x_mii_write(esw, i, 29, 0x7058); ++ /* TX100 slew rate control */ ++ rt305x_mii_write(esw, i, 30, 0x0018); ++ } ++ ++ /* PHY IOT */ ++ /* select global register */ ++ rt305x_mii_write(esw, 0, 31, 0x0); ++ /* tune TP_IDL tail and head waveform */ ++ rt305x_mii_write(esw, 0, 22, 0x052f); ++ /* set TX10 signal amplitude threshold to minimum */ ++ rt305x_mii_write(esw, 0, 17, 0x0fe0); ++ /* set squelch amplitude to higher threshold */ ++ rt305x_mii_write(esw, 0, 18, 0x40ba); ++ /* longer TP_IDL tail length */ ++ rt305x_mii_write(esw, 0, 14, 0x65); ++ /* select local register */ ++ rt305x_mii_write(esw, 0, 31, 0x8000); ++ } ++ ++ if (esw->port_map) ++ port_map = esw->port_map; ++ else ++ port_map = RT305X_ESW_PMAP_LLLLLL; ++ ++ /* ++ * Unused HW feature, but still nice to be consistent here... ++ * This is also exported to userspace ('lan' attribute) so it's ++ * conveniently usable to decide which ports go into the wan vlan by ++ * default. ++ */ ++ esw_rmw(esw, RT305X_ESW_REG_SGC2, ++ RT305X_ESW_SGC2_LAN_PMAP_M << RT305X_ESW_SGC2_LAN_PMAP_S, ++ port_map << RT305X_ESW_SGC2_LAN_PMAP_S); ++ ++ /* make the switch leds blink */ ++ for (i = 0; i < RT305X_ESW_NUM_LEDS; i++) ++ esw->ports[i].led = 0x05; ++ ++ /* Apply the empty config. */ ++ esw_apply_config(&esw->swdev); ++ ++ /* Only unmask the port change interrupt */ ++ esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR); ++} ++ ++static irqreturn_t esw_interrupt(int irq, void *_esw) ++{ ++ struct rt305x_esw *esw = (struct rt305x_esw *) _esw; ++ u32 status; ++ ++ status = esw_r32(esw, RT305X_ESW_REG_ISR); ++ if (status & RT305X_ESW_PORT_ST_CHG) { ++ u32 link = esw_r32(esw, RT305X_ESW_REG_POA); ++ link >>= RT305X_ESW_POA_LINK_SHIFT; ++ link &= RT305X_ESW_POA_LINK_MASK; ++ dev_info(esw->dev, "link changed 0x%02X\n", link); ++ } ++ esw_w32(esw, status, RT305X_ESW_REG_ISR); ++ ++ return IRQ_HANDLED; ++} ++ ++static int esw_apply_config(struct switch_dev *dev) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int i; ++ u8 disable = 0; ++ u8 doubletag = 0; ++ u8 en_vlan = 0; ++ u8 untag = 0; ++ ++ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { ++ u32 vid, vmsc; ++ if (esw->global_vlan_enable) { ++ vid = esw->vlans[i].vid; ++ vmsc = esw->vlans[i].ports; ++ } else { ++ vid = RT305X_ESW_VLAN_NONE; ++ vmsc = RT305X_ESW_PORTS_NONE; ++ } ++ esw_set_vlan_id(esw, i, vid); ++ esw_set_vmsc(esw, i, vmsc); ++ } ++ ++ for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) { ++ u32 pvid; ++ disable |= esw->ports[i].disable << i; ++ if (esw->global_vlan_enable) { ++ doubletag |= esw->ports[i].doubletag << i; ++ en_vlan |= 1 << i; ++ untag |= esw->ports[i].untag << i; ++ pvid = esw->ports[i].pvid; ++ } else { ++ int x = esw->alt_vlan_disable ? 0 : 1; ++ doubletag |= x << i; ++ en_vlan |= x << i; ++ untag |= x << i; ++ pvid = 0; ++ } ++ esw_set_pvid(esw, i, pvid); ++ if (i < RT305X_ESW_NUM_LEDS) ++ esw_w32(esw, esw->ports[i].led, ++ RT305X_ESW_REG_P0LED + 4*i); ++ } ++ ++ esw_set_gsc(esw); ++ esw_set_port_disable(esw, disable); ++ esw_rmw(esw, RT305X_ESW_REG_SGC2, ++ (RT305X_ESW_SGC2_DOUBLE_TAG_M << ++ RT305X_ESW_SGC2_DOUBLE_TAG_S), ++ doubletag << RT305X_ESW_SGC2_DOUBLE_TAG_S); ++ esw_rmw(esw, RT305X_ESW_REG_PFC1, ++ RT305X_ESW_PFC1_EN_VLAN_M << RT305X_ESW_PFC1_EN_VLAN_S, ++ en_vlan << RT305X_ESW_PFC1_EN_VLAN_S); ++ esw_rmw(esw, RT305X_ESW_REG_POC2, ++ RT305X_ESW_POC2_UNTAG_EN_M << RT305X_ESW_POC2_UNTAG_EN_S, ++ untag << RT305X_ESW_POC2_UNTAG_EN_S); ++ ++ if (!esw->global_vlan_enable) { ++ /* ++ * Still need to put all ports into vlan 0 or they'll be ++ * isolated. ++ * NOTE: vlan 0 is special, no vlan tag is prepended ++ */ ++ esw_set_vlan_id(esw, 0, 0); ++ esw_set_vmsc(esw, 0, RT305X_ESW_PORTS_ALL); ++ } ++ ++ return 0; ++} ++ ++static int esw_reset_switch(struct switch_dev *dev) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ esw->global_vlan_enable = 0; ++ memset(esw->ports, 0, sizeof(esw->ports)); ++ memset(esw->vlans, 0, sizeof(esw->vlans)); ++ esw_hw_init(esw); ++ ++ return 0; ++} ++ ++static int esw_get_vlan_enable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ val->value.i = esw->global_vlan_enable; ++ ++ return 0; ++} ++ ++static int esw_set_vlan_enable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ esw->global_vlan_enable = val->value.i != 0; ++ ++ return 0; ++} ++ ++static int esw_get_alt_vlan_disable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ val->value.i = esw->alt_vlan_disable; ++ ++ return 0; ++} ++ ++static int esw_set_alt_vlan_disable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ esw->alt_vlan_disable = val->value.i != 0; ++ ++ return 0; ++} ++ ++static int ++rt305x_esw_set_bc_status(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ esw->bc_storm_protect = val->value.i & RT305X_ESW_GSC_BC_STROM_MASK; ++ ++ return 0; ++} ++ ++static int ++rt305x_esw_get_bc_status(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ val->value.i = esw->bc_storm_protect; ++ ++ return 0; ++} ++ ++static int ++rt305x_esw_set_led_freq(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ esw->led_frequency = val->value.i & RT305X_ESW_GSC_LED_FREQ_MASK; ++ ++ return 0; ++} ++ ++static int ++rt305x_esw_get_led_freq(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ val->value.i = esw->led_frequency; ++ ++ return 0; ++} ++ ++static int esw_get_port_link(struct switch_dev *dev, ++ int port, ++ struct switch_port_link *link) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ u32 speed, poa; ++ ++ if (port < 0 || port >= RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ poa = esw_r32(esw, RT305X_ESW_REG_POA) >> port; ++ ++ link->link = (poa >> RT305X_ESW_LINK_S) & 1; ++ link->duplex = (poa >> RT305X_ESW_DUPLEX_S) & 1; ++ if (port < RT305X_ESW_NUM_LEDS) { ++ speed = (poa >> RT305X_ESW_SPD_S) & 1; ++ } else { ++ if (port == RT305X_ESW_NUM_PORTS - 1) ++ poa >>= 1; ++ speed = (poa >> RT305X_ESW_SPD_S) & 3; ++ } ++ switch (speed) { ++ case 0: ++ link->speed = SWITCH_PORT_SPEED_10; ++ break; ++ case 1: ++ link->speed = SWITCH_PORT_SPEED_100; ++ break; ++ case 2: ++ case 3: /* forced gige speed can be 2 or 3 */ ++ link->speed = SWITCH_PORT_SPEED_1000; ++ break; ++ default: ++ link->speed = SWITCH_PORT_SPEED_UNKNOWN; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int esw_get_port_bool(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int idx = val->port_vlan; ++ u32 x, reg, shift; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ switch (attr->id) { ++ case RT305X_ESW_ATTR_PORT_DISABLE: ++ reg = RT305X_ESW_REG_POC0; ++ shift = RT305X_ESW_POC0_DIS_PORT_S; ++ break; ++ case RT305X_ESW_ATTR_PORT_DOUBLETAG: ++ reg = RT305X_ESW_REG_SGC2; ++ shift = RT305X_ESW_SGC2_DOUBLE_TAG_S; ++ break; ++ case RT305X_ESW_ATTR_PORT_UNTAG: ++ reg = RT305X_ESW_REG_POC2; ++ shift = RT305X_ESW_POC2_UNTAG_EN_S; ++ break; ++ case RT305X_ESW_ATTR_PORT_LAN: ++ reg = RT305X_ESW_REG_SGC2; ++ shift = RT305X_ESW_SGC2_LAN_PMAP_S; ++ if (idx >= RT305X_ESW_NUM_LANWAN) ++ return -EINVAL; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ x = esw_r32(esw, reg); ++ val->value.i = (x >> (idx + shift)) & 1; ++ ++ return 0; ++} ++ ++static int esw_set_port_bool(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int idx = val->port_vlan; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS || ++ val->value.i < 0 || val->value.i > 1) ++ return -EINVAL; ++ ++ switch (attr->id) { ++ case RT305X_ESW_ATTR_PORT_DISABLE: ++ esw->ports[idx].disable = val->value.i; ++ break; ++ case RT305X_ESW_ATTR_PORT_DOUBLETAG: ++ esw->ports[idx].doubletag = val->value.i; ++ break; ++ case RT305X_ESW_ATTR_PORT_UNTAG: ++ esw->ports[idx].untag = val->value.i; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int esw_get_port_recv_badgood(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int idx = val->port_vlan; ++ int shift = attr->id == RT305X_ESW_ATTR_PORT_RECV_GOOD ? 0 : 16; ++ u32 reg; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN) ++ return -EINVAL; ++ reg = esw_r32(esw, RT305X_ESW_REG_PXPC(idx)); ++ val->value.i = (reg >> shift) & 0xffff; ++ ++ return 0; ++} ++ ++static int ++esw_get_port_tr_badgood(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ int idx = val->port_vlan; ++ int shift = attr->id == RT5350_ESW_ATTR_PORT_TR_GOOD ? 0 : 16; ++ u32 reg; ++ ++ if (!soc_is_rt5350()) ++ return -EINVAL; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN) ++ return -EINVAL; ++ ++ reg = esw_r32(esw, RT5350_ESW_REG_PXTPC(idx)); ++ val->value.i = (reg >> shift) & 0xffff; ++ ++ return 0; ++} ++ ++static int esw_get_port_led(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int idx = val->port_vlan; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS || ++ idx >= RT305X_ESW_NUM_LEDS) ++ return -EINVAL; ++ ++ val->value.i = esw_r32(esw, RT305X_ESW_REG_P0LED + 4*idx); ++ ++ return 0; ++} ++ ++static int esw_set_port_led(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int idx = val->port_vlan; ++ ++ if (idx < 0 || idx >= RT305X_ESW_NUM_LEDS) ++ return -EINVAL; ++ ++ esw->ports[idx].led = val->value.i; ++ ++ return 0; ++} ++ ++static int esw_get_port_pvid(struct switch_dev *dev, int port, int *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ if (port >= RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ *val = esw_get_pvid(esw, port); ++ ++ return 0; ++} ++ ++static int esw_set_port_pvid(struct switch_dev *dev, int port, int val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ ++ if (port >= RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ esw->ports[port].pvid = val; ++ ++ return 0; ++} ++ ++static int esw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ u32 vmsc, poc2; ++ int vlan_idx = -1; ++ int i; ++ ++ val->len = 0; ++ ++ if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS) ++ return -EINVAL; ++ ++ /* valid vlan? */ ++ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { ++ if (esw_get_vlan_id(esw, i) == val->port_vlan && ++ esw_get_vmsc(esw, i) != RT305X_ESW_PORTS_NONE) { ++ vlan_idx = i; ++ break; ++ } ++ } ++ ++ if (vlan_idx == -1) ++ return -EINVAL; ++ ++ vmsc = esw_get_vmsc(esw, vlan_idx); ++ poc2 = esw_r32(esw, RT305X_ESW_REG_POC2); ++ ++ for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) { ++ struct switch_port *p; ++ int port_mask = 1 << i; ++ ++ if (!(vmsc & port_mask)) ++ continue; ++ ++ p = &val->value.ports[val->len++]; ++ p->id = i; ++ if (poc2 & (port_mask << RT305X_ESW_POC2_UNTAG_EN_S)) ++ p->flags = 0; ++ else ++ p->flags = 1 << SWITCH_PORT_FLAG_TAGGED; ++ } ++ ++ return 0; ++} ++ ++static int esw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev); ++ int ports; ++ int vlan_idx = -1; ++ int i; ++ ++ if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS || ++ val->len > RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ /* one of the already defined vlans? */ ++ for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) { ++ if (esw->vlans[i].vid == val->port_vlan && ++ esw->vlans[i].ports != RT305X_ESW_PORTS_NONE) { ++ vlan_idx = i; ++ break; ++ } ++ } ++ ++ /* select a free slot */ ++ for (i = 0; vlan_idx == -1 && i < RT305X_ESW_NUM_VLANS; i++) { ++ if (esw->vlans[i].ports == RT305X_ESW_PORTS_NONE) ++ vlan_idx = i; ++ } ++ ++ /* bail if all slots are in use */ ++ if (vlan_idx == -1) ++ return -EINVAL; ++ ++ ports = RT305X_ESW_PORTS_NONE; ++ for (i = 0; i < val->len; i++) { ++ struct switch_port *p = &val->value.ports[i]; ++ int port_mask = 1 << p->id; ++ bool untagged = !(p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)); ++ ++ if (p->id >= RT305X_ESW_NUM_PORTS) ++ return -EINVAL; ++ ++ ports |= port_mask; ++ esw->ports[p->id].untag = untagged; ++ } ++ esw->vlans[vlan_idx].ports = ports; ++ if (ports == RT305X_ESW_PORTS_NONE) ++ esw->vlans[vlan_idx].vid = RT305X_ESW_VLAN_NONE; ++ else ++ esw->vlans[vlan_idx].vid = val->port_vlan; ++ ++ return 0; ++} ++ ++static const struct switch_attr esw_global[] = { ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "enable_vlan", ++ .description = "VLAN mode (1:enabled)", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_ENABLE_VLAN, ++ .get = esw_get_vlan_enable, ++ .set = esw_set_vlan_enable, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "alternate_vlan_disable", ++ .description = "Use en_vlan instead of doubletag to disable" ++ " VLAN mode", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_ALT_VLAN_DISABLE, ++ .get = esw_get_alt_vlan_disable, ++ .set = esw_set_alt_vlan_disable, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "bc_storm_protect", ++ .description = "Global broadcast storm protection (0:Disable, 1:64 blocks, 2:96 blocks, 3:128 blocks)", ++ .max = 3, ++ .id = RT305X_ESW_ATTR_BC_STATUS, ++ .get = rt305x_esw_get_bc_status, ++ .set = rt305x_esw_set_bc_status, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "led_frequency", ++ .description = "LED Flash frequency (0:30mS, 1:60mS, 2:240mS, 3:480mS)", ++ .max = 3, ++ .id = RT305X_ESW_ATTR_LED_FREQ, ++ .get = rt305x_esw_get_led_freq, ++ .set = rt305x_esw_set_led_freq, ++ } ++}; ++ ++static const struct switch_attr esw_port[] = { ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "disable", ++ .description = "Port state (1:disabled)", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_PORT_DISABLE, ++ .get = esw_get_port_bool, ++ .set = esw_set_port_bool, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "doubletag", ++ .description = "Double tagging for incoming vlan packets " ++ "(1:enabled)", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_PORT_DOUBLETAG, ++ .get = esw_get_port_bool, ++ .set = esw_set_port_bool, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "untag", ++ .description = "Untag (1:strip outgoing vlan tag)", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_PORT_UNTAG, ++ .get = esw_get_port_bool, ++ .set = esw_set_port_bool, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "led", ++ .description = "LED mode (0:link, 1:100m, 2:duplex, 3:activity," ++ " 4:collision, 5:linkact, 6:duplcoll, 7:10mact," ++ " 8:100mact, 10:blink, 11:off, 12:on)", ++ .max = 15, ++ .id = RT305X_ESW_ATTR_PORT_LED, ++ .get = esw_get_port_led, ++ .set = esw_set_port_led, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "lan", ++ .description = "HW port group (0:wan, 1:lan)", ++ .max = 1, ++ .id = RT305X_ESW_ATTR_PORT_LAN, ++ .get = esw_get_port_bool, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "recv_bad", ++ .description = "Receive bad packet counter", ++ .id = RT305X_ESW_ATTR_PORT_RECV_BAD, ++ .get = esw_get_port_recv_badgood, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "recv_good", ++ .description = "Receive good packet counter", ++ .id = RT305X_ESW_ATTR_PORT_RECV_GOOD, ++ .get = esw_get_port_recv_badgood, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "tr_bad", ++ ++ .description = "Transmit bad packet counter. rt5350 only", ++ .id = RT5350_ESW_ATTR_PORT_TR_BAD, ++ .get = esw_get_port_tr_badgood, ++ }, ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "tr_good", ++ ++ .description = "Transmit good packet counter. rt5350 only", ++ .id = RT5350_ESW_ATTR_PORT_TR_GOOD, ++ .get = esw_get_port_tr_badgood, ++ }, ++}; ++ ++static const struct switch_attr esw_vlan[] = { ++}; ++ ++static const struct switch_dev_ops esw_ops = { ++ .attr_global = { ++ .attr = esw_global, ++ .n_attr = ARRAY_SIZE(esw_global), ++ }, ++ .attr_port = { ++ .attr = esw_port, ++ .n_attr = ARRAY_SIZE(esw_port), ++ }, ++ .attr_vlan = { ++ .attr = esw_vlan, ++ .n_attr = ARRAY_SIZE(esw_vlan), ++ }, ++ .get_vlan_ports = esw_get_vlan_ports, ++ .set_vlan_ports = esw_set_vlan_ports, ++ .get_port_pvid = esw_get_port_pvid, ++ .set_port_pvid = esw_set_port_pvid, ++ .get_port_link = esw_get_port_link, ++ .apply_config = esw_apply_config, ++ .reset_switch = esw_reset_switch, ++}; ++ ++static struct rt305x_esw_platform_data rt3050_esw_data = { ++ /* All ports are LAN ports. */ ++ .vlan_config = RT305X_ESW_VLAN_CONFIG_NONE, ++ .reg_initval_fct2 = 0x00d6500c, ++ /* ++ * ext phy base addr 31, enable port 5 polling, rx/tx clock skew 1, ++ * turbo mii off, rgmi 3.3v off ++ * port5: disabled ++ * port6: enabled, gige, full-duplex, rx/tx-flow-control ++ */ ++ .reg_initval_fpa2 = 0x3f502b28, ++}; ++ ++static const struct of_device_id ralink_esw_match[] = { ++ { .compatible = "ralink,rt3050-esw", .data = &rt3050_esw_data }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ralink_esw_match); ++ ++static int esw_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ const struct rt305x_esw_platform_data *pdata; ++ const __be32 *port_map, *reg_init; ++ struct rt305x_esw *esw; ++ struct switch_dev *swdev; ++ struct resource *res, *irq; ++ int err; ++ ++ pdata = pdev->dev.platform_data; ++ if (!pdata) { ++ const struct of_device_id *match; ++ match = of_match_device(ralink_esw_match, &pdev->dev); ++ if (match) ++ pdata = (struct rt305x_esw_platform_data *) match->data; ++ } ++ if (!pdata) ++ return -EINVAL; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "no memory resource found\n"); ++ return -ENOMEM; ++ } ++ ++ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!irq) { ++ dev_err(&pdev->dev, "no irq resource found\n"); ++ return -ENOMEM; ++ } ++ ++ esw = kzalloc(sizeof(struct rt305x_esw), GFP_KERNEL); ++ if (!esw) { ++ dev_err(&pdev->dev, "no memory for private data\n"); ++ return -ENOMEM; ++ } ++ ++ esw->dev = &pdev->dev; ++ esw->irq = irq->start; ++ esw->base = ioremap(res->start, resource_size(res)); ++ if (!esw->base) { ++ dev_err(&pdev->dev, "ioremap failed\n"); ++ err = -ENOMEM; ++ goto free_esw; ++ } ++ ++ port_map = of_get_property(np, "ralink,portmap", NULL); ++ if (port_map) ++ esw->port_map = be32_to_cpu(*port_map); ++ ++ reg_init = of_get_property(np, "ralink,fct2", NULL); ++ if (reg_init) ++ esw->reg_initval_fct2 = be32_to_cpu(*reg_init); ++ ++ reg_init = of_get_property(np, "ralink,fpa2", NULL); ++ if (reg_init) ++ esw->reg_initval_fpa2 = be32_to_cpu(*reg_init); ++ ++ reg_init = of_get_property(np, "ralink,led_polarity", NULL); ++ if (reg_init) ++ esw->reg_led_polarity = be32_to_cpu(*reg_init); ++ ++ swdev = &esw->swdev; ++ swdev->of_node = pdev->dev.of_node; ++ swdev->name = "rt305x-esw"; ++ swdev->alias = "rt305x"; ++ swdev->cpu_port = RT305X_ESW_PORT6; ++ swdev->ports = RT305X_ESW_NUM_PORTS; ++ swdev->vlans = RT305X_ESW_NUM_VIDS; ++ swdev->ops = &esw_ops; ++ ++ err = register_switch(swdev, NULL); ++ if (err < 0) { ++ dev_err(&pdev->dev, "register_switch failed\n"); ++ goto unmap_base; ++ } ++ ++ platform_set_drvdata(pdev, esw); ++ ++ esw->pdata = pdata; ++ spin_lock_init(&esw->reg_rw_lock); ++ ++ esw_hw_init(esw); ++ ++ esw_w32(esw, RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_ISR); ++ esw_w32(esw, ~RT305X_ESW_PORT_ST_CHG, RT305X_ESW_REG_IMR); ++ request_irq(esw->irq, esw_interrupt, 0, "esw", esw); ++ ++ return 0; ++ ++unmap_base: ++ iounmap(esw->base); ++free_esw: ++ kfree(esw); ++ return err; ++} ++ ++static int esw_remove(struct platform_device *pdev) ++{ ++ struct rt305x_esw *esw; ++ ++ esw = platform_get_drvdata(pdev); ++ if (esw) { ++ unregister_switch(&esw->swdev); ++ platform_set_drvdata(pdev, NULL); ++ iounmap(esw->base); ++ kfree(esw); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver esw_driver = { ++ .probe = esw_probe, ++ .remove = esw_remove, ++ .driver = { ++ .name = "rt305x-esw", ++ .owner = THIS_MODULE, ++ .of_match_table = ralink_esw_match, ++ }, ++}; ++ ++int __init rtesw_init(void) ++{ ++ return platform_driver_register(&esw_driver); ++} ++ ++void rtesw_exit(void) ++{ ++ platform_driver_unregister(&esw_driver); ++} +diff --git a/drivers/net/ethernet/ralink/esw_rt3052.h b/drivers/net/ethernet/ralink/esw_rt3052.h +new file mode 100644 +index 0000000..2ced3dff +--- /dev/null ++++ b/drivers/net/ethernet/ralink/esw_rt3052.h +@@ -0,0 +1,32 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#ifndef _RALINK_ESW_RT3052_H__ ++#define _RALINK_ESW_RT3052_H__ ++ ++#ifdef CONFIG_NET_RALINK_ESW_RT3052 ++ ++int __init rtesw_init(void); ++void rtesw_exit(void); ++ ++#else ++ ++static inline int __init rtesw_init(void) { return 0; } ++static inline void rtesw_exit(void) { } ++ ++#endif ++#endif +diff --git a/drivers/net/ethernet/ralink/gsw_mt7620a.c b/drivers/net/ethernet/ralink/gsw_mt7620a.c +new file mode 100644 +index 0000000..9fa6a54 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/gsw_mt7620a.c +@@ -0,0 +1,1027 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "ralink_soc_eth.h" ++#include "gsw_mt7620a.h" ++#include "mdio.h" ++ ++#define GSW_REG_PHY_TIMEOUT (5 * HZ) ++ ++#define MT7620A_GSW_REG_PIAC 0x7004 ++ ++#define GSW_NUM_VLANS 16 ++#define GSW_NUM_VIDS 4096 ++#define GSW_NUM_PORTS 7 ++#define GSW_PORT6 6 ++ ++#define GSW_MDIO_ACCESS BIT(31) ++#define GSW_MDIO_READ BIT(19) ++#define GSW_MDIO_WRITE BIT(18) ++#define GSW_MDIO_START BIT(16) ++#define GSW_MDIO_ADDR_SHIFT 20 ++#define GSW_MDIO_REG_SHIFT 25 ++ ++#define GSW_REG_PORT_PMCR(x) (0x3000 + (x * 0x100)) ++#define GSW_REG_PORT_STATUS(x) (0x3008 + (x * 0x100)) ++#define GSW_REG_SMACCR0 0x3fE4 ++#define GSW_REG_SMACCR1 0x3fE8 ++#define GSW_REG_CKGCR 0x3ff0 ++ ++#define GSW_REG_IMR 0x7008 ++#define GSW_REG_ISR 0x700c ++ ++#define SYSC_REG_CFG1 0x14 ++ ++#define PORT_IRQ_ST_CHG 0x7f ++ ++#define GSW_VLAN_VTCR 0x90 ++#define GSW_VLAN_VTCR_VID_M 0xfff ++#define GSW_VLAN_ID(_x) (0x100 + (4 * (_x))) ++#define GSW_VLAN_ID_VID_S 12 ++#define GSW_VLAN_ID_VID_M 0xfff ++ ++#define GSW_VAWD1 0x94 ++#define GSW_VAWD1_VTAG_EN BIT(28) ++#define GSW_VAWD1_PORTM_S 16 ++#define GSW_VAWD1_PORTM_M 0xff ++ ++#define GSW_VAWD2 0x98 ++#define GSW_VAWD2_PORTT_S 16 ++#define GSW_VAWD2_PORTT_M 0xff ++ ++#define GSW_VTIM(_x) (0x100 + (4 * (_x))) ++#define GSW_VTIM_M 0xfff ++#define GSW_VTIM_S 12 ++ ++#define GSW_REG_PCR(x) (0x2004 + (x * 0x100)) ++#define GSW_REG_PCR_EG_TAG_S 28 ++#define GSW_REG_PCR_EG_TAG_M 0x3 ++ ++#define SYSCFG1 0x14 ++ ++#define ESW_PHY_POLLING 0x7000 ++ ++#define PMCR_IPG BIT(18) ++#define PMCR_MAC_MODE BIT(16) ++#define PMCR_FORCE BIT(15) ++#define PMCR_TX_EN BIT(14) ++#define PMCR_RX_EN BIT(13) ++#define PMCR_BACKOFF BIT(9) ++#define PMCR_BACKPRES BIT(8) ++#define PMCR_RX_FC BIT(5) ++#define PMCR_TX_FC BIT(4) ++#define PMCR_SPEED(_x) (_x << 2) ++#define PMCR_DUPLEX BIT(1) ++#define PMCR_LINK BIT(0) ++ ++#define PHY_AN_EN BIT(31) ++#define PHY_PRE_EN BIT(30) ++#define PMY_MDC_CONF(_x) ((_x & 0x3f) << 24) ++ ++enum { ++ /* Global attributes. */ ++ GSW_ATTR_ENABLE_VLAN, ++ /* Port attributes. */ ++ GSW_ATTR_PORT_UNTAG, ++}; ++ ++enum { ++ PORT4_EPHY = 0, ++ PORT4_EXT, ++}; ++ ++struct gsw_port { ++ bool disable; ++ bool untag; ++ u16 pvid; ++}; ++ ++struct gsw_vlan { ++ u8 ports; ++ u16 vid; ++}; ++ ++struct mt7620_gsw { ++ struct device *dev; ++ void __iomem *base; ++ int irq; ++ ++ struct switch_dev swdev; ++ bool global_vlan_enable; ++ struct gsw_vlan vlans[GSW_NUM_VLANS]; ++ struct gsw_port ports[GSW_NUM_PORTS]; ++ long unsigned int autopoll; ++ int port4; ++}; ++ ++static inline void gsw_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg) ++{ ++ iowrite32(val, gsw->base + reg); ++} ++ ++static inline u32 gsw_r32(struct mt7620_gsw *gsw, unsigned reg) ++{ ++ return ioread32(gsw->base + reg); ++} ++ ++static int mt7620_mii_busy_wait(struct mt7620_gsw *gsw) ++{ ++ unsigned long t_start = jiffies; ++ ++ while (1) { ++ if (!(gsw_r32(gsw, MT7620A_GSW_REG_PIAC) & GSW_MDIO_ACCESS)) ++ return 0; ++ if (time_after(jiffies, t_start + GSW_REG_PHY_TIMEOUT)) { ++ break; ++ } ++ } ++ ++ printk(KERN_ERR "mdio: MDIO timeout\n"); ++ return -1; ++} ++ ++static u32 _mt7620_mii_write(struct mt7620_gsw *gsw, u32 phy_addr, u32 phy_register, ++ u32 write_data) ++{ ++ if (mt7620_mii_busy_wait(gsw)) ++ return -1; ++ ++ write_data &= 0xffff; ++ ++ gsw_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_WRITE | ++ (phy_register << GSW_MDIO_REG_SHIFT) | ++ (phy_addr << GSW_MDIO_ADDR_SHIFT) | write_data, ++ MT7620A_GSW_REG_PIAC); ++ ++ if (mt7620_mii_busy_wait(gsw)) ++ return -1; ++ ++ return 0; ++} ++ ++int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val) ++{ ++ struct fe_priv *priv = bus->priv; ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ ++ return _mt7620_mii_write(gsw, phy_addr, phy_reg, val); ++} ++ ++int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) ++{ ++ struct fe_priv *priv = bus->priv; ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ u32 d; ++ ++ if (mt7620_mii_busy_wait(gsw)) ++ return 0xffff; ++ ++ gsw_w32(gsw, GSW_MDIO_ACCESS | GSW_MDIO_START | GSW_MDIO_READ | ++ (phy_reg << GSW_MDIO_REG_SHIFT) | ++ (phy_addr << GSW_MDIO_ADDR_SHIFT), ++ MT7620A_GSW_REG_PIAC); ++ ++ if (mt7620_mii_busy_wait(gsw)) ++ return 0xffff; ++ ++ d = gsw_r32(gsw, MT7620A_GSW_REG_PIAC) & 0xffff; ++ ++ return d; ++} ++ ++static unsigned char *fe_speed_str(int speed) ++{ ++ switch (speed) { ++ case 2: ++ case SPEED_1000: ++ return "1000"; ++ case 1: ++ case SPEED_100: ++ return "100"; ++ case 0: ++ case SPEED_10: ++ return "10"; ++ } ++ ++ return "? "; ++} ++ ++int mt7620a_has_carrier(struct fe_priv *priv) ++{ ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ int i; ++ ++ for (i = 0; i < GSW_PORT6; i++) ++ if (gsw_r32(gsw, GSW_REG_PORT_STATUS(i)) & 0x1) ++ return 1; ++ return 0; ++} ++ ++static void mt7620a_handle_carrier(struct fe_priv *priv) ++{ ++ if (!priv->phy) ++ return; ++ ++ if (mt7620a_has_carrier(priv)) ++ netif_carrier_on(priv->netdev); ++ else ++ netif_carrier_off(priv->netdev); ++} ++ ++void mt7620_mdio_link_adjust(struct fe_priv *priv, int port) ++{ ++ if (priv->link[port]) ++ netdev_info(priv->netdev, "port %d link up (%sMbps/%s duplex)\n", ++ port, fe_speed_str(priv->phy->speed[port]), ++ (DUPLEX_FULL == priv->phy->duplex[port]) ? "Full" : "Half"); ++ else ++ netdev_info(priv->netdev, "port %d link down\n", port); ++ mt7620a_handle_carrier(priv); ++} ++ ++static irqreturn_t gsw_interrupt(int irq, void *_priv) ++{ ++ struct fe_priv *priv = (struct fe_priv *) _priv; ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ u32 status; ++ int i, max = (gsw->port4 == PORT4_EPHY) ? (4) : (3); ++ ++ status = gsw_r32(gsw, GSW_REG_ISR); ++ if (status & PORT_IRQ_ST_CHG) ++ for (i = 0; i <= max; i++) { ++ u32 status = gsw_r32(gsw, GSW_REG_PORT_STATUS(i)); ++ int link = status & 0x1; ++ ++ if (link != priv->link[i]) { ++ if (link) ++ netdev_info(priv->netdev, "port %d link up (%sMbps/%s duplex)\n", ++ i, fe_speed_str((status >> 2) & 3), ++ (status & 0x2) ? "Full" : "Half"); ++ else ++ netdev_info(priv->netdev, "port %d link down\n", i); ++ } ++ ++ priv->link[i] = link; ++ } ++ mt7620a_handle_carrier(priv); ++ ++ gsw_w32(gsw, status, GSW_REG_ISR); ++ ++ return IRQ_HANDLED; ++} ++ ++static int mt7620_is_bga(void) ++{ ++ u32 bga = rt_sysc_r32(0x0c); ++ ++ return (bga >> 16) & 1; ++} ++ ++static void gsw_auto_poll(struct mt7620_gsw *gsw) ++{ ++ int phy; ++ int lsb = -1, msb = 0; ++ ++ for_each_set_bit(phy, &gsw->autopoll, 32) { ++ if (lsb < 0) ++ lsb = phy; ++ msb = phy; ++ } ++ ++ gsw_w32(gsw, PHY_AN_EN | PHY_PRE_EN | PMY_MDC_CONF(5) | (msb << 8) | lsb, ESW_PHY_POLLING); ++} ++ ++void mt7620_port_init(struct fe_priv *priv, struct device_node *np) ++{ ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ const __be32 *_id = of_get_property(np, "reg", NULL); ++ int phy_mode, size, id; ++ int shift = 12; ++ u32 val, mask = 0; ++ int min = (gsw->port4 == PORT4_EPHY) ? (5) : (4); ++ ++ if (!_id || (be32_to_cpu(*_id) < min) || (be32_to_cpu(*_id) > 5)) { ++ if (_id) ++ pr_err("%s: invalid port id %d\n", np->name, be32_to_cpu(*_id)); ++ else ++ pr_err("%s: invalid port id\n", np->name); ++ return; ++ } ++ ++ id = be32_to_cpu(*_id); ++ ++ if (id == 4) ++ shift = 14; ++ ++ priv->phy->phy_fixed[id] = of_get_property(np, "ralink,fixed-link", &size); ++ if (priv->phy->phy_fixed[id] && (size != (4 * sizeof(*priv->phy->phy_fixed[id])))) { ++ pr_err("%s: invalid fixed link property\n", np->name); ++ priv->phy->phy_fixed[id] = NULL; ++ return; ++ } ++ ++ phy_mode = of_get_phy_mode(np); ++ switch (phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ mask = 0; ++ break; ++ case PHY_INTERFACE_MODE_MII: ++ mask = 1; ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ mask = 2; ++ break; ++ default: ++ dev_err(priv->device, "port %d - invalid phy mode\n", priv->phy->speed[id]); ++ return; ++ } ++ ++ priv->phy->phy_node[id] = of_parse_phandle(np, "phy-handle", 0); ++ if (!priv->phy->phy_node[id] && !priv->phy->phy_fixed[id]) ++ return; ++ ++ val = rt_sysc_r32(SYSCFG1); ++ val &= ~(3 << shift); ++ val |= mask << shift; ++ rt_sysc_w32(val, SYSCFG1); ++ ++ if (priv->phy->phy_fixed[id]) { ++ const __be32 *link = priv->phy->phy_fixed[id]; ++ int tx_fc = be32_to_cpup(link++); ++ int rx_fc = be32_to_cpup(link++); ++ u32 val = 0; ++ ++ priv->phy->speed[id] = be32_to_cpup(link++); ++ priv->phy->duplex[id] = be32_to_cpup(link++); ++ priv->link[id] = 1; ++ ++ switch (priv->phy->speed[id]) { ++ case SPEED_10: ++ val = 0; ++ break; ++ case SPEED_100: ++ val = 1; ++ break; ++ case SPEED_1000: ++ val = 2; ++ break; ++ default: ++ dev_err(priv->device, "invalid link speed: %d\n", priv->phy->speed[id]); ++ priv->phy->phy_fixed[id] = 0; ++ return; ++ } ++ val = PMCR_SPEED(val); ++ val |= PMCR_LINK | PMCR_BACKPRES | PMCR_BACKOFF | PMCR_RX_EN | ++ PMCR_TX_EN | PMCR_FORCE | PMCR_MAC_MODE | PMCR_IPG; ++ if (tx_fc) ++ val |= PMCR_TX_FC; ++ if (rx_fc) ++ val |= PMCR_RX_FC; ++ if (priv->phy->duplex[id]) ++ val |= PMCR_DUPLEX; ++ gsw_w32(gsw, val, GSW_REG_PORT_PMCR(id)); ++ dev_info(priv->device, "using fixed link parameters\n"); ++ return; ++ } ++ ++ if (priv->phy->phy_node[id] && priv->mii_bus->phy_map[id]) { ++ u32 val = PMCR_BACKPRES | PMCR_BACKOFF | PMCR_RX_EN | ++ PMCR_TX_EN | PMCR_MAC_MODE | PMCR_IPG; ++ ++ gsw_w32(gsw, val, GSW_REG_PORT_PMCR(id)); ++ fe_connect_phy_node(priv, priv->phy->phy_node[id]); ++ gsw->autopoll |= BIT(id); ++ gsw_auto_poll(gsw); ++ return; ++ } ++} ++ ++static void gsw_hw_init(struct mt7620_gsw *gsw) ++{ ++ u32 is_BGA = mt7620_is_bga(); ++ ++ rt_sysc_w32(rt_sysc_r32(SYSC_REG_CFG1) | BIT(8), SYSC_REG_CFG1); ++ gsw_w32(gsw, gsw_r32(gsw, GSW_REG_CKGCR) & ~(0x3 << 4), GSW_REG_CKGCR); ++ ++ /*correct PHY setting L3.0 BGA*/ ++ _mt7620_mii_write(gsw, 1, 31, 0x4000); //global, page 4 ++ ++ _mt7620_mii_write(gsw, 1, 17, 0x7444); ++ if (is_BGA) ++ _mt7620_mii_write(gsw, 1, 19, 0x0114); ++ else ++ _mt7620_mii_write(gsw, 1, 19, 0x0117); ++ ++ _mt7620_mii_write(gsw, 1, 22, 0x10cf); ++ _mt7620_mii_write(gsw, 1, 25, 0x6212); ++ _mt7620_mii_write(gsw, 1, 26, 0x0777); ++ _mt7620_mii_write(gsw, 1, 29, 0x4000); ++ _mt7620_mii_write(gsw, 1, 28, 0xc077); ++ _mt7620_mii_write(gsw, 1, 24, 0x0000); ++ ++ _mt7620_mii_write(gsw, 1, 31, 0x3000); //global, page 3 ++ _mt7620_mii_write(gsw, 1, 17, 0x4838); ++ ++ _mt7620_mii_write(gsw, 1, 31, 0x2000); //global, page 2 ++ if (is_BGA) { ++ _mt7620_mii_write(gsw, 1, 21, 0x0515); ++ _mt7620_mii_write(gsw, 1, 22, 0x0053); ++ _mt7620_mii_write(gsw, 1, 23, 0x00bf); ++ _mt7620_mii_write(gsw, 1, 24, 0x0aaf); ++ _mt7620_mii_write(gsw, 1, 25, 0x0fad); ++ _mt7620_mii_write(gsw, 1, 26, 0x0fc1); ++ } else { ++ _mt7620_mii_write(gsw, 1, 21, 0x0517); ++ _mt7620_mii_write(gsw, 1, 22, 0x0fd2); ++ _mt7620_mii_write(gsw, 1, 23, 0x00bf); ++ _mt7620_mii_write(gsw, 1, 24, 0x0aab); ++ _mt7620_mii_write(gsw, 1, 25, 0x00ae); ++ _mt7620_mii_write(gsw, 1, 26, 0x0fff); ++ } ++ _mt7620_mii_write(gsw, 1, 31, 0x1000); //global, page 1 ++ _mt7620_mii_write(gsw, 1, 17, 0xe7f8); ++ ++ _mt7620_mii_write(gsw, 1, 31, 0x8000); //local, page 0 ++ _mt7620_mii_write(gsw, 0, 30, 0xa000); ++ _mt7620_mii_write(gsw, 1, 30, 0xa000); ++ _mt7620_mii_write(gsw, 2, 30, 0xa000); ++ _mt7620_mii_write(gsw, 3, 30, 0xa000); ++ ++ _mt7620_mii_write(gsw, 0, 4, 0x05e1); ++ _mt7620_mii_write(gsw, 1, 4, 0x05e1); ++ _mt7620_mii_write(gsw, 2, 4, 0x05e1); ++ _mt7620_mii_write(gsw, 3, 4, 0x05e1); ++ _mt7620_mii_write(gsw, 1, 31, 0xa000); //local, page 2 ++ _mt7620_mii_write(gsw, 0, 16, 0x1111); ++ _mt7620_mii_write(gsw, 1, 16, 0x1010); ++ _mt7620_mii_write(gsw, 2, 16, 0x1515); ++ _mt7620_mii_write(gsw, 3, 16, 0x0f0f); ++ ++ /* CPU Port6 Force Link 1G, FC ON */ ++ gsw_w32(gsw, 0x5e33b, GSW_REG_PORT_PMCR(6)); ++ /* Set Port6 CPU Port */ ++ gsw_w32(gsw, 0x7f7f7fe0, 0x0010); ++ ++// GSW_VAWD2 ++ ++ /* setup port 4 */ ++ if (gsw->port4 == PORT4_EPHY) { ++ u32 val = rt_sysc_r32(SYSCFG1); ++ val |= 3 << 14; ++ rt_sysc_w32(val, SYSCFG1); ++ _mt7620_mii_write(gsw, 4, 30, 0xa000); ++ _mt7620_mii_write(gsw, 4, 4, 0x05e1); ++ _mt7620_mii_write(gsw, 4, 16, 0x1313); ++ pr_info("gsw: setting port4 to ephy mode\n"); ++ } ++} ++ ++static int gsw_reset_switch(struct switch_dev *dev) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ ++ gsw->global_vlan_enable = 0; ++ memset(gsw->ports, 0, sizeof(gsw->ports)); ++ memset(gsw->vlans, 0, sizeof(gsw->vlans)); ++ gsw_hw_init(gsw); ++ ++ return 0; ++} ++ ++static int gsw_get_vlan_enable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ ++ val->value.i = gsw->global_vlan_enable; ++ ++ return 0; ++} ++ ++static int gsw_set_vlan_enable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ ++ gsw->global_vlan_enable = val->value.i != 0; ++ ++ return 0; ++} ++ ++static unsigned gsw_get_pvid(struct mt7620_gsw *gsw, unsigned port) ++{ ++ unsigned s, val; ++ ++ s = GSW_VTIM_S * (port % 2); ++ val = gsw_r32(gsw, GSW_VTIM(port / 2)); ++ ++ return (val >> s) & GSW_VTIM_M; ++} ++ ++static void gsw_set_pvid(struct mt7620_gsw *gsw, unsigned port, unsigned pvid) ++{ ++ unsigned s, val; ++ ++ s = GSW_VTIM_S * (port % 2); ++ val = gsw_r32(gsw, GSW_VTIM(port / 2)); ++ val &= ~(GSW_VTIM_M << s); ++ val |= (pvid && GSW_VTIM_M) << s; ++ gsw_w32(gsw, val, GSW_VTIM(port / 2)); ++} ++ ++static int gsw_get_port_bool(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int idx = val->port_vlan; ++ ++ if (idx < 0 || idx >= GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ switch (attr->id) { ++ case GSW_ATTR_PORT_UNTAG: ++ return gsw->ports[idx].untag; ++ } ++ ++ return -EINVAL; ++} ++ ++static int gsw_get_port_pvid(struct switch_dev *dev, int port, int *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ ++ if (port >= GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ *val = gsw_get_pvid(gsw, port); ++ ++ return 0; ++} ++ ++static int gsw_set_port_pvid(struct switch_dev *dev, int port, int val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ ++ if (port >= GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ gsw->ports[port].pvid = val; ++ ++ return 0; ++} ++ ++static void gsw_set_vtcr(struct switch_dev *dev, u32 vid) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int retry = 1000; ++ ++ gsw_w32(gsw, 0x80000000 | (BIT(vid) & GSW_VLAN_VTCR_VID_M), GSW_VLAN_VTCR); ++ while (retry-- && (gsw_r32(gsw, GSW_VLAN_VTCR) & 0x80000000)) ++ ; ++} ++ ++static void gsw_apply_vtcr(struct switch_dev *dev, u32 vid) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int retry = 1000; ++ ++ gsw_w32(gsw, 0x80001000 | (BIT(vid) & GSW_VLAN_VTCR_VID_M), GSW_VLAN_VTCR); ++ while (retry-- && (gsw_r32(gsw, GSW_VLAN_VTCR) & 0x80000000)) ++ ; ++} ++ ++static unsigned gsw_get_vlan_id(struct mt7620_gsw *gsw, unsigned vlan) ++{ ++ unsigned s; ++ unsigned val; ++ ++ s = GSW_VLAN_ID_VID_S * (vlan % 2); ++ val = gsw_r32(gsw, GSW_VLAN_ID(vlan / 2)); ++ val = (val >> s) & GSW_VLAN_ID_VID_M; ++ ++ return val; ++} ++ ++static void gsw_set_vlan_id(struct mt7620_gsw *gsw, unsigned vlan, unsigned vid) ++{ ++ unsigned s; ++ unsigned val; ++ ++ s = GSW_VLAN_ID_VID_S * (vlan % 2); ++ val = gsw_r32(gsw, GSW_VLAN_ID(vlan / 2)); ++ val &= ~(GSW_VLAN_ID_VID_M << s); ++ val |= (vid << s); ++ gsw_w32(gsw, val, GSW_VLAN_ID(vlan / 2)); ++} ++ ++static void gsw_vlan_tagging_enable(struct mt7620_gsw *gsw, unsigned vlan, unsigned enable) ++{ ++ unsigned val; ++ ++ val = gsw_r32(gsw, GSW_VAWD1); ++ if (enable) ++ val |= GSW_VAWD1_VTAG_EN; ++ else ++ val &= ~GSW_VAWD1_VTAG_EN; ++ gsw_w32(gsw, val, GSW_VAWD1); ++} ++ ++static unsigned gsw_get_port_member(struct mt7620_gsw *gsw, unsigned vlan) ++{ ++ unsigned val; ++ ++ gsw_set_vtcr(&gsw->swdev, vlan); ++ ++ val = gsw_r32(gsw, GSW_VAWD1); ++ val = (val >> GSW_VAWD1_PORTM_S) & GSW_VAWD1_PORTM_M; ++ ++ return val; ++} ++ ++static void gsw_set_port_member(struct mt7620_gsw *gsw, unsigned vlan, unsigned member) ++{ ++ unsigned val; ++ ++ val = gsw_r32(gsw, GSW_VAWD1); ++ val = ~(GSW_VAWD1_PORTM_M << GSW_VAWD1_PORTM_S); ++ val |= (member & GSW_VAWD1_PORTM_M) << GSW_VAWD1_PORTM_S; ++ gsw_w32(gsw, val, GSW_VAWD1); ++} ++ ++static unsigned gsw_get_port_tag(struct mt7620_gsw *gsw, unsigned port) ++{ ++ unsigned val; ++ ++ val = gsw_r32(gsw, GSW_REG_PCR(port)); ++ val >>= GSW_REG_PCR_EG_TAG_S; ++ val &= GSW_REG_PCR_EG_TAG_M; ++ ++ return !!val; ++} ++ ++static void gsw_set_port_untag(struct mt7620_gsw *gsw, unsigned port, unsigned untag) ++{ ++ unsigned val; ++ ++ val = gsw_r32(gsw, GSW_REG_PCR(port)); ++ if (!untag) ++ untag = 0x2; ++ else ++ untag = 0; ++ val &= ~(GSW_REG_PCR_EG_TAG_M << GSW_REG_PCR_EG_TAG_S); ++ val |= (untag & GSW_REG_PCR_EG_TAG_M) << GSW_REG_PCR_EG_TAG_S; ++ gsw_w32(gsw, val, GSW_REG_PCR(port)); ++} ++ ++static int gsw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int vlan_idx = -1; ++ u32 member; ++ int i; ++ ++ val->len = 0; ++ ++ if (val->port_vlan < 0 || val->port_vlan >= GSW_NUM_VIDS) ++ return -EINVAL; ++ ++ /* valid vlan? */ ++ for (i = 0; i < GSW_NUM_VLANS; i++) { ++ if (gsw_get_vlan_id(gsw, i) != val->port_vlan) ++ continue; ++ member = gsw_get_port_member(gsw, i); ++ vlan_idx = i; ++ break; ++ } ++ ++ if (vlan_idx == -1) ++ return -EINVAL; ++ ++ for (i = 0; i < GSW_NUM_PORTS; i++) { ++ struct switch_port *p; ++ int port_mask = 1 << i; ++ ++ if (!(member & port_mask)) ++ continue; ++ ++ p = &val->value.ports[val->len++]; ++ p->id = i; ++ if (gsw_get_port_tag(gsw, i)) ++ p->flags = 1 << SWITCH_PORT_FLAG_TAGGED; ++ else ++ p->flags = 0; ++ } ++ ++ return 0; ++} ++ ++static int gsw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int ports; ++ int vlan_idx = -1; ++ int i; ++ ++ if (val->port_vlan < 0 || val->port_vlan >= GSW_NUM_VIDS || ++ val->len > GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ /* one of the already defined vlans? */ ++ for (i = 0; i < GSW_NUM_VLANS; i++) { ++ if (gsw->vlans[i].vid == val->port_vlan && ++ gsw->vlans[i].ports) { ++ vlan_idx = i; ++ break; ++ } ++ } ++ ++ /* select a free slot */ ++ for (i = 0; vlan_idx == -1 && i < GSW_NUM_VLANS; i++) { ++ if (!gsw->vlans[i].ports) ++ vlan_idx = i; ++ } ++ ++ /* bail if all slots are in use */ ++ if (vlan_idx == -1) ++ return -EINVAL; ++ ++ ports = 0; ++ for (i = 0; i < val->len; i++) { ++ struct switch_port *p = &val->value.ports[i]; ++ int port_mask = 1 << p->id; ++ bool untagged = !(p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)); ++ ++ if (p->id >= GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ ports |= port_mask; ++ gsw->ports[p->id].untag = untagged; ++ } ++ gsw->vlans[vlan_idx].ports = ports; ++ if (!ports) ++ gsw->vlans[vlan_idx].vid = 0xfff; ++ else ++ gsw->vlans[vlan_idx].vid = val->port_vlan; ++ ++ return 0; ++} ++ ++static int gsw_apply_config(struct switch_dev *dev) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int i; ++ ++ for (i = 0; i < GSW_NUM_VLANS; i++) { ++ gsw_set_vtcr(&gsw->swdev, i); ++ if (gsw->global_vlan_enable) { ++ gsw_set_vlan_id(gsw, i, gsw->vlans[i].vid); ++ gsw_set_port_member(gsw, i, gsw->vlans[i].ports); ++ gsw_vlan_tagging_enable(gsw, i, 1); ++ } else { ++ gsw_set_vlan_id(gsw, i, 0xfff); ++ gsw_set_port_member(gsw, i, 0); ++ gsw_vlan_tagging_enable(gsw, i, 0); ++ } ++ gsw_apply_vtcr(&gsw->swdev, i); ++ } ++ ++ for (i = 0; i < GSW_NUM_PORTS; i++) { ++ if (gsw->global_vlan_enable) { ++ gsw_set_port_untag(gsw, i, !gsw->ports[i].untag); ++ gsw_set_pvid(gsw, i, gsw->ports[i].pvid); ++ } else { ++ gsw_set_port_untag(gsw, i, 0); ++ gsw_set_pvid(gsw, i, 0); ++ } ++ } ++ ++ if (!gsw->global_vlan_enable) ++ gsw_set_vlan_id(gsw, 0, 0); ++ ++ return 0; ++} ++ ++static int gsw_get_port_link(struct switch_dev *dev, ++ int port, ++ struct switch_port_link *link) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ u32 status; ++ ++ if (port < 0 || port >= GSW_NUM_PORTS) ++ return -EINVAL; ++ ++ status = gsw_r32(gsw, GSW_REG_PORT_STATUS(port)); ++ link->link = status & 0x1; ++ link->duplex = (status >> 1) & 1; ++ ++ switch ((status >> 2) & 0x3) { ++ case 0: ++ link->speed = SWITCH_PORT_SPEED_10; ++ break; ++ case 1: ++ link->speed = SWITCH_PORT_SPEED_100; ++ break; ++ case 2: ++ case 3: // forced gige speed can be 2 or 3 ++ link->speed = SWITCH_PORT_SPEED_1000; ++ break; ++ } ++ ++ return 0; ++} ++ ++static int gsw_set_port_bool(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); ++ int idx = val->port_vlan; ++ ++ if (idx < 0 || idx >= GSW_NUM_PORTS || ++ val->value.i < 0 || val->value.i > 1) ++ return -EINVAL; ++ ++ switch (attr->id) { ++ case GSW_ATTR_PORT_UNTAG: ++ gsw->ports[idx].untag = val->value.i; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static const struct switch_attr gsw_global[] = { ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "enable_vlan", ++ .description = "VLAN mode (1:enabled)", ++ .max = 1, ++ .id = GSW_ATTR_ENABLE_VLAN, ++ .get = gsw_get_vlan_enable, ++ .set = gsw_set_vlan_enable, ++ }, ++}; ++ ++static const struct switch_attr gsw_port[] = { ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "untag", ++ .description = "Untag (1:strip outgoing vlan tag)", ++ .max = 1, ++ .id = GSW_ATTR_PORT_UNTAG, ++ .get = gsw_get_port_bool, ++ .set = gsw_set_port_bool, ++ }, ++}; ++ ++static const struct switch_attr gsw_vlan[] = { ++}; ++ ++static const struct switch_dev_ops gsw_ops = { ++ .attr_global = { ++ .attr = gsw_global, ++ .n_attr = ARRAY_SIZE(gsw_global), ++ }, ++ .attr_port = { ++ .attr = gsw_port, ++ .n_attr = ARRAY_SIZE(gsw_port), ++ }, ++ .attr_vlan = { ++ .attr = gsw_vlan, ++ .n_attr = ARRAY_SIZE(gsw_vlan), ++ }, ++ .get_vlan_ports = gsw_get_vlan_ports, ++ .set_vlan_ports = gsw_set_vlan_ports, ++ .get_port_pvid = gsw_get_port_pvid, ++ .set_port_pvid = gsw_set_port_pvid, ++ .get_port_link = gsw_get_port_link, ++ .apply_config = gsw_apply_config, ++ .reset_switch = gsw_reset_switch, ++}; ++ ++void mt7620_set_mac(struct fe_priv *priv, unsigned char *mac) ++{ ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ gsw_w32(gsw, (mac[0] << 8) | mac[1], GSW_REG_SMACCR1); ++ gsw_w32(gsw, (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], ++ GSW_REG_SMACCR0); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++} ++ ++static struct of_device_id gsw_match[] = { ++ { .compatible = "ralink,mt7620a-gsw" }, ++ {} ++}; ++ ++int mt7620_gsw_probe(struct fe_priv *priv) ++{ ++ struct mt7620_gsw *gsw; ++ struct device_node *np; ++ struct switch_dev *swdev; ++ const char *port4 = NULL; ++ ++ np = of_find_matching_node(NULL, gsw_match); ++ if (!np) { ++ dev_err(priv->device, "no gsw node found\n"); ++ return -EINVAL; ++ } ++ np = of_node_get(np); ++ ++ gsw = devm_kzalloc(priv->device, sizeof(struct mt7620_gsw), GFP_KERNEL); ++ if (!gsw) { ++ dev_err(priv->device, "no gsw memory for private data\n"); ++ return -ENOMEM; ++ } ++ ++ gsw->irq = irq_of_parse_and_map(np, 0); ++ if (!gsw->irq) { ++ dev_err(priv->device, "no gsw irq resource found\n"); ++ return -ENOMEM; ++ } ++ ++ gsw->base = of_iomap(np, 0); ++ if (!gsw->base) { ++ dev_err(priv->device, "gsw ioremap failed\n"); ++ } ++ ++ gsw->dev = priv->device; ++ priv->soc->swpriv = gsw; ++ ++ swdev = &gsw->swdev; ++ swdev->of_node = np; ++ swdev->name = "mt7620a-gsw"; ++ swdev->alias = "mt7620x"; ++ swdev->cpu_port = GSW_PORT6; ++ swdev->ports = GSW_NUM_PORTS; ++ swdev->vlans = GSW_NUM_VLANS; ++ swdev->ops = &gsw_ops; ++ ++ if (register_switch(swdev, NULL)) ++ dev_err(priv->device, "register_switch failed\n"); ++ ++ of_property_read_string(np, "ralink,port4", &port4); ++ if (port4 && !strcmp(port4, "ephy")) ++ gsw->port4 = PORT4_EPHY; ++ else if (port4 && !strcmp(port4, "gmac")) ++ gsw->port4 = PORT4_EXT; ++ else ++ WARN_ON(port4); ++ ++ gsw_hw_init(gsw); ++ ++ gsw_w32(gsw, ~PORT_IRQ_ST_CHG, GSW_REG_IMR); ++ request_irq(gsw->irq, gsw_interrupt, 0, "gsw", priv); ++ ++ return 0; ++} +diff --git a/drivers/net/ethernet/ralink/gsw_mt7620a.h b/drivers/net/ethernet/ralink/gsw_mt7620a.h +new file mode 100644 +index 0000000..fd4add5 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/gsw_mt7620a.h +@@ -0,0 +1,29 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#ifndef _RALINK_GSW_MT7620_H__ ++#define _RALINK_GSW_MT7620_H__ ++ ++extern int mt7620_gsw_probe(struct fe_priv *priv); ++extern void mt7620_set_mac(struct fe_priv *priv, unsigned char *mac); ++extern int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val); ++extern int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg); ++extern void mt7620_mdio_link_adjust(struct fe_priv *priv, int port); ++extern void mt7620_port_init(struct fe_priv *priv, struct device_node *np); ++extern int mt7620a_has_carrier(struct fe_priv *priv); ++ ++#endif +diff --git a/drivers/net/ethernet/ralink/mdio.c b/drivers/net/ethernet/ralink/mdio.c +new file mode 100644 +index 0000000..b265c75 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/mdio.c +@@ -0,0 +1,245 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ralink_soc_eth.h" ++#include "mdio.h" ++ ++static int fe_mdio_reset(struct mii_bus *bus) ++{ ++ /* TODO */ ++ return 0; ++} ++ ++static void fe_phy_link_adjust(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&priv->phy->lock, flags); ++ for (i = 0; i < 8; i++) { ++ if (priv->phy->phy_node[i]) { ++ struct phy_device *phydev = priv->phy->phy[i]; ++ int status_change = 0; ++ ++ if (phydev->link) ++ if (priv->phy->duplex[i] != phydev->duplex || ++ priv->phy->speed[i] != phydev->speed) ++ status_change = 1; ++ ++ if (phydev->link != priv->link[i]) ++ status_change = 1; ++ ++ switch (phydev->speed) { ++ case SPEED_1000: ++ case SPEED_100: ++ case SPEED_10: ++ priv->link[i] = phydev->link; ++ priv->phy->duplex[i] = phydev->duplex; ++ priv->phy->speed[i] = phydev->speed; ++ ++ if (status_change && priv->soc->mdio_adjust_link) ++ priv->soc->mdio_adjust_link(priv, i); ++ break; ++ } ++ } ++ } ++ spin_unlock_irqrestore(&priv->phy->lock, flags); ++} ++ ++int fe_connect_phy_node(struct fe_priv *priv, struct device_node *phy_node) ++{ ++ const __be32 *_port = NULL; ++ struct phy_device *phydev; ++ int phy_mode, port; ++ ++ _port = of_get_property(phy_node, "reg", NULL); ++ ++ if (!_port || (be32_to_cpu(*_port) >= 8)) { ++ pr_err("%s: invalid port id\n", phy_node->name); ++ return -EINVAL; ++ } ++ port = be32_to_cpu(*_port); ++ phy_mode = of_get_phy_mode(phy_node); ++ if (phy_mode < 0) { ++ dev_err(priv->device, "incorrect phy-mode %d\n", phy_mode); ++ priv->phy->phy_node[port] = NULL; ++ return -EINVAL; ++ } ++ ++ phydev = of_phy_connect(priv->netdev, phy_node, fe_phy_link_adjust, ++ 0, phy_mode); ++ if (IS_ERR(phydev)) { ++ dev_err(priv->device, "could not connect to PHY\n"); ++ priv->phy->phy_node[port] = NULL; ++ return PTR_ERR(phydev); ++ } ++ ++ phydev->supported &= PHY_GBIT_FEATURES; ++ phydev->advertising = phydev->supported; ++ phydev->no_auto_carrier_off = 1; ++ ++ dev_info(priv->device, ++ "connected port %d to PHY at %s [uid=%08x, driver=%s]\n", ++ port, dev_name(&phydev->dev), phydev->phy_id, ++ phydev->drv->name); ++ ++ priv->phy->phy[port] = phydev; ++ priv->link[port] = 0; ++ ++ return 0; ++} ++ ++static int fe_phy_connect(struct fe_priv *priv) ++{ ++ return 0; ++} ++ ++static void fe_phy_disconnect(struct fe_priv *priv) ++{ ++ unsigned long flags; ++ int i; ++ ++ for (i = 0; i < 8; i++) ++ if (priv->phy->phy_fixed[i]) { ++ spin_lock_irqsave(&priv->phy->lock, flags); ++ priv->link[i] = 0; ++ if (priv->soc->mdio_adjust_link) ++ priv->soc->mdio_adjust_link(priv, i); ++ spin_unlock_irqrestore(&priv->phy->lock, flags); ++ } else if (priv->phy->phy[i]) { ++ phy_disconnect(priv->phy->phy[i]); ++ } ++} ++ ++static void fe_phy_start(struct fe_priv *priv) ++{ ++ unsigned long flags; ++ int i; ++ ++ for (i = 0; i < 8; i++) { ++ if (priv->phy->phy_fixed[i]) { ++ spin_lock_irqsave(&priv->phy->lock, flags); ++ priv->link[i] = 1; ++ if (priv->soc->mdio_adjust_link) ++ priv->soc->mdio_adjust_link(priv, i); ++ spin_unlock_irqrestore(&priv->phy->lock, flags); ++ } else if (priv->phy->phy[i]) { ++ phy_start(priv->phy->phy[i]); ++ } ++ } ++} ++ ++static void fe_phy_stop(struct fe_priv *priv) ++{ ++ unsigned long flags; ++ int i; ++ ++ for (i = 0; i < 8; i++) ++ if (priv->phy->phy_fixed[i]) { ++ spin_lock_irqsave(&priv->phy->lock, flags); ++ priv->link[i] = 0; ++ if (priv->soc->mdio_adjust_link) ++ priv->soc->mdio_adjust_link(priv, i); ++ spin_unlock_irqrestore(&priv->phy->lock, flags); ++ } else if (priv->phy->phy[i]) { ++ phy_stop(priv->phy->phy[i]); ++ } ++} ++ ++static struct fe_phy phy_ralink = { ++ .connect = fe_phy_connect, ++ .disconnect = fe_phy_disconnect, ++ .start = fe_phy_start, ++ .stop = fe_phy_stop, ++}; ++ ++int fe_mdio_init(struct fe_priv *priv) ++{ ++ struct device_node *mii_np; ++ int err; ++ ++ if (!priv->soc->mdio_read || !priv->soc->mdio_write) ++ return 0; ++ ++ spin_lock_init(&phy_ralink.lock); ++ priv->phy = &phy_ralink; ++ ++ mii_np = of_get_child_by_name(priv->device->of_node, "mdio-bus"); ++ if (!mii_np) { ++ dev_err(priv->device, "no %s child node found", "mdio-bus"); ++ return -ENODEV; ++ } ++ ++ if (!of_device_is_available(mii_np)) { ++ err = 0; ++ goto err_put_node; ++ } ++ ++ priv->mii_bus = mdiobus_alloc(); ++ if (priv->mii_bus == NULL) { ++ err = -ENOMEM; ++ goto err_put_node; ++ } ++ ++ priv->mii_bus->name = "mdio"; ++ priv->mii_bus->read = priv->soc->mdio_read; ++ priv->mii_bus->write = priv->soc->mdio_write; ++ priv->mii_bus->reset = fe_mdio_reset; ++ priv->mii_bus->irq = priv->mii_irq; ++ priv->mii_bus->priv = priv; ++ priv->mii_bus->parent = priv->device; ++ ++ snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s", mii_np->name); ++ err = of_mdiobus_register(priv->mii_bus, mii_np); ++ if (err) ++ goto err_free_bus; ++ ++ return 0; ++ ++err_free_bus: ++ kfree(priv->mii_bus); ++err_put_node: ++ of_node_put(mii_np); ++ priv->mii_bus = NULL; ++ return err; ++} ++ ++void fe_mdio_cleanup(struct fe_priv *priv) ++{ ++ if (!priv->mii_bus) ++ return; ++ ++ mdiobus_unregister(priv->mii_bus); ++ of_node_put(priv->mii_bus->dev.of_node); ++ kfree(priv->mii_bus); ++} +diff --git a/drivers/net/ethernet/ralink/mdio.h b/drivers/net/ethernet/ralink/mdio.h +new file mode 100644 +index 0000000..c3910a0 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/mdio.h +@@ -0,0 +1,29 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#ifndef _RALINK_MDIO_H__ ++#define _RALINK_MDIO_H__ ++ ++#ifdef CONFIG_NET_RALINK_MDIO ++extern int fe_mdio_init(struct fe_priv *priv); ++extern void fe_mdio_cleanup(struct fe_priv *priv); ++extern int fe_connect_phy_node(struct fe_priv *priv, struct device_node *phy_node); ++#else ++static inline int fe_mdio_init(struct fe_priv *priv) { return 0; } ++static inline void fe_mdio_cleanup(struct fe_priv *priv) {} ++#endif ++#endif +diff --git a/drivers/net/ethernet/ralink/mdio_rt2880.c b/drivers/net/ethernet/ralink/mdio_rt2880.c +new file mode 100644 +index 0000000..54dbc53 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/mdio_rt2880.c +@@ -0,0 +1,163 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ralink_soc_eth.h" ++#include "mdio_rt2880.h" ++ ++#define FE_MDIO_RETRY 1000 ++ ++static unsigned char *rt2880_speed_str(struct fe_priv *priv) ++{ ++ switch (priv->phy->speed[0]) { ++ case SPEED_1000: ++ return "1000"; ++ case SPEED_100: ++ return "100"; ++ case SPEED_10: ++ return "10"; ++ } ++ ++ return "?"; ++} ++ ++void rt2880_mdio_link_adjust(struct fe_priv *priv) ++{ ++ u32 mdio_cfg; ++ ++ if (!priv->link[0]) { ++ netif_carrier_off(priv->netdev); ++ netdev_info(priv->netdev, "link down\n"); ++ return; ++ } ++ ++ mdio_cfg = FE_MDIO_CFG_TX_CLK_SKEW_200 | ++ FE_MDIO_CFG_RX_CLK_SKEW_200 | ++ FE_MDIO_CFG_GP1_FRC_EN; ++ ++ if (priv->phy->duplex[0] == DUPLEX_FULL) ++ mdio_cfg |= FE_MDIO_CFG_GP1_DUPLEX; ++ ++ if (priv->phy->tx_fc) ++ mdio_cfg |= FE_MDIO_CFG_GP1_FC_TX; ++ ++ if (priv->phy->rx_fc) ++ mdio_cfg |= FE_MDIO_CFG_GP1_FC_RX; ++ ++ switch (priv->phy->speed[0]) { ++ case SPEED_10: ++ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_10; ++ break; ++ case SPEED_100: ++ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_100; ++ break; ++ case SPEED_1000: ++ mdio_cfg |= FE_MDIO_CFG_GP1_SPEED_1000; ++ break; ++ default: ++ BUG(); ++ } ++ ++ fe_w32(mdio_cfg, FE_MDIO_CFG); ++ ++ netif_carrier_on(priv->netdev); ++ netdev_info(priv->netdev, "link up (%sMbps/%s duplex)\n", ++ rt2880_speed_str(priv), ++ (DUPLEX_FULL == priv->phy->duplex[0]) ? "Full" : "Half"); ++} ++ ++static int rt2880_mdio_wait_ready(struct fe_priv *priv) ++{ ++ int retries; ++ ++ retries = FE_MDIO_RETRY; ++ while (1) { ++ u32 t; ++ ++ t = fe_r32(FE_MDIO_ACCESS); ++ if ((t & (0x1 << 31)) == 0) ++ return 0; ++ ++ if (retries-- == 0) ++ break; ++ ++ udelay(1); ++ } ++ ++ dev_err(priv->device, "MDIO operation timed out\n"); ++ return -ETIMEDOUT; ++} ++ ++int rt2880_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) ++{ ++ struct fe_priv *priv = bus->priv; ++ int err; ++ u32 t; ++ ++ err = rt2880_mdio_wait_ready(priv); ++ if (err) ++ return 0xffff; ++ ++ t = (phy_addr << 24) | (phy_reg << 16); ++ fe_w32(t, FE_MDIO_ACCESS); ++ t |= (1 << 31); ++ fe_w32(t, FE_MDIO_ACCESS); ++ ++ err = rt2880_mdio_wait_ready(priv); ++ if (err) ++ return 0xffff; ++ ++ pr_info("%s: addr=%04x, reg=%04x, value=%04x\n", __func__, ++ phy_addr, phy_reg, fe_r32(FE_MDIO_ACCESS) & 0xffff); ++ ++ return fe_r32(FE_MDIO_ACCESS) & 0xffff; ++} ++ ++int rt2880_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val) ++{ ++ struct fe_priv *priv = bus->priv; ++ int err; ++ u32 t; ++ ++ pr_info("%s: addr=%04x, reg=%04x, value=%04x\n", __func__, ++ phy_addr, phy_reg, fe_r32(FE_MDIO_ACCESS) & 0xffff); ++ ++ err = rt2880_mdio_wait_ready(priv); ++ if (err) ++ return err; ++ ++ t = (1 << 30) | (phy_addr << 24) | (phy_reg << 16) | val; ++ fe_w32(t, FE_MDIO_ACCESS); ++ t |= (1 << 31); ++ fe_w32(t, FE_MDIO_ACCESS); ++ ++ return rt2880_mdio_wait_ready(priv); ++} +diff --git a/drivers/net/ethernet/ralink/mdio_rt2880.h b/drivers/net/ethernet/ralink/mdio_rt2880.h +new file mode 100644 +index 0000000..c9ac0fe +--- /dev/null ++++ b/drivers/net/ethernet/ralink/mdio_rt2880.h +@@ -0,0 +1,25 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#ifndef _RALINK_MDIO_RT2880_H__ ++#define _RALINK_MDIO_RT2880_H__ ++ ++void rt2880_mdio_link_adjust(struct fe_priv *priv); ++int rt2880_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg); ++int rt2880_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val); ++ ++#endif +diff --git a/drivers/net/ethernet/ralink/ralink_soc_eth.c b/drivers/net/ethernet/ralink/ralink_soc_eth.c +new file mode 100644 +index 0000000..04e82eb +--- /dev/null ++++ b/drivers/net/ethernet/ralink/ralink_soc_eth.c +@@ -0,0 +1,759 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++#include "esw_rt3052.h" ++#include "mdio.h" ++ ++#define TX_TIMEOUT (20 * HZ / 100) ++#define MAX_RX_LENGTH 1536 ++ ++static const u32 fe_reg_table_default[FE_REG_COUNT] = { ++ [FE_REG_PDMA_GLO_CFG] = FE_PDMA_GLO_CFG, ++ [FE_REG_PDMA_RST_CFG] = FE_PDMA_RST_CFG, ++ [FE_REG_DLY_INT_CFG] = FE_DLY_INT_CFG, ++ [FE_REG_TX_BASE_PTR0] = FE_TX_BASE_PTR0, ++ [FE_REG_TX_MAX_CNT0] = FE_TX_MAX_CNT0, ++ [FE_REG_TX_CTX_IDX0] = FE_TX_CTX_IDX0, ++ [FE_REG_RX_BASE_PTR0] = FE_RX_BASE_PTR0, ++ [FE_REG_RX_MAX_CNT0] = FE_RX_MAX_CNT0, ++ [FE_REG_RX_CALC_IDX0] = FE_RX_CALC_IDX0, ++ [FE_REG_FE_INT_ENABLE] = FE_FE_INT_ENABLE, ++ [FE_REG_FE_INT_STATUS] = FE_FE_INT_STATUS, ++ [FE_REG_FE_DMA_VID_BASE] = FE_DMA_VID0, ++}; ++ ++static const u32 *fe_reg_table = fe_reg_table_default; ++ ++static void __iomem *fe_base = 0; ++ ++void fe_w32(u32 val, unsigned reg) ++{ ++ __raw_writel(val, fe_base + reg); ++} ++ ++u32 fe_r32(unsigned reg) ++{ ++ return __raw_readl(fe_base + reg); ++} ++ ++static inline void fe_reg_w32(u32 val, enum fe_reg reg) ++{ ++ fe_w32(val, fe_reg_table[reg]); ++} ++ ++static inline u32 fe_reg_r32(enum fe_reg reg) ++{ ++ return fe_r32(fe_reg_table[reg]); ++} ++ ++static inline void fe_int_disable(u32 mask) ++{ ++ fe_reg_w32(fe_reg_r32(FE_REG_FE_INT_ENABLE) & ~mask, ++ FE_REG_FE_INT_ENABLE); ++ /* flush write */ ++ fe_reg_r32(FE_REG_FE_INT_ENABLE); ++} ++ ++static inline void fe_int_enable(u32 mask) ++{ ++ fe_reg_w32(fe_reg_r32(FE_REG_FE_INT_ENABLE) | mask, ++ FE_REG_FE_INT_ENABLE); ++ /* flush write */ ++ fe_reg_r32(FE_REG_FE_INT_ENABLE); ++} ++ ++static inline void fe_hw_set_macaddr(struct fe_priv *priv, unsigned char *mac) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ fe_w32((mac[0] << 8) | mac[1], FE_GDMA1_MAC_ADRH); ++ fe_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], ++ FE_GDMA1_MAC_ADRL); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++} ++ ++static int fe_set_mac_address(struct net_device *dev, void *p) ++{ ++ int ret = eth_mac_addr(dev, p); ++ ++ if (!ret) { ++ struct fe_priv *priv = netdev_priv(dev); ++ ++ if (priv->soc->set_mac) ++ priv->soc->set_mac(priv, dev->dev_addr); ++ else ++ fe_hw_set_macaddr(priv, p); ++ } ++ ++ return ret; ++} ++ ++static struct sk_buff* fe_alloc_skb(struct fe_priv *priv) ++{ ++ struct sk_buff *skb; ++ ++ skb = netdev_alloc_skb(priv->netdev, MAX_RX_LENGTH + NET_IP_ALIGN); ++ if (!skb) ++ return NULL; ++ ++ skb_reserve(skb, NET_IP_ALIGN); ++ ++ return skb; ++} ++ ++static int fe_alloc_rx(struct fe_priv *priv) ++{ ++ int size = NUM_DMA_DESC * sizeof(struct fe_rx_dma); ++ int i; ++ ++ priv->rx_dma = dma_alloc_coherent(&priv->netdev->dev, size, ++ &priv->rx_phys, GFP_ATOMIC); ++ if (!priv->rx_dma) ++ return -ENOMEM; ++ ++ memset(priv->rx_dma, 0, size); ++ ++ for (i = 0; i < NUM_DMA_DESC; i++) { ++ priv->rx_skb[i] = fe_alloc_skb(priv); ++ if (!priv->rx_skb[i]) ++ return -ENOMEM; ++ } ++ ++ for (i = 0; i < NUM_DMA_DESC; i++) { ++ dma_addr_t dma_addr = dma_map_single(&priv->netdev->dev, ++ priv->rx_skb[i]->data, ++ MAX_RX_LENGTH, ++ DMA_FROM_DEVICE); ++ priv->rx_dma[i].rxd1 = (unsigned int) dma_addr; ++ ++ if (priv->soc->rx_dma) ++ priv->soc->rx_dma(priv, i, MAX_RX_LENGTH); ++ else ++ priv->rx_dma[i].rxd2 = RX_DMA_LSO; ++ } ++ wmb(); ++ ++ fe_reg_w32(priv->rx_phys, FE_REG_RX_BASE_PTR0); ++ fe_reg_w32(NUM_DMA_DESC, FE_REG_RX_MAX_CNT0); ++ fe_reg_w32((NUM_DMA_DESC - 1), FE_REG_RX_CALC_IDX0); ++ fe_reg_w32(FE_PST_DRX_IDX0, FE_REG_PDMA_RST_CFG); ++ ++ return 0; ++} ++ ++static int fe_alloc_tx(struct fe_priv *priv) ++{ ++ int size = NUM_DMA_DESC * sizeof(struct fe_tx_dma); ++ int i; ++ ++ priv->tx_free_idx = 0; ++ ++ priv->tx_dma = dma_alloc_coherent(&priv->netdev->dev, size, ++ &priv->tx_phys, GFP_ATOMIC); ++ if (!priv->tx_dma) ++ return -ENOMEM; ++ ++ memset(priv->tx_dma, 0, size); ++ ++ for (i = 0; i < NUM_DMA_DESC; i++) { ++ if (priv->soc->tx_dma) { ++ priv->soc->tx_dma(priv, i, 0); ++ continue; ++ } ++ ++ priv->tx_dma[i].txd2 = TX_DMA_LSO | TX_DMA_DONE; ++ priv->tx_dma[i].txd4 = TX_DMA_QN(3) | TX_DMA_PN(1); ++ } ++ ++ fe_reg_w32(priv->tx_phys, FE_REG_TX_BASE_PTR0); ++ fe_reg_w32(NUM_DMA_DESC, FE_REG_TX_MAX_CNT0); ++ fe_reg_w32(0, FE_REG_TX_CTX_IDX0); ++ fe_reg_w32(FE_PST_DTX_IDX0, FE_REG_PDMA_RST_CFG); ++ ++ return 0; ++} ++ ++static void fe_free_dma(struct fe_priv *priv) ++{ ++ int i; ++ ++ for (i = 0; i < NUM_DMA_DESC; i++) { ++ if (priv->rx_skb[i]) { ++ dma_unmap_single(&priv->netdev->dev, priv->rx_dma[i].rxd1, ++ MAX_RX_LENGTH, DMA_FROM_DEVICE); ++ dev_kfree_skb_any(priv->rx_skb[i]); ++ priv->rx_skb[i] = NULL; ++ } ++ ++ if (priv->tx_skb[i]) { ++ dev_kfree_skb_any(priv->tx_skb[i]); ++ priv->tx_skb[i] = NULL; ++ } ++ } ++ ++ if (priv->rx_dma) { ++ int size = NUM_DMA_DESC * sizeof(struct fe_rx_dma); ++ dma_free_coherent(&priv->netdev->dev, size, priv->rx_dma, ++ priv->rx_phys); ++ } ++ ++ if (priv->tx_dma) { ++ int size = NUM_DMA_DESC * sizeof(struct fe_tx_dma); ++ dma_free_coherent(&priv->netdev->dev, size, priv->tx_dma, ++ priv->tx_phys); ++ } ++ ++ netdev_reset_queue(priv->netdev); ++} ++ ++static int fe_start_xmit(struct sk_buff *skb, struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ dma_addr_t mapped_addr; ++ u32 tx_next; ++ u32 tx; ++ ++ if (priv->soc->min_pkt_len) { ++ if (skb->len < priv->soc->min_pkt_len) { ++ if (skb_padto(skb, priv->soc->min_pkt_len)) { ++ printk(KERN_ERR ++ "fe_eth: skb_padto failed\n"); ++ kfree_skb(skb); ++ return 0; ++ } ++ skb_put(skb, priv->soc->min_pkt_len - skb->len); ++ } ++ } ++ ++ dev->trans_start = jiffies; ++ mapped_addr = dma_map_single(&priv->netdev->dev, skb->data, ++ skb->len, DMA_TO_DEVICE); ++ ++ spin_lock(&priv->page_lock); ++ ++ tx = fe_reg_r32(FE_REG_TX_CTX_IDX0); ++ tx_next = (tx + 1) % NUM_DMA_DESC; ++ ++ if ((priv->tx_skb[tx]) || (priv->tx_skb[tx_next]) || ++ !(priv->tx_dma[tx].txd2 & TX_DMA_DONE) || ++ !(priv->tx_dma[tx_next].txd2 & TX_DMA_DONE)) ++ { ++ spin_unlock(&priv->page_lock); ++ dev->stats.tx_dropped++; ++ kfree_skb(skb); ++ ++ return NETDEV_TX_OK; ++ } ++ ++ priv->tx_skb[tx] = skb; ++ priv->tx_dma[tx].txd1 = (unsigned int) mapped_addr; ++ wmb(); ++ if (priv->soc->tx_dma) ++ priv->soc->tx_dma(priv, tx, skb->len); ++ else ++ priv->tx_dma[tx].txd2 = TX_DMA_LSO | TX_DMA_PLEN0(skb->len); ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) ++ priv->tx_dma[tx].txd4 |= TX_DMA_CHKSUM; ++ else ++ priv->tx_dma[tx].txd4 &= ~TX_DMA_CHKSUM; ++ ++ if (fe_reg_table[FE_REG_FE_DMA_VID_BASE] && vlan_tx_tag_present(skb)) ++ priv->tx_dma[tx].txd4 |= 0x80 | (vlan_tx_tag_get(skb) >> 13) << 4 | (vlan_tx_tag_get(skb) & 0xF); ++ else ++ priv->tx_dma[tx].txd4 &= ~0x80; ++ ++ dev->stats.tx_packets++; ++ dev->stats.tx_bytes += skb->len; ++ ++ fe_reg_w32(tx_next, FE_REG_TX_CTX_IDX0); ++ netdev_sent_queue(dev, skb->len); ++ ++ spin_unlock(&priv->page_lock); ++ ++ return NETDEV_TX_OK; ++} ++ ++static int fe_poll_rx(struct napi_struct *napi, int budget) ++{ ++ struct fe_priv *priv = container_of(napi, struct fe_priv, rx_napi); ++ int idx = fe_reg_r32(FE_REG_RX_CALC_IDX0); ++ unsigned long flags; ++ int complete = 0; ++ int rx = 0; ++ ++ while ((rx < budget) && !complete) { ++ ++ idx = (idx + 1) % NUM_DMA_DESC; ++ ++ if (priv->rx_dma[idx].rxd2 & RX_DMA_DONE) { ++ struct sk_buff *new_skb = fe_alloc_skb(priv); ++ ++ if (new_skb) { ++ int pktlen = RX_DMA_PLEN0(priv->rx_dma[idx].rxd2); ++ dma_addr_t dma_addr; ++ ++ dma_unmap_single(&priv->netdev->dev, priv->rx_dma[idx].rxd1, ++ MAX_RX_LENGTH, DMA_FROM_DEVICE); ++ ++ skb_put(priv->rx_skb[idx], pktlen); ++ priv->rx_skb[idx]->dev = priv->netdev; ++ priv->rx_skb[idx]->protocol = eth_type_trans(priv->rx_skb[idx], priv->netdev); ++ if (priv->rx_dma[idx].rxd4 & priv->soc->checksum_bit) ++ priv->rx_skb[idx]->ip_summed = CHECKSUM_UNNECESSARY; ++ else ++ priv->rx_skb[idx]->ip_summed = CHECKSUM_NONE; ++ priv->netdev->stats.rx_packets++; ++ priv->netdev->stats.rx_bytes += pktlen; ++ netif_rx(priv->rx_skb[idx]); ++ ++ priv->rx_skb[idx] = new_skb; ++ ++ dma_addr = dma_map_single(&priv->netdev->dev, ++ new_skb->data, ++ MAX_RX_LENGTH, ++ DMA_FROM_DEVICE); ++ priv->rx_dma[idx].rxd1 = (unsigned int) dma_addr; ++ wmb(); ++ } else { ++ priv->netdev->stats.rx_dropped++; ++ } ++ ++ if (priv->soc->rx_dma) ++ priv->soc->rx_dma(priv, idx, MAX_RX_LENGTH); ++ else ++ priv->rx_dma[idx].rxd2 = RX_DMA_LSO; ++ fe_reg_w32(idx, FE_REG_RX_CALC_IDX0); ++ rx++; ++ } else { ++ complete = 1; ++ } ++ } ++ ++ if (complete || !rx) { ++ napi_complete(&priv->rx_napi); ++ spin_lock_irqsave(&priv->page_lock, flags); ++ fe_int_enable(priv->soc->rx_dly_int); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++ } ++ ++ return rx; ++} ++ ++static int fe_poll_tx(struct napi_struct *napi, int budget) ++{ ++ struct fe_priv *priv = container_of(napi, struct fe_priv, tx_napi); ++ unsigned int bytes_compl = 0; ++ unsigned int pkts_compl = 0; ++ struct netdev_queue *txq; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ while (pkts_compl < budget) { ++ struct fe_tx_dma *txd; ++ ++ txd = &priv->tx_dma[priv->tx_free_idx]; ++ ++ if (!(txd->txd2 & TX_DMA_DONE) || !(priv->tx_skb[priv->tx_free_idx])) ++ break; ++ ++ bytes_compl += priv->tx_skb[priv->tx_free_idx]->len; ++ pkts_compl++; ++ ++ dev_kfree_skb_irq(priv->tx_skb[priv->tx_free_idx]); ++ priv->tx_skb[priv->tx_free_idx] = NULL; ++ priv->tx_free_idx++; ++ if (priv->tx_free_idx >= NUM_DMA_DESC) ++ priv->tx_free_idx = 0; ++ } ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++ ++ txq = netdev_get_tx_queue(priv->netdev, 0); ++ if (netif_tx_queue_stopped(txq)) ++ netif_tx_start_queue(txq); ++ ++ napi_complete(napi); ++ netdev_completed_queue(priv->netdev, pkts_compl, bytes_compl); ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ fe_int_enable(priv->soc->tx_dly_int); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++ ++ return pkts_compl; ++} ++ ++static void fe_tx_timeout(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ ++ priv->netdev->stats.tx_errors++; ++ netdev_err(dev, "transmit timed out, waking up the queue\n"); ++ netif_wake_queue(dev); ++} ++ ++static irqreturn_t fe_handle_irq(int irq, void *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ unsigned int status; ++ unsigned int mask; ++ ++ status = fe_reg_r32(FE_REG_FE_INT_STATUS); ++ mask = fe_reg_r32(FE_REG_FE_INT_ENABLE); ++ ++ if (!(status & mask)) ++ return IRQ_NONE; ++ ++ if (status & priv->soc->rx_dly_int) { ++ fe_int_disable(priv->soc->rx_dly_int); ++ napi_schedule(&priv->rx_napi); ++ } ++ ++ if (status & priv->soc->tx_dly_int) { ++ fe_int_disable(priv->soc->tx_dly_int); ++ napi_schedule(&priv->tx_napi); ++ } ++ ++ fe_reg_w32(status, FE_REG_FE_INT_STATUS); ++ ++ return IRQ_HANDLED; ++} ++ ++static int fe_hw_init(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ int err, i; ++ ++ err = devm_request_irq(priv->device, dev->irq, fe_handle_irq, 0, ++ dev_name(priv->device), dev); ++ if (err) ++ return err; ++ ++ err = fe_alloc_rx(priv); ++ if (!err) ++ err = fe_alloc_tx(priv); ++ if (err) ++ return err; ++ ++ if (priv->soc->set_mac) ++ priv->soc->set_mac(priv, dev->dev_addr); ++ else ++ fe_hw_set_macaddr(priv, dev->dev_addr); ++ ++ fe_reg_w32(FE_DELAY_INIT, FE_REG_DLY_INT_CFG); ++ ++ fe_int_disable(priv->soc->tx_dly_int | priv->soc->rx_dly_int); ++ ++ ++ if (fe_reg_table[FE_REG_FE_DMA_VID_BASE]) ++ for (i = 0; i < 16; i += 2) ++ fe_w32((i + 1) << 16 | i, fe_reg_table[FE_REG_FE_DMA_VID_BASE] + (i * 4)); ++ ++ if (priv->soc->fwd_config) { ++ priv->soc->fwd_config(priv); ++ } else { ++ unsigned long sysclk = priv->sysclk; ++ ++ if (!sysclk) { ++ netdev_err(dev, "unable to get clock\n"); ++ return -EINVAL; ++ } ++ ++ sysclk /= FE_US_CYC_CNT_DIVISOR; ++ sysclk <<= FE_US_CYC_CNT_SHIFT; ++ ++ fe_w32((fe_r32(FE_FE_GLO_CFG) & ++ ~(FE_US_CYC_CNT_MASK << FE_US_CYC_CNT_SHIFT)) | priv->sysclk, ++ FE_FE_GLO_CFG); ++ ++ fe_w32(fe_r32(FE_GDMA1_FWD_CFG) & ~0xffff, FE_GDMA1_FWD_CFG); ++ fe_w32(fe_r32(FE_GDMA1_FWD_CFG) | (FE_GDM1_ICS_EN | FE_GDM1_TCS_EN | FE_GDM1_UCS_EN), ++ FE_GDMA1_FWD_CFG); ++ fe_w32(fe_r32(FE_CDMA_CSG_CFG) | (FE_ICS_GEN_EN | FE_TCS_GEN_EN | FE_UCS_GEN_EN), ++ FE_CDMA_CSG_CFG); ++ fe_w32(FE_PSE_FQFC_CFG_INIT, FE_PSE_FQ_CFG); ++ } ++ ++ fe_w32(1, FE_FE_RST_GL); ++ fe_w32(0, FE_FE_RST_GL); ++ ++ return 0; ++} ++ ++static int fe_open(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ unsigned long flags; ++ u32 val; ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ napi_enable(&priv->rx_napi); ++ napi_enable(&priv->tx_napi); ++ ++ val = FE_TX_WB_DDONE | FE_RX_DMA_EN | FE_TX_DMA_EN; ++ val |= priv->soc->pdma_glo_cfg; ++ fe_reg_w32(val, FE_REG_PDMA_GLO_CFG); ++ ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++ ++ if (priv->phy) ++ priv->phy->start(priv); ++ ++ if (priv->soc->has_carrier && priv->soc->has_carrier(priv)) ++ netif_carrier_on(dev); ++ ++ netif_start_queue(dev); ++ fe_int_enable(priv->soc->tx_dly_int | priv->soc->rx_dly_int); ++ ++ return 0; ++} ++ ++static int fe_stop(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ unsigned long flags; ++ ++ fe_int_disable(priv->soc->tx_dly_int | priv->soc->rx_dly_int); ++ ++ netif_stop_queue(dev); ++ ++ if (priv->phy) ++ priv->phy->stop(priv); ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ napi_disable(&priv->rx_napi); ++ napi_disable(&priv->tx_napi); ++ ++ fe_reg_w32(fe_reg_r32(FE_REG_PDMA_GLO_CFG) & ++ ~(FE_TX_WB_DDONE | FE_RX_DMA_EN | FE_TX_DMA_EN), ++ FE_REG_PDMA_GLO_CFG); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++ ++ return 0; ++} ++ ++static int __init fe_init(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ struct device_node *port; ++ int err; ++ ++ BUG_ON(!priv->soc->reset_fe); ++ priv->soc->reset_fe(); ++ ++ if (priv->soc->switch_init) ++ priv->soc->switch_init(priv); ++ ++ net_srandom(jiffies); ++ memcpy(dev->dev_addr, priv->soc->mac, ETH_ALEN); ++ of_get_mac_address_mtd(priv->device->of_node, dev->dev_addr); ++ ++ err = fe_mdio_init(priv); ++ if (err) ++ return err; ++ ++ if (priv->phy) { ++ err = priv->phy->connect(priv); ++ if (err) ++ goto err_mdio_cleanup; ++ } ++ ++ if (priv->soc->port_init) ++ for_each_child_of_node(priv->device->of_node, port) ++ if (of_device_is_compatible(port, "ralink,eth-port")) ++ priv->soc->port_init(priv, port); ++ ++ err = fe_hw_init(dev); ++ if (err) ++ goto err_phy_disconnect; ++ ++ return 0; ++ ++err_phy_disconnect: ++ if (priv->phy) ++ priv->phy->disconnect(priv); ++err_mdio_cleanup: ++ fe_mdio_cleanup(priv); ++ ++ return err; ++} ++ ++static void fe_uninit(struct net_device *dev) ++{ ++ struct fe_priv *priv = netdev_priv(dev); ++ ++ if (priv->phy) ++ priv->phy->disconnect(priv); ++ fe_mdio_cleanup(priv); ++ ++ fe_reg_w32(0, FE_REG_FE_INT_ENABLE); ++ free_irq(dev->irq, dev); ++ ++ fe_free_dma(priv); ++} ++ ++static const struct net_device_ops fe_netdev_ops = { ++ .ndo_init = fe_init, ++ .ndo_uninit = fe_uninit, ++ .ndo_open = fe_open, ++ .ndo_stop = fe_stop, ++ .ndo_start_xmit = fe_start_xmit, ++ .ndo_tx_timeout = fe_tx_timeout, ++ .ndo_set_mac_address = fe_set_mac_address, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_validate_addr = eth_validate_addr, ++}; ++ ++static int fe_probe(struct platform_device *pdev) ++{ ++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ const struct of_device_id *match; ++ struct fe_soc_data *soc = NULL; ++ struct net_device *netdev; ++ struct fe_priv *priv; ++ struct clk *sysclk; ++ int err; ++ ++ match = of_match_device(of_fe_match, &pdev->dev); ++ soc = (struct fe_soc_data *) match->data; ++ if (soc->reg_table) ++ fe_reg_table = soc->reg_table; ++ ++ fe_base = devm_request_and_ioremap(&pdev->dev, res); ++ if (!fe_base) ++ return -ENOMEM; ++ ++ netdev = alloc_etherdev(sizeof(struct fe_priv)); ++ if (!netdev) { ++ dev_err(&pdev->dev, "alloc_etherdev failed\n"); ++ return -ENOMEM; ++ } ++ ++ strcpy(netdev->name, "eth%d"); ++ netdev->netdev_ops = &fe_netdev_ops; ++ netdev->base_addr = (unsigned long) fe_base; ++ netdev->watchdog_timeo = TX_TIMEOUT; ++ netdev->features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM; ++ if (fe_reg_table[FE_REG_FE_DMA_VID_BASE]) ++ netdev->features |= NETIF_F_HW_VLAN_TX; ++ ++ netdev->irq = platform_get_irq(pdev, 0); ++ if (netdev->irq < 0) { ++ dev_err(&pdev->dev, "no IRQ resource found\n"); ++ kfree(netdev); ++ return -ENXIO; ++ } ++ ++ priv = netdev_priv(netdev); ++ memset(priv, 0, sizeof(struct fe_priv)); ++ spin_lock_init(&priv->page_lock); ++ ++ sysclk = devm_clk_get(&pdev->dev, NULL); ++ if (!IS_ERR(sysclk)) ++ priv->sysclk = clk_get_rate(sysclk); ++ ++ priv->netdev = netdev; ++ priv->device = &pdev->dev; ++ priv->soc = soc; ++ ++ err = register_netdev(netdev); ++ if (err) { ++ dev_err(&pdev->dev, "error bringing up device\n"); ++ kfree(netdev); ++ return err; ++ } ++ ++ netif_napi_add(netdev, &priv->rx_napi, fe_poll_rx, 32); ++ netif_napi_add(netdev, &priv->tx_napi, fe_poll_tx, 8); ++ ++ platform_set_drvdata(pdev, netdev); ++ ++ netdev_info(netdev, "done loading\n"); ++ ++ return 0; ++} ++ ++static int fe_remove(struct platform_device *pdev) ++{ ++ struct net_device *dev = platform_get_drvdata(pdev); ++ struct fe_priv *priv = netdev_priv(dev); ++ ++ netif_stop_queue(dev); ++ netif_napi_del(&priv->rx_napi); ++ netif_napi_del(&priv->tx_napi); ++ ++ unregister_netdev(dev); ++ free_netdev(dev); ++ ++ return 0; ++} ++ ++static struct platform_driver fe_driver = { ++ .probe = fe_probe, ++ .remove = fe_remove, ++ .driver = { ++ .name = "ralink_soc_eth", ++ .owner = THIS_MODULE, ++ .of_match_table = of_fe_match, ++ }, ++}; ++ ++static int __init init_rtfe(void) ++{ ++ int ret; ++ ++ ret = rtesw_init(); ++ if (ret) ++ return ret; ++ ++ ret = platform_driver_register(&fe_driver); ++ if (ret) ++ rtesw_exit(); ++ ++ return ret; ++} ++ ++static void __exit exit_rtfe(void) ++{ ++ platform_driver_unregister(&fe_driver); ++ rtesw_exit(); ++} ++ ++module_init(init_rtfe); ++module_exit(exit_rtfe); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("John Crispin "); ++MODULE_DESCRIPTION("Ethernet driver for Ralink SoC"); +diff --git a/drivers/net/ethernet/ralink/ralink_soc_eth.h b/drivers/net/ethernet/ralink/ralink_soc_eth.h +new file mode 100644 +index 0000000..0c769ef +--- /dev/null ++++ b/drivers/net/ethernet/ralink/ralink_soc_eth.h +@@ -0,0 +1,372 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * based on Ralink SDK3.3 ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#ifndef FE_ETH_H ++#define FE_ETH_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++enum fe_reg { ++ FE_REG_PDMA_GLO_CFG = 0, ++ FE_REG_PDMA_RST_CFG, ++ FE_REG_DLY_INT_CFG, ++ FE_REG_TX_BASE_PTR0, ++ FE_REG_TX_MAX_CNT0, ++ FE_REG_TX_CTX_IDX0, ++ FE_REG_RX_BASE_PTR0, ++ FE_REG_RX_MAX_CNT0, ++ FE_REG_RX_CALC_IDX0, ++ FE_REG_FE_INT_ENABLE, ++ FE_REG_FE_INT_STATUS, ++ FE_REG_FE_DMA_VID_BASE, ++ FE_REG_COUNT ++}; ++ ++#define NUM_DMA_DESC 0x100 ++ ++#define FE_DELAY_EN_INT 0x80 ++#define FE_DELAY_MAX_INT 0x04 ++#define FE_DELAY_MAX_TOUT 0x04 ++#define FE_DELAY_CHAN (((FE_DELAY_EN_INT | FE_DELAY_MAX_INT) << 8) | FE_DELAY_MAX_TOUT) ++#define FE_DELAY_INIT ((FE_DELAY_CHAN << 16) | FE_DELAY_CHAN) ++#define FE_PSE_FQFC_CFG_INIT 0x80504000 ++ ++/* interrupt bits */ ++#define FE_CNT_PPE_AF BIT(31) ++#define FE_CNT_GDM_AF BIT(29) ++#define FE_PSE_P2_FC BIT(26) ++#define FE_PSE_BUF_DROP BIT(24) ++#define FE_GDM_OTHER_DROP BIT(23) ++#define FE_PSE_P1_FC BIT(22) ++#define FE_PSE_P0_FC BIT(21) ++#define FE_PSE_FQ_EMPTY BIT(20) ++#define FE_GE1_STA_CHG BIT(18) ++#define FE_TX_COHERENT BIT(17) ++#define FE_RX_COHERENT BIT(16) ++#define FE_TX_DONE_INT3 BIT(11) ++#define FE_TX_DONE_INT2 BIT(10) ++#define FE_TX_DONE_INT1 BIT(9) ++#define FE_TX_DONE_INT0 BIT(8) ++#define FE_RX_DONE_INT0 BIT(2) ++#define FE_TX_DLY_INT BIT(1) ++#define FE_RX_DLY_INT BIT(0) ++ ++#define RT5350_RX_DLY_INT BIT(30) ++#define RT5350_TX_DLY_INT BIT(28) ++ ++/* registers */ ++#define FE_FE_OFFSET 0x0000 ++#define FE_GDMA_OFFSET 0x0020 ++#define FE_PSE_OFFSET 0x0040 ++#define FE_GDMA2_OFFSET 0x0060 ++#define FE_CDMA_OFFSET 0x0080 ++#define FE_DMA_VID0 0x00a8 ++#define FE_PDMA_OFFSET 0x0100 ++#define FE_PPE_OFFSET 0x0200 ++#define FE_CMTABLE_OFFSET 0x0400 ++#define FE_POLICYTABLE_OFFSET 0x1000 ++ ++#define RT5350_PDMA_OFFSET 0x0800 ++#define RT5350_SDM_OFFSET 0x0c00 ++ ++#define FE_MDIO_ACCESS (FE_FE_OFFSET + 0x00) ++#define FE_MDIO_CFG (FE_FE_OFFSET + 0x04) ++#define FE_FE_GLO_CFG (FE_FE_OFFSET + 0x08) ++#define FE_FE_RST_GL (FE_FE_OFFSET + 0x0C) ++#define FE_FE_INT_STATUS (FE_FE_OFFSET + 0x10) ++#define FE_FE_INT_ENABLE (FE_FE_OFFSET + 0x14) ++#define FE_MDIO_CFG2 (FE_FE_OFFSET + 0x18) ++#define FE_FOC_TS_T (FE_FE_OFFSET + 0x1C) ++ ++#define FE_GDMA1_FWD_CFG (FE_GDMA_OFFSET + 0x00) ++#define FE_GDMA1_SCH_CFG (FE_GDMA_OFFSET + 0x04) ++#define FE_GDMA1_SHPR_CFG (FE_GDMA_OFFSET + 0x08) ++#define FE_GDMA1_MAC_ADRL (FE_GDMA_OFFSET + 0x0C) ++#define FE_GDMA1_MAC_ADRH (FE_GDMA_OFFSET + 0x10) ++ ++#define FE_GDMA2_FWD_CFG (FE_GDMA2_OFFSET + 0x00) ++#define FE_GDMA2_SCH_CFG (FE_GDMA2_OFFSET + 0x04) ++#define FE_GDMA2_SHPR_CFG (FE_GDMA2_OFFSET + 0x08) ++#define FE_GDMA2_MAC_ADRL (FE_GDMA2_OFFSET + 0x0C) ++#define FE_GDMA2_MAC_ADRH (FE_GDMA2_OFFSET + 0x10) ++ ++#define FE_PSE_FQ_CFG (FE_PSE_OFFSET + 0x00) ++#define FE_CDMA_FC_CFG (FE_PSE_OFFSET + 0x04) ++#define FE_GDMA1_FC_CFG (FE_PSE_OFFSET + 0x08) ++#define FE_GDMA2_FC_CFG (FE_PSE_OFFSET + 0x0C) ++ ++#define FE_CDMA_CSG_CFG (FE_CDMA_OFFSET + 0x00) ++#define FE_CDMA_SCH_CFG (FE_CDMA_OFFSET + 0x04) ++ ++#define MT7620A_GDMA_OFFSET 0x0600 ++#define MT7620A_GDMA1_FWD_CFG (MT7620A_GDMA_OFFSET + 0x00) ++#define MT7620A_FE_GDMA1_SCH_CFG (MT7620A_GDMA_OFFSET + 0x04) ++#define MT7620A_FE_GDMA1_SHPR_CFG (MT7620A_GDMA_OFFSET + 0x08) ++#define MT7620A_FE_GDMA1_MAC_ADRL (MT7620A_GDMA_OFFSET + 0x0C) ++#define MT7620A_FE_GDMA1_MAC_ADRH (MT7620A_GDMA_OFFSET + 0x10) ++ ++#define RT5350_TX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x00) ++#define RT5350_TX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x04) ++#define RT5350_TX_CTX_IDX0 (RT5350_PDMA_OFFSET + 0x08) ++#define RT5350_TX_DTX_IDX0 (RT5350_PDMA_OFFSET + 0x0C) ++#define RT5350_TX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x10) ++#define RT5350_TX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x14) ++#define RT5350_TX_CTX_IDX1 (RT5350_PDMA_OFFSET + 0x18) ++#define RT5350_TX_DTX_IDX1 (RT5350_PDMA_OFFSET + 0x1C) ++#define RT5350_TX_BASE_PTR2 (RT5350_PDMA_OFFSET + 0x20) ++#define RT5350_TX_MAX_CNT2 (RT5350_PDMA_OFFSET + 0x24) ++#define RT5350_TX_CTX_IDX2 (RT5350_PDMA_OFFSET + 0x28) ++#define RT5350_TX_DTX_IDX2 (RT5350_PDMA_OFFSET + 0x2C) ++#define RT5350_TX_BASE_PTR3 (RT5350_PDMA_OFFSET + 0x30) ++#define RT5350_TX_MAX_CNT3 (RT5350_PDMA_OFFSET + 0x34) ++#define RT5350_TX_CTX_IDX3 (RT5350_PDMA_OFFSET + 0x38) ++#define RT5350_TX_DTX_IDX3 (RT5350_PDMA_OFFSET + 0x3C) ++#define RT5350_RX_BASE_PTR0 (RT5350_PDMA_OFFSET + 0x100) ++#define RT5350_RX_MAX_CNT0 (RT5350_PDMA_OFFSET + 0x104) ++#define RT5350_RX_CALC_IDX0 (RT5350_PDMA_OFFSET + 0x108) ++#define RT5350_RX_DRX_IDX0 (RT5350_PDMA_OFFSET + 0x10C) ++#define RT5350_RX_BASE_PTR1 (RT5350_PDMA_OFFSET + 0x110) ++#define RT5350_RX_MAX_CNT1 (RT5350_PDMA_OFFSET + 0x114) ++#define RT5350_RX_CALC_IDX1 (RT5350_PDMA_OFFSET + 0x118) ++#define RT5350_RX_DRX_IDX1 (RT5350_PDMA_OFFSET + 0x11C) ++#define RT5350_PDMA_GLO_CFG (RT5350_PDMA_OFFSET + 0x204) ++#define RT5350_PDMA_RST_CFG (RT5350_PDMA_OFFSET + 0x208) ++#define RT5350_DLY_INT_CFG (RT5350_PDMA_OFFSET + 0x20c) ++#define RT5350_FE_INT_STATUS (RT5350_PDMA_OFFSET + 0x220) ++#define RT5350_FE_INT_ENABLE (RT5350_PDMA_OFFSET + 0x228) ++#define RT5350_PDMA_SCH_CFG (RT5350_PDMA_OFFSET + 0x280) ++ ++#define FE_PDMA_GLO_CFG (FE_PDMA_OFFSET + 0x00) ++#define FE_PDMA_RST_CFG (FE_PDMA_OFFSET + 0x04) ++#define FE_PDMA_SCH_CFG (FE_PDMA_OFFSET + 0x08) ++#define FE_DLY_INT_CFG (FE_PDMA_OFFSET + 0x0C) ++#define FE_TX_BASE_PTR0 (FE_PDMA_OFFSET + 0x10) ++#define FE_TX_MAX_CNT0 (FE_PDMA_OFFSET + 0x14) ++#define FE_TX_CTX_IDX0 (FE_PDMA_OFFSET + 0x18) ++#define FE_TX_DTX_IDX0 (FE_PDMA_OFFSET + 0x1C) ++#define FE_TX_BASE_PTR1 (FE_PDMA_OFFSET + 0x20) ++#define FE_TX_MAX_CNT1 (FE_PDMA_OFFSET + 0x24) ++#define FE_TX_CTX_IDX1 (FE_PDMA_OFFSET + 0x28) ++#define FE_TX_DTX_IDX1 (FE_PDMA_OFFSET + 0x2C) ++#define FE_RX_BASE_PTR0 (FE_PDMA_OFFSET + 0x30) ++#define FE_RX_MAX_CNT0 (FE_PDMA_OFFSET + 0x34) ++#define FE_RX_CALC_IDX0 (FE_PDMA_OFFSET + 0x38) ++#define FE_RX_DRX_IDX0 (FE_PDMA_OFFSET + 0x3C) ++#define FE_TX_BASE_PTR2 (FE_PDMA_OFFSET + 0x40) ++#define FE_TX_MAX_CNT2 (FE_PDMA_OFFSET + 0x44) ++#define FE_TX_CTX_IDX2 (FE_PDMA_OFFSET + 0x48) ++#define FE_TX_DTX_IDX2 (FE_PDMA_OFFSET + 0x4C) ++#define FE_TX_BASE_PTR3 (FE_PDMA_OFFSET + 0x50) ++#define FE_TX_MAX_CNT3 (FE_PDMA_OFFSET + 0x54) ++#define FE_TX_CTX_IDX3 (FE_PDMA_OFFSET + 0x58) ++#define FE_TX_DTX_IDX3 (FE_PDMA_OFFSET + 0x5C) ++#define FE_RX_BASE_PTR1 (FE_PDMA_OFFSET + 0x60) ++#define FE_RX_MAX_CNT1 (FE_PDMA_OFFSET + 0x64) ++#define FE_RX_CALC_IDX1 (FE_PDMA_OFFSET + 0x68) ++#define FE_RX_DRX_IDX1 (FE_PDMA_OFFSET + 0x6C) ++ ++#define RT5350_SDM_CFG (RT5350_SDM_OFFSET + 0x00) //Switch DMA configuration ++#define RT5350_SDM_RRING (RT5350_SDM_OFFSET + 0x04) //Switch DMA Rx Ring ++#define RT5350_SDM_TRING (RT5350_SDM_OFFSET + 0x08) //Switch DMA Tx Ring ++#define RT5350_SDM_MAC_ADRL (RT5350_SDM_OFFSET + 0x0C) //Switch MAC address LSB ++#define RT5350_SDM_MAC_ADRH (RT5350_SDM_OFFSET + 0x10) //Switch MAC Address MSB ++#define RT5350_SDM_TPCNT (RT5350_SDM_OFFSET + 0x100) //Switch DMA Tx packet count ++#define RT5350_SDM_TBCNT (RT5350_SDM_OFFSET + 0x104) //Switch DMA Tx byte count ++#define RT5350_SDM_RPCNT (RT5350_SDM_OFFSET + 0x108) //Switch DMA rx packet count ++#define RT5350_SDM_RBCNT (RT5350_SDM_OFFSET + 0x10C) //Switch DMA rx byte count ++#define RT5350_SDM_CS_ERR (RT5350_SDM_OFFSET + 0x110) //Switch DMA rx checksum error count ++ ++#define RT5350_SDM_ICS_EN BIT(16) ++#define RT5350_SDM_TCS_EN BIT(17) ++#define RT5350_SDM_UCS_EN BIT(18) ++ ++ ++/* MDIO_CFG register bits */ ++#define FE_MDIO_CFG_AUTO_POLL_EN BIT(29) ++#define FE_MDIO_CFG_GP1_BP_EN BIT(16) ++#define FE_MDIO_CFG_GP1_FRC_EN BIT(15) ++#define FE_MDIO_CFG_GP1_SPEED_10 (0 << 13) ++#define FE_MDIO_CFG_GP1_SPEED_100 (1 << 13) ++#define FE_MDIO_CFG_GP1_SPEED_1000 (2 << 13) ++#define FE_MDIO_CFG_GP1_DUPLEX BIT(12) ++#define FE_MDIO_CFG_GP1_FC_TX BIT(11) ++#define FE_MDIO_CFG_GP1_FC_RX BIT(10) ++#define FE_MDIO_CFG_GP1_LNK_DWN BIT(9) ++#define FE_MDIO_CFG_GP1_AN_FAIL BIT(8) ++#define FE_MDIO_CFG_MDC_CLK_DIV_1 (0 << 6) ++#define FE_MDIO_CFG_MDC_CLK_DIV_2 (1 << 6) ++#define FE_MDIO_CFG_MDC_CLK_DIV_4 (2 << 6) ++#define FE_MDIO_CFG_MDC_CLK_DIV_8 (3 << 6) ++#define FE_MDIO_CFG_TURBO_MII_FREQ BIT(5) ++#define FE_MDIO_CFG_TURBO_MII_MODE BIT(4) ++#define FE_MDIO_CFG_RX_CLK_SKEW_0 (0 << 2) ++#define FE_MDIO_CFG_RX_CLK_SKEW_200 (1 << 2) ++#define FE_MDIO_CFG_RX_CLK_SKEW_400 (2 << 2) ++#define FE_MDIO_CFG_RX_CLK_SKEW_INV (3 << 2) ++#define FE_MDIO_CFG_TX_CLK_SKEW_0 0 ++#define FE_MDIO_CFG_TX_CLK_SKEW_200 1 ++#define FE_MDIO_CFG_TX_CLK_SKEW_400 2 ++#define FE_MDIO_CFG_TX_CLK_SKEW_INV 3 ++ ++/* uni-cast port */ ++#define FE_GDM1_ICS_EN BIT(22) ++#define FE_GDM1_TCS_EN BIT(21) ++#define FE_GDM1_UCS_EN BIT(20) ++#define FE_GDM1_JMB_EN BIT(19) ++#define FE_GDM1_STRPCRC BIT(16) ++#define FE_GDM1_UFRC_P_CPU (0 << 12) ++#define FE_GDM1_UFRC_P_GDMA1 (1 << 12) ++#define FE_GDM1_UFRC_P_PPE (6 << 12) ++ ++/* checksums */ ++#define FE_ICS_GEN_EN BIT(2) ++#define FE_UCS_GEN_EN BIT(1) ++#define FE_TCS_GEN_EN BIT(0) ++ ++/* dma ring */ ++#define FE_PST_DRX_IDX0 BIT(16) ++#define FE_PST_DTX_IDX3 BIT(3) ++#define FE_PST_DTX_IDX2 BIT(2) ++#define FE_PST_DTX_IDX1 BIT(1) ++#define FE_PST_DTX_IDX0 BIT(0) ++ ++#define FE_TX_WB_DDONE BIT(6) ++#define FE_RX_DMA_BUSY BIT(3) ++#define FE_TX_DMA_BUSY BIT(1) ++#define FE_RX_DMA_EN BIT(2) ++#define FE_TX_DMA_EN BIT(0) ++ ++#define FE_PDMA_SIZE_4DWORDS (0 << 4) ++#define FE_PDMA_SIZE_8DWORDS (1 << 4) ++#define FE_PDMA_SIZE_16DWORDS (2 << 4) ++ ++#define FE_US_CYC_CNT_MASK 0xff ++#define FE_US_CYC_CNT_SHIFT 0x8 ++#define FE_US_CYC_CNT_DIVISOR 1000000 ++ ++#define RX_DMA_PLEN0(_x) (((_x) >> 16) & 0x3fff) ++#define RX_DMA_LSO BIT(30) ++#define RX_DMA_DONE BIT(31) ++#define RX_DMA_L4VALID BIT(30) ++ ++struct fe_rx_dma { ++ unsigned int rxd1; ++ unsigned int rxd2; ++ unsigned int rxd3; ++ unsigned int rxd4; ++} __packed __aligned(4); ++ ++#define TX_DMA_PLEN0_MASK ((0x3fff) << 16) ++#define TX_DMA_PLEN0(_x) (((_x) & 0x3fff) << 16) ++#define TX_DMA_LSO BIT(30) ++#define TX_DMA_DONE BIT(31) ++#define TX_DMA_QN(_x) ((_x) << 16) ++#define TX_DMA_PN(_x) ((_x) << 24) ++#define TX_DMA_QN_MASK TX_DMA_QN(0x7) ++#define TX_DMA_PN_MASK TX_DMA_PN(0x7) ++#define TX_DMA_CHKSUM (0x7 << 29) ++ ++struct fe_tx_dma { ++ unsigned int txd1; ++ unsigned int txd2; ++ unsigned int txd3; ++ unsigned int txd4; ++} __packed __aligned(4); ++ ++struct fe_priv; ++ ++struct fe_phy { ++ struct phy_device *phy[8]; ++ struct device_node *phy_node[8]; ++ const __be32 *phy_fixed[8]; ++ int duplex[8]; ++ int speed[8]; ++ spinlock_t lock; ++ ++ int (*connect)(struct fe_priv *priv); ++ void (*disconnect)(struct fe_priv *priv); ++ void (*start)(struct fe_priv *priv); ++ void (*stop)(struct fe_priv *priv); ++}; ++ ++struct fe_soc_data ++{ ++ unsigned char mac[6]; ++ const u32 *reg_table; ++ ++ void (*reset_fe)(void); ++ void (*set_mac)(struct fe_priv *priv, unsigned char *mac); ++ void (*fwd_config)(struct fe_priv *priv); ++ void (*tx_dma)(struct fe_priv *priv, int idx, int len); ++ void (*rx_dma)(struct fe_priv *priv, int idx, int len); ++ int (*switch_init)(struct fe_priv *priv); ++ void (*port_init)(struct fe_priv *priv, struct device_node *port); ++ int (*has_carrier)(struct fe_priv *priv); ++ int (*mdio_init)(struct fe_priv *priv); ++ void (*mdio_cleanup)(struct fe_priv *priv); ++ int (*mdio_write)(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val); ++ int (*mdio_read)(struct mii_bus *bus, int phy_addr, int phy_reg); ++ void (*mdio_adjust_link)(struct fe_priv *priv, int port); ++ ++ void *swpriv; ++ u32 pdma_glo_cfg; ++ u32 rx_dly_int; ++ u32 tx_dly_int; ++ u32 checksum_bit; ++ ++ int min_pkt_len; ++}; ++ ++struct fe_priv ++{ ++ spinlock_t page_lock; ++ ++ struct fe_soc_data *soc; ++ struct net_device *netdev; ++ struct device *device; ++ unsigned long sysclk; ++ ++ struct fe_rx_dma *rx_dma; ++ struct sk_buff *rx_skb[NUM_DMA_DESC]; ++ struct napi_struct rx_napi; ++ dma_addr_t rx_phys; ++ ++ struct fe_tx_dma *tx_dma; ++ struct sk_buff *tx_skb[NUM_DMA_DESC]; ++ struct napi_struct tx_napi; ++ dma_addr_t tx_phys; ++ unsigned int tx_free_idx; ++ ++ struct fe_phy *phy; ++ struct mii_bus *mii_bus; ++ int mii_irq[PHY_MAX_ADDR]; ++ ++ int link[8]; ++}; ++ ++extern const struct of_device_id of_fe_match[]; ++ ++void fe_w32(u32 val, unsigned reg); ++u32 fe_r32(unsigned reg); ++ ++#endif /* FE_ETH_H */ +diff --git a/drivers/net/ethernet/ralink/soc_mt7620.c b/drivers/net/ethernet/ralink/soc_mt7620.c +new file mode 100644 +index 0000000..55e303f +--- /dev/null ++++ b/drivers/net/ethernet/ralink/soc_mt7620.c +@@ -0,0 +1,111 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++#include "gsw_mt7620a.h" ++ ++#define MT7620A_CDMA_CSG_CFG 0x400 ++#define MT7620_DMA_VID (MT7620A_CDMA_CSG_CFG | 0x30) ++#define MT7620A_DMA_2B_OFFSET BIT(31) ++#define MT7620A_RESET_FE BIT(21) ++#define MT7620A_RESET_ESW BIT(23) ++#define MT7620_L4_VALID BIT(23) ++ ++#define SYSC_REG_RESET_CTRL 0x34 ++#define MAX_RX_LENGTH 1536 ++ ++#define CDMA_ICS_EN BIT(2) ++#define CDMA_UCS_EN BIT(1) ++#define CDMA_TCS_EN BIT(0) ++ ++#define GDMA_ICS_EN BIT(22) ++#define GDMA_TCS_EN BIT(21) ++#define GDMA_UCS_EN BIT(20) ++ ++static const u32 rt5350_reg_table[FE_REG_COUNT] = { ++ [FE_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG, ++ [FE_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG, ++ [FE_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG, ++ [FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0, ++ [FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0, ++ [FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0, ++ [FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0, ++ [FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0, ++ [FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0, ++ [FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE, ++ [FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS, ++ [FE_REG_FE_DMA_VID_BASE] = MT7620_DMA_VID, ++}; ++ ++static void mt7620_fe_reset(void) ++{ ++ rt_sysc_w32(MT7620A_RESET_FE | MT7620A_RESET_ESW, SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(0, SYSC_REG_RESET_CTRL); ++} ++ ++static void mt7620_fwd_config(struct fe_priv *priv) ++{ ++ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) & ~7, MT7620A_GDMA1_FWD_CFG); ++ fe_w32(fe_r32(MT7620A_GDMA1_FWD_CFG) | (GDMA_ICS_EN | GDMA_TCS_EN | GDMA_UCS_EN), MT7620A_GDMA1_FWD_CFG); ++ fe_w32(fe_r32(MT7620A_CDMA_CSG_CFG) | (CDMA_ICS_EN | CDMA_UCS_EN | CDMA_TCS_EN), MT7620A_CDMA_CSG_CFG); ++} ++ ++static void mt7620_tx_dma(struct fe_priv *priv, int idx, int len) ++{ ++ if (len) ++ priv->tx_dma[idx].txd2 = TX_DMA_LSO | TX_DMA_PLEN0(len); ++ else ++ priv->tx_dma[idx].txd2 = TX_DMA_LSO | TX_DMA_DONE; ++} ++ ++static void mt7620_rx_dma(struct fe_priv *priv, int idx, int len) ++{ ++ priv->rx_dma[idx].rxd2 = RX_DMA_PLEN0(len); ++} ++ ++static struct fe_soc_data mt7620_data = { ++ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, ++ .reset_fe = mt7620_fe_reset, ++ .set_mac = mt7620_set_mac, ++ .fwd_config = mt7620_fwd_config, ++ .tx_dma = mt7620_tx_dma, ++ .rx_dma = mt7620_rx_dma, ++ .switch_init = mt7620_gsw_probe, ++ .port_init = mt7620_port_init, ++ .min_pkt_len = 0, ++ .reg_table = rt5350_reg_table, ++ .pdma_glo_cfg = FE_PDMA_SIZE_16DWORDS | MT7620A_DMA_2B_OFFSET, ++ .rx_dly_int = RT5350_RX_DLY_INT, ++ .tx_dly_int = RT5350_TX_DLY_INT, ++ .checksum_bit = MT7620_L4_VALID, ++ .has_carrier = mt7620a_has_carrier, ++ .mdio_read = mt7620_mdio_read, ++ .mdio_write = mt7620_mdio_write, ++ .mdio_adjust_link = mt7620_mdio_link_adjust, ++}; ++ ++const struct of_device_id of_fe_match[] = { ++ { .compatible = "ralink,mt7620a-eth", .data = &mt7620_data }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_fe_match); +diff --git a/drivers/net/ethernet/ralink/soc_rt2880.c b/drivers/net/ethernet/ralink/soc_rt2880.c +new file mode 100644 +index 0000000..fdbd118 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/soc_rt2880.c +@@ -0,0 +1,51 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++#include "mdio_rt2880.h" ++ ++#define SYSC_REG_RESET_CTRL 0x034 ++#define RT2880_RESET_FE BIT(18) ++ ++void rt2880_fe_reset(void) ++{ ++ rt_sysc_w32(RT2880_RESET_FE, SYSC_REG_RESET_CTRL); ++} ++ ++struct fe_soc_data rt2880_data = { ++ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, ++ .reset_fe = rt2880_fe_reset, ++ .min_pkt_len = 64, ++ .pdma_glo_cfg = FE_PDMA_SIZE_4DWORDS, ++ .checksum_bit = RX_DMA_L4VALID, ++ .rx_dly_int = FE_RX_DLY_INT, ++ .tx_dly_int = FE_TX_DLY_INT, ++ .mdio_read = rt2880_mdio_read, ++ .mdio_write = rt2880_mdio_write, ++ .mdio_link_adjust = rt2880_mdio_link_adjust, ++}; ++ ++const struct of_device_id of_fe_match[] = { ++ { .compatible = "ralink,rt2880-eth", .data = &rt2880_data }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_fe_match); +diff --git a/drivers/net/ethernet/ralink/soc_rt305x.c b/drivers/net/ethernet/ralink/soc_rt305x.c +new file mode 100644 +index 0000000..c43d3f9 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/soc_rt305x.c +@@ -0,0 +1,102 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++ ++#define RT305X_RESET_FE BIT(21) ++#define RT305X_RESET_ESW BIT(23) ++#define SYSC_REG_RESET_CTRL 0x034 ++ ++static const u32 rt5350_reg_table[FE_REG_COUNT] = { ++ [FE_REG_PDMA_GLO_CFG] = RT5350_PDMA_GLO_CFG, ++ [FE_REG_PDMA_RST_CFG] = RT5350_PDMA_RST_CFG, ++ [FE_REG_DLY_INT_CFG] = RT5350_DLY_INT_CFG, ++ [FE_REG_TX_BASE_PTR0] = RT5350_TX_BASE_PTR0, ++ [FE_REG_TX_MAX_CNT0] = RT5350_TX_MAX_CNT0, ++ [FE_REG_TX_CTX_IDX0] = RT5350_TX_CTX_IDX0, ++ [FE_REG_RX_BASE_PTR0] = RT5350_RX_BASE_PTR0, ++ [FE_REG_RX_MAX_CNT0] = RT5350_RX_MAX_CNT0, ++ [FE_REG_RX_CALC_IDX0] = RT5350_RX_CALC_IDX0, ++ [FE_REG_FE_INT_ENABLE] = RT5350_FE_INT_ENABLE, ++ [FE_REG_FE_INT_STATUS] = RT5350_FE_INT_STATUS, ++ [FE_REG_FE_DMA_VID_BASE] = 0, ++}; ++ ++static void rt305x_fe_reset(void) ++{ ++ rt_sysc_w32(RT305X_RESET_FE, SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(0, SYSC_REG_RESET_CTRL); ++} ++ ++static void rt5350_set_mac(struct fe_priv *priv, unsigned char *mac) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->page_lock, flags); ++ fe_w32((mac[0] << 8) | mac[1], RT5350_SDM_MAC_ADRH); ++ fe_w32((mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5], ++ RT5350_SDM_MAC_ADRL); ++ spin_unlock_irqrestore(&priv->page_lock, flags); ++} ++ ++static void rt5350_fwd_config(struct fe_priv *priv) ++{ ++ fe_w32(fe_r32(RT5350_SDM_CFG) & ~0xffff, RT5350_SDM_CFG); ++ fe_w32(fe_r32(RT5350_SDM_CFG) | RT5350_SDM_ICS_EN | RT5350_SDM_TCS_EN | RT5350_SDM_UCS_EN, ++ RT5350_SDM_CFG); ++} ++ ++static void rt5350_fe_reset(void) ++{ ++ rt_sysc_w32(RT305X_RESET_FE | RT305X_RESET_ESW, SYSC_REG_RESET_CTRL); ++ rt_sysc_w32(0, SYSC_REG_RESET_CTRL); ++} ++ ++static struct fe_soc_data rt3050_data = { ++ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, ++ .reset_fe = rt305x_fe_reset, ++ .min_pkt_len = 64, ++ .pdma_glo_cfg = FE_PDMA_SIZE_4DWORDS, ++ .checksum_bit = RX_DMA_L4VALID, ++ .rx_dly_int = FE_RX_DLY_INT, ++ .tx_dly_int = FE_TX_DLY_INT, ++}; ++ ++static struct fe_soc_data rt5350_data = { ++ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, ++ .reg_table = rt5350_reg_table, ++ .reset_fe = rt5350_fe_reset, ++ .set_mac = rt5350_set_mac, ++ .fwd_config = rt5350_fwd_config, ++ .min_pkt_len = 64, ++ .pdma_glo_cfg = FE_PDMA_SIZE_4DWORDS, ++ .checksum_bit = RX_DMA_L4VALID, ++ .rx_dly_int = RT5350_RX_DLY_INT, ++ .tx_dly_int = RT5350_TX_DLY_INT, ++}; ++ ++const struct of_device_id of_fe_match[] = { ++ { .compatible = "ralink,rt3050-eth", .data = &rt3050_data }, ++ { .compatible = "ralink,rt5350-eth", .data = &rt5350_data }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_fe_match); +diff --git a/drivers/net/ethernet/ralink/soc_rt3883.c b/drivers/net/ethernet/ralink/soc_rt3883.c +new file mode 100644 +index 0000000..3886be1 +--- /dev/null ++++ b/drivers/net/ethernet/ralink/soc_rt3883.c +@@ -0,0 +1,59 @@ ++/* ++ * 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 ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2009-2013 John Crispin ++ */ ++ ++#include ++ ++#include ++ ++#include "ralink_soc_eth.h" ++#include "mdio_rt2880.h" ++ ++#define RT3883_SYSC_REG_RSTCTRL 0x34 ++#define RT3883_RSTCTRL_FE BIT(21) ++ ++static void rt3883_fe_reset(void) ++{ ++ u32 t; ++ ++ t = rt_sysc_r32(RT3883_SYSC_REG_RSTCTRL); ++ t |= RT3883_RSTCTRL_FE; ++ rt_sysc_w32(t , RT3883_SYSC_REG_RSTCTRL); ++ ++ t &= ~RT3883_RSTCTRL_FE; ++ rt_sysc_w32(t, RT3883_SYSC_REG_RSTCTRL); ++} ++ ++static struct fe_soc_data rt3883_data = { ++ .mac = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }, ++ .reset_fe = rt3883_fe_reset, ++ .min_pkt_len = 64, ++ .pdma_glo_cfg = FE_PDMA_SIZE_4DWORDS, ++ .rx_dly_int = FE_RX_DLY_INT, ++ .tx_dly_int = FE_TX_DLY_INT, ++ .checksum_bit = RX_DMA_L4VALID, ++ .mdio_read = rt2880_mdio_read, ++ .mdio_write = rt2880_mdio_write, ++ .mdio_link_adjust = rt2880_mdio_link_adjust, ++}; ++ ++const struct of_device_id of_fe_match[] = { ++ { .compatible = "ralink,rt3883-eth", .data = &rt3883_data }, ++ {}, ++}; ++ ++MODULE_DEVICE_TABLE(of, of_fe_match); ++ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0160-USB-phy-add-ralink-SoC-driver.patch b/target/linux/ramips/patches-3.9/0160-USB-phy-add-ralink-SoC-driver.patch new file mode 100644 index 0000000000..095f911809 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0160-USB-phy-add-ralink-SoC-driver.patch @@ -0,0 +1,238 @@ +From efe391a3614b98a0f84fd49e63b4009185ff9a1a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 2 May 2013 23:29:08 +0200 +Subject: [PATCH 160/164] USB: phy: add ralink SoC driver + +Signed-off-by: John Crispin +--- + drivers/usb/phy/Kconfig | 8 ++ + drivers/usb/phy/Makefile | 1 + + drivers/usb/phy/ralink-phy.c | 191 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 200 insertions(+) + create mode 100644 drivers/usb/phy/ralink-phy.c + +diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig +index 9054938..b197c70 100644 +--- a/drivers/usb/phy/Kconfig ++++ b/drivers/usb/phy/Kconfig +@@ -74,3 +74,11 @@ config SAMSUNG_USBPHY + help + Enable this to support Samsung USB phy controller for samsung + SoCs. ++ ++config RALINK_USBPHY ++ bool "Ralink USB PHY controller Driver" ++ depends on MIPS && RALINK ++ select USB_OTG_UTILS ++ help ++ Enable this to support ralink USB phy controller for ralink ++ SoCs. +diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile +index b13faa1..2df30f1 100644 +--- a/drivers/usb/phy/Makefile ++++ b/drivers/usb/phy/Makefile +@@ -12,3 +12,4 @@ obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o + obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o + obj-$(CONFIG_USB_RCAR_PHY) += rcar-phy.o + obj-$(CONFIG_SAMSUNG_USBPHY) += samsung-usbphy.o ++obj-$(CONFIG_RALINK_USBPHY) += ralink-phy.o +diff --git a/drivers/usb/phy/ralink-phy.c b/drivers/usb/phy/ralink-phy.c +new file mode 100644 +index 0000000..3fbabea +--- /dev/null ++++ b/drivers/usb/phy/ralink-phy.c +@@ -0,0 +1,191 @@ ++/* ++ * Copyright (C) 2013 John Crispin ++ * ++ * based on: Renesas R-Car USB phy driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define RT_SYSC_REG_SYSCFG1 0x014 ++#define RT_SYSC_REG_CLKCFG1 0x030 ++#define RT_SYSC_REG_USB_PHY_CFG 0x05c ++ ++#define RT_RSTCTRL_UDEV BIT(25) ++#define RT_RSTCTRL_UHST BIT(22) ++#define RT_SYSCFG1_USB0_HOST_MODE BIT(10) ++ ++#define MT7620_CLKCFG1_UPHY0_CLK_EN BIT(25) ++#define RT_CLKCFG1_UPHY1_CLK_EN BIT(20) ++#define RT_CLKCFG1_UPHY0_CLK_EN BIT(18) ++ ++#define USB_PHY_UTMI_8B60M BIT(1) ++#define UDEV_WAKEUP BIT(0) ++ ++static atomic_t usb_pwr_ref = ATOMIC_INIT(0); ++static struct reset_control *rstdev; ++static struct reset_control *rsthost; ++static u32 phy_clk; ++ ++static void usb_phy_enable(int state) ++{ ++ if (state) ++ rt_sysc_m32(0, phy_clk, RT_SYSC_REG_CLKCFG1); ++ else ++ rt_sysc_m32(phy_clk, 0, RT_SYSC_REG_CLKCFG1); ++ mdelay(100); ++} ++ ++static int usb_power_on(struct usb_phy *phy) ++{ ++ if (atomic_inc_return(&usb_pwr_ref) == 1) { ++ u32 t; ++ ++ usb_phy_enable(1); ++ ++ reset_control_assert(rstdev); ++ reset_control_assert(rsthost); ++ ++ if (OTG_STATE_B_HOST) { ++ rt_sysc_m32(0, RT_SYSCFG1_USB0_HOST_MODE, RT_SYSC_REG_SYSCFG1); ++ reset_control_deassert(rsthost); ++ } else { ++ rt_sysc_m32(RT_SYSCFG1_USB0_HOST_MODE, 0, RT_SYSC_REG_SYSCFG1); ++ reset_control_deassert(rstdev); ++ } ++ mdelay(100); ++ ++ t = rt_sysc_r32(RT_SYSC_REG_USB_PHY_CFG); ++ dev_info(phy->dev, "remote usb device wakeup %s\n", ++ (t & UDEV_WAKEUP) ? ("enabbled") : ("disabled")); ++ if (t & USB_PHY_UTMI_8B60M) ++ dev_info(phy->dev, "UTMI 8bit 60MHz\n"); ++ else ++ dev_info(phy->dev, "UTMI 16bit 30MHz\n"); ++ } ++ ++ return 0; ++} ++ ++static void usb_power_off(struct usb_phy *phy) ++{ ++ if (atomic_dec_return(&usb_pwr_ref) == 0) { ++ usb_phy_enable(0); ++ reset_control_assert(rstdev); ++ reset_control_assert(rsthost); ++ } ++} ++ ++static int usb_set_host(struct usb_otg *otg, struct usb_bus *host) ++{ ++ otg->gadget = NULL; ++ otg->host = host; ++ ++ return 0; ++} ++ ++static int usb_set_peripheral(struct usb_otg *otg, ++ struct usb_gadget *gadget) ++{ ++ otg->host = NULL; ++ otg->gadget = gadget; ++ ++ return 0; ++} ++ ++static const struct of_device_id ralink_usbphy_dt_match[] = { ++ { .compatible = "ralink,rt3xxx-usbphy", .data = (void *) (RT_CLKCFG1_UPHY1_CLK_EN | RT_CLKCFG1_UPHY0_CLK_EN) }, ++ { .compatible = "ralink,mt7620a-usbphy", .data = (void *) MT7620_CLKCFG1_UPHY0_CLK_EN }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ralink_usbphy_dt_match); ++ ++static int usb_phy_probe(struct platform_device *pdev) ++{ ++ const struct of_device_id *match; ++ struct device *dev = &pdev->dev; ++ struct usb_otg *otg; ++ struct usb_phy *phy; ++ int ret; ++ ++ match = of_match_device(ralink_usbphy_dt_match, &pdev->dev); ++ phy_clk = (int) match->data; ++ ++ rsthost = devm_reset_control_get(&pdev->dev, "host"); ++ if (IS_ERR(rsthost)) ++ return PTR_ERR(rsthost); ++ ++ rstdev = devm_reset_control_get(&pdev->dev, "device"); ++ if (IS_ERR(rstdev)) ++ return PTR_ERR(rstdev); ++ ++ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); ++ if (!phy) { ++ dev_err(&pdev->dev, "unable to allocate memory for USB PHY\n"); ++ return -ENOMEM; ++ } ++ ++ otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL); ++ if (!otg) { ++ dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n"); ++ return -ENOMEM; ++ } ++ ++ phy->dev = dev; ++ phy->label = dev_name(dev); ++ phy->init = usb_power_on; ++ phy->shutdown = usb_power_off; ++ otg->set_host = usb_set_host; ++ otg->set_peripheral = usb_set_peripheral; ++ otg->phy = phy; ++ phy->otg = otg; ++ ret = usb_add_phy(phy, USB_PHY_TYPE_USB2); ++ ++ if (ret < 0) { ++ dev_err(dev, "usb phy addition error\n"); ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, phy); ++ ++ dev_info(&pdev->dev, "loaded\n"); ++ ++ return ret; ++} ++ ++static int usb_phy_remove(struct platform_device *pdev) ++{ ++ struct usb_phy *phy = platform_get_drvdata(pdev); ++ ++ usb_remove_phy(phy); ++ ++ return 0; ++} ++ ++static struct platform_driver usb_phy_driver = { ++ .driver = { ++ .owner = THIS_MODULE, ++ .name = "rt3xxx-usbphy", ++ .of_match_table = of_match_ptr(ralink_usbphy_dt_match), ++ }, ++ .probe = usb_phy_probe, ++ .remove = usb_phy_remove, ++}; ++ ++module_platform_driver(usb_phy_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_DESCRIPTION("Ralink USB phy"); ++MODULE_AUTHOR("John Crispin "); +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0161-USB-add-OHCI-EHCI-OF-binding.patch b/target/linux/ramips/patches-3.9/0161-USB-add-OHCI-EHCI-OF-binding.patch new file mode 100644 index 0000000000..abb1171391 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0161-USB-add-OHCI-EHCI-OF-binding.patch @@ -0,0 +1,245 @@ +From 9a15efea2e55327cf7c813da64589509f9c40786 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 16 May 2013 23:11:45 +0200 +Subject: [PATCH 161/164] USB: add OHCI/EHCI OF binding + +based on f3bc64d6d1f21c1b92d75f233a37b75d77af6963 + +Signed-off-by: John Crispin +--- + arch/mips/ralink/Kconfig | 2 ++ + drivers/usb/Makefile | 3 ++- + drivers/usb/host/ehci-platform.c | 44 ++++++++++++++++++++++++++++++++------ + drivers/usb/host/ohci-platform.c | 37 +++++++++++++++++++++++++++----- + 4 files changed, 74 insertions(+), 12 deletions(-) + +diff --git a/arch/mips/ralink/Kconfig b/arch/mips/ralink/Kconfig +index c8d5b6c..7cd1188 100644 +--- a/arch/mips/ralink/Kconfig ++++ b/arch/mips/ralink/Kconfig +@@ -27,6 +27,8 @@ choice + bool "MT7620" + select CLKEVT_RT3352 + select HW_HAS_PCI ++ select USB_ARCH_HAS_OHCI ++ select USB_ARCH_HAS_EHCI + + endchoice + +diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile +index 8f5ebce..b766256 100644 +--- a/drivers/usb/Makefile ++++ b/drivers/usb/Makefile +@@ -12,6 +12,8 @@ obj-$(CONFIG_USB_DWC3) += dwc3/ + + obj-$(CONFIG_USB_MON) += mon/ + ++obj-$(CONFIG_USB_OTG_UTILS) += phy/ ++ + obj-$(CONFIG_PCI) += host/ + obj-$(CONFIG_USB_EHCI_HCD) += host/ + obj-$(CONFIG_USB_ISP116X_HCD) += host/ +@@ -46,7 +48,6 @@ obj-$(CONFIG_USB_MICROTEK) += image/ + obj-$(CONFIG_USB_SERIAL) += serial/ + + obj-$(CONFIG_USB) += misc/ +-obj-$(CONFIG_USB_OTG_UTILS) += phy/ + obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/ + + obj-$(CONFIG_USB_ATM) += atm/ +diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c +index ca75063..ae32410 100644 +--- a/drivers/usb/host/ehci-platform.c ++++ b/drivers/usb/host/ehci-platform.c +@@ -18,14 +18,18 @@ + * + * Licensed under the GNU/GPL. See COPYING for details. + */ ++#include + #include + #include + #include + #include + #include ++#include + #include + #include + #include ++#include ++#include + #include + + #include "ehci.h" +@@ -62,22 +66,32 @@ static const struct ehci_driver_overrides platform_overrides __initdata = { + .reset = ehci_platform_reset, + }; + ++static struct usb_ehci_pdata ehci_platform_defaults; ++ + static int ehci_platform_probe(struct platform_device *dev) + { + struct usb_hcd *hcd; + struct resource *res_mem; +- struct usb_ehci_pdata *pdata = dev->dev.platform_data; ++ struct usb_ehci_pdata *pdata; + int irq; + int err = -ENOMEM; + +- if (!pdata) { +- WARN_ON(1); +- return -ENODEV; +- } +- + if (usb_disabled()) + return -ENODEV; + ++ /* ++ * use reasonable defaults so platforms don't have to provide these. ++ * with DT probing on ARM, none of these are set. ++ */ ++ if (!dev->dev.platform_data) ++ dev->dev.platform_data = &ehci_platform_defaults; ++ if (!dev->dev.dma_mask) ++ dev->dev.dma_mask = &dev->dev.coherent_dma_mask; ++ if (!dev->dev.coherent_dma_mask) ++ dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); ++ ++ pdata = dev->dev.platform_data; ++ + irq = platform_get_irq(dev, 0); + if (irq < 0) { + dev_err(&dev->dev, "no irq provided"); +@@ -105,6 +119,15 @@ static int ehci_platform_probe(struct platform_device *dev) + hcd->rsrc_start = res_mem->start; + hcd->rsrc_len = resource_size(res_mem); + ++#ifdef CONFIG_USB_OTG_UTILS ++ hcd->phy = devm_usb_get_phy(&dev->dev, USB_PHY_TYPE_USB2); ++ if (!IS_ERR_OR_NULL(hcd->phy)) { ++ otg_set_host(hcd->phy->otg, ++ &hcd->self); ++ usb_phy_init(hcd->phy); ++ } ++#endif ++ + hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); + if (IS_ERR(hcd->regs)) { + err = PTR_ERR(hcd->regs); +@@ -139,6 +162,9 @@ static int ehci_platform_remove(struct platform_device *dev) + if (pdata->power_off) + pdata->power_off(dev); + ++ if (pdata == &ehci_platform_defaults) ++ dev->dev.platform_data = NULL; ++ + return 0; + } + +@@ -183,6 +209,11 @@ static int ehci_platform_resume(struct device *dev) + #define ehci_platform_resume NULL + #endif /* CONFIG_PM */ + ++static const struct of_device_id ralink_ehci_ids[] = { ++ { .compatible = "ralink,rt3xxx-ehci", }, ++ {} ++}; ++ + static const struct platform_device_id ehci_platform_table[] = { + { "ehci-platform", 0 }, + { } +@@ -203,6 +234,7 @@ static struct platform_driver ehci_platform_driver = { + .owner = THIS_MODULE, + .name = "ehci-platform", + .pm = &ehci_platform_pm_ops, ++ .of_match_table = of_match_ptr(ralink_ehci_ids), + } + }; + +diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c +index c3e7287..dd9bac6 100644 +--- a/drivers/usb/host/ohci-platform.c ++++ b/drivers/usb/host/ohci-platform.c +@@ -16,6 +16,10 @@ + #include + #include + #include ++#include ++#include ++ ++static struct usb_ohci_pdata ohci_platform_defaults; + + static int ohci_platform_reset(struct usb_hcd *hcd) + { +@@ -88,14 +92,22 @@ static int ohci_platform_probe(struct platform_device *dev) + { + struct usb_hcd *hcd; + struct resource *res_mem; +- struct usb_ohci_pdata *pdata = dev->dev.platform_data; ++ struct usb_ohci_pdata *pdata; + int irq; + int err = -ENOMEM; + +- if (!pdata) { +- WARN_ON(1); +- return -ENODEV; +- } ++ /* ++ * use reasonable defaults so platforms don't have to provide these. ++ * with DT probing on ARM, none of these are set. ++ */ ++ if (!dev->dev.platform_data) ++ dev->dev.platform_data = &ohci_platform_defaults; ++ if (!dev->dev.dma_mask) ++ dev->dev.dma_mask = &dev->dev.coherent_dma_mask; ++ if (!dev->dev.coherent_dma_mask) ++ dev->dev.coherent_dma_mask = DMA_BIT_MASK(32); ++ ++ pdata = dev->dev.platform_data; + + if (usb_disabled()) + return -ENODEV; +@@ -128,6 +140,12 @@ static int ohci_platform_probe(struct platform_device *dev) + hcd->rsrc_start = res_mem->start; + hcd->rsrc_len = resource_size(res_mem); + ++#ifdef CONFIG_USB_OTG_UTILS ++ hcd->phy = devm_usb_get_phy(&dev->dev, USB_PHY_TYPE_USB2); ++ if (!IS_ERR_OR_NULL(hcd->phy)) ++ usb_phy_init(hcd->phy); ++#endif ++ + hcd->regs = devm_ioremap_resource(&dev->dev, res_mem); + if (IS_ERR(hcd->regs)) { + err = PTR_ERR(hcd->regs); +@@ -162,6 +180,9 @@ static int ohci_platform_remove(struct platform_device *dev) + if (pdata->power_off) + pdata->power_off(dev); + ++ if (pdata == &ohci_platform_defaults) ++ dev->dev.platform_data = NULL; ++ + return 0; + } + +@@ -201,6 +222,11 @@ static int ohci_platform_resume(struct device *dev) + #define ohci_platform_resume NULL + #endif /* CONFIG_PM */ + ++static const struct of_device_id ralink_ohci_ids[] = { ++ { .compatible = "ralink,rt3xxx-ohci", }, ++ {} ++}; ++ + static const struct platform_device_id ohci_platform_table[] = { + { "ohci-platform", 0 }, + { } +@@ -221,5 +247,6 @@ static struct platform_driver ohci_platform_driver = { + .owner = THIS_MODULE, + .name = "ohci-platform", + .pm = &ohci_platform_pm_ops, ++ .of_match_table = of_match_ptr(ralink_ohci_ids), + } + }; +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0162-USB-MIPS-ralink-add-rt5350-mt7620-UDC.patch b/target/linux/ramips/patches-3.9/0162-USB-MIPS-ralink-add-rt5350-mt7620-UDC.patch new file mode 100644 index 0000000000..d1110a54cd --- /dev/null +++ b/target/linux/ramips/patches-3.9/0162-USB-MIPS-ralink-add-rt5350-mt7620-UDC.patch @@ -0,0 +1,3025 @@ +From 0e3b1bffd1974e6852912865a7cea481617b1c39 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 30 May 2013 16:06:35 +0200 +Subject: [PATCH 162/164] USB: MIPS: ralink: add rt5350/mt7620 UDC + +Signed-off-by: John Crispin +--- + drivers/usb/gadget/Kconfig | 8 + + drivers/usb/gadget/Makefile | 1 + + drivers/usb/gadget/rt_udc.h | 417 +++++++ + drivers/usb/gadget/rt_udc_pdma.c | 2547 ++++++++++++++++++++++++++++++++++++++ + 4 files changed, 2973 insertions(+) + create mode 100644 drivers/usb/gadget/rt_udc.h + create mode 100644 drivers/usb/gadget/rt_udc_pdma.c + +diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig +index c7525b1..6f0e293 100644 +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -336,6 +336,14 @@ config USB_MV_U3D + MARVELL PXA2128 Processor series include a super speed USB3.0 device + controller, which support super speed USB peripheral. + ++config USB_RT_UDC ++ boolean "Ralink USB Device Port" ++ depends on SOC_MT7620 ++ help ++ Say "y" to link the driver statically, or "m" to build a ++ dynamically linked module called "rt_udc" and force all ++ gadget drivers to also be dynamically linked. ++ + # + # Controllers available in both integrated and discrete versions + # +diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile +index 82fb225..f78a3b2 100644 +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -34,6 +34,7 @@ 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_MV_U3D) += mv_u3d_core.o ++obj-$(CONFIG_USB_RT_UDC) += rt_udc_pdma.o + + # USB Functions + obj-$(CONFIG_USB_F_ACM) += f_acm.o +diff --git a/drivers/usb/gadget/rt_udc.h b/drivers/usb/gadget/rt_udc.h +new file mode 100644 +index 0000000..088e0d9 +--- /dev/null ++++ b/drivers/usb/gadget/rt_udc.h +@@ -0,0 +1,417 @@ ++/* ++ * Copyright (C) 2009 Y.Y. Huang, Ralink Tech.(yy_huang@ralinktech.com) ++ * ++ * This udc driver is now under testing and code is based on pxa2xx_udc.h ++ * Please use it with your own risk! ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++#ifndef __LINUX_USB_GADGET_RT_UDC_H ++#define __LINUX_USB_GADGET_RT_UDC_H ++ ++#define CONFIG_RALINK_MT7620 ++ ++#include ++ ++//#include "../host/ralink_usb.h" /* for port sharing setting and power saving purpose */ ++ ++#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) ++#define IN_EP_NUM 2 ++#define OUT_EP_NUM 2 ++#elif defined (CONFIG_RALINK_RT5350) ++#define IN_EP_NUM 1 ++#define OUT_EP_NUM 1 ++#else ++#error "Please define a platform." ++#endif ++ ++/* Helper macros */ ++#define EP_IDX(ep) ((ep->bEndpointAddress & ~USB_DIR_IN)+(EP_DIR(ep)? 0:IN_EP_NUM)) /* IN:1, OUT:0 */ ++#define EP_NO(ep) ((ep->bEndpointAddress & ~USB_DIR_IN)) /* IN:1, OUT:0 */ ++#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0) ++#define EP_IN 1 ++#define EP_OUT 0 ++#define RT_USB_NB_EP (IN_EP_NUM + OUT_EP_NUM + 1) ++ ++/* Driver structures */ ++struct rt_request { ++ struct usb_request req; ++ struct list_head queue; ++ unsigned int in_use; ++ struct rt_ep_struct *rt_ep; // test for rx tasklet ++ int zlp_dma_done; // used for DMA ZLP packet. ++ int txd_count; ++}; ++ ++enum ep0_state { ++ EP0_IDLE, ++ EP0_IN_DATA_PHASE, ++ EP0_OUT_DATA_PHASE, ++ EP0_NO_DATA_PHASE, ++ EP0_STALL, ++}; ++ ++struct rt_ep_struct { ++ struct usb_ep ep; ++ struct rt_udc_struct *rt_usb; ++ struct list_head queue; ++ unsigned char stopped; ++ unsigned char bEndpointAddress; ++ unsigned char bmAttributes; ++ ++ unsigned char pending; ++ unsigned int rx_done_count; /* used by OUT EP only */ ++ unsigned int tx_done_count; /* used by OUT EP only */ ++}; ++ ++struct rt_udc_struct { ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ struct device *dev; ++ struct rt_ep_struct rt_ep[RT_USB_NB_EP]; ++ /* struct clk *clk; */ ++ struct timer_list timer; ++ enum ep0_state ep0state; ++ struct resource *res; ++ void __iomem *base; ++ unsigned char set_config; ++ int cfg, ++ intf, ++ alt, ++ interrupt; ++}; ++ ++#define USB_BASE (0xB0120000) ++ ++#define OUT0BC (0x000) ++#define IN0BC (0x001) ++#define EP0CS (0x002) ++ ++#define OUT1CON (0x00A) ++#define IN1CON (0x00E) ++#define OUT2CON (0x012) ++#define IN2CON (0x016) ++#define OUT3CON (0x01A) ++#define IN3CON (0x01E) ++#define OUT4CON (0x022) ++#define IN4CON (0x026) ++ ++ ++#define EP0INDAT (0x100) ++#define EP0OUTDAT (0x140) ++#define SETUPDATA (0x180) ++ ++#define IN07IRQ (0x188) ++#define IN815IRQ (0x189) ++#define OUT07IRQ (0x18A) ++#define OUT815IRQ (0x18B) ++#define USBIRQ (0x18C) ++#define OUT07PNGIRQ (0x18E) ++#define OUT815PNGIRQ (0x18F) ++ ++#define IN07IEN (0x194) ++#define OUT07IEN (0x196) ++#define USBIEN (0x198) ++ ++#define OUT07PNGIEN (0x19A) ++#define OUT815PNGIEN (0x19B) ++ ++#define ENDPRST (0x1A2) ++#define ENDPRST_IO (0x1 << 4) ++#define ENDPRST_TOGRST (0x1 << 5) ++#define ENDPRST_FIFORST (0x1 << 6) ++ ++#define FIFOCTRL (0x1A8) ++ ++#define EP_CS_EP0_STALL (0x1 << 0) ++#define EP_CS_EP0_HSNAK (0x1 << 1) ++#define EP_CS_EP0_INBSY (0x1 << 2) ++#define EP_CS_EP0_OUTBSY (0x1 << 3) ++#define EP_CS_AUTO (0x1 << 4) ++#define EP_CS_NPAK1 (0x1 << 3) ++#define EP_CS_NPAK0 (0x1 << 2) ++#define EP_CS_BSY (0x1 << 1) ++#define EP_CS_ERR (0x1 << 0) ++ ++#define EP0_OUT_BSY (0x1 << 3) ++#define EP0_IN_BSY (0x1 << 2) ++ ++#define USB_INTR_HSPEED (0x20) ++#define USB_INTR_RESET (0x10) ++#define USB_INTR_SUSPEND (0x08) ++#define USB_INTR_SETUP_TOKEN (0x04) ++#define USB_INTR_SOF (0x02) ++#define USB_INTR_SETUP_TOKEN_VALID (0x01) ++ ++/* UDMA */ ++#define RTUSB_UDMA_CTRL (USB_BASE + 0x800) ++#define RTUSB_UDMA_WRR (USB_BASE + 0x804) ++ ++/* PDMA */ ++#define RTUSB_TX_BASE_PTR0 (USB_BASE + 0x1000) ++#define RTUSB_TX_MAX_CNT0 (USB_BASE + 0x1004) ++#define RTUSB_TX_CTX_IDX0 (USB_BASE + 0x1008) ++#define RTUSB_TX_DTX_IDX0 (USB_BASE + 0x100C) ++#define RTUSB_TX_BASE_PTR1 (USB_BASE + 0x1010) ++#define RTUSB_TX_MAX_CNT1 (USB_BASE + 0x1014) ++#define RTUSB_TX_CTX_IDX1 (USB_BASE + 0x1018) ++#define RTUSB_TX_DTX_IDX1 (USB_BASE + 0x101C) ++#define RTUSB_RX_BASE_PTR0 (USB_BASE + 0x1100) ++#define RTUSB_RX_MAX_CNT0 (USB_BASE + 0x1104) ++#define RTUSB_RX_CALC_IDX0 (USB_BASE + 0x1108) ++#define RTUSB_RX_DRX_IDX0 (USB_BASE + 0x110C) ++#define RTUSB_PDMA_GLO_CFG (USB_BASE + 0x1204) ++ ++#define RTUSB_TX_WB_DDONE (0x1 << 6) ++#define RTUSB_RX_DMA_BUSY (0x1 << 3) ++#define RTUSB_RX_DMA_EN (0x1 << 2) ++#define RTUSB_TX_DMA_BUSY (0x1 << 1) ++#define RTUSB_TX_DMA_EN (0x1 << 0) ++ ++#define RTUSB_PDMA_RST_IDX (USB_BASE + 0x1208) ++ ++#define RTUSB_RST_DRX_IDX1 (0x1 << 17) ++#define RTUSB_RST_DRX_IDX0 (0x1 << 16) ++#define RTUSB_RST_DTX_IDX3 (0x1 << 3) ++#define RTUSB_RST_DTX_IDX2 (0x1 << 2) ++#define RTUSB_RST_DTX_IDX1 (0x1 << 1) ++#define RTUSB_RST_DTX_IDX0 (0x1 << 0) ++ ++#define RTUSB_DELAY_INT_CFG (USB_BASE + 0x120C) ++#define RTUSB_INT_STATUS (USB_BASE + 0x1220) ++#define RTUSB_RX_DONE_INT1 (0x1 << 17) ++#define RTUSB_RX_DONE_INT0 (0x1 << 16) ++#define RTUSB_TX_DONE_INT3 (0x1 << 3) ++#define RTUSB_TX_DONE_INT2 (0x1 << 2) ++#define RTUSB_TX_DONE_INT1 (0x1 << 1) ++#define RTUSB_TX_DONE_INT0 (0x1 << 0) ++ ++#define RTUSB_INT_MASK (USB_BASE + 0x1228) ++#define RTUSB_RX_DONE_INT_MSK1 (0x1 << 17) ++#define RTUSB_RX_DONE_INT_MSK0 (0x1 << 16) ++#define RTUSB_TX_DONE_INT_MSK3 (0x1 << 3) ++#define RTUSB_TX_DONE_INT_MSK2 (0x1 << 2) ++#define RTUSB_TX_DONE_INT_MSK1 (0x1 << 1) ++#define RTUSB_TX_DONE_INT_MSK0 (0x1 << 0) ++ ++ ++/*========================================= ++ PDMA RX Descriptor Format define ++=========================================*/ ++//------------------------------------------------- ++typedef struct _PDMA_RXD_INFO1_ PDMA_RXD_INFO1_T; ++ ++struct _PDMA_RXD_INFO1_ { ++ unsigned int PDP0; ++}; ++//------------------------------------------------- ++typedef struct _PDMA_RXD_INFO2_ PDMA_RXD_INFO2_T; ++ ++struct _PDMA_RXD_INFO2_ { ++ unsigned int PLEN1 : 14; ++ unsigned int LS1 : 1; ++ unsigned int UN_USED : 1; ++ unsigned int PLEN0 : 14; ++ unsigned int LS0 : 1; ++ unsigned int DDONE_bit : 1; ++}; ++//------------------------------------------------- ++typedef struct _PDMA_RXD_INFO3_ PDMA_RXD_INFO3_T; ++ ++struct _PDMA_RXD_INFO3_ { ++ unsigned int PDP1; ++}; ++//------------------------------------------------- ++typedef struct _PDMA_RXD_INFO4_ PDMA_RXD_INFO4_T; ++ ++struct _PDMA_RXD_INFO4_ { ++ unsigned int Rx_bcnt:16; ++ unsigned int Reserved1:8; ++ unsigned int Out_ep_addr:4; ++ unsigned int Reserved0:4; ++}; ++struct PDMA_rxdesc { ++ PDMA_RXD_INFO1_T rxd_info1; ++ PDMA_RXD_INFO2_T rxd_info2; ++ PDMA_RXD_INFO3_T rxd_info3; ++ PDMA_RXD_INFO4_T rxd_info4; ++}; ++/*========================================= ++ PDMA TX Descriptor Format define ++=========================================*/ ++//------------------------------------------------- ++typedef struct _PDMA_TXD_INFO1_ PDMA_TXD_INFO1_T; ++ ++struct _PDMA_TXD_INFO1_ { ++ unsigned int SDP0; ++}; ++//------------------------------------------------- ++typedef struct _PDMA_TXD_INFO2_ PDMA_TXD_INFO2_T; ++ ++struct _PDMA_TXD_INFO2_ { ++ unsigned int SDL1 : 14; ++ unsigned int LS1_bit : 1; ++ unsigned int BURST_bit : 1; ++ unsigned int SDL0 : 14; ++ unsigned int LS0_bit : 1; ++ unsigned int DDONE_bit : 1; ++}; ++//------------------------------------------------- ++typedef struct _PDMA_TXD_INFO3_ PDMA_TXD_INFO3_T; ++ ++struct _PDMA_TXD_INFO3_ { ++ unsigned int SDP1; ++}; ++//------------------------------------------------- ++typedef struct _PDMA_TXD_INFO4_ PDMA_TXD_INFO4_T; ++struct _PDMA_TXD_INFO4_ { ++ unsigned int reserved2:17; ++ unsigned int zlp_flag:1; ++ unsigned int reserved1:6; ++ unsigned int In_ep_addr:4; ++ unsigned int rsv:4; ++}; ++ ++struct PDMA_txdesc { ++ PDMA_TXD_INFO1_T txd_info1; ++ PDMA_TXD_INFO2_T txd_info2; ++ PDMA_TXD_INFO3_T txd_info3; ++ PDMA_TXD_INFO4_T txd_info4; ++}; ++ ++ ++#ifdef DEBUG ++#define DBG do{ if(debuglevel) printk("%s()\n", __FUNCTION__); }while(0); ++#define DD do{ printk("%s: %s %d\n", driver_name, __FUNCTION__, __LINE__); } while(0); ++#define xprintk(fmt, args...) do{ if(debuglevel) printk(fmt, ## args); } while(0); ++#else ++#define DBG ++#define DD ++#define xprintk(fmt, args...) ++#endif ++ ++#define FATAL_ERROR(fmt, args...) do{ printk(fmt, ## args); printk("\n############### ERROR #####################\n %s %d\n############### ERROR #####################\n", __FUNCTION__, __LINE__); BUG(); } while(0) ++ ++static void inline dump_usbirq(u32 irqreg) ++{ ++ if(irqreg) ++ xprintk("U%s%s%s%s%s%s\n", ++ (irqreg & USB_INTR_SOF) ? "sof" : "", ++ (irqreg & USB_INTR_RESET) ? " rst" : "", ++ (irqreg & USB_INTR_SUSPEND) ? " sus" : "", ++ (irqreg & USB_INTR_SETUP_TOKEN) ? "st" : "", ++ (irqreg & USB_INTR_SETUP_TOKEN_VALID) ? "sv" : "", ++ (irqreg & USB_INTR_HSPEED) ? " HS" : ""); ++ ++// if(irqreg & USB_INTR_SETUP_TOKEN) ++// printk("ST\n"); ++// if(irqreg & USB_INTR_SETUP_TOKEN_VALID) ++// printk("SV\n"); ++ ++} ++ ++static void inline dump_epirq(u32 irqreg, u32 ienreg, int dir) ++{ ++ if(irqreg) ++ xprintk("%s%x\n", dir? "I" : "O", irqreg); ++} ++ ++static __inline__ u32 usb_read(u32 addr) ++{ ++ return ioread32( (void __iomem *)(USB_BASE + (addr << 2)) ); ++} ++ ++static __inline__ void usb_write(u32 addr, u32 value) ++{ ++ iowrite32(value, (void __iomem *)(USB_BASE + (addr << 2)) ); ++} ++ ++static __inline__ void reg_write(u32 addr, u32 value) ++{ ++ iowrite32(value, (void __iomem *)0x0 + addr); ++} ++ ++static __inline__ u32 reg_read(u32 addr) ++{ ++ return ioread32( (void __iomem *)0x0 + addr); ++} ++ ++ ++static void handle_pending_epoutirq(struct rt_udc_struct *rt_usb, struct rt_ep_struct *rt_ep, struct rt_request *req); ++ ++/* Debug macros */ ++#ifdef DEBUG ++#define DEBUG_REQ ++#define DEBUG_TRX ++#define DEBUG_INIT ++#define DEBUG_EP0 ++#define DEBUG_EPX ++#define DEBUG_ERR ++ ++#ifdef DEBUG_REQ ++ #define D_REQ(dev, args...) printk(args) ++#else ++ #define D_REQ(dev, args...) do {} while (0) ++#endif /* DEBUG_REQ */ ++ ++#ifdef DEBUG_TRX ++ #define D_TRX(dev, args...) printk(args) ++#else ++ #define D_TRX(dev, args...) do {} while (0) ++#endif /* DEBUG_TRX */ ++ ++#ifdef DEBUG_INIT ++ #define D_INI(dev, args...) printk(args) ++#else ++ #define D_INI(dev, args...) do {} while (0) ++#endif /* DEBUG_INIT */ ++ ++#ifdef DEBUG_EP0 ++ static const char *state_name[] = { ++ "IDLE", ++ "IN", ++ "OUT", ++ "NODATA", ++ "STALL" ++ }; ++ #define D_EP0(dev, args...) printk(args) ++#else ++ #define D_EP0(dev, args...) do {} while (0) ++#endif /* DEBUG_EP0 */ ++ ++#ifdef DEBUG_EPX ++ #define D_EPX(dev, args...) printk(args) ++#else ++ #define D_EPX(dev, args...) do {} while (0) ++#endif /* DEBUG_EP0 */ ++ ++#ifdef DEBUG_ERR ++ #define D_ERR(dev, args...) printk(args) ++#else ++ #define D_ERR(dev, args...) do {} while (0) ++#endif ++ ++#else ++ #define D_REQ(dev, args...) do {} while (0) ++ #define D_TRX(dev, args...) do {} while (0) ++ #define D_INI(dev, args...) do {} while (0) ++ #define D_EP0(dev, args...) do {} while (0) ++ #define D_EPX(dev, args...) do {} while (0) ++ #define dump_ep_intr(x, y, z, i) do {} while (0) ++ #define dump_intr(x, y, z) do {} while (0) ++ #define dump_ep_stat(x, y) do {} while (0) ++ #define dump_usb_stat(x, y) do {} while (0) ++ #define dump_req(x, y, z) do {} while (0) ++ #define D_ERR(dev, args...) do {} while (0) ++#endif /* DEBUG */ ++ ++#endif /* __LINUX_USB_GADGET_RT_UDC_H */ +diff --git a/drivers/usb/gadget/rt_udc_pdma.c b/drivers/usb/gadget/rt_udc_pdma.c +new file mode 100644 +index 0000000..d5b89a2 +--- /dev/null ++++ b/drivers/usb/gadget/rt_udc_pdma.c +@@ -0,0 +1,2547 @@ ++/* ++ * driver/usb/gadget/rt_udc.c ++ * ++ * Copyright (C) 2009 Ying Yuan Huang, Ralink Tech. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ */ ++ ++/* ++ * 1) [ USB composite device ]. The USB PDMA architecture is not suitable for USB composite ++ * device support. A passive gadget driver(device) may slow down or block other gadget ++ * (device) because they are in the same ring. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ++#include ++#else ++#include ++#endif ++ ++static const char driver_name[] = "rt_udc"; ++static const char ep0name[] = "ep0"; ++static unsigned debuglevel = 0; ++module_param (debuglevel, uint, S_IRUGO); ++ ++#define DEBUG ++#include "rt_udc.h" ++ ++#define PROC_DIR driver_name ++#define DEBUGLEVEL_PROCFILE "debuglevel" ++static struct proc_dir_entry *pProcDir = NULL; ++static struct proc_dir_entry *pProcDebugLevel = NULL; ++ ++/* ++ * USB PDMA related ++ */ ++#define NUM_RX_DESC 256 ++#define NUM_TX_DESC 256 ++#define RX_BUFF_SZ 1600 /* 1536 */ ++#define RING_RESET_TIMEOUT 3000 /* 3 secs */ ++#define RX_RESCHEDULE 64 ++#define TX_RESCHEDULE 4 ++static unsigned dma = 0; ++module_param (dma, uint, S_IRUGO); ++static unsigned sm = 0; ++module_param (sm, uint, S_IRUGO); ++static unsigned int TXMAXCAP = 512; ++module_param (TXMAXCAP, uint, S_IRUGO); ++ ++static struct PDMA_txdesc *tx_ring0_cache = NULL; ++static struct PDMA_rxdesc *rx_ring0_cache = NULL; ++static volatile struct PDMA_rxdesc *rx_ring0_noncache = NULL; ++static volatile struct PDMA_txdesc *tx_ring0_noncache = NULL; ++static dma_addr_t tx_ring_bus_addr; ++static dma_addr_t rx_ring_bus_addr; ++ ++static int rx_dma_owner_idx0; /* Point to the next RXD DMA wants to use in RXD Ring#0. */ ++static int tx_cpu_owner_idx0; ++static int tx_need_free_idx0; ++ ++static volatile unsigned char *USBRxPackets[NUM_RX_DESC]; /* Receive packets */ ++static unsigned char tx_zlp_dummy_buf[8]; ++struct tasklet_struct rx_dma_tasklet; ++struct tasklet_struct tx_dma_tasklet; ++ ++static struct rt_udc_struct controller; ++static struct rt_request *handle_outep(struct rt_ep_struct *rt_ep); ++ ++static int debuglevel_read(char *page, char **start, off_t off,int count, int *eof, void *data) ++{ ++ int len; ++ sprintf(page, "%d\n", debuglevel); ++ len = strlen(page) + 1; ++ *eof = 1; ++ return len; ++} ++ ++static int debuglevel_write(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ char tmp[32]; ++ count = (count > 32) ? 32 : count; ++ memset(tmp, 0, 32); ++ if (copy_from_user(tmp, buffer, count)) ++ return -EFAULT; ++ debuglevel = simple_strtol(tmp, 0, 10); ++ return count; ++} ++ ++static void ep0_chg_stat(const char *label, struct rt_udc_struct *rt_usb, enum ep0_state stat) ++{ ++ xprintk("<0st>%s->%s\n", state_name[rt_usb->ep0state], state_name[stat]); ++ ++ if (rt_usb->ep0state == stat) ++ return; ++ rt_usb->ep0state = stat; ++} ++ ++static u8 read_epcs(struct rt_ep_struct *rt_ep) ++{ ++ int idx = EP_NO(rt_ep); ++ int dir = EP_DIR(rt_ep); ++ ++ if(idx == 0) ++ return usb_read(EP0CS); ++ ++ return (dir == EP_IN ? usb_read(0x7 + idx*8) : usb_read(0x3 + idx*8) ); ++} ++ ++static void write_epcs(struct rt_ep_struct *rt_ep, u8 val) ++{ ++ int idx = EP_NO(rt_ep); ++ int dir = EP_DIR(rt_ep); ++ ++ if(idx == 0) ++ usb_write(EP0CS, val); ++ else ++ (dir == EP_IN ? /*IN */ usb_write(0x7 + idx*8, val) : usb_write(0x3 + idx*8, val) ); ++} ++ ++static u16 read_inbc(int epnum) ++{ ++ u16 low, high = 0; ++ if(epnum == 0){ /* EP0 */ ++ low = usb_read(IN0BC); ++ }else{ ++ low = usb_read(epnum * 8 + 4); ++ high = usb_read((epnum * 8 + 4)+1); ++ } ++ return (low | (high << 8)); ++} ++ ++static u16 read_outbc(int epnum) ++{ ++ u16 low, high = 0; ++ if(epnum == 0){ /* EP0 */ ++ low = usb_read(OUT0BC); ++ }else{ ++ low = usb_read(epnum * 8); ++ high = usb_read((epnum * 8)+1); ++ } ++ return (low | (high << 8)); ++} ++ ++ ++static void rt_all_eps_reset(void) ++{ ++ // reset(toggle & fifo) all 16 IN & 16 OUT endpoints ++ usb_write(ENDPRST, 0x10); ++ usb_write(ENDPRST, 0x70); ++ usb_write(ENDPRST, 0x00); ++ usb_write(ENDPRST, 0x60); ++} ++ ++static void rt_ep_rst(struct rt_ep_struct *rt_ep) ++{ ++ u8 reg = 0; ++ u8 idx = EP_NO(rt_ep); ++ u8 dir = EP_DIR(rt_ep); ++ if(dir == EP_IN ) ++ reg |= ENDPRST_IO | idx; ++ usb_write(ENDPRST, reg); ++ ++ reg |= ENDPRST_TOGRST | ENDPRST_FIFORST; ++ usb_write(ENDPRST, reg); ++} ++ ++static void rt_ep_irq_enable(struct rt_ep_struct *rt_ep) ++{ ++ u8 reg; ++ u8 idx = EP_NO(rt_ep); ++ u8 dir = EP_DIR(rt_ep); ++ ++ if(idx == 0 /* ep0 */){ ++ usb_write(IN07IEN, (usb_read(IN07IEN) | 0x1) ); ++ usb_write(OUT07IEN, (usb_read(OUT07IEN) | 0x1) ); ++ }else{ /* epX */ ++ reg = usb_read(dir ? IN07IEN : OUT07IEN); ++ reg = reg | (0x1 << idx); ++ usb_write(dir == EP_IN ? IN07IEN : OUT07IEN, reg); ++ reg = usb_read(dir ? IN07IEN : OUT07IEN); ++ } ++} ++ ++static void rt_udc_init_ep(struct rt_udc_struct *rt_usb) ++{ ++ DBG; ++ if(dma){ ++#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) ++ usb_write(IN1CON, 0x8D); // InEP1 : Int, 2 subfifos ++ usb_write(IN2CON, 0x89); // InEP2 : Bulk, 2 subfifos ++ usb_write(OUT1CON, 0x8D); // OutEP1 : Int, 2 subfifos ++ usb_write(OUT2CON, 0x89); // OutEP2 : Bulk, 2 subfifos ++ //usb_write(OUT3CON, 0x89); // OutEP3 : Bulk, 2 subfifos ++ //usb_write(OUT4CON, 0x89); // OutEP4 : Bulk, 2 subfifos ++#elif defined (CONFIG_RALINK_RT5350) ++ usb_write(IN1CON, 0x89); // InEP1 : BULK, 2 subfifos ++ usb_write(OUT1CON, 0x89); // OutEP1 : BULK, 2 subfifos ++#else ++#error "define a platform" ++#endif ++ }else{ ++#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) ++ usb_write(IN1CON, 0x8C); // InEP1 : Int , 1 subfifos ++ usb_write(IN2CON, 0x88); // InEP2 : Bulk, 1 subfifo ++ usb_write(OUT1CON, 0x8C); // OutEP1 : Int, 1 subfifos ++ usb_write(OUT2CON, 0x88); // OutEP2 : Bulk, 1 subfifos ++ //usb_write(OUT3CON, 0x88); // OutEP3 : Bulk, 1 subfifo ++ //usb_write(OUT4CON, 0x88); // OutEP4 : Bulk. 1 subfifo ++ ++#elif defined (CONFIG_RALINK_RT5350) ++ usb_write(IN1CON, 0x88); // InEP1 : BULK , 1 subfifos ++ usb_write(OUT1CON, 0x88); // OutEP1 : BULK, 1 subfifos ++#else ++#error "define a platform" ++#endif ++ } ++ // clear all pending HW interrupts ++ usb_write(IN07IRQ, 0xFF); ++ usb_write(OUT07IRQ, 0xFF); ++ rt_all_eps_reset(); ++ rt_ep_irq_enable(&rt_usb->rt_ep[0]); ++} ++ ++static void rt_udc_init_fifo(struct rt_udc_struct *rt_usb) ++{ ++ // fifo control ++ if(dma){ ++ usb_write(FIFOCTRL, 0x31); // INEP1, Autoin = 1 ++ usb_write(FIFOCTRL, 0x32); // INEP2, Autoin = 1 ++ usb_write(FIFOCTRL, 0x21); // OUTEP1, Autoin = 1 ++ usb_write(FIFOCTRL, 0x22); // OUTEP2, Autoin = 1 ++ //usb_write(FIFOCTRL, 0x23);// OUTEP3, Autoin = 1 ++ //usb_write(FIFOCTRL, 0x24);// OUTEP4, Autoin = 1 ++ ++ usb_write(FIFOCTRL, 0x00); // Access by DMA ++ }else{ ++ usb_write(FIFOCTRL, 0x11); // INEP1, Autoin = 0 ++ usb_write(FIFOCTRL, 0x12); // INEP2, Autoin = 0 ++ usb_write(FIFOCTRL, 0x01); // OUTEP1, Autoin = 0 ++ usb_write(FIFOCTRL, 0x02); // OUTEP2, Autoin = 0 ++ //usb_write(FIFOCTRL, 0x03);// OUTEP3, Autoin = 0 ++ //usb_write(FIFOCTRL, 0x04);// OUTEP4, Autoin = 0 ++ ++ usb_write(FIFOCTRL, 0x80); // Access by CPU ++ } ++} ++ ++static void rt_udc_init(struct rt_udc_struct *rt_usb) ++{ ++ /* Setup & Init endpoints */ ++ rt_udc_init_ep(rt_usb); ++ ++ // Enable HS, reset, suspend, SETUP valid data interrupt ++ usb_write(USBIRQ, 0xff); // clear first ++ usb_write(USBIEN, 0x21); ++ ++ /* Setup ep fifos */ ++ rt_udc_init_fifo(rt_usb); ++} ++ ++static void rt_ep_irq_disable(struct rt_ep_struct *rt_ep) ++{ ++ u8 reg; ++ u8 idx = EP_NO(rt_ep); ++ u8 dir = EP_DIR(rt_ep); ++ ++ if(idx == 0 /* ep0 */){ ++ usb_write(IN07IEN, (usb_read(IN07IEN) & ~(0x1)) ); ++ usb_write(OUT07IEN, (usb_read(OUT07IEN) & ~(0x1)) ); ++ }else{ ++ reg = usb_read(dir ? IN07IEN : OUT07IEN); ++ reg = reg & ~(0x1 << idx); ++ usb_write(dir == EP_IN ? IN07IEN : OUT07IEN, reg); ++ reg = usb_read(dir ? IN07IEN : OUT07IEN); ++ } ++} ++ ++static u32 rt_fifo_bcount(struct rt_ep_struct *rt_ep) ++{ ++ u8 low, high; ++ u32 rc; ++ ++ int idx = EP_NO(rt_ep); ++ int dir = EP_DIR(rt_ep); ++ ++ if(idx == 0) ++ return 0; ++ ++ if(dir /* IN */){ ++ low = usb_read(0x004 + idx*8); ++ high = usb_read( (0x004 + idx*8)+1 ); ++ }else{ /* OUT */ ++ low = usb_read(0x000 + idx*8); ++ high = usb_read( (0x000 + idx*8)+1 ); ++ } ++ rc = high | low; ++ return rc; ++} ++ ++void rt_flush(struct rt_ep_struct *rt_ep) ++{ ++ rt_ep_rst(rt_ep); ++} ++ ++void rt_ep_stall(struct rt_ep_struct *rt_ep, int value) ++{ ++ u8 tmp; ++ u32 addr; ++ int idx = EP_NO(rt_ep); ++ int dir = EP_DIR(rt_ep); ++ ++ if(idx == 0){ ++ tmp = usb_read(EP0CS); ++ if(value) ++ tmp |= 0x1; ++ else ++ tmp &= ~(0x1); ++ usb_write(EP0CS, tmp); ++ }else{ ++ addr = (dir == EP_IN ? 0x006 : 0x002) + idx * 8; ++ tmp = usb_read(addr); ++ if(value) ++ tmp |= 0x40; ++ else ++ tmp &= ~(0x40); ++ usb_write(addr, tmp); ++ } ++ ++ return; ++} ++ ++static int rt_udc_get_frame(struct usb_gadget *_gadget) ++{ ++ return 0; ++} ++ ++static int rt_udc_wakeup(struct usb_gadget *_gadget) ++{ ++ DBG; ++ return 0; ++} ++ ++ ++/******************************************************************************* ++ * USB request control functions ++ ******************************************************************************* ++ */ ++static inline void ep_add_request(struct rt_ep_struct *rt_ep, struct rt_request *req) ++{ ++ if (unlikely(!req)) ++ return; ++ req->in_use = 1; ++ req->zlp_dma_done = 0; ++ req->rt_ep = rt_ep; ++ list_add_tail(&req->queue, &rt_ep->queue); ++} ++ ++static inline void ep_del_request(struct rt_ep_struct *rt_ep, struct rt_request *req) ++{ ++ if (unlikely(!req)) ++ return; ++ list_del_init(&req->queue); ++ req->zlp_dma_done = 0; ++ req->in_use = 0; ++} ++ ++static void done(struct rt_ep_struct *rt_ep, struct rt_request *req, int status) ++{ ++ ep_del_request(rt_ep, req); ++ ++ if (likely(req->req.status == -EINPROGRESS)) ++ req->req.status = status; ++ else ++ status = req->req.status; ++ ++ if (status && status != -ESHUTDOWN) ++ D_ERR(rt_ep->rt_usb->dev, "<%s> complete %s req %p stat %d len %u/%u\n", __func__, rt_ep->ep.name, &req->req, status,req->req.actual, req->req.length); ++ ++ req->req.complete(&rt_ep->ep, &req->req); ++} ++ ++#if 0 ++/* for reference */ ++struct tasklet_struct rx_tasklet_tmp; ++static void rx_done_do_tasklet(unsigned long arg) ++{ ++ struct rt_ep_struct *rt_ep; ++ struct rt_request *rt_req; ++ struct usb_request *usb_req; ++ struct usb_ep *ep; ++ int i, rx_count, status = 0; ++ struct rt_udc_struct *rt_usb = &controller; ++ ++ for (i = (IN_EP_NUM+1); i < RT_USB_NB_EP; i++) { ++ rt_ep = &rt_usb->rt_ep[i]; ++ ep = &rt_ep->ep; ++ ++ // shared by irq handler, protect it ++ spin_lock_irqsave(&rx_done_lock, rx_done_lock_flags); ++ rx_count = rt_ep->rx_done_count; ++ ++ //spin_unlock_irqrestore(&rx_done_lock, rx_done_lock_flags); ++ ++ for (;rx_count > 0; rx_count--) { ++ if(unlikely(list_empty(&rt_ep->queue))) ++ FATAL_ERROR("empty queue"); ++ ++ rt_req = list_entry(rt_ep->queue.next, struct rt_request, queue); ++ usb_req = &rt_req->req; ++ ++ ep_del_request(rt_ep, rt_req); ++ rt_ep->rx_done_count--; ++ ++ spin_unlock_irqrestore(&rx_done_lock, rx_done_lock_flags); ++ ++ if (unlikely(rt_req->req.status == -EINPROGRESS)) ++ rt_req->req.status = status; ++ else ++ status = rt_req->req.status; ++ ++ if (unlikely(status && status != -ESHUTDOWN)) ++ D_ERR(rt_ep->rt_usb->dev, "<%s> complete %s req %p stat %d len %u/%u\n", __func__, rt_ep->ep.name, &rt_req->req, status,rt_req->req.actual, rt_req->req.length); ++ ++ // indicate gadget driver. ++ usb_req->complete(ep, usb_req); ++ ++ spin_lock_irqsave(&rx_done_lock, rx_done_lock_flags); ++ } ++ spin_unlock_irqrestore(&rx_done_lock, rx_done_lock_flags); ++ } ++} ++#endif ++ ++struct tasklet_struct tx_tasklet; ++static void tx_do_tasklet(unsigned long arg) ++{ ++ return; ++} ++ ++struct tasklet_struct rx_tasklet; ++static void rx_do_tasklet(unsigned long arg) ++{ ++ struct rt_ep_struct *rt_ep; ++ struct rt_request *req; ++ struct usb_ep *ep; ++ int i; ++ struct rt_udc_struct *rt_usb = &controller; ++ ++ for (i = (IN_EP_NUM+1/* EP0 */); i < RT_USB_NB_EP; i++){ ++ u8 epcs; ++ rt_ep = &rt_usb->rt_ep[i]; ++ ep = &rt_ep->ep; ++ ++ epcs = read_epcs(rt_ep); ++ while(!(epcs & EP_CS_BSY)){ ++ req = handle_outep(rt_ep); ++ if(!req){ ++ // No usb request found. ++ // Just set up the flag (pending) and clear int. ++ rt_ep->pending = 1; ++ break; ++ }else{ ++ if(req && ( (req->req.actual % rt_ep->ep.maxpacket) || (req->req.actual >= req->req.length))){ ++ xprintk("q.l=%d,q.a=%d\n", req->req.length, req->req.actual); ++ done(rt_ep, req, 0); ++ } ++ } ++ ++ epcs = read_epcs(rt_ep); ++ write_epcs(rt_ep, 0x0); ++ epcs = read_epcs(rt_ep); ++ } ++ } ++} ++ ++static void nuke(struct rt_ep_struct *rt_ep, int status) ++{ ++ struct rt_request *req; ++ ++ DBG; ++ while (!list_empty(&rt_ep->queue)) { ++ req = list_entry(rt_ep->queue.next, struct rt_request, queue); ++ done(rt_ep, req, status); ++ } ++} ++ ++/* ++ ******************************************************************************* ++ * Data tansfer over USB functions ++ ******************************************************************************* ++ */ ++static int read_ep0_fifo(struct rt_ep_struct *rt_ep, struct rt_request *req) ++{ ++ u8 *buf; ++ int byte_count, req_bufferspace, count, i; ++ ++DBG; ++ if(!in_irq()) ++ FATAL_ERROR("not irq context."); ++ ++ byte_count = read_outbc(EP_NO(rt_ep)); ++ req_bufferspace = req->req.length - req->req.actual; ++ ++ buf = req->req.buf + req->req.actual; ++ ++ if(!req_bufferspace) ++ FATAL_ERROR("zlp"); ++ ++ if(byte_count > req_bufferspace) ++ FATAL_ERROR("buffer overflow, byte_count=%d, req->req.length=%d, req->req.actual=%d\n", byte_count, req->req.length ,req->req.actual); ++ ++ count = min(byte_count, req_bufferspace); ++ ++ //test, Access by CPU ++ if(dma) ++ usb_write(FIFOCTRL, 0x80); ++ ++ for (i = 0; i < count; i++){ ++ *buf = usb_read(EP0OUTDAT+i); ++ buf++; ++ } ++ req->req.actual += count; ++ ++ //test, Access by DMA ++ if(dma) ++ usb_write(FIFOCTRL, 0x00); ++ ++ return count; ++} ++ ++ ++static int read_ep_fifo(struct rt_ep_struct *rt_ep, struct rt_request *req) ++{ ++ u8 *buf, ep_no, ep_no_shift; ++ int byte_count, req_bufferspace, count, i; ++ ++DBG; ++ ep_no = EP_NO(rt_ep); ++ ++ byte_count = read_outbc(ep_no); ++ if(unlikely(!byte_count)) ++ FATAL_ERROR("ep_no:%d bc = 0", ep_no); ++ ++ req_bufferspace = req->req.length - req->req.actual; ++ ++ buf = req->req.buf + req->req.actual; ++ ++ if(unlikely(!req_bufferspace)) ++ FATAL_ERROR("zlp"); ++ ++ xprintk("bc=%d,r.l=%d,r.a=%d\n", byte_count, req->req.length ,req->req.actual); ++ if(unlikely(byte_count > req_bufferspace)) ++ FATAL_ERROR("buffer overflow, byte_count=%d, req->req.length=%d, req->req.actual=%d\n", byte_count, req->req.length ,req->req.actual); ++ ++ count = min(byte_count, req_bufferspace); ++ ++ ep_no_shift = 0x80+ep_no * 4; ++ for (i = 0; i < count; i++){ ++ *buf = usb_read(ep_no_shift); ++ buf++; ++ } ++ ++ req->req.actual += count; ++ ++ // EP Out irq handler would arm another transaction. ++ return count; ++} ++ ++static int write_ep_fifo_zlp(struct rt_ep_struct *rt_ep) ++{ ++ u8 epcs; ++ int ep_no = EP_NO(rt_ep); ++ ++DBG; ++ xprintk("w%d ZLP\n", EP_NO(rt_ep)); ++ epcs = read_epcs(rt_ep); ++ if(epcs & EP_CS_BSY) ++ FATAL_ERROR("EP%d busy. cs=%x\n", ep_no, epcs); ++ ++ /* check INEP byte count is zero? */ ++ if(read_inbc(ep_no)) ++ FATAL_ERROR("EP%d bc zero. bc=%d\n", ep_no, read_inbc(ep_no)); ++ ++ epcs = read_epcs(rt_ep); ++ write_epcs(rt_ep, epcs); ++ return 0; ++} ++ ++ ++/* ++ * handle_epinirq() ++*/ ++static int write_ep_fifo(struct rt_ep_struct *rt_ep, struct rt_request *req) ++{ ++ u8 *buf, epcs; ++ int length, i, ep_no = EP_NO(rt_ep); ++ ++DBG; ++ xprintk("w ep%d req=%p,r.l=%d,r.a=%d\n",EP_NO(rt_ep),&req->req,req->req.length,req->req.actual); ++ epcs = read_epcs(rt_ep); ++ if(epcs & EP_CS_BSY) ++ FATAL_ERROR("EP%d busy. epcs=%x\n", ep_no, epcs); ++ ++ /* check INEP byte count is zero? */ ++ if(read_inbc(ep_no)) ++ FATAL_ERROR("EP%d bc=%d\n", ep_no, read_inbc(ep_no)); ++ ++ buf = req->req.buf + req->req.actual; ++ length = (req->req.length - req->req.actual) < rt_ep->ep.maxpacket ? (req->req.length - req->req.actual) : rt_ep->ep.maxpacket; ++ req->req.actual += length; ++ if (!length) { /* zlp */ ++ // for debug ++ xprintk("<%s> zero packet\n", __func__); ++ write_ep_fifo_zlp(rt_ep); ++ return 0; ++ } ++ ++ // write to ep in fifo ++ for (i=0; i< length; i++) ++ usb_write(0x80+ep_no*4, *buf++); ++ ++ epcs = read_epcs(rt_ep); ++ write_epcs(rt_ep, epcs); ++ ++ return length; ++} ++ ++/* ++ * ++ */ ++static int write_ep0_fifo(struct rt_ep_struct *rt_ep, struct rt_request *req) ++{ ++ u8 *buf; ++ int length, i; ++ u32 maxpacket; ++ ++DBG; ++ xprintk("q.l=%d, q.a=%d, maxp=%d\n", req->req.length, req->req.actual, rt_ep->ep.maxpacket); ++ ++ buf = req->req.buf + req->req.actual; ++ maxpacket = (u32)(rt_ep->ep.maxpacket); ++ length = min(req->req.length - req->req.actual, maxpacket); ++ ++ req->req.actual += length; ++ ++ if (!length && req->req.zero) ++ FATAL_ERROR("zlp"); ++ ++ if(!in_irq()) ++ FATAL_ERROR("Not in irq context"); ++ ++ //test, Access by CPU ++ if(dma) ++ usb_write(FIFOCTRL, 0x80); ++ ++ //write to ep0in fifo ++ for (i=0; i< length; i++) ++ usb_write(EP0INDAT+i, *buf++); ++ ++ // arm ep0in ++ usb_write(IN0BC, length); ++ if(length != rt_ep->ep.maxpacket) ++ usb_write(EP0CS, 0x2); // clear NAK bit to ACK host. ++ ++ //test, Access by CPU ++ if(dma) ++ usb_write(FIFOCTRL, 0x00); ++ ++ return length; ++} ++ ++static struct rt_request *get_unhandled_req(struct rt_ep_struct *rt_ep) ++{ ++ struct list_head *p; ++ struct rt_request *req = NULL; ++ ++ if(EP_DIR(rt_ep) == EP_OUT) ++ FATAL_ERROR("Out EP"); ++ ++ if(dma){ ++ list_for_each(p, &rt_ep->queue){ ++ req = list_entry(p, struct rt_request, queue); ++ if(req->req.length > req->req.actual ) ++ return req; ++ else if(unlikely(req->req.length == 0 && req->zlp_dma_done == 0)) ++ return req; ++ else ++ continue; ++ } ++ return NULL; ++ }else{ ++ if (!list_empty(&rt_ep->queue)){ ++ req = list_entry(rt_ep->queue.next, struct rt_request, queue); ++ }else { ++ FATAL_ERROR("%s No request", rt_ep->ep.name); ++ } ++ return req; ++ } ++} ++ ++#define PADDING_LENGTH 64 ++static int write_dma_txring(struct rt_ep_struct *rt_ep,struct rt_request *req) ++{ ++ u8 *buf; ++ int length; ++ int retry_times = 0; ++ u32 hw_current_idx; ++DBG; ++ xprintk("w%dr=%p,r.l=%d,r.a=%d\n", EP_NO(rt_ep), &req->req,req->req.length,req->req.actual); ++ ++ length = req->req.length; ++ ++ while(length > 0 || (req->req.length == 0 && req->zlp_dma_done == 0)){ ++retry: ++ /* wait for a free TXD */ ++ hw_current_idx = reg_read(RTUSB_TX_DTX_IDX0); ++ if ( tx_ring0_cache[tx_cpu_owner_idx0].txd_info2.DDONE_bit == 0 || ++ ((tx_cpu_owner_idx0+1) % NUM_TX_DESC == hw_current_idx) ) { ++ if(retry_times > 1000) ++ return -1; ++ mdelay(1); ++ retry_times++; ++ goto retry; ++ } ++ ++ if(length > TXMAXCAP) ++ length = TXMAXCAP; ++ ++ buf = req->req.buf + req->req.actual; ++ req->req.actual += length; ++ ++ /* deal with ZLP.*/ ++ if(req->req.length == 0 && req->zlp_dma_done == 0) ++ req->zlp_dma_done = 1; ++ ++ req->txd_count++; ++ ++#define phys_to_bus(a) ((u32)a & 0x1FFFFFFF) ++ if(length){ ++ tx_ring0_cache[tx_cpu_owner_idx0].txd_info1.SDP0 = cpu_to_le32(phys_to_bus(buf)); ++ tx_ring0_cache[tx_cpu_owner_idx0].txd_info2.SDL0 = cpu_to_le32(length); ++ tx_ring0_cache[tx_cpu_owner_idx0].txd_info4.zlp_flag = 0; ++ dma_cache_sync(NULL, (void *)buf, length, DMA_TO_DEVICE); ++ }else{ ++ tx_ring0_cache[tx_cpu_owner_idx0].txd_info1.SDP0 = cpu_to_le32(phys_to_bus(tx_zlp_dummy_buf)); ++ tx_ring0_cache[tx_cpu_owner_idx0].txd_info2.SDL0 = cpu_to_le32(sizeof(tx_zlp_dummy_buf)); ++ tx_ring0_cache[tx_cpu_owner_idx0].txd_info4.zlp_flag = 1; ++ } ++ ++ tx_ring0_cache[tx_cpu_owner_idx0].txd_info4.In_ep_addr = cpu_to_le32(EP_NO(rt_ep)); ++ tx_ring0_cache[tx_cpu_owner_idx0].txd_info2.DDONE_bit = 0; ++ tx_cpu_owner_idx0 = (tx_cpu_owner_idx0 + 1) % NUM_TX_DESC; ++ ++ length = req->req.length - req->req.actual; ++ } ++ ++ reg_write(RTUSB_TX_CTX_IDX0, tx_cpu_owner_idx0); ++ ++ return 0; ++} ++ ++ ++/******************************************************************************* ++ * Endpoint handlers ++ ******************************************************************************* ++ */ ++ ++/* ++ * Handle In Endpoint ++ * CPU(FIFO): ++ * Enqueue -> Write fifo -> TX_DONE -> Write fifo -> TX_DONE -> .. ++ * ++ * DMA ++ * Enqueue -> Kick off TxD. Enqueue -> Kick off TxD. Enqueue -> Kick off TxD. ++ */ ++static int handle_inep(struct rt_ep_struct *rt_ep) ++{ ++ struct rt_request *req; ++ ++DBG; ++ if(!(req = get_unhandled_req(rt_ep))) ++ return -1; ++ ++ if(dma){ ++ write_dma_txring(rt_ep, req); ++ }else{ ++ write_ep_fifo(rt_ep, req); ++ rt_ep->tx_done_count = 1; ++ } ++ return 0; ++} ++ ++/* ++ * IRQ context. ++ */ ++static struct rt_request *handle_outep(struct rt_ep_struct *rt_ep) ++{ ++ struct rt_request *req; ++ struct list_head *p; ++ int count = 0; ++ ++DBG; ++ if (list_empty(&rt_ep->queue)){ ++ return NULL; ++ } ++ ++ list_for_each(p, &rt_ep->queue){ ++ if(count != rt_ep->rx_done_count){ ++ count++; ++ continue; ++ } ++ req = list_entry(p, struct rt_request, queue); ++ read_ep_fifo(rt_ep, req); ++ return req; ++ } ++ ++ return NULL; ++} ++ ++static struct rt_request *handle_inep0(struct rt_ep_struct *rt_ep) ++{ ++ struct rt_request *req = NULL; ++ ++DBG; ++ if (list_empty(&rt_ep->queue)) { ++ D_ERR(rt_ep->rt_usb->dev, "<%s> no request on %s\n", __func__, rt_ep->ep.name); ++ return NULL; ++ } ++ ++ req = list_entry(rt_ep->queue.next, struct rt_request, queue); ++ switch (rt_ep->rt_usb->ep0state) { ++ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR */ ++ write_ep0_fifo(rt_ep, req); ++ break; ++ ++ // Impossible: ++ //case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR */ ++ //case EP0_NO_DATA_PHASE: /* for no data stage control transfer */ ++ ++ default: ++ D_ERR(rt_ep->rt_usb->dev, "<%s> ep0 i/o, odd state %d\n", __func__, rt_ep->rt_usb->ep0state); ++ ep_del_request(rt_ep, req); ++ req = NULL; ++ break; ++ } ++ ++ return req; ++} ++ ++static struct rt_request *handle_outep0(struct rt_ep_struct *rt_ep) ++{ ++ struct rt_request *req = NULL; ++ ++DBG; ++ if (list_empty(&rt_ep->queue)) { ++ D_ERR(rt_ep->rt_usb->dev, "<%s> no request on %s\n", __func__, rt_ep->ep.name); ++ return NULL; ++ } ++ ++ if(rt_ep->rt_usb->ep0state != EP0_OUT_DATA_PHASE){ ++ D_EP0(rt_ep->rt_usb->dev, "<%s> ep0 i/o, odd state %d\n", __func__, rt_ep->rt_usb->ep0state); ++ ep_del_request(rt_ep, req); ++ req = NULL; ++ } ++ ++ req = list_entry(rt_ep->queue.next, struct rt_request, queue); ++ ++ read_ep0_fifo(rt_ep, req); ++ ++ return req; ++} ++ ++/******************************************************************************* ++ * USB gadget callback functions ++ ******************************************************************************* ++ */ ++static void handle_dma_rxdone(struct rt_udc_struct *rt_usb) ++{ ++ DBG; ++ tasklet_schedule(&rx_dma_tasklet); ++} ++ ++static void handle_dma_txdone(struct rt_udc_struct *rt_usb) ++{ ++ DBG; ++ tasklet_schedule(&tx_dma_tasklet); ++} ++ ++static void handle_dmairq(struct rt_udc_struct *rt_usb, u32 irq) ++{ ++ if(irq & RTUSB_RX_DONE_INT0){ ++ handle_dma_rxdone(rt_usb); ++ } ++ ++ if(irq & RTUSB_TX_DONE_INT0){ ++ handle_dma_txdone(rt_usb); ++ } ++ ++ reg_write(RTUSB_INT_STATUS, irq); ++} ++ ++static inline int udc_dma_reset_txring(void) ++{ ++ int count = 0; ++ u32 reg; ++ ++ while(count++< RING_RESET_TIMEOUT){ ++ reg = reg_read(RTUSB_PDMA_GLO_CFG); ++ if(reg & RTUSB_TX_DMA_BUSY){ ++ mdelay(1); ++ }else ++ break; ++ ++ } ++ if(count== RING_RESET_TIMEOUT) ++ return -1; ++ ++ reg = reg_read(RTUSB_PDMA_RST_IDX); ++ udelay(100); ++ reg |= (RTUSB_RST_DTX_IDX1 | RTUSB_RST_DTX_IDX0); ++ reg_write(RTUSB_PDMA_RST_IDX, reg); ++ udelay(100); ++ return 0; ++} ++ ++static inline int udc_dma_reset_rxring(void) ++{ ++ int count = 0; ++ u32 reg; ++ ++ while(count++< RING_RESET_TIMEOUT){ ++ reg = reg_read(RTUSB_PDMA_GLO_CFG); ++ if(reg & RTUSB_RX_DMA_BUSY){ ++ mdelay(1); ++ }else ++ break; ++ } ++ if(count== RING_RESET_TIMEOUT) ++ return -1; ++ ++ reg = reg_read(RTUSB_PDMA_RST_IDX); ++ udelay(100); ++ reg |= (RTUSB_RST_DRX_IDX1 | RTUSB_RST_DRX_IDX0); ++ reg_write(RTUSB_PDMA_RST_IDX, reg); ++ udelay(100);\ ++ return 0; ++} ++ ++static int udc_dma_hw_reset(void) ++{ ++ if(udc_dma_reset_rxring() == -1) ++ return -1; ++ if(udc_dma_reset_txring() == -1) ++ return -1; ++ return 0; ++} ++ ++static void udc_dma_enable(int enable) ++{ ++ u32 reg; ++ reg = reg_read(RTUSB_PDMA_GLO_CFG); ++ udelay(100); ++ if(enable) ++ reg |= RTUSB_TX_WB_DDONE | RTUSB_RX_DMA_EN | RTUSB_TX_DMA_EN ; ++ else ++ reg &= ~(RTUSB_TX_WB_DDONE | RTUSB_RX_DMA_EN | RTUSB_TX_DMA_EN) ; ++ reg_write(RTUSB_PDMA_GLO_CFG, reg); ++ udelay(500); ++} ++ ++static void udc_dma_int_enable(int enable) ++{ ++ u32 reg; ++ reg = reg_read(RTUSB_INT_MASK); ++ udelay(100); ++ if(enable) ++ reg |= RTUSB_RX_DONE_INT_MSK0 | RTUSB_TX_DONE_INT_MSK0 ; ++ else ++ reg &= ~(RTUSB_RX_DONE_INT_MSK0 | RTUSB_TX_DONE_INT_MSK0) ; ++ reg_write(RTUSB_INT_MASK, reg); ++ udelay(100); ++} ++ ++static inline void udc_dma_tx_int_clear(void) ++{ ++ reg_write(RTUSB_INT_STATUS, 0x0000000F); ++} ++ ++static inline void udc_dma_rx_int_clear(void) ++{ ++ reg_write(RTUSB_INT_STATUS, 0x00030000); ++} ++ ++static inline void udc_dma_all_int_clear(void) ++{ ++ reg_write(RTUSB_INT_STATUS, 0xFFFFFFFF); ++} ++ ++static int copy_data_to_ep(void *src, int length, int ep_num) ++{ ++ struct rt_ep_struct *rt_ep; ++ struct rt_udc_struct *rt_usb = &controller; ++ struct rt_request *req; ++ int req_bufferspace, count; ++ u8 *buf; ++ ++ DBG; ++ rt_ep = &rt_usb->rt_ep[ep_num+IN_EP_NUM]; ++ ++ if (list_empty(&rt_ep->queue)){ ++ /* It is safe to return 0 if no req queued. */ ++ return 0; ++ } ++ ++ req = list_entry(rt_ep->queue.next, struct rt_request, queue); ++ req_bufferspace = req->req.length - req->req.actual; ++ ++ if(unlikely(!req_bufferspace)){ ++ // for debug ++ FATAL_ERROR("zlp"); ++ return -1; ++ } ++ ++ if(length > req_bufferspace){ ++ FATAL_ERROR("buffer overflow"); ++ return -1; ++ } ++ ++ // sync with cache. ++ if(likely(length)) ++ dma_cache_sync(NULL, src, length, DMA_FROM_DEVICE); ++ ++ buf = req->req.buf + req->req.actual; ++ count = min(length, req_bufferspace); ++ memcpy(buf, src, count); ++ ++ req->req.actual += count; ++ ++ if((req->req.actual % rt_ep->ep.maxpacket) || (req->req.actual >= req->req.length)){ ++ done(rt_ep, req, 0); // short packet indicates transaction is done. ++ } ++ return count; ++} ++ ++static void rx_dma_done_do_tasklet(unsigned long arg) ++{ ++ u32 *rxd_info; ++ u32 length; ++ int ep, rc; ++ int processed_count=0; ++ ++ DBG; ++ for (;;){ ++ if (rx_ring0_cache[rx_dma_owner_idx0].rxd_info2.DDONE_bit == 0) ++ break; ++ ++ if(processed_count++ > RX_RESCHEDULE){ ++ tasklet_schedule(&rx_dma_tasklet); ++ break; ++ } ++ ++ length = rx_ring0_cache[rx_dma_owner_idx0].rxd_info4.Rx_bcnt; ++ ep = rx_ring0_cache[rx_dma_owner_idx0].rxd_info4.Out_ep_addr; ++ ++ // copy data from RXD->buffer to ep queue. ++ rc = copy_data_to_ep((void *)USBRxPackets[rx_dma_owner_idx0], length, ep); ++ if(rc <= 0) ++ return; ++ ++ rxd_info = (u32 *)&rx_ring0_cache[rx_dma_owner_idx0].rxd_info4; ++ *rxd_info = 0; ++ ++ /* clear DDONE bit*/ ++ rxd_info = (u32 *)&rx_ring0_cache[rx_dma_owner_idx0].rxd_info2; ++ *rxd_info = 0; ++ //rx_ring0_cache[rx_dma_owner_idx0].rxd_info2.DDONE_bit = 0; ++ //rx_ring0_cache[i].rxd_info2.LS0= 0; ++ rx_ring0_cache[rx_dma_owner_idx0].rxd_info2.PLEN0= sizeof(u8) * RX_BUFF_SZ; ++ ++ /* Move point to next RXD which wants to alloc */ ++ //OUTL(cpu_to_le32((u32) rx_dma_owner_idx0), RTUSB_RX_CALC_IDX0); ++ reg_write(RTUSB_RX_CALC_IDX0, rx_dma_owner_idx0); ++ ++ /* Update to Next packet point that was received. ++ */ ++ rx_dma_owner_idx0 = (rx_dma_owner_idx0 + 1) % NUM_RX_DESC; ++ } ++} ++ ++/* ++ * Recycle reqs and call gadget complete callback function. ++ */ ++static void tx_dma_done_do_tasklet(unsigned long arg) ++{ ++ int ep_num; ++ u32 hw_current; ++ struct rt_ep_struct *rt_ep; ++ struct rt_request *rt_req; ++ struct rt_udc_struct *rt_usb = &controller; ++ ++ DBG; ++ while(tx_need_free_idx0 != (hw_current = reg_read(RTUSB_TX_DTX_IDX0))){ ++ int retry = 0; ++ while(tx_ring0_cache[tx_need_free_idx0].txd_info2.DDONE_bit != 1){ ++ mdelay(1); ++ retry++; ++ if(retry > 1000) ++ FATAL_ERROR("tx timeout"); ++ } ++ ++ // rt_ep = tx_ring0_req_mapping[tx_need_free_idx0]; ++ ep_num = tx_ring0_cache[tx_need_free_idx0].txd_info4.In_ep_addr; ++ if(!ep_num || ep_num > IN_EP_NUM) ++ FATAL_ERROR("Out of range"); ++ ++ rt_ep = &rt_usb->rt_ep[ep_num]; ++ if(list_empty(&rt_ep->queue)) ++ FATAL_ERROR("ep[%d] No request", ep_num); ++ ++ rt_req = list_entry(rt_ep->queue.next, struct rt_request, queue); ++ rt_req->txd_count--; ++ ++ ++ if(rt_req->txd_count == 0) ++ done(rt_ep, rt_req, 0); ++ ++ tx_need_free_idx0 = (tx_need_free_idx0 + 1) % NUM_TX_DESC; ++ ++ } ++} ++ ++static int udc_dma_rst(void) ++{ ++ if( udc_dma_reset_txring() == -1) ++ return -1; ++ if( udc_dma_reset_rxring() == -1) ++ return -1; ++ ++ tx_cpu_owner_idx0 = 0; ++ tx_need_free_idx0 = 0; ++ rx_dma_owner_idx0 = 0; ++ reg_write(RTUSB_RX_CALC_IDX0, cpu_to_le32(NUM_RX_DESC - 1)); ++ return 0; ++} ++ ++static int rt_udc_dma_init(void) ++{ ++ int i; ++ ++ if( udc_dma_hw_reset() == -1) ++ return -1; ++ ++ for(i=0; i=0; i--) ++ kfree((void *)USBRxPackets[i]); ++ printk("No mem."); ++ return -1; ++ } ++ } ++ ++ tx_ring0_cache = dma_alloc_coherent(NULL, sizeof(struct PDMA_txdesc) * NUM_TX_DESC, &tx_ring_bus_addr, GFP_KERNEL); ++ rx_ring0_cache = dma_alloc_coherent(NULL, sizeof(struct PDMA_rxdesc) * NUM_RX_DESC, &rx_ring_bus_addr, GFP_KERNEL); ++ ++ printk("USB PDMA mode enabled.\n"); ++ printk("tx_ring=%p\n", tx_ring0_cache); ++ printk("rx_ring=%p\n", rx_ring0_cache); ++ ++ tx_ring0_noncache = tx_ring0_cache; ++ rx_ring0_noncache = rx_ring0_cache; ++ ++ for(i=0; i < NUM_RX_DESC; i++){ ++ memset((void *)&rx_ring0_noncache[i], 0, 16 /* sizeof()*/); ++ rx_ring0_noncache[i].rxd_info2.DDONE_bit = 0; ++ rx_ring0_noncache[i].rxd_info2.LS0= 0; ++ rx_ring0_noncache[i].rxd_info2.PLEN0= sizeof(u8) * RX_BUFF_SZ; ++ rx_ring0_noncache[i].rxd_info1.PDP0 = dma_map_single(NULL, (void *)USBRxPackets[i], sizeof(u8) * RX_BUFF_SZ, DMA_FROM_DEVICE); ++ } ++ ++ for (i=0; i < NUM_TX_DESC; i++) { ++ memset((void *)&tx_ring0_noncache[i],0, 16 /* sizeof()*/); ++ tx_ring0_noncache[i].txd_info2.LS0_bit = 1; ++ tx_ring0_noncache[i].txd_info2.DDONE_bit = 1; ++ // we would map dma buffer dynamically in IRQ handler & ep_queue(); ++ } ++ ++ rx_dma_owner_idx0 = 0; ++ tx_cpu_owner_idx0 = 0; ++ tx_need_free_idx0 = 0; ++ ++ /* initial UDMA register */ ++ //OUTL(cpu_to_le32((u32) UDMA_Init_Setting), RTUSB_UDMA_CTRL); ++ ++ if(sm){ ++ printk("Storage mode enabled.\n"); ++ reg_write(RTUSB_UDMA_CTRL, 0x3F000063); /* enable storage mode */ ++ }else ++ reg_write(RTUSB_UDMA_CTRL, 0x3F000003); ++ ++ ++ /* Tell the adapter where the TX/RX rings are located. */ ++ //OUTL(phys_to_bus((u32) &rx_ring[0]), RTUSB_RX_BASE_PTR0); ++ reg_write(RTUSB_RX_BASE_PTR0, rx_ring_bus_addr); ++ ++ //OUTL(phys_to_bus((u32) &tx_ring0[0]), RTUSB_TX_BASE_PTR0); ++ reg_write(RTUSB_TX_BASE_PTR0, tx_ring_bus_addr); ++ ++ //OUTL(cpu_to_le32((u32) NUM_RX_DESC), RTUSB_RX_MAX_CNT0); ++ //OUTL(cpu_to_le32((u32) NUM_TX_DESC), RTUSB_TX_MAX_CNT0); ++ reg_write(RTUSB_RX_MAX_CNT0, cpu_to_le32(NUM_RX_DESC)); ++ reg_write(RTUSB_TX_MAX_CNT0, cpu_to_le32(NUM_TX_DESC)); ++ ++ //OUTL(cpu_to_le32((u32) tx_cpu_owner_idx0), RTUSB_TX_CTX_IDX0); ++ //OUTL(cpu_to_le32((u32) (NUM_RX_DESC - 1)), RTUSB_RX_CALC_IDX0); ++ reg_write(RTUSB_TX_CTX_IDX0, cpu_to_le32(tx_cpu_owner_idx0)); ++ reg_write(RTUSB_RX_CALC_IDX0, cpu_to_le32(NUM_RX_DESC - 1)); ++ ++ udelay(500); ++ return 0; ++} ++ ++static int udc_dma_fini(void) ++{ ++ int i; ++ u32 len; ++ dma_addr_t addr; ++ ++ udc_dma_enable(false); ++ udc_dma_int_enable(false); ++ ++ /* restore UDMA register */ ++ reg_write(RTUSB_UDMA_CTRL, 0x0); ++ ++ // unmap & free RX buffer ++ for(i=0; irt_usb; ++ unsigned long flags; ++ ++ DBG; ++ ++ if (!usb_ep || !desc || !EP_NO(rt_ep) || desc->bDescriptorType != USB_DT_ENDPOINT || rt_ep->bEndpointAddress != desc->bEndpointAddress) { ++ D_ERR(rt_usb->dev, "<%s> bad ep or descriptor\n", __func__); ++ return -EINVAL; ++ } ++ if (rt_ep->bmAttributes != desc->bmAttributes) { ++ D_ERR(rt_usb->dev, "<%s> %s type mismatch, 0x%x, 0x%x\n", __func__, usb_ep->name, rt_ep->bmAttributes, desc->bmAttributes); ++ return -EINVAL; ++ } ++ if (!rt_usb->driver || rt_usb->gadget.speed == USB_SPEED_UNKNOWN) { ++ D_ERR(rt_usb->dev, "<%s> bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ local_irq_save(flags); ++ rt_ep->stopped = 0; ++ if(dma){ ++ //rt_ep_irq_enable(rt_ep); ++ }else ++ rt_ep_irq_enable(rt_ep); ++ local_irq_restore(flags); ++ ++ xprintk("<%s> ENABLED %s\n", __func__, usb_ep->name); ++ return 0; ++} ++ ++static int rt_ep_disable(struct usb_ep *usb_ep) ++{ ++ struct rt_ep_struct *rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); ++ unsigned long flags; ++ ++DBG; ++ if (!usb_ep || !EP_NO(rt_ep) /* || !list_empty(&rt_ep->queue) */) { ++ D_ERR(rt_ep->rt_usb->dev, "<%s> %s can not be disabled\n", __func__, usb_ep ? rt_ep->ep.name : NULL); ++ return -EINVAL; ++ } ++ ++ local_irq_save(flags); ++ rt_ep->stopped = 1; ++ nuke(rt_ep, -ESHUTDOWN); ++ rt_flush(rt_ep); ++ rt_ep_irq_disable(rt_ep); ++ ++ local_irq_restore(flags); ++ ++ xprintk("<%s> DISABLED %s\n", __func__, usb_ep->name); ++ return 0; ++} ++ ++static struct usb_request *rt_ep_alloc_request (struct usb_ep *usb_ep, gfp_t gfp_flags) ++{ ++ struct rt_request *req; ++ ++ DBG; ++ req = kzalloc(sizeof *req, gfp_flags); ++ if (!req || !usb_ep) ++ return 0; ++ ++ INIT_LIST_HEAD(&req->queue); ++ req->in_use = 0; ++ return &req->req; ++} ++ ++static void rt_ep_free_request(struct usb_ep *usb_ep, struct usb_request *usb_req) ++{ ++ struct rt_request *req; ++ ++ DBG; ++ req = container_of(usb_req, struct rt_request, req); ++ WARN_ON(!list_empty(&req->queue)); ++ kfree(req); ++} ++ ++/* ++ * Two cases : ++ * 1) UDC TX (IN EPs) ++ * enqueue req -> handle_ep() -> write fifo -> TX_DONE -> handle_ep() -> write next fifo -> TX_DONE... ++ * ++ * 2) UDC RX (OUT EPs) ++ * enqueue req -> RX_DONE -> handle_ep() -> read_fifo -> RX_DONE -> handle_ep() -> read fifo... ++ */ ++static int rt_ep_queue(struct usb_ep *usb_ep, struct usb_request *req, gfp_t gfp_flags) ++{ ++ struct rt_ep_struct *rt_ep; ++ struct rt_udc_struct *rt_usb; ++ struct rt_request *rt_req; ++ unsigned long flags; ++ int ret = 0; ++ int handle_right_now = 0; ++ ++ rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); ++ rt_usb = rt_ep->rt_usb; ++ rt_req = container_of(req, struct rt_request, req); ++ rt_req->rt_ep = rt_ep; ++ ++ if (rt_usb->set_config && !EP_NO(rt_ep)) { ++ rt_usb->set_config = 0; ++ D_ERR(rt_usb->dev, "<%s> gadget reply set config\n", __func__); ++ return 0; ++ } ++ ++ if (unlikely(!req || !rt_req || !req->complete || !req->buf)) { ++ D_ERR(rt_usb->dev, "<%s> bad params\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (unlikely(!usb_ep || !rt_ep)) { ++ D_ERR(rt_usb->dev, "<%s> bad ep\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (!rt_usb->driver || rt_usb->gadget.speed == USB_SPEED_UNKNOWN) { ++ D_ERR(rt_usb->dev, "<%s> bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ /* debug */ ++ xprintk(" ep%d%s %p %dB\n", EP_NO(rt_ep), ((!EP_NO(rt_ep) && rt_ep->rt_usb->ep0state == EP0_IN_DATA_PHASE) || (EP_NO(rt_ep) && EP_DIR(rt_ep) == EP_IN )) ? "IN" : "OUT", &rt_req->req, req->length); ++ ++ if (rt_ep->stopped) { ++ printk("EP%d -> stopped.\n", EP_NO(rt_ep)); ++ req->status = -ESHUTDOWN; ++ return -ESHUTDOWN; ++ } ++ ++ if (rt_req->in_use) { ++ D_ERR(rt_usb->dev, "<%s> refusing to queue req %p (already queued)\n", __func__, req); ++ return -1; ++ } ++ ++ local_irq_save(flags); ++ /* ++ * handle No-data Ctrl transfer. ++ */ ++ if(!EP_NO(rt_ep)/* EP0 */ && EP_DIR(rt_ep) == EP_OUT && !req->length){ ++ done(rt_ep, rt_req, 0); ++ local_irq_restore(flags); ++ return ret; ++ } ++ ++ req->status = -EINPROGRESS; ++ req->actual = 0; ++ ++ if(dma || list_empty(&rt_ep->queue)) ++ handle_right_now = 1; ++ ++ ep_add_request(rt_ep, rt_req); ++ ++ if(handle_right_now){ ++ if(!EP_NO(rt_ep) && rt_ep->rt_usb->ep0state != EP0_OUT_DATA_PHASE){ /* ep0 && TX*/ ++ handle_inep0(rt_ep); ++ }else if( EP_DIR(rt_ep) == EP_IN){ /* epin[1-x] */ ++ handle_inep(rt_ep); ++ }else{ ++ // other reqs are waiting for TX_DONE int. ++ } ++ } ++ ++ if(dma){ ++ if(EP_NO(rt_ep) && (EP_DIR(rt_ep) == EP_OUT)) ++ tasklet_schedule(&rx_dma_tasklet); ++ }else{ ++ if( (EP_DIR(rt_ep) == EP_OUT)/* OUT EP */ && rt_ep->pending){ ++ rt_ep->pending = 0; ++ handle_pending_epoutirq(rt_usb, rt_ep, rt_req); ++ } ++ } ++ ++ local_irq_restore(flags); ++ return ret; ++} ++ ++static int rt_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) ++{ ++ struct rt_ep_struct *rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); ++ struct rt_request *req; ++ unsigned long flags; ++ ++ DBG; ++ if (unlikely(!usb_ep || !EP_NO(rt_ep))) { ++ D_ERR(rt_ep->rt_usb->dev, "<%s> bad ep\n", __func__); ++ return -EINVAL; ++ } ++ ++ local_irq_save(flags); ++ ++ /* make sure it's actually queued on this endpoint */ ++ list_for_each_entry(req, &rt_ep->queue, queue) { ++ if (&req->req == usb_req) ++ break; ++ } ++ if (&req->req != usb_req) { ++ local_irq_restore(flags); ++ return -EINVAL; ++ } ++ ++ done(rt_ep, req, -ECONNRESET); ++ ++ local_irq_restore(flags); ++ return 0; ++} ++ ++static int rt_ep_set_halt(struct usb_ep *usb_ep, int value) ++{ ++ struct rt_ep_struct *rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); ++ unsigned long flags; ++ ++ DBG; ++ if (unlikely(!usb_ep || !EP_NO(rt_ep))) { ++ D_ERR(rt_ep->rt_usb->dev, "<%s> bad ep\n", __func__); ++ return -EINVAL; ++ } ++ ++ local_irq_save(flags); ++ ++ if ((rt_ep->bEndpointAddress & USB_DIR_IN) && !list_empty(&rt_ep->queue)) { ++ local_irq_restore(flags); ++ return -EAGAIN; ++ } ++ ++ rt_ep_stall(rt_ep, 1); ++ ++ local_irq_restore(flags); ++ ++ D_EPX(rt_ep->rt_usb->dev, "<%s> %s halt\n", __func__, usb_ep->name); ++ return 0; ++} ++ ++static int rt_ep_fifo_status(struct usb_ep *usb_ep) ++{ ++ struct rt_ep_struct *rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); ++ ++ DBG; ++ if (!usb_ep) { ++ D_ERR(rt_ep->rt_usb->dev, "<%s> bad ep\n", __func__); ++ return -ENODEV; ++ } ++ ++ if (rt_ep->rt_usb->gadget.speed == USB_SPEED_UNKNOWN) ++ return 0; ++ else ++ return rt_fifo_bcount(rt_ep); ++} ++ ++static void rt_ep_fifo_flush(struct usb_ep *usb_ep) ++{ ++ struct rt_ep_struct *rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); ++ unsigned long flags; ++ ++ DBG; ++ local_irq_save(flags); ++ ++ if (!usb_ep || !EP_NO(rt_ep) || !list_empty(&rt_ep->queue)) { ++ D_ERR(rt_ep->rt_usb->dev, "<%s> bad ep\n", __func__); ++ local_irq_restore(flags); ++ return; ++ } ++ ++ /* toggle and halt bits stay unchanged */ ++ rt_flush(rt_ep); ++ ++ local_irq_restore(flags); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ++static void *rt_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes, dma_addr_t *dma, gfp_t gfp_flags) ++{ ++ char *retval; ++ ++ retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM)); ++ if (retval) ++// *dma = virt_to_bus (retval); ++ *dma = (dma_addr_t)~0; ++ return retval; ++} ++ ++static void rt_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes) ++{ ++ kfree (buf); ++} ++#endif ++ ++static struct usb_ep_ops rt_ep_ops = { ++ .enable = rt_ep_enable, ++ .disable = rt_ep_disable, ++ ++ .alloc_request = rt_ep_alloc_request, ++ .free_request = rt_ep_free_request, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) ++ .alloc_buffer = rt_ep_alloc_buffer, ++ .free_buffer = rt_ep_free_buffer, ++#endif ++ .queue = rt_ep_queue, ++ .dequeue = rt_ep_dequeue, ++ ++ .set_halt = rt_ep_set_halt, ++ .fifo_status= rt_ep_fifo_status, ++ .fifo_flush = rt_ep_fifo_flush, ++}; ++ ++/******************************************************************************* ++ * USB endpoint control functions ++ ******************************************************************************* ++ */ ++static void usb_init_data(struct rt_udc_struct *rt_usb) ++{ ++ struct rt_ep_struct *rt_ep; ++ u8 i; ++ ++ DBG; ++ /* device/ep0 records init */ ++ INIT_LIST_HEAD(&rt_usb->gadget.ep_list); ++ INIT_LIST_HEAD(&rt_usb->gadget.ep0->ep_list); ++ ep0_chg_stat(__func__, rt_usb, EP0_IDLE); ++ ++ /* basic endpoint records init */ ++ for (i = 0; i < RT_USB_NB_EP; i++) { ++ rt_ep = &rt_usb->rt_ep[i]; ++ ++ if (i) { ++ list_add_tail(&rt_ep->ep.ep_list, &rt_usb->gadget.ep_list); ++ rt_ep->stopped = 1; ++ } else ++ rt_ep->stopped = 0; ++ ++ INIT_LIST_HEAD(&rt_ep->queue); ++ } ++} ++ ++static void udc_stop_activity(struct rt_udc_struct *rt_usb, struct usb_gadget_driver *driver) ++{ ++ struct rt_ep_struct *rt_ep; ++ int i; ++ ++ if (rt_usb->gadget.speed == USB_SPEED_UNKNOWN) ++ driver = NULL; ++ ++ /* prevent new request submissions, kill any outstanding requests */ ++ for (i = 0; i < RT_USB_NB_EP; i++) { ++ rt_ep = &rt_usb->rt_ep[i]; ++ if(i != 0){ /* don't have to flush EP[0]. */ ++ rt_flush(rt_ep); ++ rt_ep->stopped = 1; ++ rt_ep_irq_disable(rt_ep); ++ } ++ nuke(rt_ep, -ESHUTDOWN); ++ } ++ ++ rt_usb->cfg = 0; ++ rt_usb->intf = 0; ++ rt_usb->alt = 0; ++ ++ if (driver) ++ driver->disconnect(&rt_usb->gadget); ++} ++ ++/* ++ * keep for reference. ++ */ ++static void handle_config(unsigned long data) ++{ ++ DBG; ++#if 0 ++ struct imx_udc_struct *imx_usb = (void *)data; ++ struct usb_ctrlrequest u; ++ int temp, cfg, intf, alt; ++ ++ local_irq_disable(); ++ ++ temp = __raw_readl(imx_usb->base + USB_STAT); ++ cfg = (temp & STAT_CFG) >> 5; ++ intf = (temp & STAT_INTF) >> 3; ++ alt = temp & STAT_ALTSET; ++ ++ xprintk("<%s> orig config C=%d, I=%d, A=%d / req config C=%d, I=%d, A=%d\n", __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt, cfg, intf, alt); ++ ++ if (cfg == 1 || cfg == 2) { ++ ++ if (imx_usb->cfg != cfg) { ++ u.bRequest = USB_REQ_SET_CONFIGURATION; ++ u.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; ++ u.wValue = cfg; ++ u.wIndex = 0; ++ u.wLength = 0; ++ imx_usb->cfg = cfg; ++ imx_usb->driver->setup(&imx_usb->gadget, &u); ++ ++ } ++ if (imx_usb->intf != intf || imx_usb->alt != alt) { ++ u.bRequest = USB_REQ_SET_INTERFACE; ++ u.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE; ++ u.wValue = alt; ++ u.wIndex = intf; ++ u.wLength = 0; ++ imx_usb->intf = intf; ++ imx_usb->alt = alt; ++ imx_usb->driver->setup(&imx_usb->gadget, &u); ++ } ++ } ++ ++ imx_usb->set_config = 0; ++ ++ local_irq_enable(); ++#endif ++} ++ ++static void handle_setup(struct rt_udc_struct *rt_usb) ++{ ++ u8 epcs; ++ int i; ++ union { ++ struct usb_ctrlrequest r; ++ u8 raw[8]; ++ u32 word[2]; ++ } u; ++ struct rt_ep_struct *rt_ep = &rt_usb->rt_ep[0]; ++ ++ nuke(rt_ep, -EPROTO); ++ ++ // read setup packet ++ for (i = 0; i < 8; i++) ++ u.raw[i] = usb_read(SETUPDATA + i); ++ ++ le16_to_cpus(&u.r.wValue); ++ le16_to_cpus(&u.r.wIndex); ++ le16_to_cpus(&u.r.wLength); ++ ++ xprintk(" %02x.%02x v%04x\n", u.r.bRequestType, u.r.bRequest, u.r.wValue); ++ ++ switch(u.r.bRequest){ ++ /* HW(CUSB2) has handled it. */ ++ case USB_REQ_SET_ADDRESS: ++ if (u.r.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) ++ break; ++ return; ++ } ++ ++ if(!u.r.wLength){ ++ ep0_chg_stat(__func__, rt_usb, EP0_NO_DATA_PHASE); ++ }else if (u.r.bRequestType & USB_DIR_IN){ ++ ep0_chg_stat(__func__, rt_usb, EP0_IN_DATA_PHASE); ++ }else{ ++ // reload and clear out0bc ++ usb_write(OUT0BC, 0); ++ ep0_chg_stat(__func__, rt_usb, EP0_OUT_DATA_PHASE); ++ } ++ ++ if(!rt_usb->driver){ ++ printk("<%s> please insert gadget driver/module.\n", __func__); ++ goto stall; ++ } ++ ++ if(!rt_usb->driver->setup) ++ goto stall; ++ ++ i = rt_usb->driver->setup(&rt_usb->gadget, &u.r); // gadget would queue more usb req here ++ ++ if (i < 0) { ++ printk("<%s> device setup error %d\n", __func__, i); ++ goto stall; ++ } ++ ++ if(rt_usb->ep0state == EP0_NO_DATA_PHASE){ ++ epcs = read_epcs(rt_ep); ++ epcs |= EP_CS_EP0_HSNAK; // clear hsnak to let HW ack the status stage. ++ write_epcs(rt_ep, epcs); ++ } ++ ++ return; ++stall: ++ printk("<%s> protocol STALL\n", __func__); ++ rt_ep_stall(rt_ep, 1); ++ ep0_chg_stat(__func__, rt_usb, EP0_STALL); ++ return; ++} ++ ++/* ++ * handle TX done interrupt ++ */ ++static void handle_epinirq(struct rt_udc_struct *rt_usb, u8 epinirq) ++{ ++ u8 irq = 0x0; ++ struct rt_request *req; ++ struct rt_ep_struct *rt_ep; ++ ++ rt_ep = &rt_usb->rt_ep[epinirq]; ++ ++ if (list_empty(&rt_ep->queue)) ++ FATAL_ERROR("empty queue"); ++ ++ // clear ep interrupt ++ if(epinirq < 8){ ++ irq |= 1 << epinirq; ++ usb_write(IN07IRQ, irq); ++ }else{ ++ irq |= 1 << (epinirq-8); ++ usb_write(IN815IRQ, irq); ++ } ++ ++ req = list_entry(rt_ep->queue.next, struct rt_request, queue); ++ xprintk("r.l=%d, r.a=%d\n", req->req.length, req->req.actual); ++ if(req->req.actual >= req->req.length ){ ++ if( req->req.actual && (!(req->req.actual % rt_ep->ep.maxpacket)) && req->req.zero){ ++ // deal with one more "zlp" ++ req->req.zero = 0; ++ write_ep_fifo_zlp(rt_ep); ++ return; ++ } ++ ++ // the first tx req in ep->queue is done. ++ rt_ep->tx_done_count = 0; ++ if(!epinirq /* EP0 */) ++ ep0_chg_stat(__func__, rt_usb, EP0_IDLE); ++ done(rt_ep, req, 0); ++#if 1 ++ // more reqs there. ++ if (!list_empty(&rt_ep->queue) && !rt_ep->tx_done_count){ ++ if(!epinirq /* EP0 */){ ++ handle_inep0(rt_ep); ++ }else{ ++ handle_inep(rt_ep); ++ } ++ } ++#endif ++ }else{ ++ if(!epinirq /* EP0 */){ ++ handle_inep0(rt_ep); ++ }else ++ handle_inep(rt_ep); ++ } ++} ++ ++static void handle_ep0outirq(struct rt_udc_struct *rt_usb, u8 epoutirq) ++{ ++ u8 epcs, irq = 0x0; ++ struct rt_request *req = NULL; ++ struct rt_ep_struct *rt_ep = NULL; ++ ++DBG; ++ rt_ep = &rt_usb->rt_ep[0]; ++ ++ if(rt_usb->ep0state == EP0_STALL){ ++ printk("<%s> protocol STALL\n", __func__); ++ rt_ep_stall(rt_ep, 1); ++ return; ++ } ++ ++ if(rt_usb->ep0state != EP0_OUT_DATA_PHASE) ++ FATAL_ERROR("Odd stage"); ++ ++ do{ ++ if(unlikely(!read_outbc(0x0))) ++ FATAL_ERROR("EP0 BC"); ++ ++ if (unlikely(list_empty(&rt_ep->queue))) ++ FATAL_ERROR("EP0 no req"); ++ ++ req = handle_outep0(rt_ep); ++ ++ //req = list_entry(rt_ep->queue.next, struct rt_request, queue); ++ xprintk("q.l=%d,q.a=%d\n", req->req.length, req->req.actual); ++ ++ // clear ep interrupt ++ irq |= 1; ++ usb_write(OUT07IRQ, irq); ++ ++ if(req && ((req->req.actual % rt_ep->ep.maxpacket) || (req->req.actual >= req->req.length))){ ++ ep0_chg_stat(__func__, rt_usb, EP0_IDLE); ++ done(rt_ep, req, 0); // short packet indicates transaction is done. ++ ++ epcs = read_epcs(rt_ep); ++ epcs |= EP_CS_EP0_HSNAK; // clear hsnak bit to let HW ack the status stage. ++ write_epcs(rt_ep, epcs); ++ break; ++ } ++ ++ // reload EP[0] ++ usb_write(OUT0BC /*out0bc*/, 0x0); ++ epcs = read_epcs(rt_ep); ++ }while(!(epcs & EP0_OUT_BSY)); ++} ++ ++static void handle_pending_epoutirq(struct rt_udc_struct *rt_usb, struct rt_ep_struct *rt_ep, struct rt_request *req) ++{ ++ u8 epcs; ++ ++DBG; ++ do{ ++ if(unlikely(!read_outbc(EP_NO(rt_ep)))) ++ FATAL_ERROR("No BC"); ++ ++ handle_outep(rt_ep); ++ if(req && ( (req->req.actual % rt_ep->ep.maxpacket) || (req->req.actual >= req->req.length))){ ++ xprintk("q.l=%d,q.a=%d\n", req->req.length, req->req.actual); ++ ++ //rx_done(rt_ep, req, 0); ++ done(rt_ep, req, 0); ++ } ++ ++ epcs = read_epcs(rt_ep); ++ write_epcs(rt_ep, 0x0); ++ epcs = read_epcs(rt_ep); ++ ++ }while(!(epcs & EP_CS_BSY)); ++} ++ ++static void handle_epoutirq(struct rt_udc_struct *rt_usb, u8 epoutirq) ++{ ++ u8 irq = 0x0; ++ ++DBG; ++ if(unlikely(epoutirq == 0x0)){ ++ handle_ep0outirq(rt_usb, 0x0); ++ return; ++ } ++ ++ tasklet_schedule(&rx_tasklet); ++ ++ // clear ep interrupt ++ irq |= 1 << epoutirq; ++ usb_write(OUT07IRQ, irq); ++ return; ++} ++ ++static void eps_change_to_hs(struct rt_udc_struct *rt_usb) ++{ ++ int i; ++ struct rt_ep_struct *rt_ep; ++ for(i = 0; i < RT_USB_NB_EP; i++){ ++ rt_ep = &rt_usb->rt_ep[i]; ++ if(rt_ep->bmAttributes == USB_ENDPOINT_XFER_BULK){ ++ rt_ep->ep.maxpacket = 512; ++ } ++ } ++} ++ ++static void eps_change_to_fs(struct rt_udc_struct *rt_usb) ++{ ++ int i; ++ struct rt_ep_struct *rt_ep; ++ for(i = 0; i < RT_USB_NB_EP; i++){ ++ rt_ep = &rt_usb->rt_ep[i]; ++ if(rt_ep->bmAttributes == USB_ENDPOINT_XFER_BULK){ ++ rt_ep->ep.maxpacket = 64; ++ } ++ } ++} ++ ++void handle_highspeed(struct rt_udc_struct *rt_usb) ++{ ++ DBG; ++ ++ eps_change_to_hs(rt_usb); ++ ++ if(dma){ ++#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) ++ usb_write(IN1CON, 0x8D); // InEP1 : Int, 2 subfifos ++ usb_write(IN2CON, 0x89); // InEP2 : Bulk, 2 subfifo ++ usb_write(OUT1CON, 0x8D); // OutEP1 : Int, 2 subfifos ++ usb_write(OUT2CON, 0x89); // OutEP2 : Bulk, 2 subfifos ++ //usb_write(OUT3CON, 0x89); // OutEP3 : Bulk, 2 subfifo ++ //usb_write(OUT4CON, 0x89); // OutEP4 : Bulk. 2 subfifo ++#elif defined (CONFIG_RALINK_RT5350) ++ // Access by CPU ++ usb_write(IN1CON, 0x89); // InEP1 : Bulk, 2 subfifos ++ usb_write(OUT1CON, 0x89); // OutEP1 : Bulk, 2 subfifos ++#else ++#error "define a platform" ++#endif ++ }else{ ++ // Access by CPU ++#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) ++ usb_write(IN1CON, 0x8C); // InEP1 : Int , 1 subfifos ++ usb_write(IN2CON, 0x88); // InEP2 : Bulk, 1 subfifo ++ ++ usb_write(OUT1CON, 0x8C); // OutEP1 : Int, 1 subfifos ++ usb_write(OUT2CON, 0x88); // OutEP2 : Bulk, 1 subfifos ++ //usb_write(OUT3CON, 0x88); // OutEP3 : Bulk, 1 subfifo ++ //usb_write(OUT4CON, 0x88); // OutEP4 : Bulk. 1 subfifo ++#elif defined (CONFIG_RALINK_RT5350) ++ // Access by CPU ++ usb_write(IN1CON, 0x88); // InEP1 : Bulk , 1 subfifos ++ usb_write(OUT1CON, 0x88); // OutEP1 : Bulk, 1 subfifos ++#else ++#error "define a platform" ++#endif ++ ++ } ++ // clear all pending interrupts ++ usb_write(IN07IRQ, 0xFF); ++ usb_write(OUT07IRQ, 0xFF); ++ ++ rt_usb->gadget.speed = USB_SPEED_HIGH; ++ ++ // reset ALL endpoints ++ rt_all_eps_reset(); ++ ++ // Enable ep0 interrupt. ++ // (EPx interrupt is enabled in EPx_enable(). ) ++ rt_ep_irq_enable(&rt_usb->rt_ep[0]); ++} ++ ++static void handle_reset(struct rt_udc_struct *rt_usb) ++{ ++ struct rt_ep_struct *rt_ep; ++ int i; ++ ++ eps_change_to_fs(rt_usb); ++ ++ // remove all EPs' usb request ++ for (i = 0; i < RT_USB_NB_EP; i++) { ++ rt_ep = &rt_usb->rt_ep[i]; ++ if(i != 0){ /* don't have to flush EP[0]. */ ++ rt_flush(rt_ep); ++ rt_ep->stopped = 1; ++ rt_ep_irq_disable(rt_ep); ++ } ++ nuke(rt_ep, -ESHUTDOWN); ++ } ++ ++ rt_usb->cfg = 0; ++ rt_usb->intf = 0; ++ rt_usb->alt = 0; ++ ++ if(dma){ ++ // clear all PDMA interrupts ++ udc_dma_all_int_clear(); ++ // reset PDMA ++ udc_dma_rst(); ++ } ++ ++ // clear all pending interrupts ++ usb_write(IN07IRQ, 0xFF); ++ usb_write(OUT07IRQ, 0xFF); ++ ++ // flush all EP's fifo ++ rt_all_eps_reset(); ++} ++ ++static void handle_usbirq(struct rt_udc_struct *rt_usb, u8 usbirq) ++{ ++ if(usbirq & USB_INTR_SETUP_TOKEN_VALID){ ++ // Setup token is arrival. ++ // get setup data and pass it to gadget driver. ++ handle_setup(rt_usb); ++ } ++ ++ if(usbirq & USB_INTR_RESET) ++ handle_reset(rt_usb); ++ ++ if(usbirq & USB_INTR_HSPEED) ++ handle_highspeed(rt_usb); ++ ++ /* ++ * DO NOT try to clear SoF and token Interrupt! ++ */ ++ if( (usbirq & USB_INTR_SETUP_TOKEN_VALID) || ++ (usbirq & USB_INTR_HSPEED) || ++ (usbirq & USB_INTR_RESET)) ++ usb_write(USBIRQ, usbirq); ++} ++ ++static int irq_count = 100; /* for debug */ ++/* ++ * Interrupt handler ++ */ ++irqreturn_t rt_irq_handler(int irq, void *_dev) ++{ ++ u32 usbirq,epin07irq,epin07ien,epout07irq,epout07ien; ++ struct rt_udc_struct *rt_usb = _dev; ++#ifdef DEBUG ++ u32 count_tmp = irq_count; ++#endif ++ ++ ++ DBG; ++ irq_count++; ++ ++ usbirq = usb_read(USBIRQ); ++ epin07irq = usb_read(IN07IRQ); ++ epin07ien = usb_read(IN07IEN); ++ epout07irq = usb_read(OUT07IRQ); ++ epout07ien = usb_read(OUT07IEN); ++ ++ //epin07irq = epin07irq & epin07ien; ++ //epout07irq = epout07irq & epout07ien; ++ ++ xprintk(">%x\n", count_tmp); ++ dump_usbirq(usbirq); ++ dump_epirq(epin07irq, epin07ien, 1); ++ dump_epirq(epout07irq, epout07ien, 0); ++ ++ if(dma){ ++ u32 dma_irq = reg_read(RTUSB_INT_STATUS); ++ if(epin07irq & 0x1) // INEP0 ++ handle_epinirq(rt_usb, 0); ++ ++ if(usbirq) // HS, Reset, SetupValid ++ handle_usbirq(rt_usb, usbirq); ++ ++ if(epout07irq & 0x1) // OUTEP0 ++ handle_epoutirq(rt_usb, 0); ++ ++ if(dma_irq) ++ handle_dmairq(rt_usb, dma_irq); ++ ++ }else{ ++ if(epin07irq & 0x1) // INEP0 ++ handle_epinirq(rt_usb, 0); ++ ++ if(usbirq) // HS, Reset, SetupValid ++ handle_usbirq(rt_usb, usbirq); ++ ++ if(epout07irq & 0x1) // OUTEP0 ++ handle_epoutirq(rt_usb, 0); ++ ++ if(epout07irq & 0x2) // OUTEP1 ++ handle_epoutirq(rt_usb, 1); ++ ++ if(epin07irq & 0x2) // INEP1 ++ handle_epinirq(rt_usb, 1); ++ ++ if(epout07irq & 0x4) // OUTEP2 ++ handle_epoutirq(rt_usb, 2); ++ ++ if(epin07irq & 0x4) // INEP2 ++ handle_epinirq(rt_usb, 2); ++ ++ //if(epout07irq & 0x8) // OUTEP3 ++ // handle_epoutirq(rt_usb, 3); ++ ++ //if(epout07irq & 0x10) // OUTEP4 ++ // handle_epoutirq(rt_usb, 4); ++ } ++ xprintk("<%x\n", count_tmp); ++ return IRQ_HANDLED; ++} ++ ++/* ++ ****************************************************************************** ++ * Static defined Ralink UDC structure ++ ******************************************************************************* ++ */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) ++static void nop_release(struct device *dev) ++{ ++ return; ++} ++#endif ++ ++static const struct usb_gadget_ops rt_udc_ops = { ++ .get_frame = rt_udc_get_frame, ++ .wakeup = rt_udc_wakeup, ++}; ++ ++static struct rt_udc_struct controller = { ++ .gadget = { ++ .ops = &rt_udc_ops, ++ .ep0 = &controller.rt_ep[0].ep, ++ .name = driver_name, ++ .dev = { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) ++ .init_name = "gadget", ++ .release = nop_release, ++#else ++ .bus_id = "gadget", ++#endif ++ }, ++ }, ++ .rt_ep[0] = { ++ .ep = { ++ .name = ep0name, ++ .ops = &rt_ep_ops, ++ .maxpacket = 64, ++ }, ++ .rt_usb = &controller, ++ .bEndpointAddress = 0, ++ .bmAttributes = USB_ENDPOINT_XFER_CONTROL, ++ .pending = 0, ++ }, ++#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) ++ .rt_ep[1] = { ++ .ep = { ++ .name = "ep1in-int", ++ .ops = &rt_ep_ops, ++ .maxpacket = 64, ++ }, ++ .rt_usb = &controller, ++ .bEndpointAddress = USB_DIR_IN | 1, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .pending = 0, ++ }, ++ .rt_ep[2] = { ++ .ep = { ++ .name = "ep2in-bulk", ++ .ops = &rt_ep_ops, ++ .maxpacket = 64, ++ }, ++ .rt_usb = &controller, ++ .bEndpointAddress = USB_DIR_IN | 2, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .pending = 0, ++ }, ++ .rt_ep[3] = { ++ .ep = { ++ .name = "ep1out-int", ++ .ops = &rt_ep_ops, ++ .maxpacket = 64, ++ }, ++ .rt_usb = &controller, ++ .bEndpointAddress = USB_DIR_OUT | 1, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ .pending = 0, ++ }, ++ .rt_ep[4] = { ++ .ep = { ++ .name = "ep2out-bulk", ++ .ops = &rt_ep_ops, ++ .maxpacket = 64, ++ }, ++ .rt_usb = &controller, ++ .bEndpointAddress = USB_DIR_OUT | 2, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .pending = 0, ++ }, ++ /* ++ .rt_ep[5] = { ++ .ep = { ++ .name = "ep3out-bulk", ++ .ops = &rt_ep_ops, ++ .maxpacket = 64, ++ }, ++ .rt_usb = &controller, ++ .bEndpointAddress = USB_DIR_OUT | 3, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .pending = 0, ++ }, ++ .rt_ep[6] = { ++ .ep = { ++ .name = "ep4out-bulk", ++ .ops = &rt_ep_ops, ++ .maxpacket = 64, ++ }, ++ .rt_usb = &controller, ++ .bEndpointAddress = USB_DIR_OUT | 4, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .pending = 0, ++ }, ++ */ ++ ++#elif defined (CONFIG_RALINK_RT5350) ++ .rt_ep[1] = { ++ .ep = { ++ .name = "ep1in-bulk", ++ .ops = &rt_ep_ops, ++ .maxpacket = 64, ++ }, ++ .rt_usb = &controller, ++ .bEndpointAddress = USB_DIR_IN | 1, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .pending = 0, ++ }, ++ .rt_ep[2] = { ++ .ep = { ++ .name = "ep1out-bulk", ++ .ops = &rt_ep_ops, ++ .maxpacket = 64, ++ }, ++ .rt_usb = &controller, ++ .bEndpointAddress = USB_DIR_OUT | 1, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ .pending = 0, ++ }, ++#else ++#error "define a platform" ++#endif ++}; ++ ++/* ++ ******************************************************************************* ++ * USB gadged driver functions ++ ******************************************************************************* ++ */ ++ ++static void rt_udc_enable(struct rt_udc_struct *rt_usb) ++{ ++ DBG; ++ rt_usb->gadget.speed = USB_SPEED_FULL; ++ if(dma){ ++ // enable dma interrupts ++ udc_dma_all_int_clear(); ++ udc_dma_int_enable(true); ++ ++ udc_dma_rst(); ++ ++ // enable dma ++ udc_dma_enable(true); ++ } ++} ++ ++static void rt_udc_disable(struct rt_udc_struct *rt_usb) ++{ ++ DBG; ++ ep0_chg_stat(__func__, rt_usb, EP0_IDLE); ++ rt_usb->gadget.speed = USB_SPEED_UNKNOWN; ++ if(dma){ ++ // disable dma interrupts ++ udc_dma_all_int_clear(); ++ udc_dma_int_enable(false); ++ ++ udc_dma_rst(); ++ ++ // disable dma ++ udc_dma_enable(false); ++ } ++} ++ ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++{ ++ struct rt_udc_struct *rt_usb = &controller; ++ int retval; ++ ++ DBG; ++ if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind || !driver->disconnect || !driver->setup) ++ return -EINVAL; ++ if (!rt_usb) ++ return -ENODEV; ++ if (rt_usb->driver) ++ return -EBUSY; ++ ++ /* first hook up the driver ... */ ++ rt_usb->driver = driver; ++ rt_usb->gadget.dev.driver = &driver->driver; ++ retval = device_add(&rt_usb->gadget.dev); ++ if (retval) ++ goto fail; ++ ++ retval = driver->bind(&rt_usb->gadget); ++ if (retval) { ++ D_ERR(rt_usb->dev, "<%s> bind to driver --> error %d\n", __func__, retval); ++ device_del(&rt_usb->gadget.dev); ++ goto fail; ++ } ++ ++ D_INI(rt_usb->dev, "<%s> registered gadget driver '%s'\n", __func__, driver->driver.name); ++ rt_udc_enable(rt_usb); ++ return 0; ++ ++fail: ++ rt_usb->driver = NULL; ++ rt_usb->gadget.dev.driver = NULL; ++ return retval; ++} ++EXPORT_SYMBOL(usb_gadget_register_driver); ++ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ struct rt_udc_struct *rt_usb = &controller; ++ ++DBG; ++ if (!rt_usb) ++ return -ENODEV; ++ if (!driver || driver != rt_usb->driver || !driver->unbind) ++ return -EINVAL; ++ ++ udc_stop_activity(rt_usb, driver); ++ rt_udc_disable(rt_usb); ++ del_timer(&rt_usb->timer); ++ ++ driver->unbind(&rt_usb->gadget); ++ rt_usb->gadget.dev.driver = NULL; ++ rt_usb->driver = NULL; ++ ++ device_del(&rt_usb->gadget.dev); ++ ++ D_INI(rt_usb->dev, "<%s> unregistered gadget driver '%s'\n", __func__, driver->driver.name); ++ ++ return 0; ++} ++EXPORT_SYMBOL(usb_gadget_unregister_driver); ++ ++/******************************************************************************* ++ * Module functions ++ ******************************************************************************* ++ */ ++static int __init rt_udc_probe(struct platform_device *pdev) ++{ ++ struct rt_udc_struct *rt_usb = &controller; ++ struct resource *res_mem, *res_irq; ++ void __iomem *base; ++ int ret = 0, res_mem_size; ++ ++DBG; ++ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res_mem) { ++ dev_err(&pdev->dev, "can't get device resources\n"); ++ return -ENODEV; ++ } ++ ++ res_mem_size = res_mem->end - res_mem->start + 1; ++ if (!request_mem_region(res_mem->start, res_mem_size, res_mem->name)) { ++ dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", res_mem_size, res_mem->start); ++ return -ENOMEM; ++ } ++ ++ base = ioremap(res_mem->start, res_mem_size); ++ if (!base) { ++ dev_err(&pdev->dev, "ioremap failed\n"); ++ ret = -EIO; ++ goto fail1; ++ } ++ ++ res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!res_irq) { ++ dev_err(&pdev->dev, "can't get irq number\n"); ++ ret = -ENODEV; ++ goto fail3; ++ } ++ rt_usb->interrupt = res_irq->start; ++ ++ ret = request_irq(rt_usb->interrupt, rt_irq_handler, IRQF_DISABLED, driver_name, rt_usb); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get irq %i, err %d\n", rt_usb->interrupt, ret); ++ goto fail3; ++ } ++ ++ rt_usb->res = res_mem; ++ rt_usb->base = base; ++ rt_usb->dev = &pdev->dev; ++ ++ device_initialize(&rt_usb->gadget.dev); ++ ++ rt_usb->gadget.dev.parent = &pdev->dev; ++ rt_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; ++ ++ platform_set_drvdata(pdev, rt_usb); ++ ++ usb_init_data(rt_usb); ++ ++ if(dma){ ++ if(rt_udc_dma_init()) ++ goto fail4; ++ } ++ ++ rt_udc_init(rt_usb); ++ ++ init_timer(&rt_usb->timer); ++ rt_usb->timer.function = handle_config; ++ rt_usb->timer.data = (unsigned long)rt_usb; ++ ++ return 0; ++fail4: ++ free_irq(rt_usb->interrupt, rt_usb); ++fail3: ++ iounmap(base); ++fail1: ++ release_mem_region(res_mem->start, res_mem_size); ++ return ret; ++} ++ ++static int __exit rt_udc_remove(struct platform_device *pdev) ++{ ++ struct rt_udc_struct *rt_usb = platform_get_drvdata(pdev); ++ ++ DBG; ++ rt_udc_disable(rt_usb); ++ del_timer(&rt_usb->timer); ++ ++ free_irq(rt_usb->interrupt, rt_usb); ++ ++ iounmap(rt_usb->base); ++ release_mem_region(rt_usb->res->start, rt_usb->res->end - rt_usb->res->start + 1); ++ ++ //if (pdata->exit) ++ // pdata->exit(&pdev->dev); ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++static void set_device_mode(void) ++{ ++ u32 val; ++ val = le32_to_cpu(*(volatile u_long *)(SYSCFG1)); ++ val = val & ~(USB0_HOST_MODE); ++ *(volatile u_long *)(SYSCFG1) = cpu_to_le32(val); ++ udelay(10000); ++} ++ ++/*----------------------------------------------------------------------------*/ ++static struct platform_driver udc_driver = { ++ ++ .driver = { ++ .name = driver_name, ++ .owner = THIS_MODULE, ++ }, ++ .probe = rt_udc_probe, ++ .remove = __exit_p(rt_udc_remove), ++ .suspend = NULL, ++ .resume = NULL, ++}; ++ ++static int udc_create_proc(void) ++{ ++ pProcDir = proc_mkdir(PROC_DIR, NULL); ++ if ((pProcDebugLevel = create_proc_entry(DEBUGLEVEL_PROCFILE, 0, pProcDir))){ ++ pProcDebugLevel->read_proc = (read_proc_t*)&debuglevel_read; ++ pProcDebugLevel->write_proc = (write_proc_t*)&debuglevel_write; ++ } ++ return 0; ++} ++ ++static int udc_remove_proc(void) ++{ ++ if (pProcDebugLevel) ++ remove_proc_entry(DEBUGLEVEL_PROCFILE, pProcDir); ++ if (pProcDir) ++ remove_proc_entry(PROC_DIR, 0); ++ ++ return 0; ++} ++ ++static int __init udc_init(void) ++{ ++ int ret; ++ udc_create_proc(); ++ ++ try_wake_up(); ++ set_device_mode(); ++ ++ ret = platform_driver_register(&udc_driver); ++ ++ tasklet_init(&rx_tasklet, rx_do_tasklet, 0); ++ tasklet_init(&tx_tasklet, tx_do_tasklet, 0); ++ ++ if(dma){ ++ printk("DMA TXMAXCAP=%d\n", TXMAXCAP); ++ tasklet_init(&rx_dma_tasklet, rx_dma_done_do_tasklet, 0); ++ tasklet_init(&tx_dma_tasklet, tx_dma_done_do_tasklet, 0); ++ } ++ ++ return ret; //platform_driver_probe(&udc_driver, rt_udc_probe); ++} ++module_init(udc_init); ++ ++static void __exit udc_exit(void) ++{ ++ DBG; ++ udc_remove_proc(); ++ if(dma) ++ udc_dma_fini(); ++ platform_driver_unregister(&udc_driver); ++} ++module_exit(udc_exit); ++ ++MODULE_DESCRIPTION("Ralink USB Device Controller driver"); ++MODULE_AUTHOR("Ying Yuan Huang "); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:rt_udc"); ++ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0163-USB-MIPS-ralink-fix-usb-issue-on-mt7620.patch b/target/linux/ramips/patches-3.9/0163-USB-MIPS-ralink-fix-usb-issue-on-mt7620.patch new file mode 100644 index 0000000000..f7f6d32a68 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0163-USB-MIPS-ralink-fix-usb-issue-on-mt7620.patch @@ -0,0 +1,48 @@ +From 7bb04eed36ee0b2f3148fc33db7f75d9b4c8548c Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 24 May 2013 21:28:08 +0200 +Subject: [PATCH 163/164] USB: MIPS: ralink: fix usb issue on mt7620 + +USB fails when frequency scaling is enabled. Increase the idle cpu speed when +scaled. + +Signed-off-by: John Crispin +--- + arch/mips/include/asm/mach-ralink/mt7620.h | 1 + + arch/mips/ralink/mt7620.c | 8 ++++++++ + 2 files changed, 9 insertions(+) + +diff --git a/arch/mips/include/asm/mach-ralink/mt7620.h b/arch/mips/include/asm/mach-ralink/mt7620.h +index 9809972..d469c69 100644 +--- a/arch/mips/include/asm/mach-ralink/mt7620.h ++++ b/arch/mips/include/asm/mach-ralink/mt7620.h +@@ -20,6 +20,7 @@ + #define SYSC_REG_CHIP_REV 0x0c + #define SYSC_REG_SYSTEM_CONFIG0 0x10 + #define SYSC_REG_SYSTEM_CONFIG1 0x14 ++#define SYSC_REG_CPU_SYS_CLKCFG 0x3c + #define SYSC_REG_CPLL_CONFIG0 0x54 + #define SYSC_REG_CPLL_CONFIG1 0x58 + +diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c +index 4956d96..d76eb85 100644 +--- a/arch/mips/ralink/mt7620.c ++++ b/arch/mips/ralink/mt7620.c +@@ -186,6 +186,14 @@ void __init ralink_clk_init(void) + ralink_clk_add("10000500.uart", 40000000); + ralink_clk_add("10000b00.spi", 40000000); + ralink_clk_add("10000c00.uartlite", 40000000); ++ ++#ifdef CONFIG_USB ++ /* ++ * When the CPU goes into sleep mode, the BUS clock will be too low for ++ * USB to function properly ++ */ ++ rt_sysc_m32(0x1f1f, 0x303, SYSC_REG_CPU_SYS_CLKCFG); ++#endif + } + + void __init ralink_of_remap(void) +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0164-Kbuild-add-missing-space.patch b/target/linux/ramips/patches-3.9/0164-Kbuild-add-missing-space.patch new file mode 100644 index 0000000000..5242d2081b --- /dev/null +++ b/target/linux/ramips/patches-3.9/0164-Kbuild-add-missing-space.patch @@ -0,0 +1,38 @@ +From 3630032605c18986555263eccc08b929e4c6d473 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 17 May 2013 01:03:45 +0200 +Subject: [PATCH 164/164] Kbuild: add missing space + +Currently the output looks like this: +DTC arch/mips/ralink/dts/mt7620a_eval.dtb +DTB arch/mips/ralink/dts/mt7620a_eval.dtb.S +AS arch/mips/ralink/dts/mt7620a_eval.dtb.o + +Whitespace error was introduced by initial commit +commit aab94339cd85d726abeae78fc02351fc1910e6a4 +Author: Dirk Brandewie +Date: Wed Dec 22 11:57:26 2010 -0800 + +of: Add support for linking device tree blobs into vmlinux + +Signed-off-by: John Crispin +--- + scripts/Makefile.lib | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib +index 07125e6..e13fff1 100644 +--- a/scripts/Makefile.lib ++++ b/scripts/Makefile.lib +@@ -251,7 +251,7 @@ cmd_gzip = (cat $(filter-out FORCE,$^) | gzip -n -f -9 > $@) || \ + # --------------------------------------------------------------------------- + + # Generate an assembly file to wrap the output of the device tree compiler +-quiet_cmd_dt_S_dtb= DTB $@ ++quiet_cmd_dt_S_dtb= DTB $@ + cmd_dt_S_dtb= \ + ( \ + echo '\#include '; \ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0200-owrt-GPIO-add-gpio_export_with_name.patch b/target/linux/ramips/patches-3.9/0200-owrt-GPIO-add-gpio_export_with_name.patch new file mode 100644 index 0000000000..74ef79b7d6 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0200-owrt-GPIO-add-gpio_export_with_name.patch @@ -0,0 +1,338 @@ +From 7d8a8b0e46a970fba505d967e93123e9729d97b5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 23 Jun 2013 00:16:22 +0200 +Subject: [PATCH] owrt: GPIO: add gpio_export_with_name + +http://lists.infradead.org/pipermail/linux-arm-kernel/2012-November/133856.html + +Signed-off-by: John Crispin +--- + Documentation/devicetree/bindings/gpio/gpio.txt | 60 ++++++++++++++++++++ + drivers/gpio/gpiolib-of.c | 68 +++++++++++++++++++++++ + drivers/gpio/gpiolib.c | 24 +++++--- + include/asm-generic/gpio.h | 6 +- + include/linux/gpio.h | 26 ++++++++- + 5 files changed, 172 insertions(+), 12 deletions(-) + +diff --git a/Documentation/devicetree/bindings/gpio/gpio.txt b/Documentation/devicetree/bindings/gpio/gpio.txt +index a336287..c2a9024 100644 +--- a/Documentation/devicetree/bindings/gpio/gpio.txt ++++ b/Documentation/devicetree/bindings/gpio/gpio.txt +@@ -112,3 +112,63 @@ where, + + The pinctrl node must have "#gpio-range-cells" property to show number of + arguments to pass with phandle from gpio controllers node. ++ ++3) gpio-export ++-------------- ++ ++gpio-export will allow you to automatically export gpio ++ ++required properties: ++- compatible: Should be "gpio-export" ++ ++in each child node will reprensent a gpio or if no name is specified ++a list of gpio to export ++ ++required properties: ++- gpios: gpio to export ++ ++optional properties: ++ - gpio-export,name: export name ++ - gpio-export,output: to set the as output with default value ++ if no present gpio as input ++ - pio-export,direction_may_change: boolean to allow the direction to be controllable ++ ++Example: ++ ++ ++gpio_export { ++ compatible = "gpio-export"; ++ #size-cells = <0>; ++ ++ in { ++ gpio-export,name = "in"; ++ gpios = <&pioC 20 0>; ++ }; ++ ++ out { ++ gpio-export,name = "out"; ++ gpio-export,output = <1>; ++ gpio-export,direction_may_change; ++ gpios = <&pioC 21 0>; ++ }; ++ ++ in_out { ++ gpio-export,name = "in_out"; ++ gpio-export,direction_may_change; ++ gpios = <&pioC 21 0>; ++ }; ++ ++ gpios_in { ++ gpios = <&pioB 0 0 ++ &pioB 3 0 ++ &pioC 4 0>; ++ gpio-export,direction_may_change; ++ }; ++ ++ gpios_out { ++ gpios = <&pioB 1 0 ++ &pioB 2 0 ++ &pioC 3 0>; ++ gpio-export,output = <1>; ++ }; ++}; +diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c +index 5150df6..195491b 100644 +--- a/drivers/gpio/gpiolib-of.c ++++ b/drivers/gpio/gpiolib-of.c +@@ -21,6 +21,8 @@ + #include + #include + #include ++#include ++#include + + /* Private data structure for of_gpiochip_find_and_xlate */ + struct gg_data { +@@ -253,3 +255,69 @@ void of_gpiochip_remove(struct gpio_chip *chip) + if (chip->of_node) + of_node_put(chip->of_node); + } ++ ++static struct of_device_id gpio_export_ids[] = { ++ { .compatible = "gpio-export" }, ++ { /* sentinel */ } ++}; ++ ++static int __init of_gpio_export_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device_node *cnp; ++ u32 val; ++ int nb = 0; ++ ++ for_each_child_of_node(np, cnp) { ++ const char *name = NULL; ++ int gpio; ++ bool dmc; ++ int max_gpio = 1; ++ int i; ++ ++ of_property_read_string(cnp, "gpio-export,name", &name); ++ ++ if (!name) ++ max_gpio = of_gpio_count(cnp); ++ ++ for (i = 0; i < max_gpio; i++) { ++ unsigned flags = 0; ++ enum of_gpio_flags of_flags; ++ ++ gpio = of_get_gpio_flags(cnp, i, &of_flags); ++ ++ if (of_flags == OF_GPIO_ACTIVE_LOW) ++ flags |= GPIOF_ACTIVE_LOW; ++ ++ if (!of_property_read_u32(cnp, "gpio-export,output", &val)) ++ flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; ++ else ++ flags |= GPIOF_IN; ++ ++ if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) ++ continue; ++ ++ dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); ++ gpio_export_with_name(gpio, dmc, name); ++ nb++; ++ } ++ } ++ ++ dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); ++ ++ return 0; ++} ++ ++static struct platform_driver gpio_export_driver = { ++ .driver = { ++ .name = "gpio-export", ++ .owner = THIS_MODULE, ++ .of_match_table = of_match_ptr(gpio_export_ids), ++ }, ++}; ++ ++static int __init of_gpio_export_init(void) ++{ ++ return platform_driver_probe(&gpio_export_driver, of_gpio_export_probe); ++} ++device_initcall(of_gpio_export_init); +diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c +index c2534d6..8697c82 100644 +--- a/drivers/gpio/gpiolib.c ++++ b/drivers/gpio/gpiolib.c +@@ -96,7 +96,7 @@ static int gpiod_get_value(const struct gpio_desc *desc); + static void gpiod_set_value(struct gpio_desc *desc, int value); + static int gpiod_cansleep(const struct gpio_desc *desc); + static int gpiod_to_irq(const struct gpio_desc *desc); +-static int gpiod_export(struct gpio_desc *desc, bool direction_may_change); ++static int gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); + static int gpiod_export_link(struct device *dev, const char *name, + struct gpio_desc *desc); + static int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value); +@@ -674,7 +674,7 @@ static ssize_t export_store(struct class *class, + status = -ENODEV; + goto done; + } +- status = gpiod_export(desc, true); ++ status = gpiod_export(desc, true, NULL); + if (status < 0) + gpiod_free(desc); + else +@@ -736,9 +736,10 @@ static struct class gpio_class = { + + + /** +- * gpio_export - export a GPIO through sysfs ++ * gpio_export_with_name - export a GPIO through sysfs + * @gpio: gpio to make available, already requested + * @direction_may_change: true if userspace may change gpio direction ++ * @name: gpio name + * Context: arch_initcall or later + * + * When drivers want to make a GPIO accessible to userspace after they +@@ -750,7 +751,7 @@ static struct class gpio_class = { + * + * Returns zero on success, else an error. + */ +-static int gpiod_export(struct gpio_desc *desc, bool direction_may_change) ++static int gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) + { + unsigned long flags; + int status; +@@ -783,6 +784,8 @@ static int gpiod_export(struct gpio_desc *desc, bool direction_may_change) + goto fail_unlock; + } + ++ if (name) ++ ioname = name; + if (!desc->chip->direction_input || !desc->chip->direction_output) + direction_may_change = false; + spin_unlock_irqrestore(&gpio_lock, flags); +@@ -829,11 +832,11 @@ fail_unlock: + return status; + } + +-int gpio_export(unsigned gpio, bool direction_may_change) ++int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) + { +- return gpiod_export(gpio_to_desc(gpio), direction_may_change); ++ return gpiod_export(gpio_to_desc(gpio), direction_may_change, name); + } +-EXPORT_SYMBOL_GPL(gpio_export); ++EXPORT_SYMBOL_GPL(gpio_export_with_name); + + static int match_export(struct device *dev, const void *data) + { +@@ -1092,7 +1095,7 @@ static inline void gpiochip_unexport(struct gpio_chip *chip) + } + + static inline int gpiod_export(struct gpio_desc *desc, +- bool direction_may_change) ++ bool direction_may_change, const char *name) + { + return -ENOSYS; + } +@@ -1521,6 +1524,9 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) + if (flags & GPIOF_OPEN_SOURCE) + set_bit(FLAG_OPEN_SOURCE, &desc->flags); + ++ if (flags & GPIOF_ACTIVE_LOW) ++ set_bit(FLAG_ACTIVE_LOW, &gpio_desc[gpio].flags); ++ + if (flags & GPIOF_DIR_IN) + err = gpiod_direction_input(desc); + else +@@ -1531,7 +1537,7 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) + goto free_gpio; + + if (flags & GPIOF_EXPORT) { +- err = gpiod_export(desc, flags & GPIOF_EXPORT_CHANGEABLE); ++ err = gpiod_export(desc, flags & GPIOF_EXPORT_CHANGEABLE, NULL); + if (err) + goto free_gpio; + } +diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h +index bde6469..3290572 100644 +--- a/include/asm-generic/gpio.h ++++ b/include/asm-generic/gpio.h +@@ -202,7 +202,8 @@ extern void gpio_free_array(const struct gpio *array, size_t num); + * A sysfs interface can be exported by individual drivers if they want, + * but more typically is configured entirely from userspace. + */ +-extern int gpio_export(unsigned gpio, bool direction_may_change); ++extern int gpio_export_with_name(unsigned gpio, bool direction_may_change, ++ const char *name); + extern int gpio_export_link(struct device *dev, const char *name, + unsigned gpio); + extern int gpio_sysfs_set_active_low(unsigned gpio, int value); +@@ -284,7 +285,8 @@ struct device; + + /* sysfs support is only available with gpiolib, where it's optional */ + +-static inline int gpio_export(unsigned gpio, bool direction_may_change) ++static inline int gpio_export_with_name(unsigned gpio, ++ bool direction_may_change, const char *name) + { + return -ENOSYS; + } +diff --git a/include/linux/gpio.h b/include/linux/gpio.h +index f6c7ae3..80a574a 100644 +--- a/include/linux/gpio.h ++++ b/include/linux/gpio.h +@@ -27,6 +27,9 @@ + #define GPIOF_EXPORT_DIR_FIXED (GPIOF_EXPORT) + #define GPIOF_EXPORT_DIR_CHANGEABLE (GPIOF_EXPORT | GPIOF_EXPORT_CHANGEABLE) + ++#define GPIOF_ACTIVE_LOW (1 << 6) ++ ++ + /** + * struct gpio - a structure describing a GPIO with configuration + * @gpio: the GPIO number +@@ -169,7 +172,8 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value) + WARN_ON(1); + } + +-static inline int gpio_export(unsigned gpio, bool direction_may_change) ++static inline int gpio_export_with_name(unsigned gpio, ++ bool direction_may_change, const char *name) + { + /* GPIO can never have been requested or set as {in,out}put */ + WARN_ON(1); +@@ -236,4 +240,24 @@ int devm_gpio_request_one(struct device *dev, unsigned gpio, + unsigned long flags, const char *label); + void devm_gpio_free(struct device *dev, unsigned int gpio); + ++/** ++ * gpio_export - export a GPIO through sysfs ++ * @gpio: gpio to make available, already requested ++ * @direction_may_change: true if userspace may change gpio direction ++ * Context: arch_initcall or later ++ * ++ * When drivers want to make a GPIO accessible to userspace after they ++ * have requested it -- perhaps while debugging, or as part of their ++ * public interface -- they may use this routine. If the GPIO can ++ * change direction (some can't) and the caller allows it, userspace ++ * will see "direction" sysfs attribute which may be used to change ++ * the gpio's direction. A "value" attribute will always be provided. ++ * ++ * Returns zero on success, else an error. ++ */ ++static inline int gpio_export(unsigned gpio,bool direction_may_change) ++{ ++ return gpio_export_with_name(gpio, direction_may_change, NULL); ++} ++ + #endif /* __LINUX_GPIO_H */ +-- +1.7.10.4 + diff --git a/target/linux/ramips/patches-3.9/0202-owrt-USB-adds-dwc_otg.patch b/target/linux/ramips/patches-3.9/0202-owrt-USB-adds-dwc_otg.patch new file mode 100644 index 0000000000..65ed512792 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0202-owrt-USB-adds-dwc_otg.patch @@ -0,0 +1,24517 @@ +From 1a44a003bdaf917193114d0d40534496c39644ba Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 15 Mar 2013 20:58:18 +0100 +Subject: [PATCH 202/208] owrt: USB: adds dwc_otg + +Signed-off-by: John Crispin +--- + drivers/usb/Kconfig | 2 + + drivers/usb/Makefile | 1 + + drivers/usb/dwc_otg/Kconfig | 24 + + drivers/usb/dwc_otg/Makefile | 25 + + drivers/usb/dwc_otg/dummy_audio.c | 1575 +++++++++++++ + drivers/usb/dwc_otg/dwc_otg_attr.c | 966 ++++++++ + drivers/usb/dwc_otg/dwc_otg_attr.h | 67 + + drivers/usb/dwc_otg/dwc_otg_cil.c | 3692 ++++++++++++++++++++++++++++++ + drivers/usb/dwc_otg/dwc_otg_cil.h | 1098 +++++++++ + drivers/usb/dwc_otg/dwc_otg_cil_intr.c | 750 ++++++ + drivers/usb/dwc_otg/dwc_otg_driver.c | 1273 ++++++++++ + drivers/usb/dwc_otg/dwc_otg_driver.h | 83 + + drivers/usb/dwc_otg/dwc_otg_hcd.c | 2852 +++++++++++++++++++++++ + drivers/usb/dwc_otg/dwc_otg_hcd.h | 668 ++++++ + drivers/usb/dwc_otg/dwc_otg_hcd_intr.c | 1873 +++++++++++++++ + drivers/usb/dwc_otg/dwc_otg_hcd_queue.c | 684 ++++++ + drivers/usb/dwc_otg/dwc_otg_pcd.c | 2523 ++++++++++++++++++++ + drivers/usb/dwc_otg/dwc_otg_pcd.h | 248 ++ + drivers/usb/dwc_otg/dwc_otg_pcd_intr.c | 3654 +++++++++++++++++++++++++++++ + drivers/usb/dwc_otg/dwc_otg_regs.h | 2075 +++++++++++++++++ + drivers/usb/dwc_otg/linux/dwc_otg_plat.h | 260 +++ + 21 files changed, 24393 insertions(+) + create mode 100644 drivers/usb/dwc_otg/Kconfig + create mode 100644 drivers/usb/dwc_otg/Makefile + create mode 100644 drivers/usb/dwc_otg/dummy_audio.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_attr.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_attr.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_cil.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_cil.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_cil_intr.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_driver.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_driver.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd_intr.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd_queue.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_pcd.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_pcd.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_pcd_intr.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_regs.h + create mode 100644 drivers/usb/dwc_otg/linux/dwc_otg_plat.h + +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -126,6 +126,8 @@ source "drivers/usb/core/Kconfig" + + source "drivers/usb/dwc3/Kconfig" + ++source "drivers/usb/dwc_otg/Kconfig" ++ + source "drivers/usb/mon/Kconfig" + + source "drivers/usb/wusbcore/Kconfig" +--- a/drivers/usb/Makefile ++++ b/drivers/usb/Makefile +@@ -9,6 +9,7 @@ obj-$(CONFIG_USB) += core/ + obj-$(CONFIG_USB_OTG_UTILS) += otg/ + + obj-$(CONFIG_USB_DWC3) += dwc3/ ++obj-$(CONFIG_DWC_OTG) += dwc_otg/ + + obj-$(CONFIG_USB_MON) += mon/ + +--- /dev/null ++++ b/drivers/usb/dwc_otg/Kconfig +@@ -0,0 +1,24 @@ ++config DWC_OTG ++ tristate "Ralink RT305X DWC_OTG support" ++ depends on SOC_RT305X ++ ---help--- ++ This driver supports Ralink DWC_OTG ++ ++choice ++ prompt "USB Operation Mode" ++ depends on DWC_OTG ++ default DWC_OTG_HOST_ONLY ++ ++config DWC_OTG_HOST_ONLY ++ bool "HOST ONLY MODE" ++ depends on DWC_OTG ++ ++config DWC_OTG_DEVICE_ONLY ++ bool "DEVICE ONLY MODE" ++ depends on DWC_OTG ++ ++endchoice ++ ++config DWC_OTG_DEBUG ++ bool "Enable debug mode" ++ depends on DWC_OTG +--- /dev/null ++++ b/drivers/usb/dwc_otg/Makefile +@@ -0,0 +1,25 @@ ++# ++# Makefile for DWC_otg Highspeed USB controller driver ++# ++ ++ifeq ($(CONFIG_DWC_OTG_DEBUG),y) ++EXTRA_CFLAGS += -DDEBUG ++endif ++ ++# Use one of the following flags to compile the software in host-only or ++# device-only mode. ++ifeq ($(CONFIG_DWC_OTG_HOST_ONLY),y) ++EXTRA_CFLAGS += -DDWC_HOST_ONLY ++EXTRA_CFLAGS += -DDWC_EN_ISOC ++endif ++ ++ifeq ($(CONFIG_DWC_OTG_DEVICE_ONLY),y) ++EXTRA_CFLAGS += -DDWC_DEVICE_ONLY ++endif ++ ++obj-$(CONFIG_DWC_OTG) := dwc_otg.o ++ ++dwc_otg-objs := dwc_otg_driver.o dwc_otg_attr.o ++dwc_otg-objs += dwc_otg_cil.o dwc_otg_cil_intr.o ++dwc_otg-objs += dwc_otg_pcd.o dwc_otg_pcd_intr.o ++dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o +--- /dev/null ++++ b/drivers/usb/dwc_otg/dummy_audio.c +@@ -0,0 +1,1575 @@ ++/* ++ * zero.c -- Gadget Zero, for USB development ++ * ++ * Copyright (C) 2003-2004 David Brownell ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++ ++/* ++ * Gadget Zero only needs two bulk endpoints, and is an example of how you ++ * can write a hardware-agnostic gadget driver running inside a USB device. ++ * ++ * Hardware details are visible (see CONFIG_USB_ZERO_* below) but don't ++ * affect most of the driver. ++ * ++ * Use it with the Linux host/master side "usbtest" driver to get a basic ++ * functional test of your device-side usb stack, or with "usb-skeleton". ++ * ++ * It supports two similar configurations. One sinks whatever the usb host ++ * writes, and in return sources zeroes. The other loops whatever the host ++ * writes back, so the host can read it. Module options include: ++ * ++ * buflen=N default N=4096, buffer size used ++ * qlen=N default N=32, how many buffers in the loopback queue ++ * loopdefault default false, list loopback config first ++ * ++ * Many drivers will only have one configuration, letting them be much ++ * simpler if they also don't support high speed operation (like this ++ * driver does). ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++# include ++#else ++# include ++#endif ++ ++#include ++ ++ ++/*-------------------------------------------------------------------------*/ ++/*-------------------------------------------------------------------------*/ ++ ++ ++static int utf8_to_utf16le(const char *s, u16 *cp, unsigned len) ++{ ++ int count = 0; ++ u8 c; ++ u16 uchar; ++ ++ /* this insists on correct encodings, though not minimal ones. ++ * BUT it currently rejects legit 4-byte UTF-8 code points, ++ * which need surrogate pairs. (Unicode 3.1 can use them.) ++ */ ++ while (len != 0 && (c = (u8) *s++) != 0) { ++ if (unlikely(c & 0x80)) { ++ // 2-byte sequence: ++ // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx ++ if ((c & 0xe0) == 0xc0) { ++ uchar = (c & 0x1f) << 6; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c; ++ ++ // 3-byte sequence (most CJKV characters): ++ // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx ++ } else if ((c & 0xf0) == 0xe0) { ++ uchar = (c & 0x0f) << 12; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c << 6; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c; ++ ++ /* no bogus surrogates */ ++ if (0xd800 <= uchar && uchar <= 0xdfff) ++ goto fail; ++ ++ // 4-byte sequence (surrogate pairs, currently rare): ++ // 11101110wwwwzzzzyy + 110111yyyyxxxxxx ++ // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx ++ // (uuuuu = wwww + 1) ++ // FIXME accept the surrogate code points (only) ++ ++ } else ++ goto fail; ++ } else ++ uchar = c; ++ put_unaligned (cpu_to_le16 (uchar), cp++); ++ count++; ++ len--; ++ } ++ return count; ++fail: ++ return -1; ++} ++ ++ ++/** ++ * usb_gadget_get_string - fill out a string descriptor ++ * @table: of c strings encoded using UTF-8 ++ * @id: string id, from low byte of wValue in get string descriptor ++ * @buf: at least 256 bytes ++ * ++ * Finds the UTF-8 string matching the ID, and converts it into a ++ * string descriptor in utf16-le. ++ * Returns length of descriptor (always even) or negative errno ++ * ++ * If your driver needs stings in multiple languages, you'll probably ++ * "switch (wIndex) { ... }" in your ep0 string descriptor logic, ++ * using this routine after choosing which set of UTF-8 strings to use. ++ * Note that US-ASCII is a strict subset of UTF-8; any string bytes with ++ * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 ++ * characters (which are also widely used in C strings). ++ */ ++int ++usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) ++{ ++ struct usb_string *s; ++ int len; ++ ++ /* descriptor 0 has the language id */ ++ if (id == 0) { ++ buf [0] = 4; ++ buf [1] = USB_DT_STRING; ++ buf [2] = (u8) table->language; ++ buf [3] = (u8) (table->language >> 8); ++ return 4; ++ } ++ for (s = table->strings; s && s->s; s++) ++ if (s->id == id) ++ break; ++ ++ /* unrecognized: stall. */ ++ if (!s || !s->s) ++ return -EINVAL; ++ ++ /* string descriptors have length, tag, then UTF16-LE text */ ++ len = min ((size_t) 126, strlen (s->s)); ++ memset (buf + 2, 0, 2 * len); /* zero all the bytes */ ++ len = utf8_to_utf16le(s->s, (u16 *)&buf[2], len); ++ if (len < 0) ++ return -EINVAL; ++ buf [0] = (len + 1) * 2; ++ buf [1] = USB_DT_STRING; ++ return buf [0]; ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++/*-------------------------------------------------------------------------*/ ++ ++ ++/** ++ * usb_descriptor_fillbuf - fill buffer with descriptors ++ * @buf: Buffer to be filled ++ * @buflen: Size of buf ++ * @src: Array of descriptor pointers, terminated by null pointer. ++ * ++ * Copies descriptors into the buffer, returning the length or a ++ * negative error code if they can't all be copied. Useful when ++ * assembling descriptors for an associated set of interfaces used ++ * as part of configuring a composite device; or in other cases where ++ * sets of descriptors need to be marshaled. ++ */ ++int ++usb_descriptor_fillbuf(void *buf, unsigned buflen, ++ const struct usb_descriptor_header **src) ++{ ++ u8 *dest = buf; ++ ++ if (!src) ++ return -EINVAL; ++ ++ /* fill buffer from src[] until null descriptor ptr */ ++ for (; 0 != *src; src++) { ++ unsigned len = (*src)->bLength; ++ ++ if (len > buflen) ++ return -EINVAL; ++ memcpy(dest, *src, len); ++ buflen -= len; ++ dest += len; ++ } ++ return dest - (u8 *)buf; ++} ++ ++ ++/** ++ * usb_gadget_config_buf - builts a complete configuration descriptor ++ * @config: Header for the descriptor, including characteristics such ++ * as power requirements and number of interfaces. ++ * @desc: Null-terminated vector of pointers to the descriptors (interface, ++ * endpoint, etc) defining all functions in this device configuration. ++ * @buf: Buffer for the resulting configuration descriptor. ++ * @length: Length of buffer. If this is not big enough to hold the ++ * entire configuration descriptor, an error code will be returned. ++ * ++ * This copies descriptors into the response buffer, building a descriptor ++ * for that configuration. It returns the buffer length or a negative ++ * status code. The config.wTotalLength field is set to match the length ++ * of the result, but other descriptor fields (including power usage and ++ * interface count) must be set by the caller. ++ * ++ * Gadget drivers could use this when constructing a config descriptor ++ * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the ++ * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed. ++ */ ++int usb_gadget_config_buf( ++ const struct usb_config_descriptor *config, ++ void *buf, ++ unsigned length, ++ const struct usb_descriptor_header **desc ++) ++{ ++ struct usb_config_descriptor *cp = buf; ++ int len; ++ ++ /* config descriptor first */ ++ if (length < USB_DT_CONFIG_SIZE || !desc) ++ return -EINVAL; ++ *cp = *config; ++ ++ /* then interface/endpoint/class/vendor/... */ ++ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, ++ length - USB_DT_CONFIG_SIZE, desc); ++ if (len < 0) ++ return len; ++ len += USB_DT_CONFIG_SIZE; ++ if (len > 0xffff) ++ return -EINVAL; ++ ++ /* patch up the config descriptor */ ++ cp->bLength = USB_DT_CONFIG_SIZE; ++ cp->bDescriptorType = USB_DT_CONFIG; ++ cp->wTotalLength = cpu_to_le16(len); ++ cp->bmAttributes |= USB_CONFIG_ATT_ONE; ++ return len; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/*-------------------------------------------------------------------------*/ ++ ++ ++#define RBUF_LEN (1024*1024) ++static int rbuf_start; ++static int rbuf_len; ++static __u8 rbuf[RBUF_LEN]; ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define DRIVER_VERSION "St Patrick's Day 2004" ++ ++static const char shortname [] = "zero"; ++static const char longname [] = "YAMAHA YST-MS35D USB Speaker "; ++ ++static const char source_sink [] = "source and sink data"; ++static const char loopback [] = "loop input to output"; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * driver assumes self-powered hardware, and ++ * has no way for users to trigger remote wakeup. ++ * ++ * this version autoconfigures as much as possible, ++ * which is reasonable for most "bulk-only" drivers. ++ */ ++static const char *EP_IN_NAME; /* source */ ++static const char *EP_OUT_NAME; /* sink */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* big enough to hold our biggest descriptor */ ++#define USB_BUFSIZ 512 ++ ++struct zero_dev { ++ spinlock_t lock; ++ struct usb_gadget *gadget; ++ struct usb_request *req; /* for control responses */ ++ ++ /* when configured, we have one of two configs: ++ * - source data (in to host) and sink it (out from host) ++ * - or loop it back (out from host back in to host) ++ */ ++ u8 config; ++ struct usb_ep *in_ep, *out_ep; ++ ++ /* autoresume timer */ ++ struct timer_list resume; ++}; ++ ++#define xprintk(d,level,fmt,args...) \ ++ dev_printk(level , &(d)->gadget->dev , fmt , ## args) ++ ++#ifdef DEBUG ++#define DBG(dev,fmt,args...) \ ++ xprintk(dev , KERN_DEBUG , fmt , ## args) ++#else ++#define DBG(dev,fmt,args...) \ ++ do { } while (0) ++#endif /* DEBUG */ ++ ++#ifdef VERBOSE ++#define VDBG DBG ++#else ++#define VDBG(dev,fmt,args...) \ ++ do { } while (0) ++#endif /* VERBOSE */ ++ ++#define ERROR(dev,fmt,args...) \ ++ xprintk(dev , KERN_ERR , fmt , ## args) ++#define WARN(dev,fmt,args...) \ ++ xprintk(dev , KERN_WARNING , fmt , ## args) ++#define INFO(dev,fmt,args...) \ ++ xprintk(dev , KERN_INFO , fmt , ## args) ++ ++/*-------------------------------------------------------------------------*/ ++ ++static unsigned buflen = 4096; ++static unsigned qlen = 32; ++static unsigned pattern = 0; ++ ++module_param (buflen, uint, S_IRUGO|S_IWUSR); ++module_param (qlen, uint, S_IRUGO|S_IWUSR); ++module_param (pattern, uint, S_IRUGO|S_IWUSR); ++ ++/* ++ * if it's nonzero, autoresume says how many seconds to wait ++ * before trying to wake up the host after suspend. ++ */ ++static unsigned autoresume = 0; ++module_param (autoresume, uint, 0); ++ ++/* ++ * Normally the "loopback" configuration is second (index 1) so ++ * it's not the default. Here's where to change that order, to ++ * work better with hosts where config changes are problematic. ++ * Or controllers (like superh) that only support one config. ++ */ ++static int loopdefault = 0; ++ ++module_param (loopdefault, bool, S_IRUGO|S_IWUSR); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Thanks to NetChip Technologies for donating this product ID. ++ * ++ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! ++ * Instead: allocate your own, using normal USB-IF procedures. ++ */ ++#ifndef CONFIG_USB_ZERO_HNPTEST ++#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */ ++#define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */ ++#else ++#define DRIVER_VENDOR_NUM 0x1a0a /* OTG test device IDs */ ++#define DRIVER_PRODUCT_NUM 0xbadd ++#endif ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * DESCRIPTORS ... most are static, but strings and (full) ++ * configuration descriptors are built on demand. ++ */ ++ ++/* ++#define STRING_MANUFACTURER 25 ++#define STRING_PRODUCT 42 ++#define STRING_SERIAL 101 ++*/ ++#define STRING_MANUFACTURER 1 ++#define STRING_PRODUCT 2 ++#define STRING_SERIAL 3 ++ ++#define STRING_SOURCE_SINK 250 ++#define STRING_LOOPBACK 251 ++ ++/* ++ * This device advertises two configurations; these numbers work ++ * on a pxa250 as well as more flexible hardware. ++ */ ++#define CONFIG_SOURCE_SINK 3 ++#define CONFIG_LOOPBACK 2 ++ ++/* ++static struct usb_device_descriptor ++device_desc = { ++ .bLength = sizeof device_desc, ++ .bDescriptorType = USB_DT_DEVICE, ++ ++ .bcdUSB = __constant_cpu_to_le16 (0x0200), ++ .bDeviceClass = USB_CLASS_VENDOR_SPEC, ++ ++ .idVendor = __constant_cpu_to_le16 (DRIVER_VENDOR_NUM), ++ .idProduct = __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM), ++ .iManufacturer = STRING_MANUFACTURER, ++ .iProduct = STRING_PRODUCT, ++ .iSerialNumber = STRING_SERIAL, ++ .bNumConfigurations = 2, ++}; ++*/ ++static struct usb_device_descriptor ++device_desc = { ++ .bLength = sizeof device_desc, ++ .bDescriptorType = USB_DT_DEVICE, ++ .bcdUSB = __constant_cpu_to_le16 (0x0100), ++ .bDeviceClass = USB_CLASS_PER_INTERFACE, ++ .bDeviceSubClass = 0, ++ .bDeviceProtocol = 0, ++ .bMaxPacketSize0 = 64, ++ .bcdDevice = __constant_cpu_to_le16 (0x0100), ++ .idVendor = __constant_cpu_to_le16 (0x0499), ++ .idProduct = __constant_cpu_to_le16 (0x3002), ++ .iManufacturer = STRING_MANUFACTURER, ++ .iProduct = STRING_PRODUCT, ++ .iSerialNumber = STRING_SERIAL, ++ .bNumConfigurations = 1, ++}; ++ ++static struct usb_config_descriptor ++z_config = { ++ .bLength = sizeof z_config, ++ .bDescriptorType = USB_DT_CONFIG, ++ ++ /* compute wTotalLength on the fly */ ++ .bNumInterfaces = 2, ++ .bConfigurationValue = 1, ++ .iConfiguration = 0, ++ .bmAttributes = 0x40, ++ .bMaxPower = 0, /* self-powered */ ++}; ++ ++ ++static struct usb_otg_descriptor ++otg_descriptor = { ++ .bLength = sizeof otg_descriptor, ++ .bDescriptorType = USB_DT_OTG, ++ ++ .bmAttributes = USB_OTG_SRP, ++}; ++ ++/* one interface in each configuration */ ++#ifdef CONFIG_USB_GADGET_DUALSPEED ++ ++/* ++ * usb 2.0 devices need to expose both high speed and full speed ++ * descriptors, unless they only run at full speed. ++ * ++ * that means alternate endpoint descriptors (bigger packets) ++ * and a "device qualifier" ... plus more construction options ++ * for the config descriptor. ++ */ ++ ++static struct usb_qualifier_descriptor ++dev_qualifier = { ++ .bLength = sizeof dev_qualifier, ++ .bDescriptorType = USB_DT_DEVICE_QUALIFIER, ++ ++ .bcdUSB = __constant_cpu_to_le16 (0x0200), ++ .bDeviceClass = USB_CLASS_VENDOR_SPEC, ++ ++ .bNumConfigurations = 2, ++}; ++ ++ ++struct usb_cs_as_general_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __u8 bDescriptorSubType; ++ __u8 bTerminalLink; ++ __u8 bDelay; ++ __u16 wFormatTag; ++} __attribute__ ((packed)); ++ ++struct usb_cs_as_format_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __u8 bDescriptorSubType; ++ __u8 bFormatType; ++ __u8 bNrChannels; ++ __u8 bSubframeSize; ++ __u8 bBitResolution; ++ __u8 bSamfreqType; ++ __u8 tLowerSamFreq[3]; ++ __u8 tUpperSamFreq[3]; ++} __attribute__ ((packed)); ++ ++static const struct usb_interface_descriptor ++z_audio_control_if_desc = { ++ .bLength = sizeof z_audio_control_if_desc, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0, ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 0, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = 0x1, ++ .bInterfaceProtocol = 0, ++ .iInterface = 0, ++}; ++ ++static const struct usb_interface_descriptor ++z_audio_if_desc = { ++ .bLength = sizeof z_audio_if_desc, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 1, ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 0, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = 0x2, ++ .bInterfaceProtocol = 0, ++ .iInterface = 0, ++}; ++ ++static const struct usb_interface_descriptor ++z_audio_if_desc2 = { ++ .bLength = sizeof z_audio_if_desc, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 1, ++ .bAlternateSetting = 1, ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = 0x2, ++ .bInterfaceProtocol = 0, ++ .iInterface = 0, ++}; ++ ++static const struct usb_cs_as_general_descriptor ++z_audio_cs_as_if_desc = { ++ .bLength = 7, ++ .bDescriptorType = 0x24, ++ ++ .bDescriptorSubType = 0x01, ++ .bTerminalLink = 0x01, ++ .bDelay = 0x0, ++ .wFormatTag = __constant_cpu_to_le16 (0x0001) ++}; ++ ++ ++static const struct usb_cs_as_format_descriptor ++z_audio_cs_as_format_desc = { ++ .bLength = 0xe, ++ .bDescriptorType = 0x24, ++ ++ .bDescriptorSubType = 2, ++ .bFormatType = 1, ++ .bNrChannels = 1, ++ .bSubframeSize = 1, ++ .bBitResolution = 8, ++ .bSamfreqType = 0, ++ .tLowerSamFreq = {0x7e, 0x13, 0x00}, ++ .tUpperSamFreq = {0xe2, 0xd6, 0x00}, ++}; ++ ++static const struct usb_endpoint_descriptor ++z_iso_ep = { ++ .bLength = 0x09, ++ .bDescriptorType = 0x05, ++ .bEndpointAddress = 0x04, ++ .bmAttributes = 0x09, ++ .wMaxPacketSize = 0x0038, ++ .bInterval = 0x01, ++ .bRefresh = 0x00, ++ .bSynchAddress = 0x00, ++}; ++ ++static char z_iso_ep2[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++// 9 bytes ++static char z_ac_interface_header_desc[] = ++{ 0x09, 0x24, 0x01, 0x00, 0x01, 0x2b, 0x00, 0x01, 0x01 }; ++ ++// 12 bytes ++static char z_0[] = {0x0c, 0x24, 0x02, 0x01, 0x01, 0x01, 0x00, 0x02, ++ 0x03, 0x00, 0x00, 0x00}; ++// 13 bytes ++static char z_1[] = {0x0d, 0x24, 0x06, 0x02, 0x01, 0x02, 0x15, 0x00, ++ 0x02, 0x00, 0x02, 0x00, 0x00}; ++// 9 bytes ++static char z_2[] = {0x09, 0x24, 0x03, 0x03, 0x01, 0x03, 0x00, 0x02, ++ 0x00}; ++ ++static char za_0[] = {0x09, 0x04, 0x01, 0x02, 0x01, 0x01, 0x02, 0x00, ++ 0x00}; ++ ++static char za_1[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00}; ++ ++static char za_2[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x01, 0x08, 0x00, ++ 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00}; ++ ++static char za_3[] = {0x09, 0x05, 0x04, 0x09, 0x70, 0x00, 0x01, 0x00, ++ 0x00}; ++ ++static char za_4[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++static char za_5[] = {0x09, 0x04, 0x01, 0x03, 0x01, 0x01, 0x02, 0x00, ++ 0x00}; ++ ++static char za_6[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00}; ++ ++static char za_7[] = {0x0e, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x00, ++ 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00}; ++ ++static char za_8[] = {0x09, 0x05, 0x04, 0x09, 0x70, 0x00, 0x01, 0x00, ++ 0x00}; ++ ++static char za_9[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++static char za_10[] = {0x09, 0x04, 0x01, 0x04, 0x01, 0x01, 0x02, 0x00, ++ 0x00}; ++ ++static char za_11[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00}; ++ ++static char za_12[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x02, 0x10, 0x00, ++ 0x73, 0x13, 0x00, 0xe2, 0xd6, 0x00}; ++ ++static char za_13[] = {0x09, 0x05, 0x04, 0x09, 0xe0, 0x00, 0x01, 0x00, ++ 0x00}; ++ ++static char za_14[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++static char za_15[] = {0x09, 0x04, 0x01, 0x05, 0x01, 0x01, 0x02, 0x00, ++ 0x00}; ++ ++static char za_16[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00}; ++ ++static char za_17[] = {0x0e, 0x24, 0x02, 0x01, 0x01, 0x03, 0x14, 0x00, ++ 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00}; ++ ++static char za_18[] = {0x09, 0x05, 0x04, 0x09, 0xa8, 0x00, 0x01, 0x00, ++ 0x00}; ++ ++static char za_19[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++static char za_20[] = {0x09, 0x04, 0x01, 0x06, 0x01, 0x01, 0x02, 0x00, ++ 0x00}; ++ ++static char za_21[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00}; ++ ++static char za_22[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x03, 0x14, 0x00, ++ 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00}; ++ ++static char za_23[] = {0x09, 0x05, 0x04, 0x09, 0x50, 0x01, 0x01, 0x00, ++ 0x00}; ++ ++static char za_24[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++ ++ ++static const struct usb_descriptor_header *z_function [] = { ++ (struct usb_descriptor_header *) &z_audio_control_if_desc, ++ (struct usb_descriptor_header *) &z_ac_interface_header_desc, ++ (struct usb_descriptor_header *) &z_0, ++ (struct usb_descriptor_header *) &z_1, ++ (struct usb_descriptor_header *) &z_2, ++ (struct usb_descriptor_header *) &z_audio_if_desc, ++ (struct usb_descriptor_header *) &z_audio_if_desc2, ++ (struct usb_descriptor_header *) &z_audio_cs_as_if_desc, ++ (struct usb_descriptor_header *) &z_audio_cs_as_format_desc, ++ (struct usb_descriptor_header *) &z_iso_ep, ++ (struct usb_descriptor_header *) &z_iso_ep2, ++ (struct usb_descriptor_header *) &za_0, ++ (struct usb_descriptor_header *) &za_1, ++ (struct usb_descriptor_header *) &za_2, ++ (struct usb_descriptor_header *) &za_3, ++ (struct usb_descriptor_header *) &za_4, ++ (struct usb_descriptor_header *) &za_5, ++ (struct usb_descriptor_header *) &za_6, ++ (struct usb_descriptor_header *) &za_7, ++ (struct usb_descriptor_header *) &za_8, ++ (struct usb_descriptor_header *) &za_9, ++ (struct usb_descriptor_header *) &za_10, ++ (struct usb_descriptor_header *) &za_11, ++ (struct usb_descriptor_header *) &za_12, ++ (struct usb_descriptor_header *) &za_13, ++ (struct usb_descriptor_header *) &za_14, ++ (struct usb_descriptor_header *) &za_15, ++ (struct usb_descriptor_header *) &za_16, ++ (struct usb_descriptor_header *) &za_17, ++ (struct usb_descriptor_header *) &za_18, ++ (struct usb_descriptor_header *) &za_19, ++ (struct usb_descriptor_header *) &za_20, ++ (struct usb_descriptor_header *) &za_21, ++ (struct usb_descriptor_header *) &za_22, ++ (struct usb_descriptor_header *) &za_23, ++ (struct usb_descriptor_header *) &za_24, ++ NULL, ++}; ++ ++/* maxpacket and other transfer characteristics vary by speed. */ ++#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs)) ++ ++#else ++ ++/* if there's no high speed support, maxpacket doesn't change. */ ++#define ep_desc(g,hs,fs) fs ++ ++#endif /* !CONFIG_USB_GADGET_DUALSPEED */ ++ ++static char manufacturer [40]; ++//static char serial [40]; ++static char serial [] = "Ser 00 em"; ++ ++/* static strings, in UTF-8 */ ++static struct usb_string strings [] = { ++ { STRING_MANUFACTURER, manufacturer, }, ++ { STRING_PRODUCT, longname, }, ++ { STRING_SERIAL, serial, }, ++ { STRING_LOOPBACK, loopback, }, ++ { STRING_SOURCE_SINK, source_sink, }, ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings, ++}; ++ ++/* ++ * config descriptors are also handcrafted. these must agree with code ++ * that sets configurations, and with code managing interfaces and their ++ * altsettings. other complexity may come from: ++ * ++ * - high speed support, including "other speed config" rules ++ * - multiple configurations ++ * - interfaces with alternate settings ++ * - embedded class or vendor-specific descriptors ++ * ++ * this handles high speed, and has a second config that could as easily ++ * have been an alternate interface setting (on most hardware). ++ * ++ * NOTE: to demonstrate (and test) more USB capabilities, this driver ++ * should include an altsetting to test interrupt transfers, including ++ * high bandwidth modes at high speed. (Maybe work like Intel's test ++ * device?) ++ */ ++static int ++config_buf (struct usb_gadget *gadget, u8 *buf, u8 type, unsigned index) ++{ ++ int len; ++ const struct usb_descriptor_header **function; ++ ++ function = z_function; ++ len = usb_gadget_config_buf (&z_config, buf, USB_BUFSIZ, function); ++ if (len < 0) ++ return len; ++ ((struct usb_config_descriptor *) buf)->bDescriptorType = type; ++ return len; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_request * ++alloc_ep_req (struct usb_ep *ep, unsigned length) ++{ ++ struct usb_request *req; ++ ++ req = usb_ep_alloc_request (ep, GFP_ATOMIC); ++ if (req) { ++ req->length = length; ++ req->buf = usb_ep_alloc_buffer (ep, length, ++ &req->dma, GFP_ATOMIC); ++ if (!req->buf) { ++ usb_ep_free_request (ep, req); ++ req = NULL; ++ } ++ } ++ return req; ++} ++ ++static void free_ep_req (struct usb_ep *ep, struct usb_request *req) ++{ ++ if (req->buf) ++ usb_ep_free_buffer (ep, req->buf, req->dma, req->length); ++ usb_ep_free_request (ep, req); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* optionally require specific source/sink data patterns */ ++ ++static int ++check_read_data ( ++ struct zero_dev *dev, ++ struct usb_ep *ep, ++ struct usb_request *req ++) ++{ ++ unsigned i; ++ u8 *buf = req->buf; ++ ++ for (i = 0; i < req->actual; i++, buf++) { ++ switch (pattern) { ++ /* all-zeroes has no synchronization issues */ ++ case 0: ++ if (*buf == 0) ++ continue; ++ break; ++ /* mod63 stays in sync with short-terminated transfers, ++ * or otherwise when host and gadget agree on how large ++ * each usb transfer request should be. resync is done ++ * with set_interface or set_config. ++ */ ++ case 1: ++ if (*buf == (u8)(i % 63)) ++ continue; ++ break; ++ } ++ ERROR (dev, "bad OUT byte, buf [%d] = %d\n", i, *buf); ++ usb_ep_set_halt (ep); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void zero_reset_config (struct zero_dev *dev) ++{ ++ if (dev->config == 0) ++ return; ++ ++ DBG (dev, "reset config\n"); ++ ++ /* just disable endpoints, forcing completion of pending i/o. ++ * all our completion handlers free their requests in this case. ++ */ ++ if (dev->in_ep) { ++ usb_ep_disable (dev->in_ep); ++ dev->in_ep = NULL; ++ } ++ if (dev->out_ep) { ++ usb_ep_disable (dev->out_ep); ++ dev->out_ep = NULL; ++ } ++ dev->config = 0; ++ del_timer (&dev->resume); ++} ++ ++#define _write(f, buf, sz) (f->f_op->write(f, buf, sz, &f->f_pos)) ++ ++static void ++zero_isoc_complete (struct usb_ep *ep, struct usb_request *req) ++{ ++ struct zero_dev *dev = ep->driver_data; ++ int status = req->status; ++ int i, j; ++ ++ switch (status) { ++ ++ case 0: /* normal completion? */ ++ //printk ("\nzero ---------------> isoc normal completion %d bytes\n", req->actual); ++ for (i=0, j=rbuf_start; iactual; i++) { ++ //printk ("%02x ", ((__u8*)req->buf)[i]); ++ rbuf[j] = ((__u8*)req->buf)[i]; ++ j++; ++ if (j >= RBUF_LEN) j=0; ++ } ++ rbuf_start = j; ++ //printk ("\n\n"); ++ ++ if (rbuf_len < RBUF_LEN) { ++ rbuf_len += req->actual; ++ if (rbuf_len > RBUF_LEN) { ++ rbuf_len = RBUF_LEN; ++ } ++ } ++ ++ break; ++ ++ /* this endpoint is normally active while we're configured */ ++ case -ECONNABORTED: /* hardware forced ep reset */ ++ case -ECONNRESET: /* request dequeued */ ++ case -ESHUTDOWN: /* disconnect from host */ ++ VDBG (dev, "%s gone (%d), %d/%d\n", ep->name, status, ++ req->actual, req->length); ++ if (ep == dev->out_ep) ++ check_read_data (dev, ep, req); ++ free_ep_req (ep, req); ++ return; ++ ++ case -EOVERFLOW: /* buffer overrun on read means that ++ * we didn't provide a big enough ++ * buffer. ++ */ ++ default: ++#if 1 ++ DBG (dev, "%s complete --> %d, %d/%d\n", ep->name, ++ status, req->actual, req->length); ++#endif ++ case -EREMOTEIO: /* short read */ ++ break; ++ } ++ ++ status = usb_ep_queue (ep, req, GFP_ATOMIC); ++ if (status) { ++ ERROR (dev, "kill %s: resubmit %d bytes --> %d\n", ++ ep->name, req->length, status); ++ usb_ep_set_halt (ep); ++ /* FIXME recover later ... somehow */ ++ } ++} ++ ++static struct usb_request * ++zero_start_isoc_ep (struct usb_ep *ep, int gfp_flags) ++{ ++ struct usb_request *req; ++ int status; ++ ++ req = alloc_ep_req (ep, 512); ++ if (!req) ++ return NULL; ++ ++ req->complete = zero_isoc_complete; ++ ++ status = usb_ep_queue (ep, req, gfp_flags); ++ if (status) { ++ struct zero_dev *dev = ep->driver_data; ++ ++ ERROR (dev, "start %s --> %d\n", ep->name, status); ++ free_ep_req (ep, req); ++ req = NULL; ++ } ++ ++ return req; ++} ++ ++/* change our operational config. this code must agree with the code ++ * that returns config descriptors, and altsetting code. ++ * ++ * it's also responsible for power management interactions. some ++ * configurations might not work with our current power sources. ++ * ++ * note that some device controller hardware will constrain what this ++ * code can do, perhaps by disallowing more than one configuration or ++ * by limiting configuration choices (like the pxa2xx). ++ */ ++static int ++zero_set_config (struct zero_dev *dev, unsigned number, int gfp_flags) ++{ ++ int result = 0; ++ struct usb_gadget *gadget = dev->gadget; ++ const struct usb_endpoint_descriptor *d; ++ struct usb_ep *ep; ++ ++ if (number == dev->config) ++ return 0; ++ ++ zero_reset_config (dev); ++ ++ gadget_for_each_ep (ep, gadget) { ++ ++ if (strcmp (ep->name, "ep4") == 0) { ++ ++ d = (struct usb_endpoint_descripter *)&za_23; // isoc ep desc for audio i/f alt setting 6 ++ result = usb_ep_enable (ep, d); ++ ++ if (result == 0) { ++ ep->driver_data = dev; ++ dev->in_ep = ep; ++ ++ if (zero_start_isoc_ep (ep, gfp_flags) != 0) { ++ ++ dev->in_ep = ep; ++ continue; ++ } ++ ++ usb_ep_disable (ep); ++ result = -EIO; ++ } ++ } ++ ++ } ++ ++ dev->config = number; ++ return result; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void zero_setup_complete (struct usb_ep *ep, struct usb_request *req) ++{ ++ if (req->status || req->actual != req->length) ++ DBG ((struct zero_dev *) ep->driver_data, ++ "setup complete --> %d, %d/%d\n", ++ req->status, req->actual, req->length); ++} ++ ++/* ++ * The setup() callback implements all the ep0 functionality that's ++ * not handled lower down, in hardware or the hardware driver (like ++ * device and endpoint feature flags, and their status). It's all ++ * housekeeping for the gadget function we're implementing. Most of ++ * the work is in config-specific setup. ++ */ ++static int ++zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ++{ ++ struct zero_dev *dev = get_gadget_data (gadget); ++ struct usb_request *req = dev->req; ++ int value = -EOPNOTSUPP; ++ ++ /* usually this stores reply data in the pre-allocated ep0 buffer, ++ * but config change events will reconfigure hardware. ++ */ ++ req->zero = 0; ++ switch (ctrl->bRequest) { ++ ++ case USB_REQ_GET_DESCRIPTOR: ++ ++ switch (ctrl->wValue >> 8) { ++ ++ case USB_DT_DEVICE: ++ value = min (ctrl->wLength, (u16) sizeof device_desc); ++ memcpy (req->buf, &device_desc, value); ++ break; ++#ifdef CONFIG_USB_GADGET_DUALSPEED ++ case USB_DT_DEVICE_QUALIFIER: ++ if (!gadget->is_dualspeed) ++ break; ++ value = min (ctrl->wLength, (u16) sizeof dev_qualifier); ++ memcpy (req->buf, &dev_qualifier, value); ++ break; ++ ++ case USB_DT_OTHER_SPEED_CONFIG: ++ if (!gadget->is_dualspeed) ++ break; ++ // FALLTHROUGH ++#endif /* CONFIG_USB_GADGET_DUALSPEED */ ++ case USB_DT_CONFIG: ++ value = config_buf (gadget, req->buf, ++ ctrl->wValue >> 8, ++ ctrl->wValue & 0xff); ++ if (value >= 0) ++ value = min (ctrl->wLength, (u16) value); ++ break; ++ ++ case USB_DT_STRING: ++ /* wIndex == language code. ++ * this driver only handles one language, you can ++ * add string tables for other languages, using ++ * any UTF-8 characters ++ */ ++ value = usb_gadget_get_string (&stringtab, ++ ctrl->wValue & 0xff, req->buf); ++ if (value >= 0) { ++ value = min (ctrl->wLength, (u16) value); ++ } ++ break; ++ } ++ break; ++ ++ /* currently two configs, two speeds */ ++ case USB_REQ_SET_CONFIGURATION: ++ if (ctrl->bRequestType != 0) ++ goto unknown; ++ ++ spin_lock (&dev->lock); ++ value = zero_set_config (dev, ctrl->wValue, GFP_ATOMIC); ++ spin_unlock (&dev->lock); ++ break; ++ case USB_REQ_GET_CONFIGURATION: ++ if (ctrl->bRequestType != USB_DIR_IN) ++ goto unknown; ++ *(u8 *)req->buf = dev->config; ++ value = min (ctrl->wLength, (u16) 1); ++ break; ++ ++ /* until we add altsetting support, or other interfaces, ++ * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) ++ * and already killed pending endpoint I/O. ++ */ ++ case USB_REQ_SET_INTERFACE: ++ ++ if (ctrl->bRequestType != USB_RECIP_INTERFACE) ++ goto unknown; ++ spin_lock (&dev->lock); ++ if (dev->config) { ++ u8 config = dev->config; ++ ++ /* resets interface configuration, forgets about ++ * previous transaction state (queued bufs, etc) ++ * and re-inits endpoint state (toggle etc) ++ * no response queued, just zero status == success. ++ * if we had more than one interface we couldn't ++ * use this "reset the config" shortcut. ++ */ ++ zero_reset_config (dev); ++ zero_set_config (dev, config, GFP_ATOMIC); ++ value = 0; ++ } ++ spin_unlock (&dev->lock); ++ break; ++ case USB_REQ_GET_INTERFACE: ++ if ((ctrl->bRequestType == 0x21) && (ctrl->wIndex == 0x02)) { ++ value = ctrl->wLength; ++ break; ++ } ++ else { ++ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) ++ goto unknown; ++ if (!dev->config) ++ break; ++ if (ctrl->wIndex != 0) { ++ value = -EDOM; ++ break; ++ } ++ *(u8 *)req->buf = 0; ++ value = min (ctrl->wLength, (u16) 1); ++ } ++ break; ++ ++ /* ++ * These are the same vendor-specific requests supported by ++ * Intel's USB 2.0 compliance test devices. We exceed that ++ * device spec by allowing multiple-packet requests. ++ */ ++ case 0x5b: /* control WRITE test -- fill the buffer */ ++ if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) ++ goto unknown; ++ if (ctrl->wValue || ctrl->wIndex) ++ break; ++ /* just read that many bytes into the buffer */ ++ if (ctrl->wLength > USB_BUFSIZ) ++ break; ++ value = ctrl->wLength; ++ break; ++ case 0x5c: /* control READ test -- return the buffer */ ++ if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) ++ goto unknown; ++ if (ctrl->wValue || ctrl->wIndex) ++ break; ++ /* expect those bytes are still in the buffer; send back */ ++ if (ctrl->wLength > USB_BUFSIZ ++ || ctrl->wLength != req->length) ++ break; ++ value = ctrl->wLength; ++ break; ++ ++ case 0x01: // SET_CUR ++ case 0x02: ++ case 0x03: ++ case 0x04: ++ case 0x05: ++ value = ctrl->wLength; ++ break; ++ case 0x81: ++ switch (ctrl->wValue) { ++ case 0x0201: ++ case 0x0202: ++ ((u8*)req->buf)[0] = 0x00; ++ ((u8*)req->buf)[1] = 0xe3; ++ break; ++ case 0x0300: ++ case 0x0500: ++ ((u8*)req->buf)[0] = 0x00; ++ break; ++ } ++ //((u8*)req->buf)[0] = 0x81; ++ //((u8*)req->buf)[1] = 0x81; ++ value = ctrl->wLength; ++ break; ++ case 0x82: ++ switch (ctrl->wValue) { ++ case 0x0201: ++ case 0x0202: ++ ((u8*)req->buf)[0] = 0x00; ++ ((u8*)req->buf)[1] = 0xc3; ++ break; ++ case 0x0300: ++ case 0x0500: ++ ((u8*)req->buf)[0] = 0x00; ++ break; ++ } ++ //((u8*)req->buf)[0] = 0x82; ++ //((u8*)req->buf)[1] = 0x82; ++ value = ctrl->wLength; ++ break; ++ case 0x83: ++ switch (ctrl->wValue) { ++ case 0x0201: ++ case 0x0202: ++ ((u8*)req->buf)[0] = 0x00; ++ ((u8*)req->buf)[1] = 0x00; ++ break; ++ case 0x0300: ++ ((u8*)req->buf)[0] = 0x60; ++ break; ++ case 0x0500: ++ ((u8*)req->buf)[0] = 0x18; ++ break; ++ } ++ //((u8*)req->buf)[0] = 0x83; ++ //((u8*)req->buf)[1] = 0x83; ++ value = ctrl->wLength; ++ break; ++ case 0x84: ++ switch (ctrl->wValue) { ++ case 0x0201: ++ case 0x0202: ++ ((u8*)req->buf)[0] = 0x00; ++ ((u8*)req->buf)[1] = 0x01; ++ break; ++ case 0x0300: ++ case 0x0500: ++ ((u8*)req->buf)[0] = 0x08; ++ break; ++ } ++ //((u8*)req->buf)[0] = 0x84; ++ //((u8*)req->buf)[1] = 0x84; ++ value = ctrl->wLength; ++ break; ++ case 0x85: ++ ((u8*)req->buf)[0] = 0x85; ++ ((u8*)req->buf)[1] = 0x85; ++ value = ctrl->wLength; ++ break; ++ ++ ++ default: ++unknown: ++ printk("unknown control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ ctrl->wValue, ctrl->wIndex, ctrl->wLength); ++ } ++ ++ /* respond with data transfer before status phase? */ ++ if (value >= 0) { ++ req->length = value; ++ req->zero = value < ctrl->wLength ++ && (value % gadget->ep0->maxpacket) == 0; ++ value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) { ++ DBG (dev, "ep_queue < 0 --> %d\n", value); ++ req->status = 0; ++ zero_setup_complete (gadget->ep0, req); ++ } ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++static void ++zero_disconnect (struct usb_gadget *gadget) ++{ ++ struct zero_dev *dev = get_gadget_data (gadget); ++ unsigned long flags; ++ ++ spin_lock_irqsave (&dev->lock, flags); ++ zero_reset_config (dev); ++ ++ /* a more significant application might have some non-usb ++ * activities to quiesce here, saving resources like power ++ * or pushing the notification up a network stack. ++ */ ++ spin_unlock_irqrestore (&dev->lock, flags); ++ ++ /* next we may get setup() calls to enumerate new connections; ++ * or an unbind() during shutdown (including removing module). ++ */ ++} ++ ++static void ++zero_autoresume (unsigned long _dev) ++{ ++ struct zero_dev *dev = (struct zero_dev *) _dev; ++ int status; ++ ++ /* normally the host would be woken up for something ++ * more significant than just a timer firing... ++ */ ++ if (dev->gadget->speed != USB_SPEED_UNKNOWN) { ++ status = usb_gadget_wakeup (dev->gadget); ++ DBG (dev, "wakeup --> %d\n", status); ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void ++zero_unbind (struct usb_gadget *gadget) ++{ ++ struct zero_dev *dev = get_gadget_data (gadget); ++ ++ DBG (dev, "unbind\n"); ++ ++ /* we've already been disconnected ... no i/o is active */ ++ if (dev->req) ++ free_ep_req (gadget->ep0, dev->req); ++ del_timer_sync (&dev->resume); ++ kfree (dev); ++ set_gadget_data (gadget, NULL); ++} ++ ++static int ++zero_bind (struct usb_gadget *gadget) ++{ ++ struct zero_dev *dev; ++ //struct usb_ep *ep; ++ ++ printk("binding\n"); ++ /* ++ * DRIVER POLICY CHOICE: you may want to do this differently. ++ * One thing to avoid is reusing a bcdDevice revision code ++ * with different host-visible configurations or behavior ++ * restrictions -- using ep1in/ep2out vs ep1out/ep3in, etc ++ */ ++ //device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201); ++ ++ ++ /* ok, we made sense of the hardware ... */ ++ dev = kmalloc (sizeof *dev, SLAB_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ memset (dev, 0, sizeof *dev); ++ spin_lock_init (&dev->lock); ++ dev->gadget = gadget; ++ set_gadget_data (gadget, dev); ++ ++ /* preallocate control response and buffer */ ++ dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); ++ if (!dev->req) ++ goto enomem; ++ dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ, ++ &dev->req->dma, GFP_KERNEL); ++ if (!dev->req->buf) ++ goto enomem; ++ ++ dev->req->complete = zero_setup_complete; ++ ++ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; ++ ++#ifdef CONFIG_USB_GADGET_DUALSPEED ++ /* assume ep0 uses the same value for both speeds ... */ ++ dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; ++ ++ /* and that all endpoints are dual-speed */ ++ //hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; ++ //hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; ++#endif ++ ++ usb_gadget_set_selfpowered (gadget); ++ ++ init_timer (&dev->resume); ++ dev->resume.function = zero_autoresume; ++ dev->resume.data = (unsigned long) dev; ++ ++ gadget->ep0->driver_data = dev; ++ ++ INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname); ++ INFO (dev, "using %s, OUT %s IN %s\n", gadget->name, ++ EP_OUT_NAME, EP_IN_NAME); ++ ++ snprintf (manufacturer, sizeof manufacturer, ++ UTS_SYSNAME " " UTS_RELEASE " with %s", ++ gadget->name); ++ ++ return 0; ++ ++enomem: ++ zero_unbind (gadget); ++ return -ENOMEM; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void ++zero_suspend (struct usb_gadget *gadget) ++{ ++ struct zero_dev *dev = get_gadget_data (gadget); ++ ++ if (gadget->speed == USB_SPEED_UNKNOWN) ++ return; ++ ++ if (autoresume) { ++ mod_timer (&dev->resume, jiffies + (HZ * autoresume)); ++ DBG (dev, "suspend, wakeup in %d seconds\n", autoresume); ++ } else ++ DBG (dev, "suspend\n"); ++} ++ ++static void ++zero_resume (struct usb_gadget *gadget) ++{ ++ struct zero_dev *dev = get_gadget_data (gadget); ++ ++ DBG (dev, "resume\n"); ++ del_timer (&dev->resume); ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_gadget_driver zero_driver = { ++#ifdef CONFIG_USB_GADGET_DUALSPEED ++ .speed = USB_SPEED_HIGH, ++#else ++ .speed = USB_SPEED_FULL, ++#endif ++ .function = (char *) longname, ++ .bind = zero_bind, ++ .unbind = zero_unbind, ++ ++ .setup = zero_setup, ++ .disconnect = zero_disconnect, ++ ++ .suspend = zero_suspend, ++ .resume = zero_resume, ++ ++ .driver = { ++ .name = (char *) shortname, ++ // .shutdown = ... ++ // .suspend = ... ++ // .resume = ... ++ }, ++}; ++ ++MODULE_AUTHOR ("David Brownell"); ++MODULE_LICENSE ("Dual BSD/GPL"); ++ ++static struct proc_dir_entry *pdir, *pfile; ++ ++static int isoc_read_data (char *page, char **start, ++ off_t off, int count, ++ int *eof, void *data) ++{ ++ int i; ++ static int c = 0; ++ static int done = 0; ++ static int s = 0; ++ ++/* ++ printk ("\ncount: %d\n", count); ++ printk ("rbuf_start: %d\n", rbuf_start); ++ printk ("rbuf_len: %d\n", rbuf_len); ++ printk ("off: %d\n", off); ++ printk ("start: %p\n\n", *start); ++*/ ++ if (done) { ++ c = 0; ++ done = 0; ++ *eof = 1; ++ return 0; ++ } ++ ++ if (c == 0) { ++ if (rbuf_len == RBUF_LEN) ++ s = rbuf_start; ++ else s = 0; ++ } ++ ++ for (i=0; i= rbuf_len) { ++ *eof = 1; ++ done = 1; ++ } ++ ++ ++ return i; ++} ++ ++static int __init init (void) ++{ ++ ++ int retval = 0; ++ ++ pdir = proc_mkdir("isoc_test", NULL); ++ if(pdir == NULL) { ++ retval = -ENOMEM; ++ printk("Error creating dir\n"); ++ goto done; ++ } ++ pdir->owner = THIS_MODULE; ++ ++ pfile = create_proc_read_entry("isoc_data", ++ 0444, pdir, ++ isoc_read_data, ++ NULL); ++ if (pfile == NULL) { ++ retval = -ENOMEM; ++ printk("Error creating file\n"); ++ goto no_file; ++ } ++ pfile->owner = THIS_MODULE; ++ ++ return usb_gadget_register_driver (&zero_driver); ++ ++ no_file: ++ remove_proc_entry("isoc_data", NULL); ++ done: ++ return retval; ++} ++module_init (init); ++ ++static void __exit cleanup (void) ++{ ++ ++ usb_gadget_unregister_driver (&zero_driver); ++ ++ remove_proc_entry("isoc_data", pdir); ++ remove_proc_entry("isoc_test", NULL); ++} ++module_exit (cleanup); +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_attr.c +@@ -0,0 +1,966 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.c $ ++ * $Revision: 1.2 $ ++ * $Date: 2008-11-21 05:39:15 $ ++ * $Change: 1064918 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The diagnostic interface will provide access to the controller for ++ * bringing up the hardware and testing. The Linux driver attributes ++ * feature will be used to provide the Linux Diagnostic ++ * Interface. These attributes are accessed through sysfs. ++ */ ++ ++/** @page "Linux Module Attributes" ++ * ++ * The Linux module attributes feature is used to provide the Linux ++ * Diagnostic Interface. These attributes are accessed through sysfs. ++ * The diagnostic interface will provide access to the controller for ++ * bringing up the hardware and testing. ++ ++ ++ The following table shows the attributes. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++
Name Description Access
mode Returns the current mode: 0 for device mode, 1 for host mode Read
hnpcapable Gets or sets the "HNP-capable" bit in the Core USB Configuraton Register. ++ Read returns the current value. Read/Write
srpcapable Gets or sets the "SRP-capable" bit in the Core USB Configuraton Register. ++ Read returns the current value. Read/Write
hnp Initiates the Host Negotiation Protocol. Read returns the status. Read/Write
srp Initiates the Session Request Protocol. Read returns the status. Read/Write
buspower Gets or sets the Power State of the bus (0 - Off or 1 - On) Read/Write
bussuspend Suspends the USB bus. Read/Write
busconnected Gets the connection status of the bus Read
gotgctl Gets or sets the Core Control Status Register. Read/Write
gusbcfg Gets or sets the Core USB Configuration Register Read/Write
grxfsiz Gets or sets the Receive FIFO Size Register Read/Write
gnptxfsiz Gets or sets the non-periodic Transmit Size Register Read/Write
gpvndctl Gets or sets the PHY Vendor Control Register Read/Write
ggpio Gets the value in the lower 16-bits of the General Purpose IO Register ++ or sets the upper 16 bits. Read/Write
guid Gets or sets the value of the User ID Register Read/Write
gsnpsid Gets the value of the Synopsys ID Regester Read
devspeed Gets or sets the device speed setting in the DCFG register Read/Write
enumspeed Gets the device enumeration Speed. Read
hptxfsiz Gets the value of the Host Periodic Transmit FIFO Read
hprt0 Gets or sets the value in the Host Port Control and Status Register Read/Write
regoffset Sets the register offset for the next Register Access Read/Write
regvalue Gets or sets the value of the register at the offset in the regoffset attribute. Read/Write
remote_wakeup On read, shows the status of Remote Wakeup. On write, initiates a remote ++ wakeup of the host. When bit 0 is 1 and Remote Wakeup is enabled, the Remote ++ Wakeup signalling bit in the Device Control Register is set for 1 ++ milli-second. Read/Write
regdump Dumps the contents of core registers. Read
spramdump Dumps the contents of core registers. Read
hcddump Dumps the current HCD state. Read
hcd_frrem Shows the average value of the Frame Remaining ++ field in the Host Frame Number/Frame Remaining register when an SOF interrupt ++ occurs. This can be used to determine the average interrupt latency. Also ++ shows the average Frame Remaining value for start_transfer and the "a" and ++ "b" sample points. The "a" and "b" sample points may be used during debugging ++ bto determine how long it takes to execute a section of the HCD code. Read
rd_reg_test Displays the time required to read the GNPTXFSIZ register many times ++ (the output shows the number of times the register is read). ++ Read
wr_reg_test Displays the time required to write the GNPTXFSIZ register many times ++ (the output shows the number of times the register is written). ++ Read
++ ++ Example usage: ++ To get the current mode: ++ cat /sys/devices/lm0/mode ++ ++ To power down the USB: ++ echo 0 > /sys/devices/lm0/buspower ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* permission constants */ ++#include ++ ++#include ++ ++#include "linux/dwc_otg_plat.h" ++#include "dwc_otg_attr.h" ++#include "dwc_otg_driver.h" ++#include "dwc_otg_pcd.h" ++#include "dwc_otg_hcd.h" ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++/* ++ * MACROs for defining sysfs attribute ++ */ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ ++ uint32_t val; \ ++ val = dwc_read_reg32 (_addr_); \ ++ val = (val & (_mask_)) >> _shift_; \ ++ return sprintf (buf, "%s = 0x%x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ ++ uint32_t set = simple_strtoul(buf, NULL, 16); \ ++ uint32_t clear = set; \ ++ clear = ((~clear) << _shift_) & _mask_; \ ++ set = (set << _shift_) & _mask_; \ ++ dev_dbg(_dev, "Storing Address=0x%08x Set=0x%08x Clear=0x%08x\n", (uint32_t)_addr_, set, clear); \ ++ dwc_modify_reg32(_addr_, clear, set); \ ++ return count; \ ++} ++ ++/* ++ * MACROs for defining sysfs attribute for 32-bit registers ++ */ ++#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ ++ uint32_t val; \ ++ val = dwc_read_reg32 (_addr_); \ ++ return sprintf (buf, "%s = 0x%08x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_addr_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ ++ uint32_t val = simple_strtoul(buf, NULL, 16); \ ++ dev_dbg(_dev, "Storing Address=0x%08x Val=0x%08x\n", (uint32_t)_addr_, val); \ ++ dwc_write_reg32(_addr_, val); \ ++ return count; \ ++} ++ ++#else ++ ++/* ++ * MACROs for defining sysfs attribute ++ */ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, char *buf) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);\ ++ uint32_t val; \ ++ val = dwc_read_reg32 (_addr_); \ ++ val = (val & (_mask_)) >> _shift_; \ ++ return sprintf (buf, "%s = 0x%x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, const char *buf, size_t count) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);\ ++ uint32_t set = simple_strtoul(buf, NULL, 16); \ ++ uint32_t clear = set; \ ++ clear = ((~clear) << _shift_) & _mask_; \ ++ set = (set << _shift_) & _mask_; \ ++ dev_dbg(_dev, "Storing Address=0x%08x Set=0x%08x Clear=0x%08x\n", (uint32_t)_addr_, set, clear); \ ++ dwc_modify_reg32(_addr_, clear, set); \ ++ return count; \ ++} ++ ++/* ++ * MACROs for defining sysfs attribute for 32-bit registers ++ */ ++#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, char *buf) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);\ ++ uint32_t val; \ ++ val = dwc_read_reg32 (_addr_); \ ++ return sprintf (buf, "%s = 0x%08x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_addr_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, const char *buf, size_t count) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);\ ++ uint32_t val = simple_strtoul(buf, NULL, 16); \ ++ dev_dbg(_dev, "Storing Address=0x%08x Val=0x%08x\n", (uint32_t)_addr_, val); \ ++ dwc_write_reg32(_addr_, val); \ ++ return count; \ ++} ++ ++#endif ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_RW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store); ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_RO(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL); ++ ++#define DWC_OTG_DEVICE_ATTR_REG32_RW(_otg_attr_name_,_addr_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_addr_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store); ++ ++#define DWC_OTG_DEVICE_ATTR_REG32_RO(_otg_attr_name_,_addr_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL); ++ ++ ++/** @name Functions for Show/Store of Attributes */ ++/**@{*/ ++ ++/** ++ * Show the register offset of the Register Access. ++ */ ++static ssize_t regoffset_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ return snprintf(buf, sizeof("0xFFFFFFFF\n")+1,"0x%08x\n", otg_dev->reg_offset); ++} ++ ++/** ++ * Set the register offset for the next Register Access Read/Write ++ */ ++static ssize_t regoffset_store( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ const char *buf, ++ size_t count ) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ uint32_t offset = simple_strtoul(buf, NULL, 16); ++ //dev_dbg(_dev, "Offset=0x%08x\n", offset); ++ if (offset < 0x00040000 ) { ++ otg_dev->reg_offset = offset; ++ } ++ else { ++ dev_err( _dev, "invalid offset\n" ); ++ } ++ ++ return count; ++} ++DEVICE_ATTR(regoffset, S_IRUGO|S_IWUSR, (void *)regoffset_show, regoffset_store); ++ ++ ++/** ++ * Show the value of the register at the offset in the reg_offset ++ * attribute. ++ */ ++static ssize_t regvalue_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ uint32_t val; ++ volatile uint32_t *addr; ++ ++ if (otg_dev->reg_offset != 0xFFFFFFFF && ++ 0 != otg_dev->base) { ++ /* Calculate the address */ ++ addr = (uint32_t*)(otg_dev->reg_offset + ++ (uint8_t*)otg_dev->base); ++ //dev_dbg(_dev, "@0x%08x\n", (unsigned)addr); ++ val = dwc_read_reg32( addr ); ++ return snprintf(buf, sizeof("Reg@0xFFFFFFFF = 0xFFFFFFFF\n")+1, ++ "Reg@0x%06x = 0x%08x\n", ++ otg_dev->reg_offset, val); ++ } ++ else { ++ dev_err(_dev, "Invalid offset (0x%0x)\n", ++ otg_dev->reg_offset); ++ return sprintf(buf, "invalid offset\n" ); ++ } ++} ++ ++/** ++ * Store the value in the register at the offset in the reg_offset ++ * attribute. ++ * ++ */ ++static ssize_t regvalue_store( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ const char *buf, ++ size_t count ) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ volatile uint32_t * addr; ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ //dev_dbg(_dev, "Offset=0x%08x Val=0x%08x\n", otg_dev->reg_offset, val); ++ if (otg_dev->reg_offset != 0xFFFFFFFF && 0 != otg_dev->base) { ++ /* Calculate the address */ ++ addr = (uint32_t*)(otg_dev->reg_offset + ++ (uint8_t*)otg_dev->base); ++ //dev_dbg(_dev, "@0x%08x\n", (unsigned)addr); ++ dwc_write_reg32( addr, val ); ++ } ++ else { ++ dev_err(_dev, "Invalid Register Offset (0x%08x)\n", ++ otg_dev->reg_offset); ++ } ++ return count; ++} ++DEVICE_ATTR(regvalue, S_IRUGO|S_IWUSR, regvalue_show, regvalue_store); ++ ++/* ++ * Attributes ++ */ ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(mode,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<20),20,"Mode"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hnpcapable,&(otg_dev->core_if->core_global_regs->gusbcfg),(1<<9),9,"Mode"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(srpcapable,&(otg_dev->core_if->core_global_regs->gusbcfg),(1<<8),8,"Mode"); ++ ++//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(buspower,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode"); ++//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(bussuspend,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(busconnected,otg_dev->core_if->host_if->hprt0,0x01,0,"Bus Connected"); ++ ++DWC_OTG_DEVICE_ATTR_REG32_RW(gotgctl,&(otg_dev->core_if->core_global_regs->gotgctl),"GOTGCTL"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gusbcfg,&(otg_dev->core_if->core_global_regs->gusbcfg),"GUSBCFG"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(grxfsiz,&(otg_dev->core_if->core_global_regs->grxfsiz),"GRXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gnptxfsiz,&(otg_dev->core_if->core_global_regs->gnptxfsiz),"GNPTXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gpvndctl,&(otg_dev->core_if->core_global_regs->gpvndctl),"GPVNDCTL"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(ggpio,&(otg_dev->core_if->core_global_regs->ggpio),"GGPIO"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(guid,&(otg_dev->core_if->core_global_regs->guid),"GUID"); ++DWC_OTG_DEVICE_ATTR_REG32_RO(gsnpsid,&(otg_dev->core_if->core_global_regs->gsnpsid),"GSNPSID"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(devspeed,&(otg_dev->core_if->dev_if->dev_global_regs->dcfg),0x3,0,"Device Speed"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(enumspeed,&(otg_dev->core_if->dev_if->dev_global_regs->dsts),0x6,1,"Device Enumeration Speed"); ++ ++DWC_OTG_DEVICE_ATTR_REG32_RO(hptxfsiz,&(otg_dev->core_if->core_global_regs->hptxfsiz),"HPTXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(hprt0,otg_dev->core_if->host_if->hprt0,"HPRT0"); ++ ++ ++/** ++ * @todo Add code to initiate the HNP. ++ */ ++/** ++ * Show the HNP status bit ++ */ ++static ssize_t hnp_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ gotgctl_data_t val; ++ val.d32 = dwc_read_reg32 (&(otg_dev->core_if->core_global_regs->gotgctl)); ++ return sprintf (buf, "HstNegScs = 0x%x\n", val.b.hstnegscs); ++} ++ ++/** ++ * Set the HNP Request bit ++ */ ++static ssize_t hnp_store( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ const char *buf, ++ size_t count ) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ uint32_t in = simple_strtoul(buf, NULL, 16); ++ uint32_t *addr = (uint32_t *)&(otg_dev->core_if->core_global_regs->gotgctl); ++ gotgctl_data_t mem; ++ mem.d32 = dwc_read_reg32(addr); ++ mem.b.hnpreq = in; ++ dev_dbg(_dev, "Storing Address=0x%08x Data=0x%08x\n", (uint32_t)addr, mem.d32); ++ dwc_write_reg32(addr, mem.d32); ++ return count; ++} ++DEVICE_ATTR(hnp, 0644, hnp_show, hnp_store); ++ ++/** ++ * @todo Add code to initiate the SRP. ++ */ ++/** ++ * Show the SRP status bit ++ */ ++static ssize_t srp_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++#ifndef DWC_HOST_ONLY ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ gotgctl_data_t val; ++ val.d32 = dwc_read_reg32 (&(otg_dev->core_if->core_global_regs->gotgctl)); ++ return sprintf (buf, "SesReqScs = 0x%x\n", val.b.sesreqscs); ++#else ++ return sprintf(buf, "Host Only Mode!\n"); ++#endif ++} ++ ++ ++ ++/** ++ * Set the SRP Request bit ++ */ ++static ssize_t srp_store( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ const char *buf, ++ size_t count ) ++{ ++#ifndef DWC_HOST_ONLY ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ dwc_otg_pcd_initiate_srp(otg_dev->pcd); ++#endif ++ return count; ++} ++DEVICE_ATTR(srp, 0644, srp_show, srp_store); ++ ++/** ++ * @todo Need to do more for power on/off? ++ */ ++/** ++ * Show the Bus Power status ++ */ ++static ssize_t buspower_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ hprt0_data_t val; ++ val.d32 = dwc_read_reg32 (otg_dev->core_if->host_if->hprt0); ++ return sprintf (buf, "Bus Power = 0x%x\n", val.b.prtpwr); ++} ++ ++ ++/** ++ * Set the Bus Power status ++ */ ++static ssize_t buspower_store( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ const char *buf, ++ size_t count ) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ uint32_t on = simple_strtoul(buf, NULL, 16); ++ uint32_t *addr = (uint32_t *)otg_dev->core_if->host_if->hprt0; ++ hprt0_data_t mem; ++ ++ mem.d32 = dwc_read_reg32(addr); ++ mem.b.prtpwr = on; ++ ++ //dev_dbg(_dev, "Storing Address=0x%08x Data=0x%08x\n", (uint32_t)addr, mem.d32); ++ dwc_write_reg32(addr, mem.d32); ++ ++ return count; ++} ++DEVICE_ATTR(buspower, 0644, buspower_show, buspower_store); ++ ++/** ++ * @todo Need to do more for suspend? ++ */ ++/** ++ * Show the Bus Suspend status ++ */ ++static ssize_t bussuspend_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ hprt0_data_t val; ++ val.d32 = dwc_read_reg32 (otg_dev->core_if->host_if->hprt0); ++ return sprintf (buf, "Bus Suspend = 0x%x\n", val.b.prtsusp); ++} ++ ++/** ++ * Set the Bus Suspend status ++ */ ++static ssize_t bussuspend_store( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ const char *buf, ++ size_t count ) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ uint32_t in = simple_strtoul(buf, NULL, 16); ++ uint32_t *addr = (uint32_t *)otg_dev->core_if->host_if->hprt0; ++ hprt0_data_t mem; ++ mem.d32 = dwc_read_reg32(addr); ++ mem.b.prtsusp = in; ++ dev_dbg(_dev, "Storing Address=0x%08x Data=0x%08x\n", (uint32_t)addr, mem.d32); ++ dwc_write_reg32(addr, mem.d32); ++ return count; ++} ++DEVICE_ATTR(bussuspend, 0644, bussuspend_show, bussuspend_store); ++ ++/** ++ * Show the status of Remote Wakeup. ++ */ ++static ssize_t remote_wakeup_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++#ifndef DWC_HOST_ONLY ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ dctl_data_t val; ++ val.d32 = ++ dwc_read_reg32( &otg_dev->core_if->dev_if->dev_global_regs->dctl); ++ return sprintf( buf, "Remote Wakeup = %d Enabled = %d\n", ++ val.b.rmtwkupsig, otg_dev->pcd->remote_wakeup_enable); ++#else ++ return sprintf(buf, "Host Only Mode!\n"); ++#endif ++} ++/** ++ * Initiate a remote wakeup of the host. The Device control register ++ * Remote Wakeup Signal bit is written if the PCD Remote wakeup enable ++ * flag is set. ++ * ++ */ ++static ssize_t remote_wakeup_store( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ const char *buf, ++ size_t count ) ++{ ++#ifndef DWC_HOST_ONLY ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ if (val&1) { ++ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 1); ++ } ++ else { ++ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 0); ++ } ++#endif ++ return count; ++} ++DEVICE_ATTR(remote_wakeup, S_IRUGO|S_IWUSR, remote_wakeup_show, ++ remote_wakeup_store); ++ ++/** ++ * Dump global registers and either host or device registers (depending on the ++ * current mode of the core). ++ */ ++static ssize_t regdump_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ dwc_otg_dump_global_registers( otg_dev->core_if); ++ if (dwc_otg_is_host_mode(otg_dev->core_if)) { ++ dwc_otg_dump_host_registers( otg_dev->core_if); ++ } else { ++ dwc_otg_dump_dev_registers( otg_dev->core_if); ++ ++ } ++ return sprintf( buf, "Register Dump\n" ); ++} ++ ++DEVICE_ATTR(regdump, S_IRUGO|S_IWUSR, regdump_show, 0); ++ ++/** ++ * Dump global registers and either host or device registers (depending on the ++ * current mode of the core). ++ */ ++static ssize_t spramdump_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ dwc_otg_dump_spram( otg_dev->core_if); ++ ++ return sprintf( buf, "SPRAM Dump\n" ); ++} ++ ++DEVICE_ATTR(spramdump, S_IRUGO|S_IWUSR, spramdump_show, 0); ++ ++/** ++ * Dump the current hcd state. ++ */ ++static ssize_t hcddump_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++#ifndef DWC_DEVICE_ONLY ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ dwc_otg_hcd_dump_state(otg_dev->hcd); ++#endif ++ return sprintf( buf, "HCD Dump\n" ); ++} ++ ++DEVICE_ATTR(hcddump, S_IRUGO|S_IWUSR, hcddump_show, 0); ++ ++/** ++ * Dump the average frame remaining at SOF. This can be used to ++ * determine average interrupt latency. Frame remaining is also shown for ++ * start transfer and two additional sample points. ++ */ ++static ssize_t hcd_frrem_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++#ifndef DWC_DEVICE_ONLY ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ dwc_otg_hcd_dump_frrem(otg_dev->hcd); ++#endif ++ return sprintf( buf, "HCD Dump Frame Remaining\n" ); ++} ++ ++DEVICE_ATTR(hcd_frrem, S_IRUGO|S_IWUSR, hcd_frrem_show, 0); ++ ++/** ++ * Displays the time required to read the GNPTXFSIZ register many times (the ++ * output shows the number of times the register is read). ++ */ ++#define RW_REG_COUNT 10000000 ++#define MSEC_PER_JIFFIE 1000/HZ ++static ssize_t rd_reg_test_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ int i; ++ int time; ++ int start_jiffies; ++ ++ printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n", ++ HZ, MSEC_PER_JIFFIE, loops_per_jiffy); ++ start_jiffies = jiffies; ++ for (i = 0; i < RW_REG_COUNT; i++) { ++ dwc_read_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz); ++ } ++ time = jiffies - start_jiffies; ++ return sprintf( buf, "Time to read GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n", ++ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time ); ++} ++ ++DEVICE_ATTR(rd_reg_test, S_IRUGO|S_IWUSR, rd_reg_test_show, 0); ++ ++/** ++ * Displays the time required to write the GNPTXFSIZ register many times (the ++ * output shows the number of times the register is written). ++ */ ++static ssize_t wr_reg_test_show( struct device *_dev, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct device_attribute *attr, ++#endif ++ char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ uint32_t reg_val; ++ int i; ++ int time; ++ int start_jiffies; ++ ++ printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n", ++ HZ, MSEC_PER_JIFFIE, loops_per_jiffy); ++ reg_val = dwc_read_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz); ++ start_jiffies = jiffies; ++ for (i = 0; i < RW_REG_COUNT; i++) { ++ dwc_write_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz, reg_val); ++ } ++ time = jiffies - start_jiffies; ++ return sprintf( buf, "Time to write GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n", ++ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); ++} ++ ++DEVICE_ATTR(wr_reg_test, S_IRUGO|S_IWUSR, wr_reg_test_show, 0); ++/**@}*/ ++ ++/** ++ * Create the device files ++ */ ++void dwc_otg_attr_create (struct device *dev) ++{ ++ int error; ++ ++ error = device_create_file(dev, &dev_attr_regoffset); ++ error = device_create_file(dev, &dev_attr_regvalue); ++ error = device_create_file(dev, &dev_attr_mode); ++ error = device_create_file(dev, &dev_attr_hnpcapable); ++ error = device_create_file(dev, &dev_attr_srpcapable); ++ error = device_create_file(dev, &dev_attr_hnp); ++ error = device_create_file(dev, &dev_attr_srp); ++ error = device_create_file(dev, &dev_attr_buspower); ++ error = device_create_file(dev, &dev_attr_bussuspend); ++ error = device_create_file(dev, &dev_attr_busconnected); ++ error = device_create_file(dev, &dev_attr_gotgctl); ++ error = device_create_file(dev, &dev_attr_gusbcfg); ++ error = device_create_file(dev, &dev_attr_grxfsiz); ++ error = device_create_file(dev, &dev_attr_gnptxfsiz); ++ error = device_create_file(dev, &dev_attr_gpvndctl); ++ error = device_create_file(dev, &dev_attr_ggpio); ++ error = device_create_file(dev, &dev_attr_guid); ++ error = device_create_file(dev, &dev_attr_gsnpsid); ++ error = device_create_file(dev, &dev_attr_devspeed); ++ error = device_create_file(dev, &dev_attr_enumspeed); ++ error = device_create_file(dev, &dev_attr_hptxfsiz); ++ error = device_create_file(dev, &dev_attr_hprt0); ++ error = device_create_file(dev, &dev_attr_remote_wakeup); ++ error = device_create_file(dev, &dev_attr_regdump); ++ error = device_create_file(dev, &dev_attr_spramdump); ++ error = device_create_file(dev, &dev_attr_hcddump); ++ error = device_create_file(dev, &dev_attr_hcd_frrem); ++ error = device_create_file(dev, &dev_attr_rd_reg_test); ++ error = device_create_file(dev, &dev_attr_wr_reg_test); ++} ++ ++/** ++ * Remove the device files ++ */ ++void dwc_otg_attr_remove (struct device *dev) ++{ ++ device_remove_file(dev, &dev_attr_regoffset); ++ device_remove_file(dev, &dev_attr_regvalue); ++ device_remove_file(dev, &dev_attr_mode); ++ device_remove_file(dev, &dev_attr_hnpcapable); ++ device_remove_file(dev, &dev_attr_srpcapable); ++ device_remove_file(dev, &dev_attr_hnp); ++ device_remove_file(dev, &dev_attr_srp); ++ device_remove_file(dev, &dev_attr_buspower); ++ device_remove_file(dev, &dev_attr_bussuspend); ++ device_remove_file(dev, &dev_attr_busconnected); ++ device_remove_file(dev, &dev_attr_gotgctl); ++ device_remove_file(dev, &dev_attr_gusbcfg); ++ device_remove_file(dev, &dev_attr_grxfsiz); ++ device_remove_file(dev, &dev_attr_gnptxfsiz); ++ device_remove_file(dev, &dev_attr_gpvndctl); ++ device_remove_file(dev, &dev_attr_ggpio); ++ device_remove_file(dev, &dev_attr_guid); ++ device_remove_file(dev, &dev_attr_gsnpsid); ++ device_remove_file(dev, &dev_attr_devspeed); ++ device_remove_file(dev, &dev_attr_enumspeed); ++ device_remove_file(dev, &dev_attr_hptxfsiz); ++ device_remove_file(dev, &dev_attr_hprt0); ++ device_remove_file(dev, &dev_attr_remote_wakeup); ++ device_remove_file(dev, &dev_attr_regdump); ++ device_remove_file(dev, &dev_attr_spramdump); ++ device_remove_file(dev, &dev_attr_hcddump); ++ device_remove_file(dev, &dev_attr_hcd_frrem); ++ device_remove_file(dev, &dev_attr_rd_reg_test); ++ device_remove_file(dev, &dev_attr_wr_reg_test); ++} +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_attr.h +@@ -0,0 +1,67 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.h $ ++ * $Revision: 1.2 $ ++ * $Date: 2008-11-21 05:39:15 $ ++ * $Change: 477051 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_OTG_ATTR_H__) ++#define __DWC_OTG_ATTR_H__ ++ ++/** @file ++ * This file contains the interface to the Linux device attributes. ++ */ ++extern struct device_attribute dev_attr_regoffset; ++extern struct device_attribute dev_attr_regvalue; ++ ++extern struct device_attribute dev_attr_mode; ++extern struct device_attribute dev_attr_hnpcapable; ++extern struct device_attribute dev_attr_srpcapable; ++extern struct device_attribute dev_attr_hnp; ++extern struct device_attribute dev_attr_srp; ++extern struct device_attribute dev_attr_buspower; ++extern struct device_attribute dev_attr_bussuspend; ++extern struct device_attribute dev_attr_busconnected; ++extern struct device_attribute dev_attr_gotgctl; ++extern struct device_attribute dev_attr_gusbcfg; ++extern struct device_attribute dev_attr_grxfsiz; ++extern struct device_attribute dev_attr_gnptxfsiz; ++extern struct device_attribute dev_attr_gpvndctl; ++extern struct device_attribute dev_attr_ggpio; ++extern struct device_attribute dev_attr_guid; ++extern struct device_attribute dev_attr_gsnpsid; ++extern struct device_attribute dev_attr_devspeed; ++extern struct device_attribute dev_attr_enumspeed; ++extern struct device_attribute dev_attr_hptxfsiz; ++extern struct device_attribute dev_attr_hprt0; ++ ++void dwc_otg_attr_create (struct device *dev); ++void dwc_otg_attr_remove (struct device *dev); ++ ++#endif +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_cil.c +@@ -0,0 +1,3692 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.c $ ++ * $Revision: 1.7 $ ++ * $Date: 2008-12-22 11:43:05 $ ++ * $Change: 1117667 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The Core Interface Layer provides basic services for accessing and ++ * managing the DWC_otg hardware. These services are used by both the ++ * Host Controller Driver and the Peripheral Controller Driver. ++ * ++ * The CIL manages the memory map for the core so that the HCD and PCD ++ * don't have to do this separately. It also handles basic tasks like ++ * reading/writing the registers and data FIFOs in the controller. ++ * Some of the data access functions provide encapsulation of several ++ * operations required to perform a task, such as writing multiple ++ * registers to start a transfer. Finally, the CIL performs basic ++ * services that are not specific to either the host or device modes ++ * of operation. These services include management of the OTG Host ++ * Negotiation Protocol (HNP) and Session Request Protocol (SRP). A ++ * Diagnostic API is also provided to allow testing of the controller ++ * hardware. ++ * ++ * The Core Interface Layer has the following requirements: ++ * - Provides basic controller operations. ++ * - Minimal use of OS services. ++ * - The OS services used will be abstracted by using inline functions ++ * or macros. ++ * ++ */ ++#include ++#include ++#ifdef DEBUG ++#include ++#endif ++ ++#include "linux/dwc_otg_plat.h" ++#include "dwc_otg_regs.h" ++#include "dwc_otg_cil.h" ++ ++/* Included only to access hc->qh for non-dword buffer handling ++ * TODO: account it ++ */ ++#include "dwc_otg_hcd.h" ++ ++/** ++ * This function is called to initialize the DWC_otg CSR data ++ * structures. The register addresses in the device and host ++ * structures are initialized from the base address supplied by the ++ * caller. The calling function must make the OS calls to get the ++ * base address of the DWC_otg controller registers. The core_params ++ * argument holds the parameters that specify how the core should be ++ * configured. ++ * ++ * @param[in] reg_base_addr Base address of DWC_otg core registers ++ * @param[in] core_params Pointer to the core configuration parameters ++ * ++ */ ++dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t *reg_base_addr, ++ dwc_otg_core_params_t *core_params) ++{ ++ dwc_otg_core_if_t *core_if = 0; ++ dwc_otg_dev_if_t *dev_if = 0; ++ dwc_otg_host_if_t *host_if = 0; ++ uint8_t *reg_base = (uint8_t *)reg_base_addr; ++ int i = 0; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s(%p,%p)\n", __func__, reg_base_addr, core_params); ++ ++ core_if = kmalloc(sizeof(dwc_otg_core_if_t), GFP_KERNEL); ++ ++ if (core_if == 0) { ++ DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_core_if_t failed\n"); ++ return 0; ++ } ++ ++ memset(core_if, 0, sizeof(dwc_otg_core_if_t)); ++ ++ core_if->core_params = core_params; ++ core_if->core_global_regs = (dwc_otg_core_global_regs_t *)reg_base; ++ ++ /* ++ * Allocate the Device Mode structures. ++ */ ++ dev_if = kmalloc(sizeof(dwc_otg_dev_if_t), GFP_KERNEL); ++ ++ if (dev_if == 0) { ++ DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_dev_if_t failed\n"); ++ kfree(core_if); ++ return 0; ++ } ++ ++ dev_if->dev_global_regs = ++ (dwc_otg_device_global_regs_t *)(reg_base + DWC_DEV_GLOBAL_REG_OFFSET); ++ ++ for (i=0; iin_ep_regs[i] = (dwc_otg_dev_in_ep_regs_t *) ++ (reg_base + DWC_DEV_IN_EP_REG_OFFSET + ++ (i * DWC_EP_REG_OFFSET)); ++ ++ dev_if->out_ep_regs[i] = (dwc_otg_dev_out_ep_regs_t *) ++ (reg_base + DWC_DEV_OUT_EP_REG_OFFSET + ++ (i * DWC_EP_REG_OFFSET)); ++ DWC_DEBUGPL(DBG_CILV, "in_ep_regs[%d]->diepctl=%p\n", ++ i, &dev_if->in_ep_regs[i]->diepctl); ++ DWC_DEBUGPL(DBG_CILV, "out_ep_regs[%d]->doepctl=%p\n", ++ i, &dev_if->out_ep_regs[i]->doepctl); ++ } ++ ++ dev_if->speed = 0; // unknown ++ ++ core_if->dev_if = dev_if; ++ ++ /* ++ * Allocate the Host Mode structures. ++ */ ++ host_if = kmalloc(sizeof(dwc_otg_host_if_t), GFP_KERNEL); ++ ++ if (host_if == 0) { ++ DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_host_if_t failed\n"); ++ kfree(dev_if); ++ kfree(core_if); ++ return 0; ++ } ++ ++ host_if->host_global_regs = (dwc_otg_host_global_regs_t *) ++ (reg_base + DWC_OTG_HOST_GLOBAL_REG_OFFSET); ++ ++ host_if->hprt0 = (uint32_t*)(reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET); ++ ++ for (i=0; ihc_regs[i] = (dwc_otg_hc_regs_t *) ++ (reg_base + DWC_OTG_HOST_CHAN_REGS_OFFSET + ++ (i * DWC_OTG_CHAN_REGS_OFFSET)); ++ DWC_DEBUGPL(DBG_CILV, "hc_reg[%d]->hcchar=%p\n", ++ i, &host_if->hc_regs[i]->hcchar); ++ } ++ ++ host_if->num_host_channels = MAX_EPS_CHANNELS; ++ core_if->host_if = host_if; ++ ++ for (i=0; idata_fifo[i] = ++ (uint32_t *)(reg_base + DWC_OTG_DATA_FIFO_OFFSET + ++ (i * DWC_OTG_DATA_FIFO_SIZE)); ++ DWC_DEBUGPL(DBG_CILV, "data_fifo[%d]=0x%08x\n", ++ i, (unsigned)core_if->data_fifo[i]); ++ } ++ ++ core_if->pcgcctl = (uint32_t*)(reg_base + DWC_OTG_PCGCCTL_OFFSET); ++ ++ /* ++ * Store the contents of the hardware configuration registers here for ++ * easy access later. ++ */ ++ core_if->hwcfg1.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg1); ++ core_if->hwcfg2.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg2); ++ core_if->hwcfg3.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg3); ++ core_if->hwcfg4.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg4); ++ ++ DWC_DEBUGPL(DBG_CILV,"hwcfg1=%08x\n",core_if->hwcfg1.d32); ++ DWC_DEBUGPL(DBG_CILV,"hwcfg2=%08x\n",core_if->hwcfg2.d32); ++ DWC_DEBUGPL(DBG_CILV,"hwcfg3=%08x\n",core_if->hwcfg3.d32); ++ DWC_DEBUGPL(DBG_CILV,"hwcfg4=%08x\n",core_if->hwcfg4.d32); ++ ++ core_if->hcfg.d32 = dwc_read_reg32(&core_if->host_if->host_global_regs->hcfg); ++ core_if->dcfg.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dcfg); ++ ++ DWC_DEBUGPL(DBG_CILV,"hcfg=%08x\n",core_if->hcfg.d32); ++ DWC_DEBUGPL(DBG_CILV,"dcfg=%08x\n",core_if->dcfg.d32); ++ ++ DWC_DEBUGPL(DBG_CILV,"op_mode=%0x\n",core_if->hwcfg2.b.op_mode); ++ DWC_DEBUGPL(DBG_CILV,"arch=%0x\n",core_if->hwcfg2.b.architecture); ++ DWC_DEBUGPL(DBG_CILV,"num_dev_ep=%d\n",core_if->hwcfg2.b.num_dev_ep); ++ DWC_DEBUGPL(DBG_CILV,"num_host_chan=%d\n",core_if->hwcfg2.b.num_host_chan); ++ DWC_DEBUGPL(DBG_CILV,"nonperio_tx_q_depth=0x%0x\n",core_if->hwcfg2.b.nonperio_tx_q_depth); ++ DWC_DEBUGPL(DBG_CILV,"host_perio_tx_q_depth=0x%0x\n",core_if->hwcfg2.b.host_perio_tx_q_depth); ++ DWC_DEBUGPL(DBG_CILV,"dev_token_q_depth=0x%0x\n",core_if->hwcfg2.b.dev_token_q_depth); ++ ++ DWC_DEBUGPL(DBG_CILV,"Total FIFO SZ=%d\n", core_if->hwcfg3.b.dfifo_depth); ++ DWC_DEBUGPL(DBG_CILV,"xfer_size_cntr_width=%0x\n", core_if->hwcfg3.b.xfer_size_cntr_width); ++ ++ /* ++ * Set the SRP sucess bit for FS-I2c ++ */ ++ core_if->srp_success = 0; ++ core_if->srp_timer_started = 0; ++ ++ ++ /* ++ * Create new workqueue and init works ++ */ ++ core_if->wq_otg = create_singlethread_workqueue("dwc_otg"); ++ if(core_if->wq_otg == 0) { ++ DWC_DEBUGPL(DBG_CIL, "Creation of wq_otg failed\n"); ++ kfree(host_if); ++ kfree(dev_if); ++ kfree(core_if); ++ return 0 * HZ; ++ } ++ ++ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ ++ INIT_WORK(&core_if->w_conn_id, w_conn_id_status_change, core_if); ++ INIT_WORK(&core_if->w_wkp, w_wakeup_detected, core_if); ++ ++#else ++ ++ INIT_WORK(&core_if->w_conn_id, w_conn_id_status_change); ++ INIT_DELAYED_WORK(&core_if->w_wkp, w_wakeup_detected); ++ ++#endif ++ return core_if; ++} ++ ++/** ++ * This function frees the structures allocated by dwc_otg_cil_init(). ++ * ++ * @param[in] core_if The core interface pointer returned from ++ * dwc_otg_cil_init(). ++ * ++ */ ++void dwc_otg_cil_remove(dwc_otg_core_if_t *core_if) ++{ ++ /* Disable all interrupts */ ++ dwc_modify_reg32(&core_if->core_global_regs->gahbcfg, 1, 0); ++ dwc_write_reg32(&core_if->core_global_regs->gintmsk, 0); ++ ++ if (core_if->wq_otg) { ++ destroy_workqueue(core_if->wq_otg); ++ } ++ if (core_if->dev_if) { ++ kfree(core_if->dev_if); ++ } ++ if (core_if->host_if) { ++ kfree(core_if->host_if); ++ } ++ kfree(core_if); ++} ++ ++/** ++ * This function enables the controller's Global Interrupt in the AHB Config ++ * register. ++ * ++ * @param[in] core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_enable_global_interrupts(dwc_otg_core_if_t *core_if) ++{ ++ gahbcfg_data_t ahbcfg = { .d32 = 0}; ++ ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */ ++ dwc_modify_reg32(&core_if->core_global_regs->gahbcfg, 0, ahbcfg.d32); ++} ++ ++/** ++ * This function disables the controller's Global Interrupt in the AHB Config ++ * register. ++ * ++ * @param[in] core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_disable_global_interrupts(dwc_otg_core_if_t *core_if) ++{ ++ gahbcfg_data_t ahbcfg = { .d32 = 0}; ++ ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */ ++ dwc_modify_reg32(&core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0); ++} ++ ++/** ++ * This function initializes the commmon interrupts, used in both ++ * device and host modes. ++ * ++ * @param[in] core_if Programming view of the DWC_otg controller ++ * ++ */ ++static void dwc_otg_enable_common_interrupts(dwc_otg_core_if_t *core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = ++ core_if->core_global_regs; ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ ++ /* Clear any pending OTG Interrupts */ ++ dwc_write_reg32(&global_regs->gotgint, 0xFFFFFFFF); ++ ++ /* Clear any pending interrupts */ ++ dwc_write_reg32(&global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* ++ * Enable the interrupts in the GINTMSK. ++ */ ++ intr_mask.b.modemismatch = 1; ++ intr_mask.b.otgintr = 1; ++ ++ if (!core_if->dma_enable) { ++ intr_mask.b.rxstsqlvl = 1; ++ } ++ ++ intr_mask.b.conidstschng = 1; ++ intr_mask.b.wkupintr = 1; ++ intr_mask.b.disconnect = 1; ++ intr_mask.b.usbsuspend = 1; ++ intr_mask.b.sessreqintr = 1; ++ dwc_write_reg32(&global_regs->gintmsk, intr_mask.d32); ++} ++ ++/** ++ * Initializes the FSLSPClkSel field of the HCFG register depending on the PHY ++ * type. ++ */ ++static void init_fslspclksel(dwc_otg_core_if_t *core_if) ++{ ++ uint32_t val; ++ hcfg_data_t hcfg; ++ ++ if (((core_if->hwcfg2.b.hs_phy_type == 2) && ++ (core_if->hwcfg2.b.fs_phy_type == 1) && ++ (core_if->core_params->ulpi_fs_ls)) || ++ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) { ++ /* Full speed PHY */ ++ val = DWC_HCFG_48_MHZ; ++ } ++ else { ++ /* High speed PHY running at full speed or high speed */ ++ val = DWC_HCFG_30_60_MHZ; ++ } ++ ++ DWC_DEBUGPL(DBG_CIL, "Initializing HCFG.FSLSPClkSel to 0x%1x\n", val); ++ hcfg.d32 = dwc_read_reg32(&core_if->host_if->host_global_regs->hcfg); ++ hcfg.b.fslspclksel = val; ++ dwc_write_reg32(&core_if->host_if->host_global_regs->hcfg, hcfg.d32); ++} ++ ++/** ++ * Initializes the DevSpd field of the DCFG register depending on the PHY type ++ * and the enumeration speed of the device. ++ */ ++static void init_devspd(dwc_otg_core_if_t *core_if) ++{ ++ uint32_t val; ++ dcfg_data_t dcfg; ++ ++ if (((core_if->hwcfg2.b.hs_phy_type == 2) && ++ (core_if->hwcfg2.b.fs_phy_type == 1) && ++ (core_if->core_params->ulpi_fs_ls)) || ++ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) { ++ /* Full speed PHY */ ++ val = 0x3; ++ } ++ else if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) { ++ /* High speed PHY running at full speed */ ++ val = 0x1; ++ } ++ else { ++ /* High speed PHY running at high speed */ ++ val = 0x0; ++ } ++ ++ DWC_DEBUGPL(DBG_CIL, "Initializing DCFG.DevSpd to 0x%1x\n", val); ++ ++ dcfg.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dcfg); ++ dcfg.b.devspd = val; ++ dwc_write_reg32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32); ++} ++ ++/** ++ * This function calculates the number of IN EPS ++ * using GHWCFG1 and GHWCFG2 registers values ++ * ++ * @param core_if Programming view of the DWC_otg controller ++ */ ++static uint32_t calc_num_in_eps(dwc_otg_core_if_t *core_if) ++{ ++ uint32_t num_in_eps = 0; ++ uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep; ++ uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 3; ++ uint32_t num_tx_fifos = core_if->hwcfg4.b.num_in_eps; ++ int i; ++ ++ ++ for(i = 0; i < num_eps; ++i) ++ { ++ if(!(hwcfg1 & 0x1)) ++ num_in_eps++; ++ ++ hwcfg1 >>= 2; ++ } ++ ++ if(core_if->hwcfg4.b.ded_fifo_en) { ++ num_in_eps = (num_in_eps > num_tx_fifos) ? num_tx_fifos : num_in_eps; ++ } ++ ++ return num_in_eps; ++} ++ ++ ++/** ++ * This function calculates the number of OUT EPS ++ * using GHWCFG1 and GHWCFG2 registers values ++ * ++ * @param core_if Programming view of the DWC_otg controller ++ */ ++static uint32_t calc_num_out_eps(dwc_otg_core_if_t *core_if) ++{ ++ uint32_t num_out_eps = 0; ++ uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep; ++ uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 2; ++ int i; ++ ++ for(i = 0; i < num_eps; ++i) ++ { ++ if(!(hwcfg1 & 0x2)) ++ num_out_eps++; ++ ++ hwcfg1 >>= 2; ++ } ++ return num_out_eps; ++} ++/** ++ * This function initializes the DWC_otg controller registers and ++ * prepares the core for device mode or host mode operation. ++ * ++ * @param core_if Programming view of the DWC_otg controller ++ * ++ */ ++void dwc_otg_core_init(dwc_otg_core_if_t *core_if) ++{ ++ int i = 0; ++ dwc_otg_core_global_regs_t *global_regs = ++ core_if->core_global_regs; ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ gahbcfg_data_t ahbcfg = { .d32 = 0 }; ++ gusbcfg_data_t usbcfg = { .d32 = 0 }; ++ gi2cctl_data_t i2cctl = { .d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CILV, "dwc_otg_core_init(%p)\n", core_if); ++ ++ /* Common Initialization */ ++ ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ ++// usbcfg.b.tx_end_delay = 1; ++ /* Program the ULPI External VBUS bit if needed */ ++ usbcfg.b.ulpi_ext_vbus_drv = ++ (core_if->core_params->phy_ulpi_ext_vbus == DWC_PHY_ULPI_EXTERNAL_VBUS) ? 1 : 0; ++ ++ /* Set external TS Dline pulsing */ ++ usbcfg.b.term_sel_dl_pulse = (core_if->core_params->ts_dline == 1) ? 1 : 0; ++ dwc_write_reg32 (&global_regs->gusbcfg, usbcfg.d32); ++ ++ ++ /* Reset the Controller */ ++ dwc_otg_core_reset(core_if); ++ ++ /* Initialize parameters from Hardware configuration registers. */ ++ dev_if->num_in_eps = calc_num_in_eps(core_if); ++ dev_if->num_out_eps = calc_num_out_eps(core_if); ++ ++ ++ DWC_DEBUGPL(DBG_CIL, "num_dev_perio_in_ep=%d\n", core_if->hwcfg4.b.num_dev_perio_in_ep); ++ ++ for (i=0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) ++ { ++ dev_if->perio_tx_fifo_size[i] = ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i]) >> 16; ++ DWC_DEBUGPL(DBG_CIL, "Periodic Tx FIFO SZ #%d=0x%0x\n", ++ i, dev_if->perio_tx_fifo_size[i]); ++ } ++ ++ for (i=0; i < core_if->hwcfg4.b.num_in_eps; i++) ++ { ++ dev_if->tx_fifo_size[i] = ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i]) >> 16; ++ DWC_DEBUGPL(DBG_CIL, "Tx FIFO SZ #%d=0x%0x\n", ++ i, dev_if->perio_tx_fifo_size[i]); ++ } ++ ++ core_if->total_fifo_size = core_if->hwcfg3.b.dfifo_depth; ++ core_if->rx_fifo_size = ++ dwc_read_reg32(&global_regs->grxfsiz); ++ core_if->nperio_tx_fifo_size = ++ dwc_read_reg32(&global_regs->gnptxfsiz) >> 16; ++ ++ DWC_DEBUGPL(DBG_CIL, "Total FIFO SZ=%d\n", core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "Rx FIFO SZ=%d\n", core_if->rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO SZ=%d\n", core_if->nperio_tx_fifo_size); ++ ++ /* This programming sequence needs to happen in FS mode before any other ++ * programming occurs */ ++ if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) && ++ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) { ++ /* If FS mode with FS PHY */ ++ ++ /* core_init() is now called on every switch so only call the ++ * following for the first time through. */ ++ if (!core_if->phy_init_done) { ++ core_if->phy_init_done = 1; ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY detected\n"); ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.physel = 1; ++ dwc_write_reg32 (&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Reset after a PHY select */ ++ dwc_otg_core_reset(core_if); ++ } ++ ++ /* Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also ++ * do this on HNP Dev/Host mode switches (done in dev_init and ++ * host_init). */ ++ if (dwc_otg_is_host_mode(core_if)) { ++ init_fslspclksel(core_if); ++ } ++ else { ++ init_devspd(core_if); ++ } ++ ++ if (core_if->core_params->i2c_enable) { ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY Enabling I2c\n"); ++ /* Program GUSBCFG.OtgUtmifsSel to I2C */ ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.otgutmifssel = 1; ++ dwc_write_reg32 (&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Program GI2CCTL.I2CEn */ ++ i2cctl.d32 = dwc_read_reg32(&global_regs->gi2cctl); ++ i2cctl.b.i2cdevaddr = 1; ++ i2cctl.b.i2cen = 0; ++ dwc_write_reg32 (&global_regs->gi2cctl, i2cctl.d32); ++ i2cctl.b.i2cen = 1; ++ dwc_write_reg32 (&global_regs->gi2cctl, i2cctl.d32); ++ } ++ ++ } /* endif speed == DWC_SPEED_PARAM_FULL */ ++ ++ else { ++ /* High speed PHY. */ ++ if (!core_if->phy_init_done) { ++ core_if->phy_init_done = 1; ++ /* HS PHY parameters. These parameters are preserved ++ * during soft reset so only program the first time. Do ++ * a soft reset immediately after setting phyif. */ ++ usbcfg.b.ulpi_utmi_sel = core_if->core_params->phy_type; ++ if (usbcfg.b.ulpi_utmi_sel == 1) { ++ /* ULPI interface */ ++ usbcfg.b.phyif = 0; ++ usbcfg.b.ddrsel = core_if->core_params->phy_ulpi_ddr; ++ } ++ else { ++ /* UTMI+ interface */ ++ if (core_if->core_params->phy_utmi_width == 16) { ++ usbcfg.b.phyif = 1; ++ } ++ else { ++ usbcfg.b.phyif = 0; ++ } ++ } ++ ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Reset after setting the PHY parameters */ ++ dwc_otg_core_reset(core_if); ++ } ++ } ++ ++ if ((core_if->hwcfg2.b.hs_phy_type == 2) && ++ (core_if->hwcfg2.b.fs_phy_type == 1) && ++ (core_if->core_params->ulpi_fs_ls)) { ++ DWC_DEBUGPL(DBG_CIL, "Setting ULPI FSLS\n"); ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.ulpi_fsls = 1; ++ usbcfg.b.ulpi_clk_sus_m = 1; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ } ++ else { ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.ulpi_fsls = 0; ++ usbcfg.b.ulpi_clk_sus_m = 0; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ } ++ ++ /* Program the GAHBCFG Register.*/ ++ switch (core_if->hwcfg2.b.architecture) { ++ ++ case DWC_SLAVE_ONLY_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "Slave Only Mode\n"); ++ ahbcfg.b.nptxfemplvl_txfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY; ++ ahbcfg.b.ptxfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY; ++ core_if->dma_enable = 0; ++ core_if->dma_desc_enable = 0; ++ break; ++ ++ case DWC_EXT_DMA_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "External DMA Mode\n"); ++ ahbcfg.b.hburstlen = core_if->core_params->dma_burst_size; ++ core_if->dma_enable = (core_if->core_params->dma_enable != 0); ++ core_if->dma_desc_enable = (core_if->core_params->dma_desc_enable != 0); ++ break; ++ ++ case DWC_INT_DMA_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "Internal DMA Mode\n"); ++ ahbcfg.b.hburstlen = DWC_GAHBCFG_INT_DMA_BURST_INCR; ++ core_if->dma_enable = (core_if->core_params->dma_enable != 0); ++ core_if->dma_desc_enable = (core_if->core_params->dma_desc_enable != 0); ++ break; ++ ++ } ++ ahbcfg.b.dmaenable = core_if->dma_enable; ++ dwc_write_reg32(&global_regs->gahbcfg, ahbcfg.d32); ++ ++ core_if->en_multiple_tx_fifo = core_if->hwcfg4.b.ded_fifo_en; ++ ++ core_if->pti_enh_enable = core_if->core_params->pti_enable != 0; ++ core_if->multiproc_int_enable = core_if->core_params->mpi_enable; ++ DWC_PRINT("Periodic Transfer Interrupt Enhancement - %s\n", ((core_if->pti_enh_enable) ? "enabled": "disabled")); ++ DWC_PRINT("Multiprocessor Interrupt Enhancement - %s\n", ((core_if->multiproc_int_enable) ? "enabled": "disabled")); ++ ++ /* ++ * Program the GUSBCFG register. ++ */ ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ ++ switch (core_if->hwcfg2.b.op_mode) { ++ case DWC_MODE_HNP_SRP_CAPABLE: ++ usbcfg.b.hnpcap = (core_if->core_params->otg_cap == ++ DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE); ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_SRP_ONLY_CAPABLE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_HNP_SRP_CAPABLE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ ++ case DWC_MODE_SRP_CAPABLE_DEVICE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_SRP_CAPABLE_DEVICE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ ++ case DWC_MODE_SRP_CAPABLE_HOST: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_SRP_CAPABLE_HOST: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ } ++ ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Enable common interrupts */ ++ dwc_otg_enable_common_interrupts(core_if); ++ ++ /* Do device or host intialization based on mode during PCD ++ * and HCD initialization */ ++ if (dwc_otg_is_host_mode(core_if)) { ++ DWC_DEBUGPL(DBG_ANY, "Host Mode\n"); ++ core_if->op_state = A_HOST; ++ } ++ else { ++ DWC_DEBUGPL(DBG_ANY, "Device Mode\n"); ++ core_if->op_state = B_PERIPHERAL; ++#ifdef DWC_DEVICE_ONLY ++ dwc_otg_core_dev_init(core_if); ++#endif ++ } ++} ++ ++ ++/** ++ * This function enables the Device mode interrupts. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t *core_if) ++{ ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ dwc_otg_core_global_regs_t *global_regs = ++ core_if->core_global_regs; ++ ++ DWC_DEBUGPL(DBG_CIL, "%s()\n", __func__); ++ ++ /* Disable all interrupts. */ ++ dwc_write_reg32(&global_regs->gintmsk, 0); ++ ++ /* Clear any pending interrupts */ ++ dwc_write_reg32(&global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Enable the common interrupts */ ++ dwc_otg_enable_common_interrupts(core_if); ++ ++ /* Enable interrupts */ ++ intr_mask.b.usbreset = 1; ++ intr_mask.b.enumdone = 1; ++ ++ if(!core_if->multiproc_int_enable) { ++ intr_mask.b.inepintr = 1; ++ intr_mask.b.outepintr = 1; ++ } ++ ++ intr_mask.b.erlysuspend = 1; ++ ++ if(core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.epmismatch = 1; ++ } ++ ++ ++#ifdef DWC_EN_ISOC ++ if(core_if->dma_enable) { ++ if(core_if->dma_desc_enable == 0) { ++ if(core_if->pti_enh_enable) { ++ dctl_data_t dctl = { .d32 = 0 }; ++ dctl.b.ifrmnum = 1; ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dctl, 0, dctl.d32); ++ } else { ++ intr_mask.b.incomplisoin = 1; ++ intr_mask.b.incomplisoout = 1; ++ } ++ } ++ } else { ++ intr_mask.b.incomplisoin = 1; ++ intr_mask.b.incomplisoout = 1; ++ } ++#endif // DWC_EN_ISOC ++ ++/** @todo NGS: Should this be a module parameter? */ ++#ifdef USE_PERIODIC_EP ++ intr_mask.b.isooutdrop = 1; ++ intr_mask.b.eopframe = 1; ++ intr_mask.b.incomplisoin = 1; ++ intr_mask.b.incomplisoout = 1; ++#endif ++ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "%s() gintmsk=%0x\n", __func__, ++ dwc_read_reg32(&global_regs->gintmsk)); ++} ++ ++/** ++ * This function initializes the DWC_otg controller registers for ++ * device mode. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ */ ++void dwc_otg_core_dev_init(dwc_otg_core_if_t *core_if) ++{ ++ int i; ++ dwc_otg_core_global_regs_t *global_regs = ++ core_if->core_global_regs; ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_core_params_t *params = core_if->core_params; ++ dcfg_data_t dcfg = { .d32 = 0}; ++ grstctl_t resetctl = { .d32 = 0 }; ++ uint32_t rx_fifo_size; ++ fifosize_data_t nptxfifosize; ++ fifosize_data_t txfifosize; ++ dthrctl_data_t dthrctl; ++ fifosize_data_t ptxfifosize; ++ ++ /* Restart the Phy Clock */ ++ dwc_write_reg32(core_if->pcgcctl, 0); ++ ++ /* Device configuration register */ ++ init_devspd(core_if); ++ dcfg.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dcfg); ++ dcfg.b.descdma = (core_if->dma_desc_enable) ? 1 : 0; ++ dcfg.b.perfrint = DWC_DCFG_FRAME_INTERVAL_80; ++ ++ dwc_write_reg32(&dev_if->dev_global_regs->dcfg, dcfg.d32); ++ ++ /* Configure data FIFO sizes */ ++ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) { ++ DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n", core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n", params->dev_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n", params->dev_nperio_tx_fifo_size); ++ ++ /* Rx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->grxfsiz)); ++ ++ rx_fifo_size = params->dev_rx_fifo_size; ++ dwc_write_reg32(&global_regs->grxfsiz, rx_fifo_size); ++ ++ DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->grxfsiz)); ++ ++ /** Set Periodic Tx FIFO Mask all bits 0 */ ++ core_if->p_tx_msk = 0; ++ ++ /** Set Tx FIFO Mask all bits 0 */ ++ core_if->tx_msk = 0; ++ ++ if(core_if->en_multiple_tx_fifo == 0) { ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->dev_rx_fifo_size; ++ ++ dwc_write_reg32(&global_regs->gnptxfsiz, nptxfifosize.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++ /**@todo NGS: Fix Periodic FIFO Sizing! */ ++ /* ++ * Periodic Tx FIFOs These FIFOs are numbered from 1 to 15. ++ * Indexes of the FIFO size module parameters in the ++ * dev_perio_tx_fifo_size array and the FIFO size registers in ++ * the dptxfsiz array run from 0 to 14. ++ */ ++ /** @todo Finish debug of this */ ++ ptxfifosize.b.startaddr = nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ for (i=0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) ++ { ++ ptxfifosize.b.depth = params->dev_perio_tx_fifo_size[i]; ++ DWC_DEBUGPL(DBG_CIL, "initial dptxfsiz_dieptxf[%d]=%08x\n", i, ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i])); ++ dwc_write_reg32(&global_regs->dptxfsiz_dieptxf[i], ++ ptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL, "new dptxfsiz_dieptxf[%d]=%08x\n", i, ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i])); ++ ptxfifosize.b.startaddr += ptxfifosize.b.depth; ++ } ++ } ++ else { ++ /* ++ * Tx FIFOs These FIFOs are numbered from 1 to 15. ++ * Indexes of the FIFO size module parameters in the ++ * dev_tx_fifo_size array and the FIFO size registers in ++ * the dptxfsiz_dieptxf array run from 0 to 14. ++ */ ++ ++ ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->dev_rx_fifo_size; ++ ++ dwc_write_reg32(&global_regs->gnptxfsiz, nptxfifosize.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++ txfifosize.b.startaddr = nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ /* ++ Modify by kaiker ,for RT3052 device mode config ++ ++ In RT3052,Since the _core_if->hwcfg4.b.num_dev_perio_in_ep is ++ configed to 0 so these TX_FIF0 not config.IN EP will can't ++ more than 1 if not modify it. ++ ++ */ ++#if 1 ++ for (i=1 ; i <= dev_if->num_in_eps; i++) ++#else ++ for (i=1; i < _core_if->hwcfg4.b.num_dev_perio_in_ep; i++) ++#endif ++ { ++ ++ txfifosize.b.depth = params->dev_tx_fifo_size[i]; ++ ++ DWC_DEBUGPL(DBG_CIL, "initial dptxfsiz_dieptxf[%d]=%08x\n", i, ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i])); ++ ++ dwc_write_reg32(&global_regs->dptxfsiz_dieptxf[i-1], ++ txfifosize.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "new dptxfsiz_dieptxf[%d]=%08x\n", i, ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i-1])); ++ ++ txfifosize.b.startaddr += txfifosize.b.depth; ++ } ++ } ++ } ++ /* Flush the FIFOs */ ++ dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */ ++ dwc_otg_flush_rx_fifo(core_if); ++ ++ /* Flush the Learning Queue. */ ++ resetctl.b.intknqflsh = 1; ++ dwc_write_reg32(&core_if->core_global_regs->grstctl, resetctl.d32); ++ ++ /* Clear all pending Device Interrupts */ ++ ++ if(core_if->multiproc_int_enable) { ++ } ++ ++ /** @todo - if the condition needed to be checked ++ * or in any case all pending interrutps should be cleared? ++ */ ++ if(core_if->multiproc_int_enable) { ++ for(i = 0; i < core_if->dev_if->num_in_eps; ++i) { ++ dwc_write_reg32(&dev_if->dev_global_regs->diepeachintmsk[i], 0); ++ } ++ ++ for(i = 0; i < core_if->dev_if->num_out_eps; ++i) { ++ dwc_write_reg32(&dev_if->dev_global_regs->doepeachintmsk[i], 0); ++ } ++ ++ dwc_write_reg32(&dev_if->dev_global_regs->deachint, 0xFFFFFFFF); ++ dwc_write_reg32(&dev_if->dev_global_regs->deachintmsk, 0); ++ } else { ++ dwc_write_reg32(&dev_if->dev_global_regs->diepmsk, 0); ++ dwc_write_reg32(&dev_if->dev_global_regs->doepmsk, 0); ++ dwc_write_reg32(&dev_if->dev_global_regs->daint, 0xFFFFFFFF); ++ dwc_write_reg32(&dev_if->dev_global_regs->daintmsk, 0); ++ } ++ ++ for (i=0; i <= dev_if->num_in_eps; i++) ++ { ++ depctl_data_t depctl; ++ depctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl); ++ if (depctl.b.epena) { ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ } ++ else { ++ depctl.d32 = 0; ++ } ++ ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32); ++ ++ ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->dieptsiz, 0); ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->diepdma, 0); ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->diepint, 0xFF); ++ } ++ ++ for (i=0; i <= dev_if->num_out_eps; i++) ++ { ++ depctl_data_t depctl; ++ depctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[i]->doepctl); ++ if (depctl.b.epena) { ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ } ++ else { ++ depctl.d32 = 0; ++ } ++ ++ dwc_write_reg32(&dev_if->out_ep_regs[i]->doepctl, depctl.d32); ++ ++ dwc_write_reg32(&dev_if->out_ep_regs[i]->doeptsiz, 0); ++ dwc_write_reg32(&dev_if->out_ep_regs[i]->doepdma, 0); ++ dwc_write_reg32(&dev_if->out_ep_regs[i]->doepint, 0xFF); ++ } ++ ++ if(core_if->en_multiple_tx_fifo && core_if->dma_enable) { ++ dev_if->non_iso_tx_thr_en = params->thr_ctl & 0x1; ++ dev_if->iso_tx_thr_en = (params->thr_ctl >> 1) & 0x1; ++ dev_if->rx_thr_en = (params->thr_ctl >> 2) & 0x1; ++ ++ dev_if->rx_thr_length = params->rx_thr_length; ++ dev_if->tx_thr_length = params->tx_thr_length; ++ ++ dev_if->setup_desc_index = 0; ++ ++ dthrctl.d32 = 0; ++ dthrctl.b.non_iso_thr_en = dev_if->non_iso_tx_thr_en; ++ dthrctl.b.iso_thr_en = dev_if->iso_tx_thr_en; ++ dthrctl.b.tx_thr_len = dev_if->tx_thr_length; ++ dthrctl.b.rx_thr_en = dev_if->rx_thr_en; ++ dthrctl.b.rx_thr_len = dev_if->rx_thr_length; ++ ++ dwc_write_reg32(&dev_if->dev_global_regs->dtknqr3_dthrctl, dthrctl.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "Non ISO Tx Thr - %d\nISO Tx Thr - %d\nRx Thr - %d\nTx Thr Len - %d\nRx Thr Len - %d\n", ++ dthrctl.b.non_iso_thr_en, dthrctl.b.iso_thr_en, dthrctl.b.rx_thr_en, dthrctl.b.tx_thr_len, dthrctl.b.rx_thr_len); ++ ++ } ++ ++ dwc_otg_enable_device_interrupts(core_if); ++ ++ { ++ diepmsk_data_t msk = { .d32 = 0 }; ++ msk.b.txfifoundrn = 1; ++ if(core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&dev_if->dev_global_regs->diepeachintmsk[0], msk.d32, msk.d32); ++ } else { ++ dwc_modify_reg32(&dev_if->dev_global_regs->diepmsk, msk.d32, msk.d32); ++ } ++ } ++ ++ ++ if(core_if->multiproc_int_enable) { ++ /* Set NAK on Babble */ ++ dctl_data_t dctl = { .d32 = 0}; ++ dctl.b.nakonbble = 1; ++ dwc_modify_reg32(&dev_if->dev_global_regs->dctl, 0, dctl.d32); ++ } ++} ++ ++/** ++ * This function enables the Host mode interrupts. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t *core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gintmsk_data_t intr_mask = { .d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CIL, "%s()\n", __func__); ++ ++ /* Disable all interrupts. */ ++ dwc_write_reg32(&global_regs->gintmsk, 0); ++ ++ /* Clear any pending interrupts. */ ++ dwc_write_reg32(&global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Enable the common interrupts */ ++ dwc_otg_enable_common_interrupts(core_if); ++ ++ /* ++ * Enable host mode interrupts without disturbing common ++ * interrupts. ++ */ ++ intr_mask.b.sofintr = 1; ++ intr_mask.b.portintr = 1; ++ intr_mask.b.hcintr = 1; ++ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++} ++ ++/** ++ * This function disables the Host Mode interrupts. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t *core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = ++ core_if->core_global_regs; ++ gintmsk_data_t intr_mask = { .d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s()\n", __func__); ++ ++ /* ++ * Disable host mode interrupts without disturbing common ++ * interrupts. ++ */ ++ intr_mask.b.sofintr = 1; ++ intr_mask.b.portintr = 1; ++ intr_mask.b.hcintr = 1; ++ intr_mask.b.ptxfempty = 1; ++ intr_mask.b.nptxfempty = 1; ++ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, 0); ++} ++ ++/** ++ * This function initializes the DWC_otg controller registers for ++ * host mode. ++ * ++ * This function flushes the Tx and Rx FIFOs and it flushes any entries in the ++ * request queues. Host channels are reset to ensure that they are ready for ++ * performing transfers. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ */ ++void dwc_otg_core_host_init(dwc_otg_core_if_t *core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ dwc_otg_host_if_t *host_if = core_if->host_if; ++ dwc_otg_core_params_t *params = core_if->core_params; ++ hprt0_data_t hprt0 = { .d32 = 0 }; ++ fifosize_data_t nptxfifosize; ++ fifosize_data_t ptxfifosize; ++ int i; ++ hcchar_data_t hcchar; ++ hcfg_data_t hcfg; ++ dwc_otg_hc_regs_t *hc_regs; ++ int num_channels; ++ gotgctl_data_t gotgctl = { .d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CILV,"%s(%p)\n", __func__, core_if); ++ ++ /* Restart the Phy Clock */ ++ dwc_write_reg32(core_if->pcgcctl, 0); ++ ++ /* Initialize Host Configuration Register */ ++ init_fslspclksel(core_if); ++ if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) ++ { ++ hcfg.d32 = dwc_read_reg32(&host_if->host_global_regs->hcfg); ++ hcfg.b.fslssupp = 1; ++ dwc_write_reg32(&host_if->host_global_regs->hcfg, hcfg.d32); ++ } ++ ++ /* Configure data FIFO sizes */ ++ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) { ++ DWC_DEBUGPL(DBG_CIL,"Total FIFO Size=%d\n", core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL,"Rx FIFO Size=%d\n", params->host_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL,"NP Tx FIFO Size=%d\n", params->host_nperio_tx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL,"P Tx FIFO Size=%d\n", params->host_perio_tx_fifo_size); ++ ++ /* Rx FIFO */ ++ DWC_DEBUGPL(DBG_CIL,"initial grxfsiz=%08x\n", dwc_read_reg32(&global_regs->grxfsiz)); ++ dwc_write_reg32(&global_regs->grxfsiz, params->host_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL,"new grxfsiz=%08x\n", dwc_read_reg32(&global_regs->grxfsiz)); ++ ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL,"initial gnptxfsiz=%08x\n", dwc_read_reg32(&global_regs->gnptxfsiz)); ++ nptxfifosize.b.depth = params->host_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->host_rx_fifo_size; ++ dwc_write_reg32(&global_regs->gnptxfsiz, nptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL,"new gnptxfsiz=%08x\n", dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++ /* Periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL,"initial hptxfsiz=%08x\n", dwc_read_reg32(&global_regs->hptxfsiz)); ++ ptxfifosize.b.depth = params->host_perio_tx_fifo_size; ++ ptxfifosize.b.startaddr = nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ dwc_write_reg32(&global_regs->hptxfsiz, ptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL,"new hptxfsiz=%08x\n", dwc_read_reg32(&global_regs->hptxfsiz)); ++ } ++ ++ /* Clear Host Set HNP Enable in the OTG Control Register */ ++ gotgctl.b.hstsethnpen = 1; ++ dwc_modify_reg32(&global_regs->gotgctl, gotgctl.d32, 0); ++ ++ /* Make sure the FIFOs are flushed. */ ++ dwc_otg_flush_tx_fifo(core_if, 0x10 /* all Tx FIFOs */); ++ dwc_otg_flush_rx_fifo(core_if); ++ ++ /* Flush out any leftover queued requests. */ ++ num_channels = core_if->core_params->host_channels; ++ for (i = 0; i < num_channels; i++) ++ { ++ hc_regs = core_if->host_if->hc_regs[i]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.chen = 0; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ } ++ ++ /* Halt all channels to put them into a known state. */ ++ for (i = 0; i < num_channels; i++) ++ { ++ int count = 0; ++ hc_regs = core_if->host_if->hc_regs[i]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ DWC_DEBUGPL(DBG_HCDV, "%s: Halt channel %d\n", __func__, i); ++ do { ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (++count > 1000) ++ { ++ DWC_ERROR("%s: Unable to clear halt on channel %d\n", ++ __func__, i); ++ break; ++ } ++ } ++ while (hcchar.b.chen); ++ } ++ ++ /* Turn on the vbus power. */ ++ DWC_PRINT("Init: Port Power? op_state=%d\n", core_if->op_state); ++ if (core_if->op_state == A_HOST) { ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ DWC_PRINT("Init: Power Port (%d)\n", hprt0.b.prtpwr); ++ if (hprt0.b.prtpwr == 0) { ++ hprt0.b.prtpwr = 1; ++ dwc_write_reg32(host_if->hprt0, hprt0.d32); ++ } ++ } ++ ++ dwc_otg_enable_host_interrupts(core_if); ++} ++ ++/** ++ * Prepares a host channel for transferring packets to/from a specific ++ * endpoint. The HCCHARn register is set up with the characteristics specified ++ * in _hc. Host channel interrupts that may need to be serviced while this ++ * transfer is in progress are enabled. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * @param hc Information needed to initialize the host channel ++ */ ++void dwc_otg_hc_init(dwc_otg_core_if_t *core_if, dwc_hc_t *hc) ++{ ++ uint32_t intr_enable; ++ hcintmsk_data_t hc_intr_mask; ++ gintmsk_data_t gintmsk = { .d32 = 0 }; ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ ++ uint8_t hc_num = hc->hc_num; ++ dwc_otg_host_if_t *host_if = core_if->host_if; ++ dwc_otg_hc_regs_t *hc_regs = host_if->hc_regs[hc_num]; ++ ++ /* Clear old interrupt conditions for this host channel. */ ++ hc_intr_mask.d32 = 0xFFFFFFFF; ++ hc_intr_mask.b.reserved = 0; ++ dwc_write_reg32(&hc_regs->hcint, hc_intr_mask.d32); ++ ++ /* Enable channel interrupts required for this transfer. */ ++ hc_intr_mask.d32 = 0; ++ hc_intr_mask.b.chhltd = 1; ++ if (core_if->dma_enable) { ++ hc_intr_mask.b.ahberr = 1; ++ if (hc->error_state && !hc->do_split && ++ hc->ep_type != DWC_OTG_EP_TYPE_ISOC) { ++ hc_intr_mask.b.ack = 1; ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.datatglerr = 1; ++ if (hc->ep_type != DWC_OTG_EP_TYPE_INTR) { ++ hc_intr_mask.b.nak = 1; ++ } ++ } ++ } ++ } ++ else { ++ switch (hc->ep_type) { ++ case DWC_OTG_EP_TYPE_CONTROL: ++ case DWC_OTG_EP_TYPE_BULK: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.stall = 1; ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.datatglerr = 1; ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.bblerr = 1; ++ } ++ else { ++ hc_intr_mask.b.nak = 1; ++ hc_intr_mask.b.nyet = 1; ++ if (hc->do_ping) { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ ++ if (hc->do_split) { ++ hc_intr_mask.b.nak = 1; ++ if (hc->complete_split) { ++ hc_intr_mask.b.nyet = 1; ++ } ++ else { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ ++ if (hc->error_state) { ++ hc_intr_mask.b.ack = 1; ++ } ++ break; ++ case DWC_OTG_EP_TYPE_INTR: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.nak = 1; ++ hc_intr_mask.b.stall = 1; ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.datatglerr = 1; ++ hc_intr_mask.b.frmovrun = 1; ++ ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.bblerr = 1; ++ } ++ if (hc->error_state) { ++ hc_intr_mask.b.ack = 1; ++ } ++ if (hc->do_split) { ++ if (hc->complete_split) { ++ hc_intr_mask.b.nyet = 1; ++ } ++ else { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ break; ++ case DWC_OTG_EP_TYPE_ISOC: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.frmovrun = 1; ++ hc_intr_mask.b.ack = 1; ++ ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.bblerr = 1; ++ } ++ break; ++ } ++ } ++ dwc_write_reg32(&hc_regs->hcintmsk, hc_intr_mask.d32); ++ ++// if(hc->ep_type == DWC_OTG_EP_TYPE_BULK && !hc->ep_is_in) ++// hc->max_packet = 512; ++ /* Enable the top level host channel interrupt. */ ++ intr_enable = (1 << hc_num); ++ dwc_modify_reg32(&host_if->host_global_regs->haintmsk, 0, intr_enable); ++ ++ /* Make sure host channel interrupts are enabled. */ ++ gintmsk.b.hcintr = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32); ++ ++ /* ++ * Program the HCCHARn register with the endpoint characteristics for ++ * the current transfer. ++ */ ++ hcchar.d32 = 0; ++ hcchar.b.devaddr = hc->dev_addr; ++ hcchar.b.epnum = hc->ep_num; ++ hcchar.b.epdir = hc->ep_is_in; ++ hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW); ++ hcchar.b.eptype = hc->ep_type; ++ hcchar.b.mps = hc->max_packet; ++ ++ dwc_write_reg32(&host_if->hc_regs[hc_num]->hcchar, hcchar.d32); ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " Dev Addr: %d\n", hcchar.b.devaddr); ++ DWC_DEBUGPL(DBG_HCDV, " Ep Num: %d\n", hcchar.b.epnum); ++ DWC_DEBUGPL(DBG_HCDV, " Is In: %d\n", hcchar.b.epdir); ++ DWC_DEBUGPL(DBG_HCDV, " Is Low Speed: %d\n", hcchar.b.lspddev); ++ DWC_DEBUGPL(DBG_HCDV, " Ep Type: %d\n", hcchar.b.eptype); ++ DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps); ++ DWC_DEBUGPL(DBG_HCDV, " Multi Cnt: %d\n", hcchar.b.multicnt); ++ ++ /* ++ * Program the HCSPLIT register for SPLITs ++ */ ++ hcsplt.d32 = 0; ++ if (hc->do_split) { ++ DWC_DEBUGPL(DBG_HCDV, "Programming HC %d with split --> %s\n", hc->hc_num, ++ hc->complete_split ? "CSPLIT" : "SSPLIT"); ++ hcsplt.b.compsplt = hc->complete_split; ++ hcsplt.b.xactpos = hc->xact_pos; ++ hcsplt.b.hubaddr = hc->hub_addr; ++ hcsplt.b.prtaddr = hc->port_addr; ++ DWC_DEBUGPL(DBG_HCDV, " comp split %d\n", hc->complete_split); ++ DWC_DEBUGPL(DBG_HCDV, " xact pos %d\n", hc->xact_pos); ++ DWC_DEBUGPL(DBG_HCDV, " hub addr %d\n", hc->hub_addr); ++ DWC_DEBUGPL(DBG_HCDV, " port addr %d\n", hc->port_addr); ++ DWC_DEBUGPL(DBG_HCDV, " is_in %d\n", hc->ep_is_in); ++ DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps); ++ DWC_DEBUGPL(DBG_HCDV, " xferlen: %d\n", hc->xfer_len); ++ } ++ dwc_write_reg32(&host_if->hc_regs[hc_num]->hcsplt, hcsplt.d32); ++ ++} ++ ++/** ++ * Attempts to halt a host channel. This function should only be called in ++ * Slave mode or to abort a transfer in either Slave mode or DMA mode. Under ++ * normal circumstances in DMA mode, the controller halts the channel when the ++ * transfer is complete or a condition occurs that requires application ++ * intervention. ++ * ++ * In slave mode, checks for a free request queue entry, then sets the Channel ++ * Enable and Channel Disable bits of the Host Channel Characteristics ++ * register of the specified channel to intiate the halt. If there is no free ++ * request queue entry, sets only the Channel Disable bit of the HCCHARn ++ * register to flush requests for this channel. In the latter case, sets a ++ * flag to indicate that the host channel needs to be halted when a request ++ * queue slot is open. ++ * ++ * In DMA mode, always sets the Channel Enable and Channel Disable bits of the ++ * HCCHARn register. The controller ensures there is space in the request ++ * queue before submitting the halt request. ++ * ++ * Some time may elapse before the core flushes any posted requests for this ++ * host channel and halts. The Channel Halted interrupt handler completes the ++ * deactivation of the host channel. ++ * ++ * @param core_if Controller register interface. ++ * @param hc Host channel to halt. ++ * @param halt_status Reason for halting the channel. ++ */ ++void dwc_otg_hc_halt(dwc_otg_core_if_t *core_if, ++ dwc_hc_t *hc, ++ dwc_otg_halt_status_e halt_status) ++{ ++ gnptxsts_data_t nptxsts; ++ hptxsts_data_t hptxsts; ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs; ++ dwc_otg_core_global_regs_t *global_regs; ++ dwc_otg_host_global_regs_t *host_global_regs; ++ ++ hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ global_regs = core_if->core_global_regs; ++ host_global_regs = core_if->host_if->host_global_regs; ++ ++ WARN_ON(halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ ++ if (halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE || ++ halt_status == DWC_OTG_HC_XFER_AHB_ERR) { ++ /* ++ * Disable all channel interrupts except Ch Halted. The QTD ++ * and QH state associated with this transfer has been cleared ++ * (in the case of URB_DEQUEUE), so the channel needs to be ++ * shut down carefully to prevent crashes. ++ */ ++ hcintmsk_data_t hcintmsk; ++ hcintmsk.d32 = 0; ++ hcintmsk.b.chhltd = 1; ++ dwc_write_reg32(&hc_regs->hcintmsk, hcintmsk.d32); ++ ++ /* ++ * Make sure no other interrupts besides halt are currently ++ * pending. Handling another interrupt could cause a crash due ++ * to the QTD and QH state. ++ */ ++ dwc_write_reg32(&hc_regs->hcint, ~hcintmsk.d32); ++ ++ /* ++ * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR ++ * even if the channel was already halted for some other ++ * reason. ++ */ ++ hc->halt_status = halt_status; ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen == 0) { ++ /* ++ * The channel is either already halted or it hasn't ++ * started yet. In DMA mode, the transfer may halt if ++ * it finishes normally or a condition occurs that ++ * requires driver intervention. Don't want to halt ++ * the channel again. In either Slave or DMA mode, ++ * it's possible that the transfer has been assigned ++ * to a channel, but not started yet when an URB is ++ * dequeued. Don't want to halt a channel that hasn't ++ * started yet. ++ */ ++ return; ++ } ++ } ++ ++ if (hc->halt_pending) { ++ /* ++ * A halt has already been issued for this channel. This might ++ * happen when a transfer is aborted by a higher level in ++ * the stack. ++ */ ++#ifdef DEBUG ++ DWC_PRINT("*** %s: Channel %d, _hc->halt_pending already set ***\n", ++ __func__, hc->hc_num); ++ ++/* dwc_otg_dump_global_registers(core_if); */ ++/* dwc_otg_dump_host_registers(core_if); */ ++#endif ++ return; ++ } ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 1; ++ ++ if (!core_if->dma_enable) { ++ /* Check for space in the request queue to issue the halt. */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || ++ hc->ep_type == DWC_OTG_EP_TYPE_BULK) { ++ nptxsts.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ if (nptxsts.b.nptxqspcavail == 0) { ++ hcchar.b.chen = 0; ++ } ++ } ++ else { ++ hptxsts.d32 = dwc_read_reg32(&host_global_regs->hptxsts); ++ if ((hptxsts.b.ptxqspcavail == 0) || (core_if->queuing_high_bandwidth)) { ++ hcchar.b.chen = 0; ++ } ++ } ++ } ++ ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ hc->halt_status = halt_status; ++ ++ if (hcchar.b.chen) { ++ hc->halt_pending = 1; ++ hc->halt_on_queue = 0; ++ } ++ else { ++ hc->halt_on_queue = 1; ++ } ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " hcchar: 0x%08x\n", hcchar.d32); ++ DWC_DEBUGPL(DBG_HCDV, " halt_pending: %d\n", hc->halt_pending); ++ DWC_DEBUGPL(DBG_HCDV, " halt_on_queue: %d\n", hc->halt_on_queue); ++ DWC_DEBUGPL(DBG_HCDV, " halt_status: %d\n", hc->halt_status); ++ ++ return; ++} ++ ++/** ++ * Clears the transfer state for a host channel. This function is normally ++ * called after a transfer is done and the host channel is being released. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param hc Identifies the host channel to clean up. ++ */ ++void dwc_otg_hc_cleanup(dwc_otg_core_if_t *core_if, dwc_hc_t *hc) ++{ ++ dwc_otg_hc_regs_t *hc_regs; ++ ++ hc->xfer_started = 0; ++ ++ /* ++ * Clear channel interrupt enables and any unhandled channel interrupt ++ * conditions. ++ */ ++ hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ dwc_write_reg32(&hc_regs->hcintmsk, 0); ++ dwc_write_reg32(&hc_regs->hcint, 0xFFFFFFFF); ++ ++#ifdef DEBUG ++ del_timer(&core_if->hc_xfer_timer[hc->hc_num]); ++ { ++ hcchar_data_t hcchar; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", ++ __func__, hc->hc_num, hcchar.d32); ++ } ++ } ++#endif ++} ++ ++/** ++ * Sets the channel property that indicates in which frame a periodic transfer ++ * should occur. This is always set to the _next_ frame. This function has no ++ * effect on non-periodic transfers. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param hc Identifies the host channel to set up and its properties. ++ * @param hcchar Current value of the HCCHAR register for the specified host ++ * channel. ++ */ ++static inline void hc_set_even_odd_frame(dwc_otg_core_if_t *core_if, ++ dwc_hc_t *hc, ++ hcchar_data_t *hcchar) ++{ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ hfnum_data_t hfnum; ++ hfnum.d32 = dwc_read_reg32(&core_if->host_if->host_global_regs->hfnum); ++ ++ /* 1 if _next_ frame is odd, 0 if it's even */ ++ hcchar->b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1; ++#ifdef DEBUG ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR && hc->do_split && !hc->complete_split) { ++ switch (hfnum.b.frnum & 0x7) { ++ case 7: ++ core_if->hfnum_7_samples++; ++ core_if->hfnum_7_frrem_accum += hfnum.b.frrem; ++ break; ++ case 0: ++ core_if->hfnum_0_samples++; ++ core_if->hfnum_0_frrem_accum += hfnum.b.frrem; ++ break; ++ default: ++ core_if->hfnum_other_samples++; ++ core_if->hfnum_other_frrem_accum += hfnum.b.frrem; ++ break; ++ } ++ } ++#endif ++ } ++} ++ ++#ifdef DEBUG ++static void hc_xfer_timeout(unsigned long ptr) ++{ ++ hc_xfer_info_t *xfer_info = (hc_xfer_info_t *)ptr; ++ int hc_num = xfer_info->hc->hc_num; ++ DWC_WARN("%s: timeout on channel %d\n", __func__, hc_num); ++ DWC_WARN(" start_hcchar_val 0x%08x\n", xfer_info->core_if->start_hcchar_val[hc_num]); ++} ++#endif ++ ++/* ++ * This function does the setup for a data transfer for a host channel and ++ * starts the transfer. May be called in either Slave mode or DMA mode. In ++ * Slave mode, the caller must ensure that there is sufficient space in the ++ * request queue and Tx Data FIFO. ++ * ++ * For an OUT transfer in Slave mode, it loads a data packet into the ++ * appropriate FIFO. If necessary, additional data packets will be loaded in ++ * the Host ISR. ++ * ++ * For an IN transfer in Slave mode, a data packet is requested. The data ++ * packets are unloaded from the Rx FIFO in the Host ISR. If necessary, ++ * additional data packets are requested in the Host ISR. ++ * ++ * For a PING transfer in Slave mode, the Do Ping bit is set in the egards, ++ * ++ * Steven ++ * ++ * register along with a packet count of 1 and the channel is enabled. This ++ * causes a single PING transaction to occur. Other fields in HCTSIZ are ++ * simply set to 0 since no data transfer occurs in this case. ++ * ++ * For a PING transfer in DMA mode, the HCTSIZ register is initialized with ++ * all the information required to perform the subsequent data transfer. In ++ * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the ++ * controller performs the entire PING protocol, then starts the data ++ * transfer. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param hc Information needed to initialize the host channel. The xfer_len ++ * value may be reduced to accommodate the max widths of the XferSize and ++ * PktCnt fields in the HCTSIZn register. The multi_count value may be changed ++ * to reflect the final xfer_len value. ++ */ ++void dwc_otg_hc_start_transfer(dwc_otg_core_if_t *core_if, dwc_hc_t *hc) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ uint16_t num_packets; ++ uint32_t max_hc_xfer_size = core_if->core_params->max_transfer_size; ++ uint16_t max_hc_pkt_count = core_if->core_params->max_packet_count; ++ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ ++ hctsiz.d32 = 0; ++ ++ if (hc->do_ping) { ++ if (!core_if->dma_enable) { ++ dwc_otg_hc_do_ping(core_if, hc); ++ hc->xfer_started = 1; ++ return; ++ } ++ else { ++ hctsiz.b.dopng = 1; ++ } ++ } ++ ++ if (hc->do_split) { ++ num_packets = 1; ++ ++ if (hc->complete_split && !hc->ep_is_in) { ++ /* For CSPLIT OUT Transfer, set the size to 0 so the ++ * core doesn't expect any data written to the FIFO */ ++ hc->xfer_len = 0; ++ } ++ else if (hc->ep_is_in || (hc->xfer_len > hc->max_packet)) { ++ hc->xfer_len = hc->max_packet; ++ } ++ else if (!hc->ep_is_in && (hc->xfer_len > 188)) { ++ hc->xfer_len = 188; ++ } ++ ++ hctsiz.b.xfersize = hc->xfer_len; ++ } ++ else { ++ /* ++ * Ensure that the transfer length and packet count will fit ++ * in the widths allocated for them in the HCTSIZn register. ++ */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * Make sure the transfer size is no larger than one ++ * (micro)frame's worth of data. (A check was done ++ * when the periodic transfer was accepted to ensure ++ * that a (micro)frame's worth of data can be ++ * programmed into a channel.) ++ */ ++ uint32_t max_periodic_len = hc->multi_count * hc->max_packet; ++ if (hc->xfer_len > max_periodic_len) { ++ hc->xfer_len = max_periodic_len; ++ } ++ else { ++ } ++ ++ } ++ else if (hc->xfer_len > max_hc_xfer_size) { ++ /* Make sure that xfer_len is a multiple of max packet size. */ ++ hc->xfer_len = max_hc_xfer_size - hc->max_packet + 1; ++ } ++ ++ if (hc->xfer_len > 0) { ++ num_packets = (hc->xfer_len + hc->max_packet - 1) / hc->max_packet; ++ if (num_packets > max_hc_pkt_count) { ++ num_packets = max_hc_pkt_count; ++ hc->xfer_len = num_packets * hc->max_packet; ++ } ++ } ++ else { ++ /* Need 1 packet for transfer length of 0. */ ++ num_packets = 1; ++ } ++ ++ if (hc->ep_is_in) { ++ /* Always program an integral # of max packets for IN transfers. */ ++ hc->xfer_len = num_packets * hc->max_packet; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * Make sure that the multi_count field matches the ++ * actual transfer length. ++ */ ++ hc->multi_count = num_packets; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* Set up the initial PID for the transfer. */ ++ if (hc->speed == DWC_OTG_EP_SPEED_HIGH) { ++ if (hc->ep_is_in) { ++ if (hc->multi_count == 1) { ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA0; ++ } ++ else if (hc->multi_count == 2) { ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA1; ++ } ++ else { ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA2; ++ } ++ } ++ else { ++ if (hc->multi_count == 1) { ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA0; ++ } ++ else { ++ hc->data_pid_start = DWC_OTG_HC_PID_MDATA; ++ } ++ } ++ } ++ else { ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA0; ++ } ++ } ++ ++ hctsiz.b.xfersize = hc->xfer_len; ++ } ++ ++ hc->start_pkt_count = num_packets; ++ hctsiz.b.pktcnt = num_packets; ++ hctsiz.b.pid = hc->data_pid_start; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " Xfer Size: %d\n", hctsiz.b.xfersize); ++ DWC_DEBUGPL(DBG_HCDV, " Num Pkts: %d\n", hctsiz.b.pktcnt); ++ DWC_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid); ++ ++ if (core_if->dma_enable) { ++#if defined (CONFIG_DWC_OTG_HOST_ONLY) ++ if ((uint32_t)hc->xfer_buff & 0x3) { ++ /* non DWORD-aligned buffer case*/ ++ if(!hc->qh->dw_align_buf) { ++ hc->qh->dw_align_buf = ++ dma_alloc_coherent(NULL, ++ core_if->core_params->max_transfer_size, ++ &hc->qh->dw_align_buf_dma, ++ GFP_ATOMIC | GFP_DMA); ++ if (!hc->qh->dw_align_buf) { ++ ++ DWC_ERROR("%s: Failed to allocate memory to handle " ++ "non-dword aligned buffer case\n", __func__); ++ return; ++ } ++ ++ } ++ if (!hc->ep_is_in) { ++ memcpy(hc->qh->dw_align_buf, phys_to_virt((uint32_t)hc->xfer_buff), hc->xfer_len); ++ } ++ ++ dwc_write_reg32(&hc_regs->hcdma, hc->qh->dw_align_buf_dma); ++ } ++ else ++#endif ++ dwc_write_reg32(&hc_regs->hcdma, (uint32_t)hc->xfer_buff); ++ } ++ ++ /* Start the split */ ++ if (hc->do_split) { ++ hcsplt_data_t hcsplt; ++ hcsplt.d32 = dwc_read_reg32 (&hc_regs->hcsplt); ++ hcsplt.b.spltena = 1; ++ dwc_write_reg32(&hc_regs->hcsplt, hcsplt.d32); ++ } ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.multicnt = hc->multi_count; ++ hc_set_even_odd_frame(core_if, hc, &hcchar); ++#ifdef DEBUG ++ core_if->start_hcchar_val[hc->hc_num] = hcchar.d32; ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", ++ __func__, hc->hc_num, hcchar.d32); ++ } ++#endif ++ ++ /* Set host channel enable after all other setup is complete. */ ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ hc->xfer_started = 1; ++ hc->requests++; ++ ++ if (!core_if->dma_enable && ++ !hc->ep_is_in && hc->xfer_len > 0) { ++ /* Load OUT packet into the appropriate Tx FIFO. */ ++ dwc_otg_hc_write_packet(core_if, hc); ++ } ++ ++#ifdef DEBUG ++ /* Start a timer for this transfer. */ ++ core_if->hc_xfer_timer[hc->hc_num].function = hc_xfer_timeout; ++ core_if->hc_xfer_info[hc->hc_num].core_if = core_if; ++ core_if->hc_xfer_info[hc->hc_num].hc = hc; ++ core_if->hc_xfer_timer[hc->hc_num].data = (unsigned long)(&core_if->hc_xfer_info[hc->hc_num]); ++ core_if->hc_xfer_timer[hc->hc_num].expires = jiffies + (HZ*10); ++ add_timer(&core_if->hc_xfer_timer[hc->hc_num]); ++#endif ++} ++ ++/** ++ * This function continues a data transfer that was started by previous call ++ * to dwc_otg_hc_start_transfer. The caller must ensure there is ++ * sufficient space in the request queue and Tx Data FIFO. This function ++ * should only be called in Slave mode. In DMA mode, the controller acts ++ * autonomously to complete transfers programmed to a host channel. ++ * ++ * For an OUT transfer, a new data packet is loaded into the appropriate FIFO ++ * if there is any data remaining to be queued. For an IN transfer, another ++ * data packet is always requested. For the SETUP phase of a control transfer, ++ * this function does nothing. ++ * ++ * @return 1 if a new request is queued, 0 if no more requests are required ++ * for this transfer. ++ */ ++int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t *core_if, dwc_hc_t *hc) ++{ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ ++ if (hc->do_split) { ++ /* SPLITs always queue just once per channel */ ++ return 0; ++ } ++ else if (hc->data_pid_start == DWC_OTG_HC_PID_SETUP) { ++ /* SETUPs are queued only once since they can't be NAKed. */ ++ return 0; ++ } ++ else if (hc->ep_is_in) { ++ /* ++ * Always queue another request for other IN transfers. If ++ * back-to-back INs are issued and NAKs are received for both, ++ * the driver may still be processing the first NAK when the ++ * second NAK is received. When the interrupt handler clears ++ * the NAK interrupt for the first NAK, the second NAK will ++ * not be seen. So we can't depend on the NAK interrupt ++ * handler to requeue a NAKed request. Instead, IN requests ++ * are issued each time this function is called. When the ++ * transfer completes, the extra requests for the channel will ++ * be flushed. ++ */ ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hc_set_even_odd_frame(core_if, hc, &hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ DWC_DEBUGPL(DBG_HCDV, " IN xfer: hcchar = 0x%08x\n", hcchar.d32); ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ hc->requests++; ++ return 1; ++ } ++ else { ++ /* OUT transfers. */ ++ if (hc->xfer_count < hc->xfer_len) { ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs; ++ hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hc_set_even_odd_frame(core_if, hc, &hcchar); ++ } ++ ++ /* Load OUT packet into the appropriate Tx FIFO. */ ++ dwc_otg_hc_write_packet(core_if, hc); ++ hc->requests++; ++ return 1; ++ } ++ else { ++ return 0; ++ } ++ } ++} ++ ++/** ++ * Starts a PING transfer. This function should only be called in Slave mode. ++ * The Do Ping bit is set in the HCTSIZ register, then the channel is enabled. ++ */ ++void dwc_otg_hc_do_ping(dwc_otg_core_if_t *core_if, dwc_hc_t *hc) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ ++ hctsiz.d32 = 0; ++ hctsiz.b.dopng = 1; ++ hctsiz.b.pktcnt = 1; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++} ++ ++/* ++ * This function writes a packet into the Tx FIFO associated with the Host ++ * Channel. For a channel associated with a non-periodic EP, the non-periodic ++ * Tx FIFO is written. For a channel associated with a periodic EP, the ++ * periodic Tx FIFO is written. This function should only be called in Slave ++ * mode. ++ * ++ * Upon return the xfer_buff and xfer_count fields in _hc are incremented by ++ * then number of bytes written to the Tx FIFO. ++ */ ++void dwc_otg_hc_write_packet(dwc_otg_core_if_t *core_if, dwc_hc_t *hc) ++{ ++ uint32_t i; ++ uint32_t remaining_count; ++ uint32_t byte_count; ++ uint32_t dword_count; ++ ++ uint32_t *data_buff = (uint32_t *)(hc->xfer_buff); ++ uint32_t *data_fifo = core_if->data_fifo[hc->hc_num]; ++ ++ remaining_count = hc->xfer_len - hc->xfer_count; ++ if (remaining_count > hc->max_packet) { ++ byte_count = hc->max_packet; ++ } ++ else { ++ byte_count = remaining_count; ++ } ++ ++ dword_count = (byte_count + 3) / 4; ++ ++ if ((((unsigned long)data_buff) & 0x3) == 0) { ++ /* xfer_buff is DWORD aligned. */ ++ for (i = 0; i < dword_count; i++, data_buff++) ++ { ++ dwc_write_reg32(data_fifo, *data_buff); ++ } ++ } ++ else { ++ /* xfer_buff is not DWORD aligned. */ ++ for (i = 0; i < dword_count; i++, data_buff++) ++ { ++ dwc_write_reg32(data_fifo, get_unaligned(data_buff)); ++ } ++ } ++ ++ hc->xfer_count += byte_count; ++ hc->xfer_buff += byte_count; ++} ++ ++/** ++ * Gets the current USB frame number. This is the frame number from the last ++ * SOF packet. ++ */ ++uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t *core_if) ++{ ++ dsts_data_t dsts; ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ /* read current frame/microframe number from DSTS register */ ++ return dsts.b.soffn; ++} ++ ++/** ++ * This function reads a setup packet from the Rx FIFO into the destination ++ * buffer. This function is called from the Rx Status Queue Level (RxStsQLvl) ++ * Interrupt routine when a SETUP packet has been received in Slave mode. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dest Destination buffer for packet data. ++ */ ++void dwc_otg_read_setup_packet(dwc_otg_core_if_t *core_if, uint32_t *dest) ++{ ++ /* Get the 8 bytes of a setup transaction data */ ++ ++ /* Pop 2 DWORDS off the receive data FIFO into memory */ ++ dest[0] = dwc_read_reg32(core_if->data_fifo[0]); ++ dest[1] = dwc_read_reg32(core_if->data_fifo[0]); ++} ++ ++ ++/** ++ * This function enables EP0 OUT to receive SETUP packets and configures EP0 ++ * IN for transmitting packets. It is normally called when the ++ * "Enumeration Done" interrupt occurs. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP0 data. ++ */ ++void dwc_otg_ep0_activate(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dsts_data_t dsts; ++ depctl_data_t diepctl; ++ depctl_data_t doepctl; ++ dctl_data_t dctl = { .d32 = 0 }; ++ ++ /* Read the Device Status and Endpoint 0 Control registers */ ++ dsts.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dsts); ++ diepctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl); ++ doepctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl); ++ ++ /* Set the MPS of the IN EP based on the enumeration speed */ ++ switch (dsts.b.enumspd) { ++ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: ++ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: ++ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: ++ diepctl.b.mps = DWC_DEP0CTL_MPS_64; ++ break; ++ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ: ++ diepctl.b.mps = DWC_DEP0CTL_MPS_8; ++ break; ++ } ++ ++ dwc_write_reg32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32); ++ ++ /* Enable OUT EP for receive */ ++ doepctl.b.epena = 1; ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32); ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV,"doepctl0=%0x\n", ++ dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl)); ++ DWC_DEBUGPL(DBG_PCDV,"diepctl0=%0x\n", ++ dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl)); ++#endif ++ dctl.b.cgnpinnak = 1; ++ ++ dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); ++ DWC_DEBUGPL(DBG_PCDV,"dctl=%0x\n", ++ dwc_read_reg32(&dev_if->dev_global_regs->dctl)); ++} ++ ++/** ++ * This function activates an EP. The Device EP control register for ++ * the EP is configured as defined in the ep structure. Note: This ++ * function is not used for EP0. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to activate. ++ */ ++void dwc_otg_ep_activate(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ depctl_data_t depctl; ++ volatile uint32_t *addr; ++ daint_data_t daintmsk = { .d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s() EP%d-%s\n", __func__, ep->num, ++ (ep->is_in?"IN":"OUT")); ++ ++ /* Read DEPCTLn register */ ++ if (ep->is_in == 1) { ++ addr = &dev_if->in_ep_regs[ep->num]->diepctl; ++ daintmsk.ep.in = 1<num; ++ } ++ else { ++ addr = &dev_if->out_ep_regs[ep->num]->doepctl; ++ daintmsk.ep.out = 1<num; ++ } ++ ++ /* If the EP is already active don't change the EP Control ++ * register. */ ++ depctl.d32 = dwc_read_reg32(addr); ++ if (!depctl.b.usbactep) { ++ depctl.b.mps = ep->maxpacket; ++ depctl.b.eptype = ep->type; ++ depctl.b.txfnum = ep->tx_fifo_num; ++ ++ if (ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ depctl.b.setd0pid = 1; // ??? ++ } ++ else { ++ depctl.b.setd0pid = 1; ++ } ++ depctl.b.usbactep = 1; ++ ++ dwc_write_reg32(addr, depctl.d32); ++ DWC_DEBUGPL(DBG_PCDV,"DEPCTL=%08x\n", dwc_read_reg32(addr)); ++ } ++ ++ /* Enable the Interrupt for this EP */ ++ if(core_if->multiproc_int_enable) { ++ if (ep->is_in == 1) { ++ diepmsk_data_t diepmsk = { .d32 = 0}; ++ diepmsk.b.xfercompl = 1; ++ diepmsk.b.timeout = 1; ++ diepmsk.b.epdisabled = 1; ++ diepmsk.b.ahberr = 1; ++ diepmsk.b.intknepmis = 1; ++ diepmsk.b.txfifoundrn = 1; //????? ++ ++ ++ if(core_if->dma_desc_enable) { ++ diepmsk.b.bna = 1; ++ } ++/* ++ if(core_if->dma_enable) { ++ doepmsk.b.nak = 1; ++ } ++*/ ++ dwc_write_reg32(&dev_if->dev_global_regs->diepeachintmsk[ep->num], diepmsk.d32); ++ ++ } else { ++ doepmsk_data_t doepmsk = { .d32 = 0}; ++ doepmsk.b.xfercompl = 1; ++ doepmsk.b.ahberr = 1; ++ doepmsk.b.epdisabled = 1; ++ ++ ++ if(core_if->dma_desc_enable) { ++ doepmsk.b.bna = 1; ++ } ++/* ++ doepmsk.b.babble = 1; ++ doepmsk.b.nyet = 1; ++ doepmsk.b.nak = 1; ++*/ ++ dwc_write_reg32(&dev_if->dev_global_regs->doepeachintmsk[ep->num], doepmsk.d32); ++ } ++ dwc_modify_reg32(&dev_if->dev_global_regs->deachintmsk, ++ 0, daintmsk.d32); ++ } else { ++ dwc_modify_reg32(&dev_if->dev_global_regs->daintmsk, ++ 0, daintmsk.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV,"DAINTMSK=%0x\n", ++ dwc_read_reg32(&dev_if->dev_global_regs->daintmsk)); ++ ++ ep->stall_clear_flag = 0; ++ return; ++} ++ ++/** ++ * This function deactivates an EP. This is done by clearing the USB Active ++ * EP bit in the Device EP control register. Note: This function is not used ++ * for EP0. EP0 cannot be deactivated. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to deactivate. ++ */ ++void dwc_otg_ep_deactivate(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ depctl_data_t depctl = { .d32 = 0 }; ++ volatile uint32_t *addr; ++ daint_data_t daintmsk = { .d32 = 0}; ++ ++ /* Read DEPCTLn register */ ++ if (ep->is_in == 1) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ daintmsk.ep.in = 1<num; ++ } ++ else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ daintmsk.ep.out = 1<num; ++ } ++ ++ depctl.b.usbactep = 0; ++ ++ if(core_if->dma_desc_enable) ++ depctl.b.epdis = 1; ++ ++ dwc_write_reg32(addr, depctl.d32); ++ ++ /* Disable the Interrupt for this EP */ ++ if(core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->deachintmsk, ++ daintmsk.d32, 0); ++ ++ if (ep->is_in == 1) { ++ dwc_write_reg32(&core_if->dev_if->dev_global_regs->diepeachintmsk[ep->num], 0); ++ } else { ++ dwc_write_reg32(&core_if->dev_if->dev_global_regs->doepeachintmsk[ep->num], 0); ++ } ++ } else { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->daintmsk, ++ daintmsk.d32, 0); ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for an EP and ++ * starts the transfer. For an IN transfer, the packets will be ++ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, ++ * the packets are unloaded from the Rx FIFO in the ISR. the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++static void init_dma_desc_chain(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ dwc_otg_dma_desc_t* dma_desc; ++ uint32_t offset; ++ uint32_t xfer_est; ++ int i; ++ ++ ep->desc_cnt = ( ep->total_len / ep->maxxfer) + ++ ((ep->total_len % ep->maxxfer) ? 1 : 0); ++ if(!ep->desc_cnt) ++ ep->desc_cnt = 1; ++ ++ dma_desc = ep->desc_addr; ++ xfer_est = ep->total_len; ++ offset = 0; ++ for( i = 0; i < ep->desc_cnt; ++i) { ++ /** DMA Descriptor Setup */ ++ if(xfer_est > ep->maxxfer) { ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 0; ++ dma_desc->status.b.ioc = 0; ++ dma_desc->status.b.sp = 0; ++ dma_desc->status.b.bytes = ep->maxxfer; ++ dma_desc->buf = ep->dma_addr + offset; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ xfer_est -= ep->maxxfer; ++ offset += ep->maxxfer; ++ } else { ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ if(ep->is_in) { ++ dma_desc->status.b.sp = (xfer_est % ep->maxpacket) ? ++ 1 : ((ep->sent_zlp) ? 1 : 0); ++ dma_desc->status.b.bytes = xfer_est; ++ } else { ++ dma_desc->status.b.bytes = xfer_est + ((4 - (xfer_est & 0x3)) & 0x3) ; ++ } ++ ++ dma_desc->buf = ep->dma_addr + offset; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ } ++ dma_desc ++; ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for an EP and ++ * starts the transfer. For an IN transfer, the packets will be ++ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, ++ * the packets are unloaded from the Rx FIFO in the ISR. the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++ ++void dwc_otg_ep_start_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ depctl_data_t depctl; ++ deptsiz_data_t deptsiz; ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__); ++ ++ DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d " ++ "xfer_buff=%p start_xfer_buff=%p\n", ++ ep->num, (ep->is_in?"IN":"OUT"), ep->xfer_len, ++ ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff); ++ ++ /* IN endpoint */ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[ep->num]; ++ ++ gnptxsts_data_t gtxstatus; ++ ++ gtxstatus.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->gnptxsts); ++ ++ if(core_if->en_multiple_tx_fifo == 0 && gtxstatus.b.nptxqspcavail == 0) { ++#ifdef DEBUG ++ DWC_PRINT("TX Queue Full (0x%0x)\n", gtxstatus.d32); ++#endif ++ return; ++ } ++ ++ depctl.d32 = dwc_read_reg32(&(in_regs->diepctl)); ++ deptsiz.d32 = dwc_read_reg32(&(in_regs->dieptsiz)); ++ ++ ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ? ++ ep->maxxfer : (ep->total_len - ep->xfer_len); ++ ++ /* Zero Length Packet? */ ++ if ((ep->xfer_len - ep->xfer_count) == 0) { ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 1; ++ } ++ else { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count; ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - ep->xfer_count - 1 + ep->maxpacket) / ++ ep->maxpacket; ++ } ++ ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ dwc_write_reg32 (&(in_regs->diepdma), ++ (uint32_t)ep->dma_addr); ++ } ++ else { ++ init_dma_desc_chain(core_if, ep); ++ /** DIEPDMAn Register write */ ++ dwc_write_reg32(&in_regs->diepdma, ep->dma_desc_addr); ++ } ++ } ++ else { ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ if(ep->type != DWC_OTG_EP_TYPE_ISOC) { ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, ++ * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode, ++ * the data will be written into the fifo by the ISR. ++ */ ++ if(core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ } ++ else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if(ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk = 1 << ep->num; ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ ++ } ++ } ++ } ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ ++ depctl.d32 = dwc_read_reg32 (&core_if->dev_if->in_ep_regs[0]->diepctl); ++ depctl.b.nextep = ep->num; ++ dwc_write_reg32 (&core_if->dev_if->in_ep_regs[0]->diepctl, depctl.d32); ++ ++ } ++ else { ++ /* OUT endpoint */ ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[ep->num]; ++ ++ depctl.d32 = dwc_read_reg32(&(out_regs->doepctl)); ++ deptsiz.d32 = dwc_read_reg32(&(out_regs->doeptsiz)); ++ ++ ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ? ++ ep->maxxfer : (ep->total_len - ep->xfer_len); ++ ++ /* Program the transfer size and packet count as follows: ++ * ++ * pktcnt = N ++ * xfersize = N * maxpacket ++ */ ++ if ((ep->xfer_len - ep->xfer_count) == 0) { ++ /* Zero Length Packet */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ } ++ else { ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - ep->xfer_count + (ep->maxpacket - 1)) / ++ ep->maxpacket; ++ ep->xfer_len = deptsiz.b.pktcnt * ep->maxpacket + ep->xfer_count; ++ deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count; ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "ep%d xfersize=%d pktcnt=%d\n", ++ ep->num, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ ++ if (core_if->dma_enable) { ++ if (!core_if->dma_desc_enable) { ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ ++ dwc_write_reg32 (&(out_regs->doepdma), ++ (uint32_t)ep->dma_addr); ++ } ++ else { ++ init_dma_desc_chain(core_if, ep); ++ ++ /** DOEPDMAn Register write */ ++ dwc_write_reg32(&out_regs->doepdma, ep->dma_desc_addr); ++ } ++ } ++ else { ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ } ++ ++ /* EP enable */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ ++ dwc_write_reg32(&out_regs->doepctl, depctl.d32); ++ ++ DWC_DEBUGPL(DBG_PCD, "DOEPCTL=%08x DOEPTSIZ=%08x\n", ++ dwc_read_reg32(&out_regs->doepctl), ++ dwc_read_reg32(&out_regs->doeptsiz)); ++ DWC_DEBUGPL(DBG_PCD, "DAINTMSK=%08x GINTMSK=%08x\n", ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->daintmsk), ++ dwc_read_reg32(&core_if->core_global_regs->gintmsk)); ++ } ++} ++ ++/** ++ * This function setup a zero length transfer in Buffer DMA and ++ * Slave modes for usb requests with zero field set ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ ++ depctl_data_t depctl; ++ deptsiz_data_t deptsiz; ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__); ++ ++ /* IN endpoint */ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[ep->num]; ++ ++ depctl.d32 = dwc_read_reg32(&(in_regs->diepctl)); ++ deptsiz.d32 = dwc_read_reg32(&(in_regs->dieptsiz)); ++ ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 1; ++ ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ dwc_write_reg32 (&(in_regs->diepdma), ++ (uint32_t)ep->dma_addr); ++ } ++ } ++ else { ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, ++ * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode, ++ * the data will be written into the fifo by the ISR. ++ */ ++ if(core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ } ++ else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if(ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk = 1 << ep->num; ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ } ++ } ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ ++ depctl.d32 = dwc_read_reg32 (&core_if->dev_if->in_ep_regs[0]->diepctl); ++ depctl.b.nextep = ep->num; ++ dwc_write_reg32 (&core_if->dev_if->in_ep_regs[0]->diepctl, depctl.d32); ++ ++ } ++ else { ++ /* OUT endpoint */ ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[ep->num]; ++ ++ depctl.d32 = dwc_read_reg32(&(out_regs->doepctl)); ++ deptsiz.d32 = dwc_read_reg32(&(out_regs->doeptsiz)); ++ ++ /* Zero Length Packet */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ ++ if (core_if->dma_enable) { ++ if (!core_if->dma_desc_enable) { ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ ++ dwc_write_reg32 (&(out_regs->doepdma), ++ (uint32_t)ep->dma_addr); ++ } ++ } ++ else { ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ } ++ ++ /* EP enable */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ ++ dwc_write_reg32(&out_regs->doepctl, depctl.d32); ++ ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for EP0 and starts ++ * the transfer. For an IN transfer, the packets will be loaded into ++ * the appropriate Tx FIFO in the ISR. For OUT transfers, the packets are ++ * unloaded from the Rx FIFO in the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP0 data. ++ */ ++void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ depctl_data_t depctl; ++ deptsiz0_data_t deptsiz; ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ dwc_otg_dma_desc_t* dma_desc; ++ ++ DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d " ++ "xfer_buff=%p start_xfer_buff=%p \n", ++ ep->num, (ep->is_in?"IN":"OUT"), ep->xfer_len, ++ ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff); ++ ++ ep->total_len = ep->xfer_len; ++ ++ /* IN endpoint */ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[0]; ++ ++ gnptxsts_data_t gtxstatus; ++ ++ gtxstatus.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->gnptxsts); ++ ++ if(core_if->en_multiple_tx_fifo == 0 && gtxstatus.b.nptxqspcavail == 0) { ++#ifdef DEBUG ++ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz); ++ DWC_DEBUGPL(DBG_PCD,"DIEPCTL0=%0x\n", ++ dwc_read_reg32(&in_regs->diepctl)); ++ DWC_DEBUGPL(DBG_PCD, "DIEPTSIZ0=%0x (sz=%d, pcnt=%d)\n", ++ deptsiz.d32, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ DWC_PRINT("TX Queue or FIFO Full (0x%0x)\n", ++ gtxstatus.d32); ++#endif ++ return; ++ } ++ ++ ++ depctl.d32 = dwc_read_reg32(&in_regs->diepctl); ++ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz); ++ ++ /* Zero Length Packet? */ ++ if (ep->xfer_len == 0) { ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 1; ++ } ++ else { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ if (ep->xfer_len > ep->maxpacket) { ++ ep->xfer_len = ep->maxpacket; ++ deptsiz.b.xfersize = ep->maxpacket; ++ } ++ else { ++ deptsiz.b.xfersize = ep->xfer_len; ++ } ++ deptsiz.b.pktcnt = 1; ++ ++ } ++ DWC_DEBUGPL(DBG_PCDV, "IN len=%d xfersize=%d pktcnt=%d [%08x]\n", ++ ep->xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ if(core_if->dma_desc_enable == 0) { ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ ++ dwc_write_reg32 (&(in_regs->diepdma), ++ (uint32_t)ep->dma_addr); ++ } ++ else { ++ dma_desc = core_if->dev_if->in_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.sp = (ep->xfer_len == ep->maxpacket) ? 0 : 1; ++ dma_desc->status.b.bytes = ep->xfer_len; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DIEPDMA0 Register write */ ++ dwc_write_reg32(&in_regs->diepdma, core_if->dev_if->dma_in_desc_addr); ++ } ++ } ++ else { ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, the ++ * data will be written into the fifo by the ISR. ++ */ ++ if (!core_if->dma_enable) { ++ if(core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ } ++ else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if(ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk |= 1 << ep->num; ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ } ++ } ++ } ++ } ++ else { ++ /* OUT endpoint */ ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[0]; ++ ++ depctl.d32 = dwc_read_reg32(&out_regs->doepctl); ++ deptsiz.d32 = dwc_read_reg32(&out_regs->doeptsiz); ++ ++ /* Program the transfer size and packet count as follows: ++ * xfersize = N * (maxpacket + 4 - (maxpacket % 4)) ++ * pktcnt = N */ ++ /* Zero Length Packet */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ ++ DWC_DEBUGPL(DBG_PCDV, "len=%d xfersize=%d pktcnt=%d\n", ++ ep->xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ ++ if (core_if->dma_enable) { ++ if(!core_if->dma_desc_enable) { ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ ++ dwc_write_reg32 (&(out_regs->doepdma), ++ (uint32_t)ep->dma_addr); ++ } ++ else { ++ dma_desc = core_if->dev_if->out_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.bytes = ep->maxpacket; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DOEPDMA0 Register write */ ++ dwc_write_reg32(&out_regs->doepdma, core_if->dev_if->dma_out_desc_addr); ++ } ++ } ++ else { ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ } ++ ++ /* EP enable */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32 (&(out_regs->doepctl), depctl.d32); ++ } ++} ++ ++/** ++ * This function continues control IN transfers started by ++ * dwc_otg_ep0_start_transfer, when the transfer does not fit in a ++ * single packet. NOTE: The DIEPCTL0/DOEPCTL0 registers only have one ++ * bit for the packet count. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP0 data. ++ */ ++void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ depctl_data_t depctl; ++ deptsiz0_data_t deptsiz; ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ dwc_otg_dma_desc_t* dma_desc; ++ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[0]; ++ gnptxsts_data_t tx_status = { .d32 = 0 }; ++ ++ tx_status.d32 = dwc_read_reg32(&core_if->core_global_regs->gnptxsts); ++ /** @todo Should there be check for room in the Tx ++ * Status Queue. If not remove the code above this comment. */ ++ ++ depctl.d32 = dwc_read_reg32(&in_regs->diepctl); ++ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz); ++ ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ ++ ++ if(core_if->dma_desc_enable == 0) { ++ deptsiz.b.xfersize = (ep->total_len - ep->xfer_count) > ep->maxpacket ? ep->maxpacket : ++ (ep->total_len - ep->xfer_count); ++ deptsiz.b.pktcnt = 1; ++ if(core_if->dma_enable == 0) { ++ ep->xfer_len += deptsiz.b.xfersize; ++ } else { ++ ep->xfer_len = deptsiz.b.xfersize; ++ } ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ } ++ else { ++ ep->xfer_len = (ep->total_len - ep->xfer_count) > ep->maxpacket ? ep->maxpacket : ++ (ep->total_len - ep->xfer_count); ++ ++ dma_desc = core_if->dev_if->in_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.sp = (ep->xfer_len == ep->maxpacket) ? 0 : 1; ++ dma_desc->status.b.bytes = ep->xfer_len; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DIEPDMA0 Register write */ ++ dwc_write_reg32(&in_regs->diepdma, core_if->dev_if->dma_in_desc_addr); ++ } ++ ++ ++ DWC_DEBUGPL(DBG_PCDV, "IN len=%d xfersize=%d pktcnt=%d [%08x]\n", ++ ep->xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) { ++ if(core_if->dma_desc_enable == 0) ++ dwc_write_reg32 (&(in_regs->diepdma), (uint32_t)ep->dma_addr); ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, the ++ * data will be written into the fifo by the ISR. ++ */ ++ if (!core_if->dma_enable) { ++ if(core_if->en_multiple_tx_fifo == 0) { ++ /* First clear it from GINTSTS */ ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ ++ } ++ else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if(ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk |= 1 << ep->num; ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ } ++ } ++ } ++ } ++ else { ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[0]; ++ ++ ++ depctl.d32 = dwc_read_reg32(&out_regs->doepctl); ++ deptsiz.d32 = dwc_read_reg32(&out_regs->doeptsiz); ++ ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ ++ ++ if(core_if->dma_desc_enable == 0) { ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ } ++ else { ++ dma_desc = core_if->dev_if->out_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.bytes = ep->maxpacket; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DOEPDMA0 Register write */ ++ dwc_write_reg32(&out_regs->doepdma, core_if->dev_if->dma_out_desc_addr); ++ } ++ ++ ++ DWC_DEBUGPL(DBG_PCDV, "IN len=%d xfersize=%d pktcnt=%d [%08x]\n", ++ ep->xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) { ++ if(core_if->dma_desc_enable == 0) ++ dwc_write_reg32 (&(out_regs->doepdma), (uint32_t)ep->dma_addr); ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&out_regs->doepctl, depctl.d32); ++ ++ } ++} ++ ++#ifdef DEBUG ++void dump_msg(const u8 *buf, unsigned int length) ++{ ++ unsigned int start, num, i; ++ char line[52], *p; ++ ++ if (length >= 512) ++ return; ++ start = 0; ++ while (length > 0) { ++ num = min(length, 16u); ++ p = line; ++ for (i = 0; i < num; ++i) ++ { ++ if (i == 8) ++ *p++ = ' '; ++ sprintf(p, " %02x", buf[i]); ++ p += 3; ++ } ++ *p = 0; ++ DWC_PRINT("%6x: %s\n", start, line); ++ buf += num; ++ start += num; ++ length -= num; ++ } ++} ++#else ++static inline void dump_msg(const u8 *buf, unsigned int length) ++{ ++} ++#endif ++ ++/** ++ * This function writes a packet into the Tx FIFO associated with the ++ * EP. For non-periodic EPs the non-periodic Tx FIFO is written. For ++ * periodic EPs the periodic Tx FIFO associated with the EP is written ++ * with all packets for the next micro-frame. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to write packet for. ++ * @param dma Indicates if DMA is being used. ++ */ ++void dwc_otg_ep_write_packet(dwc_otg_core_if_t *core_if, dwc_ep_t *ep, int dma) ++{ ++ /** ++ * The buffer is padded to DWORD on a per packet basis in ++ * slave/dma mode if the MPS is not DWORD aligned. The last ++ * packet, if short, is also padded to a multiple of DWORD. ++ * ++ * ep->xfer_buff always starts DWORD aligned in memory and is a ++ * multiple of DWORD in length ++ * ++ * ep->xfer_len can be any number of bytes ++ * ++ * ep->xfer_count is a multiple of ep->maxpacket until the last ++ * packet ++ * ++ * FIFO access is DWORD */ ++ ++ uint32_t i; ++ uint32_t byte_count; ++ uint32_t dword_count; ++ uint32_t *fifo; ++ uint32_t *data_buff = (uint32_t *)ep->xfer_buff; ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p)\n", __func__, core_if, ep); ++ if (ep->xfer_count >= ep->xfer_len) { ++ DWC_WARN("%s() No data for EP%d!!!\n", __func__, ep->num); ++ return; ++ } ++ ++ /* Find the byte length of the packet either short packet or MPS */ ++ if ((ep->xfer_len - ep->xfer_count) < ep->maxpacket) { ++ byte_count = ep->xfer_len - ep->xfer_count; ++ } ++ else { ++ byte_count = ep->maxpacket; ++ } ++ ++ /* Find the DWORD length, padded by extra bytes as neccessary if MPS ++ * is not a multiple of DWORD */ ++ dword_count = (byte_count + 3) / 4; ++ ++#ifdef VERBOSE ++ dump_msg(ep->xfer_buff, byte_count); ++#endif ++ ++ /**@todo NGS Where are the Periodic Tx FIFO addresses ++ * intialized? What should this be? */ ++ ++ fifo = core_if->data_fifo[ep->num]; ++ ++ ++ DWC_DEBUGPL((DBG_PCDV|DBG_CILV), "fifo=%p buff=%p *p=%08x bc=%d\n", fifo, data_buff, *data_buff, byte_count); ++ ++ if (!dma) { ++ for (i=0; ixfer_count += byte_count; ++ ep->xfer_buff += byte_count; ++ ep->dma_addr += byte_count; ++} ++ ++/** ++ * Set the EP STALL. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to set the stall on. ++ */ ++void dwc_otg_ep_set_stall(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ depctl_data_t depctl; ++ volatile uint32_t *depctl_addr; ++ ++ DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num, ++ (ep->is_in?"IN":"OUT")); ++ ++ DWC_PRINT("%s ep%d-%s\n", __func__, ep->num, ++ (ep->is_in?"in":"out")); ++ ++ if (ep->is_in == 1) { ++ depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl); ++ depctl.d32 = dwc_read_reg32(depctl_addr); ++ ++ /* set the disable and stall bits */ ++ if (depctl.b.epena) { ++ depctl.b.epdis = 1; ++ } ++ depctl.b.stall = 1; ++ dwc_write_reg32(depctl_addr, depctl.d32); ++ } ++ else { ++ depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl); ++ depctl.d32 = dwc_read_reg32(depctl_addr); ++ ++ /* set the stall bit */ ++ depctl.b.stall = 1; ++ dwc_write_reg32(depctl_addr, depctl.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCD,"DEPCTL=%0x\n",dwc_read_reg32(depctl_addr)); ++ ++ return; ++} ++ ++/** ++ * Clear the EP STALL. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to clear stall from. ++ */ ++void dwc_otg_ep_clear_stall(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ depctl_data_t depctl; ++ volatile uint32_t *depctl_addr; ++ ++ DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num, ++ (ep->is_in?"IN":"OUT")); ++ ++ if (ep->is_in == 1) { ++ depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl); ++ } ++ else { ++ depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl); ++ } ++ ++ depctl.d32 = dwc_read_reg32(depctl_addr); ++ ++ /* clear the stall bits */ ++ depctl.b.stall = 0; ++ ++ /* ++ * USB Spec 9.4.5: For endpoints using data toggle, regardless ++ * of whether an endpoint has the Halt feature set, a ++ * ClearFeature(ENDPOINT_HALT) request always results in the ++ * data toggle being reinitialized to DATA0. ++ */ ++ if (ep->type == DWC_OTG_EP_TYPE_INTR || ++ ep->type == DWC_OTG_EP_TYPE_BULK) { ++ depctl.b.setd0pid = 1; /* DATA0 */ ++ } ++ ++ dwc_write_reg32(depctl_addr, depctl.d32); ++ DWC_DEBUGPL(DBG_PCD,"DEPCTL=%0x\n",dwc_read_reg32(depctl_addr)); ++ return; ++} ++ ++/** ++ * This function reads a packet from the Rx FIFO into the destination ++ * buffer. To read SETUP data use dwc_otg_read_setup_packet. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dest Destination buffer for the packet. ++ * @param bytes Number of bytes to copy to the destination. ++ */ ++void dwc_otg_read_packet(dwc_otg_core_if_t *core_if, ++ uint8_t *dest, ++ uint16_t bytes) ++{ ++ int i; ++ int word_count = (bytes + 3) / 4; ++ ++ volatile uint32_t *fifo = core_if->data_fifo[0]; ++ uint32_t *data_buff = (uint32_t *)dest; ++ ++ /** ++ * @todo Account for the case where _dest is not dword aligned. This ++ * requires reading data from the FIFO into a uint32_t temp buffer, ++ * then moving it into the data buffer. ++ */ ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p,%d)\n", __func__, ++ core_if, dest, bytes); ++ ++ for (i=0; idev_if->dev_global_regs->dcfg; ++ DWC_PRINT("DCFG @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->dev_global_regs->dctl; ++ DWC_PRINT("DCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->dev_global_regs->dsts; ++ DWC_PRINT("DSTS @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->dev_global_regs->diepmsk; ++ DWC_PRINT("DIEPMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->dev_global_regs->doepmsk; ++ DWC_PRINT("DOEPMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->dev_global_regs->daint; ++ DWC_PRINT("DAINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->dev_global_regs->daintmsk; ++ DWC_PRINT("DAINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->dev_global_regs->dtknqr1; ++ DWC_PRINT("DTKNQR1 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ if (core_if->hwcfg2.b.dev_token_q_depth > 6) { ++ addr=&core_if->dev_if->dev_global_regs->dtknqr2; ++ DWC_PRINT("DTKNQR2 @0x%08X : 0x%08X\n", ++ (uint32_t)addr,dwc_read_reg32(addr)); ++ } ++ ++ addr=&core_if->dev_if->dev_global_regs->dvbusdis; ++ DWC_PRINT("DVBUSID @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ ++ addr=&core_if->dev_if->dev_global_regs->dvbuspulse; ++ DWC_PRINT("DVBUSPULSE @0x%08X : 0x%08X\n", ++ (uint32_t)addr,dwc_read_reg32(addr)); ++ ++ if (core_if->hwcfg2.b.dev_token_q_depth > 14) { ++ addr=&core_if->dev_if->dev_global_regs->dtknqr3_dthrctl; ++ DWC_PRINT("DTKNQR3_DTHRCTL @0x%08X : 0x%08X\n", ++ (uint32_t)addr, dwc_read_reg32(addr)); ++ } ++/* ++ if (core_if->hwcfg2.b.dev_token_q_depth > 22) { ++ addr=&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk; ++ DWC_PRINT("DTKNQR4 @0x%08X : 0x%08X\n", ++ (uint32_t)addr, dwc_read_reg32(addr)); ++ } ++*/ ++ addr=&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk; ++ DWC_PRINT("FIFOEMPMSK @0x%08X : 0x%08X\n", (uint32_t)addr, dwc_read_reg32(addr)); ++ ++ addr=&core_if->dev_if->dev_global_regs->deachint; ++ DWC_PRINT("DEACHINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->dev_global_regs->deachintmsk; ++ DWC_PRINT("DEACHINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ ++ for (i=0; i<= core_if->dev_if->num_in_eps; i++) { ++ addr=&core_if->dev_if->dev_global_regs->diepeachintmsk[i]; ++ DWC_PRINT("DIEPEACHINTMSK[%d] @0x%08X : 0x%08X\n", i, (uint32_t)addr, dwc_read_reg32(addr)); ++ } ++ ++ ++ for (i=0; i<= core_if->dev_if->num_out_eps; i++) { ++ addr=&core_if->dev_if->dev_global_regs->doepeachintmsk[i]; ++ DWC_PRINT("DOEPEACHINTMSK[%d] @0x%08X : 0x%08X\n", i, (uint32_t)addr, dwc_read_reg32(addr)); ++ } ++ ++ for (i=0; i<= core_if->dev_if->num_in_eps; i++) { ++ DWC_PRINT("Device IN EP %d Registers\n", i); ++ addr=&core_if->dev_if->in_ep_regs[i]->diepctl; ++ DWC_PRINT("DIEPCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->in_ep_regs[i]->diepint; ++ DWC_PRINT("DIEPINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->in_ep_regs[i]->dieptsiz; ++ DWC_PRINT("DIETSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->in_ep_regs[i]->diepdma; ++ DWC_PRINT("DIEPDMA @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->in_ep_regs[i]->dtxfsts; ++ DWC_PRINT("DTXFSTS @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->in_ep_regs[i]->diepdmab; ++ DWC_PRINT("DIEPDMAB @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ } ++ ++ ++ for (i=0; i<= core_if->dev_if->num_out_eps; i++) { ++ DWC_PRINT("Device OUT EP %d Registers\n", i); ++ addr=&core_if->dev_if->out_ep_regs[i]->doepctl; ++ DWC_PRINT("DOEPCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->out_ep_regs[i]->doepfn; ++ DWC_PRINT("DOEPFN @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->out_ep_regs[i]->doepint; ++ DWC_PRINT("DOEPINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->out_ep_regs[i]->doeptsiz; ++ DWC_PRINT("DOETSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->out_ep_regs[i]->doepdma; ++ DWC_PRINT("DOEPDMA @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->dev_if->out_ep_regs[i]->doepdmab; ++ DWC_PRINT("DOEPDMAB @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ ++ } ++ ++ ++ ++ return; ++} ++ ++/** ++ * This functions reads the SPRAM and prints its content ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_spram(dwc_otg_core_if_t *core_if) ++{ ++ volatile uint8_t *addr, *start_addr, *end_addr; ++ ++ DWC_PRINT("SPRAM Data:\n"); ++ start_addr = (void*)core_if->core_global_regs; ++ DWC_PRINT("Base Address: 0x%8X\n", (uint32_t)start_addr); ++ start_addr += 0x00028000; ++ end_addr=(void*)core_if->core_global_regs; ++ end_addr += 0x000280e0; ++ ++ for(addr = start_addr; addr < end_addr; addr+=16) ++ { ++ DWC_PRINT("0x%8X:\t%2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X\n", (uint32_t)addr, ++ addr[0], ++ addr[1], ++ addr[2], ++ addr[3], ++ addr[4], ++ addr[5], ++ addr[6], ++ addr[7], ++ addr[8], ++ addr[9], ++ addr[10], ++ addr[11], ++ addr[12], ++ addr[13], ++ addr[14], ++ addr[15] ++ ); ++ } ++ ++ return; ++} ++/** ++ * This function reads the host registers and prints them ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_host_registers(dwc_otg_core_if_t *core_if) ++{ ++ int i; ++ volatile uint32_t *addr; ++ ++ DWC_PRINT("Host Global Registers\n"); ++ addr=&core_if->host_if->host_global_regs->hcfg; ++ DWC_PRINT("HCFG @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->host_if->host_global_regs->hfir; ++ DWC_PRINT("HFIR @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->host_if->host_global_regs->hfnum; ++ DWC_PRINT("HFNUM @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->host_if->host_global_regs->hptxsts; ++ DWC_PRINT("HPTXSTS @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->host_if->host_global_regs->haint; ++ DWC_PRINT("HAINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->host_if->host_global_regs->haintmsk; ++ DWC_PRINT("HAINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=core_if->host_if->hprt0; ++ DWC_PRINT("HPRT0 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ ++ for (i=0; icore_params->host_channels; i++) ++ { ++ DWC_PRINT("Host Channel %d Specific Registers\n", i); ++ addr=&core_if->host_if->hc_regs[i]->hcchar; ++ DWC_PRINT("HCCHAR @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->host_if->hc_regs[i]->hcsplt; ++ DWC_PRINT("HCSPLT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->host_if->hc_regs[i]->hcint; ++ DWC_PRINT("HCINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->host_if->hc_regs[i]->hcintmsk; ++ DWC_PRINT("HCINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->host_if->hc_regs[i]->hctsiz; ++ DWC_PRINT("HCTSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->host_if->hc_regs[i]->hcdma; ++ DWC_PRINT("HCDMA @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ } ++ return; ++} ++ ++/** ++ * This function reads the core global registers and prints them ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_global_registers(dwc_otg_core_if_t *core_if) ++{ ++ int i; ++ volatile uint32_t *addr; ++ ++ DWC_PRINT("Core Global Registers\n"); ++ addr=&core_if->core_global_regs->gotgctl; ++ DWC_PRINT("GOTGCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->gotgint; ++ DWC_PRINT("GOTGINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->gahbcfg; ++ DWC_PRINT("GAHBCFG @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->gusbcfg; ++ DWC_PRINT("GUSBCFG @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->grstctl; ++ DWC_PRINT("GRSTCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->gintsts; ++ DWC_PRINT("GINTSTS @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->gintmsk; ++ DWC_PRINT("GINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->grxstsr; ++ DWC_PRINT("GRXSTSR @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ //addr=&core_if->core_global_regs->grxstsp; ++ //DWC_PRINT("GRXSTSP @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->grxfsiz; ++ DWC_PRINT("GRXFSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->gnptxfsiz; ++ DWC_PRINT("GNPTXFSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->gnptxsts; ++ DWC_PRINT("GNPTXSTS @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->gi2cctl; ++ DWC_PRINT("GI2CCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->gpvndctl; ++ DWC_PRINT("GPVNDCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->ggpio; ++ DWC_PRINT("GGPIO @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->guid; ++ DWC_PRINT("GUID @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->gsnpsid; ++ DWC_PRINT("GSNPSID @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->ghwcfg1; ++ DWC_PRINT("GHWCFG1 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->ghwcfg2; ++ DWC_PRINT("GHWCFG2 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->ghwcfg3; ++ DWC_PRINT("GHWCFG3 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->ghwcfg4; ++ DWC_PRINT("GHWCFG4 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&core_if->core_global_regs->hptxfsiz; ++ DWC_PRINT("HPTXFSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ ++ for (i=0; ihwcfg4.b.num_dev_perio_in_ep; i++) ++ { ++ addr=&core_if->core_global_regs->dptxfsiz_dieptxf[i]; ++ DWC_PRINT("DPTXFSIZ[%d] @0x%08X : 0x%08X\n",i,(uint32_t)addr,dwc_read_reg32(addr)); ++ } ++} ++ ++/** ++ * Flush a Tx FIFO. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param num Tx FIFO to flush. ++ */ ++void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t *core_if, ++ const int num) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ volatile grstctl_t greset = { .d32 = 0}; ++ int count = 0; ++ ++ DWC_DEBUGPL((DBG_CIL|DBG_PCDV), "Flush Tx FIFO %d\n", num); ++ ++ greset.b.txfflsh = 1; ++ greset.b.txfnum = num; ++ dwc_write_reg32(&global_regs->grstctl, greset.d32); ++ ++ do { ++ greset.d32 = dwc_read_reg32(&global_regs->grstctl); ++ if (++count > 10000) { ++ DWC_WARN("%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n", ++ __func__, greset.d32, ++ dwc_read_reg32(&global_regs->gnptxsts)); ++ break; ++ } ++ } ++ while (greset.b.txfflsh == 1); ++ ++ /* Wait for 3 PHY Clocks*/ ++ UDELAY(1); ++} ++ ++/** ++ * Flush Rx FIFO. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t *core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ volatile grstctl_t greset = { .d32 = 0}; ++ int count = 0; ++ ++ DWC_DEBUGPL((DBG_CIL|DBG_PCDV), "%s\n", __func__); ++ /* ++ * ++ */ ++ greset.b.rxfflsh = 1; ++ dwc_write_reg32(&global_regs->grstctl, greset.d32); ++ ++ do { ++ greset.d32 = dwc_read_reg32(&global_regs->grstctl); ++ if (++count > 10000) { ++ DWC_WARN("%s() HANG! GRSTCTL=%0x\n", __func__, ++ greset.d32); ++ break; ++ } ++ } ++ while (greset.b.rxfflsh == 1); ++ ++ /* Wait for 3 PHY Clocks*/ ++ UDELAY(1); ++} ++ ++/** ++ * Do core a soft reset of the core. Be careful with this because it ++ * resets all the internal state machines of the core. ++ */ ++void dwc_otg_core_reset(dwc_otg_core_if_t *core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ volatile grstctl_t greset = { .d32 = 0}; ++ int count = 0; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s\n", __func__); ++ /* Wait for AHB master IDLE state. */ ++ do { ++ UDELAY(10); ++ greset.d32 = dwc_read_reg32(&global_regs->grstctl); ++ if (++count > 100000) { ++ DWC_WARN("%s() HANG! AHB Idle GRSTCTL=%0x\n", __func__, ++ greset.d32); ++ return; ++ } ++ } ++ while (greset.b.ahbidle == 0); ++ ++ /* Core Soft Reset */ ++ count = 0; ++ greset.b.csftrst = 1; ++ dwc_write_reg32(&global_regs->grstctl, greset.d32); ++ do { ++ greset.d32 = dwc_read_reg32(&global_regs->grstctl); ++ if (++count > 10000) { ++ DWC_WARN("%s() HANG! Soft Reset GRSTCTL=%0x\n", __func__, ++ greset.d32); ++ break; ++ } ++ } ++ while (greset.b.csftrst == 1); ++ ++ /* Wait for 3 PHY Clocks*/ ++ MDELAY(100); ++} ++ ++ ++ ++/** ++ * Register HCD callbacks. The callbacks are used to start and stop ++ * the HCD for interrupt processing. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param cb the HCD callback structure. ++ * @param p pointer to be passed to callback function (usb_hcd*). ++ */ ++void dwc_otg_cil_register_hcd_callbacks(dwc_otg_core_if_t *core_if, ++ dwc_otg_cil_callbacks_t *cb, ++ void *p) ++{ ++ core_if->hcd_cb = cb; ++ cb->p = p; ++} ++ ++/** ++ * Register PCD callbacks. The callbacks are used to start and stop ++ * the PCD for interrupt processing. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param cb the PCD callback structure. ++ * @param p pointer to be passed to callback function (pcd*). ++ */ ++void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t *core_if, ++ dwc_otg_cil_callbacks_t *cb, ++ void *p) ++{ ++ core_if->pcd_cb = cb; ++ cb->p = p; ++} ++ ++#ifdef DWC_EN_ISOC ++ ++/** ++ * This function writes isoc data per 1 (micro)frame into tx fifo ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void write_isoc_frame_data(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ dwc_otg_dev_in_ep_regs_t *ep_regs; ++ dtxfsts_data_t txstatus = {.d32 = 0}; ++ uint32_t len = 0; ++ uint32_t dwords; ++ ++ ep->xfer_len = ep->data_per_frame; ++ ep->xfer_count = 0; ++ ++ ep_regs = core_if->dev_if->in_ep_regs[ep->num]; ++ ++ len = ep->xfer_len - ep->xfer_count; ++ ++ if (len > ep->maxpacket) { ++ len = ep->maxpacket; ++ } ++ ++ dwords = (len + 3)/4; ++ ++ /* While there is space in the queue and space in the FIFO and ++ * More data to tranfer, Write packets to the Tx FIFO */ ++ txstatus.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[ep->num]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n",ep->num,txstatus.d32); ++ ++ while (txstatus.b.txfspcavail > dwords && ++ ep->xfer_count < ep->xfer_len && ++ ep->xfer_len != 0) { ++ /* Write the FIFO */ ++ dwc_otg_ep_write_packet(core_if, ep, 0); ++ ++ len = ep->xfer_len - ep->xfer_count; ++ if (len > ep->maxpacket) { ++ len = ep->maxpacket; ++ } ++ ++ dwords = (len + 3)/4; ++ txstatus.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[ep->num]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV,"dtxfsts[%d]=0x%08x\n", ep->num, txstatus.d32); ++ } ++} ++ ++ ++/** ++ * This function initializes a descriptor chain for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ deptsiz_data_t deptsiz = { .d32 = 0 }; ++ depctl_data_t depctl = { .d32 = 0 }; ++ dsts_data_t dsts = { .d32 = 0 }; ++ volatile uint32_t *addr; ++ ++ if(ep->is_in) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ } else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ } ++ ++ ep->xfer_len = ep->data_per_frame; ++ ep->xfer_count = 0; ++ ep->xfer_buff = ep->cur_pkt_addr; ++ ep->dma_addr = ep->cur_pkt_dma_addr; ++ ++ if(ep->is_in) { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.xfersize = ep->xfer_len; ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - 1 + ep->maxpacket) / ++ ep->maxpacket; ++ deptsiz.b.mc = deptsiz.b.pktcnt; ++ dwc_write_reg32(&core_if->dev_if->in_ep_regs[ep->num]->dieptsiz, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ dwc_write_reg32 (&(core_if->dev_if->in_ep_regs[ep->num]->diepdma), (uint32_t)ep->dma_addr); ++ } ++ } else { ++ deptsiz.b.pktcnt = ++ (ep->xfer_len + (ep->maxpacket - 1)) / ++ ep->maxpacket; ++ deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket; ++ ++ dwc_write_reg32(&core_if->dev_if->out_ep_regs[ep->num]->doeptsiz, deptsiz.d32); ++ ++ if (core_if->dma_enable) { ++ dwc_write_reg32 (&(core_if->dev_if->out_ep_regs[ep->num]->doepdma), ++ (uint32_t)ep->dma_addr); ++ } ++ } ++ ++ ++ /** Enable endpoint, clear nak */ ++ ++ depctl.d32 = 0; ++ if(ep->bInterval == 1) { ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ep->next_frame = dsts.b.soffn + ep->bInterval; ++ ++ if(ep->next_frame & 0x1) { ++ depctl.b.setd1pid = 1; ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ } else { ++ ep->next_frame += ep->bInterval; ++ ++ if(ep->next_frame & 0x1) { ++ depctl.b.setd1pid = 1; ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ } ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ ++ dwc_modify_reg32(addr, 0, depctl.d32); ++ depctl.d32 = dwc_read_reg32(addr); ++ ++ if(ep->is_in && core_if->dma_enable == 0) { ++ write_isoc_frame_data(core_if, ep); ++ } ++ ++} ++ ++#endif //DWC_EN_ISOC +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_cil.h +@@ -0,0 +1,1098 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.h $ ++ * $Revision: 1.2 $ ++ * $Date: 2008-11-21 05:39:15 $ ++ * $Change: 1099526 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_CIL_H__) ++#define __DWC_CIL_H__ ++ ++#include ++#include ++#include ++ ++#include "linux/dwc_otg_plat.h" ++#include "dwc_otg_regs.h" ++#ifdef DEBUG ++#include "linux/timer.h" ++#endif ++ ++/** ++ * @file ++ * This file contains the interface to the Core Interface Layer. ++ */ ++ ++ ++/** Macros defined for DWC OTG HW Release verison */ ++#define OTG_CORE_REV_2_00 0x4F542000 ++#define OTG_CORE_REV_2_60a 0x4F54260A ++#define OTG_CORE_REV_2_71a 0x4F54271A ++#define OTG_CORE_REV_2_72a 0x4F54272A ++ ++/** ++*/ ++typedef struct iso_pkt_info ++{ ++ uint32_t offset; ++ uint32_t length; ++ int32_t status; ++} iso_pkt_info_t; ++/** ++ * The dwc_ep structure represents the state of a single ++ * endpoint when acting in device mode. It contains the data items ++ * needed for an endpoint to be activated and transfer packets. ++ */ ++typedef struct dwc_ep ++{ ++ /** EP number used for register address lookup */ ++ uint8_t num; ++ /** EP direction 0 = OUT */ ++ unsigned is_in : 1; ++ /** EP active. */ ++ unsigned active : 1; ++ ++ /** Periodic Tx FIFO # for IN EPs For INTR EP set to 0 to use non-periodic Tx FIFO ++ If dedicated Tx FIFOs are enabled for all IN Eps - Tx FIFO # FOR IN EPs*/ ++ unsigned tx_fifo_num : 4; ++ /** EP type: 0 - Control, 1 - ISOC, 2 - BULK, 3 - INTR */ ++ unsigned type : 2; ++#define DWC_OTG_EP_TYPE_CONTROL 0 ++#define DWC_OTG_EP_TYPE_ISOC 1 ++#define DWC_OTG_EP_TYPE_BULK 2 ++#define DWC_OTG_EP_TYPE_INTR 3 ++ ++ /** DATA start PID for INTR and BULK EP */ ++ unsigned data_pid_start : 1; ++ /** Frame (even/odd) for ISOC EP */ ++ unsigned even_odd_frame : 1; ++ /** Max Packet bytes */ ++ unsigned maxpacket : 11; ++ ++ /** Max Transfer size */ ++ unsigned maxxfer : 16; ++ ++ /** @name Transfer state */ ++ /** @{ */ ++ ++ /** ++ * Pointer to the beginning of the transfer buffer -- do not modify ++ * during transfer. ++ */ ++ ++ uint32_t dma_addr; ++ ++ uint32_t dma_desc_addr; ++ dwc_otg_dma_desc_t* desc_addr; ++ ++ ++ uint8_t *start_xfer_buff; ++ /** pointer to the transfer buffer */ ++ uint8_t *xfer_buff; ++ /** Number of bytes to transfer */ ++ unsigned xfer_len : 19; ++ /** Number of bytes transferred. */ ++ unsigned xfer_count : 19; ++ /** Sent ZLP */ ++ unsigned sent_zlp : 1; ++ /** Total len for control transfer */ ++ unsigned total_len : 19; ++ ++ /** stall clear flag */ ++ unsigned stall_clear_flag : 1; ++ ++ /** Allocated DMA Desc count */ ++ uint32_t desc_cnt; ++ ++#ifdef DWC_EN_ISOC ++ /** ++ * Variables specific for ISOC EPs ++ * ++ */ ++ /** DMA addresses of ISOC buffers */ ++ uint32_t dma_addr0; ++ uint32_t dma_addr1; ++ ++ uint32_t iso_dma_desc_addr; ++ dwc_otg_dma_desc_t* iso_desc_addr; ++ ++ /** pointer to the transfer buffers */ ++ uint8_t *xfer_buff0; ++ uint8_t *xfer_buff1; ++ ++ /** number of ISOC Buffer is processing */ ++ uint32_t proc_buf_num; ++ /** Interval of ISOC Buffer processing */ ++ uint32_t buf_proc_intrvl; ++ /** Data size for regular frame */ ++ uint32_t data_per_frame; ++ ++ /* todo - pattern data support is to be implemented in the future */ ++ /** Data size for pattern frame */ ++ uint32_t data_pattern_frame; ++ /** Frame number of pattern data */ ++ uint32_t sync_frame; ++ ++ /** bInterval */ ++ uint32_t bInterval; ++ /** ISO Packet number per frame */ ++ uint32_t pkt_per_frm; ++ /** Next frame num for which will be setup DMA Desc */ ++ uint32_t next_frame; ++ /** Number of packets per buffer processing */ ++ uint32_t pkt_cnt; ++ /** Info for all isoc packets */ ++ iso_pkt_info_t *pkt_info; ++ /** current pkt number */ ++ uint32_t cur_pkt; ++ /** current pkt number */ ++ uint8_t *cur_pkt_addr; ++ /** current pkt number */ ++ uint32_t cur_pkt_dma_addr; ++#endif //DWC_EN_ISOC ++/** @} */ ++} dwc_ep_t; ++ ++/* ++ * Reasons for halting a host channel. ++ */ ++typedef enum dwc_otg_halt_status ++{ ++ DWC_OTG_HC_XFER_NO_HALT_STATUS, ++ DWC_OTG_HC_XFER_COMPLETE, ++ DWC_OTG_HC_XFER_URB_COMPLETE, ++ DWC_OTG_HC_XFER_ACK, ++ DWC_OTG_HC_XFER_NAK, ++ DWC_OTG_HC_XFER_NYET, ++ DWC_OTG_HC_XFER_STALL, ++ DWC_OTG_HC_XFER_XACT_ERR, ++ DWC_OTG_HC_XFER_FRAME_OVERRUN, ++ DWC_OTG_HC_XFER_BABBLE_ERR, ++ DWC_OTG_HC_XFER_DATA_TOGGLE_ERR, ++ DWC_OTG_HC_XFER_AHB_ERR, ++ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE, ++ DWC_OTG_HC_XFER_URB_DEQUEUE ++} dwc_otg_halt_status_e; ++ ++/** ++ * Host channel descriptor. This structure represents the state of a single ++ * host channel when acting in host mode. It contains the data items needed to ++ * transfer packets to an endpoint via a host channel. ++ */ ++typedef struct dwc_hc ++{ ++ /** Host channel number used for register address lookup */ ++ uint8_t hc_num; ++ ++ /** Device to access */ ++ unsigned dev_addr : 7; ++ ++ /** EP to access */ ++ unsigned ep_num : 4; ++ ++ /** EP direction. 0: OUT, 1: IN */ ++ unsigned ep_is_in : 1; ++ ++ /** ++ * EP speed. ++ * One of the following values: ++ * - DWC_OTG_EP_SPEED_LOW ++ * - DWC_OTG_EP_SPEED_FULL ++ * - DWC_OTG_EP_SPEED_HIGH ++ */ ++ unsigned speed : 2; ++#define DWC_OTG_EP_SPEED_LOW 0 ++#define DWC_OTG_EP_SPEED_FULL 1 ++#define DWC_OTG_EP_SPEED_HIGH 2 ++ ++ /** ++ * Endpoint type. ++ * One of the following values: ++ * - DWC_OTG_EP_TYPE_CONTROL: 0 ++ * - DWC_OTG_EP_TYPE_ISOC: 1 ++ * - DWC_OTG_EP_TYPE_BULK: 2 ++ * - DWC_OTG_EP_TYPE_INTR: 3 ++ */ ++ unsigned ep_type : 2; ++ ++ /** Max packet size in bytes */ ++ unsigned max_packet : 11; ++ ++ /** ++ * PID for initial transaction. ++ * 0: DATA0,
++ * 1: DATA2,
++ * 2: DATA1,
++ * 3: MDATA (non-Control EP), ++ * SETUP (Control EP) ++ */ ++ unsigned data_pid_start : 2; ++#define DWC_OTG_HC_PID_DATA0 0 ++#define DWC_OTG_HC_PID_DATA2 1 ++#define DWC_OTG_HC_PID_DATA1 2 ++#define DWC_OTG_HC_PID_MDATA 3 ++#define DWC_OTG_HC_PID_SETUP 3 ++ ++ /** Number of periodic transactions per (micro)frame */ ++ unsigned multi_count: 2; ++ ++ /** @name Transfer State */ ++ /** @{ */ ++ ++ /** Pointer to the current transfer buffer position. */ ++ uint8_t *xfer_buff; ++ /** Total number of bytes to transfer. */ ++ uint32_t xfer_len; ++ /** Number of bytes transferred so far. */ ++ uint32_t xfer_count; ++ /** Packet count at start of transfer.*/ ++ uint16_t start_pkt_count; ++ ++ /** ++ * Flag to indicate whether the transfer has been started. Set to 1 if ++ * it has been started, 0 otherwise. ++ */ ++ uint8_t xfer_started; ++ ++ /** ++ * Set to 1 to indicate that a PING request should be issued on this ++ * channel. If 0, process normally. ++ */ ++ uint8_t do_ping; ++ ++ /** ++ * Set to 1 to indicate that the error count for this transaction is ++ * non-zero. Set to 0 if the error count is 0. ++ */ ++ uint8_t error_state; ++ ++ /** ++ * Set to 1 to indicate that this channel should be halted the next ++ * time a request is queued for the channel. This is necessary in ++ * slave mode if no request queue space is available when an attempt ++ * is made to halt the channel. ++ */ ++ uint8_t halt_on_queue; ++ ++ /** ++ * Set to 1 if the host channel has been halted, but the core is not ++ * finished flushing queued requests. Otherwise 0. ++ */ ++ uint8_t halt_pending; ++ ++ /** ++ * Reason for halting the host channel. ++ */ ++ dwc_otg_halt_status_e halt_status; ++ ++ /* ++ * Split settings for the host channel ++ */ ++ uint8_t do_split; /**< Enable split for the channel */ ++ uint8_t complete_split; /**< Enable complete split */ ++ uint8_t hub_addr; /**< Address of high speed hub */ ++ ++ uint8_t port_addr; /**< Port of the low/full speed device */ ++ /** Split transaction position ++ * One of the following values: ++ * - DWC_HCSPLIT_XACTPOS_MID ++ * - DWC_HCSPLIT_XACTPOS_BEGIN ++ * - DWC_HCSPLIT_XACTPOS_END ++ * - DWC_HCSPLIT_XACTPOS_ALL */ ++ uint8_t xact_pos; ++ ++ /** Set when the host channel does a short read. */ ++ uint8_t short_read; ++ ++ /** ++ * Number of requests issued for this channel since it was assigned to ++ * the current transfer (not counting PINGs). ++ */ ++ uint8_t requests; ++ ++ /** ++ * Queue Head for the transfer being processed by this channel. ++ */ ++ struct dwc_otg_qh *qh; ++ ++ /** @} */ ++ ++ /** Entry in list of host channels. */ ++ struct list_head hc_list_entry; ++} dwc_hc_t; ++ ++/** ++ * The following parameters may be specified when starting the module. These ++ * parameters define how the DWC_otg controller should be configured. ++ * Parameter values are passed to the CIL initialization function ++ * dwc_otg_cil_init. ++ */ ++typedef struct dwc_otg_core_params ++{ ++ int32_t opt; ++#define dwc_param_opt_default 1 ++ ++ /** ++ * Specifies the OTG capabilities. The driver will automatically ++ * detect the value for this parameter if none is specified. ++ * 0 - HNP and SRP capable (default) ++ * 1 - SRP Only capable ++ * 2 - No HNP/SRP capable ++ */ ++ int32_t otg_cap; ++#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE 0 ++#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE 1 ++#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE 2 ++#define dwc_param_otg_cap_default DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE ++ ++ /** ++ * Specifies whether to use slave or DMA mode for accessing the data ++ * FIFOs. The driver will automatically detect the value for this ++ * parameter if none is specified. ++ * 0 - Slave ++ * 1 - DMA (default, if available) ++ */ ++ int32_t dma_enable; ++#define dwc_param_dma_enable_default 1 ++ ++ /** ++ * When DMA mode is enabled specifies whether to use address DMA or DMA Descritor mode for accessing the data ++ * FIFOs in device mode. The driver will automatically detect the value for this ++ * parameter if none is specified. ++ * 0 - address DMA ++ * 1 - DMA Descriptor(default, if available) ++ */ ++ int32_t dma_desc_enable; ++#define dwc_param_dma_desc_enable_default 0 ++ /** The DMA Burst size (applicable only for External DMA ++ * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32) ++ */ ++ int32_t dma_burst_size; /* Translate this to GAHBCFG values */ ++#define dwc_param_dma_burst_size_default 32 ++ ++ /** ++ * Specifies the maximum speed of operation in host and device mode. ++ * The actual speed depends on the speed of the attached device and ++ * the value of phy_type. The actual speed depends on the speed of the ++ * attached device. ++ * 0 - High Speed (default) ++ * 1 - Full Speed ++ */ ++ int32_t speed; ++#define dwc_param_speed_default 0 ++#define DWC_SPEED_PARAM_HIGH 0 ++#define DWC_SPEED_PARAM_FULL 1 ++ ++ /** Specifies whether low power mode is supported when attached ++ * to a Full Speed or Low Speed device in host mode. ++ * 0 - Don't support low power mode (default) ++ * 1 - Support low power mode ++ */ ++ int32_t host_support_fs_ls_low_power; ++#define dwc_param_host_support_fs_ls_low_power_default 0 ++ ++ /** Specifies the PHY clock rate in low power mode when connected to a ++ * Low Speed device in host mode. This parameter is applicable only if ++ * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS ++ * then defaults to 6 MHZ otherwise 48 MHZ. ++ * ++ * 0 - 48 MHz ++ * 1 - 6 MHz ++ */ ++ int32_t host_ls_low_power_phy_clk; ++#define dwc_param_host_ls_low_power_phy_clk_default 0 ++#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0 ++#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1 ++ ++ /** ++ * 0 - Use cC FIFO size parameters ++ * 1 - Allow dynamic FIFO sizing (default) ++ */ ++ int32_t enable_dynamic_fifo; ++#define dwc_param_enable_dynamic_fifo_default 1 ++ ++ /** Total number of 4-byte words in the data FIFO memory. This ++ * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic ++ * Tx FIFOs. ++ * 32 to 32768 (default 8192) ++ * Note: The total FIFO memory depth in the FPGA configuration is 8192. ++ */ ++ int32_t data_fifo_size; ++#define dwc_param_data_fifo_size_default 8192 ++ ++ /** Number of 4-byte words in the Rx FIFO in device mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1064) ++ */ ++ int32_t dev_rx_fifo_size; ++#define dwc_param_dev_rx_fifo_size_default 1064 ++ ++ /** Number of 4-byte words in the non-periodic Tx FIFO in device mode ++ * when dynamic FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t dev_nperio_tx_fifo_size; ++#define dwc_param_dev_nperio_tx_fifo_size_default 1024 ++ ++ /** Number of 4-byte words in each of the periodic Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++ uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS]; ++#define dwc_param_dev_perio_tx_fifo_size_default 256 ++ ++ /** Number of 4-byte words in the Rx FIFO in host mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_rx_fifo_size; ++#define dwc_param_host_rx_fifo_size_default 1024 ++ ++ /** Number of 4-byte words in the non-periodic Tx FIFO in host mode ++ * when Dynamic FIFO sizing is enabled in the core. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_nperio_tx_fifo_size; ++#define dwc_param_host_nperio_tx_fifo_size_default 1024 ++ ++ /** Number of 4-byte words in the host periodic Tx FIFO when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_perio_tx_fifo_size; ++#define dwc_param_host_perio_tx_fifo_size_default 1024 ++ ++ /** The maximum transfer size supported in bytes. ++ * 2047 to 65,535 (default 65,535) ++ */ ++ int32_t max_transfer_size; ++#define dwc_param_max_transfer_size_default 65535 ++ ++ /** The maximum number of packets in a transfer. ++ * 15 to 511 (default 511) ++ */ ++ int32_t max_packet_count; ++#define dwc_param_max_packet_count_default 511 ++ ++ /** The number of host channel registers to use. ++ * 1 to 16 (default 12) ++ * Note: The FPGA configuration supports a maximum of 12 host channels. ++ */ ++ int32_t host_channels; ++#define dwc_param_host_channels_default 12 ++ ++ /** The number of endpoints in addition to EP0 available for device ++ * mode operations. ++ * 1 to 15 (default 6 IN and OUT) ++ * Note: The FPGA configuration supports a maximum of 6 IN and OUT ++ * endpoints in addition to EP0. ++ */ ++ int32_t dev_endpoints; ++#define dwc_param_dev_endpoints_default 6 ++ ++ /** ++ * Specifies the type of PHY interface to use. By default, the driver ++ * will automatically detect the phy_type. ++ * ++ * 0 - Full Speed PHY ++ * 1 - UTMI+ (default) ++ * 2 - ULPI ++ */ ++ int32_t phy_type; ++#define DWC_PHY_TYPE_PARAM_FS 0 ++#define DWC_PHY_TYPE_PARAM_UTMI 1 ++#define DWC_PHY_TYPE_PARAM_ULPI 2 ++#define dwc_param_phy_type_default DWC_PHY_TYPE_PARAM_UTMI ++ ++ /** ++ * Specifies the UTMI+ Data Width. This parameter is ++ * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI ++ * PHY_TYPE, this parameter indicates the data width between ++ * the MAC and the ULPI Wrapper.) Also, this parameter is ++ * applicable only if the OTG_HSPHY_WIDTH cC parameter was set ++ * to "8 and 16 bits", meaning that the core has been ++ * configured to work at either data path width. ++ * ++ * 8 or 16 bits (default 16) ++ */ ++ int32_t phy_utmi_width; ++#define dwc_param_phy_utmi_width_default 16 ++ ++ /** ++ * Specifies whether the ULPI operates at double or single ++ * data rate. This parameter is only applicable if PHY_TYPE is ++ * ULPI. ++ * ++ * 0 - single data rate ULPI interface with 8 bit wide data ++ * bus (default) ++ * 1 - double data rate ULPI interface with 4 bit wide data ++ * bus ++ */ ++ int32_t phy_ulpi_ddr; ++#define dwc_param_phy_ulpi_ddr_default 0 ++ ++ /** ++ * Specifies whether to use the internal or external supply to ++ * drive the vbus with a ULPI phy. ++ */ ++ int32_t phy_ulpi_ext_vbus; ++#define DWC_PHY_ULPI_INTERNAL_VBUS 0 ++#define DWC_PHY_ULPI_EXTERNAL_VBUS 1 ++#define dwc_param_phy_ulpi_ext_vbus_default DWC_PHY_ULPI_INTERNAL_VBUS ++ ++ /** ++ * Specifies whether to use the I2Cinterface for full speed PHY. This ++ * parameter is only applicable if PHY_TYPE is FS. ++ * 0 - No (default) ++ * 1 - Yes ++ */ ++ int32_t i2c_enable; ++#define dwc_param_i2c_enable_default 0 ++ ++ int32_t ulpi_fs_ls; ++#define dwc_param_ulpi_fs_ls_default 0 ++ ++ int32_t ts_dline; ++#define dwc_param_ts_dline_default 0 ++ ++ /** ++ * Specifies whether dedicated transmit FIFOs are ++ * enabled for non periodic IN endpoints in device mode ++ * 0 - No ++ * 1 - Yes ++ */ ++ int32_t en_multiple_tx_fifo; ++#define dwc_param_en_multiple_tx_fifo_default 1 ++ ++ /** Number of 4-byte words in each of the Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++ uint32_t dev_tx_fifo_size[MAX_TX_FIFOS]; ++#define dwc_param_dev_tx_fifo_size_default 256 ++ ++ /** Thresholding enable flag- ++ * bit 0 - enable non-ISO Tx thresholding ++ * bit 1 - enable ISO Tx thresholding ++ * bit 2 - enable Rx thresholding ++ */ ++ uint32_t thr_ctl; ++#define dwc_param_thr_ctl_default 0 ++ ++ /** Thresholding length for Tx ++ * FIFOs in 32 bit DWORDs ++ */ ++ uint32_t tx_thr_length; ++#define dwc_param_tx_thr_length_default 64 ++ ++ /** Thresholding length for Rx ++ * FIFOs in 32 bit DWORDs ++ */ ++ uint32_t rx_thr_length; ++#define dwc_param_rx_thr_length_default 64 ++ ++ /** Per Transfer Interrupt ++ * mode enable flag ++ * 1 - Enabled ++ * 0 - Disabled ++ */ ++ uint32_t pti_enable; ++#define dwc_param_pti_enable_default 0 ++ ++ /** Molti Processor Interrupt ++ * mode enable flag ++ * 1 - Enabled ++ * 0 - Disabled ++ */ ++ uint32_t mpi_enable; ++#define dwc_param_mpi_enable_default 0 ++ ++} dwc_otg_core_params_t; ++ ++#ifdef DEBUG ++struct dwc_otg_core_if; ++typedef struct hc_xfer_info ++{ ++ struct dwc_otg_core_if *core_if; ++ dwc_hc_t *hc; ++} hc_xfer_info_t; ++#endif ++ ++/** ++ * The dwc_otg_core_if structure contains information needed to manage ++ * the DWC_otg controller acting in either host or device mode. It ++ * represents the programming view of the controller as a whole. ++ */ ++typedef struct dwc_otg_core_if ++{ ++ /** Parameters that define how the core should be configured.*/ ++ dwc_otg_core_params_t *core_params; ++ ++ /** Core Global registers starting at offset 000h. */ ++ dwc_otg_core_global_regs_t *core_global_regs; ++ ++ /** Device-specific information */ ++ dwc_otg_dev_if_t *dev_if; ++ /** Host-specific information */ ++ dwc_otg_host_if_t *host_if; ++ ++ /** Value from SNPSID register */ ++ uint32_t snpsid; ++ ++ /* ++ * Set to 1 if the core PHY interface bits in USBCFG have been ++ * initialized. ++ */ ++ uint8_t phy_init_done; ++ ++ /* ++ * SRP Success flag, set by srp success interrupt in FS I2C mode ++ */ ++ uint8_t srp_success; ++ uint8_t srp_timer_started; ++ ++ /* Common configuration information */ ++ /** Power and Clock Gating Control Register */ ++ volatile uint32_t *pcgcctl; ++#define DWC_OTG_PCGCCTL_OFFSET 0xE00 ++ ++ /** Push/pop addresses for endpoints or host channels.*/ ++ uint32_t *data_fifo[MAX_EPS_CHANNELS]; ++#define DWC_OTG_DATA_FIFO_OFFSET 0x1000 ++#define DWC_OTG_DATA_FIFO_SIZE 0x1000 ++ ++ /** Total RAM for FIFOs (Bytes) */ ++ uint16_t total_fifo_size; ++ /** Size of Rx FIFO (Bytes) */ ++ uint16_t rx_fifo_size; ++ /** Size of Non-periodic Tx FIFO (Bytes) */ ++ uint16_t nperio_tx_fifo_size; ++ ++ ++ /** 1 if DMA is enabled, 0 otherwise. */ ++ uint8_t dma_enable; ++ ++ /** 1 if Descriptor DMA mode is enabled, 0 otherwise. */ ++ uint8_t dma_desc_enable; ++ ++ /** 1 if PTI Enhancement mode is enabled, 0 otherwise. */ ++ uint8_t pti_enh_enable; ++ ++ /** 1 if MPI Enhancement mode is enabled, 0 otherwise. */ ++ uint8_t multiproc_int_enable; ++ ++ /** 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */ ++ uint8_t en_multiple_tx_fifo; ++ ++ /** Set to 1 if multiple packets of a high-bandwidth transfer is in ++ * process of being queued */ ++ uint8_t queuing_high_bandwidth; ++ ++ /** Hardware Configuration -- stored here for convenience.*/ ++ hwcfg1_data_t hwcfg1; ++ hwcfg2_data_t hwcfg2; ++ hwcfg3_data_t hwcfg3; ++ hwcfg4_data_t hwcfg4; ++ ++ /** Host and Device Configuration -- stored here for convenience.*/ ++ hcfg_data_t hcfg; ++ dcfg_data_t dcfg; ++ ++ /** The operational State, during transations ++ * (a_host>>a_peripherial and b_device=>b_host) this may not ++ * match the core but allows the software to determine ++ * transitions. ++ */ ++ uint8_t op_state; ++ ++ /** ++ * Set to 1 if the HCD needs to be restarted on a session request ++ * interrupt. This is required if no connector ID status change has ++ * occurred since the HCD was last disconnected. ++ */ ++ uint8_t restart_hcd_on_session_req; ++ ++ /** HCD callbacks */ ++ /** A-Device is a_host */ ++#define A_HOST (1) ++ /** A-Device is a_suspend */ ++#define A_SUSPEND (2) ++ /** A-Device is a_peripherial */ ++#define A_PERIPHERAL (3) ++ /** B-Device is operating as a Peripheral. */ ++#define B_PERIPHERAL (4) ++ /** B-Device is operating as a Host. */ ++#define B_HOST (5) ++ ++ /** HCD callbacks */ ++ struct dwc_otg_cil_callbacks *hcd_cb; ++ /** PCD callbacks */ ++ struct dwc_otg_cil_callbacks *pcd_cb; ++ ++ /** Device mode Periodic Tx FIFO Mask */ ++ uint32_t p_tx_msk; ++ /** Device mode Periodic Tx FIFO Mask */ ++ uint32_t tx_msk; ++ ++ /** Workqueue object used for handling several interrupts */ ++ struct workqueue_struct *wq_otg; ++ ++ /** Work object used for handling "Connector ID Status Change" Interrupt */ ++ struct work_struct w_conn_id; ++ ++ /** Work object used for handling "Wakeup Detected" Interrupt */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ struct work_struct w_wkp; ++#else ++ struct delayed_work w_wkp; ++#endif ++ ++#ifdef DEBUG ++ uint32_t start_hcchar_val[MAX_EPS_CHANNELS]; ++ ++ hc_xfer_info_t hc_xfer_info[MAX_EPS_CHANNELS]; ++ struct timer_list hc_xfer_timer[MAX_EPS_CHANNELS]; ++ ++ uint32_t hfnum_7_samples; ++ uint64_t hfnum_7_frrem_accum; ++ uint32_t hfnum_0_samples; ++ uint64_t hfnum_0_frrem_accum; ++ uint32_t hfnum_other_samples; ++ uint64_t hfnum_other_frrem_accum; ++#endif ++ ++ ++} dwc_otg_core_if_t; ++ ++/*We must clear S3C24XX_EINTPEND external interrupt register ++ * because after clearing in this register trigerred IRQ from ++ * H/W core in kernel interrupt can be occured again before OTG ++ * handlers clear all IRQ sources of Core registers because of ++ * timing latencies and Low Level IRQ Type. ++ */ ++ ++#ifdef CONFIG_MACH_IPMATE ++#define S3C2410X_CLEAR_EINTPEND() \ ++do { \ ++ if (!dwc_otg_read_core_intr(core_if)) { \ ++ __raw_writel(1UL << 11,S3C24XX_EINTPEND); \ ++ } \ ++} while (0) ++#else ++#define S3C2410X_CLEAR_EINTPEND() do { } while (0) ++#endif ++ ++/* ++ * The following functions are functions for works ++ * using during handling some interrupts ++ */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ ++extern void w_conn_id_status_change(void *p); ++extern void w_wakeup_detected(void *p); ++ ++#else ++ ++extern void w_conn_id_status_change(struct work_struct *p); ++extern void w_wakeup_detected(struct work_struct *p); ++ ++#endif ++ ++ ++/* ++ * The following functions support initialization of the CIL driver component ++ * and the DWC_otg controller. ++ */ ++extern dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t *_reg_base_addr, ++ dwc_otg_core_params_t *_core_params); ++extern void dwc_otg_cil_remove(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_core_init(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_core_host_init(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_core_dev_init(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_enable_global_interrupts( dwc_otg_core_if_t *_core_if ); ++extern void dwc_otg_disable_global_interrupts( dwc_otg_core_if_t *_core_if ); ++ ++/** @name Device CIL Functions ++ * The following functions support managing the DWC_otg controller in device ++ * mode. ++ */ ++/**@{*/ ++extern void dwc_otg_wakeup(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_read_setup_packet (dwc_otg_core_if_t *_core_if, uint32_t *_dest); ++extern uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_ep0_activate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep_activate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep_deactivate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep_start_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep_write_packet(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep, int _dma); ++extern void dwc_otg_ep_set_stall(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep_clear_stall(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_dump_dev_registers(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_dump_spram(dwc_otg_core_if_t *_core_if); ++#ifdef DWC_EN_ISOC ++extern void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep); ++extern void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep); ++#endif //DWC_EN_ISOC ++/**@}*/ ++ ++/** @name Host CIL Functions ++ * The following functions support managing the DWC_otg controller in host ++ * mode. ++ */ ++/**@{*/ ++extern void dwc_otg_hc_init(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern void dwc_otg_hc_halt(dwc_otg_core_if_t *_core_if, ++ dwc_hc_t *_hc, ++ dwc_otg_halt_status_e _halt_status); ++extern void dwc_otg_hc_cleanup(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern void dwc_otg_hc_start_transfer(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern void dwc_otg_hc_do_ping(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern void dwc_otg_hc_write_packet(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t *_core_if); ++ ++/** ++ * This function Reads HPRT0 in preparation to modify. It keeps the ++ * WC bits 0 so that if they are read as 1, they won't clear when you ++ * write it back ++ */ ++static inline uint32_t dwc_otg_read_hprt0(dwc_otg_core_if_t *_core_if) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_read_reg32(_core_if->host_if->hprt0); ++ hprt0.b.prtena = 0; ++ hprt0.b.prtconndet = 0; ++ hprt0.b.prtenchng = 0; ++ hprt0.b.prtovrcurrchng = 0; ++ return hprt0.d32; ++} ++ ++extern void dwc_otg_dump_host_registers(dwc_otg_core_if_t *_core_if); ++/**@}*/ ++ ++/** @name Common CIL Functions ++ * The following functions support managing the DWC_otg controller in either ++ * device or host mode. ++ */ ++/**@{*/ ++ ++extern void dwc_otg_read_packet(dwc_otg_core_if_t *core_if, ++ uint8_t *dest, ++ uint16_t bytes); ++ ++extern void dwc_otg_dump_global_registers(dwc_otg_core_if_t *_core_if); ++ ++extern void dwc_otg_flush_tx_fifo( dwc_otg_core_if_t *_core_if, ++ const int _num ); ++extern void dwc_otg_flush_rx_fifo( dwc_otg_core_if_t *_core_if ); ++extern void dwc_otg_core_reset( dwc_otg_core_if_t *_core_if ); ++ ++extern dwc_otg_dma_desc_t* dwc_otg_ep_alloc_desc_chain(uint32_t * dma_desc_addr, uint32_t count); ++extern void dwc_otg_ep_free_desc_chain(dwc_otg_dma_desc_t* desc_addr, uint32_t dma_desc_addr, uint32_t count); ++ ++/** ++ * This function returns the Core Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_core_intr(dwc_otg_core_if_t *_core_if) ++{ ++ return (dwc_read_reg32(&_core_if->core_global_regs->gintsts) & ++ dwc_read_reg32(&_core_if->core_global_regs->gintmsk)); ++} ++ ++/** ++ * This function returns the OTG Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_otg_intr (dwc_otg_core_if_t *_core_if) ++{ ++ return (dwc_read_reg32 (&_core_if->core_global_regs->gotgint)); ++} ++ ++/** ++ * This function reads the Device All Endpoints Interrupt register and ++ * returns the IN endpoint interrupt bits. ++ */ ++static inline uint32_t dwc_otg_read_dev_all_in_ep_intr(dwc_otg_core_if_t *core_if) ++{ ++ uint32_t v; ++ ++ if(core_if->multiproc_int_enable) { ++ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs->deachint) & ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->deachintmsk); ++ } else { ++ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs->daint) & ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->daintmsk); ++ } ++ return (v & 0xffff); ++ ++} ++ ++/** ++ * This function reads the Device All Endpoints Interrupt register and ++ * returns the OUT endpoint interrupt bits. ++ */ ++static inline uint32_t dwc_otg_read_dev_all_out_ep_intr(dwc_otg_core_if_t *core_if) ++{ ++ uint32_t v; ++ ++ if(core_if->multiproc_int_enable) { ++ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs->deachint) & ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->deachintmsk); ++ } else { ++ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs->daint) & ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->daintmsk); ++ } ++ ++ return ((v & 0xffff0000) >> 16); ++} ++ ++/** ++ * This function returns the Device IN EP Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_dev_in_ep_intr(dwc_otg_core_if_t *core_if, ++ dwc_ep_t *ep) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ uint32_t v, msk, emp; ++ ++ if(core_if->multiproc_int_enable) { ++ msk = dwc_read_reg32(&dev_if->dev_global_regs->diepeachintmsk[ep->num]); ++ emp = dwc_read_reg32(&dev_if->dev_global_regs->dtknqr4_fifoemptymsk); ++ msk |= ((emp >> ep->num) & 0x1) << 7; ++ v = dwc_read_reg32(&dev_if->in_ep_regs[ep->num]->diepint) & msk; ++ } else { ++ msk = dwc_read_reg32(&dev_if->dev_global_regs->diepmsk); ++ emp = dwc_read_reg32(&dev_if->dev_global_regs->dtknqr4_fifoemptymsk); ++ msk |= ((emp >> ep->num) & 0x1) << 7; ++ v = dwc_read_reg32(&dev_if->in_ep_regs[ep->num]->diepint) & msk; ++ } ++ ++ ++ return v; ++} ++/** ++ * This function returns the Device OUT EP Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_dev_out_ep_intr(dwc_otg_core_if_t *_core_if, ++ dwc_ep_t *_ep) ++{ ++ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; ++ uint32_t v; ++ doepmsk_data_t msk = { .d32 = 0 }; ++ ++ if(_core_if->multiproc_int_enable) { ++ msk.d32 = dwc_read_reg32(&dev_if->dev_global_regs->doepeachintmsk[_ep->num]); ++ if(_core_if->pti_enh_enable) { ++ msk.b.pktdrpsts = 1; ++ } ++ v = dwc_read_reg32( &dev_if->out_ep_regs[_ep->num]->doepint) & msk.d32; ++ } else { ++ msk.d32 = dwc_read_reg32(&dev_if->dev_global_regs->doepmsk); ++ if(_core_if->pti_enh_enable) { ++ msk.b.pktdrpsts = 1; ++ } ++ v = dwc_read_reg32( &dev_if->out_ep_regs[_ep->num]->doepint) & msk.d32; ++ } ++ return v; ++} ++ ++/** ++ * This function returns the Host All Channel Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_host_all_channels_intr (dwc_otg_core_if_t *_core_if) ++{ ++ return (dwc_read_reg32 (&_core_if->host_if->host_global_regs->haint)); ++} ++ ++static inline uint32_t dwc_otg_read_host_channel_intr (dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc) ++{ ++ return (dwc_read_reg32 (&_core_if->host_if->hc_regs[_hc->hc_num]->hcint)); ++} ++ ++ ++/** ++ * This function returns the mode of the operation, host or device. ++ * ++ * @return 0 - Device Mode, 1 - Host Mode ++ */ ++static inline uint32_t dwc_otg_mode(dwc_otg_core_if_t *_core_if) ++{ ++ return (dwc_read_reg32( &_core_if->core_global_regs->gintsts ) & 0x1); ++} ++ ++static inline uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t *_core_if) ++{ ++ return (dwc_otg_mode(_core_if) != DWC_HOST_MODE); ++} ++static inline uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t *_core_if) ++{ ++ return (dwc_otg_mode(_core_if) == DWC_HOST_MODE); ++} ++ ++extern int32_t dwc_otg_handle_common_intr( dwc_otg_core_if_t *_core_if ); ++ ++ ++/**@}*/ ++ ++/** ++ * DWC_otg CIL callback structure. This structure allows the HCD and ++ * PCD to register functions used for starting and stopping the PCD ++ * and HCD for role change on for a DRD. ++ */ ++typedef struct dwc_otg_cil_callbacks ++{ ++ /** Start function for role change */ ++ int (*start) (void *_p); ++ /** Stop Function for role change */ ++ int (*stop) (void *_p); ++ /** Disconnect Function for role change */ ++ int (*disconnect) (void *_p); ++ /** Resume/Remote wakeup Function */ ++ int (*resume_wakeup) (void *_p); ++ /** Suspend function */ ++ int (*suspend) (void *_p); ++ /** Session Start (SRP) */ ++ int (*session_start) (void *_p); ++ /** Pointer passed to start() and stop() */ ++ void *p; ++} dwc_otg_cil_callbacks_t; ++ ++extern void dwc_otg_cil_register_pcd_callbacks( dwc_otg_core_if_t *_core_if, ++ dwc_otg_cil_callbacks_t *_cb, ++ void *_p); ++extern void dwc_otg_cil_register_hcd_callbacks( dwc_otg_core_if_t *_core_if, ++ dwc_otg_cil_callbacks_t *_cb, ++ void *_p); ++ ++#endif ++ +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_cil_intr.c +@@ -0,0 +1,750 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil_intr.c $ ++ * $Revision: 1.2 $ ++ * $Date: 2008-11-21 05:39:15 $ ++ * $Change: 1065567 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The Core Interface Layer provides basic services for accessing and ++ * managing the DWC_otg hardware. These services are used by both the ++ * Host Controller Driver and the Peripheral Controller Driver. ++ * ++ * This file contains the Common Interrupt handlers. ++ */ ++#include "linux/dwc_otg_plat.h" ++#include "dwc_otg_regs.h" ++#include "dwc_otg_cil.h" ++ ++#ifdef DEBUG ++inline const char *op_state_str(dwc_otg_core_if_t *core_if) ++{ ++ return (core_if->op_state==A_HOST?"a_host": ++ (core_if->op_state==A_SUSPEND?"a_suspend": ++ (core_if->op_state==A_PERIPHERAL?"a_peripheral": ++ (core_if->op_state==B_PERIPHERAL?"b_peripheral": ++ (core_if->op_state==B_HOST?"b_host": ++ "unknown"))))); ++} ++#endif ++ ++/** This function will log a debug message ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_mode_mismatch_intr (dwc_otg_core_if_t *core_if) ++{ ++ gintsts_data_t gintsts; ++ DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n", ++ dwc_otg_mode(core_if) ? "Host" : "Device"); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.modemismatch = 1; ++ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++/** Start the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_start(dwc_otg_core_if_t *core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->start) { ++ core_if->hcd_cb->start(core_if->hcd_cb->p); ++ } ++} ++/** Stop the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_stop(dwc_otg_core_if_t *core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->stop) { ++ core_if->hcd_cb->stop(core_if->hcd_cb->p); ++ } ++} ++/** Disconnect the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_disconnect(dwc_otg_core_if_t *core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->disconnect) { ++ core_if->hcd_cb->disconnect(core_if->hcd_cb->p); ++ } ++} ++/** Inform the HCD the a New Session has begun. Helper function for ++ * using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_session_start(dwc_otg_core_if_t *core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->session_start) { ++ core_if->hcd_cb->session_start(core_if->hcd_cb->p); ++ } ++} ++ ++/** Start the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_start(dwc_otg_core_if_t *core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->start) { ++ core_if->pcd_cb->start(core_if->pcd_cb->p); ++ } ++} ++/** Stop the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_stop(dwc_otg_core_if_t *core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->stop) { ++ core_if->pcd_cb->stop(core_if->pcd_cb->p); ++ } ++} ++/** Suspend the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_suspend(dwc_otg_core_if_t *core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->suspend) { ++ core_if->pcd_cb->suspend(core_if->pcd_cb->p); ++ } ++} ++/** Resume the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_resume(dwc_otg_core_if_t *core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { ++ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); ++ } ++} ++ ++/** ++ * This function handles the OTG Interrupts. It reads the OTG ++ * Interrupt Register (GOTGINT) to determine what interrupt has ++ * occurred. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t *core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = ++ core_if->core_global_regs; ++ gotgint_data_t gotgint; ++ gotgctl_data_t gotgctl; ++ gintmsk_data_t gintmsk; ++ ++ gotgint.d32 = dwc_read_reg32(&global_regs->gotgint); ++ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl); ++ DWC_DEBUGPL(DBG_CIL, "gotgctl=%08x\n", gotgctl.d32); ++ ++ if (gotgint.b.sesenddet) { ++ DWC_DEBUGPL(DBG_ANY, "OTG Interrupt: " ++ "Session End Detected++ (%s)\n", ++ op_state_str(core_if)); ++ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl); ++ ++ if (core_if->op_state == B_HOST) { ++ pcd_start(core_if); ++ core_if->op_state = B_PERIPHERAL; ++ } else { ++ /* If not B_HOST and Device HNP still set. HNP ++ * Did not succeed!*/ ++ if (gotgctl.b.devhnpen) { ++ DWC_DEBUGPL(DBG_ANY, "Session End Detected\n"); ++ DWC_ERROR("Device Not Connected/Responding!\n"); ++ } ++ ++ /* If Session End Detected the B-Cable has ++ * been disconnected. */ ++ /* Reset PCD and Gadget driver to a ++ * clean state. */ ++ pcd_stop(core_if); ++ } ++ gotgctl.d32 = 0; ++ gotgctl.b.devhnpen = 1; ++ dwc_modify_reg32(&global_regs->gotgctl, ++ gotgctl.d32, 0); ++ } ++ if (gotgint.b.sesreqsucstschng) { ++ DWC_DEBUGPL(DBG_ANY, " OTG Interrupt: " ++ "Session Reqeust Success Status Change++\n"); ++ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl); ++ if (gotgctl.b.sesreqscs) { ++ if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) && ++ (core_if->core_params->i2c_enable)) { ++ core_if->srp_success = 1; ++ } ++ else { ++ pcd_resume(core_if); ++ /* Clear Session Request */ ++ gotgctl.d32 = 0; ++ gotgctl.b.sesreq = 1; ++ dwc_modify_reg32(&global_regs->gotgctl, ++ gotgctl.d32, 0); ++ } ++ } ++ } ++ if (gotgint.b.hstnegsucstschng) { ++ /* Print statements during the HNP interrupt handling ++ * can cause it to fail.*/ ++ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl); ++ if (gotgctl.b.hstnegscs) { ++ if (dwc_otg_is_host_mode(core_if)) { ++ core_if->op_state = B_HOST; ++ /* ++ * Need to disable SOF interrupt immediately. ++ * When switching from device to host, the PCD ++ * interrupt handler won't handle the ++ * interrupt if host mode is already set. The ++ * HCD interrupt handler won't get called if ++ * the HCD state is HALT. This means that the ++ * interrupt does not get handled and Linux ++ * complains loudly. ++ */ ++ gintmsk.d32 = 0; ++ gintmsk.b.sofintr = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, ++ gintmsk.d32, 0); ++ pcd_stop(core_if); ++ /* ++ * Initialize the Core for Host mode. ++ */ ++ hcd_start(core_if); ++ core_if->op_state = B_HOST; ++ } ++ } else { ++ gotgctl.d32 = 0; ++ gotgctl.b.hnpreq = 1; ++ gotgctl.b.devhnpen = 1; ++ dwc_modify_reg32(&global_regs->gotgctl, ++ gotgctl.d32, 0); ++ DWC_DEBUGPL(DBG_ANY, "HNP Failed\n"); ++ DWC_ERROR("Device Not Connected/Responding\n"); ++ } ++ } ++ if (gotgint.b.hstnegdet) { ++ /* The disconnect interrupt is set at the same time as ++ * Host Negotiation Detected. During the mode ++ * switch all interrupts are cleared so the disconnect ++ * interrupt handler will not get executed. ++ */ ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Host Negotiation Detected++ (%s)\n", ++ (dwc_otg_is_host_mode(core_if)?"Host":"Device")); ++ if (dwc_otg_is_device_mode(core_if)){ ++ DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n", core_if->op_state); ++ hcd_disconnect(core_if); ++ pcd_start(core_if); ++ core_if->op_state = A_PERIPHERAL; ++ } else { ++ /* ++ * Need to disable SOF interrupt immediately. When ++ * switching from device to host, the PCD interrupt ++ * handler won't handle the interrupt if host mode is ++ * already set. The HCD interrupt handler won't get ++ * called if the HCD state is HALT. This means that ++ * the interrupt does not get handled and Linux ++ * complains loudly. ++ */ ++ gintmsk.d32 = 0; ++ gintmsk.b.sofintr = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, ++ gintmsk.d32, 0); ++ pcd_stop(core_if); ++ hcd_start(core_if); ++ core_if->op_state = A_HOST; ++ } ++ } ++ if (gotgint.b.adevtoutchng) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "A-Device Timeout Change++\n"); ++ } ++ if (gotgint.b.debdone) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Debounce Done++\n"); ++ } ++ ++ /* Clear GOTGINT */ ++ dwc_write_reg32 (&core_if->core_global_regs->gotgint, gotgint.d32); ++ ++ return 1; ++} ++ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ ++void w_conn_id_status_change(void *p) ++{ ++ dwc_otg_core_if_t *core_if = p; ++ ++#else ++ ++void w_conn_id_status_change(struct work_struct *p) ++{ ++ dwc_otg_core_if_t *core_if = container_of(p, dwc_otg_core_if_t, w_conn_id); ++ ++#endif ++ ++ ++ uint32_t count = 0; ++ gotgctl_data_t gotgctl = { .d32 = 0 }; ++ ++ gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl); ++ DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32); ++ DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts); ++ ++ /* B-Device connector (Device Mode) */ ++ if (gotgctl.b.conidsts) { ++ /* Wait for switch to device mode. */ ++ while (!dwc_otg_is_device_mode(core_if)){ ++ DWC_PRINT("Waiting for Peripheral Mode, Mode=%s\n", ++ (dwc_otg_is_host_mode(core_if)?"Host":"Peripheral")); ++ MDELAY(100); ++ if (++count > 10000) *(uint32_t*)NULL=0; ++ } ++ core_if->op_state = B_PERIPHERAL; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ pcd_start(core_if); ++ } else { ++ /* A-Device connector (Host Mode) */ ++ while (!dwc_otg_is_host_mode(core_if)) { ++ DWC_PRINT("Waiting for Host Mode, Mode=%s\n", ++ (dwc_otg_is_host_mode(core_if)?"Host":"Peripheral")); ++ MDELAY(100); ++ if (++count > 10000) *(uint32_t*)NULL=0; ++ } ++ core_if->op_state = A_HOST; ++ /* ++ * Initialize the Core for Host mode. ++ */ ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ hcd_start(core_if); ++ } ++} ++ ++ ++/** ++ * This function handles the Connector ID Status Change Interrupt. It ++ * reads the OTG Interrupt Register (GOTCTL) to determine whether this ++ * is a Device to Host Mode transition or a Host Mode to Device ++ * Transition. ++ * ++ * This only occurs when the cable is connected/removed from the PHY ++ * connector. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t *core_if) ++{ ++ ++ /* ++ * Need to disable SOF interrupt immediately. If switching from device ++ * to host, the PCD interrupt handler won't handle the interrupt if ++ * host mode is already set. The HCD interrupt handler won't get ++ * called if the HCD state is HALT. This means that the interrupt does ++ * not get handled and Linux complains loudly. ++ */ ++ gintmsk_data_t gintmsk = { .d32 = 0 }; ++ gintsts_data_t gintsts = { .d32 = 0 }; ++ ++ gintmsk.b.sofintr = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0); ++ ++ DWC_DEBUGPL(DBG_CIL, " ++Connector ID Status Change Interrupt++ (%s)\n", ++ (dwc_otg_is_host_mode(core_if)?"Host":"Device")); ++ ++ /* ++ * Need to schedule a work, as there are possible DELAY function calls ++ */ ++ queue_work(core_if->wq_otg, &core_if->w_conn_id); ++ ++ /* Set flag and clear interrupt */ ++ gintsts.b.conidstschng = 1; ++ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that a device is initiating the Session ++ * Request Protocol to request the host to turn on bus power so a new ++ * session can begin. The handler responds by turning on bus power. If ++ * the DWC_otg controller is in low power mode, the handler brings the ++ * controller out of low power mode before turning on bus power. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t *core_if) ++{ ++ gintsts_data_t gintsts; ++ ++#ifndef DWC_HOST_ONLY ++ hprt0_data_t hprt0; ++ DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n"); ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ DWC_PRINT("SRP: Device mode\n"); ++ } else { ++ DWC_PRINT("SRP: Host mode\n"); ++ ++ /* Turn on the port power bit. */ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ ++ /* Start the Connection timer. So a message can be displayed ++ * if connect does not occur within 10 seconds. */ ++ hcd_session_start(core_if); ++ } ++#endif ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.sessreqintr = 1; ++ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++void w_wakeup_detected(void *p) ++{ ++ dwc_otg_core_if_t* core_if = p; ++ ++#else ++ ++void w_wakeup_detected(struct work_struct *p) ++{ ++ struct delayed_work *dw = container_of(p, struct delayed_work, work); ++ dwc_otg_core_if_t *core_if = container_of(dw, dwc_otg_core_if_t, w_wkp); ++ ++#endif ++ /* ++ * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms ++ * so that OPT tests pass with all PHYs). ++ */ ++ hprt0_data_t hprt0 = {.d32=0}; ++#if 0 ++ pcgcctl_data_t pcgcctl = {.d32=0}; ++ /* Restart the Phy Clock */ ++ pcgcctl.b.stoppclk = 1; ++ dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, 0); ++ UDELAY(10); ++#endif //0 ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ DWC_DEBUGPL(DBG_ANY,"Resume: HPRT0=%0x\n", hprt0.d32); ++// MDELAY(70); ++ hprt0.b.prtres = 0; /* Resume */ ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ DWC_DEBUGPL(DBG_ANY,"Clear Resume: HPRT0=%0x\n", dwc_read_reg32(core_if->host_if->hprt0)); ++} ++/** ++ * This interrupt indicates that the DWC_otg controller has detected a ++ * resume or remote wakeup sequence. If the DWC_otg controller is in ++ * low power mode, the handler must brings the controller out of low ++ * power mode. The controller automatically begins resume ++ * signaling. The handler schedules a time to stop resume signaling. ++ */ ++int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t *core_if) ++{ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_ANY, "++Resume and Remote Wakeup Detected Interrupt++\n"); ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ dctl_data_t dctl = {.d32=0}; ++ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts)); ++#ifdef PARTIAL_POWER_DOWN ++ if (core_if->hwcfg4.b.power_optimiz) { ++ pcgcctl_data_t power = {.d32=0}; ++ ++ power.d32 = dwc_read_reg32(core_if->pcgcctl); ++ DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", power.d32); ++ ++ power.b.stoppclk = 0; ++ dwc_write_reg32(core_if->pcgcctl, power.d32); ++ ++ power.b.pwrclmp = 0; ++ dwc_write_reg32(core_if->pcgcctl, power.d32); ++ ++ power.b.rstpdwnmodule = 0; ++ dwc_write_reg32(core_if->pcgcctl, power.d32); ++ } ++#endif ++ /* Clear the Remote Wakeup Signalling */ ++ dctl.b.rmtwkupsig = 1; ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dctl, ++ dctl.d32, 0); ++ ++ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { ++ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); ++ } ++ ++ } else { ++ pcgcctl_data_t pcgcctl = {.d32=0}; ++ ++ /* Restart the Phy Clock */ ++ pcgcctl.b.stoppclk = 1; ++ dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, 0); ++ ++ queue_delayed_work(core_if->wq_otg, &core_if->w_wkp, ((70 * HZ / 1000) + 1)); ++ } ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.wkupintr = 1; ++ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that a device has been disconnected from ++ * the root port. ++ */ ++int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t *core_if) ++{ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n", ++ (dwc_otg_is_host_mode(core_if)?"Host":"Device"), ++ op_state_str(core_if)); ++ ++/** @todo Consolidate this if statement. */ ++#ifndef DWC_HOST_ONLY ++ if (core_if->op_state == B_HOST) { ++ /* If in device mode Disconnect and stop the HCD, then ++ * start the PCD. */ ++ hcd_disconnect(core_if); ++ pcd_start(core_if); ++ core_if->op_state = B_PERIPHERAL; ++ } else if (dwc_otg_is_device_mode(core_if)) { ++ gotgctl_data_t gotgctl = { .d32 = 0 }; ++ gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl); ++ if (gotgctl.b.hstsethnpen==1) { ++ /* Do nothing, if HNP in process the OTG ++ * interrupt "Host Negotiation Detected" ++ * interrupt will do the mode switch. ++ */ ++ } else if (gotgctl.b.devhnpen == 0) { ++ /* If in device mode Disconnect and stop the HCD, then ++ * start the PCD. */ ++ hcd_disconnect(core_if); ++ pcd_start(core_if); ++ core_if->op_state = B_PERIPHERAL; ++ } else { ++ DWC_DEBUGPL(DBG_ANY,"!a_peripheral && !devhnpen\n"); ++ } ++ } else { ++ if (core_if->op_state == A_HOST) { ++ /* A-Cable still connected but device disconnected. */ ++ hcd_disconnect(core_if); ++ } ++ } ++#endif ++ ++ gintsts.d32 = 0; ++ gintsts.b.disconnect = 1; ++ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++/** ++ * This interrupt indicates that SUSPEND state has been detected on ++ * the USB. ++ * ++ * For HNP the USB Suspend interrupt signals the change from ++ * "a_peripheral" to "a_host". ++ * ++ * When power management is enabled the core will be put in low power ++ * mode. ++ */ ++int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t *core_if) ++{ ++ dsts_data_t dsts; ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_ANY,"USB SUSPEND\n"); ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ /* Check the Device status register to determine if the Suspend ++ * state is active. */ ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32); ++ DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d " ++ "HWCFG4.power Optimize=%d\n", ++ dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz); ++ ++ ++#ifdef PARTIAL_POWER_DOWN ++/** @todo Add a module parameter for power management. */ ++ ++ if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) { ++ pcgcctl_data_t power = {.d32=0}; ++ DWC_DEBUGPL(DBG_CIL, "suspend\n"); ++ ++ power.b.pwrclmp = 1; ++ dwc_write_reg32(core_if->pcgcctl, power.d32); ++ ++ power.b.rstpdwnmodule = 1; ++ dwc_modify_reg32(core_if->pcgcctl, 0, power.d32); ++ ++ power.b.stoppclk = 1; ++ dwc_modify_reg32(core_if->pcgcctl, 0, power.d32); ++ ++ } else { ++ DWC_DEBUGPL(DBG_ANY,"disconnect?\n"); ++ } ++#endif ++ /* PCD callback for suspend. */ ++ pcd_suspend(core_if); ++ } else { ++ if (core_if->op_state == A_PERIPHERAL) { ++ DWC_DEBUGPL(DBG_ANY,"a_peripheral->a_host\n"); ++ /* Clear the a_peripheral flag, back to a_host. */ ++ pcd_stop(core_if); ++ hcd_start(core_if); ++ core_if->op_state = A_HOST; ++ } ++ } ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.usbsuspend = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++ ++/** ++ * This function returns the Core Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t *core_if) ++{ ++ gintsts_data_t gintsts; ++ gintmsk_data_t gintmsk; ++ gintmsk_data_t gintmsk_common = {.d32=0}; ++ gintmsk_common.b.wkupintr = 1; ++ gintmsk_common.b.sessreqintr = 1; ++ gintmsk_common.b.conidstschng = 1; ++ gintmsk_common.b.otgintr = 1; ++ gintmsk_common.b.modemismatch = 1; ++ gintmsk_common.b.disconnect = 1; ++ gintmsk_common.b.usbsuspend = 1; ++ /** @todo: The port interrupt occurs while in device ++ * mode. Added code to CIL to clear the interrupt for now! ++ */ ++ gintmsk_common.b.portintr = 1; ++ ++ gintsts.d32 = dwc_read_reg32(&core_if->core_global_regs->gintsts); ++ gintmsk.d32 = dwc_read_reg32(&core_if->core_global_regs->gintmsk); ++#ifdef DEBUG ++ /* if any common interrupts set */ ++ if (gintsts.d32 & gintmsk_common.d32) { ++ DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n", ++ gintsts.d32, gintmsk.d32); ++ } ++#endif ++ ++ return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); ++ ++} ++ ++/** ++ * Common interrupt handler. ++ * ++ * The common interrupts are those that occur in both Host and Device mode. ++ * This handler handles the following interrupts: ++ * - Mode Mismatch Interrupt ++ * - Disconnect Interrupt ++ * - OTG Interrupt ++ * - Connector ID Status Change Interrupt ++ * - Session Request Interrupt. ++ * - Resume / Remote Wakeup Detected Interrupt. ++ * ++ */ ++int32_t dwc_otg_handle_common_intr(dwc_otg_core_if_t *core_if) ++{ ++ int retval = 0; ++ gintsts_data_t gintsts; ++ ++ gintsts.d32 = dwc_otg_read_common_intr(core_if); ++ ++ if (gintsts.b.modemismatch) { ++ retval |= dwc_otg_handle_mode_mismatch_intr(core_if); ++ } ++ if (gintsts.b.otgintr) { ++ retval |= dwc_otg_handle_otg_intr(core_if); ++ } ++ if (gintsts.b.conidstschng) { ++ retval |= dwc_otg_handle_conn_id_status_change_intr(core_if); ++ } ++ if (gintsts.b.disconnect) { ++ retval |= dwc_otg_handle_disconnect_intr(core_if); ++ } ++ if (gintsts.b.sessreqintr) { ++ retval |= dwc_otg_handle_session_req_intr(core_if); ++ } ++ if (gintsts.b.wkupintr) { ++ retval |= dwc_otg_handle_wakeup_detected_intr(core_if); ++ } ++ if (gintsts.b.usbsuspend) { ++ retval |= dwc_otg_handle_usb_suspend_intr(core_if); ++ } ++ if (gintsts.b.portintr && dwc_otg_is_device_mode(core_if)) { ++ /* The port interrupt occurs while in device mode with HPRT0 ++ * Port Enable/Disable. ++ */ ++ gintsts.d32 = 0; ++ gintsts.b.portintr = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, ++ gintsts.d32); ++ retval |= 1; ++ ++ } ++ ++ S3C2410X_CLEAR_EINTPEND(); ++ ++ return retval; ++} +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_driver.c +@@ -0,0 +1,1273 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_driver.c $ ++ * $Revision: 1.7 $ ++ * $Date: 2008-11-21 05:39:15 $ ++ * $Change: 791271 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * The dwc_otg_driver module provides the initialization and cleanup entry ++ * points for the DWC_otg driver. This module will be dynamically installed ++ * after Linux is booted using the insmod command. When the module is ++ * installed, the dwc_otg_driver_init function is called. When the module is ++ * removed (using rmmod), the dwc_otg_driver_cleanup function is called. ++ * ++ * This module also defines a data structure for the dwc_otg_driver, which is ++ * used in conjunction with the standard ARM platform_device structure. These ++ * structures allow the OTG driver to comply with the standard Linux driver ++ * model in which devices and drivers are registered with a bus driver. This ++ * has the benefit that Linux can expose attributes of the driver and device ++ * in its special sysfs file system. Users can then read or write files in ++ * this file system to perform diagnostics on the driver components or the ++ * device. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* permission constants */ ++#include ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++# include ++#endif ++ ++#include ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++# include ++#endif ++ ++#include "linux/dwc_otg_plat.h" ++#include "dwc_otg_attr.h" ++#include "dwc_otg_driver.h" ++#include "dwc_otg_cil.h" ++#include "dwc_otg_pcd.h" ++#include "dwc_otg_hcd.h" ++ ++#define DWC_DRIVER_VERSION "2.72a 24-JUN-2008" ++#define DWC_DRIVER_DESC "HS OTG USB Controller driver" ++ ++static const char dwc_driver_name[] = "dwc_otg"; ++ ++/*-------------------------------------------------------------------------*/ ++/* Encapsulate the module parameter settings */ ++ ++static dwc_otg_core_params_t dwc_otg_module_params = { ++ .opt = -1, ++ .otg_cap = -1, ++ .dma_enable = -1, ++ .dma_desc_enable = -1, ++ .dma_burst_size = -1, ++ .speed = -1, ++ .host_support_fs_ls_low_power = -1, ++ .host_ls_low_power_phy_clk = -1, ++ .enable_dynamic_fifo = -1, ++ .data_fifo_size = -1, ++ .dev_rx_fifo_size = -1, ++ .dev_nperio_tx_fifo_size = -1, ++ .dev_perio_tx_fifo_size = { ++ /* dev_perio_tx_fifo_size_1 */ ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1 ++ /* 15 */ ++ }, ++ .host_rx_fifo_size = -1, ++ .host_nperio_tx_fifo_size = -1, ++ .host_perio_tx_fifo_size = -1, ++ .max_transfer_size = -1, ++ .max_packet_count = -1, ++ .host_channels = -1, ++ .dev_endpoints = -1, ++ .phy_type = -1, ++ .phy_utmi_width = -1, ++ .phy_ulpi_ddr = -1, ++ .phy_ulpi_ext_vbus = -1, ++ .i2c_enable = -1, ++ .ulpi_fs_ls = -1, ++ .ts_dline = -1, ++ .en_multiple_tx_fifo = -1, ++ .dev_tx_fifo_size = { ++ /* dev_tx_fifo_size */ ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1 ++ /* 15 */ ++ }, ++ .thr_ctl = -1, ++ .tx_thr_length = -1, ++ .rx_thr_length = -1, ++ .pti_enable = -1, ++ .mpi_enable = -1, ++}; ++ ++/** ++ * This function shows the Driver Version. ++ */ ++static ssize_t version_show(struct device_driver *dev, char *buf) ++{ ++ return snprintf(buf, sizeof(DWC_DRIVER_VERSION)+2, "%s\n", ++ DWC_DRIVER_VERSION); ++} ++static DRIVER_ATTR(version, S_IRUGO, version_show, NULL); ++ ++/** ++ * Global Debug Level Mask. ++ */ ++uint32_t g_dbg_lvl = 0; /* OFF */ ++ ++/** ++ * This function shows the driver Debug Level. ++ */ ++static ssize_t dbg_level_show(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "0x%0x\n", g_dbg_lvl); ++} ++ ++/** ++ * This function stores the driver Debug Level. ++ */ ++static ssize_t dbg_level_store(struct device_driver *drv, const char *buf, ++ size_t count) ++{ ++ g_dbg_lvl = simple_strtoul(buf, NULL, 16); ++ return count; ++} ++static DRIVER_ATTR(debuglevel, S_IRUGO|S_IWUSR, dbg_level_show, dbg_level_store); ++ ++/** ++ * This function is called during module intialization to verify that ++ * the module parameters are in a valid state. ++ */ ++static int check_parameters(dwc_otg_core_if_t *core_if) ++{ ++ int i; ++ int retval = 0; ++ ++/* Checks if the parameter is outside of its valid range of values */ ++#define DWC_OTG_PARAM_TEST(_param_, _low_, _high_) \ ++ ((dwc_otg_module_params._param_ < (_low_)) || \ ++ (dwc_otg_module_params._param_ > (_high_))) ++ ++/* If the parameter has been set by the user, check that the parameter value is ++ * within the value range of values. If not, report a module error. */ ++#define DWC_OTG_PARAM_ERR(_param_, _low_, _high_, _string_) \ ++ do { \ ++ if (dwc_otg_module_params._param_ != -1) { \ ++ if (DWC_OTG_PARAM_TEST(_param_, (_low_), (_high_))) { \ ++ DWC_ERROR("`%d' invalid for parameter `%s'\n", \ ++ dwc_otg_module_params._param_, _string_); \ ++ dwc_otg_module_params._param_ = dwc_param_##_param_##_default; \ ++ retval++; \ ++ } \ ++ } \ ++ } while (0) ++ ++ DWC_OTG_PARAM_ERR(opt,0,1,"opt"); ++ DWC_OTG_PARAM_ERR(otg_cap,0,2,"otg_cap"); ++ DWC_OTG_PARAM_ERR(dma_enable,0,1,"dma_enable"); ++ DWC_OTG_PARAM_ERR(dma_desc_enable,0,1,"dma_desc_enable"); ++ DWC_OTG_PARAM_ERR(speed,0,1,"speed"); ++ DWC_OTG_PARAM_ERR(host_support_fs_ls_low_power,0,1,"host_support_fs_ls_low_power"); ++ DWC_OTG_PARAM_ERR(host_ls_low_power_phy_clk,0,1,"host_ls_low_power_phy_clk"); ++ DWC_OTG_PARAM_ERR(enable_dynamic_fifo,0,1,"enable_dynamic_fifo"); ++ DWC_OTG_PARAM_ERR(data_fifo_size,32,32768,"data_fifo_size"); ++ DWC_OTG_PARAM_ERR(dev_rx_fifo_size,16,32768,"dev_rx_fifo_size"); ++ DWC_OTG_PARAM_ERR(dev_nperio_tx_fifo_size,16,32768,"dev_nperio_tx_fifo_size"); ++ DWC_OTG_PARAM_ERR(host_rx_fifo_size,16,32768,"host_rx_fifo_size"); ++ DWC_OTG_PARAM_ERR(host_nperio_tx_fifo_size,16,32768,"host_nperio_tx_fifo_size"); ++ DWC_OTG_PARAM_ERR(host_perio_tx_fifo_size,16,32768,"host_perio_tx_fifo_size"); ++ DWC_OTG_PARAM_ERR(max_transfer_size,2047,524288,"max_transfer_size"); ++ DWC_OTG_PARAM_ERR(max_packet_count,15,511,"max_packet_count"); ++ DWC_OTG_PARAM_ERR(host_channels,1,16,"host_channels"); ++ DWC_OTG_PARAM_ERR(dev_endpoints,1,15,"dev_endpoints"); ++ DWC_OTG_PARAM_ERR(phy_type,0,2,"phy_type"); ++ DWC_OTG_PARAM_ERR(phy_ulpi_ddr,0,1,"phy_ulpi_ddr"); ++ DWC_OTG_PARAM_ERR(phy_ulpi_ext_vbus,0,1,"phy_ulpi_ext_vbus"); ++ DWC_OTG_PARAM_ERR(i2c_enable,0,1,"i2c_enable"); ++ DWC_OTG_PARAM_ERR(ulpi_fs_ls,0,1,"ulpi_fs_ls"); ++ DWC_OTG_PARAM_ERR(ts_dline,0,1,"ts_dline"); ++ ++ if (dwc_otg_module_params.dma_burst_size != -1) { ++ if (DWC_OTG_PARAM_TEST(dma_burst_size,1,1) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,4,4) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,8,8) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,16,16) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,32,32) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,64,64) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,128,128) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,256,256)) { ++ DWC_ERROR("`%d' invalid for parameter `dma_burst_size'\n", ++ dwc_otg_module_params.dma_burst_size); ++ dwc_otg_module_params.dma_burst_size = 32; ++ retval++; ++ } ++ ++ { ++ uint8_t brst_sz = 0; ++ while(dwc_otg_module_params.dma_burst_size > 1) { ++ brst_sz ++; ++ dwc_otg_module_params.dma_burst_size >>= 1; ++ } ++ dwc_otg_module_params.dma_burst_size = brst_sz; ++ } ++ } ++ ++ if (dwc_otg_module_params.phy_utmi_width != -1) { ++ if (DWC_OTG_PARAM_TEST(phy_utmi_width, 8, 8) && ++ DWC_OTG_PARAM_TEST(phy_utmi_width, 16, 16)) { ++ DWC_ERROR("`%d' invalid for parameter `phy_utmi_width'\n", ++ dwc_otg_module_params.phy_utmi_width); ++ dwc_otg_module_params.phy_utmi_width = 16; ++ retval++; ++ } ++ } ++ ++ for (i = 0; i < 15; i++) { ++ /** @todo should be like above */ ++ //DWC_OTG_PARAM_ERR(dev_perio_tx_fifo_size[i], 4, 768, "dev_perio_tx_fifo_size"); ++ if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] != -1) { ++ if (DWC_OTG_PARAM_TEST(dev_perio_tx_fifo_size[i], 4, 768)) { ++ DWC_ERROR("`%d' invalid for parameter `%s_%d'\n", ++ dwc_otg_module_params.dev_perio_tx_fifo_size[i], "dev_perio_tx_fifo_size", i); ++ dwc_otg_module_params.dev_perio_tx_fifo_size[i] = dwc_param_dev_perio_tx_fifo_size_default; ++ retval++; ++ } ++ } ++ } ++ ++ DWC_OTG_PARAM_ERR(en_multiple_tx_fifo, 0, 1, "en_multiple_tx_fifo"); ++ ++ for (i = 0; i < 15; i++) { ++ /** @todo should be like above */ ++ //DWC_OTG_PARAM_ERR(dev_tx_fifo_size[i], 4, 768, "dev_tx_fifo_size"); ++ if (dwc_otg_module_params.dev_tx_fifo_size[i] != -1) { ++ if (DWC_OTG_PARAM_TEST(dev_tx_fifo_size[i], 4, 768)) { ++ DWC_ERROR("`%d' invalid for parameter `%s_%d'\n", ++ dwc_otg_module_params.dev_tx_fifo_size[i], "dev_tx_fifo_size", i); ++ dwc_otg_module_params.dev_tx_fifo_size[i] = dwc_param_dev_tx_fifo_size_default; ++ retval++; ++ } ++ } ++ } ++ ++ DWC_OTG_PARAM_ERR(thr_ctl, 0, 7, "thr_ctl"); ++ DWC_OTG_PARAM_ERR(tx_thr_length, 8, 128, "tx_thr_length"); ++ DWC_OTG_PARAM_ERR(rx_thr_length, 8, 128, "rx_thr_length"); ++ ++ DWC_OTG_PARAM_ERR(pti_enable,0,1,"pti_enable"); ++ DWC_OTG_PARAM_ERR(mpi_enable,0,1,"mpi_enable"); ++ ++ /* At this point, all module parameters that have been set by the user ++ * are valid, and those that have not are left unset. Now set their ++ * default values and/or check the parameters against the hardware ++ * configurations of the OTG core. */ ++ ++/* This sets the parameter to the default value if it has not been set by the ++ * user */ ++#define DWC_OTG_PARAM_SET_DEFAULT(_param_) \ ++ ({ \ ++ int changed = 1; \ ++ if (dwc_otg_module_params._param_ == -1) { \ ++ changed = 0; \ ++ dwc_otg_module_params._param_ = dwc_param_##_param_##_default; \ ++ } \ ++ changed; \ ++ }) ++ ++/* This checks the macro agains the hardware configuration to see if it is ++ * valid. It is possible that the default value could be invalid. In this ++ * case, it will report a module error if the user touched the parameter. ++ * Otherwise it will adjust the value without any error. */ ++#define DWC_OTG_PARAM_CHECK_VALID(_param_, _str_, _is_valid_, _set_valid_) \ ++ ({ \ ++ int changed = DWC_OTG_PARAM_SET_DEFAULT(_param_); \ ++ int error = 0; \ ++ if (!(_is_valid_)) { \ ++ if (changed) { \ ++ DWC_ERROR("`%d' invalid for parameter `%s'. Check HW configuration.\n", dwc_otg_module_params._param_, _str_); \ ++ error = 1; \ ++ } \ ++ dwc_otg_module_params._param_ = (_set_valid_); \ ++ } \ ++ error; \ ++ }) ++ ++ /* OTG Cap */ ++ retval += DWC_OTG_PARAM_CHECK_VALID(otg_cap, "otg_cap", ++ ({ ++ int valid; ++ valid = 1; ++ switch (dwc_otg_module_params.otg_cap) { ++ case DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE: ++ if (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) ++ valid = 0; ++ break; ++ case DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE: ++ if ((core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) && ++ (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) && ++ (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) && ++ (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) { ++ valid = 0; ++ } ++ break; ++ case DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE: ++ /* always valid */ ++ break; ++ } ++ valid; ++ }), ++ (((core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) || ++ (core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) || ++ (core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) || ++ (core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) ? ++ DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE : ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(dma_enable, "dma_enable", ++ ((dwc_otg_module_params.dma_enable == 1) && (core_if->hwcfg2.b.architecture == 0)) ? 0 : 1, ++ 0); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(dma_desc_enable, "dma_desc_enable", ++ ((dwc_otg_module_params.dma_desc_enable == 1) && ++ ((dwc_otg_module_params.dma_enable == 0) || (core_if->hwcfg4.b.desc_dma == 0))) ? 0 : 1, ++ 0); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(opt, "opt", 1, 0); ++ ++ DWC_OTG_PARAM_SET_DEFAULT(dma_burst_size); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_support_fs_ls_low_power, ++ "host_support_fs_ls_low_power", ++ 1, 0); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(enable_dynamic_fifo, ++ "enable_dynamic_fifo", ++ ((dwc_otg_module_params.enable_dynamic_fifo == 0) || ++ (core_if->hwcfg2.b.dynamic_fifo == 1)), 0); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(data_fifo_size, ++ "data_fifo_size", ++ (dwc_otg_module_params.data_fifo_size <= core_if->hwcfg3.b.dfifo_depth), ++ core_if->hwcfg3.b.dfifo_depth); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(dev_rx_fifo_size, ++ "dev_rx_fifo_size", ++ (dwc_otg_module_params.dev_rx_fifo_size <= dwc_read_reg32(&core_if->core_global_regs->grxfsiz)), ++ dwc_read_reg32(&core_if->core_global_regs->grxfsiz)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(dev_nperio_tx_fifo_size, ++ "dev_nperio_tx_fifo_size", ++ (dwc_otg_module_params.dev_nperio_tx_fifo_size <= (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)), ++ (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_rx_fifo_size, ++ "host_rx_fifo_size", ++ (dwc_otg_module_params.host_rx_fifo_size <= dwc_read_reg32(&core_if->core_global_regs->grxfsiz)), ++ dwc_read_reg32(&core_if->core_global_regs->grxfsiz)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_nperio_tx_fifo_size, ++ "host_nperio_tx_fifo_size", ++ (dwc_otg_module_params.host_nperio_tx_fifo_size <= (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)), ++ (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_perio_tx_fifo_size, ++ "host_perio_tx_fifo_size", ++ (dwc_otg_module_params.host_perio_tx_fifo_size <= ((dwc_read_reg32(&core_if->core_global_regs->hptxfsiz) >> 16))), ++ ((dwc_read_reg32(&core_if->core_global_regs->hptxfsiz) >> 16))); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(max_transfer_size, ++ "max_transfer_size", ++ (dwc_otg_module_params.max_transfer_size < (1 << (core_if->hwcfg3.b.xfer_size_cntr_width + 11))), ++ ((1 << (core_if->hwcfg3.b.xfer_size_cntr_width + 11)) - 1)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(max_packet_count, ++ "max_packet_count", ++ (dwc_otg_module_params.max_packet_count < (1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4))), ++ ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4)) - 1)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_channels, ++ "host_channels", ++ (dwc_otg_module_params.host_channels <= (core_if->hwcfg2.b.num_host_chan + 1)), ++ (core_if->hwcfg2.b.num_host_chan + 1)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(dev_endpoints, ++ "dev_endpoints", ++ (dwc_otg_module_params.dev_endpoints <= (core_if->hwcfg2.b.num_dev_ep)), ++ core_if->hwcfg2.b.num_dev_ep); ++ ++/* ++ * Define the following to disable the FS PHY Hardware checking. This is for ++ * internal testing only. ++ * ++ * #define NO_FS_PHY_HW_CHECKS ++ */ ++ ++#ifdef NO_FS_PHY_HW_CHECKS ++ retval += DWC_OTG_PARAM_CHECK_VALID(phy_type, ++ "phy_type", 1, 0); ++#else ++ retval += DWC_OTG_PARAM_CHECK_VALID(phy_type, ++ "phy_type", ++ ({ ++ int valid = 0; ++ if ((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_UTMI) && ++ ((core_if->hwcfg2.b.hs_phy_type == 1) || ++ (core_if->hwcfg2.b.hs_phy_type == 3))) { ++ valid = 1; ++ } ++ else if ((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_ULPI) && ++ ((core_if->hwcfg2.b.hs_phy_type == 2) || ++ (core_if->hwcfg2.b.hs_phy_type == 3))) { ++ valid = 1; ++ } ++ else if ((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) && ++ (core_if->hwcfg2.b.fs_phy_type == 1)) { ++ valid = 1; ++ } ++ valid; ++ }), ++ ({ ++ int set = DWC_PHY_TYPE_PARAM_FS; ++ if (core_if->hwcfg2.b.hs_phy_type) { ++ if ((core_if->hwcfg2.b.hs_phy_type == 3) || ++ (core_if->hwcfg2.b.hs_phy_type == 1)) { ++ set = DWC_PHY_TYPE_PARAM_UTMI; ++ } ++ else { ++ set = DWC_PHY_TYPE_PARAM_ULPI; ++ } ++ } ++ set; ++ })); ++#endif ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(speed, "speed", ++ (dwc_otg_module_params.speed == 0) && (dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) ? 0 : 1, ++ dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS ? 1 : 0); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_ls_low_power_phy_clk, ++ "host_ls_low_power_phy_clk", ++ ((dwc_otg_module_params.host_ls_low_power_phy_clk == DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ) && (dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) ? 0 : 1), ++ ((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) ? DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ : DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ)); ++ ++ DWC_OTG_PARAM_SET_DEFAULT(phy_ulpi_ddr); ++ DWC_OTG_PARAM_SET_DEFAULT(phy_ulpi_ext_vbus); ++ DWC_OTG_PARAM_SET_DEFAULT(phy_utmi_width); ++ DWC_OTG_PARAM_SET_DEFAULT(ulpi_fs_ls); ++ DWC_OTG_PARAM_SET_DEFAULT(ts_dline); ++ ++#ifdef NO_FS_PHY_HW_CHECKS ++ retval += DWC_OTG_PARAM_CHECK_VALID(i2c_enable, "i2c_enable", 1, 0); ++#else ++ retval += DWC_OTG_PARAM_CHECK_VALID(i2c_enable, ++ "i2c_enable", ++ (dwc_otg_module_params.i2c_enable == 1) && (core_if->hwcfg3.b.i2c == 0) ? 0 : 1, ++ 0); ++#endif ++ ++ for (i = 0; i < 15; i++) { ++ int changed = 1; ++ int error = 0; ++ ++ if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] == -1) { ++ changed = 0; ++ dwc_otg_module_params.dev_perio_tx_fifo_size[i] = dwc_param_dev_perio_tx_fifo_size_default; ++ } ++ if (!(dwc_otg_module_params.dev_perio_tx_fifo_size[i] <= (dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i])))) { ++ if (changed) { ++ DWC_ERROR("`%d' invalid for parameter `dev_perio_fifo_size_%d'. Check HW configuration.\n", dwc_otg_module_params.dev_perio_tx_fifo_size[i], i); ++ error = 1; ++ } ++ dwc_otg_module_params.dev_perio_tx_fifo_size[i] = dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i]); ++ } ++ retval += error; ++ } ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(en_multiple_tx_fifo, "en_multiple_tx_fifo", ++ ((dwc_otg_module_params.en_multiple_tx_fifo == 1) && (core_if->hwcfg4.b.ded_fifo_en == 0)) ? 0 : 1, ++ 0); ++ ++ for (i = 0; i < 15; i++) { ++ int changed = 1; ++ int error = 0; ++ ++ if (dwc_otg_module_params.dev_tx_fifo_size[i] == -1) { ++ changed = 0; ++ dwc_otg_module_params.dev_tx_fifo_size[i] = dwc_param_dev_tx_fifo_size_default; ++ } ++ if (!(dwc_otg_module_params.dev_tx_fifo_size[i] <= (dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i])))) { ++ if (changed) { ++ DWC_ERROR("%d' invalid for parameter `dev_perio_fifo_size_%d'. Check HW configuration.\n", dwc_otg_module_params.dev_tx_fifo_size[i], i); ++ error = 1; ++ } ++ dwc_otg_module_params.dev_tx_fifo_size[i] = dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i]); ++ } ++ retval += error; ++ } ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(thr_ctl, "thr_ctl", ++ ((dwc_otg_module_params.thr_ctl != 0) && ((dwc_otg_module_params.dma_enable == 0) || (core_if->hwcfg4.b.ded_fifo_en == 0))) ? 0 : 1, ++ 0); ++ ++ DWC_OTG_PARAM_SET_DEFAULT(tx_thr_length); ++ DWC_OTG_PARAM_SET_DEFAULT(rx_thr_length); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(pti_enable, "pti_enable", ++ ((dwc_otg_module_params.pti_enable == 0) || ((dwc_otg_module_params.pti_enable == 1) && (core_if->snpsid >= 0x4F54272A))) ? 1 : 0, ++ 0); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(mpi_enable, "mpi_enable", ++ ((dwc_otg_module_params.mpi_enable == 0) || ((dwc_otg_module_params.mpi_enable == 1) && (core_if->hwcfg2.b.multi_proc_int == 1))) ? 1 : 0, ++ 0); ++ return retval; ++} ++ ++/** ++ * This function is the top level interrupt handler for the Common ++ * (Device and host modes) interrupts. ++ */ ++static irqreturn_t dwc_otg_common_irq(int irq, void *dev ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ , struct pt_regs *r ++#endif ++ ) ++{ ++ dwc_otg_device_t *otg_dev = dev; ++ int32_t retval = IRQ_NONE; ++ ++ retval = dwc_otg_handle_common_intr(otg_dev->core_if); ++ return IRQ_RETVAL(retval); ++} ++ ++/** ++ * This function is called when a platform_device is unregistered with the ++ * dwc_otg_driver. This happens, for example, when the rmmod command is ++ * executed. The device may or may not be electrically present. If it is ++ * present, the driver stops device processing. Any resources used on behalf ++ * of this device are freed. ++ * ++ * @param[in] pdev ++ */ ++static int dwc_otg_driver_remove(struct platform_device *pdev) ++{ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(pdev); ++ DWC_DEBUGPL(DBG_ANY, "%s(%p)\n", __func__, pdev); ++ ++ if (!otg_dev) { ++ /* Memory allocation for the dwc_otg_device failed. */ ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__); ++ return 0; ++ } ++ ++ /* ++ * Free the IRQ ++ */ ++ if (otg_dev->common_irq_installed) { ++ free_irq(otg_dev->irq, otg_dev); ++ } ++ ++#ifndef DWC_DEVICE_ONLY ++ if (otg_dev->hcd) { ++ dwc_otg_hcd_remove(&pdev->dev); ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__); ++ return 0; ++ } ++#endif ++ ++#ifndef DWC_HOST_ONLY ++ if (otg_dev->pcd) { ++ dwc_otg_pcd_remove(&pdev->dev); ++ } ++#endif ++ if (otg_dev->core_if) { ++ dwc_otg_cil_remove(otg_dev->core_if); ++ } ++ ++ /* ++ * Remove the device attributes ++ */ ++ dwc_otg_attr_remove(otg_dev->parent); ++ ++ /* Disable USB port */ ++ dwc_write_reg32((uint32_t *)((uint8_t *)otg_dev->base + 0xe00), 0xf); ++ ++ /* ++ * Return the memory. ++ */ ++ if (otg_dev->base) { ++ iounmap(otg_dev->base); ++ } ++ ++ if (otg_dev->phys_addr != 0) { ++ release_mem_region(otg_dev->phys_addr, otg_dev->base_len); ++ } ++ ++ kfree(otg_dev); ++ ++ /* ++ * Clear the drvdata pointer. ++ */ ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++/** ++ * This function is called when an platform_device is bound to a ++ * dwc_otg_driver. It creates the driver components required to ++ * control the device (CIL, HCD, and PCD) and it initializes the ++ * device. The driver components are stored in a dwc_otg_device ++ * structure. A reference to the dwc_otg_device is saved in the ++ * platform_device. This allows the driver to access the dwc_otg_device ++ * structure on subsequent calls to driver methods for this device. ++ * ++ * @param[in] pdev platform_device definition ++ */ ++static int dwc_otg_driver_probe(struct platform_device *pdev) ++{ ++ int retval = 0; ++ uint32_t snpsid; ++ dwc_otg_device_t *otg_dev; ++ struct resource *res; ++ ++ dev_dbg(&pdev->dev, "dwc_otg_driver_probe(%p)\n", pdev); ++ ++ otg_dev= kzalloc(sizeof(dwc_otg_device_t), GFP_KERNEL); ++ if (!otg_dev) { ++ dev_err(&pdev->dev, "kmalloc of dwc_otg_device failed\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ otg_dev->reg_offset = 0xFFFFFFFF; ++ ++ /* ++ * Retrieve the memory and IRQ resources. ++ */ ++ otg_dev->irq = platform_get_irq(pdev, 0); ++ if (otg_dev->irq <= 0) { ++ dev_err(&pdev->dev, "no device irq\n"); ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ dev_err(&pdev->dev, "no CSR address\n"); ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ otg_dev->parent = &pdev->dev; ++ otg_dev->phys_addr = res->start; ++ otg_dev->base_len = res->end - res->start + 1; ++ if (request_mem_region(otg_dev->phys_addr, ++ otg_dev->base_len, ++ dwc_driver_name) == NULL) { ++ dev_err(&pdev->dev, "request_mem_region failed\n"); ++ retval = -EBUSY; ++ goto fail; ++ } ++ ++ /* ++ * Map the DWC_otg Core memory into virtual address space. ++ */ ++ otg_dev->base = ioremap(otg_dev->phys_addr, otg_dev->base_len); ++ if (!otg_dev->base) { ++ dev_err(&pdev->dev, "ioremap() failed\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ dev_dbg(&pdev->dev, "mapped base=0x%08x\n", (unsigned) otg_dev->base); ++ ++ /* Enable USB Port */ ++ dwc_write_reg32((uint32_t *)((uint8_t *)otg_dev->base + 0xe00), 0); ++ ++ /* ++ * Attempt to ensure this device is really a DWC_otg Controller. ++ * Read and verify the SNPSID register contents. The value should be ++ * 0x45F42XXX, which corresponds to "OT2", as in "OTG version 2.XX". ++ */ ++ snpsid = dwc_read_reg32((uint32_t *)((uint8_t *)otg_dev->base + 0x40)); ++ ++ if ((snpsid & 0xFFFFF000) != OTG_CORE_REV_2_00) { ++ dev_err(&pdev->dev, "Bad value for SNPSID: 0x%08x\n", snpsid); ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ DWC_PRINT("Core Release: %x.%x%x%x\n", ++ (snpsid >> 12 & 0xF), ++ (snpsid >> 8 & 0xF), ++ (snpsid >> 4 & 0xF), ++ (snpsid & 0xF)); ++ ++ /* ++ * Initialize driver data to point to the global DWC_otg ++ * Device structure. ++ */ ++ platform_set_drvdata(pdev, otg_dev); ++ dev_dbg(&pdev->dev, "dwc_otg_device=0x%p\n", otg_dev); ++ ++ ++ otg_dev->core_if = dwc_otg_cil_init(otg_dev->base, ++ &dwc_otg_module_params); ++ ++ otg_dev->core_if->snpsid = snpsid; ++ ++ if (!otg_dev->core_if) { ++ dev_err(&pdev->dev, "CIL initialization failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ /* ++ * Validate parameter values. ++ */ ++ if (check_parameters(otg_dev->core_if)) { ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ /* ++ * Create Device Attributes in sysfs ++ */ ++ dwc_otg_attr_create(&pdev->dev); ++ ++ /* ++ * Disable the global interrupt until all the interrupt ++ * handlers are installed. ++ */ ++ dwc_otg_disable_global_interrupts(otg_dev->core_if); ++ ++ /* ++ * Install the interrupt handler for the common interrupts before ++ * enabling common interrupts in core_init below. ++ */ ++ DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n", ++ otg_dev->irq); ++ retval = request_irq(otg_dev->irq, dwc_otg_common_irq, ++ IRQF_SHARED, "dwc_otg", otg_dev); ++ if (retval) { ++ DWC_ERROR("request of irq%d failed\n", otg_dev->irq); ++ retval = -EBUSY; ++ goto fail; ++ } else { ++ otg_dev->common_irq_installed = 1; ++ } ++ ++ /* ++ * Initialize the DWC_otg core. ++ */ ++ dwc_otg_core_init(otg_dev->core_if); ++ ++#ifndef DWC_HOST_ONLY ++ /* ++ * Initialize the PCD ++ */ ++ retval = dwc_otg_pcd_init(&pdev->dev); ++ if (retval != 0) { ++ DWC_ERROR("dwc_otg_pcd_init failed\n"); ++ otg_dev->pcd = NULL; ++ goto fail; ++ } ++#endif ++#ifndef DWC_DEVICE_ONLY ++ /* ++ * Initialize the HCD ++ */ ++ retval = dwc_otg_hcd_init(&pdev->dev); ++ if (retval != 0) { ++ DWC_ERROR("dwc_otg_hcd_init failed\n"); ++ otg_dev->hcd = NULL; ++ goto fail; ++ } ++#endif ++ ++ /* ++ * Enable the global interrupt after all the interrupt ++ * handlers are installed. ++ */ ++ dwc_otg_enable_global_interrupts(otg_dev->core_if); ++ ++ return 0; ++ ++ fail: ++ dwc_otg_driver_remove(pdev); ++ return retval; ++} ++ ++/** ++ * This structure defines the methods to be called by a bus driver ++ * during the lifecycle of a device on that bus. Both drivers and ++ * devices are registered with a bus driver. The bus driver matches ++ * devices to drivers based on information in the device and driver ++ * structures. ++ * ++ * The probe function is called when the bus driver matches a device ++ * to this driver. The remove function is called when a device is ++ * unregistered with the bus driver. ++ */ ++ ++static const struct of_device_id ralink_otg_match[] = { ++ { .compatible = "ralink,rt3050-otg" }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, ralink_otg_match); ++ ++static struct platform_driver dwc_otg_driver = { ++ .driver = { ++ .name = (char *)dwc_driver_name, ++ .of_match_table = ralink_otg_match, ++ }, ++ .probe = dwc_otg_driver_probe, ++ .remove = dwc_otg_driver_remove, ++}; ++ ++/** ++ * This function is called when the dwc_otg_driver is installed with the ++ * insmod command. It registers the dwc_otg_driver structure with the ++ * appropriate bus driver. This will cause the dwc_otg_driver_probe function ++ * to be called. In addition, the bus driver will automatically expose ++ * attributes defined for the device and driver in the special sysfs file ++ * system. ++ * ++ * @return ++ */ ++static int __init dwc_otg_driver_init(void) ++{ ++ int retval = 0; ++ int error; ++ ++ printk(KERN_INFO "%s: version %s\n", dwc_driver_name, DWC_DRIVER_VERSION); ++ ++ retval = platform_driver_register(&dwc_otg_driver); ++ if (retval) { ++ printk(KERN_ERR "%s retval=%d\n", __func__, retval); ++ return retval; ++ } ++ ++ error = driver_create_file(&dwc_otg_driver.driver, &driver_attr_version); ++ error = driver_create_file(&dwc_otg_driver.driver, &driver_attr_debuglevel); ++ ++ return retval; ++} ++module_init(dwc_otg_driver_init); ++ ++/** ++ * This function is called when the driver is removed from the kernel ++ * with the rmmod command. The driver unregisters itself with its bus ++ * driver. ++ * ++ */ ++static void __exit dwc_otg_driver_cleanup(void) ++{ ++ printk(KERN_DEBUG "dwc_otg_driver_cleanup()\n"); ++ ++ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_debuglevel); ++ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_version); ++ ++ platform_driver_unregister(&dwc_otg_driver); ++ ++ printk(KERN_INFO "%s module removed\n", dwc_driver_name); ++} ++module_exit(dwc_otg_driver_cleanup); ++ ++MODULE_DESCRIPTION(DWC_DRIVER_DESC); ++MODULE_AUTHOR("Synopsys Inc."); ++MODULE_LICENSE("GPL"); ++ ++module_param_named(otg_cap, dwc_otg_module_params.otg_cap, int, 0444); ++MODULE_PARM_DESC(otg_cap, "OTG Capabilities 0=HNP&SRP 1=SRP Only 2=None"); ++module_param_named(opt, dwc_otg_module_params.opt, int, 0444); ++MODULE_PARM_DESC(opt, "OPT Mode"); ++module_param_named(dma_enable, dwc_otg_module_params.dma_enable, int, 0444); ++MODULE_PARM_DESC(dma_enable, "DMA Mode 0=Slave 1=DMA enabled"); ++ ++module_param_named(dma_desc_enable, dwc_otg_module_params.dma_desc_enable, int, 0444); ++MODULE_PARM_DESC(dma_desc_enable, "DMA Desc Mode 0=Address DMA 1=DMA Descriptor enabled"); ++ ++module_param_named(dma_burst_size, dwc_otg_module_params.dma_burst_size, int, 0444); ++MODULE_PARM_DESC(dma_burst_size, "DMA Burst Size 1, 4, 8, 16, 32, 64, 128, 256"); ++module_param_named(speed, dwc_otg_module_params.speed, int, 0444); ++MODULE_PARM_DESC(speed, "Speed 0=High Speed 1=Full Speed"); ++module_param_named(host_support_fs_ls_low_power, dwc_otg_module_params.host_support_fs_ls_low_power, int, 0444); ++MODULE_PARM_DESC(host_support_fs_ls_low_power, "Support Low Power w/FS or LS 0=Support 1=Don't Support"); ++module_param_named(host_ls_low_power_phy_clk, dwc_otg_module_params.host_ls_low_power_phy_clk, int, 0444); ++MODULE_PARM_DESC(host_ls_low_power_phy_clk, "Low Speed Low Power Clock 0=48Mhz 1=6Mhz"); ++module_param_named(enable_dynamic_fifo, dwc_otg_module_params.enable_dynamic_fifo, int, 0444); ++MODULE_PARM_DESC(enable_dynamic_fifo, "0=cC Setting 1=Allow Dynamic Sizing"); ++module_param_named(data_fifo_size, dwc_otg_module_params.data_fifo_size, int, 0444); ++MODULE_PARM_DESC(data_fifo_size, "Total number of words in the data FIFO memory 32-32768"); ++module_param_named(dev_rx_fifo_size, dwc_otg_module_params.dev_rx_fifo_size, int, 0444); ++MODULE_PARM_DESC(dev_rx_fifo_size, "Number of words in the Rx FIFO 16-32768"); ++module_param_named(dev_nperio_tx_fifo_size, dwc_otg_module_params.dev_nperio_tx_fifo_size, int, 0444); ++MODULE_PARM_DESC(dev_nperio_tx_fifo_size, "Number of words in the non-periodic Tx FIFO 16-32768"); ++module_param_named(dev_perio_tx_fifo_size_1, dwc_otg_module_params.dev_perio_tx_fifo_size[0], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_1, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_2, dwc_otg_module_params.dev_perio_tx_fifo_size[1], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_2, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_3, dwc_otg_module_params.dev_perio_tx_fifo_size[2], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_3, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_4, dwc_otg_module_params.dev_perio_tx_fifo_size[3], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_4, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_5, dwc_otg_module_params.dev_perio_tx_fifo_size[4], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_5, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_6, dwc_otg_module_params.dev_perio_tx_fifo_size[5], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_6, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_7, dwc_otg_module_params.dev_perio_tx_fifo_size[6], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_7, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_8, dwc_otg_module_params.dev_perio_tx_fifo_size[7], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_8, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_9, dwc_otg_module_params.dev_perio_tx_fifo_size[8], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_9, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_10, dwc_otg_module_params.dev_perio_tx_fifo_size[9], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_10, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_11, dwc_otg_module_params.dev_perio_tx_fifo_size[10], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_11, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_12, dwc_otg_module_params.dev_perio_tx_fifo_size[11], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_12, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_13, dwc_otg_module_params.dev_perio_tx_fifo_size[12], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_13, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_14, dwc_otg_module_params.dev_perio_tx_fifo_size[13], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_14, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_15, dwc_otg_module_params.dev_perio_tx_fifo_size[14], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_15, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(host_rx_fifo_size, dwc_otg_module_params.host_rx_fifo_size, int, 0444); ++MODULE_PARM_DESC(host_rx_fifo_size, "Number of words in the Rx FIFO 16-32768"); ++module_param_named(host_nperio_tx_fifo_size, dwc_otg_module_params.host_nperio_tx_fifo_size, int, 0444); ++MODULE_PARM_DESC(host_nperio_tx_fifo_size, "Number of words in the non-periodic Tx FIFO 16-32768"); ++module_param_named(host_perio_tx_fifo_size, dwc_otg_module_params.host_perio_tx_fifo_size, int, 0444); ++MODULE_PARM_DESC(host_perio_tx_fifo_size, "Number of words in the host periodic Tx FIFO 16-32768"); ++module_param_named(max_transfer_size, dwc_otg_module_params.max_transfer_size, int, 0444); ++/** @todo Set the max to 512K, modify checks */ ++MODULE_PARM_DESC(max_transfer_size, "The maximum transfer size supported in bytes 2047-65535"); ++module_param_named(max_packet_count, dwc_otg_module_params.max_packet_count, int, 0444); ++MODULE_PARM_DESC(max_packet_count, "The maximum number of packets in a transfer 15-511"); ++module_param_named(host_channels, dwc_otg_module_params.host_channels, int, 0444); ++MODULE_PARM_DESC(host_channels, "The number of host channel registers to use 1-16"); ++module_param_named(dev_endpoints, dwc_otg_module_params.dev_endpoints, int, 0444); ++MODULE_PARM_DESC(dev_endpoints, "The number of endpoints in addition to EP0 available for device mode 1-15"); ++module_param_named(phy_type, dwc_otg_module_params.phy_type, int, 0444); ++MODULE_PARM_DESC(phy_type, "0=Reserved 1=UTMI+ 2=ULPI"); ++module_param_named(phy_utmi_width, dwc_otg_module_params.phy_utmi_width, int, 0444); ++MODULE_PARM_DESC(phy_utmi_width, "Specifies the UTMI+ Data Width 8 or 16 bits"); ++module_param_named(phy_ulpi_ddr, dwc_otg_module_params.phy_ulpi_ddr, int, 0444); ++MODULE_PARM_DESC(phy_ulpi_ddr, "ULPI at double or single data rate 0=Single 1=Double"); ++module_param_named(phy_ulpi_ext_vbus, dwc_otg_module_params.phy_ulpi_ext_vbus, int, 0444); ++MODULE_PARM_DESC(phy_ulpi_ext_vbus, "ULPI PHY using internal or external vbus 0=Internal"); ++module_param_named(i2c_enable, dwc_otg_module_params.i2c_enable, int, 0444); ++MODULE_PARM_DESC(i2c_enable, "FS PHY Interface"); ++module_param_named(ulpi_fs_ls, dwc_otg_module_params.ulpi_fs_ls, int, 0444); ++MODULE_PARM_DESC(ulpi_fs_ls, "ULPI PHY FS/LS mode only"); ++module_param_named(ts_dline, dwc_otg_module_params.ts_dline, int, 0444); ++MODULE_PARM_DESC(ts_dline, "Term select Dline pulsing for all PHYs"); ++module_param_named(debug, g_dbg_lvl, int, 0444); ++MODULE_PARM_DESC(debug, ""); ++ ++module_param_named(en_multiple_tx_fifo, dwc_otg_module_params.en_multiple_tx_fifo, int, 0444); ++MODULE_PARM_DESC(en_multiple_tx_fifo, "Dedicated Non Periodic Tx FIFOs 0=disabled 1=enabled"); ++module_param_named(dev_tx_fifo_size_1, dwc_otg_module_params.dev_tx_fifo_size[0], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_1, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_2, dwc_otg_module_params.dev_tx_fifo_size[1], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_2, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_3, dwc_otg_module_params.dev_tx_fifo_size[2], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_3, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_4, dwc_otg_module_params.dev_tx_fifo_size[3], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_4, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_5, dwc_otg_module_params.dev_tx_fifo_size[4], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_5, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_6, dwc_otg_module_params.dev_tx_fifo_size[5], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_6, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_7, dwc_otg_module_params.dev_tx_fifo_size[6], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_7, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_8, dwc_otg_module_params.dev_tx_fifo_size[7], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_8, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_9, dwc_otg_module_params.dev_tx_fifo_size[8], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_9, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_10, dwc_otg_module_params.dev_tx_fifo_size[9], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_10, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_11, dwc_otg_module_params.dev_tx_fifo_size[10], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_11, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_12, dwc_otg_module_params.dev_tx_fifo_size[11], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_12, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_13, dwc_otg_module_params.dev_tx_fifo_size[12], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_13, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_14, dwc_otg_module_params.dev_tx_fifo_size[13], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_14, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_15, dwc_otg_module_params.dev_tx_fifo_size[14], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_15, "Number of words in the Tx FIFO 4-768"); ++ ++module_param_named(thr_ctl, dwc_otg_module_params.thr_ctl, int, 0444); ++MODULE_PARM_DESC(thr_ctl, "Thresholding enable flag bit 0 - non ISO Tx thr., 1 - ISO Tx thr., 2 - Rx thr.- bit 0=disabled 1=enabled"); ++module_param_named(tx_thr_length, dwc_otg_module_params.tx_thr_length, int, 0444); ++MODULE_PARM_DESC(tx_thr_length, "Tx Threshold length in 32 bit DWORDs"); ++module_param_named(rx_thr_length, dwc_otg_module_params.rx_thr_length, int, 0444); ++MODULE_PARM_DESC(rx_thr_length, "Rx Threshold length in 32 bit DWORDs"); ++ ++module_param_named(pti_enable, dwc_otg_module_params.pti_enable, int, 0444); ++MODULE_PARM_DESC(pti_enable, "Per Transfer Interrupt mode 0=disabled 1=enabled"); ++ ++module_param_named(mpi_enable, dwc_otg_module_params.mpi_enable, int, 0444); ++MODULE_PARM_DESC(mpi_enable, "Multiprocessor Interrupt mode 0=disabled 1=enabled"); ++ ++/** @page "Module Parameters" ++ * ++ * The following parameters may be specified when starting the module. ++ * These parameters define how the DWC_otg controller should be ++ * configured. Parameter values are passed to the CIL initialization ++ * function dwc_otg_cil_init ++ * ++ * Example: modprobe dwc_otg speed=1 otg_cap=1 ++ * ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++*/ +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_driver.h +@@ -0,0 +1,83 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.h $ ++ * $Revision: 1.2 $ ++ * $Date: 2008-11-21 05:39:15 $ ++ * $Change: 1064918 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef __DWC_OTG_DRIVER_H__ ++#define __DWC_OTG_DRIVER_H__ ++ ++/** @file ++ * This file contains the interface to the Linux driver. ++ */ ++#include "dwc_otg_cil.h" ++ ++/* Type declarations */ ++struct dwc_otg_pcd; ++struct dwc_otg_hcd; ++ ++/** ++ * This structure is a wrapper that encapsulates the driver components used to ++ * manage a single DWC_otg controller. ++ */ ++typedef struct dwc_otg_device { ++ /** Base address returned from ioremap() */ ++ void *base; ++ ++ struct device *parent; ++ ++ /** Pointer to the core interface structure. */ ++ dwc_otg_core_if_t *core_if; ++ ++ /** Register offset for Diagnostic API. */ ++ uint32_t reg_offset; ++ ++ /** Pointer to the PCD structure. */ ++ struct dwc_otg_pcd *pcd; ++ ++ /** Pointer to the HCD structure. */ ++ struct dwc_otg_hcd *hcd; ++ ++ /** Flag to indicate whether the common IRQ handler is installed. */ ++ uint8_t common_irq_installed; ++ ++ /* Interrupt request number. */ ++ unsigned int irq; ++ ++ /* Physical address of Control and Status registers, used by ++ * release_mem_region(). ++ */ ++ resource_size_t phys_addr; ++ ++ /* Length of memory region, used by release_mem_region(). */ ++ unsigned long base_len; ++} dwc_otg_device_t; ++ ++#endif +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_hcd.c +@@ -0,0 +1,2852 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.c $ ++ * $Revision: 1.4 $ ++ * $Date: 2008-11-21 05:39:15 $ ++ * $Change: 1064940 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** ++ * @file ++ * ++ * This file contains the implementation of the HCD. In Linux, the HCD ++ * implements the hc_driver API. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dwc_otg_driver.h" ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++static const char dwc_otg_hcd_name[] = "dwc_otg"; ++ ++static const struct hc_driver dwc_otg_hc_driver = { ++ ++ .description = dwc_otg_hcd_name, ++ .product_desc = "DWC OTG Controller", ++ .hcd_priv_size = sizeof(dwc_otg_hcd_t), ++ ++ .irq = dwc_otg_hcd_irq, ++ ++ .flags = HCD_MEMORY | HCD_USB2, ++ ++ //.reset = ++ .start = dwc_otg_hcd_start, ++ //.suspend = ++ //.resume = ++ .stop = dwc_otg_hcd_stop, ++ ++ .urb_enqueue = dwc_otg_hcd_urb_enqueue, ++ .urb_dequeue = dwc_otg_hcd_urb_dequeue, ++ .endpoint_disable = dwc_otg_hcd_endpoint_disable, ++ ++ .get_frame_number = dwc_otg_hcd_get_frame_number, ++ ++ .hub_status_data = dwc_otg_hcd_hub_status_data, ++ .hub_control = dwc_otg_hcd_hub_control, ++ //.hub_suspend = ++ //.hub_resume = ++}; ++ ++/** ++ * Work queue function for starting the HCD when A-Cable is connected. ++ * The dwc_otg_hcd_start() must be called in a process context. ++ */ ++static void hcd_start_func( ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ void *_vp ++#else ++ struct work_struct *_work ++#endif ++ ) ++{ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ struct usb_hcd *usb_hcd = (struct usb_hcd *)_vp; ++#else ++ struct delayed_work *dw = container_of(_work, struct delayed_work, work); ++ struct dwc_otg_hcd *otg_hcd = container_of(dw, struct dwc_otg_hcd, start_work); ++ struct usb_hcd *usb_hcd = container_of((void *)otg_hcd, struct usb_hcd, hcd_priv); ++#endif ++ DWC_DEBUGPL(DBG_HCDV, "%s() %p\n", __func__, usb_hcd); ++ if (usb_hcd) { ++ dwc_otg_hcd_start(usb_hcd); ++ } ++} ++ ++/** ++ * HCD Callback function for starting the HCD when A-Cable is ++ * connected. ++ * ++ * @param p void pointer to the struct usb_hcd ++ */ ++static int32_t dwc_otg_hcd_start_cb(void *p) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(p); ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ hprt0_data_t hprt0; ++ ++ if (core_if->op_state == B_HOST) { ++ /* ++ * Reset the port. During a HNP mode switch the reset ++ * needs to occur within 1ms and have a duration of at ++ * least 50ms. ++ */ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtrst = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ ((struct usb_hcd *)p)->self.is_b_host = 1; ++ } else { ++ ((struct usb_hcd *)p)->self.is_b_host = 0; ++ } ++ ++ /* Need to start the HCD in a non-interrupt context. */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ INIT_WORK(&dwc_otg_hcd->start_work, hcd_start_func, p); ++// INIT_DELAYED_WORK(&dwc_otg_hcd->start_work, hcd_start_func, p); ++#else ++// INIT_WORK(&dwc_otg_hcd->start_work, hcd_start_func); ++ INIT_DELAYED_WORK(&dwc_otg_hcd->start_work, hcd_start_func); ++#endif ++// schedule_work(&dwc_otg_hcd->start_work); ++ queue_delayed_work(core_if->wq_otg, &dwc_otg_hcd->start_work, 50 * HZ / 1000); ++ ++ return 1; ++} ++ ++/** ++ * HCD Callback function for stopping the HCD. ++ * ++ * @param p void pointer to the struct usb_hcd ++ */ ++static int32_t dwc_otg_hcd_stop_cb(void *p) ++{ ++ struct usb_hcd *usb_hcd = (struct usb_hcd *)p; ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p); ++ dwc_otg_hcd_stop(usb_hcd); ++ return 1; ++} ++ ++static void del_xfer_timers(dwc_otg_hcd_t *hcd) ++{ ++#ifdef DEBUG ++ int i; ++ int num_channels = hcd->core_if->core_params->host_channels; ++ for (i = 0; i < num_channels; i++) { ++ del_timer(&hcd->core_if->hc_xfer_timer[i]); ++ } ++#endif ++} ++ ++static void del_timers(dwc_otg_hcd_t *hcd) ++{ ++ del_xfer_timers(hcd); ++ del_timer(&hcd->conn_timer); ++} ++ ++/** ++ * Processes all the URBs in a single list of QHs. Completes them with ++ * -ETIMEDOUT and frees the QTD. ++ */ ++static void kill_urbs_in_qh_list(dwc_otg_hcd_t *hcd, struct list_head *qh_list) ++{ ++ struct list_head *qh_item; ++ dwc_otg_qh_t *qh; ++ struct list_head *qtd_item; ++ dwc_otg_qtd_t *qtd; ++ ++ list_for_each(qh_item, qh_list) { ++ qh = list_entry(qh_item, dwc_otg_qh_t, qh_list_entry); ++ for (qtd_item = qh->qtd_list.next; ++ qtd_item != &qh->qtd_list; ++ qtd_item = qh->qtd_list.next) { ++ qtd = list_entry(qtd_item, dwc_otg_qtd_t, qtd_list_entry); ++ if (qtd->urb != NULL) { ++ dwc_otg_hcd_complete_urb(hcd, qtd->urb, ++ -ETIMEDOUT); ++ } ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd); ++ } ++ } ++} ++ ++/** ++ * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic ++ * and periodic schedules. The QTD associated with each URB is removed from ++ * the schedule and freed. This function may be called when a disconnect is ++ * detected or when the HCD is being stopped. ++ */ ++static void kill_all_urbs(dwc_otg_hcd_t *hcd) ++{ ++ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_inactive); ++ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_active); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_inactive); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_ready); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_assigned); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_queued); ++} ++ ++/** ++ * HCD Callback function for disconnect of the HCD. ++ * ++ * @param p void pointer to the struct usb_hcd ++ */ ++static int32_t dwc_otg_hcd_disconnect_cb(void *p) ++{ ++ gintsts_data_t intr; ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(p); ++ ++ //DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p); ++ ++ /* ++ * Set status flags for the hub driver. ++ */ ++ dwc_otg_hcd->flags.b.port_connect_status_change = 1; ++ dwc_otg_hcd->flags.b.port_connect_status = 0; ++ ++ /* ++ * Shutdown any transfers in process by clearing the Tx FIFO Empty ++ * interrupt mask and status bits and disabling subsequent host ++ * channel interrupts. ++ */ ++ intr.d32 = 0; ++ intr.b.nptxfempty = 1; ++ intr.b.ptxfempty = 1; ++ intr.b.hcintr = 1; ++ dwc_modify_reg32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, intr.d32, 0); ++ dwc_modify_reg32(&dwc_otg_hcd->core_if->core_global_regs->gintsts, intr.d32, 0); ++ ++ del_timers(dwc_otg_hcd); ++ ++ /* ++ * Turn off the vbus power only if the core has transitioned to device ++ * mode. If still in host mode, need to keep power on to detect a ++ * reconnection. ++ */ ++ if (dwc_otg_is_device_mode(dwc_otg_hcd->core_if)) { ++ if (dwc_otg_hcd->core_if->op_state != A_SUSPEND) { ++ hprt0_data_t hprt0 = { .d32=0 }; ++ DWC_PRINT("Disconnect: PortPower off\n"); ++ hprt0.b.prtpwr = 0; ++ dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0.d32); ++ } ++ ++ dwc_otg_disable_host_interrupts(dwc_otg_hcd->core_if); ++ } ++ ++ /* Respond with an error status to all URBs in the schedule. */ ++ kill_all_urbs(dwc_otg_hcd); ++ ++ if (dwc_otg_is_host_mode(dwc_otg_hcd->core_if)) { ++ /* Clean up any host channels that were in use. */ ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ dwc_otg_hc_regs_t *hc_regs; ++ hcchar_data_t hcchar; ++ ++ num_channels = dwc_otg_hcd->core_if->core_params->host_channels; ++ ++ if (!dwc_otg_hcd->core_if->dma_enable) { ++ /* Flush out any channel requests in slave mode. */ ++ for (i = 0; i < num_channels; i++) { ++ channel = dwc_otg_hcd->hc_ptr_array[i]; ++ if (list_empty(&channel->hc_list_entry)) { ++ hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[i]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ hcchar.b.chen = 0; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ } ++ } ++ } ++ } ++ ++ for (i = 0; i < num_channels; i++) { ++ channel = dwc_otg_hcd->hc_ptr_array[i]; ++ if (list_empty(&channel->hc_list_entry)) { ++ hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[i]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ /* Halt the channel. */ ++ hcchar.b.chdis = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ } ++ ++ dwc_otg_hc_cleanup(dwc_otg_hcd->core_if, channel); ++ list_add_tail(&channel->hc_list_entry, ++ &dwc_otg_hcd->free_hc_list); ++ } ++ } ++ } ++ ++ /* A disconnect will end the session so the B-Device is no ++ * longer a B-host. */ ++ ((struct usb_hcd *)p)->self.is_b_host = 0; ++ return 1; ++} ++ ++/** ++ * Connection timeout function. An OTG host is required to display a ++ * message if the device does not connect within 10 seconds. ++ */ ++void dwc_otg_hcd_connect_timeout(unsigned long ptr) ++{ ++ DWC_DEBUGPL(DBG_HCDV, "%s(%x)\n", __func__, (int)ptr); ++ DWC_PRINT("Connect Timeout\n"); ++ DWC_ERROR("Device Not Connected/Responding\n"); ++} ++ ++/** ++ * Start the connection timer. An OTG host is required to display a ++ * message if the device does not connect within 10 seconds. The ++ * timer is deleted if a port connect interrupt occurs before the ++ * timer expires. ++ */ ++static void dwc_otg_hcd_start_connect_timer(dwc_otg_hcd_t *hcd) ++{ ++ init_timer(&hcd->conn_timer); ++ hcd->conn_timer.function = dwc_otg_hcd_connect_timeout; ++ hcd->conn_timer.data = 0; ++ hcd->conn_timer.expires = jiffies + (HZ * 10); ++ add_timer(&hcd->conn_timer); ++} ++ ++/** ++ * HCD Callback function for disconnect of the HCD. ++ * ++ * @param p void pointer to the struct usb_hcd ++ */ ++static int32_t dwc_otg_hcd_session_start_cb(void *p) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(p); ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p); ++ dwc_otg_hcd_start_connect_timer(dwc_otg_hcd); ++ return 1; ++} ++ ++/** ++ * HCD Callback structure for handling mode switching. ++ */ ++static dwc_otg_cil_callbacks_t hcd_cil_callbacks = { ++ .start = dwc_otg_hcd_start_cb, ++ .stop = dwc_otg_hcd_stop_cb, ++ .disconnect = dwc_otg_hcd_disconnect_cb, ++ .session_start = dwc_otg_hcd_session_start_cb, ++ .p = 0, ++}; ++ ++/** ++ * Reset tasklet function ++ */ ++static void reset_tasklet_func(unsigned long data) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *)data; ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ hprt0_data_t hprt0; ++ ++ DWC_DEBUGPL(DBG_HCDV, "USB RESET tasklet called\n"); ++ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtrst = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ mdelay(60); ++ ++ hprt0.b.prtrst = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ dwc_otg_hcd->flags.b.port_reset_change = 1; ++} ++ ++static struct tasklet_struct reset_tasklet = { ++ .next = NULL, ++ .state = 0, ++ .count = ATOMIC_INIT(0), ++ .func = reset_tasklet_func, ++ .data = 0, ++}; ++ ++/** ++ * Initializes the HCD. This function allocates memory for and initializes the ++ * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the ++ * USB bus with the core and calls the hc_driver->start() function. It returns ++ * a negative error on failure. ++ */ ++int dwc_otg_hcd_init(struct device *dev) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(dev); ++ struct usb_hcd *hcd = NULL; ++ dwc_otg_hcd_t *dwc_otg_hcd = NULL; ++ ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ ++ int retval = 0; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT\n"); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ /* 2.6.20+ requires dev.dma_mask to be set prior to calling usb_create_hcd() */ ++ ++ /* Set device flags indicating whether the HCD supports DMA. */ ++ if (otg_dev->core_if->dma_enable) { ++ DWC_PRINT("Using DMA mode\n"); ++ dev->dma_mask = (void *)~0; ++ dev->coherent_dma_mask = ~0; ++ ++ if (otg_dev->core_if->dma_desc_enable) { ++ DWC_PRINT("Device using Descriptor DMA mode\n"); ++ } else { ++ DWC_PRINT("Device using Buffer DMA mode\n"); ++ } ++ } else { ++ DWC_PRINT("Using Slave mode\n"); ++ dev->dma_mask = (void *)0; ++ dev->coherent_dma_mask = 0; ++ } ++#endif ++ /* ++ * Allocate memory for the base HCD plus the DWC OTG HCD. ++ * Initialize the base HCD. ++ */ ++ hcd = usb_create_hcd(&dwc_otg_hc_driver, dev, dev_name(dev)); ++ if (!hcd) { ++ retval = -ENOMEM; ++ goto error1; ++ } ++ ++ dev_set_drvdata(dev, otg_dev); ++ hcd->regs = otg_dev->base; ++ hcd->rsrc_start = otg_dev->phys_addr; ++ hcd->rsrc_len = otg_dev->base_len; ++ hcd->self.otg_port = 1; ++ hcd->has_tt = 1; ++ ++ /* Initialize the DWC OTG HCD. */ ++ dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ dwc_otg_hcd->core_if = otg_dev->core_if; ++ otg_dev->hcd = dwc_otg_hcd; ++ ++ /* */ ++ spin_lock_init(&dwc_otg_hcd->lock); ++ ++ /* Register the HCD CIL Callbacks */ ++ dwc_otg_cil_register_hcd_callbacks(otg_dev->core_if, ++ &hcd_cil_callbacks, hcd); ++ ++ /* Initialize the non-periodic schedule. */ ++ INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_inactive); ++ INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_active); ++ ++ /* Initialize the periodic schedule. */ ++ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_inactive); ++ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_ready); ++ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_assigned); ++ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_queued); ++ ++ /* ++ * Create a host channel descriptor for each host channel implemented ++ * in the controller. Initialize the channel descriptor array. ++ */ ++ INIT_LIST_HEAD(&dwc_otg_hcd->free_hc_list); ++ num_channels = dwc_otg_hcd->core_if->core_params->host_channels; ++ memset(dwc_otg_hcd->hc_ptr_array, 0, sizeof(dwc_otg_hcd->hc_ptr_array)); ++ for (i = 0; i < num_channels; i++) { ++ channel = kmalloc(sizeof(dwc_hc_t), GFP_KERNEL); ++ if (channel == NULL) { ++ retval = -ENOMEM; ++ DWC_ERROR("%s: host channel allocation failed\n", __func__); ++ goto error2; ++ } ++ memset(channel, 0, sizeof(dwc_hc_t)); ++ channel->hc_num = i; ++ dwc_otg_hcd->hc_ptr_array[i] = channel; ++#ifdef DEBUG ++ init_timer(&dwc_otg_hcd->core_if->hc_xfer_timer[i]); ++#endif ++ DWC_DEBUGPL(DBG_HCDV, "HCD Added channel #%d, hc=%p\n", i, channel); ++ } ++ ++ /* Initialize the Connection timeout timer. */ ++ init_timer(&dwc_otg_hcd->conn_timer); ++ ++ /* Initialize reset tasklet. */ ++ reset_tasklet.data = (unsigned long) dwc_otg_hcd; ++ dwc_otg_hcd->reset_tasklet = &reset_tasklet; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ /* Set device flags indicating whether the HCD supports DMA. */ ++ if (otg_dev->core_if->dma_enable) { ++ DWC_PRINT("Using DMA mode\n"); ++ dev->dma_mask = (void *)~0; ++ dev->coherent_dma_mask = ~0; ++ ++ if (otg_dev->core_if->dma_desc_enable){ ++ DWC_PRINT("Device using Descriptor DMA mode\n"); ++ } else { ++ DWC_PRINT("Device using Buffer DMA mode\n"); ++ } ++ } else { ++ DWC_PRINT("Using Slave mode\n"); ++ dev->dma_mask = (void *)0; ++ dev->dev.coherent_dma_mask = 0; ++ } ++#endif ++ /* ++ * Finish generic HCD initialization and start the HCD. This function ++ * allocates the DMA buffer pool, registers the USB bus, requests the ++ * IRQ line, and calls dwc_otg_hcd_start method. ++ */ ++ retval = usb_add_hcd(hcd, otg_dev->irq, IRQF_SHARED); ++ if (retval < 0) { ++ goto error2; ++ } ++ ++ /* ++ * Allocate space for storing data on status transactions. Normally no ++ * data is sent, but this space acts as a bit bucket. This must be ++ * done after usb_add_hcd since that function allocates the DMA buffer ++ * pool. ++ */ ++ if (otg_dev->core_if->dma_enable) { ++ dwc_otg_hcd->status_buf = ++ dma_alloc_coherent(dev, ++ DWC_OTG_HCD_STATUS_BUF_SIZE, ++ &dwc_otg_hcd->status_buf_dma, ++ GFP_KERNEL | GFP_DMA); ++ } else { ++ dwc_otg_hcd->status_buf = kmalloc(DWC_OTG_HCD_STATUS_BUF_SIZE, ++ GFP_KERNEL); ++ } ++ if (!dwc_otg_hcd->status_buf) { ++ retval = -ENOMEM; ++ DWC_ERROR("%s: status_buf allocation failed\n", __func__); ++ goto error3; ++ } ++ ++ dwc_otg_hcd->otg_dev = otg_dev; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Initialized HCD, bus=%s, usbbus=%d\n", ++ dev_name(dev), hcd->self.busnum); ++ ++ return 0; ++ ++ /* Error conditions */ ++ error3: ++ usb_remove_hcd(hcd); ++ error2: ++ dwc_otg_hcd_free(hcd); ++ usb_put_hcd(hcd); ++ ++ /* FIXME: 2008/05/03 by Steven ++ * write back to device: ++ * dwc_otg_hcd has already been released by dwc_otg_hcd_free() ++ */ ++ dev_set_drvdata(dev, otg_dev); ++ ++ error1: ++ return retval; ++} ++ ++/** ++ * Removes the HCD. ++ * Frees memory and resources associated with the HCD and deregisters the bus. ++ */ ++void dwc_otg_hcd_remove(struct device *dev) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(dev); ++ dwc_otg_hcd_t *dwc_otg_hcd; ++ struct usb_hcd *hcd; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD REMOVE\n"); ++ ++ if (!otg_dev) { ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__); ++ return; ++ } ++ ++ dwc_otg_hcd = otg_dev->hcd; ++ ++ if (!dwc_otg_hcd) { ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__); ++ return; ++ } ++ ++ hcd = dwc_otg_hcd_to_hcd(dwc_otg_hcd); ++ ++ if (!hcd) { ++ DWC_DEBUGPL(DBG_ANY, "%s: dwc_otg_hcd_to_hcd(dwc_otg_hcd) NULL!\n", __func__); ++ return; ++ } ++ ++ /* Turn off all interrupts */ ++ dwc_write_reg32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0); ++ dwc_modify_reg32(&dwc_otg_hcd->core_if->core_global_regs->gahbcfg, 1, 0); ++ ++ usb_remove_hcd(hcd); ++ dwc_otg_hcd_free(hcd); ++ usb_put_hcd(hcd); ++} ++ ++/* ========================================================================= ++ * Linux HC Driver Functions ++ * ========================================================================= */ ++ ++/** ++ * Initializes dynamic portions of the DWC_otg HCD state. ++ */ ++static void hcd_reinit(dwc_otg_hcd_t *hcd) ++{ ++ struct list_head *item; ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ ++ hcd->flags.d32 = 0; ++ ++ hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active; ++ hcd->non_periodic_channels = 0; ++ hcd->periodic_channels = 0; ++ ++ /* ++ * Put all channels in the free channel list and clean up channel ++ * states. ++ */ ++ item = hcd->free_hc_list.next; ++ while (item != &hcd->free_hc_list) { ++ list_del(item); ++ item = hcd->free_hc_list.next; ++ } ++ num_channels = hcd->core_if->core_params->host_channels; ++ for (i = 0; i < num_channels; i++) { ++ channel = hcd->hc_ptr_array[i]; ++ list_add_tail(&channel->hc_list_entry, &hcd->free_hc_list); ++ dwc_otg_hc_cleanup(hcd->core_if, channel); ++ } ++ ++ /* Initialize the DWC core for host mode operation. */ ++ dwc_otg_core_host_init(hcd->core_if); ++} ++ ++/** Initializes the DWC_otg controller and its root hub and prepares it for host ++ * mode operation. Activates the root port. Returns 0 on success and a negative ++ * error code on failure. */ ++int dwc_otg_hcd_start(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ struct usb_bus *bus; ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ struct usb_device *udev; ++ int retval; ++#endif ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD START\n"); ++ ++ bus = hcd_to_bus(hcd); ++ ++ /* Initialize the bus state. If the core is in Device Mode ++ * HALT the USB bus and return. */ ++ if (dwc_otg_is_device_mode(core_if)) { ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ hcd->state = HC_STATE_HALT; ++#else ++ hcd->state = HC_STATE_RUNNING; ++#endif ++ return 0; ++ } ++ hcd->state = HC_STATE_RUNNING; ++ ++ /* Initialize and connect root hub if one is not already attached */ ++ if (bus->root_hub) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n"); ++ /* Inform the HUB driver to resume. */ ++ usb_hcd_resume_root_hub(hcd); ++ } ++ else { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Does Not Have Root Hub\n"); ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ udev = usb_alloc_dev(NULL, bus, 0); ++ udev->speed = USB_SPEED_HIGH; ++ if (!udev) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Error udev alloc\n"); ++ return -ENODEV; ++ } ++ if ((retval = usb_hcd_register_root_hub(udev, hcd)) != 0) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Error registering %d\n", retval); ++ return -ENODEV; ++ } ++#endif ++ } ++ ++ hcd_reinit(dwc_otg_hcd); ++ ++ return 0; ++} ++ ++static void qh_list_free(dwc_otg_hcd_t *hcd, struct list_head *qh_list) ++{ ++ struct list_head *item; ++ dwc_otg_qh_t *qh; ++ ++ if (!qh_list->next) { ++ /* The list hasn't been initialized yet. */ ++ return; ++ } ++ ++ /* Ensure there are no QTDs or URBs left. */ ++ kill_urbs_in_qh_list(hcd, qh_list); ++ ++ for (item = qh_list->next; item != qh_list; item = qh_list->next) { ++ qh = list_entry(item, dwc_otg_qh_t, qh_list_entry); ++ dwc_otg_hcd_qh_remove_and_free(hcd, qh); ++ } ++} ++ ++/** ++ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are ++ * stopped. ++ */ ++void dwc_otg_hcd_stop(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ hprt0_data_t hprt0 = { .d32=0 }; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD STOP\n"); ++ ++ /* Turn off all host-specific interrupts. */ ++ dwc_otg_disable_host_interrupts(dwc_otg_hcd->core_if); ++ ++ /* ++ * The root hub should be disconnected before this function is called. ++ * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue) ++ * and the QH lists (via ..._hcd_endpoint_disable). ++ */ ++ ++ /* Turn off the vbus power */ ++ DWC_PRINT("PortPower off\n"); ++ hprt0.b.prtpwr = 0; ++ dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0.d32); ++} ++ ++/** Returns the current frame number. */ ++int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ hfnum_data_t hfnum; ++ ++ hfnum.d32 = dwc_read_reg32(&dwc_otg_hcd->core_if-> ++ host_if->host_global_regs->hfnum); ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD GET FRAME NUMBER %d\n", hfnum.b.frnum); ++#endif ++ return hfnum.b.frnum; ++} ++ ++/** ++ * Frees secondary storage associated with the dwc_otg_hcd structure contained ++ * in the struct usb_hcd field. ++ */ ++void dwc_otg_hcd_free(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ int i; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD FREE\n"); ++ ++ del_timers(dwc_otg_hcd); ++ ++ /* Free memory for QH/QTD lists */ ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_inactive); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_active); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_inactive); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_ready); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_assigned); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_queued); ++ ++ /* Free memory for the host channels. */ ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ dwc_hc_t *hc = dwc_otg_hcd->hc_ptr_array[i]; ++ if (hc != NULL) { ++ DWC_DEBUGPL(DBG_HCDV, "HCD Free channel #%i, hc=%p\n", i, hc); ++ kfree(hc); ++ } ++ } ++ ++ if (dwc_otg_hcd->core_if->dma_enable) { ++ if (dwc_otg_hcd->status_buf_dma) { ++ dma_free_coherent(hcd->self.controller, ++ DWC_OTG_HCD_STATUS_BUF_SIZE, ++ dwc_otg_hcd->status_buf, ++ dwc_otg_hcd->status_buf_dma); ++ } ++ } else if (dwc_otg_hcd->status_buf != NULL) { ++ kfree(dwc_otg_hcd->status_buf); ++ } ++} ++ ++#ifdef DEBUG ++static void dump_urb_info(struct urb *urb, char* fn_name) ++{ ++ DWC_PRINT("%s, urb %p\n", fn_name, urb); ++ DWC_PRINT(" Device address: %d\n", usb_pipedevice(urb->pipe)); ++ DWC_PRINT(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe), ++ (usb_pipein(urb->pipe) ? "IN" : "OUT")); ++ DWC_PRINT(" Endpoint type: %s\n", ++ ({char *pipetype; ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: pipetype = "CONTROL"; break; ++ case PIPE_BULK: pipetype = "BULK"; break; ++ case PIPE_INTERRUPT: pipetype = "INTERRUPT"; break; ++ case PIPE_ISOCHRONOUS: pipetype = "ISOCHRONOUS"; break; ++ default: pipetype = "UNKNOWN"; break; ++ }; pipetype;})); ++ DWC_PRINT(" Speed: %s\n", ++ ({char *speed; ++ switch (urb->dev->speed) { ++ case USB_SPEED_HIGH: speed = "HIGH"; break; ++ case USB_SPEED_FULL: speed = "FULL"; break; ++ case USB_SPEED_LOW: speed = "LOW"; break; ++ default: speed = "UNKNOWN"; break; ++ }; speed;})); ++ DWC_PRINT(" Max packet size: %d\n", ++ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); ++ DWC_PRINT(" Data buffer length: %d\n", urb->transfer_buffer_length); ++ DWC_PRINT(" Transfer buffer: %p, Transfer DMA: %p\n", ++ urb->transfer_buffer, (void *)urb->transfer_dma); ++ DWC_PRINT(" Setup buffer: %p, Setup DMA: %p\n", ++ urb->setup_packet, (void *)urb->setup_dma); ++ DWC_PRINT(" Interval: %d\n", urb->interval); ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ int i; ++ for (i = 0; i < urb->number_of_packets; i++) { ++ DWC_PRINT(" ISO Desc %d:\n", i); ++ DWC_PRINT(" offset: %d, length %d\n", ++ urb->iso_frame_desc[i].offset, ++ urb->iso_frame_desc[i].length); ++ } ++ } ++} ++ ++static void dump_channel_info(dwc_otg_hcd_t *hcd, ++ dwc_otg_qh_t *qh) ++{ ++ if (qh->channel != NULL) { ++ dwc_hc_t *hc = qh->channel; ++ struct list_head *item; ++ dwc_otg_qh_t *qh_item; ++ int num_channels = hcd->core_if->core_params->host_channels; ++ int i; ++ ++ dwc_otg_hc_regs_t *hc_regs; ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ uint32_t hcdma; ++ ++ hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt); ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ hcdma = dwc_read_reg32(&hc_regs->hcdma); ++ ++ DWC_PRINT(" Assigned to channel %p:\n", hc); ++ DWC_PRINT(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); ++ DWC_PRINT(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma); ++ DWC_PRINT(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", ++ hc->dev_addr, hc->ep_num, hc->ep_is_in); ++ DWC_PRINT(" ep_type: %d\n", hc->ep_type); ++ DWC_PRINT(" max_packet: %d\n", hc->max_packet); ++ DWC_PRINT(" data_pid_start: %d\n", hc->data_pid_start); ++ DWC_PRINT(" xfer_started: %d\n", hc->xfer_started); ++ DWC_PRINT(" halt_status: %d\n", hc->halt_status); ++ DWC_PRINT(" xfer_buff: %p\n", hc->xfer_buff); ++ DWC_PRINT(" xfer_len: %d\n", hc->xfer_len); ++ DWC_PRINT(" qh: %p\n", hc->qh); ++ DWC_PRINT(" NP inactive sched:\n"); ++ list_for_each(item, &hcd->non_periodic_sched_inactive) { ++ qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry); ++ DWC_PRINT(" %p\n", qh_item); ++ } ++ DWC_PRINT(" NP active sched:\n"); ++ list_for_each(item, &hcd->non_periodic_sched_active) { ++ qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry); ++ DWC_PRINT(" %p\n", qh_item); ++ } ++ DWC_PRINT(" Channels: \n"); ++ for (i = 0; i < num_channels; i++) { ++ dwc_hc_t *hc = hcd->hc_ptr_array[i]; ++ DWC_PRINT(" %2d: %p\n", i, hc); ++ } ++ } ++} ++#endif ++ ++/** Starts processing a USB transfer request specified by a USB Request Block ++ * (URB). mem_flags indicates the type of memory allocation to use while ++ * processing this URB. */ ++int dwc_otg_hcd_urb_enqueue(struct usb_hcd *hcd, ++ struct urb *urb, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ int mem_flags ++#else ++ gfp_t mem_flags ++#endif ++ ) ++{ ++ int retval = 0; ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ dwc_otg_qtd_t *qtd; ++ ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ dump_urb_info(urb, "dwc_otg_hcd_urb_enqueue"); ++ } ++#endif ++ if (!dwc_otg_hcd->flags.b.port_connect_status) { ++ /* No longer connected. */ ++ return -ENODEV; ++ } ++ ++ qtd = dwc_otg_hcd_qtd_create(urb); ++ if (qtd == NULL) { ++ DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n"); ++ return -ENOMEM; ++ } ++ ++ retval = dwc_otg_hcd_qtd_add(qtd, dwc_otg_hcd); ++ if (retval < 0) { ++ DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. " ++ "Error status %d\n", retval); ++ dwc_otg_hcd_qtd_free(qtd); ++ } ++ ++ return retval; ++} ++ ++/** Aborts/cancels a USB transfer request. Always returns 0 to indicate ++ * success. */ ++int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd, ++ struct urb *urb, ++ int status) ++{ ++ unsigned long flags; ++ dwc_otg_hcd_t *dwc_otg_hcd; ++ dwc_otg_qtd_t *urb_qtd; ++ dwc_otg_qh_t *qh; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb); ++#endif ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n"); ++ ++ dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++ SPIN_LOCK_IRQSAVE(&dwc_otg_hcd->lock, flags); ++ ++ urb_qtd = (dwc_otg_qtd_t *)urb->hcpriv; ++ qh = (dwc_otg_qh_t *)ep->hcpriv; ++ ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ dump_urb_info(urb, "dwc_otg_hcd_urb_dequeue"); ++ if (urb_qtd == qh->qtd_in_process) { ++ dump_channel_info(dwc_otg_hcd, qh); ++ } ++ } ++#endif ++ ++ if (urb_qtd == qh->qtd_in_process) { ++ /* The QTD is in process (it has been assigned to a channel). */ ++ ++ if (dwc_otg_hcd->flags.b.port_connect_status) { ++ /* ++ * If still connected (i.e. in host mode), halt the ++ * channel so it can be used for other transfers. If ++ * no longer connected, the host registers can't be ++ * written to halt the channel since the core is in ++ * device mode. ++ */ ++ dwc_otg_hc_halt(dwc_otg_hcd->core_if, qh->channel, ++ DWC_OTG_HC_XFER_URB_DEQUEUE); ++ } ++ } ++ ++ /* ++ * Free the QTD and clean up the associated QH. Leave the QH in the ++ * schedule if it has any remaining QTDs. ++ */ ++ dwc_otg_hcd_qtd_remove_and_free(dwc_otg_hcd, urb_qtd); ++ if (urb_qtd == qh->qtd_in_process) { ++ dwc_otg_hcd_qh_deactivate(dwc_otg_hcd, qh, 0); ++ qh->channel = NULL; ++ qh->qtd_in_process = NULL; ++ } else if (list_empty(&qh->qtd_list)) { ++ dwc_otg_hcd_qh_remove(dwc_otg_hcd, qh); ++ } ++ ++ SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags); ++ ++ urb->hcpriv = NULL; ++ ++ /* Higher layer software sets URB status. */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ usb_hcd_giveback_urb(hcd, urb, status); ++#else ++ usb_hcd_giveback_urb(hcd, urb, NULL); ++#endif ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ DWC_PRINT("Called usb_hcd_giveback_urb()\n"); ++ DWC_PRINT(" urb->status = %d\n", urb->status); ++ } ++ ++ return 0; ++} ++ ++/** Frees resources in the DWC_otg controller related to a given endpoint. Also ++ * clears state in the HCD related to the endpoint. Any URBs for the endpoint ++ * must already be dequeued. */ ++void dwc_otg_hcd_endpoint_disable(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ dwc_otg_qh_t *qh; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ unsigned long flags; ++ int retry = 0; ++#endif ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD EP DISABLE: _bEndpointAddress=0x%02x, " ++ "endpoint=%d\n", ep->desc.bEndpointAddress, ++ dwc_ep_addr_to_endpoint(ep->desc.bEndpointAddress)); ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++rescan: ++ SPIN_LOCK_IRQSAVE(&dwc_otg_hcd->lock, flags); ++ qh = (dwc_otg_qh_t *)(ep->hcpriv); ++ if (!qh) ++ goto done; ++ ++ /** Check that the QTD list is really empty */ ++ if (!list_empty(&qh->qtd_list)) { ++ if (retry++ < 250) { ++ SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags); ++ schedule_timeout_uninterruptible(1); ++ goto rescan; ++ } ++ ++ DWC_WARN("DWC OTG HCD EP DISABLE:" ++ " QTD List for this endpoint is not empty\n"); ++ } ++ ++ dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd, qh); ++ ep->hcpriv = NULL; ++done: ++ SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags); ++ ++#else // LINUX_VERSION_CODE ++ ++ qh = (dwc_otg_qh_t *)(ep->hcpriv); ++ if (qh != NULL) { ++#ifdef DEBUG ++ /** Check that the QTD list is really empty */ ++ if (!list_empty(&qh->qtd_list)) { ++ DWC_WARN("DWC OTG HCD EP DISABLE:" ++ " QTD List for this endpoint is not empty\n"); ++ } ++#endif ++ dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd, qh); ++ ep->hcpriv = NULL; ++ } ++#endif // LINUX_VERSION_CODE ++} ++ ++/** Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if ++ * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid ++ * interrupt. ++ * ++ * This function is called by the USB core when an interrupt occurs */ ++irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ , struct pt_regs *regs ++#endif ++ ) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ return IRQ_RETVAL(dwc_otg_hcd_handle_intr(dwc_otg_hcd)); ++} ++ ++/** Creates Status Change bitmap for the root hub and root port. The bitmap is ++ * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1 ++ * is the status change indicator for the single root port. Returns 1 if either ++ * change indicator is 1, otherwise returns 0. */ ++int dwc_otg_hcd_hub_status_data(struct usb_hcd *hcd, char *buf) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++ buf[0] = 0; ++ buf[0] |= (dwc_otg_hcd->flags.b.port_connect_status_change || ++ dwc_otg_hcd->flags.b.port_reset_change || ++ dwc_otg_hcd->flags.b.port_enable_change || ++ dwc_otg_hcd->flags.b.port_suspend_change || ++ dwc_otg_hcd->flags.b.port_over_current_change) << 1; ++ ++#ifdef DEBUG ++ if (buf[0]) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB STATUS DATA:" ++ " Root port status changed\n"); ++ DWC_DEBUGPL(DBG_HCDV, " port_connect_status_change: %d\n", ++ dwc_otg_hcd->flags.b.port_connect_status_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_reset_change: %d\n", ++ dwc_otg_hcd->flags.b.port_reset_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_enable_change: %d\n", ++ dwc_otg_hcd->flags.b.port_enable_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_suspend_change: %d\n", ++ dwc_otg_hcd->flags.b.port_suspend_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_over_current_change: %d\n", ++ dwc_otg_hcd->flags.b.port_over_current_change); ++ } ++#endif ++ return (buf[0] != 0); ++} ++ ++#ifdef DWC_HS_ELECT_TST ++/* ++ * Quick and dirty hack to implement the HS Electrical Test ++ * SINGLE_STEP_GET_DEVICE_DESCRIPTOR feature. ++ * ++ * This code was copied from our userspace app "hset". It sends a ++ * Get Device Descriptor control sequence in two parts, first the ++ * Setup packet by itself, followed some time later by the In and ++ * Ack packets. Rather than trying to figure out how to add this ++ * functionality to the normal driver code, we just hijack the ++ * hardware, using these two function to drive the hardware ++ * directly. ++ */ ++ ++dwc_otg_core_global_regs_t *global_regs; ++dwc_otg_host_global_regs_t *hc_global_regs; ++dwc_otg_hc_regs_t *hc_regs; ++uint32_t *data_fifo; ++ ++static void do_setup(void) ++{ ++ gintsts_data_t gintsts; ++ hctsiz_data_t hctsiz; ++ hcchar_data_t hcchar; ++ haint_data_t haint; ++ hcint_data_t hcint; ++ ++ /* Enable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0001); ++ ++ /* Enable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x04a3); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* ++ * Send Setup packet (Get Device Descriptor) ++ */ ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ //fprintf(stderr, "Channel already enabled 1, HCCHAR = %08x\n", hcchar.d32); ++ hcchar.b.chdis = 1; ++// hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //if (hcchar.b.chen) { ++ // fprintf(stderr, "** Channel _still_ enabled 1, HCCHAR = %08x **\n", hcchar.d32); ++ //} ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 8; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_SETUP; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 0; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ /* Fill FIFO with Setup data for Get Device Descriptor */ ++ data_fifo = (uint32_t *)((char *)global_regs + 0x1000); ++ dwc_write_reg32(data_fifo++, 0x01000680); ++ dwc_write_reg32(data_fifo++, 0x00080000); ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for HCINTR intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ //fprintf(stderr, "Got HCINTR intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Disable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x0000); ++ ++ /* Disable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0000); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++} ++ ++static void do_in_ack(void) ++{ ++ gintsts_data_t gintsts; ++ hctsiz_data_t hctsiz; ++ hcchar_data_t hcchar; ++ haint_data_t haint; ++ hcint_data_t hcint; ++ host_grxsts_data_t grxsts; ++ ++ /* Enable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0001); ++ ++ /* Enable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x04a3); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* ++ * Receive Control In packet ++ */ ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ //fprintf(stderr, "Channel already enabled 2, HCCHAR = %08x\n", hcchar.d32); ++ hcchar.b.chdis = 1; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //if (hcchar.b.chen) { ++ // fprintf(stderr, "** Channel _still_ enabled 2, HCCHAR = %08x **\n", hcchar.d32); ++ //} ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 8; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_DATA1; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 1; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for RXSTSQLVL intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for receive status queue interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.rxstsqlvl == 0); ++ ++ //fprintf(stderr, "Got RXSTSQLVL intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Read RXSTS */ ++ grxsts.d32 = dwc_read_reg32(&global_regs->grxstsp); ++ //fprintf(stderr, "GRXSTS: %08x\n", grxsts.d32); ++ ++ /* Clear RXSTSQLVL in GINTSTS */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN: ++ /* Read the data into the host buffer */ ++ if (grxsts.b.bcnt > 0) { ++ int i; ++ int word_count = (grxsts.b.bcnt + 3) / 4; ++ ++ data_fifo = (uint32_t *)((char *)global_regs + 0x1000); ++ ++ for (i = 0; i < word_count; i++) { ++ (void)dwc_read_reg32(data_fifo++); ++ } ++ } ++ ++ //fprintf(stderr, "Received %u bytes\n", (unsigned)grxsts.b.bcnt); ++ break; ++ ++ default: ++ //fprintf(stderr, "** Unexpected GRXSTS packet status 1 **\n"); ++ break; ++ } ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for RXSTSQLVL intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for receive status queue interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.rxstsqlvl == 0); ++ ++ //fprintf(stderr, "Got RXSTSQLVL intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Read RXSTS */ ++ grxsts.d32 = dwc_read_reg32(&global_regs->grxstsp); ++ //fprintf(stderr, "GRXSTS: %08x\n", grxsts.d32); ++ ++ /* Clear RXSTSQLVL in GINTSTS */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP: ++ break; ++ ++ default: ++ //fprintf(stderr, "** Unexpected GRXSTS packet status 2 **\n"); ++ break; ++ } ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for HCINTR intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ //fprintf(stderr, "Got HCINTR intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++// usleep(100000); ++// mdelay(100); ++ mdelay(1); ++ ++ /* ++ * Send handshake packet ++ */ ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ //fprintf(stderr, "Channel already enabled 3, HCCHAR = %08x\n", hcchar.d32); ++ hcchar.b.chdis = 1; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //if (hcchar.b.chen) { ++ // fprintf(stderr, "** Channel _still_ enabled 3, HCCHAR = %08x **\n", hcchar.d32); ++ //} ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 0; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_DATA1; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 0; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for HCINTR intr 3, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ //fprintf(stderr, "Got HCINTR intr 3, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Disable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x0000); ++ ++ /* Disable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0000); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++} ++#endif /* DWC_HS_ELECT_TST */ ++ ++/** Handles hub class-specific requests. */ ++int dwc_otg_hcd_hub_control(struct usb_hcd *hcd, ++ u16 typeReq, ++ u16 wValue, ++ u16 wIndex, ++ char *buf, ++ u16 wLength) ++{ ++ int retval = 0; ++ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ dwc_otg_core_if_t *core_if = hcd_to_dwc_otg_hcd(hcd)->core_if; ++ struct usb_hub_descriptor *desc; ++ hprt0_data_t hprt0 = {.d32 = 0}; ++ ++ uint32_t port_status; ++ ++ switch (typeReq) { ++ case ClearHubFeature: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearHubFeature 0x%x\n", wValue); ++ switch (wValue) { ++ case C_HUB_LOCAL_POWER: ++ case C_HUB_OVER_CURRENT: ++ /* Nothing required here */ ++ break; ++ default: ++ retval = -EINVAL; ++ DWC_ERROR("DWC OTG HCD - " ++ "ClearHubFeature request %xh unknown\n", wValue); ++ } ++ break; ++ case ClearPortFeature: ++ if (!wIndex || wIndex > 1) ++ goto error; ++ ++ switch (wValue) { ++ case USB_PORT_FEAT_ENABLE: ++ DWC_DEBUGPL(DBG_ANY, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_ENABLE\n"); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtena = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_SUSPEND: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtres = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ /* Clear Resume bit */ ++ mdelay(100); ++ hprt0.b.prtres = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_POWER: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_POWER\n"); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_INDICATOR: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_INDICATOR\n"); ++ /* Port inidicator not supported */ ++ break; ++ case USB_PORT_FEAT_C_CONNECTION: ++ /* Clears drivers internal connect status change ++ * flag */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n"); ++ dwc_otg_hcd->flags.b.port_connect_status_change = 0; ++ break; ++ case USB_PORT_FEAT_C_RESET: ++ /* Clears the driver's internal Port Reset Change ++ * flag */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_RESET\n"); ++ dwc_otg_hcd->flags.b.port_reset_change = 0; ++ break; ++ case USB_PORT_FEAT_C_ENABLE: ++ /* Clears the driver's internal Port ++ * Enable/Disable Change flag */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n"); ++ dwc_otg_hcd->flags.b.port_enable_change = 0; ++ break; ++ case USB_PORT_FEAT_C_SUSPEND: ++ /* Clears the driver's internal Port Suspend ++ * Change flag, which is set when resume signaling on ++ * the host port is complete */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n"); ++ dwc_otg_hcd->flags.b.port_suspend_change = 0; ++ break; ++ case USB_PORT_FEAT_C_OVER_CURRENT: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n"); ++ dwc_otg_hcd->flags.b.port_over_current_change = 0; ++ break; ++ default: ++ retval = -EINVAL; ++ DWC_ERROR("DWC OTG HCD - " ++ "ClearPortFeature request %xh " ++ "unknown or unsupported\n", wValue); ++ } ++ break; ++ case GetHubDescriptor: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetHubDescriptor\n"); ++ desc = (struct usb_hub_descriptor *)buf; ++ desc->bDescLength = 9; ++ desc->bDescriptorType = 0x29; ++ desc->bNbrPorts = 1; ++ desc->wHubCharacteristics = 0x08; ++ desc->bPwrOn2PwrGood = 1; ++ desc->bHubContrCurrent = 0; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39) ++ desc->u.hs.DeviceRemovable[0] = 0; ++ desc->u.hs.DeviceRemovable[1] = 0xff; ++#endif ++ break; ++ case GetHubStatus: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetHubStatus\n"); ++ memset(buf, 0, 4); ++ break; ++ case GetPortStatus: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetPortStatus\n"); ++ ++ if (!wIndex || wIndex > 1) ++ goto error; ++ ++ port_status = 0; ++ ++ if (dwc_otg_hcd->flags.b.port_connect_status_change) ++ port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); ++ ++ if (dwc_otg_hcd->flags.b.port_enable_change) ++ port_status |= (1 << USB_PORT_FEAT_C_ENABLE); ++ ++ if (dwc_otg_hcd->flags.b.port_suspend_change) ++ port_status |= (1 << USB_PORT_FEAT_C_SUSPEND); ++ ++ if (dwc_otg_hcd->flags.b.port_reset_change) ++ port_status |= (1 << USB_PORT_FEAT_C_RESET); ++ ++ if (dwc_otg_hcd->flags.b.port_over_current_change) { ++ DWC_ERROR("Device Not Supported\n"); ++ port_status |= (1 << USB_PORT_FEAT_C_OVER_CURRENT); ++ } ++ ++ if (!dwc_otg_hcd->flags.b.port_connect_status) { ++ /* ++ * The port is disconnected, which means the core is ++ * either in device mode or it soon will be. Just ++ * return 0's for the remainder of the port status ++ * since the port register can't be read if the core ++ * is in device mode. ++ */ ++ *((__le32 *) buf) = cpu_to_le32(port_status); ++ break; ++ } ++ ++ hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0); ++ DWC_DEBUGPL(DBG_HCDV, " HPRT0: 0x%08x\n", hprt0.d32); ++ ++ if (hprt0.b.prtconnsts) ++ port_status |= (1 << USB_PORT_FEAT_CONNECTION); ++ ++ if (hprt0.b.prtena) ++ port_status |= (1 << USB_PORT_FEAT_ENABLE); ++ ++ if (hprt0.b.prtsusp) ++ port_status |= (1 << USB_PORT_FEAT_SUSPEND); ++ ++ if (hprt0.b.prtovrcurract) ++ port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT); ++ ++ if (hprt0.b.prtrst) ++ port_status |= (1 << USB_PORT_FEAT_RESET); ++ ++ if (hprt0.b.prtpwr) ++ port_status |= (1 << USB_PORT_FEAT_POWER); ++ ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) ++ port_status |= USB_PORT_STAT_HIGH_SPEED; ++ else if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED) ++ port_status |= USB_PORT_STAT_LOW_SPEED; ++ ++ if (hprt0.b.prttstctl) ++ port_status |= (1 << USB_PORT_FEAT_TEST); ++ ++ /* USB_PORT_FEAT_INDICATOR unsupported always 0 */ ++ ++ *((__le32 *) buf) = cpu_to_le32(port_status); ++ ++ break; ++ case SetHubFeature: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetHubFeature\n"); ++ /* No HUB features supported */ ++ break; ++ case SetPortFeature: ++ if (wValue != USB_PORT_FEAT_TEST && (!wIndex || wIndex > 1)) ++ goto error; ++ ++ if (!dwc_otg_hcd->flags.b.port_connect_status) { ++ /* ++ * The port is disconnected, which means the core is ++ * either in device mode or it soon will be. Just ++ * return without doing anything since the port ++ * register can't be written if the core is in device ++ * mode. ++ */ ++ break; ++ } ++ ++ switch (wValue) { ++ case USB_PORT_FEAT_SUSPEND: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); ++ if (hcd->self.otg_port == wIndex && ++ hcd->self.b_hnp_enable) { ++ gotgctl_data_t gotgctl = {.d32=0}; ++ gotgctl.b.hstsethnpen = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gotgctl, ++ 0, gotgctl.d32); ++ core_if->op_state = A_SUSPEND; ++ } ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtsusp = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ //DWC_PRINT("SUSPEND: HPRT0=%0x\n", hprt0.d32); ++ /* Suspend the Phy Clock */ ++ { ++ pcgcctl_data_t pcgcctl = {.d32=0}; ++ pcgcctl.b.stoppclk = 1; ++ dwc_write_reg32(core_if->pcgcctl, pcgcctl.d32); ++ } ++ ++ /* For HNP the bus must be suspended for at least 200ms. */ ++ if (hcd->self.b_hnp_enable) { ++ mdelay(200); ++ //DWC_PRINT("SUSPEND: wait complete! (%d)\n", _hcd->state); ++ } ++ break; ++ case USB_PORT_FEAT_POWER: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_POWER\n"); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_RESET: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_RESET\n"); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ /* When B-Host the Port reset bit is set in ++ * the Start HCD Callback function, so that ++ * the reset is started within 1ms of the HNP ++ * success interrupt. */ ++ if (!hcd->self.is_b_host) { ++ hprt0.b.prtrst = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ } ++ /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ ++ MDELAY(60); ++ hprt0.b.prtrst = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ ++#ifdef DWC_HS_ELECT_TST ++ case USB_PORT_FEAT_TEST: ++ { ++ uint32_t t; ++ gintmsk_data_t gintmsk; ++ ++ t = (wIndex >> 8); /* MSB wIndex USB */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_TEST %d\n", t); ++ warn("USB_PORT_FEAT_TEST %d\n", t); ++ if (t < 6) { ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prttstctl = t; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ } else { ++ /* Setup global vars with reg addresses (quick and ++ * dirty hack, should be cleaned up) ++ */ ++ global_regs = core_if->core_global_regs; ++ hc_global_regs = core_if->host_if->host_global_regs; ++ hc_regs = (dwc_otg_hc_regs_t *)((char *)global_regs + 0x500); ++ data_fifo = (uint32_t *)((char *)global_regs + 0x1000); ++ ++ if (t == 6) { /* HS_HOST_PORT_SUSPEND_RESUME */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ dwc_write_reg32(&global_regs->gintmsk, 0); ++ ++ /* 15 second delay per the test spec */ ++ mdelay(15000); ++ ++ /* Drive suspend on the root port */ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtsusp = 1; ++ hprt0.b.prtres = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ ++ /* 15 second delay per the test spec */ ++ mdelay(15000); ++ ++ /* Drive resume on the root port */ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtsusp = 0; ++ hprt0.b.prtres = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ mdelay(100); ++ ++ /* Clear the resume bit */ ++ hprt0.b.prtres = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ ++ /* Restore interrupts */ ++ dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); ++ } else if (t == 7) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ dwc_write_reg32(&global_regs->gintmsk, 0); ++ ++ /* 15 second delay per the test spec */ ++ mdelay(15000); ++ ++ /* Send the Setup packet */ ++ do_setup(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ mdelay(15000); ++ ++ /* Restore interrupts */ ++ dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); ++ } else if (t == 8) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ dwc_write_reg32(&global_regs->gintmsk, 0); ++ ++ /* Send the Setup packet */ ++ do_setup(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ mdelay(15000); ++ ++ /* Send the In and Ack packets */ ++ do_in_ack(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ mdelay(15000); ++ ++ /* Restore interrupts */ ++ dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); ++ } ++ } ++ break; ++ } ++#endif /* DWC_HS_ELECT_TST */ ++ ++ case USB_PORT_FEAT_INDICATOR: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_INDICATOR\n"); ++ /* Not supported */ ++ break; ++ default: ++ retval = -EINVAL; ++ DWC_ERROR("DWC OTG HCD - " ++ "SetPortFeature request %xh " ++ "unknown or unsupported\n", wValue); ++ break; ++ } ++ break; ++ default: ++ error: ++ retval = -EINVAL; ++ DWC_WARN("DWC OTG HCD - " ++ "Unknown hub control request type or invalid typeReq: %xh wIndex: %xh wValue: %xh\n", ++ typeReq, wIndex, wValue); ++ break; ++ } ++ ++ return retval; ++} ++ ++/** ++ * Assigns transactions from a QTD to a free host channel and initializes the ++ * host channel to perform the transactions. The host channel is removed from ++ * the free list. ++ * ++ * @param hcd The HCD state structure. ++ * @param qh Transactions from the first QTD for this QH are selected and ++ * assigned to a free host channel. ++ */ ++static void assign_and_init_hc(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ dwc_hc_t *hc; ++ dwc_otg_qtd_t *qtd; ++ struct urb *urb; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p,%p)\n", __func__, hcd, qh); ++ ++ hc = list_entry(hcd->free_hc_list.next, dwc_hc_t, hc_list_entry); ++ ++ /* Remove the host channel from the free list. */ ++ list_del_init(&hc->hc_list_entry); ++ ++ qtd = list_entry(qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); ++ urb = qtd->urb; ++ qh->channel = hc; ++ qh->qtd_in_process = qtd; ++ ++ /* ++ * Use usb_pipedevice to determine device address. This address is ++ * 0 before the SET_ADDRESS command and the correct address afterward. ++ */ ++ hc->dev_addr = usb_pipedevice(urb->pipe); ++ hc->ep_num = usb_pipeendpoint(urb->pipe); ++ ++ if (urb->dev->speed == USB_SPEED_LOW) { ++ hc->speed = DWC_OTG_EP_SPEED_LOW; ++ } else if (urb->dev->speed == USB_SPEED_FULL) { ++ hc->speed = DWC_OTG_EP_SPEED_FULL; ++ } else { ++ hc->speed = DWC_OTG_EP_SPEED_HIGH; ++ } ++ ++ hc->max_packet = dwc_max_packet(qh->maxp); ++ ++ hc->xfer_started = 0; ++ hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS; ++ hc->error_state = (qtd->error_count > 0); ++ hc->halt_on_queue = 0; ++ hc->halt_pending = 0; ++ hc->requests = 0; ++ ++ /* ++ * The following values may be modified in the transfer type section ++ * below. The xfer_len value may be reduced when the transfer is ++ * started to accommodate the max widths of the XferSize and PktCnt ++ * fields in the HCTSIZn register. ++ */ ++ hc->do_ping = qh->ping_state; ++ hc->ep_is_in = (usb_pipein(urb->pipe) != 0); ++ hc->data_pid_start = qh->data_toggle; ++ hc->multi_count = 1; ++ ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *)urb->transfer_dma + urb->actual_length; ++ } else { ++ hc->xfer_buff = (uint8_t *)urb->transfer_buffer + urb->actual_length; ++ } ++ hc->xfer_len = urb->transfer_buffer_length - urb->actual_length; ++ hc->xfer_count = 0; ++ ++ /* ++ * Set the split attributes ++ */ ++ hc->do_split = 0; ++ if (qh->do_split) { ++ hc->do_split = 1; ++ hc->xact_pos = qtd->isoc_split_pos; ++ hc->complete_split = qtd->complete_split; ++ hc->hub_addr = urb->dev->tt->hub->devnum; ++ hc->port_addr = urb->dev->ttport; ++ } ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: ++ hc->ep_type = DWC_OTG_EP_TYPE_CONTROL; ++ switch (qtd->control_phase) { ++ case DWC_OTG_CONTROL_SETUP: ++ DWC_DEBUGPL(DBG_HCDV, " Control setup transaction\n"); ++ hc->do_ping = 0; ++ hc->ep_is_in = 0; ++ hc->data_pid_start = DWC_OTG_HC_PID_SETUP; ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *)urb->setup_dma; ++ } else { ++ hc->xfer_buff = (uint8_t *)urb->setup_packet; ++ } ++ hc->xfer_len = 8; ++ break; ++ case DWC_OTG_CONTROL_DATA: ++ DWC_DEBUGPL(DBG_HCDV, " Control data transaction\n"); ++ hc->data_pid_start = qtd->data_toggle; ++ break; ++ case DWC_OTG_CONTROL_STATUS: ++ /* ++ * Direction is opposite of data direction or IN if no ++ * data. ++ */ ++ DWC_DEBUGPL(DBG_HCDV, " Control status transaction\n"); ++ if (urb->transfer_buffer_length == 0) { ++ hc->ep_is_in = 1; ++ } else { ++ hc->ep_is_in = (usb_pipein(urb->pipe) != USB_DIR_IN); ++ } ++ if (hc->ep_is_in) { ++ hc->do_ping = 0; ++ } ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA1; ++ hc->xfer_len = 0; ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *)hcd->status_buf_dma; ++ } else { ++ hc->xfer_buff = (uint8_t *)hcd->status_buf; ++ } ++ break; ++ } ++ break; ++ case PIPE_BULK: ++ hc->ep_type = DWC_OTG_EP_TYPE_BULK; ++ break; ++ case PIPE_INTERRUPT: ++ hc->ep_type = DWC_OTG_EP_TYPE_INTR; ++ break; ++ case PIPE_ISOCHRONOUS: ++ { ++ struct usb_iso_packet_descriptor *frame_desc; ++ frame_desc = &urb->iso_frame_desc[qtd->isoc_frame_index]; ++ hc->ep_type = DWC_OTG_EP_TYPE_ISOC; ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *)urb->transfer_dma; ++ } else { ++ hc->xfer_buff = (uint8_t *)urb->transfer_buffer; ++ } ++ hc->xfer_buff += frame_desc->offset + qtd->isoc_split_offset; ++ hc->xfer_len = frame_desc->length - qtd->isoc_split_offset; ++ ++ if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) { ++ if (hc->xfer_len <= 188) { ++ hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ } ++ else { ++ hc->xact_pos = DWC_HCSPLIT_XACTPOS_BEGIN; ++ } ++ } ++ } ++ break; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * This value may be modified when the transfer is started to ++ * reflect the actual transfer length. ++ */ ++ hc->multi_count = dwc_hb_mult(qh->maxp); ++ } ++ ++ dwc_otg_hc_init(hcd->core_if, hc); ++ hc->qh = qh; ++} ++ ++/** ++ * This function selects transactions from the HCD transfer schedule and ++ * assigns them to available host channels. It is called from HCD interrupt ++ * handler functions. ++ * ++ * @param hcd The HCD state structure. ++ * ++ * @return The types of new transactions that were assigned to host channels. ++ */ ++dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t *hcd) ++{ ++ struct list_head *qh_ptr; ++ dwc_otg_qh_t *qh; ++ int num_channels; ++ dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, " Select Transactions\n"); ++#endif ++ ++ /* Process entries in the periodic ready list. */ ++ qh_ptr = hcd->periodic_sched_ready.next; ++ while (qh_ptr != &hcd->periodic_sched_ready && ++ !list_empty(&hcd->free_hc_list)) { ++ ++ qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ assign_and_init_hc(hcd, qh); ++ ++ /* ++ * Move the QH from the periodic ready schedule to the ++ * periodic assigned schedule. ++ */ ++ qh_ptr = qh_ptr->next; ++ list_move(&qh->qh_list_entry, &hcd->periodic_sched_assigned); ++ ++ ret_val = DWC_OTG_TRANSACTION_PERIODIC; ++ } ++ ++ /* ++ * Process entries in the inactive portion of the non-periodic ++ * schedule. Some free host channels may not be used if they are ++ * reserved for periodic transfers. ++ */ ++ qh_ptr = hcd->non_periodic_sched_inactive.next; ++ num_channels = hcd->core_if->core_params->host_channels; ++ while (qh_ptr != &hcd->non_periodic_sched_inactive && ++ (hcd->non_periodic_channels < ++ num_channels - hcd->periodic_channels) && ++ !list_empty(&hcd->free_hc_list)) { ++ ++ qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ assign_and_init_hc(hcd, qh); ++ ++ /* ++ * Move the QH from the non-periodic inactive schedule to the ++ * non-periodic active schedule. ++ */ ++ qh_ptr = qh_ptr->next; ++ list_move(&qh->qh_list_entry, &hcd->non_periodic_sched_active); ++ ++ if (ret_val == DWC_OTG_TRANSACTION_NONE) { ++ ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC; ++ } else { ++ ret_val = DWC_OTG_TRANSACTION_ALL; ++ } ++ ++ hcd->non_periodic_channels++; ++ } ++ ++ return ret_val; ++} ++ ++/** ++ * Attempts to queue a single transaction request for a host channel ++ * associated with either a periodic or non-periodic transfer. This function ++ * assumes that there is space available in the appropriate request queue. For ++ * an OUT transfer or SETUP transaction in Slave mode, it checks whether space ++ * is available in the appropriate Tx FIFO. ++ * ++ * @param hcd The HCD state structure. ++ * @param hc Host channel descriptor associated with either a periodic or ++ * non-periodic transfer. ++ * @param fifo_dwords_avail Number of DWORDs available in the periodic Tx ++ * FIFO for periodic transfers or the non-periodic Tx FIFO for non-periodic ++ * transfers. ++ * ++ * @return 1 if a request is queued and more requests may be needed to ++ * complete the transfer, 0 if no more requests are required for this ++ * transfer, -1 if there is insufficient space in the Tx FIFO. ++ */ ++static int queue_transaction(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ uint16_t fifo_dwords_avail) ++{ ++ int retval; ++ ++ if (hcd->core_if->dma_enable) { ++ if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ hc->qh->ping_state = 0; ++ } ++ retval = 0; ++ } else if (hc->halt_pending) { ++ /* Don't queue a request if the channel has been halted. */ ++ retval = 0; ++ } else if (hc->halt_on_queue) { ++ dwc_otg_hc_halt(hcd->core_if, hc, hc->halt_status); ++ retval = 0; ++ } else if (hc->do_ping) { ++ if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ } ++ retval = 0; ++ } else if (!hc->ep_is_in || ++ hc->data_pid_start == DWC_OTG_HC_PID_SETUP) { ++ if ((fifo_dwords_avail * 4) >= hc->max_packet) { ++ if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ retval = 1; ++ } else { ++ retval = dwc_otg_hc_continue_transfer(hcd->core_if, hc); ++ } ++ } else { ++ retval = -1; ++ } ++ } else { ++ if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ retval = 1; ++ } else { ++ retval = dwc_otg_hc_continue_transfer(hcd->core_if, hc); ++ } ++ } ++ ++ return retval; ++} ++ ++/** ++ * Processes active non-periodic channels and queues transactions for these ++ * channels to the DWC_otg controller. After queueing transactions, the NP Tx ++ * FIFO Empty interrupt is enabled if there are more transactions to queue as ++ * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx ++ * FIFO Empty interrupt is disabled. ++ */ ++static void process_non_periodic_channels(dwc_otg_hcd_t *hcd) ++{ ++ gnptxsts_data_t tx_status; ++ struct list_head *orig_qh_ptr; ++ dwc_otg_qh_t *qh; ++ int status; ++ int no_queue_space = 0; ++ int no_fifo_space = 0; ++ int more_to_do = 0; ++ ++ dwc_otg_core_global_regs_t *global_regs = hcd->core_if->core_global_regs; ++ ++ DWC_DEBUGPL(DBG_HCDV, "Queue non-periodic transactions\n"); ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_HCDV, " NP Tx Req Queue Space Avail (before queue): %d\n", ++ tx_status.b.nptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " NP Tx FIFO Space Avail (before queue): %d\n", ++ tx_status.b.nptxfspcavail); ++#endif ++ /* ++ * Keep track of the starting point. Skip over the start-of-list ++ * entry. ++ */ ++ if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) { ++ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; ++ } ++ orig_qh_ptr = hcd->non_periodic_qh_ptr; ++ ++ /* ++ * Process once through the active list or until no more space is ++ * available in the request queue or the Tx FIFO. ++ */ ++ do { ++ tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ if (!hcd->core_if->dma_enable && tx_status.b.nptxqspcavail == 0) { ++ no_queue_space = 1; ++ break; ++ } ++ ++ qh = list_entry(hcd->non_periodic_qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ status = queue_transaction(hcd, qh->channel, tx_status.b.nptxfspcavail); ++ ++ if (status > 0) { ++ more_to_do = 1; ++ } else if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } ++ ++ /* Advance to next QH, skipping start-of-list entry. */ ++ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; ++ if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) { ++ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; ++ } ++ ++ } while (hcd->non_periodic_qh_ptr != orig_qh_ptr); ++ ++ if (!hcd->core_if->dma_enable) { ++ gintmsk_data_t intr_mask = {.d32 = 0}; ++ intr_mask.b.nptxfempty = 1; ++ ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_HCDV, " NP Tx Req Queue Space Avail (after queue): %d\n", ++ tx_status.b.nptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " NP Tx FIFO Space Avail (after queue): %d\n", ++ tx_status.b.nptxfspcavail); ++#endif ++ if (more_to_do || no_queue_space || no_fifo_space) { ++ /* ++ * May need to queue more transactions as the request ++ * queue or Tx FIFO empties. Enable the non-periodic ++ * Tx FIFO empty interrupt. (Always use the half-empty ++ * level to ensure that new requests are loaded as ++ * soon as possible.) ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, 0, intr_mask.d32); ++ } else { ++ /* ++ * Disable the Tx FIFO empty interrupt since there are ++ * no more transactions that need to be queued right ++ * now. This function is called from interrupt ++ * handlers to queue more transactions as transfer ++ * states change. ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, 0); ++ } ++ } ++} ++ ++/** ++ * Processes periodic channels for the next frame and queues transactions for ++ * these channels to the DWC_otg controller. After queueing transactions, the ++ * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions ++ * to queue as Periodic Tx FIFO or request queue space becomes available. ++ * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled. ++ */ ++static void process_periodic_channels(dwc_otg_hcd_t *hcd) ++{ ++ hptxsts_data_t tx_status; ++ struct list_head *qh_ptr; ++ dwc_otg_qh_t *qh; ++ int status; ++ int no_queue_space = 0; ++ int no_fifo_space = 0; ++ ++ dwc_otg_host_global_regs_t *host_regs; ++ host_regs = hcd->core_if->host_if->host_global_regs; ++ ++ DWC_DEBUGPL(DBG_HCDV, "Queue periodic transactions\n"); ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); ++ DWC_DEBUGPL(DBG_HCDV, " P Tx Req Queue Space Avail (before queue): %d\n", ++ tx_status.b.ptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " P Tx FIFO Space Avail (before queue): %d\n", ++ tx_status.b.ptxfspcavail); ++#endif ++ ++ qh_ptr = hcd->periodic_sched_assigned.next; ++ while (qh_ptr != &hcd->periodic_sched_assigned) { ++ tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); ++ if (tx_status.b.ptxqspcavail == 0) { ++ no_queue_space = 1; ++ break; ++ } ++ ++ qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ ++ /* ++ * Set a flag if we're queuing high-bandwidth in slave mode. ++ * The flag prevents any halts to get into the request queue in ++ * the middle of multiple high-bandwidth packets getting queued. ++ */ ++ if (!hcd->core_if->dma_enable && ++ qh->channel->multi_count > 1) ++ { ++ hcd->core_if->queuing_high_bandwidth = 1; ++ } ++ ++ status = queue_transaction(hcd, qh->channel, tx_status.b.ptxfspcavail); ++ if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } ++ ++ /* ++ * In Slave mode, stay on the current transfer until there is ++ * nothing more to do or the high-bandwidth request count is ++ * reached. In DMA mode, only need to queue one request. The ++ * controller automatically handles multiple packets for ++ * high-bandwidth transfers. ++ */ ++ if (hcd->core_if->dma_enable || status == 0 || ++ qh->channel->requests == qh->channel->multi_count) { ++ qh_ptr = qh_ptr->next; ++ /* ++ * Move the QH from the periodic assigned schedule to ++ * the periodic queued schedule. ++ */ ++ list_move(&qh->qh_list_entry, &hcd->periodic_sched_queued); ++ ++ /* done queuing high bandwidth */ ++ hcd->core_if->queuing_high_bandwidth = 0; ++ } ++ } ++ ++ if (!hcd->core_if->dma_enable) { ++ dwc_otg_core_global_regs_t *global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0}; ++ ++ global_regs = hcd->core_if->core_global_regs; ++ intr_mask.b.ptxfempty = 1; ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); ++ DWC_DEBUGPL(DBG_HCDV, " P Tx Req Queue Space Avail (after queue): %d\n", ++ tx_status.b.ptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " P Tx FIFO Space Avail (after queue): %d\n", ++ tx_status.b.ptxfspcavail); ++#endif ++ if (!list_empty(&hcd->periodic_sched_assigned) || ++ no_queue_space || no_fifo_space) { ++ /* ++ * May need to queue more transactions as the request ++ * queue or Tx FIFO empties. Enable the periodic Tx ++ * FIFO empty interrupt. (Always use the half-empty ++ * level to ensure that new requests are loaded as ++ * soon as possible.) ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, 0, intr_mask.d32); ++ } else { ++ /* ++ * Disable the Tx FIFO empty interrupt since there are ++ * no more transactions that need to be queued right ++ * now. This function is called from interrupt ++ * handlers to queue more transactions as transfer ++ * states change. ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, 0); ++ } ++ } ++} ++ ++/** ++ * This function processes the currently active host channels and queues ++ * transactions for these channels to the DWC_otg controller. It is called ++ * from HCD interrupt handler functions. ++ * ++ * @param hcd The HCD state structure. ++ * @param tr_type The type(s) of transactions to queue (non-periodic, ++ * periodic, or both). ++ */ ++void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t *hcd, ++ dwc_otg_transaction_type_e tr_type) ++{ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, "Queue Transactions\n"); ++#endif ++ /* Process host channels associated with periodic transfers. */ ++ if ((tr_type == DWC_OTG_TRANSACTION_PERIODIC || ++ tr_type == DWC_OTG_TRANSACTION_ALL) && ++ !list_empty(&hcd->periodic_sched_assigned)) { ++ ++ process_periodic_channels(hcd); ++ } ++ ++ /* Process host channels associated with non-periodic transfers. */ ++ if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC || ++ tr_type == DWC_OTG_TRANSACTION_ALL) { ++ if (!list_empty(&hcd->non_periodic_sched_active)) { ++ process_non_periodic_channels(hcd); ++ } else { ++ /* ++ * Ensure NP Tx FIFO empty interrupt is disabled when ++ * there are no non-periodic transfers to process. ++ */ ++ gintmsk_data_t gintmsk = {.d32 = 0}; ++ gintmsk.b.nptxfempty = 1; ++ dwc_modify_reg32(&hcd->core_if->core_global_regs->gintmsk, ++ gintmsk.d32, 0); ++ } ++ } ++} ++ ++/** ++ * Sets the final status of an URB and returns it to the device driver. Any ++ * required cleanup of the URB is performed. ++ */ ++void dwc_otg_hcd_complete_urb(dwc_otg_hcd_t *hcd, struct urb *urb, int status) ++{ ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ DWC_PRINT("%s: urb %p, device %d, ep %d %s, status=%d\n", ++ __func__, urb, usb_pipedevice(urb->pipe), ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "IN" : "OUT", status); ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ int i; ++ for (i = 0; i < urb->number_of_packets; i++) { ++ DWC_PRINT(" ISO Desc %d status: %d\n", ++ i, urb->iso_frame_desc[i].status); ++ } ++ } ++ } ++#endif ++ ++ urb->status = status; ++ urb->hcpriv = NULL; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status); ++#else ++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, NULL); ++#endif ++} ++ ++/* ++ * Returns the Queue Head for an URB. ++ */ ++dwc_otg_qh_t *dwc_urb_to_qh(struct urb *urb) ++{ ++ struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb); ++ return (dwc_otg_qh_t *)ep->hcpriv; ++} ++ ++#ifdef DEBUG ++void dwc_print_setup_data(uint8_t *setup) ++{ ++ int i; ++ if (CHK_DEBUG_LEVEL(DBG_HCD)){ ++ DWC_PRINT("Setup Data = MSB "); ++ for (i = 7; i >= 0; i--) DWC_PRINT("%02x ", setup[i]); ++ DWC_PRINT("\n"); ++ DWC_PRINT(" bmRequestType Tranfer = %s\n", (setup[0] & 0x80) ? "Device-to-Host" : "Host-to-Device"); ++ DWC_PRINT(" bmRequestType Type = "); ++ switch ((setup[0] & 0x60) >> 5) { ++ case 0: DWC_PRINT("Standard\n"); break; ++ case 1: DWC_PRINT("Class\n"); break; ++ case 2: DWC_PRINT("Vendor\n"); break; ++ case 3: DWC_PRINT("Reserved\n"); break; ++ } ++ DWC_PRINT(" bmRequestType Recipient = "); ++ switch (setup[0] & 0x1f) { ++ case 0: DWC_PRINT("Device\n"); break; ++ case 1: DWC_PRINT("Interface\n"); break; ++ case 2: DWC_PRINT("Endpoint\n"); break; ++ case 3: DWC_PRINT("Other\n"); break; ++ default: DWC_PRINT("Reserved\n"); break; ++ } ++ DWC_PRINT(" bRequest = 0x%0x\n", setup[1]); ++ DWC_PRINT(" wValue = 0x%0x\n", *((uint16_t *)&setup[2])); ++ DWC_PRINT(" wIndex = 0x%0x\n", *((uint16_t *)&setup[4])); ++ DWC_PRINT(" wLength = 0x%0x\n\n", *((uint16_t *)&setup[6])); ++ } ++} ++#endif ++ ++void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t *hcd) { ++#if defined(DEBUG) && LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ DWC_PRINT("Frame remaining at SOF:\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ hcd->frrem_samples, hcd->frrem_accum, ++ (hcd->frrem_samples > 0) ? ++ hcd->frrem_accum/hcd->frrem_samples : 0); ++ ++ DWC_PRINT("\n"); ++ DWC_PRINT("Frame remaining at start_transfer (uframe 7):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ hcd->core_if->hfnum_7_samples, hcd->core_if->hfnum_7_frrem_accum, ++ (hcd->core_if->hfnum_7_samples > 0) ? ++ hcd->core_if->hfnum_7_frrem_accum/hcd->core_if->hfnum_7_samples : 0); ++ DWC_PRINT("Frame remaining at start_transfer (uframe 0):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ hcd->core_if->hfnum_0_samples, hcd->core_if->hfnum_0_frrem_accum, ++ (hcd->core_if->hfnum_0_samples > 0) ? ++ hcd->core_if->hfnum_0_frrem_accum/hcd->core_if->hfnum_0_samples : 0); ++ DWC_PRINT("Frame remaining at start_transfer (uframe 1-6):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ hcd->core_if->hfnum_other_samples, hcd->core_if->hfnum_other_frrem_accum, ++ (hcd->core_if->hfnum_other_samples > 0) ? ++ hcd->core_if->hfnum_other_frrem_accum/hcd->core_if->hfnum_other_samples : 0); ++ ++ DWC_PRINT("\n"); ++ DWC_PRINT("Frame remaining at sample point A (uframe 7):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_7_samples_a, hcd->hfnum_7_frrem_accum_a, ++ (hcd->hfnum_7_samples_a > 0) ? ++ hcd->hfnum_7_frrem_accum_a/hcd->hfnum_7_samples_a : 0); ++ DWC_PRINT("Frame remaining at sample point A (uframe 0):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_0_samples_a, hcd->hfnum_0_frrem_accum_a, ++ (hcd->hfnum_0_samples_a > 0) ? ++ hcd->hfnum_0_frrem_accum_a/hcd->hfnum_0_samples_a : 0); ++ DWC_PRINT("Frame remaining at sample point A (uframe 1-6):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_other_samples_a, hcd->hfnum_other_frrem_accum_a, ++ (hcd->hfnum_other_samples_a > 0) ? ++ hcd->hfnum_other_frrem_accum_a/hcd->hfnum_other_samples_a : 0); ++ ++ DWC_PRINT("\n"); ++ DWC_PRINT("Frame remaining at sample point B (uframe 7):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_7_samples_b, hcd->hfnum_7_frrem_accum_b, ++ (hcd->hfnum_7_samples_b > 0) ? ++ hcd->hfnum_7_frrem_accum_b/hcd->hfnum_7_samples_b : 0); ++ DWC_PRINT("Frame remaining at sample point B (uframe 0):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_0_samples_b, hcd->hfnum_0_frrem_accum_b, ++ (hcd->hfnum_0_samples_b > 0) ? ++ hcd->hfnum_0_frrem_accum_b/hcd->hfnum_0_samples_b : 0); ++ DWC_PRINT("Frame remaining at sample point B (uframe 1-6):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_other_samples_b, hcd->hfnum_other_frrem_accum_b, ++ (hcd->hfnum_other_samples_b > 0) ? ++ hcd->hfnum_other_frrem_accum_b/hcd->hfnum_other_samples_b : 0); ++#endif ++} ++ ++void dwc_otg_hcd_dump_state(dwc_otg_hcd_t *hcd) ++{ ++#ifdef DEBUG ++ int num_channels; ++ int i; ++ gnptxsts_data_t np_tx_status; ++ hptxsts_data_t p_tx_status; ++ ++ num_channels = hcd->core_if->core_params->host_channels; ++ DWC_PRINT("\n"); ++ DWC_PRINT("************************************************************\n"); ++ DWC_PRINT("HCD State:\n"); ++ DWC_PRINT(" Num channels: %d\n", num_channels); ++ for (i = 0; i < num_channels; i++) { ++ dwc_hc_t *hc = hcd->hc_ptr_array[i]; ++ DWC_PRINT(" Channel %d:\n", i); ++ DWC_PRINT(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", ++ hc->dev_addr, hc->ep_num, hc->ep_is_in); ++ DWC_PRINT(" speed: %d\n", hc->speed); ++ DWC_PRINT(" ep_type: %d\n", hc->ep_type); ++ DWC_PRINT(" max_packet: %d\n", hc->max_packet); ++ DWC_PRINT(" data_pid_start: %d\n", hc->data_pid_start); ++ DWC_PRINT(" multi_count: %d\n", hc->multi_count); ++ DWC_PRINT(" xfer_started: %d\n", hc->xfer_started); ++ DWC_PRINT(" xfer_buff: %p\n", hc->xfer_buff); ++ DWC_PRINT(" xfer_len: %d\n", hc->xfer_len); ++ DWC_PRINT(" xfer_count: %d\n", hc->xfer_count); ++ DWC_PRINT(" halt_on_queue: %d\n", hc->halt_on_queue); ++ DWC_PRINT(" halt_pending: %d\n", hc->halt_pending); ++ DWC_PRINT(" halt_status: %d\n", hc->halt_status); ++ DWC_PRINT(" do_split: %d\n", hc->do_split); ++ DWC_PRINT(" complete_split: %d\n", hc->complete_split); ++ DWC_PRINT(" hub_addr: %d\n", hc->hub_addr); ++ DWC_PRINT(" port_addr: %d\n", hc->port_addr); ++ DWC_PRINT(" xact_pos: %d\n", hc->xact_pos); ++ DWC_PRINT(" requests: %d\n", hc->requests); ++ DWC_PRINT(" qh: %p\n", hc->qh); ++ if (hc->xfer_started) { ++ hfnum_data_t hfnum; ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ hfnum.d32 = dwc_read_reg32(&hcd->core_if->host_if->host_global_regs->hfnum); ++ hcchar.d32 = dwc_read_reg32(&hcd->core_if->host_if->hc_regs[i]->hcchar); ++ hctsiz.d32 = dwc_read_reg32(&hcd->core_if->host_if->hc_regs[i]->hctsiz); ++ hcint.d32 = dwc_read_reg32(&hcd->core_if->host_if->hc_regs[i]->hcint); ++ hcintmsk.d32 = dwc_read_reg32(&hcd->core_if->host_if->hc_regs[i]->hcintmsk); ++ DWC_PRINT(" hfnum: 0x%08x\n", hfnum.d32); ++ DWC_PRINT(" hcchar: 0x%08x\n", hcchar.d32); ++ DWC_PRINT(" hctsiz: 0x%08x\n", hctsiz.d32); ++ DWC_PRINT(" hcint: 0x%08x\n", hcint.d32); ++ DWC_PRINT(" hcintmsk: 0x%08x\n", hcintmsk.d32); ++ } ++ if (hc->xfer_started && hc->qh && hc->qh->qtd_in_process) { ++ dwc_otg_qtd_t *qtd; ++ struct urb *urb; ++ qtd = hc->qh->qtd_in_process; ++ urb = qtd->urb; ++ DWC_PRINT(" URB Info:\n"); ++ DWC_PRINT(" qtd: %p, urb: %p\n", qtd, urb); ++ if (urb) { ++ DWC_PRINT(" Dev: %d, EP: %d %s\n", ++ usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "IN" : "OUT"); ++ DWC_PRINT(" Max packet size: %d\n", ++ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); ++ DWC_PRINT(" transfer_buffer: %p\n", urb->transfer_buffer); ++ DWC_PRINT(" transfer_dma: %p\n", (void *)urb->transfer_dma); ++ DWC_PRINT(" transfer_buffer_length: %d\n", urb->transfer_buffer_length); ++ DWC_PRINT(" actual_length: %d\n", urb->actual_length); ++ } ++ } ++ } ++ DWC_PRINT(" non_periodic_channels: %d\n", hcd->non_periodic_channels); ++ DWC_PRINT(" periodic_channels: %d\n", hcd->periodic_channels); ++ DWC_PRINT(" periodic_usecs: %d\n", hcd->periodic_usecs); ++ np_tx_status.d32 = dwc_read_reg32(&hcd->core_if->core_global_regs->gnptxsts); ++ DWC_PRINT(" NP Tx Req Queue Space Avail: %d\n", np_tx_status.b.nptxqspcavail); ++ DWC_PRINT(" NP Tx FIFO Space Avail: %d\n", np_tx_status.b.nptxfspcavail); ++ p_tx_status.d32 = dwc_read_reg32(&hcd->core_if->host_if->host_global_regs->hptxsts); ++ DWC_PRINT(" P Tx Req Queue Space Avail: %d\n", p_tx_status.b.ptxqspcavail); ++ DWC_PRINT(" P Tx FIFO Space Avail: %d\n", p_tx_status.b.ptxfspcavail); ++ dwc_otg_hcd_dump_frrem(hcd); ++ dwc_otg_dump_global_registers(hcd->core_if); ++ dwc_otg_dump_host_registers(hcd->core_if); ++ DWC_PRINT("************************************************************\n"); ++ DWC_PRINT("\n"); ++#endif ++} ++#endif /* DWC_DEVICE_ONLY */ +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_hcd.h +@@ -0,0 +1,668 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.h $ ++ * $Revision: 1.3 $ ++ * $Date: 2008-12-15 06:51:32 $ ++ * $Change: 1064918 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++#ifndef __DWC_HCD_H__ ++#define __DWC_HCD_H__ ++ ++#include ++#include ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) ++#include ++#else ++#include <../drivers/usb/core/hcd.h> ++#endif ++ ++struct dwc_otg_device; ++ ++#include "dwc_otg_cil.h" ++ ++/** ++ * @file ++ * ++ * This file contains the structures, constants, and interfaces for ++ * the Host Contoller Driver (HCD). ++ * ++ * The Host Controller Driver (HCD) is responsible for translating requests ++ * from the USB Driver into the appropriate actions on the DWC_otg controller. ++ * It isolates the USBD from the specifics of the controller by providing an ++ * API to the USBD. ++ */ ++ ++/** ++ * Phases for control transfers. ++ */ ++typedef enum dwc_otg_control_phase { ++ DWC_OTG_CONTROL_SETUP, ++ DWC_OTG_CONTROL_DATA, ++ DWC_OTG_CONTROL_STATUS ++} dwc_otg_control_phase_e; ++ ++/** Transaction types. */ ++typedef enum dwc_otg_transaction_type { ++ DWC_OTG_TRANSACTION_NONE, ++ DWC_OTG_TRANSACTION_PERIODIC, ++ DWC_OTG_TRANSACTION_NON_PERIODIC, ++ DWC_OTG_TRANSACTION_ALL ++} dwc_otg_transaction_type_e; ++ ++/** ++ * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control, ++ * interrupt, or isochronous transfer. A single QTD is created for each URB ++ * (of one of these types) submitted to the HCD. The transfer associated with ++ * a QTD may require one or multiple transactions. ++ * ++ * A QTD is linked to a Queue Head, which is entered in either the ++ * non-periodic or periodic schedule for execution. When a QTD is chosen for ++ * execution, some or all of its transactions may be executed. After ++ * execution, the state of the QTD is updated. The QTD may be retired if all ++ * its transactions are complete or if an error occurred. Otherwise, it ++ * remains in the schedule so more transactions can be executed later. ++ */ ++typedef struct dwc_otg_qtd { ++ /** ++ * Determines the PID of the next data packet for the data phase of ++ * control transfers. Ignored for other transfer types.
++ * One of the following values: ++ * - DWC_OTG_HC_PID_DATA0 ++ * - DWC_OTG_HC_PID_DATA1 ++ */ ++ uint8_t data_toggle; ++ ++ /** Current phase for control transfers (Setup, Data, or Status). */ ++ dwc_otg_control_phase_e control_phase; ++ ++ /** Keep track of the current split type ++ * for FS/LS endpoints on a HS Hub */ ++ uint8_t complete_split; ++ ++ /** How many bytes transferred during SSPLIT OUT */ ++ uint32_t ssplit_out_xfer_count; ++ ++ /** ++ * Holds the number of bus errors that have occurred for a transaction ++ * within this transfer. ++ */ ++ uint8_t error_count; ++ ++ /** ++ * Index of the next frame descriptor for an isochronous transfer. A ++ * frame descriptor describes the buffer position and length of the ++ * data to be transferred in the next scheduled (micro)frame of an ++ * isochronous transfer. It also holds status for that transaction. ++ * The frame index starts at 0. ++ */ ++ int isoc_frame_index; ++ ++ /** Position of the ISOC split on full/low speed */ ++ uint8_t isoc_split_pos; ++ ++ /** Position of the ISOC split in the buffer for the current frame */ ++ uint16_t isoc_split_offset; ++ ++ /** URB for this transfer */ ++ struct urb *urb; ++ ++ /** This list of QTDs */ ++ struct list_head qtd_list_entry; ++ ++} dwc_otg_qtd_t; ++ ++/** ++ * A Queue Head (QH) holds the static characteristics of an endpoint and ++ * maintains a list of transfers (QTDs) for that endpoint. A QH structure may ++ * be entered in either the non-periodic or periodic schedule. ++ */ ++typedef struct dwc_otg_qh { ++ /** ++ * Endpoint type. ++ * One of the following values: ++ * - USB_ENDPOINT_XFER_CONTROL ++ * - USB_ENDPOINT_XFER_ISOC ++ * - USB_ENDPOINT_XFER_BULK ++ * - USB_ENDPOINT_XFER_INT ++ */ ++ uint8_t ep_type; ++ uint8_t ep_is_in; ++ ++ /** wMaxPacketSize Field of Endpoint Descriptor. */ ++ uint16_t maxp; ++ ++ /** ++ * Determines the PID of the next data packet for non-control ++ * transfers. Ignored for control transfers.
++ * One of the following values: ++ * - DWC_OTG_HC_PID_DATA0 ++ * - DWC_OTG_HC_PID_DATA1 ++ */ ++ uint8_t data_toggle; ++ ++ /** Ping state if 1. */ ++ uint8_t ping_state; ++ ++ /** ++ * List of QTDs for this QH. ++ */ ++ struct list_head qtd_list; ++ ++ /** Host channel currently processing transfers for this QH. */ ++ dwc_hc_t *channel; ++ ++ /** QTD currently assigned to a host channel for this QH. */ ++ dwc_otg_qtd_t *qtd_in_process; ++ ++ /** Full/low speed endpoint on high-speed hub requires split. */ ++ uint8_t do_split; ++ ++ /** @name Periodic schedule information */ ++ /** @{ */ ++ ++ /** Bandwidth in microseconds per (micro)frame. */ ++ uint8_t usecs; ++ ++ /** Interval between transfers in (micro)frames. */ ++ uint16_t interval; ++ ++ /** ++ * (micro)frame to initialize a periodic transfer. The transfer ++ * executes in the following (micro)frame. ++ */ ++ uint16_t sched_frame; ++ ++ /** (micro)frame at which last start split was initialized. */ ++ uint16_t start_split_frame; ++ ++ /** @} */ ++ ++ /** Entry for QH in either the periodic or non-periodic schedule. */ ++ struct list_head qh_list_entry; ++ ++ /* For non-dword aligned buffer support */ ++ uint8_t *dw_align_buf; ++ dma_addr_t dw_align_buf_dma; ++} dwc_otg_qh_t; ++ ++/** ++ * This structure holds the state of the HCD, including the non-periodic and ++ * periodic schedules. ++ */ ++typedef struct dwc_otg_hcd { ++ /** The DWC otg device pointer */ ++ struct dwc_otg_device *otg_dev; ++ ++ /** DWC OTG Core Interface Layer */ ++ dwc_otg_core_if_t *core_if; ++ ++ /** Internal DWC HCD Flags */ ++ volatile union dwc_otg_hcd_internal_flags { ++ uint32_t d32; ++ struct { ++ unsigned port_connect_status_change : 1; ++ unsigned port_connect_status : 1; ++ unsigned port_reset_change : 1; ++ unsigned port_enable_change : 1; ++ unsigned port_suspend_change : 1; ++ unsigned port_over_current_change : 1; ++ unsigned reserved : 27; ++ } b; ++ } flags; ++ ++ /** ++ * Inactive items in the non-periodic schedule. This is a list of ++ * Queue Heads. Transfers associated with these Queue Heads are not ++ * currently assigned to a host channel. ++ */ ++ struct list_head non_periodic_sched_inactive; ++ ++ /** ++ * Active items in the non-periodic schedule. This is a list of ++ * Queue Heads. Transfers associated with these Queue Heads are ++ * currently assigned to a host channel. ++ */ ++ struct list_head non_periodic_sched_active; ++ ++ /** ++ * Pointer to the next Queue Head to process in the active ++ * non-periodic schedule. ++ */ ++ struct list_head *non_periodic_qh_ptr; ++ ++ /** ++ * Inactive items in the periodic schedule. This is a list of QHs for ++ * periodic transfers that are _not_ scheduled for the next frame. ++ * Each QH in the list has an interval counter that determines when it ++ * needs to be scheduled for execution. This scheduling mechanism ++ * allows only a simple calculation for periodic bandwidth used (i.e. ++ * must assume that all periodic transfers may need to execute in the ++ * same frame). However, it greatly simplifies scheduling and should ++ * be sufficient for the vast majority of OTG hosts, which need to ++ * connect to a small number of peripherals at one time. ++ * ++ * Items move from this list to periodic_sched_ready when the QH ++ * interval counter is 0 at SOF. ++ */ ++ struct list_head periodic_sched_inactive; ++ ++ /** ++ * List of periodic QHs that are ready for execution in the next ++ * frame, but have not yet been assigned to host channels. ++ * ++ * Items move from this list to periodic_sched_assigned as host ++ * channels become available during the current frame. ++ */ ++ struct list_head periodic_sched_ready; ++ ++ /** ++ * List of periodic QHs to be executed in the next frame that are ++ * assigned to host channels. ++ * ++ * Items move from this list to periodic_sched_queued as the ++ * transactions for the QH are queued to the DWC_otg controller. ++ */ ++ struct list_head periodic_sched_assigned; ++ ++ /** ++ * List of periodic QHs that have been queued for execution. ++ * ++ * Items move from this list to either periodic_sched_inactive or ++ * periodic_sched_ready when the channel associated with the transfer ++ * is released. If the interval for the QH is 1, the item moves to ++ * periodic_sched_ready because it must be rescheduled for the next ++ * frame. Otherwise, the item moves to periodic_sched_inactive. ++ */ ++ struct list_head periodic_sched_queued; ++ ++ /** ++ * Total bandwidth claimed so far for periodic transfers. This value ++ * is in microseconds per (micro)frame. The assumption is that all ++ * periodic transfers may occur in the same (micro)frame. ++ */ ++ uint16_t periodic_usecs; ++ ++ /** ++ * Frame number read from the core at SOF. The value ranges from 0 to ++ * DWC_HFNUM_MAX_FRNUM. ++ */ ++ uint16_t frame_number; ++ ++ /** ++ * Free host channels in the controller. This is a list of ++ * dwc_hc_t items. ++ */ ++ struct list_head free_hc_list; ++ ++ /** ++ * Number of host channels assigned to periodic transfers. Currently ++ * assuming that there is a dedicated host channel for each periodic ++ * transaction and at least one host channel available for ++ * non-periodic transactions. ++ */ ++ int periodic_channels; ++ ++ /** ++ * Number of host channels assigned to non-periodic transfers. ++ */ ++ int non_periodic_channels; ++ ++ /** ++ * Array of pointers to the host channel descriptors. Allows accessing ++ * a host channel descriptor given the host channel number. This is ++ * useful in interrupt handlers. ++ */ ++ dwc_hc_t *hc_ptr_array[MAX_EPS_CHANNELS]; ++ ++ /** ++ * Buffer to use for any data received during the status phase of a ++ * control transfer. Normally no data is transferred during the status ++ * phase. This buffer is used as a bit bucket. ++ */ ++ uint8_t *status_buf; ++ ++ /** ++ * DMA address for status_buf. ++ */ ++ dma_addr_t status_buf_dma; ++#define DWC_OTG_HCD_STATUS_BUF_SIZE 64 ++ ++ /** ++ * Structure to allow starting the HCD in a non-interrupt context ++ * during an OTG role change. ++ */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ struct work_struct start_work; ++#else ++ struct delayed_work start_work; ++#endif ++ ++ /** ++ * Connection timer. An OTG host must display a message if the device ++ * does not connect. Started when the VBus power is turned on via ++ * sysfs attribute "buspower". ++ */ ++ struct timer_list conn_timer; ++ ++ /* Tasket to do a reset */ ++ struct tasklet_struct *reset_tasklet; ++ ++ /* */ ++ spinlock_t lock; ++ ++#ifdef DEBUG ++ uint32_t frrem_samples; ++ uint64_t frrem_accum; ++ ++ uint32_t hfnum_7_samples_a; ++ uint64_t hfnum_7_frrem_accum_a; ++ uint32_t hfnum_0_samples_a; ++ uint64_t hfnum_0_frrem_accum_a; ++ uint32_t hfnum_other_samples_a; ++ uint64_t hfnum_other_frrem_accum_a; ++ ++ uint32_t hfnum_7_samples_b; ++ uint64_t hfnum_7_frrem_accum_b; ++ uint32_t hfnum_0_samples_b; ++ uint64_t hfnum_0_frrem_accum_b; ++ uint32_t hfnum_other_samples_b; ++ uint64_t hfnum_other_frrem_accum_b; ++#endif ++} dwc_otg_hcd_t; ++ ++/** Gets the dwc_otg_hcd from a struct usb_hcd */ ++static inline dwc_otg_hcd_t *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd) ++{ ++ return (dwc_otg_hcd_t *)(hcd->hcd_priv); ++} ++ ++/** Gets the struct usb_hcd that contains a dwc_otg_hcd_t. */ ++static inline struct usb_hcd *dwc_otg_hcd_to_hcd(dwc_otg_hcd_t *dwc_otg_hcd) ++{ ++ return container_of((void *)dwc_otg_hcd, struct usb_hcd, hcd_priv); ++} ++ ++/** @name HCD Create/Destroy Functions */ ++/** @{ */ ++extern int dwc_otg_hcd_init(struct device *dev); ++extern void dwc_otg_hcd_remove(struct device *dev); ++/** @} */ ++ ++/** @name Linux HC Driver API Functions */ ++/** @{ */ ++ ++extern int dwc_otg_hcd_start(struct usb_hcd *hcd); ++extern void dwc_otg_hcd_stop(struct usb_hcd *hcd); ++extern int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd); ++extern void dwc_otg_hcd_free(struct usb_hcd *hcd); ++extern int dwc_otg_hcd_urb_enqueue(struct usb_hcd *hcd, ++ struct urb *urb, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ int mem_flags ++#else ++ gfp_t mem_flags ++#endif ++ ); ++extern int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++#endif ++ struct urb *urb, int status); ++extern void dwc_otg_hcd_endpoint_disable(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep); ++extern irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ , struct pt_regs *regs ++#endif ++ ); ++extern int dwc_otg_hcd_hub_status_data(struct usb_hcd *hcd, ++ char *buf); ++extern int dwc_otg_hcd_hub_control(struct usb_hcd *hcd, ++ u16 typeReq, ++ u16 wValue, ++ u16 wIndex, ++ char *buf, ++ u16 wLength); ++ ++/** @} */ ++ ++/** @name Transaction Execution Functions */ ++/** @{ */ ++extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t *hcd); ++extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t *hcd, ++ dwc_otg_transaction_type_e tr_type); ++extern void dwc_otg_hcd_complete_urb(dwc_otg_hcd_t *_hcd, struct urb *urb, ++ int status); ++/** @} */ ++ ++/** @name Interrupt Handler Functions */ ++/** @{ */ ++extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_incomplete_periodic_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_conn_id_status_change_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_disconnect_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t *dwc_otg_hcd, uint32_t num); ++extern int32_t dwc_otg_hcd_handle_session_req_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_wakeup_detected_intr(dwc_otg_hcd_t *dwc_otg_hcd); ++/** @} */ ++ ++ ++/** @name Schedule Queue Functions */ ++/** @{ */ ++ ++/* Implemented in dwc_otg_hcd_queue.c */ ++extern dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t *hcd, struct urb *urb); ++extern void dwc_otg_hcd_qh_init(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, struct urb *urb); ++extern void dwc_otg_hcd_qh_free(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh); ++extern int dwc_otg_hcd_qh_add(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh); ++extern void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh); ++extern void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, int sched_csplit); ++ ++/** Remove and free a QH */ ++static inline void dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd_t *hcd, ++ dwc_otg_qh_t *qh) ++{ ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ dwc_otg_hcd_qh_free(hcd, qh); ++} ++ ++/** Allocates memory for a QH structure. ++ * @return Returns the memory allocate or NULL on error. */ ++static inline dwc_otg_qh_t *dwc_otg_hcd_qh_alloc(void) ++{ ++ return (dwc_otg_qh_t *) kmalloc(sizeof(dwc_otg_qh_t), GFP_KERNEL); ++} ++ ++extern dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(struct urb *urb); ++extern void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t *qtd, struct urb *urb); ++extern int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t *qtd, dwc_otg_hcd_t *dwc_otg_hcd); ++ ++/** Allocates memory for a QTD structure. ++ * @return Returns the memory allocate or NULL on error. */ ++static inline dwc_otg_qtd_t *dwc_otg_hcd_qtd_alloc(void) ++{ ++ return (dwc_otg_qtd_t *) kmalloc(sizeof(dwc_otg_qtd_t), GFP_KERNEL); ++} ++ ++/** Frees the memory for a QTD structure. QTD should already be removed from ++ * list. ++ * @param[in] qtd QTD to free.*/ ++static inline void dwc_otg_hcd_qtd_free(dwc_otg_qtd_t *qtd) ++{ ++ kfree(qtd); ++} ++ ++/** Removes a QTD from list. ++ * @param[in] hcd HCD instance. ++ * @param[in] qtd QTD to remove from list. */ ++static inline void dwc_otg_hcd_qtd_remove(dwc_otg_hcd_t *hcd, dwc_otg_qtd_t *qtd) ++{ ++ unsigned long flags; ++ SPIN_LOCK_IRQSAVE(&hcd->lock, flags); ++ list_del(&qtd->qtd_list_entry); ++ SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags); ++} ++ ++/** Remove and free a QTD */ ++static inline void dwc_otg_hcd_qtd_remove_and_free(dwc_otg_hcd_t *hcd, dwc_otg_qtd_t *qtd) ++{ ++ dwc_otg_hcd_qtd_remove(hcd, qtd); ++ dwc_otg_hcd_qtd_free(qtd); ++} ++ ++/** @} */ ++ ++ ++/** @name Internal Functions */ ++/** @{ */ ++dwc_otg_qh_t *dwc_urb_to_qh(struct urb *urb); ++void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t *hcd); ++void dwc_otg_hcd_dump_state(dwc_otg_hcd_t *hcd); ++/** @} */ ++ ++/** Gets the usb_host_endpoint associated with an URB. */ ++static inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *urb) ++{ ++ struct usb_device *dev = urb->dev; ++ int ep_num = usb_pipeendpoint(urb->pipe); ++ ++ if (usb_pipein(urb->pipe)) ++ return dev->ep_in[ep_num]; ++ else ++ return dev->ep_out[ep_num]; ++} ++ ++/** ++ * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is ++ * qualified with its direction (possible 32 endpoints per device). ++ */ ++#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \ ++ ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4) ++ ++/** Gets the QH that contains the list_head */ ++#define dwc_list_to_qh(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qh_t, qh_list_entry) ++ ++/** Gets the QTD that contains the list_head */ ++#define dwc_list_to_qtd(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qtd_t, qtd_list_entry) ++ ++/** Check if QH is non-periodic */ ++#define dwc_qh_is_non_per(_qh_ptr_) ((_qh_ptr_->ep_type == USB_ENDPOINT_XFER_BULK) || \ ++ (_qh_ptr_->ep_type == USB_ENDPOINT_XFER_CONTROL)) ++ ++/** High bandwidth multiplier as encoded in highspeed endpoint descriptors */ ++#define dwc_hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) ++ ++/** Packet size for any kind of endpoint descriptor */ ++#define dwc_max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) ++ ++/** ++ * Returns true if _frame1 is less than or equal to _frame2. The comparison is ++ * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the ++ * frame number when the max frame number is reached. ++ */ ++static inline int dwc_frame_num_le(uint16_t frame1, uint16_t frame2) ++{ ++ return ((frame2 - frame1) & DWC_HFNUM_MAX_FRNUM) <= ++ (DWC_HFNUM_MAX_FRNUM >> 1); ++} ++ ++/** ++ * Returns true if _frame1 is greater than _frame2. The comparison is done ++ * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the frame ++ * number when the max frame number is reached. ++ */ ++static inline int dwc_frame_num_gt(uint16_t frame1, uint16_t frame2) ++{ ++ return (frame1 != frame2) && ++ (((frame1 - frame2) & DWC_HFNUM_MAX_FRNUM) < ++ (DWC_HFNUM_MAX_FRNUM >> 1)); ++} ++ ++/** ++ * Increments _frame by the amount specified by _inc. The addition is done ++ * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value. ++ */ ++static inline uint16_t dwc_frame_num_inc(uint16_t frame, uint16_t inc) ++{ ++ return (frame + inc) & DWC_HFNUM_MAX_FRNUM; ++} ++ ++static inline uint16_t dwc_full_frame_num(uint16_t frame) ++{ ++ return (frame & DWC_HFNUM_MAX_FRNUM) >> 3; ++} ++ ++static inline uint16_t dwc_micro_frame_num(uint16_t frame) ++{ ++ return frame & 0x7; ++} ++ ++#ifdef DEBUG ++/** ++ * Macro to sample the remaining PHY clocks left in the current frame. This ++ * may be used during debugging to determine the average time it takes to ++ * execute sections of code. There are two possible sample points, "a" and ++ * "b", so the _letter argument must be one of these values. ++ * ++ * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For ++ * example, "cat /sys/devices/lm0/hcd_frrem". ++ */ ++#define dwc_sample_frrem(_hcd, _qh, _letter) \ ++{ \ ++ hfnum_data_t hfnum; \ ++ dwc_otg_qtd_t *qtd; \ ++ qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); \ ++ if (usb_pipeint(qtd->urb->pipe) && _qh->start_split_frame != 0 && !qtd->complete_split) { \ ++ hfnum.d32 = dwc_read_reg32(&_hcd->core_if->host_if->host_global_regs->hfnum); \ ++ switch (hfnum.b.frnum & 0x7) { \ ++ case 7: \ ++ _hcd->hfnum_7_samples_##_letter++; \ ++ _hcd->hfnum_7_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ case 0: \ ++ _hcd->hfnum_0_samples_##_letter++; \ ++ _hcd->hfnum_0_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ default: \ ++ _hcd->hfnum_other_samples_##_letter++; \ ++ _hcd->hfnum_other_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ } \ ++ } \ ++} ++#else ++#define dwc_sample_frrem(_hcd, _qh, _letter) ++#endif ++#endif ++#endif /* DWC_DEVICE_ONLY */ +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c +@@ -0,0 +1,1873 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_intr.c $ ++ * $Revision: 1.6.2.1 $ ++ * $Date: 2009-04-22 03:48:22 $ ++ * $Change: 1117667 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++#include ++ ++#include "dwc_otg_driver.h" ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++/** @file ++ * This file contains the implementation of the HCD Interrupt handlers. ++ */ ++ ++/** This function handles interrupts for the HCD. */ ++int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t *dwc_otg_hcd) ++{ ++ int retval = 0; ++ ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ gintsts_data_t gintsts; ++#ifdef DEBUG ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++#endif ++ ++ /* Check if HOST Mode */ ++ if (dwc_otg_is_host_mode(core_if)) { ++ gintsts.d32 = dwc_otg_read_core_intr(core_if); ++ if (!gintsts.d32) { ++ return 0; ++ } ++ ++#ifdef DEBUG ++ /* Don't print debug message in the interrupt handler on SOF */ ++# ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++# endif ++ DWC_DEBUGPL(DBG_HCD, "\n"); ++#endif ++ ++#ifdef DEBUG ++# ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++# endif ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x\n", gintsts.d32); ++#endif ++ if (gintsts.b.usbreset) { ++ DWC_PRINT("Usb Reset In Host Mode\n"); ++ } ++ ++ ++ if (gintsts.b.sofintr) { ++ retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); ++ } ++ if (gintsts.b.rxstsqlvl) { ++ retval |= dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd); ++ } ++ if (gintsts.b.nptxfempty) { ++ retval |= dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd); ++ } ++ if (gintsts.b.i2cintr) { ++ /** @todo Implement i2cintr handler. */ ++ } ++ if (gintsts.b.portintr) { ++ retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd); ++ } ++ if (gintsts.b.hcintr) { ++ retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd); ++ } ++ if (gintsts.b.ptxfempty) { ++ retval |= dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd); ++ } ++#ifdef DEBUG ++# ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++# endif ++ { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Finished Servicing Interrupts\n"); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintsts=0x%08x\n", ++ dwc_read_reg32(&global_regs->gintsts)); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintmsk=0x%08x\n", ++ dwc_read_reg32(&global_regs->gintmsk)); ++ } ++#endif ++ ++#ifdef DEBUG ++# ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++# endif ++ DWC_DEBUGPL(DBG_HCD, "\n"); ++#endif ++ ++ } ++ ++ S3C2410X_CLEAR_EINTPEND(); ++ ++ return retval; ++} ++ ++#ifdef DWC_TRACK_MISSED_SOFS ++#warning Compiling code to track missed SOFs ++#define FRAME_NUM_ARRAY_SIZE 1000 ++/** ++ * This function is for debug only. ++ */ ++static inline void track_missed_sofs(uint16_t curr_frame_number) ++{ ++ static uint16_t frame_num_array[FRAME_NUM_ARRAY_SIZE]; ++ static uint16_t last_frame_num_array[FRAME_NUM_ARRAY_SIZE]; ++ static int frame_num_idx = 0; ++ static uint16_t last_frame_num = DWC_HFNUM_MAX_FRNUM; ++ static int dumped_frame_num_array = 0; ++ ++ if (frame_num_idx < FRAME_NUM_ARRAY_SIZE) { ++ if (((last_frame_num + 1) & DWC_HFNUM_MAX_FRNUM) != curr_frame_number) { ++ frame_num_array[frame_num_idx] = curr_frame_number; ++ last_frame_num_array[frame_num_idx++] = last_frame_num; ++ } ++ } else if (!dumped_frame_num_array) { ++ int i; ++ printk(KERN_EMERG USB_DWC "Frame Last Frame\n"); ++ printk(KERN_EMERG USB_DWC "----- ----------\n"); ++ for (i = 0; i < FRAME_NUM_ARRAY_SIZE; i++) { ++ printk(KERN_EMERG USB_DWC "0x%04x 0x%04x\n", ++ frame_num_array[i], last_frame_num_array[i]); ++ } ++ dumped_frame_num_array = 1; ++ } ++ last_frame_num = curr_frame_number; ++} ++#endif ++ ++/** ++ * Handles the start-of-frame interrupt in host mode. Non-periodic ++ * transactions may be queued to the DWC_otg controller for the current ++ * (micro)frame. Periodic transactions may be queued to the controller for the ++ * next (micro)frame. ++ */ ++int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t *hcd) ++{ ++ hfnum_data_t hfnum; ++ struct list_head *qh_entry; ++ dwc_otg_qh_t *qh; ++ dwc_otg_transaction_type_e tr_type; ++ gintsts_data_t gintsts = {.d32 = 0}; ++ ++ hfnum.d32 = dwc_read_reg32(&hcd->core_if->host_if->host_global_regs->hfnum); ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, "--Start of Frame Interrupt--\n"); ++#endif ++ hcd->frame_number = hfnum.b.frnum; ++ ++#ifdef DEBUG ++ hcd->frrem_accum += hfnum.b.frrem; ++ hcd->frrem_samples++; ++#endif ++ ++#ifdef DWC_TRACK_MISSED_SOFS ++ track_missed_sofs(hcd->frame_number); ++#endif ++ ++ /* Determine whether any periodic QHs should be executed. */ ++ qh_entry = hcd->periodic_sched_inactive.next; ++ while (qh_entry != &hcd->periodic_sched_inactive) { ++ qh = list_entry(qh_entry, dwc_otg_qh_t, qh_list_entry); ++ qh_entry = qh_entry->next; ++ if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) { ++ /* ++ * Move QH to the ready list to be executed next ++ * (micro)frame. ++ */ ++ list_move(&qh->qh_list_entry, &hcd->periodic_sched_ready); ++ } ++ } ++ ++ tr_type = dwc_otg_hcd_select_transactions(hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE) { ++ dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ } ++ ++ /* Clear interrupt */ ++ gintsts.b.sofintr = 1; ++ dwc_write_reg32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** Handles the Rx Status Queue Level Interrupt, which indicates that there is at ++ * least one packet in the Rx FIFO. The packets are moved from the FIFO to ++ * memory if the DWC_otg controller is operating in Slave mode. */ ++int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t *dwc_otg_hcd) ++{ ++ host_grxsts_data_t grxsts; ++ dwc_hc_t *hc = NULL; ++ ++ DWC_DEBUGPL(DBG_HCD, "--RxStsQ Level Interrupt--\n"); ++ ++ grxsts.d32 = dwc_read_reg32(&dwc_otg_hcd->core_if->core_global_regs->grxstsp); ++ ++ hc = dwc_otg_hcd->hc_ptr_array[grxsts.b.chnum]; ++ ++ /* Packet Status */ ++ DWC_DEBUGPL(DBG_HCDV, " Ch num = %d\n", grxsts.b.chnum); ++ DWC_DEBUGPL(DBG_HCDV, " Count = %d\n", grxsts.b.bcnt); ++ DWC_DEBUGPL(DBG_HCDV, " DPID = %d, hc.dpid = %d\n", grxsts.b.dpid, hc->data_pid_start); ++ DWC_DEBUGPL(DBG_HCDV, " PStatus = %d\n", grxsts.b.pktsts); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN: ++ /* Read the data into the host buffer. */ ++ if (grxsts.b.bcnt > 0) { ++ dwc_otg_read_packet(dwc_otg_hcd->core_if, ++ hc->xfer_buff, ++ grxsts.b.bcnt); ++ ++ /* Update the HC fields for the next packet received. */ ++ hc->xfer_count += grxsts.b.bcnt; ++ hc->xfer_buff += grxsts.b.bcnt; ++ } ++ ++ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP: ++ case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR: ++ case DWC_GRXSTS_PKTSTS_CH_HALTED: ++ /* Handled in interrupt, just ignore data */ ++ break; ++ default: ++ DWC_ERROR("RX_STS_Q Interrupt: Unknown status %d\n", grxsts.b.pktsts); ++ break; ++ } ++ ++ return 1; ++} ++ ++/** This interrupt occurs when the non-periodic Tx FIFO is half-empty. More ++ * data packets may be written to the FIFO for OUT transfers. More requests ++ * may be written to the non-periodic request queue for IN transfers. This ++ * interrupt is enabled only in Slave mode. */ ++int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t *dwc_otg_hcd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Non-Periodic TxFIFO Empty Interrupt--\n"); ++ dwc_otg_hcd_queue_transactions(dwc_otg_hcd, ++ DWC_OTG_TRANSACTION_NON_PERIODIC); ++ return 1; ++} ++ ++/** This interrupt occurs when the periodic Tx FIFO is half-empty. More data ++ * packets may be written to the FIFO for OUT transfers. More requests may be ++ * written to the periodic request queue for IN transfers. This interrupt is ++ * enabled only in Slave mode. */ ++int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t *dwc_otg_hcd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Periodic TxFIFO Empty Interrupt--\n"); ++ dwc_otg_hcd_queue_transactions(dwc_otg_hcd, ++ DWC_OTG_TRANSACTION_PERIODIC); ++ return 1; ++} ++ ++/** There are multiple conditions that can cause a port interrupt. This function ++ * determines which interrupt conditions have occurred and handles them ++ * appropriately. */ ++int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t *dwc_otg_hcd) ++{ ++ int retval = 0; ++ hprt0_data_t hprt0; ++ hprt0_data_t hprt0_modify; ++ ++ hprt0.d32 = dwc_read_reg32(dwc_otg_hcd->core_if->host_if->hprt0); ++ hprt0_modify.d32 = dwc_read_reg32(dwc_otg_hcd->core_if->host_if->hprt0); ++ ++ /* Clear appropriate bits in HPRT0 to clear the interrupt bit in ++ * GINTSTS */ ++ ++ hprt0_modify.b.prtena = 0; ++ hprt0_modify.b.prtconndet = 0; ++ hprt0_modify.b.prtenchng = 0; ++ hprt0_modify.b.prtovrcurrchng = 0; ++ ++ /* Port Connect Detected ++ * Set flag and clear if detected */ ++ if (hprt0.b.prtconndet) { ++ DWC_DEBUGPL(DBG_HCD, "--Port Interrupt HPRT0=0x%08x " ++ "Port Connect Detected--\n", hprt0.d32); ++ dwc_otg_hcd->flags.b.port_connect_status_change = 1; ++ dwc_otg_hcd->flags.b.port_connect_status = 1; ++ hprt0_modify.b.prtconndet = 1; ++ ++ /* B-Device has connected, Delete the connection timer. */ ++ del_timer( &dwc_otg_hcd->conn_timer ); ++ ++ /* The Hub driver asserts a reset when it sees port connect ++ * status change flag */ ++ retval |= 1; ++ } ++ ++ /* Port Enable Changed ++ * Clear if detected - Set internal flag if disabled */ ++ if (hprt0.b.prtenchng) { ++ DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " ++ "Port Enable Changed--\n", hprt0.d32); ++ hprt0_modify.b.prtenchng = 1; ++ if (hprt0.b.prtena == 1) { ++ int do_reset = 0; ++ dwc_otg_core_params_t *params = dwc_otg_hcd->core_if->core_params; ++ dwc_otg_core_global_regs_t *global_regs = dwc_otg_hcd->core_if->core_global_regs; ++ dwc_otg_host_if_t *host_if = dwc_otg_hcd->core_if->host_if; ++ ++ /* Check if we need to adjust the PHY clock speed for ++ * low power and adjust it */ ++ if (params->host_support_fs_ls_low_power) { ++ gusbcfg_data_t usbcfg; ++ ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED || ++ hprt0.b.prtspd == DWC_HPRT0_PRTSPD_FULL_SPEED) { ++ /* ++ * Low power ++ */ ++ hcfg_data_t hcfg; ++ if (usbcfg.b.phylpwrclksel == 0) { ++ /* Set PHY low power clock select for FS/LS devices */ ++ usbcfg.b.phylpwrclksel = 1; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ do_reset = 1; ++ } ++ ++ hcfg.d32 = dwc_read_reg32(&host_if->host_global_regs->hcfg); ++ ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED && ++ params->host_ls_low_power_phy_clk == ++ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ) { ++ /* 6 MHZ */ ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY programming HCFG to 6 MHz (Low Power)\n"); ++ if (hcfg.b.fslspclksel != DWC_HCFG_6_MHZ) { ++ hcfg.b.fslspclksel = DWC_HCFG_6_MHZ; ++ dwc_write_reg32(&host_if->host_global_regs->hcfg, ++ hcfg.d32); ++ do_reset = 1; ++ } ++ } else { ++ /* 48 MHZ */ ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY programming HCFG to 48 MHz ()\n"); ++ if (hcfg.b.fslspclksel != DWC_HCFG_48_MHZ) { ++ hcfg.b.fslspclksel = DWC_HCFG_48_MHZ; ++ dwc_write_reg32(&host_if->host_global_regs->hcfg, ++ hcfg.d32); ++ do_reset = 1; ++ } ++ } ++ } else { ++ /* ++ * Not low power ++ */ ++ if (usbcfg.b.phylpwrclksel == 1) { ++ usbcfg.b.phylpwrclksel = 0; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ do_reset = 1; ++ } ++ } ++ ++ if (do_reset) { ++ tasklet_schedule(dwc_otg_hcd->reset_tasklet); ++ } ++ } ++ ++ if (!do_reset) { ++ /* Port has been enabled set the reset change flag */ ++ dwc_otg_hcd->flags.b.port_reset_change = 1; ++ } ++ } else { ++ dwc_otg_hcd->flags.b.port_enable_change = 1; ++ } ++ retval |= 1; ++ } ++ ++ /** Overcurrent Change Interrupt */ ++ if (hprt0.b.prtovrcurrchng) { ++ DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " ++ "Port Overcurrent Changed--\n", hprt0.d32); ++ dwc_otg_hcd->flags.b.port_over_current_change = 1; ++ hprt0_modify.b.prtovrcurrchng = 1; ++ retval |= 1; ++ } ++ ++ /* Clear Port Interrupts */ ++ dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0_modify.d32); ++ ++ return retval; ++} ++ ++/** This interrupt indicates that one or more host channels has a pending ++ * interrupt. There are multiple conditions that can cause each host channel ++ * interrupt. This function determines which conditions have occurred for each ++ * host channel interrupt and handles them appropriately. */ ++int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t *dwc_otg_hcd) ++{ ++ int i; ++ int retval = 0; ++ haint_data_t haint; ++ ++ /* Clear appropriate bits in HCINTn to clear the interrupt bit in ++ * GINTSTS */ ++ ++ haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); ++ ++ for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) { ++ if (haint.b2.chint & (1 << i)) { ++ retval |= dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd, i); ++ } ++ } ++ ++ return retval; ++} ++ ++/* Macro used to clear one channel interrupt */ ++#define clear_hc_int(_hc_regs_, _intr_) \ ++do { \ ++ hcint_data_t hcint_clear = {.d32 = 0}; \ ++ hcint_clear.b._intr_ = 1; \ ++ dwc_write_reg32(&(_hc_regs_)->hcint, hcint_clear.d32); \ ++} while (0) ++ ++/* ++ * Macro used to disable one channel interrupt. Channel interrupts are ++ * disabled when the channel is halted or released by the interrupt handler. ++ * There is no need to handle further interrupts of that type until the ++ * channel is re-assigned. In fact, subsequent handling may cause crashes ++ * because the channel structures are cleaned up when the channel is released. ++ */ ++#define disable_hc_int(_hc_regs_, _intr_) \ ++do { \ ++ hcintmsk_data_t hcintmsk = {.d32 = 0}; \ ++ hcintmsk.b._intr_ = 1; \ ++ dwc_modify_reg32(&(_hc_regs_)->hcintmsk, hcintmsk.d32, 0); \ ++} while (0) ++ ++/** ++ * Gets the actual length of a transfer after the transfer halts. _halt_status ++ * holds the reason for the halt. ++ * ++ * For IN transfers where halt_status is DWC_OTG_HC_XFER_COMPLETE, ++ * *short_read is set to 1 upon return if less than the requested ++ * number of bytes were transferred. Otherwise, *short_read is set to 0 upon ++ * return. short_read may also be NULL on entry, in which case it remains ++ * unchanged. ++ */ ++static uint32_t get_actual_xfer_length(dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd, ++ dwc_otg_halt_status_e halt_status, ++ int *short_read) ++{ ++ hctsiz_data_t hctsiz; ++ uint32_t length; ++ ++ if (short_read != NULL) { ++ *short_read = 0; ++ } ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ ++ if (halt_status == DWC_OTG_HC_XFER_COMPLETE) { ++ if (hc->ep_is_in) { ++ length = hc->xfer_len - hctsiz.b.xfersize; ++ if (short_read != NULL) { ++ *short_read = (hctsiz.b.xfersize != 0); ++ } ++ } else if (hc->qh->do_split) { ++ length = qtd->ssplit_out_xfer_count; ++ } else { ++ length = hc->xfer_len; ++ } ++ } else { ++ /* ++ * Must use the hctsiz.pktcnt field to determine how much data ++ * has been transferred. This field reflects the number of ++ * packets that have been transferred via the USB. This is ++ * always an integral number of packets if the transfer was ++ * halted before its normal completion. (Can't use the ++ * hctsiz.xfersize field because that reflects the number of ++ * bytes transferred via the AHB, not the USB). ++ */ ++ length = (hc->start_pkt_count - hctsiz.b.pktcnt) * hc->max_packet; ++ } ++ ++ return length; ++} ++ ++/** ++ * Updates the state of the URB after a Transfer Complete interrupt on the ++ * host channel. Updates the actual_length field of the URB based on the ++ * number of bytes transferred via the host channel. Sets the URB status ++ * if the data transfer is finished. ++ * ++ * @return 1 if the data transfer specified by the URB is completely finished, ++ * 0 otherwise. ++ */ ++static int update_urb_state_xfer_comp(dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ struct urb *urb, ++ dwc_otg_qtd_t *qtd) ++{ ++ int xfer_done = 0; ++ int short_read = 0; ++ int overflow_read=0; ++ uint32_t len = 0; ++ int max_packet; ++ ++ len = get_actual_xfer_length(hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_COMPLETE, ++ &short_read); ++ ++ /* Data overflow case: by Steven */ ++ if (len > urb->transfer_buffer_length) { ++ len = urb->transfer_buffer_length; ++ overflow_read = 1; ++ } ++ ++ /* non DWORD-aligned buffer case handling. */ ++ if (((uint32_t)hc->xfer_buff & 0x3) && len && hc->qh->dw_align_buf && hc->ep_is_in) { ++ memcpy(urb->transfer_buffer + urb->actual_length, hc->qh->dw_align_buf, len); ++ } ++ urb->actual_length +=len; ++ ++ max_packet = usb_maxpacket(urb->dev, urb->pipe, !usb_pipein(urb->pipe)); ++ if((len) && usb_pipebulk(urb->pipe) && ++ (urb->transfer_flags & URB_ZERO_PACKET) && ++ (urb->actual_length == urb->transfer_buffer_length) && ++ (!(urb->transfer_buffer_length % max_packet))) { ++ } else if (short_read || urb->actual_length == urb->transfer_buffer_length) { ++ xfer_done = 1; ++ if (short_read && (urb->transfer_flags & URB_SHORT_NOT_OK)) { ++ urb->status = -EREMOTEIO; ++ } else if (overflow_read) { ++ urb->status = -EOVERFLOW; ++ } else { ++ urb->status = 0; ++ } ++ } ++ ++#ifdef DEBUG ++ { ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n", ++ __func__, (hc->ep_is_in ? "IN" : "OUT"), hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " hc->xfer_len %d\n", hc->xfer_len); ++ DWC_DEBUGPL(DBG_HCDV, " hctsiz.xfersize %d\n", hctsiz.b.xfersize); ++ DWC_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n", ++ urb->transfer_buffer_length); ++ DWC_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n", urb->actual_length); ++ DWC_DEBUGPL(DBG_HCDV, " short_read %d, xfer_done %d\n", ++ short_read, xfer_done); ++ } ++#endif ++ ++ return xfer_done; ++} ++ ++/* ++ * Save the starting data toggle for the next transfer. The data toggle is ++ * saved in the QH for non-control transfers and it's saved in the QTD for ++ * control transfers. ++ */ ++static void save_data_toggle(dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ ++ if (hc->ep_type != DWC_OTG_EP_TYPE_CONTROL) { ++ dwc_otg_qh_t *qh = hc->qh; ++ if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) { ++ qh->data_toggle = DWC_OTG_HC_PID_DATA0; ++ } else { ++ qh->data_toggle = DWC_OTG_HC_PID_DATA1; ++ } ++ } else { ++ if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) { ++ qtd->data_toggle = DWC_OTG_HC_PID_DATA0; ++ } else { ++ qtd->data_toggle = DWC_OTG_HC_PID_DATA1; ++ } ++ } ++} ++ ++/** ++ * Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic ++ * QHs, removes the QH from the active non-periodic schedule. If any QTDs are ++ * still linked to the QH, the QH is added to the end of the inactive ++ * non-periodic schedule. For periodic QHs, removes the QH from the periodic ++ * schedule if no more QTDs are linked to the QH. ++ */ ++static void deactivate_qh(dwc_otg_hcd_t *hcd, ++ dwc_otg_qh_t *qh, ++ int free_qtd) ++{ ++ int continue_split = 0; ++ dwc_otg_qtd_t *qtd; ++ ++ DWC_DEBUGPL(DBG_HCDV, " %s(%p,%p,%d)\n", __func__, hcd, qh, free_qtd); ++ ++ qtd = list_entry(qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); ++ ++ if (qtd->complete_split) { ++ continue_split = 1; ++ } else if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID || ++ qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END) { ++ continue_split = 1; ++ } ++ ++ if (free_qtd) { ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd); ++ continue_split = 0; ++ } ++ ++ qh->channel = NULL; ++ qh->qtd_in_process = NULL; ++ dwc_otg_hcd_qh_deactivate(hcd, qh, continue_split); ++} ++ ++/** ++ * Updates the state of an Isochronous URB when the transfer is stopped for ++ * any reason. The fields of the current entry in the frame descriptor array ++ * are set based on the transfer state and the input _halt_status. Completes ++ * the Isochronous URB if all the URB frames have been completed. ++ * ++ * @return DWC_OTG_HC_XFER_COMPLETE if there are more frames remaining to be ++ * transferred in the URB. Otherwise return DWC_OTG_HC_XFER_URB_COMPLETE. ++ */ ++static dwc_otg_halt_status_e ++update_isoc_urb_state(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ struct urb *urb = qtd->urb; ++ dwc_otg_halt_status_e ret_val = halt_status; ++ struct usb_iso_packet_descriptor *frame_desc; ++ ++ frame_desc = &urb->iso_frame_desc[qtd->isoc_frame_index]; ++ switch (halt_status) { ++ case DWC_OTG_HC_XFER_COMPLETE: ++ frame_desc->status = 0; ++ frame_desc->actual_length = ++ get_actual_xfer_length(hc, hc_regs, qtd, ++ halt_status, NULL); ++ ++ /* non DWORD-aligned buffer case handling. */ ++ if (frame_desc->actual_length && ((uint32_t)hc->xfer_buff & 0x3) && ++ hc->qh->dw_align_buf && hc->ep_is_in) { ++ memcpy(urb->transfer_buffer + frame_desc->offset + qtd->isoc_split_offset, ++ hc->qh->dw_align_buf, frame_desc->actual_length); ++ ++ } ++ ++ break; ++ case DWC_OTG_HC_XFER_FRAME_OVERRUN: ++ printk("DWC_OTG_HC_XFER_FRAME_OVERRUN: %d\n", halt_status); ++ urb->error_count++; ++ if (hc->ep_is_in) { ++ frame_desc->status = -ENOSR; ++ } else { ++ frame_desc->status = -ECOMM; ++ } ++ frame_desc->actual_length = 0; ++ break; ++ case DWC_OTG_HC_XFER_BABBLE_ERR: ++ printk("DWC_OTG_HC_XFER_BABBLE_ERR: %d\n", halt_status); ++ urb->error_count++; ++ frame_desc->status = -EOVERFLOW; ++ /* Don't need to update actual_length in this case. */ ++ break; ++ case DWC_OTG_HC_XFER_XACT_ERR: ++ printk("DWC_OTG_HC_XFER_XACT_ERR: %d\n", halt_status); ++ urb->error_count++; ++ frame_desc->status = -EPROTO; ++ frame_desc->actual_length = ++ get_actual_xfer_length(hc, hc_regs, qtd, ++ halt_status, NULL); ++ ++ /* non DWORD-aligned buffer case handling. */ ++ if (frame_desc->actual_length && ((uint32_t)hc->xfer_buff & 0x3) && ++ hc->qh->dw_align_buf && hc->ep_is_in) { ++ memcpy(urb->transfer_buffer + frame_desc->offset + qtd->isoc_split_offset, ++ hc->qh->dw_align_buf, frame_desc->actual_length); ++ ++ } ++ break; ++ default: ++ ++ DWC_ERROR("%s: Unhandled _halt_status (%d)\n", __func__, ++ halt_status); ++ BUG(); ++ break; ++ } ++ ++ if (++qtd->isoc_frame_index == urb->number_of_packets) { ++ /* ++ * urb->status is not used for isoc transfers. ++ * The individual frame_desc statuses are used instead. ++ */ ++ dwc_otg_hcd_complete_urb(hcd, urb, 0); ++ ret_val = DWC_OTG_HC_XFER_URB_COMPLETE; ++ } else { ++ ret_val = DWC_OTG_HC_XFER_COMPLETE; ++ } ++ ++ return ret_val; ++} ++ ++/** ++ * Releases a host channel for use by other transfers. Attempts to select and ++ * queue more transactions since at least one host channel is available. ++ * ++ * @param hcd The HCD state structure. ++ * @param hc The host channel to release. ++ * @param qtd The QTD associated with the host channel. This QTD may be freed ++ * if the transfer is complete or an error has occurred. ++ * @param halt_status Reason the channel is being released. This status ++ * determines the actions taken by this function. ++ */ ++static void release_channel(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_qtd_t *qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ dwc_otg_transaction_type_e tr_type; ++ int free_qtd; ++ ++ DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d\n", ++ __func__, hc->hc_num, halt_status); ++ ++ switch (halt_status) { ++ case DWC_OTG_HC_XFER_URB_COMPLETE: ++ free_qtd = 1; ++ break; ++ case DWC_OTG_HC_XFER_AHB_ERR: ++ case DWC_OTG_HC_XFER_STALL: ++ case DWC_OTG_HC_XFER_BABBLE_ERR: ++ free_qtd = 1; ++ break; ++ case DWC_OTG_HC_XFER_XACT_ERR: ++ if (qtd->error_count >= 3) { ++ DWC_DEBUGPL(DBG_HCDV, " Complete URB with transaction error\n"); ++ free_qtd = 1; ++ qtd->urb->status = -EPROTO; ++ dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EPROTO); ++ } else { ++ free_qtd = 0; ++ } ++ break; ++ case DWC_OTG_HC_XFER_URB_DEQUEUE: ++ /* ++ * The QTD has already been removed and the QH has been ++ * deactivated. Don't want to do anything except release the ++ * host channel and try to queue more transfers. ++ */ ++ goto cleanup; ++ case DWC_OTG_HC_XFER_NO_HALT_STATUS: ++ DWC_ERROR("%s: No halt_status, channel %d\n", __func__, hc->hc_num); ++ free_qtd = 0; ++ break; ++ default: ++ free_qtd = 0; ++ break; ++ } ++ ++ deactivate_qh(hcd, hc->qh, free_qtd); ++ ++ cleanup: ++ /* ++ * Release the host channel for use by other transfers. The cleanup ++ * function clears the channel interrupt enables and conditions, so ++ * there's no need to clear the Channel Halted interrupt separately. ++ */ ++ dwc_otg_hc_cleanup(hcd->core_if, hc); ++ list_add_tail(&hc->hc_list_entry, &hcd->free_hc_list); ++ ++ switch (hc->ep_type) { ++ case DWC_OTG_EP_TYPE_CONTROL: ++ case DWC_OTG_EP_TYPE_BULK: ++ hcd->non_periodic_channels--; ++ break; ++ ++ default: ++ /* ++ * Don't release reservations for periodic channels here. ++ * That's done when a periodic transfer is descheduled (i.e. ++ * when the QH is removed from the periodic schedule). ++ */ ++ break; ++ } ++ ++ /* Try to queue more transfers now that there's a free channel. */ ++ tr_type = dwc_otg_hcd_select_transactions(hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE) { ++ dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ } ++} ++ ++/** ++ * Halts a host channel. If the channel cannot be halted immediately because ++ * the request queue is full, this function ensures that the FIFO empty ++ * interrupt for the appropriate queue is enabled so that the halt request can ++ * be queued when there is space in the request queue. ++ * ++ * This function may also be called in DMA mode. In that case, the channel is ++ * simply released since the core always halts the channel automatically in ++ * DMA mode. ++ */ ++static void halt_channel(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_qtd_t *qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ if (hcd->core_if->dma_enable) { ++ release_channel(hcd, hc, qtd, halt_status); ++ return; ++ } ++ ++ /* Slave mode processing... */ ++ dwc_otg_hc_halt(hcd->core_if, hc, halt_status); ++ ++ if (hc->halt_on_queue) { ++ gintmsk_data_t gintmsk = {.d32 = 0}; ++ dwc_otg_core_global_regs_t *global_regs; ++ global_regs = hcd->core_if->core_global_regs; ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || ++ hc->ep_type == DWC_OTG_EP_TYPE_BULK) { ++ /* ++ * Make sure the Non-periodic Tx FIFO empty interrupt ++ * is enabled so that the non-periodic schedule will ++ * be processed. ++ */ ++ gintmsk.b.nptxfempty = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } else { ++ /* ++ * Move the QH from the periodic queued schedule to ++ * the periodic assigned schedule. This allows the ++ * halt to be queued when the periodic schedule is ++ * processed. ++ */ ++ list_move(&hc->qh->qh_list_entry, ++ &hcd->periodic_sched_assigned); ++ ++ /* ++ * Make sure the Periodic Tx FIFO Empty interrupt is ++ * enabled so that the periodic schedule will be ++ * processed. ++ */ ++ gintmsk.b.ptxfempty = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } ++ } ++} ++ ++/** ++ * Performs common cleanup for non-periodic transfers after a Transfer ++ * Complete interrupt. This function should be called after any endpoint type ++ * specific handling is finished to release the host channel. ++ */ ++static void complete_non_periodic_xfer(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ hcint_data_t hcint; ++ ++ qtd->error_count = 0; ++ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ if (hcint.b.nyet) { ++ /* ++ * Got a NYET on the last transaction of the transfer. This ++ * means that the endpoint should be in the PING state at the ++ * beginning of the next transfer. ++ */ ++ hc->qh->ping_state = 1; ++ clear_hc_int(hc_regs, nyet); ++ } ++ ++ /* ++ * Always halt and release the host channel to make it available for ++ * more transfers. There may still be more phases for a control ++ * transfer or more data packets for a bulk transfer at this point, ++ * but the host channel is still halted. A channel will be reassigned ++ * to the transfer when the non-periodic schedule is processed after ++ * the channel is released. This allows transactions to be queued ++ * properly via dwc_otg_hcd_queue_transactions, which also enables the ++ * Tx FIFO Empty interrupt if necessary. ++ */ ++ if (hc->ep_is_in) { ++ /* ++ * IN transfers in Slave mode require an explicit disable to ++ * halt the channel. (In DMA mode, this call simply releases ++ * the channel.) ++ */ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } else { ++ /* ++ * The channel is automatically disabled by the core for OUT ++ * transfers in Slave mode. ++ */ ++ release_channel(hcd, hc, qtd, halt_status); ++ } ++} ++ ++/** ++ * Performs common cleanup for periodic transfers after a Transfer Complete ++ * interrupt. This function should be called after any endpoint type specific ++ * handling is finished to release the host channel. ++ */ ++static void complete_periodic_xfer(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ hctsiz_data_t hctsiz; ++ qtd->error_count = 0; ++ ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ if (!hc->ep_is_in || hctsiz.b.pktcnt == 0) { ++ /* Core halts channel in these cases. */ ++ release_channel(hcd, hc, qtd, halt_status); ++ } else { ++ /* Flush any outstanding requests from the Tx queue. */ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++} ++ ++/** ++ * Handles a host channel Transfer Complete interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_xfercomp_intr(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ int urb_xfer_done; ++ dwc_otg_halt_status_e halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ struct urb *urb = qtd->urb; ++ int pipe_type = usb_pipetype(urb->pipe); ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Transfer Complete--\n", hc->hc_num); ++ ++ /* ++ * Handle xfer complete on CSPLIT. ++ */ ++ if (hc->qh->do_split) { ++ qtd->complete_split = 0; ++ } ++ ++ /* Update the QTD and URB states. */ ++ switch (pipe_type) { ++ case PIPE_CONTROL: ++ switch (qtd->control_phase) { ++ case DWC_OTG_CONTROL_SETUP: ++ if (urb->transfer_buffer_length > 0) { ++ qtd->control_phase = DWC_OTG_CONTROL_DATA; ++ } else { ++ qtd->control_phase = DWC_OTG_CONTROL_STATUS; ++ } ++ DWC_DEBUGPL(DBG_HCDV, " Control setup transaction done\n"); ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ break; ++ case DWC_OTG_CONTROL_DATA: { ++ urb_xfer_done = update_urb_state_xfer_comp(hc, hc_regs, urb, qtd); ++ if (urb_xfer_done) { ++ qtd->control_phase = DWC_OTG_CONTROL_STATUS; ++ DWC_DEBUGPL(DBG_HCDV, " Control data transfer done\n"); ++ } else { ++ save_data_toggle(hc, hc_regs, qtd); ++ } ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ break; ++ } ++ case DWC_OTG_CONTROL_STATUS: ++ DWC_DEBUGPL(DBG_HCDV, " Control transfer complete\n"); ++ if (urb->status == -EINPROGRESS) { ++ urb->status = 0; ++ } ++ dwc_otg_hcd_complete_urb(hcd, urb, urb->status); ++ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE; ++ break; ++ } ++ ++ complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status); ++ break; ++ case PIPE_BULK: ++ DWC_DEBUGPL(DBG_HCDV, " Bulk transfer complete\n"); ++ urb_xfer_done = update_urb_state_xfer_comp(hc, hc_regs, urb, qtd); ++ if (urb_xfer_done) { ++ dwc_otg_hcd_complete_urb(hcd, urb, urb->status); ++ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE; ++ } else { ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ } ++ ++ save_data_toggle(hc, hc_regs, qtd); ++ complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status); ++ break; ++ case PIPE_INTERRUPT: ++ DWC_DEBUGPL(DBG_HCDV, " Interrupt transfer complete\n"); ++ update_urb_state_xfer_comp(hc, hc_regs, urb, qtd); ++ ++ /* ++ * Interrupt URB is done on the first transfer complete ++ * interrupt. ++ */ ++ dwc_otg_hcd_complete_urb(hcd, urb, urb->status); ++ save_data_toggle(hc, hc_regs, qtd); ++ complete_periodic_xfer(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_URB_COMPLETE); ++ break; ++ case PIPE_ISOCHRONOUS: ++ DWC_DEBUGPL(DBG_HCDV, " Isochronous transfer complete\n"); ++ if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_ALL) { ++ halt_status = update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_COMPLETE); ++ } ++ complete_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status); ++ break; ++ } ++ ++ disable_hc_int(hc_regs, xfercompl); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel STALL interrupt. This handler may be called in ++ * either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_stall_intr(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ struct urb *urb = qtd->urb; ++ int pipe_type = usb_pipetype(urb->pipe); ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "STALL Received--\n", hc->hc_num); ++ ++ if (pipe_type == PIPE_CONTROL) { ++ dwc_otg_hcd_complete_urb(hcd, urb, -EPIPE); ++ } ++ ++ if (pipe_type == PIPE_BULK || pipe_type == PIPE_INTERRUPT) { ++ dwc_otg_hcd_complete_urb(hcd, urb, -EPIPE); ++ /* ++ * USB protocol requires resetting the data toggle for bulk ++ * and interrupt endpoints when a CLEAR_FEATURE(ENDPOINT_HALT) ++ * setup command is issued to the endpoint. Anticipate the ++ * CLEAR_FEATURE command since a STALL has occurred and reset ++ * the data toggle now. ++ */ ++ hc->qh->data_toggle = 0; ++ } ++ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_STALL); ++ ++ disable_hc_int(hc_regs, stall); ++ ++ return 1; ++} ++ ++/* ++ * Updates the state of the URB when a transfer has been stopped due to an ++ * abnormal condition before the transfer completes. Modifies the ++ * actual_length field of the URB to reflect the number of bytes that have ++ * actually been transferred via the host channel. ++ */ ++static void update_urb_state_xfer_intr(dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ struct urb *urb, ++ dwc_otg_qtd_t *qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ uint32_t bytes_transferred = get_actual_xfer_length(hc, hc_regs, qtd, ++ halt_status, NULL); ++ urb->actual_length += bytes_transferred; ++ ++#ifdef DEBUG ++ { ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n", ++ __func__, (hc->ep_is_in ? "IN" : "OUT"), hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " hc->start_pkt_count %d\n", hc->start_pkt_count); ++ DWC_DEBUGPL(DBG_HCDV, " hctsiz.pktcnt %d\n", hctsiz.b.pktcnt); ++ DWC_DEBUGPL(DBG_HCDV, " hc->max_packet %d\n", hc->max_packet); ++ DWC_DEBUGPL(DBG_HCDV, " bytes_transferred %d\n", bytes_transferred); ++ DWC_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n", urb->actual_length); ++ DWC_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n", ++ urb->transfer_buffer_length); ++ } ++#endif ++} ++ ++/** ++ * Handles a host channel NAK interrupt. This handler may be called in either ++ * DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_nak_intr(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "NAK Received--\n", hc->hc_num); ++ ++ /* ++ * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and ++ * interrupt. Re-start the SSPLIT transfer. ++ */ ++ if (hc->do_split) { ++ if (hc->complete_split) { ++ qtd->error_count = 0; ++ } ++ qtd->complete_split = 0; ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK); ++ goto handle_nak_done; ++ } ++ ++ switch (usb_pipetype(qtd->urb->pipe)) { ++ case PIPE_CONTROL: ++ case PIPE_BULK: ++ if (hcd->core_if->dma_enable && hc->ep_is_in) { ++ /* ++ * NAK interrupts are enabled on bulk/control IN ++ * transfers in DMA mode for the sole purpose of ++ * resetting the error count after a transaction error ++ * occurs. The core will continue transferring data. ++ */ ++ qtd->error_count = 0; ++ goto handle_nak_done; ++ } ++ ++ /* ++ * NAK interrupts normally occur during OUT transfers in DMA ++ * or Slave mode. For IN transfers, more requests will be ++ * queued as request queue space is available. ++ */ ++ qtd->error_count = 0; ++ ++ if (!hc->qh->ping_state) { ++ update_urb_state_xfer_intr(hc, hc_regs, qtd->urb, ++ qtd, DWC_OTG_HC_XFER_NAK); ++ save_data_toggle(hc, hc_regs, qtd); ++ if (qtd->urb->dev->speed == USB_SPEED_HIGH) { ++ hc->qh->ping_state = 1; ++ } ++ } ++ ++ /* ++ * Halt the channel so the transfer can be re-started from ++ * the appropriate point or the PING protocol will ++ * start/continue. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK); ++ break; ++ case PIPE_INTERRUPT: ++ qtd->error_count = 0; ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK); ++ break; ++ case PIPE_ISOCHRONOUS: ++ /* Should never get called for isochronous transfers. */ ++ BUG(); ++ break; ++ } ++ ++ handle_nak_done: ++ disable_hc_int(hc_regs, nak); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel ACK interrupt. This interrupt is enabled when ++ * performing the PING protocol in Slave mode, when errors occur during ++ * either Slave mode or DMA mode, and during Start Split transactions. ++ */ ++static int32_t handle_hc_ack_intr(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "ACK Received--\n", hc->hc_num); ++ ++ if (hc->do_split) { ++ /* ++ * Handle ACK on SSPLIT. ++ * ACK should not occur in CSPLIT. ++ */ ++ if (!hc->ep_is_in && hc->data_pid_start != DWC_OTG_HC_PID_SETUP) { ++ qtd->ssplit_out_xfer_count = hc->xfer_len; ++ } ++ if (!(hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in)) { ++ /* Don't need complete for isochronous out transfers. */ ++ qtd->complete_split = 1; ++ } ++ ++ /* ISOC OUT */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) { ++ switch (hc->xact_pos) { ++ case DWC_HCSPLIT_XACTPOS_ALL: ++ break; ++ case DWC_HCSPLIT_XACTPOS_END: ++ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ qtd->isoc_split_offset = 0; ++ break; ++ case DWC_HCSPLIT_XACTPOS_BEGIN: ++ case DWC_HCSPLIT_XACTPOS_MID: ++ /* ++ * For BEGIN or MID, calculate the length for ++ * the next microframe to determine the correct ++ * SSPLIT token, either MID or END. ++ */ ++ { ++ struct usb_iso_packet_descriptor *frame_desc; ++ ++ frame_desc = &qtd->urb->iso_frame_desc[qtd->isoc_frame_index]; ++ qtd->isoc_split_offset += 188; ++ ++ if ((frame_desc->length - qtd->isoc_split_offset) <= 188) { ++ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_END; ++ } else { ++ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_MID; ++ } ++ ++ } ++ break; ++ } ++ } else { ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK); ++ } ++ } else { ++ qtd->error_count = 0; ++ ++ if (hc->qh->ping_state) { ++ hc->qh->ping_state = 0; ++ /* ++ * Halt the channel so the transfer can be re-started ++ * from the appropriate point. This only happens in ++ * Slave mode. In DMA mode, the ping_state is cleared ++ * when the transfer is started because the core ++ * automatically executes the PING, then the transfer. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK); ++ } ++ } ++ ++ /* ++ * If the ACK occurred when _not_ in the PING state, let the channel ++ * continue transferring data after clearing the error count. ++ */ ++ ++ disable_hc_int(hc_regs, ack); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel NYET interrupt. This interrupt should only occur on ++ * Bulk and Control OUT endpoints and for complete split transactions. If a ++ * NYET occurs at the same time as a Transfer Complete interrupt, it is ++ * handled in the xfercomp interrupt handler, not here. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "NYET Received--\n", hc->hc_num); ++ ++ /* ++ * NYET on CSPLIT ++ * re-do the CSPLIT immediately on non-periodic ++ */ ++ if (hc->do_split && hc->complete_split) { ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ int frnum = dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd)); ++ ++ if (dwc_full_frame_num(frnum) != ++ dwc_full_frame_num(hc->qh->sched_frame)) { ++ /* ++ * No longer in the same full speed frame. ++ * Treat this as a transaction error. ++ */ ++#if 0 ++ /** @todo Fix system performance so this can ++ * be treated as an error. Right now complete ++ * splits cannot be scheduled precisely enough ++ * due to other system activity, so this error ++ * occurs regularly in Slave mode. ++ */ ++ qtd->error_count++; ++#endif ++ qtd->complete_split = 0; ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ /** @todo add support for isoc release */ ++ goto handle_nyet_done; ++ } ++ } ++ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET); ++ goto handle_nyet_done; ++ } ++ ++ hc->qh->ping_state = 1; ++ qtd->error_count = 0; ++ ++ update_urb_state_xfer_intr(hc, hc_regs, qtd->urb, qtd, ++ DWC_OTG_HC_XFER_NYET); ++ save_data_toggle(hc, hc_regs, qtd); ++ ++ /* ++ * Halt the channel and re-start the transfer so the PING ++ * protocol will start. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET); ++ ++handle_nyet_done: ++ disable_hc_int(hc_regs, nyet); ++ return 1; ++} ++ ++/** ++ * Handles a host channel babble interrupt. This handler may be called in ++ * either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_babble_intr(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Babble Error--\n", hc->hc_num); ++ if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) { ++ dwc_otg_hcd_complete_urb(hcd, qtd->urb, -EOVERFLOW); ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_BABBLE_ERR); ++ } else { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_BABBLE_ERR); ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++ disable_hc_int(hc_regs, bblerr); ++ return 1; ++} ++ ++/** ++ * Handles a host channel AHB error interrupt. This handler is only called in ++ * DMA mode. ++ */ ++static int32_t handle_hc_ahberr_intr(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ uint32_t hcdma; ++ struct urb *urb = qtd->urb; ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "AHB Error--\n", hc->hc_num); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt); ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ hcdma = dwc_read_reg32(&hc_regs->hcdma); ++ ++ DWC_ERROR("AHB ERROR, Channel %d\n", hc->hc_num); ++ DWC_ERROR(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); ++ DWC_ERROR(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma); ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Enqueue\n"); ++ DWC_ERROR(" Device address: %d\n", usb_pipedevice(urb->pipe)); ++ DWC_ERROR(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe), ++ (usb_pipein(urb->pipe) ? "IN" : "OUT")); ++ DWC_ERROR(" Endpoint type: %s\n", ++ ({char *pipetype; ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: pipetype = "CONTROL"; break; ++ case PIPE_BULK: pipetype = "BULK"; break; ++ case PIPE_INTERRUPT: pipetype = "INTERRUPT"; break; ++ case PIPE_ISOCHRONOUS: pipetype = "ISOCHRONOUS"; break; ++ default: pipetype = "UNKNOWN"; break; ++ }; pipetype;})); ++ DWC_ERROR(" Speed: %s\n", ++ ({char *speed; ++ switch (urb->dev->speed) { ++ case USB_SPEED_HIGH: speed = "HIGH"; break; ++ case USB_SPEED_FULL: speed = "FULL"; break; ++ case USB_SPEED_LOW: speed = "LOW"; break; ++ default: speed = "UNKNOWN"; break; ++ }; speed;})); ++ DWC_ERROR(" Max packet size: %d\n", ++ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); ++ DWC_ERROR(" Data buffer length: %d\n", urb->transfer_buffer_length); ++ DWC_ERROR(" Transfer buffer: %p, Transfer DMA: %p\n", ++ urb->transfer_buffer, (void *)urb->transfer_dma); ++ DWC_ERROR(" Setup buffer: %p, Setup DMA: %p\n", ++ urb->setup_packet, (void *)urb->setup_dma); ++ DWC_ERROR(" Interval: %d\n", urb->interval); ++ ++ dwc_otg_hcd_complete_urb(hcd, urb, -EIO); ++ ++ /* ++ * Force a channel halt. Don't call halt_channel because that won't ++ * write to the HCCHARn register in DMA mode to force the halt. ++ */ ++ dwc_otg_hc_halt(hcd->core_if, hc, DWC_OTG_HC_XFER_AHB_ERR); ++ ++ disable_hc_int(hc_regs, ahberr); ++ return 1; ++} ++ ++/** ++ * Handles a host channel transaction error interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_xacterr_intr(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Transaction Error--\n", hc->hc_num); ++ ++ switch (usb_pipetype(qtd->urb->pipe)) { ++ case PIPE_CONTROL: ++ case PIPE_BULK: ++ qtd->error_count++; ++ if (!hc->qh->ping_state) { ++ update_urb_state_xfer_intr(hc, hc_regs, qtd->urb, ++ qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ save_data_toggle(hc, hc_regs, qtd); ++ if (!hc->ep_is_in && qtd->urb->dev->speed == USB_SPEED_HIGH) { ++ hc->qh->ping_state = 1; ++ } ++ } ++ ++ /* ++ * Halt the channel so the transfer can be re-started from ++ * the appropriate point or the PING protocol will start. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ break; ++ case PIPE_INTERRUPT: ++ qtd->error_count++; ++ if (hc->do_split && hc->complete_split) { ++ qtd->complete_split = 0; ++ } ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ break; ++ case PIPE_ISOCHRONOUS: ++ { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_XACT_ERR); ++ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++ break; ++ } ++ ++ disable_hc_int(hc_regs, xacterr); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel frame overrun interrupt. This handler may be called ++ * in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_frmovrun_intr(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Frame Overrun--\n", hc->hc_num); ++ ++ switch (usb_pipetype(qtd->urb->pipe)) { ++ case PIPE_CONTROL: ++ case PIPE_BULK: ++ break; ++ case PIPE_INTERRUPT: ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_FRAME_OVERRUN); ++ break; ++ case PIPE_ISOCHRONOUS: ++ { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_FRAME_OVERRUN); ++ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++ break; ++ } ++ ++ disable_hc_int(hc_regs, frmovrun); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel data toggle error interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Data Toggle Error--\n", hc->hc_num); ++ ++ if (hc->ep_is_in) { ++ qtd->error_count = 0; ++ } else { ++ DWC_ERROR("Data Toggle Error on OUT transfer," ++ "channel %d\n", hc->hc_num); ++ } ++ ++ disable_hc_int(hc_regs, datatglerr); ++ ++ return 1; ++} ++ ++#ifdef DEBUG ++/** ++ * This function is for debug only. It checks that a valid halt status is set ++ * and that HCCHARn.chdis is clear. If there's a problem, corrective action is ++ * taken and a warning is issued. ++ * @return 1 if halt status is ok, 0 otherwise. ++ */ ++static inline int halt_status_ok(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ hcsplt_data_t hcsplt; ++ ++ if (hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS) { ++ /* ++ * This code is here only as a check. This condition should ++ * never happen. Ignore the halt if it does occur. ++ */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ hcintmsk.d32 = dwc_read_reg32(&hc_regs->hcintmsk); ++ hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt); ++ DWC_WARN("%s: hc->halt_status == DWC_OTG" ++ "channel %d, hcchar 0x%08x, hctsiz 0x%08x, " ++ "hcint 0x%08x, hcintmsk 0x%08x, " ++ "hcsplt 0x%08x, qtd->complete_split %d\n", ++ __func__, hc->hc_num, hcchar.d32, hctsiz.d32, ++ hcint.d32, hcintmsk.d32, ++ hcsplt.d32, qtd->complete_split); ++ ++ DWC_WARN("%s: no halt status, channel %d, ignoring interrupt\n", ++ __func__, hc->hc_num); ++ DWC_WARN("\n"); ++ clear_hc_int(hc_regs, chhltd); ++ return 0; ++ } ++ ++ /* ++ * This code is here only as a check. hcchar.chdis should ++ * never be set when the halt interrupt occurs. Halt the ++ * channel again if it does occur. ++ */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: hcchar.chdis set unexpectedly, " ++ "hcchar 0x%08x, trying to halt again\n", ++ __func__, hcchar.d32); ++ clear_hc_int(hc_regs, chhltd); ++ hc->halt_pending = 0; ++ halt_channel(hcd, hc, qtd, hc->halt_status); ++ return 0; ++ } ++ ++ return 1; ++} ++#endif ++ ++/** ++ * Handles a host Channel Halted interrupt in DMA mode. This handler ++ * determines the reason the channel halted and proceeds accordingly. ++ */ ++static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ int out_nak_enh = 0; ++ ++ /* For core with OUT NAK enhancement, the flow for high- ++ * speed CONTROL/BULK OUT is handled a little differently. ++ */ ++ if (hcd->core_if->snpsid >= 0x4F54271A) { ++ if (hc->speed == DWC_OTG_EP_SPEED_HIGH && !hc->ep_is_in && ++ (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || ++ hc->ep_type == DWC_OTG_EP_TYPE_BULK)) { ++ printk(KERN_DEBUG "OUT NAK enhancement enabled\n"); ++ out_nak_enh = 1; ++ } else { ++ printk(KERN_DEBUG "OUT NAK enhancement disabled, not HS Ctrl/Bulk OUT EP\n"); ++ } ++ } else { ++// printk(KERN_DEBUG "OUT NAK enhancement disabled, no core support\n"); ++ } ++ ++ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE || ++ hc->halt_status == DWC_OTG_HC_XFER_AHB_ERR) { ++ /* ++ * Just release the channel. A dequeue can happen on a ++ * transfer timeout. In the case of an AHB Error, the channel ++ * was forced to halt because there's no way to gracefully ++ * recover. ++ */ ++ release_channel(hcd, hc, qtd, hc->halt_status); ++ return; ++ } ++ ++ /* Read the HCINTn register to determine the cause for the halt. */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ hcintmsk.d32 = dwc_read_reg32(&hc_regs->hcintmsk); ++ ++ if (hcint.b.xfercomp) { ++ /** @todo This is here because of a possible hardware bug. Spec ++ * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT ++ * interrupt w/ACK bit set should occur, but I only see the ++ * XFERCOMP bit, even with it masked out. This is a workaround ++ * for that behavior. Should fix this when hardware is fixed. ++ */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) { ++ handle_hc_ack_intr(hcd, hc, hc_regs, qtd); ++ } ++ handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.stall) { ++ handle_hc_stall_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.xacterr) { ++ if (out_nak_enh) { ++ if (hcint.b.nyet || hcint.b.nak || hcint.b.ack) { ++ printk(KERN_DEBUG "XactErr with NYET/NAK/ACK\n"); ++ qtd->error_count = 0; ++ } else { ++ printk(KERN_DEBUG "XactErr without NYET/NAK/ACK\n"); ++ } ++ } ++ ++ /* ++ * Must handle xacterr before nak or ack. Could get a xacterr ++ * at the same time as either of these on a BULK/CONTROL OUT ++ * that started with a PING. The xacterr takes precedence. ++ */ ++ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); ++ } else if (!out_nak_enh) { ++ if (hcint.b.nyet) { ++ /* ++ * Must handle nyet before nak or ack. Could get a nyet at the ++ * same time as either of those on a BULK/CONTROL OUT that ++ * started with a PING. The nyet takes precedence. ++ */ ++ handle_hc_nyet_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.bblerr) { ++ handle_hc_babble_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.frmovrun) { ++ handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.nak && !hcintmsk.b.nak) { ++ /* ++ * If nak is not masked, it's because a non-split IN transfer ++ * is in an error state. In that case, the nak is handled by ++ * the nak interrupt handler, not here. Handle nak here for ++ * BULK/CONTROL OUT transfers, which halt on a NAK to allow ++ * rewinding the buffer pointer. ++ */ ++ handle_hc_nak_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.ack && !hcintmsk.b.ack) { ++ /* ++ * If ack is not masked, it's because a non-split IN transfer ++ * is in an error state. In that case, the ack is handled by ++ * the ack interrupt handler, not here. Handle ack here for ++ * split transfers. Start splits halt on ACK. ++ */ ++ handle_hc_ack_intr(hcd, hc, hc_regs, qtd); ++ } else { ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * A periodic transfer halted with no other channel ++ * interrupts set. Assume it was halted by the core ++ * because it could not be completed in its scheduled ++ * (micro)frame. ++ */ ++#ifdef DEBUG ++ DWC_PRINT("%s: Halt channel %d (assume incomplete periodic transfer)\n", ++ __func__, hc->hc_num); ++#endif ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE); ++ } else { ++ DWC_ERROR("%s: Channel %d, DMA Mode -- ChHltd set, but reason " ++ "for halting is unknown, hcint 0x%08x, intsts 0x%08x\n", ++ __func__, hc->hc_num, hcint.d32, ++ dwc_read_reg32(&hcd->core_if->core_global_regs->gintsts)); ++ } ++ } ++ } else { ++ printk(KERN_DEBUG "NYET/NAK/ACK/other in non-error case, 0x%08x\n", hcint.d32); ++ } ++} ++ ++/** ++ * Handles a host channel Channel Halted interrupt. ++ * ++ * In slave mode, this handler is called only when the driver specifically ++ * requests a halt. This occurs during handling other host channel interrupts ++ * (e.g. nak, xacterr, stall, nyet, etc.). ++ * ++ * In DMA mode, this is the interrupt that occurs when the core has finished ++ * processing a transfer on a channel. Other host channel interrupts (except ++ * ahberr) are disabled in DMA mode. ++ */ ++static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_qtd_t *qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Channel Halted--\n", hc->hc_num); ++ ++ if (hcd->core_if->dma_enable) { ++ handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd); ++ } else { ++#ifdef DEBUG ++ if (!halt_status_ok(hcd, hc, hc_regs, qtd)) { ++ return 1; ++ } ++#endif ++ release_channel(hcd, hc, qtd, hc->halt_status); ++ } ++ ++ return 1; ++} ++ ++/** Handles interrupt for a specific Host Channel */ ++int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t *dwc_otg_hcd, uint32_t num) ++{ ++ int retval = 0; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ dwc_hc_t *hc; ++ dwc_otg_hc_regs_t *hc_regs; ++ dwc_otg_qtd_t *qtd; ++ ++ DWC_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", num); ++ ++ hc = dwc_otg_hcd->hc_ptr_array[num]; ++ hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num]; ++ qtd = list_entry(hc->qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); ++ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ hcintmsk.d32 = dwc_read_reg32(&hc_regs->hcintmsk); ++ DWC_DEBUGPL(DBG_HCDV, " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", ++ hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32)); ++ hcint.d32 = hcint.d32 & hcintmsk.d32; ++ ++ if (!dwc_otg_hcd->core_if->dma_enable) { ++ if (hcint.b.chhltd && hcint.d32 != 0x2) { ++ hcint.b.chhltd = 0; ++ } ++ } ++ ++ if (hcint.b.xfercomp) { ++ retval |= handle_hc_xfercomp_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ /* ++ * If NYET occurred at same time as Xfer Complete, the NYET is ++ * handled by the Xfer Complete interrupt handler. Don't want ++ * to call the NYET interrupt handler in this case. ++ */ ++ hcint.b.nyet = 0; ++ } ++ if (hcint.b.chhltd) { ++ retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.ahberr) { ++ retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.stall) { ++ retval |= handle_hc_stall_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.nak) { ++ retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.ack) { ++ retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.nyet) { ++ retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.xacterr) { ++ retval |= handle_hc_xacterr_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.bblerr) { ++ retval |= handle_hc_babble_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.frmovrun) { ++ retval |= handle_hc_frmovrun_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.datatglerr) { ++ retval |= handle_hc_datatglerr_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ ++ return retval; ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_hcd_queue.c +@@ -0,0 +1,684 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_hcd_queue.c $ ++ * $Revision: 1.5 $ ++ * $Date: 2008-12-15 06:51:32 $ ++ * $Change: 537387 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** ++ * @file ++ * ++ * This file contains the functions to manage Queue Heads and Queue ++ * Transfer Descriptors. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dwc_otg_driver.h" ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++/** ++ * This function allocates and initializes a QH. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param[in] urb Holds the information about the device/endpoint that we need ++ * to initialize the QH. ++ * ++ * @return Returns pointer to the newly allocated QH, or NULL on error. */ ++dwc_otg_qh_t *dwc_otg_hcd_qh_create (dwc_otg_hcd_t *hcd, struct urb *urb) ++{ ++ dwc_otg_qh_t *qh; ++ ++ /* Allocate memory */ ++ /** @todo add memflags argument */ ++ qh = dwc_otg_hcd_qh_alloc (); ++ if (qh == NULL) { ++ return NULL; ++ } ++ ++ dwc_otg_hcd_qh_init (hcd, qh, urb); ++ return qh; ++} ++ ++/** Free each QTD in the QH's QTD-list then free the QH. QH should already be ++ * removed from a list. QTD list should already be empty if called from URB ++ * Dequeue. ++ * ++ * @param[in] hcd HCD instance. ++ * @param[in] qh The QH to free. ++ */ ++void dwc_otg_hcd_qh_free (dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ dwc_otg_qtd_t *qtd; ++ struct list_head *pos; ++ unsigned long flags; ++ ++ /* Free each QTD in the QTD list */ ++ SPIN_LOCK_IRQSAVE(&hcd->lock, flags) ++ for (pos = qh->qtd_list.next; ++ pos != &qh->qtd_list; ++ pos = qh->qtd_list.next) ++ { ++ list_del (pos); ++ qtd = dwc_list_to_qtd (pos); ++ dwc_otg_hcd_qtd_free (qtd); ++ } ++ SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags) ++ ++ if (qh->dw_align_buf) { ++ dma_free_coherent((dwc_otg_hcd_to_hcd(hcd))->self.controller, ++ hcd->core_if->core_params->max_transfer_size, ++ qh->dw_align_buf, ++ qh->dw_align_buf_dma); ++ } ++ ++ kfree (qh); ++ return; ++} ++ ++/** Initializes a QH structure. ++ * ++ * @param[in] hcd The HCD state structure for the DWC OTG controller. ++ * @param[in] qh The QH to init. ++ * @param[in] urb Holds the information about the device/endpoint that we need ++ * to initialize the QH. */ ++#define SCHEDULE_SLOP 10 ++void dwc_otg_hcd_qh_init(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, struct urb *urb) ++{ ++ char *speed, *type; ++ memset (qh, 0, sizeof (dwc_otg_qh_t)); ++ ++ /* Initialize QH */ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: ++ qh->ep_type = USB_ENDPOINT_XFER_CONTROL; ++ break; ++ case PIPE_BULK: ++ qh->ep_type = USB_ENDPOINT_XFER_BULK; ++ break; ++ case PIPE_ISOCHRONOUS: ++ qh->ep_type = USB_ENDPOINT_XFER_ISOC; ++ break; ++ case PIPE_INTERRUPT: ++ qh->ep_type = USB_ENDPOINT_XFER_INT; ++ break; ++ } ++ ++ qh->ep_is_in = usb_pipein(urb->pipe) ? 1 : 0; ++ ++ qh->data_toggle = DWC_OTG_HC_PID_DATA0; ++ qh->maxp = usb_maxpacket(urb->dev, urb->pipe, !(usb_pipein(urb->pipe))); ++ INIT_LIST_HEAD(&qh->qtd_list); ++ INIT_LIST_HEAD(&qh->qh_list_entry); ++ qh->channel = NULL; ++ ++ /* FS/LS Enpoint on HS Hub ++ * NOT virtual root hub */ ++ qh->do_split = 0; ++ if (((urb->dev->speed == USB_SPEED_LOW) || ++ (urb->dev->speed == USB_SPEED_FULL)) && ++ (urb->dev->tt) && (urb->dev->tt->hub) && (urb->dev->tt->hub->devnum != 1)) ++ { ++ DWC_DEBUGPL(DBG_HCD, "QH init: EP %d: TT found at hub addr %d, for port %d\n", ++ usb_pipeendpoint(urb->pipe), urb->dev->tt->hub->devnum, ++ urb->dev->ttport); ++ qh->do_split = 1; ++ } ++ ++ if (qh->ep_type == USB_ENDPOINT_XFER_INT || ++ qh->ep_type == USB_ENDPOINT_XFER_ISOC) { ++ /* Compute scheduling parameters once and save them. */ ++ hprt0_data_t hprt; ++ ++ /** @todo Account for split transfers in the bus time. */ ++ int bytecount = dwc_hb_mult(qh->maxp) * dwc_max_packet(qh->maxp); ++ ++ /* FIXME: work-around patch by Steven */ ++ qh->usecs = NS_TO_US(usb_calc_bus_time(urb->dev->speed, ++ usb_pipein(urb->pipe), ++ (qh->ep_type == USB_ENDPOINT_XFER_ISOC), ++ bytecount)); ++ ++ /* Start in a slightly future (micro)frame. */ ++ qh->sched_frame = dwc_frame_num_inc(hcd->frame_number, ++ SCHEDULE_SLOP); ++ qh->interval = urb->interval; ++#if 0 ++ /* Increase interrupt polling rate for debugging. */ ++ if (qh->ep_type == USB_ENDPOINT_XFER_INT) { ++ qh->interval = 8; ++ } ++#endif ++ hprt.d32 = dwc_read_reg32(hcd->core_if->host_if->hprt0); ++ if ((hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) && ++ ((urb->dev->speed == USB_SPEED_LOW) || ++ (urb->dev->speed == USB_SPEED_FULL))) { ++ qh->interval *= 8; ++ qh->sched_frame |= 0x7; ++ qh->start_split_frame = qh->sched_frame; ++ } ++ ++ } ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n"); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", qh); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n", ++ urb->dev->devnum); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n", ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) == USB_DIR_IN ? "IN" : "OUT"); ++ ++ switch(urb->dev->speed) { ++ case USB_SPEED_LOW: ++ speed = "low"; ++ break; ++ case USB_SPEED_FULL: ++ speed = "full"; ++ break; ++ case USB_SPEED_HIGH: ++ speed = "high"; ++ break; ++ default: ++ speed = "?"; ++ break; ++ } ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n", speed); ++ ++ switch (qh->ep_type) { ++ case USB_ENDPOINT_XFER_ISOC: ++ type = "isochronous"; ++ break; ++ case USB_ENDPOINT_XFER_INT: ++ type = "interrupt"; ++ break; ++ case USB_ENDPOINT_XFER_CONTROL: ++ type = "control"; ++ break; ++ case USB_ENDPOINT_XFER_BULK: ++ type = "bulk"; ++ break; ++ default: ++ type = "?"; ++ break; ++ } ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Type = %s\n",type); ++ ++#ifdef DEBUG ++ if (qh->ep_type == USB_ENDPOINT_XFER_INT) { ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n", ++ qh->usecs); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n", ++ qh->interval); ++ } ++#endif ++ qh->dw_align_buf = NULL; ++ return; ++} ++ ++/** ++ * Checks that a channel is available for a periodic transfer. ++ * ++ * @return 0 if successful, negative error code otherise. ++ */ ++static int periodic_channel_available(dwc_otg_hcd_t *hcd) ++{ ++ /* ++ * Currently assuming that there is a dedicated host channnel for each ++ * periodic transaction plus at least one host channel for ++ * non-periodic transactions. ++ */ ++ int status; ++ int num_channels; ++ ++ num_channels = hcd->core_if->core_params->host_channels; ++ if ((hcd->periodic_channels + hcd->non_periodic_channels < num_channels) && ++ (hcd->periodic_channels < num_channels - 1)) { ++ status = 0; ++ } ++ else { ++ DWC_NOTICE("%s: Total channels: %d, Periodic: %d, Non-periodic: %d\n", ++ __func__, num_channels, hcd->periodic_channels, ++ hcd->non_periodic_channels); ++ status = -ENOSPC; ++ } ++ ++ return status; ++} ++ ++/** ++ * Checks that there is sufficient bandwidth for the specified QH in the ++ * periodic schedule. For simplicity, this calculation assumes that all the ++ * transfers in the periodic schedule may occur in the same (micro)frame. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH containing periodic bandwidth required. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++static int check_periodic_bandwidth(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ int status; ++ uint16_t max_claimed_usecs; ++ ++ status = 0; ++ ++ if (hcd->core_if->core_params->speed == DWC_SPEED_PARAM_HIGH) { ++ /* ++ * High speed mode. ++ * Max periodic usecs is 80% x 125 usec = 100 usec. ++ */ ++ max_claimed_usecs = 100 - qh->usecs; ++ } else { ++ /* ++ * Full speed mode. ++ * Max periodic usecs is 90% x 1000 usec = 900 usec. ++ */ ++ max_claimed_usecs = 900 - qh->usecs; ++ } ++ ++ if (hcd->periodic_usecs > max_claimed_usecs) { ++ DWC_NOTICE("%s: already claimed usecs %d, required usecs %d\n", ++ __func__, hcd->periodic_usecs, qh->usecs); ++ status = -ENOSPC; ++ } ++ ++ return status; ++} ++ ++/** ++ * Checks that the max transfer size allowed in a host channel is large enough ++ * to handle the maximum data transfer in a single (micro)frame for a periodic ++ * transfer. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH for a periodic endpoint. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++static int check_max_xfer_size(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ int status; ++ uint32_t max_xfer_size; ++ uint32_t max_channel_xfer_size; ++ ++ status = 0; ++ ++ max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp); ++ max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size; ++ ++ if (max_xfer_size > max_channel_xfer_size) { ++ DWC_NOTICE("%s: Periodic xfer length %d > " ++ "max xfer length for channel %d\n", ++ __func__, max_xfer_size, max_channel_xfer_size); ++ status = -ENOSPC; ++ } ++ ++ return status; ++} ++ ++/** ++ * Schedules an interrupt or isochronous transfer in the periodic schedule. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH for the periodic transfer. The QH should already contain the ++ * scheduling information. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++static int schedule_periodic(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ int status = 0; ++ ++ status = periodic_channel_available(hcd); ++ if (status) { ++ DWC_NOTICE("%s: No host channel available for periodic " ++ "transfer.\n", __func__); ++ return status; ++ } ++ ++ status = check_periodic_bandwidth(hcd, qh); ++ if (status) { ++ DWC_NOTICE("%s: Insufficient periodic bandwidth for " ++ "periodic transfer.\n", __func__); ++ return status; ++ } ++ ++ status = check_max_xfer_size(hcd, qh); ++ if (status) { ++ DWC_NOTICE("%s: Channel max transfer size too small " ++ "for periodic transfer.\n", __func__); ++ return status; ++ } ++ ++ /* Always start in the inactive schedule. */ ++ list_add_tail(&qh->qh_list_entry, &hcd->periodic_sched_inactive); ++ ++ /* Reserve the periodic channel. */ ++ hcd->periodic_channels++; ++ ++ /* Update claimed usecs per (micro)frame. */ ++ hcd->periodic_usecs += qh->usecs; ++ ++ /* Update average periodic bandwidth claimed and # periodic reqs for usbfs. */ ++ hcd_to_bus(dwc_otg_hcd_to_hcd(hcd))->bandwidth_allocated += qh->usecs / qh->interval; ++ if (qh->ep_type == USB_ENDPOINT_XFER_INT) { ++ hcd_to_bus(dwc_otg_hcd_to_hcd(hcd))->bandwidth_int_reqs++; ++ DWC_DEBUGPL(DBG_HCD, "Scheduled intr: qh %p, usecs %d, period %d\n", ++ qh, qh->usecs, qh->interval); ++ } else { ++ hcd_to_bus(dwc_otg_hcd_to_hcd(hcd))->bandwidth_isoc_reqs++; ++ DWC_DEBUGPL(DBG_HCD, "Scheduled isoc: qh %p, usecs %d, period %d\n", ++ qh, qh->usecs, qh->interval); ++ } ++ ++ return status; ++} ++ ++/** ++ * This function adds a QH to either the non periodic or periodic schedule if ++ * it is not already in the schedule. If the QH is already in the schedule, no ++ * action is taken. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++int dwc_otg_hcd_qh_add (dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ unsigned long flags; ++ int status = 0; ++ ++ SPIN_LOCK_IRQSAVE(&hcd->lock, flags) ++ ++ if (!list_empty(&qh->qh_list_entry)) { ++ /* QH already in a schedule. */ ++ goto done; ++ } ++ ++ /* Add the new QH to the appropriate schedule */ ++ if (dwc_qh_is_non_per(qh)) { ++ /* Always start in the inactive schedule. */ ++ list_add_tail(&qh->qh_list_entry, &hcd->non_periodic_sched_inactive); ++ } else { ++ status = schedule_periodic(hcd, qh); ++ } ++ ++ done: ++ SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags) ++ ++ return status; ++} ++ ++/** ++ * Removes an interrupt or isochronous transfer from the periodic schedule. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH for the periodic transfer. ++ */ ++static void deschedule_periodic(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ list_del_init(&qh->qh_list_entry); ++ ++ /* Release the periodic channel reservation. */ ++ hcd->periodic_channels--; ++ ++ /* Update claimed usecs per (micro)frame. */ ++ hcd->periodic_usecs -= qh->usecs; ++ ++ /* Update average periodic bandwidth claimed and # periodic reqs for usbfs. */ ++ hcd_to_bus(dwc_otg_hcd_to_hcd(hcd))->bandwidth_allocated -= qh->usecs / qh->interval; ++ ++ if (qh->ep_type == USB_ENDPOINT_XFER_INT) { ++ hcd_to_bus(dwc_otg_hcd_to_hcd(hcd))->bandwidth_int_reqs--; ++ DWC_DEBUGPL(DBG_HCD, "Descheduled intr: qh %p, usecs %d, period %d\n", ++ qh, qh->usecs, qh->interval); ++ } else { ++ hcd_to_bus(dwc_otg_hcd_to_hcd(hcd))->bandwidth_isoc_reqs--; ++ DWC_DEBUGPL(DBG_HCD, "Descheduled isoc: qh %p, usecs %d, period %d\n", ++ qh, qh->usecs, qh->interval); ++ } ++} ++ ++/** ++ * Removes a QH from either the non-periodic or periodic schedule. Memory is ++ * not freed. ++ * ++ * @param[in] hcd The HCD state structure. ++ * @param[in] qh QH to remove from schedule. */ ++void dwc_otg_hcd_qh_remove (dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh) ++{ ++ unsigned long flags; ++ ++ SPIN_LOCK_IRQSAVE(&hcd->lock, flags); ++ ++ if (list_empty(&qh->qh_list_entry)) { ++ /* QH is not in a schedule. */ ++ goto done; ++ } ++ ++ if (dwc_qh_is_non_per(qh)) { ++ if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry) { ++ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; ++ } ++ list_del_init(&qh->qh_list_entry); ++ } else { ++ deschedule_periodic(hcd, qh); ++ } ++ ++ done: ++ SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags) ++} ++ ++/** ++ * Deactivates a QH. For non-periodic QHs, removes the QH from the active ++ * non-periodic schedule. The QH is added to the inactive non-periodic ++ * schedule if any QTDs are still attached to the QH. ++ * ++ * For periodic QHs, the QH is removed from the periodic queued schedule. If ++ * there are any QTDs still attached to the QH, the QH is added to either the ++ * periodic inactive schedule or the periodic ready schedule and its next ++ * scheduled frame is calculated. The QH is placed in the ready schedule if ++ * the scheduled frame has been reached already. Otherwise it's placed in the ++ * inactive schedule. If there are no QTDs attached to the QH, the QH is ++ * completely removed from the periodic schedule. ++ */ ++void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh, int sched_next_periodic_split) ++{ ++ unsigned long flags; ++ SPIN_LOCK_IRQSAVE(&hcd->lock, flags); ++ ++ if (dwc_qh_is_non_per(qh)) { ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ if (!list_empty(&qh->qtd_list)) { ++ /* Add back to inactive non-periodic schedule. */ ++ dwc_otg_hcd_qh_add(hcd, qh); ++ } ++ } else { ++ uint16_t frame_number = dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(hcd)); ++ ++ if (qh->do_split) { ++ /* Schedule the next continuing periodic split transfer */ ++ if (sched_next_periodic_split) { ++ ++ qh->sched_frame = frame_number; ++ if (dwc_frame_num_le(frame_number, ++ dwc_frame_num_inc(qh->start_split_frame, 1))) { ++ /* ++ * Allow one frame to elapse after start ++ * split microframe before scheduling ++ * complete split, but DONT if we are ++ * doing the next start split in the ++ * same frame for an ISOC out. ++ */ ++ if ((qh->ep_type != USB_ENDPOINT_XFER_ISOC) || (qh->ep_is_in != 0)) { ++ qh->sched_frame = dwc_frame_num_inc(qh->sched_frame, 1); ++ } ++ } ++ } else { ++ qh->sched_frame = dwc_frame_num_inc(qh->start_split_frame, ++ qh->interval); ++ if (dwc_frame_num_le(qh->sched_frame, frame_number)) { ++ qh->sched_frame = frame_number; ++ } ++ qh->sched_frame |= 0x7; ++ qh->start_split_frame = qh->sched_frame; ++ } ++ } else { ++ qh->sched_frame = dwc_frame_num_inc(qh->sched_frame, qh->interval); ++ if (dwc_frame_num_le(qh->sched_frame, frame_number)) { ++ qh->sched_frame = frame_number; ++ } ++ } ++ ++ if (list_empty(&qh->qtd_list)) { ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ } else { ++ /* ++ * Remove from periodic_sched_queued and move to ++ * appropriate queue. ++ */ ++ if (qh->sched_frame == frame_number) { ++ list_move(&qh->qh_list_entry, ++ &hcd->periodic_sched_ready); ++ } else { ++ list_move(&qh->qh_list_entry, ++ &hcd->periodic_sched_inactive); ++ } ++ } ++ } ++ ++ SPIN_UNLOCK_IRQRESTORE(&hcd->lock, flags); ++} ++ ++/** ++ * This function allocates and initializes a QTD. ++ * ++ * @param[in] urb The URB to create a QTD from. Each URB-QTD pair will end up ++ * pointing to each other so each pair should have a unique correlation. ++ * ++ * @return Returns pointer to the newly allocated QTD, or NULL on error. */ ++dwc_otg_qtd_t *dwc_otg_hcd_qtd_create (struct urb *urb) ++{ ++ dwc_otg_qtd_t *qtd; ++ ++ qtd = dwc_otg_hcd_qtd_alloc (); ++ if (qtd == NULL) { ++ return NULL; ++ } ++ ++ dwc_otg_hcd_qtd_init (qtd, urb); ++ return qtd; ++} ++ ++/** ++ * Initializes a QTD structure. ++ * ++ * @param[in] qtd The QTD to initialize. ++ * @param[in] urb The URB to use for initialization. */ ++void dwc_otg_hcd_qtd_init (dwc_otg_qtd_t *qtd, struct urb *urb) ++{ ++ memset (qtd, 0, sizeof (dwc_otg_qtd_t)); ++ qtd->urb = urb; ++ if (usb_pipecontrol(urb->pipe)) { ++ /* ++ * The only time the QTD data toggle is used is on the data ++ * phase of control transfers. This phase always starts with ++ * DATA1. ++ */ ++ qtd->data_toggle = DWC_OTG_HC_PID_DATA1; ++ qtd->control_phase = DWC_OTG_CONTROL_SETUP; ++ } ++ ++ /* start split */ ++ qtd->complete_split = 0; ++ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ qtd->isoc_split_offset = 0; ++ ++ /* Store the qtd ptr in the urb to reference what QTD. */ ++ urb->hcpriv = qtd; ++ return; ++} ++ ++/** ++ * This function adds a QTD to the QTD-list of a QH. It will find the correct ++ * QH to place the QTD into. If it does not find a QH, then it will create a ++ * new QH. If the QH to which the QTD is added is not currently scheduled, it ++ * is placed into the proper schedule based on its EP type. ++ * ++ * @param[in] qtd The QTD to add ++ * @param[in] dwc_otg_hcd The DWC HCD structure ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++int dwc_otg_hcd_qtd_add (dwc_otg_qtd_t *qtd, ++ dwc_otg_hcd_t *dwc_otg_hcd) ++{ ++ struct usb_host_endpoint *ep; ++ dwc_otg_qh_t *qh; ++ unsigned long flags; ++ int retval = 0; ++ ++ struct urb *urb = qtd->urb; ++ ++ SPIN_LOCK_IRQSAVE(&dwc_otg_hcd->lock, flags); ++ ++ /* ++ * Get the QH which holds the QTD-list to insert to. Create QH if it ++ * doesn't exist. ++ */ ++ ep = dwc_urb_to_endpoint(urb); ++ qh = (dwc_otg_qh_t *)ep->hcpriv; ++ if (qh == NULL) { ++ qh = dwc_otg_hcd_qh_create (dwc_otg_hcd, urb); ++ if (qh == NULL) { ++ goto done; ++ } ++ ep->hcpriv = qh; ++ } ++ ++ retval = dwc_otg_hcd_qh_add(dwc_otg_hcd, qh); ++ if (retval == 0) { ++ list_add_tail(&qtd->qtd_list_entry, &qh->qtd_list); ++ } ++ ++ done: ++ SPIN_UNLOCK_IRQRESTORE(&dwc_otg_hcd->lock, flags); ++ ++ return retval; ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_pcd.c +@@ -0,0 +1,2523 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.c $ ++ * $Revision: 1.5 $ ++ * $Date: 2008-11-27 09:21:25 $ ++ * $Change: 1115682 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++ ++/** @file ++ * This file implements the Peripheral Controller Driver. ++ * ++ * The Peripheral Controller Driver (PCD) is responsible for ++ * translating requests from the Function Driver into the appropriate ++ * actions on the DWC_otg controller. It isolates the Function Driver ++ * from the specifics of the controller by providing an API to the ++ * Function Driver. ++ * ++ * The Peripheral Controller Driver for Linux will implement the ++ * Gadget API, so that the existing Gadget drivers can be used. ++ * (Gadget Driver is the Linux terminology for a Function Driver.) ++ * ++ * The Linux Gadget API is defined in the header file ++ * . The USB EP operations API is ++ * defined in the structure usb_ep_ops and the USB ++ * Controller API is defined in the structure ++ * usb_gadget_ops. ++ * ++ * An important function of the PCD is managing interrupts generated ++ * by the DWC_otg controller. The implementation of the DWC_otg device ++ * mode interrupt service routines is in dwc_otg_pcd_intr.c. ++ * ++ * @todo Add Device Mode test modes (Test J mode, Test K mode, etc). ++ * @todo Does it work when the request size is greater than DEPTSIZ ++ * transfer size ++ * ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++# include ++#else ++# include ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) ++#include ++#else ++#include ++#endif ++ ++#include "dwc_otg_driver.h" ++#include "dwc_otg_pcd.h" ++ ++ ++/** ++ * Static PCD pointer for use in usb_gadget_register_driver and ++ * usb_gadget_unregister_driver. Initialized in dwc_otg_pcd_init. ++ */ ++static dwc_otg_pcd_t *s_pcd = 0; ++ ++ ++/* Display the contents of the buffer */ ++extern void dump_msg(const u8 *buf, unsigned int length); ++ ++ ++/** ++ * This function completes a request. It call's the request call back. ++ */ ++void dwc_otg_request_done(dwc_otg_pcd_ep_t *ep, dwc_otg_pcd_request_t *req, ++ int status) ++{ ++ unsigned stopped = ep->stopped; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, ep); ++ list_del_init(&req->queue); ++ ++ if (req->req.status == -EINPROGRESS) { ++ req->req.status = status; ++ } else { ++ status = req->req.status; ++ } ++ ++ /* don't modify queue heads during completion callback */ ++ ep->stopped = 1; ++ SPIN_UNLOCK(&ep->pcd->lock); ++ req->req.complete(&ep->ep, &req->req); ++ SPIN_LOCK(&ep->pcd->lock); ++ ++ if (ep->pcd->request_pending > 0) { ++ --ep->pcd->request_pending; ++ } ++ ++ ep->stopped = stopped; ++} ++ ++/** ++ * This function terminates all the requsts in the EP request queue. ++ */ ++void dwc_otg_request_nuke(dwc_otg_pcd_ep_t *ep) ++{ ++ dwc_otg_pcd_request_t *req; ++ ++ ep->stopped = 1; ++ ++ /* called with irqs blocked?? */ ++ while (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, dwc_otg_pcd_request_t, ++ queue); ++ dwc_otg_request_done(ep, req, -ESHUTDOWN); ++ } ++} ++ ++/* USB Endpoint Operations */ ++/* ++ * The following sections briefly describe the behavior of the Gadget ++ * API endpoint operations implemented in the DWC_otg driver ++ * software. Detailed descriptions of the generic behavior of each of ++ * these functions can be found in the Linux header file ++ * include/linux/usb_gadget.h. ++ * ++ * The Gadget API provides wrapper functions for each of the function ++ * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper ++ * function, which then calls the underlying PCD function. The ++ * following sections are named according to the wrapper ++ * functions. Within each section, the corresponding DWC_otg PCD ++ * function name is specified. ++ * ++ */ ++ ++/** ++ * This function assigns periodic Tx FIFO to an periodic EP ++ * in shared Tx FIFO mode ++ */ ++static uint32_t assign_perio_tx_fifo(dwc_otg_core_if_t *core_if) ++{ ++ uint32_t PerTxMsk = 1; ++ int i; ++ for(i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; ++i) ++ { ++ if((PerTxMsk & core_if->p_tx_msk) == 0) { ++ core_if->p_tx_msk |= PerTxMsk; ++ return i + 1; ++ } ++ PerTxMsk <<= 1; ++ } ++ return 0; ++} ++/** ++ * This function releases periodic Tx FIFO ++ * in shared Tx FIFO mode ++ */ ++static void release_perio_tx_fifo(dwc_otg_core_if_t *core_if, uint32_t fifo_num) ++{ ++ core_if->p_tx_msk = (core_if->p_tx_msk & (1 << (fifo_num - 1))) ^ core_if->p_tx_msk; ++} ++/** ++ * This function assigns periodic Tx FIFO to an periodic EP ++ * in shared Tx FIFO mode ++ */ ++static uint32_t assign_tx_fifo(dwc_otg_core_if_t *core_if) ++{ ++ uint32_t TxMsk = 1; ++ int i; ++ ++ for(i = 0; i < core_if->hwcfg4.b.num_in_eps; ++i) ++ { ++ if((TxMsk & core_if->tx_msk) == 0) { ++ core_if->tx_msk |= TxMsk; ++ return i + 1; ++ } ++ TxMsk <<= 1; ++ } ++ return 0; ++} ++/** ++ * This function releases periodic Tx FIFO ++ * in shared Tx FIFO mode ++ */ ++static void release_tx_fifo(dwc_otg_core_if_t *core_if, uint32_t fifo_num) ++{ ++ core_if->tx_msk = (core_if->tx_msk & (1 << (fifo_num - 1))) ^ core_if->tx_msk; ++} ++ ++/** ++ * This function is called by the Gadget Driver for each EP to be ++ * configured for the current configuration (SET_CONFIGURATION). ++ * ++ * This function initializes the dwc_otg_ep_t data structure, and then ++ * calls dwc_otg_ep_activate. ++ */ ++static int dwc_otg_pcd_ep_enable(struct usb_ep *usb_ep, ++ const struct usb_endpoint_descriptor *ep_desc) ++{ ++ dwc_otg_pcd_ep_t *ep = 0; ++ dwc_otg_pcd_t *pcd = 0; ++ unsigned long flags; ++ ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p)\n", __func__, usb_ep, ep_desc); ++ ++ ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep); ++ if (!usb_ep || !ep_desc || ep->desc || ++ ep_desc->bDescriptorType != USB_DT_ENDPOINT) { ++ DWC_WARN("%s, bad ep or descriptor\n", __func__); ++ return -EINVAL; ++ } ++ if (ep == &ep->pcd->ep0) { ++ DWC_WARN("%s, bad ep(0)\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* Check FIFO size? */ ++ if (!ep_desc->wMaxPacketSize) { ++ DWC_WARN("%s, bad %s maxpacket\n", __func__, usb_ep->name); ++ return -ERANGE; ++ } ++ ++ pcd = ep->pcd; ++ if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_WARN("%s, bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ SPIN_LOCK_IRQSAVE(&pcd->lock, flags); ++ ++ ep->desc = ep_desc; ++ ep->ep.maxpacket = le16_to_cpu (ep_desc->wMaxPacketSize); ++ ++ /* ++ * Activate the EP ++ */ ++ ep->stopped = 0; ++ ++ ep->dwc_ep.is_in = (USB_DIR_IN & ep_desc->bEndpointAddress) != 0; ++ ep->dwc_ep.maxpacket = ep->ep.maxpacket; ++ ++ ep->dwc_ep.type = ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; ++ ++ if(ep->dwc_ep.is_in) { ++ if(!pcd->otg_dev->core_if->en_multiple_tx_fifo) { ++ ep->dwc_ep.tx_fifo_num = 0; ++ ++ if (ep->dwc_ep.type == USB_ENDPOINT_XFER_ISOC) { ++ /* ++ * if ISOC EP then assign a Periodic Tx FIFO. ++ */ ++ ep->dwc_ep.tx_fifo_num = assign_perio_tx_fifo(pcd->otg_dev->core_if); ++ } ++ } else { ++ /* ++ * if Dedicated FIFOs mode is on then assign a Tx FIFO. ++ */ ++ ep->dwc_ep.tx_fifo_num = assign_tx_fifo(pcd->otg_dev->core_if); ++ ++ } ++ } ++ /* Set initial data PID. */ ++ if (ep->dwc_ep.type == USB_ENDPOINT_XFER_BULK) { ++ ep->dwc_ep.data_pid_start = 0; ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "Activate %s-%s: type=%d, mps=%d desc=%p\n", ++ ep->ep.name, (ep->dwc_ep.is_in ?"IN":"OUT"), ++ ep->dwc_ep.type, ep->dwc_ep.maxpacket, ep->desc); ++ ++ if(ep->dwc_ep.type != USB_ENDPOINT_XFER_ISOC) { ++ ep->dwc_ep.desc_addr = dwc_otg_ep_alloc_desc_chain(&ep->dwc_ep.dma_desc_addr, MAX_DMA_DESC_CNT); ++ } ++ ++ dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep); ++ SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); ++ ++ return 0; ++} ++ ++/** ++ * This function is called when an EP is disabled due to disconnect or ++ * change in configuration. Any pending requests will terminate with a ++ * status of -ESHUTDOWN. ++ * ++ * This function modifies the dwc_otg_ep_t data structure for this EP, ++ * and then calls dwc_otg_ep_deactivate. ++ */ ++static int dwc_otg_pcd_ep_disable(struct usb_ep *usb_ep) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_pcd_t *pcd = 0; ++ unsigned long flags; ++ ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, usb_ep); ++ ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep); ++ if (!usb_ep || !ep->desc) { ++ DWC_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__, ++ usb_ep ? ep->ep.name : NULL); ++ return -EINVAL; ++ } ++ ++ SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags); ++ ++ dwc_otg_request_nuke(ep); ++ ++ dwc_otg_ep_deactivate(GET_CORE_IF(ep->pcd), &ep->dwc_ep); ++ ep->desc = 0; ++ ep->stopped = 1; ++ ++ if(ep->dwc_ep.is_in) { ++ dwc_otg_flush_tx_fifo(GET_CORE_IF(ep->pcd), ep->dwc_ep.tx_fifo_num); ++ release_perio_tx_fifo(GET_CORE_IF(ep->pcd), ep->dwc_ep.tx_fifo_num); ++ release_tx_fifo(GET_CORE_IF(ep->pcd), ep->dwc_ep.tx_fifo_num); ++ } ++ ++ /* Free DMA Descriptors */ ++ pcd = ep->pcd; ++ ++ SPIN_UNLOCK_IRQRESTORE(&ep->pcd->lock, flags); ++ ++ if(ep->dwc_ep.type != USB_ENDPOINT_XFER_ISOC && ep->dwc_ep.desc_addr) { ++ dwc_otg_ep_free_desc_chain(ep->dwc_ep.desc_addr, ep->dwc_ep.dma_desc_addr, MAX_DMA_DESC_CNT); ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "%s disabled\n", usb_ep->name); ++ return 0; ++} ++ ++ ++/** ++ * This function allocates a request object to use with the specified ++ * endpoint. ++ * ++ * @param ep The endpoint to be used with with the request ++ * @param gfp_flags the GFP_* flags to use. ++ */ ++static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *ep, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ int gfp_flags ++#else ++ gfp_t gfp_flags ++#endif ++ ) ++{ ++ dwc_otg_pcd_request_t *req; ++ ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p,%d)\n", __func__, ep, gfp_flags); ++ if (0 == ep) { ++ DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n"); ++ return 0; ++ } ++ req = kmalloc(sizeof(dwc_otg_pcd_request_t), gfp_flags); ++ if (0 == req) { ++ DWC_WARN("%s() %s\n", __func__, ++ "request allocation failed!\n"); ++ return 0; ++ } ++ memset(req, 0, sizeof(dwc_otg_pcd_request_t)); ++ req->req.dma = DMA_ADDR_INVALID; ++ INIT_LIST_HEAD(&req->queue); ++ return &req->req; ++} ++ ++/** ++ * This function frees a request object. ++ * ++ * @param ep The endpoint associated with the request ++ * @param req The request being freed ++ */ ++static void dwc_otg_pcd_free_request(struct usb_ep *ep, ++ struct usb_request *req) ++{ ++ dwc_otg_pcd_request_t *request; ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p)\n", __func__, ep, req); ++ ++ if (0 == ep || 0 == req) { ++ DWC_WARN("%s() %s\n", __func__, ++ "Invalid ep or req argument!\n"); ++ return; ++ } ++ ++ request = container_of(req, dwc_otg_pcd_request_t, req); ++ kfree(request); ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) ++/** ++ * This function allocates an I/O buffer to be used for a transfer ++ * to/from the specified endpoint. ++ * ++ * @param usb_ep The endpoint to be used with with the request ++ * @param bytes The desired number of bytes for the buffer ++ * @param dma Pointer to the buffer's DMA address; must be valid ++ * @param gfp_flags the GFP_* flags to use. ++ * @return address of a new buffer or null is buffer could not be allocated. ++ */ ++static void *dwc_otg_pcd_alloc_buffer(struct usb_ep *usb_ep, unsigned bytes, ++ dma_addr_t *dma, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ int gfp_flags ++#else ++ gfp_t gfp_flags ++#endif ++ ) ++{ ++ void *buf; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_pcd_t *pcd = 0; ++ ++ ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep); ++ pcd = ep->pcd; ++ ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p,%d,%p,%0x)\n", __func__, usb_ep, bytes, ++ dma, gfp_flags); ++ ++ /* Check dword alignment */ ++ if ((bytes & 0x3UL) != 0) { ++ DWC_WARN("%s() Buffer size is not a multiple of" ++ "DWORD size (%d)",__func__, bytes); ++ } ++ ++ if (GET_CORE_IF(pcd)->dma_enable) { ++ buf = dma_alloc_coherent (NULL, bytes, dma, gfp_flags); ++ } ++ else { ++ buf = kmalloc(bytes, gfp_flags); ++ } ++ ++ /* Check dword alignment */ ++ if (((int)buf & 0x3UL) != 0) { ++ DWC_WARN("%s() Buffer is not DWORD aligned (%p)", ++ __func__, buf); ++ } ++ ++ return buf; ++} ++ ++/** ++ * This function frees an I/O buffer that was allocated by alloc_buffer. ++ * ++ * @param usb_ep the endpoint associated with the buffer ++ * @param buf address of the buffer ++ * @param dma The buffer's DMA address ++ * @param bytes The number of bytes of the buffer ++ */ ++static void dwc_otg_pcd_free_buffer(struct usb_ep *usb_ep, void *buf, ++ dma_addr_t dma, unsigned bytes) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_pcd_t *pcd = 0; ++ ++ ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep); ++ pcd = ep->pcd; ++ ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p,%0x,%d)\n", __func__, ep, buf, dma, bytes); ++ ++ if (GET_CORE_IF(pcd)->dma_enable) { ++ dma_free_coherent (NULL, bytes, buf, dma); ++ } ++ else { ++ kfree(buf); ++ } ++} ++#endif ++ ++ ++/** ++ * This function is used to submit an I/O Request to an EP. ++ * ++ * - When the request completes the request's completion callback ++ * is called to return the request to the driver. ++ * - An EP, except control EPs, may have multiple requests ++ * pending. ++ * - Once submitted the request cannot be examined or modified. ++ * - Each request is turned into one or more packets. ++ * - A BULK EP can queue any amount of data; the transfer is ++ * packetized. ++ * - Zero length Packets are specified with the request 'zero' ++ * flag. ++ */ ++static int dwc_otg_pcd_ep_queue(struct usb_ep *usb_ep, ++ struct usb_request *usb_req, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ int gfp_flags ++#else ++ gfp_t gfp_flags ++#endif ++ ) ++{ ++ int prevented = 0; ++ dwc_otg_pcd_request_t *req; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_pcd_t *pcd; ++ unsigned long flags = 0; ++ dwc_otg_core_if_t *_core_if; ++ ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p,%d)\n", ++ __func__, usb_ep, usb_req, gfp_flags); ++ ++ req = container_of(usb_req, dwc_otg_pcd_request_t, req); ++ if (!usb_req || !usb_req->complete || !usb_req->buf || ++ !list_empty(&req->queue)) { ++ DWC_WARN("%s, bad params\n", __func__); ++ return -EINVAL; ++ } ++ ++ ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep); ++ if (!usb_ep || (!ep->desc && ep->dwc_ep.num != 0)/* || ep->stopped != 0*/) { ++ DWC_WARN("%s, bad ep\n", __func__); ++ return -EINVAL; ++ } ++ ++ pcd = ep->pcd; ++ if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", pcd->gadget.speed); ++ DWC_WARN("%s, bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ ++ DWC_DEBUGPL(DBG_PCD, "%s queue req %p, len %d buf %p\n", ++ usb_ep->name, usb_req, usb_req->length, usb_req->buf); ++ ++ if (!GET_CORE_IF(pcd)->core_params->opt) { ++ if (ep->dwc_ep.num != 0) { ++ DWC_ERROR("%s queue req %p, len %d buf %p\n", ++ usb_ep->name, usb_req, usb_req->length, usb_req->buf); ++ } ++ } ++ ++ SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags); ++ ++ ++ /************************************************** ++ New add by kaiker ,for DMA mode bug ++ ************************************************/ ++ //by kaiker ,for RT3052 USB OTG device mode ++ ++ _core_if = GET_CORE_IF(pcd); ++ ++ if (_core_if->dma_enable) ++ { ++ usb_req->dma = virt_to_phys((void *)usb_req->buf); ++ ++ if(ep->dwc_ep.is_in) ++ { ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)) || defined(CONFIG_MIPS) ++ if(usb_req->length) ++ dma_cache_wback_inv((unsigned long)usb_req->buf, usb_req->length + 2); ++#endif ++ } ++ } ++ ++ ++ ++#if defined(DEBUG) & defined(VERBOSE) ++ dump_msg(usb_req->buf, usb_req->length); ++#endif ++ ++ usb_req->status = -EINPROGRESS; ++ usb_req->actual = 0; ++ ++ /* ++ * For EP0 IN without premature status, zlp is required? ++ */ ++ if (ep->dwc_ep.num == 0 && ep->dwc_ep.is_in) { ++ DWC_DEBUGPL(DBG_PCDV, "%s-OUT ZLP\n", usb_ep->name); ++ //_req->zero = 1; ++ } ++ ++ /* Start the transfer */ ++ if (list_empty(&ep->queue) && !ep->stopped) { ++ /* EP0 Transfer? */ ++ if (ep->dwc_ep.num == 0) { ++ switch (pcd->ep0state) { ++ case EP0_IN_DATA_PHASE: ++ DWC_DEBUGPL(DBG_PCD, ++ "%s ep0: EP0_IN_DATA_PHASE\n", ++ __func__); ++ break; ++ ++ case EP0_OUT_DATA_PHASE: ++ DWC_DEBUGPL(DBG_PCD, ++ "%s ep0: EP0_OUT_DATA_PHASE\n", ++ __func__); ++ if (pcd->request_config) { ++ /* Complete STATUS PHASE */ ++ ep->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ } ++ break; ++ ++ case EP0_IN_STATUS_PHASE: ++ DWC_DEBUGPL(DBG_PCD, ++ "%s ep0: EP0_IN_STATUS_PHASE\n", ++ __func__); ++ break; ++ ++ default: ++ DWC_DEBUGPL(DBG_ANY, "ep0: odd state %d\n", ++ pcd->ep0state); ++ SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); ++ return -EL2HLT; ++ } ++ ep->dwc_ep.dma_addr = usb_req->dma; ++ ep->dwc_ep.start_xfer_buff = usb_req->buf; ++ ep->dwc_ep.xfer_buff = usb_req->buf; ++ ep->dwc_ep.xfer_len = usb_req->length; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ if(usb_req->zero) { ++ if((ep->dwc_ep.xfer_len % ep->dwc_ep.maxpacket == 0) ++ && (ep->dwc_ep.xfer_len != 0)) { ++ ep->dwc_ep.sent_zlp = 1; ++ } ++ ++ } ++ ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep->dwc_ep); ++ } ++ else { ++ ++ uint32_t max_transfer = GET_CORE_IF(ep->pcd)->core_params->max_transfer_size; ++ ++ /* Setup and start the Transfer */ ++ ep->dwc_ep.dma_addr = usb_req->dma; ++ ep->dwc_ep.start_xfer_buff = usb_req->buf; ++ ep->dwc_ep.xfer_buff = usb_req->buf; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = usb_req->length; ++ ep->dwc_ep.xfer_len = 0; ++ ep->dwc_ep.xfer_count = 0; ++ ++ if(max_transfer > MAX_TRANSFER_SIZE) { ++ ep->dwc_ep.maxxfer = max_transfer - (max_transfer % ep->dwc_ep.maxpacket); ++ } else { ++ ep->dwc_ep.maxxfer = max_transfer; ++ } ++ ++ if(usb_req->zero) { ++ if((ep->dwc_ep.total_len % ep->dwc_ep.maxpacket == 0) ++ && (ep->dwc_ep.total_len != 0)) { ++ ep->dwc_ep.sent_zlp = 1; ++ } ++ ++ } ++ dwc_otg_ep_start_transfer(GET_CORE_IF(pcd), &ep->dwc_ep); ++ } ++ } ++ ++ if ((req != 0) || prevented) { ++ ++pcd->request_pending; ++ list_add_tail(&req->queue, &ep->queue); ++ if (ep->dwc_ep.is_in && ep->stopped && !(GET_CORE_IF(pcd)->dma_enable)) { ++ /** @todo NGS Create a function for this. */ ++ diepmsk_data_t diepmsk = { .d32 = 0}; ++ diepmsk.b.intktxfemp = 1; ++ if(&GET_CORE_IF(pcd)->multiproc_int_enable) { ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->dev_if->dev_global_regs->diepeachintmsk[ep->dwc_ep.num], ++ 0, diepmsk.d32); ++ } else { ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->dev_if->dev_global_regs->diepmsk, 0, diepmsk.d32); ++ } ++ } ++ } ++ ++ SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); ++ return 0; ++} ++ ++/** ++ * This function cancels an I/O request from an EP. ++ */ ++static int dwc_otg_pcd_ep_dequeue(struct usb_ep *usb_ep, ++ struct usb_request *usb_req) ++{ ++ dwc_otg_pcd_request_t *req; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_pcd_t *pcd; ++ unsigned long flags; ++ ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p,%p)\n", __func__, usb_ep, usb_req); ++ ++ ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep); ++ if (!usb_ep || !usb_req || (!ep->desc && ep->dwc_ep.num != 0)) { ++ DWC_WARN("%s, bad argument\n", __func__); ++ return -EINVAL; ++ } ++ pcd = ep->pcd; ++ if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_WARN("%s, bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ SPIN_LOCK_IRQSAVE(&pcd->lock, flags); ++ DWC_DEBUGPL(DBG_PCDV, "%s %s %s %p\n", __func__, usb_ep->name, ++ ep->dwc_ep.is_in ? "IN" : "OUT", ++ usb_req); ++ ++ /* make sure it's actually queued on this endpoint */ ++ list_for_each_entry(req, &ep->queue, queue) ++ { ++ if (&req->req == usb_req) { ++ break; ++ } ++ } ++ ++ if (&req->req != usb_req) { ++ SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); ++ return -EINVAL; ++ } ++ ++ if (!list_empty(&req->queue)) { ++ dwc_otg_request_done(ep, req, -ECONNRESET); ++ } ++ else { ++ req = 0; ++ } ++ ++ SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); ++ ++ return req ? 0 : -EOPNOTSUPP; ++} ++ ++/** ++ * usb_ep_set_halt stalls an endpoint. ++ * ++ * usb_ep_clear_halt clears an endpoint halt and resets its data ++ * toggle. ++ * ++ * Both of these functions are implemented with the same underlying ++ * function. The behavior depends on the value argument. ++ * ++ * @param[in] usb_ep the Endpoint to halt or clear halt. ++ * @param[in] value ++ * - 0 means clear_halt. ++ * - 1 means set_halt, ++ * - 2 means clear stall lock flag. ++ * - 3 means set stall lock flag. ++ */ ++static int dwc_otg_pcd_ep_set_halt(struct usb_ep *usb_ep, int value) ++{ ++ int retval = 0; ++ unsigned long flags; ++ dwc_otg_pcd_ep_t *ep = 0; ++ ++ ++ DWC_DEBUGPL(DBG_PCD,"HALT %s %d\n", usb_ep->name, value); ++ ++ ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep); ++ ++ if (!usb_ep || (!ep->desc && ep != &ep->pcd->ep0) || ++ ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { ++ DWC_WARN("%s, bad ep\n", __func__); ++ return -EINVAL; ++ } ++ ++ SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags); ++ if (!list_empty(&ep->queue)) { ++ DWC_WARN("%s() %s XFer In process\n", __func__, usb_ep->name); ++ retval = -EAGAIN; ++ } ++ else if (value == 0) { ++ dwc_otg_ep_clear_stall(ep->pcd->otg_dev->core_if, ++ &ep->dwc_ep); ++ } ++ else if(value == 1) { ++ if (ep->dwc_ep.is_in == 1 && ep->pcd->otg_dev->core_if->dma_desc_enable) { ++ dtxfsts_data_t txstatus; ++ fifosize_data_t txfifosize; ++ ++ txfifosize.d32 = dwc_read_reg32(&ep->pcd->otg_dev->core_if->core_global_regs->dptxfsiz_dieptxf[ep->dwc_ep.tx_fifo_num]); ++ txstatus.d32 = dwc_read_reg32(&ep->pcd->otg_dev->core_if->dev_if->in_ep_regs[ep->dwc_ep.num]->dtxfsts); ++ ++ if(txstatus.b.txfspcavail < txfifosize.b.depth) { ++ DWC_WARN("%s() %s Data In Tx Fifo\n", __func__, usb_ep->name); ++ retval = -EAGAIN; ++ } ++ else { ++ if (ep->dwc_ep.num == 0) { ++ ep->pcd->ep0state = EP0_STALL; ++ } ++ ++ ep->stopped = 1; ++ dwc_otg_ep_set_stall(ep->pcd->otg_dev->core_if, ++ &ep->dwc_ep); ++ } ++ } ++ else { ++ if (ep->dwc_ep.num == 0) { ++ ep->pcd->ep0state = EP0_STALL; ++ } ++ ++ ep->stopped = 1; ++ dwc_otg_ep_set_stall(ep->pcd->otg_dev->core_if, ++ &ep->dwc_ep); ++ } ++ } ++ else if (value == 2) { ++ ep->dwc_ep.stall_clear_flag = 0; ++ } ++ else if (value == 3) { ++ ep->dwc_ep.stall_clear_flag = 1; ++ } ++ ++ SPIN_UNLOCK_IRQRESTORE(&ep->pcd->lock, flags); ++ return retval; ++} ++ ++/** ++ * This function allocates a DMA Descriptor chain for the Endpoint ++ * buffer to be used for a transfer to/from the specified endpoint. ++ */ ++dwc_otg_dma_desc_t* dwc_otg_ep_alloc_desc_chain(uint32_t * dma_desc_addr, uint32_t count) ++{ ++ ++ return dma_alloc_coherent(NULL, count * sizeof(dwc_otg_dma_desc_t), dma_desc_addr, GFP_KERNEL); ++} ++ ++/** ++ * This function frees a DMA Descriptor chain that was allocated by ep_alloc_desc. ++ */ ++void dwc_otg_ep_free_desc_chain(dwc_otg_dma_desc_t* desc_addr, uint32_t dma_desc_addr, uint32_t count) ++{ ++ dma_free_coherent(NULL, count * sizeof(dwc_otg_dma_desc_t), desc_addr, dma_desc_addr); ++} ++ ++#ifdef DWC_EN_ISOC ++ ++/** ++ * This function initializes a descriptor chain for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP to start the transfer on. ++ * ++ */ ++void dwc_otg_iso_ep_start_ddma_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *dwc_ep) ++{ ++ ++ dsts_data_t dsts = { .d32 = 0}; ++ depctl_data_t depctl = { .d32 = 0 }; ++ volatile uint32_t *addr; ++ int i, j; ++ ++ if(dwc_ep->is_in) ++ dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl / dwc_ep->bInterval; ++ else ++ dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / dwc_ep->bInterval; ++ ++ ++ /** Allocate descriptors for double buffering */ ++ dwc_ep->iso_desc_addr = dwc_otg_ep_alloc_desc_chain(&dwc_ep->iso_dma_desc_addr,dwc_ep->desc_cnt*2); ++ if(dwc_ep->desc_addr) { ++ DWC_WARN("%s, can't allocate DMA descriptor chain\n", __func__); ++ return; ++ } ++ ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ /** ISO OUT EP */ ++ if(dwc_ep->is_in == 0) { ++ desc_sts_data_t sts = { .d32 =0 }; ++ dwc_otg_dma_desc_t* dma_desc = dwc_ep->iso_desc_addr; ++ dma_addr_t dma_ad; ++ uint32_t data_per_desc; ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[dwc_ep->num]; ++ int offset; ++ ++ addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl; ++ dma_ad = (dma_addr_t)dwc_read_reg32(&(out_regs->doepdma)); ++ ++ /** Buffer 0 descriptors setup */ ++ dma_ad = dwc_ep->dma_addr0; ++ ++ sts.b_iso_out.bs = BS_HOST_READY; ++ sts.b_iso_out.rxsts = 0; ++ sts.b_iso_out.l = 0; ++ sts.b_iso_out.sp = 0; ++ sts.b_iso_out.ioc = 0; ++ sts.b_iso_out.pid = 0; ++ sts.b_iso_out.framenum = 0; ++ ++ offset = 0; ++ for(i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; i+= dwc_ep->pkt_per_frm) ++ { ++ ++ for(j = 0; j < dwc_ep->pkt_per_frm; ++j) ++ { ++ data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ ++ data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ ++ offset += data_per_desc; ++ dma_desc ++; ++ (uint32_t)dma_ad += data_per_desc; ++ } ++ } ++ ++ for(j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) ++ { ++ data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ ++ offset += data_per_desc; ++ dma_desc ++; ++ (uint32_t)dma_ad += data_per_desc; ++ } ++ ++ sts.b_iso_out.ioc = 1; ++ data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ dma_desc ++; ++ ++ /** Buffer 1 descriptors setup */ ++ sts.b_iso_out.ioc = 0; ++ dma_ad = dwc_ep->dma_addr1; ++ ++ offset = 0; ++ for(i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; i+= dwc_ep->pkt_per_frm) ++ { ++ for(j = 0; j < dwc_ep->pkt_per_frm; ++j) ++ { ++ data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ ++ offset += data_per_desc; ++ dma_desc ++; ++ (uint32_t)dma_ad += data_per_desc; ++ } ++ } ++ for(j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) ++ { ++ data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ ++ offset += data_per_desc; ++ dma_desc ++; ++ (uint32_t)dma_ad += data_per_desc; ++ } ++ ++ sts.b_iso_out.ioc = 1; ++ sts.b_iso_out.l = 1; ++ data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ ++ dwc_ep->next_frame = 0; ++ ++ /** Write dma_ad into DOEPDMA register */ ++ dwc_write_reg32(&(out_regs->doepdma),(uint32_t)dwc_ep->iso_dma_desc_addr); ++ ++ } ++ /** ISO IN EP */ ++ else { ++ desc_sts_data_t sts = { .d32 =0 }; ++ dwc_otg_dma_desc_t* dma_desc = dwc_ep->iso_desc_addr; ++ dma_addr_t dma_ad; ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[dwc_ep->num]; ++ unsigned int frmnumber; ++ fifosize_data_t txfifosize,rxfifosize; ++ ++ txfifosize.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[dwc_ep->num]->dtxfsts); ++ rxfifosize.d32 = dwc_read_reg32(&core_if->core_global_regs->grxfsiz); ++ ++ ++ addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl; ++ ++ dma_ad = dwc_ep->dma_addr0; ++ ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ sts.b_iso_in.bs = BS_HOST_READY; ++ sts.b_iso_in.txsts = 0; ++ sts.b_iso_in.sp = (dwc_ep->data_per_frame % dwc_ep->maxpacket)? 1 : 0; ++ sts.b_iso_in.ioc = 0; ++ sts.b_iso_in.pid = dwc_ep->pkt_per_frm; ++ ++ ++ frmnumber = dwc_ep->next_frame; ++ ++ sts.b_iso_in.framenum = frmnumber; ++ sts.b_iso_in.txbytes = dwc_ep->data_per_frame; ++ sts.b_iso_in.l = 0; ++ ++ /** Buffer 0 descriptors setup */ ++ for(i = 0; i < dwc_ep->desc_cnt - 1; i++) ++ { ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ dma_desc ++; ++ ++ (uint32_t)dma_ad += dwc_ep->data_per_frame; ++ sts.b_iso_in.framenum += dwc_ep->bInterval; ++ } ++ ++ sts.b_iso_in.ioc = 1; ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ ++dma_desc; ++ ++ /** Buffer 1 descriptors setup */ ++ sts.b_iso_in.ioc = 0; ++ dma_ad = dwc_ep->dma_addr1; ++ ++ for(i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; i+= dwc_ep->pkt_per_frm) ++ { ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ dma_desc ++; ++ ++ (uint32_t)dma_ad += dwc_ep->data_per_frame; ++ sts.b_iso_in.framenum += dwc_ep->bInterval; ++ ++ sts.b_iso_in.ioc = 0; ++ } ++ sts.b_iso_in.ioc = 1; ++ sts.b_iso_in.l = 1; ++ ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ ++ dwc_ep->next_frame = sts.b_iso_in.framenum + dwc_ep->bInterval; ++ ++ /** Write dma_ad into diepdma register */ ++ dwc_write_reg32(&(in_regs->diepdma),(uint32_t)dwc_ep->iso_dma_desc_addr); ++ } ++ /** Enable endpoint, clear nak */ ++ depctl.d32 = 0; ++ depctl.b.epena = 1; ++ depctl.b.usbactep = 1; ++ depctl.b.cnak = 1; ++ ++ dwc_modify_reg32(addr, depctl.d32,depctl.d32); ++ depctl.d32 = dwc_read_reg32(addr); ++} ++ ++/** ++ * This function initializes a descriptor chain for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++ ++void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ depctl_data_t depctl = { .d32 = 0 }; ++ volatile uint32_t *addr; ++ ++ ++ if(ep->is_in) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ } else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ } ++ ++ ++ if(core_if->dma_enable == 0 || core_if->dma_desc_enable!= 0) { ++ return; ++ } else { ++ deptsiz_data_t deptsiz = { .d32 = 0 }; ++ ++ ep->xfer_len = ep->data_per_frame * ep->buf_proc_intrvl / ep->bInterval; ++ ep->pkt_cnt = (ep->xfer_len - 1 + ep->maxpacket) / ++ ep->maxpacket; ++ ep->xfer_count = 0; ++ ep->xfer_buff = (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0; ++ ep->dma_addr = (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0; ++ ++ if(ep->is_in) { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.mc = ep->pkt_per_frm; ++ deptsiz.b.xfersize = ep->xfer_len; ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - 1 + ep->maxpacket) / ++ ep->maxpacket; ++ dwc_write_reg32(&core_if->dev_if->in_ep_regs[ep->num]->dieptsiz, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ dwc_write_reg32 (&(core_if->dev_if->in_ep_regs[ep->num]->diepdma), (uint32_t)ep->dma_addr); ++ ++ } else { ++ deptsiz.b.pktcnt = ++ (ep->xfer_len + (ep->maxpacket - 1)) / ++ ep->maxpacket; ++ deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket; ++ ++ dwc_write_reg32(&core_if->dev_if->out_ep_regs[ep->num]->doeptsiz, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ dwc_write_reg32 (&(core_if->dev_if->out_ep_regs[ep->num]->doepdma), (uint32_t)ep->dma_addr); ++ ++ } ++ /** Enable endpoint, clear nak */ ++ depctl.d32 = 0; ++ dwc_modify_reg32(addr, depctl.d32,depctl.d32); ++ ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ ++ dwc_modify_reg32(addr, depctl.d32,depctl.d32); ++ } ++} ++ ++ ++/** ++ * This function does the setup for a data transfer for an EP and ++ * starts the transfer. For an IN transfer, the packets will be ++ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, ++ * the packets are unloaded from the Rx FIFO in the ISR. the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++ ++void dwc_otg_iso_ep_start_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ if(core_if->dma_enable) { ++ if(core_if->dma_desc_enable) { ++ if(ep->is_in) { ++ ep->desc_cnt = ep->pkt_cnt / ep->pkt_per_frm; ++ } else { ++ ep->desc_cnt = ep->pkt_cnt; ++ } ++ dwc_otg_iso_ep_start_ddma_transfer(core_if, ep); ++ } else { ++ if(core_if->pti_enh_enable) { ++ dwc_otg_iso_ep_start_buf_transfer(core_if, ep); ++ } else { ++ ep->cur_pkt_addr = (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0; ++ ep->cur_pkt_dma_addr = (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0; ++ dwc_otg_iso_ep_start_frm_transfer(core_if, ep); ++ } ++ } ++ } else { ++ ep->cur_pkt_addr = (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0; ++ ep->cur_pkt_dma_addr = (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0; ++ dwc_otg_iso_ep_start_frm_transfer(core_if, ep); ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for an EP and ++ * starts the transfer. For an IN transfer, the packets will be ++ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, ++ * the packets are unloaded from the Rx FIFO in the ISR. the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++ ++void dwc_otg_iso_ep_stop_transfer(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ depctl_data_t depctl = { .d32 = 0 }; ++ volatile uint32_t *addr; ++ ++ if(ep->is_in == 1) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ } ++ else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ } ++ ++ /* disable the ep */ ++ depctl.d32 = dwc_read_reg32(addr); ++ ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ ++ dwc_write_reg32(addr, depctl.d32); ++ ++ if(core_if->dma_desc_enable && ++ ep->iso_desc_addr && ep->iso_dma_desc_addr) { ++ dwc_otg_ep_free_desc_chain(ep->iso_desc_addr,ep->iso_dma_desc_addr,ep->desc_cnt * 2); ++ } ++ ++ /* reset varibales */ ++ ep->dma_addr0 = 0; ++ ep->dma_addr1 = 0; ++ ep->xfer_buff0 = 0; ++ ep->xfer_buff1 = 0; ++ ep->data_per_frame = 0; ++ ep->data_pattern_frame = 0; ++ ep->sync_frame = 0; ++ ep->buf_proc_intrvl = 0; ++ ep->bInterval = 0; ++ ep->proc_buf_num = 0; ++ ep->pkt_per_frm = 0; ++ ep->pkt_per_frm = 0; ++ ep->desc_cnt = 0; ++ ep->iso_desc_addr = 0; ++ ep->iso_dma_desc_addr = 0; ++} ++ ++ ++/** ++ * This function is used to submit an ISOC Transfer Request to an EP. ++ * ++ * - Every time a sync period completes the request's completion callback ++ * is called to provide data to the gadget driver. ++ * - Once submitted the request cannot be modified. ++ * - Each request is turned into periodic data packets untill ISO ++ * Transfer is stopped.. ++ */ ++static int dwc_otg_pcd_iso_ep_start(struct usb_ep *usb_ep, struct usb_iso_request *req, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ int gfp_flags ++#else ++ gfp_t gfp_flags ++#endif ++) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_pcd_t *pcd; ++ dwc_ep_t *dwc_ep; ++ unsigned long flags = 0; ++ int32_t frm_data; ++ dwc_otg_core_if_t *core_if; ++ dcfg_data_t dcfg; ++ dsts_data_t dsts; ++ ++ ++ if (!req || !req->process_buffer || !req->buf0 || !req->buf1) { ++ DWC_WARN("%s, bad params\n", __func__); ++ return -EINVAL; ++ } ++ ++ ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep); ++ ++ if (!usb_ep || !ep->desc || ep->dwc_ep.num == 0) { ++ DWC_WARN("%s, bad ep\n", __func__); ++ return -EINVAL; ++ } ++ ++ pcd = ep->pcd; ++ core_if = GET_CORE_IF(pcd); ++ ++ dcfg.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dcfg); ++ ++ if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", pcd->gadget.speed); ++ DWC_WARN("%s, bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ SPIN_LOCK_IRQSAVE(&ep->pcd->lock, flags); ++ ++ dwc_ep = &ep->dwc_ep; ++ ++ if(ep->iso_req) { ++ DWC_WARN("%s, iso request in progress\n", __func__); ++ } ++ req->status = -EINPROGRESS; ++ ++ dwc_ep->dma_addr0 = req->dma0; ++ dwc_ep->dma_addr1 = req->dma1; ++ ++ dwc_ep->xfer_buff0 = req->buf0; ++ dwc_ep->xfer_buff1 = req->buf1; ++ ++ ep->iso_req = req; ++ ++ dwc_ep->data_per_frame = req->data_per_frame; ++ ++ /** @todo - pattern data support is to be implemented in the future */ ++ dwc_ep->data_pattern_frame = req->data_pattern_frame; ++ dwc_ep->sync_frame = req->sync_frame; ++ ++ dwc_ep->buf_proc_intrvl = req->buf_proc_intrvl; ++ ++ dwc_ep->bInterval = 1 << (ep->desc->bInterval - 1); ++ ++ dwc_ep->proc_buf_num = 0; ++ ++ dwc_ep->pkt_per_frm = 0; ++ frm_data = ep->dwc_ep.data_per_frame; ++ while(frm_data > 0) { ++ dwc_ep->pkt_per_frm++; ++ frm_data -= ep->dwc_ep.maxpacket; ++ } ++ ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ if(req->flags & USB_REQ_ISO_ASAP) { ++ dwc_ep->next_frame = dsts.b.soffn + 1; ++ if(dwc_ep->bInterval != 1){ ++ dwc_ep->next_frame = dwc_ep->next_frame + (dwc_ep->bInterval - 1 - dwc_ep->next_frame % dwc_ep->bInterval); ++ } ++ } else { ++ dwc_ep->next_frame = req->start_frame; ++ } ++ ++ ++ if(!core_if->pti_enh_enable) { ++ dwc_ep->pkt_cnt = dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / dwc_ep->bInterval; ++ } else { ++ dwc_ep->pkt_cnt = ++ (dwc_ep->data_per_frame * (dwc_ep->buf_proc_intrvl / dwc_ep->bInterval) ++ - 1 + dwc_ep->maxpacket) / dwc_ep->maxpacket; ++ } ++ ++ if(core_if->dma_desc_enable) { ++ dwc_ep->desc_cnt = ++ dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / dwc_ep->bInterval; ++ } ++ ++ dwc_ep->pkt_info = kmalloc(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt, GFP_KERNEL); ++ if(!dwc_ep->pkt_info) { ++ return -ENOMEM; ++ } ++ if(core_if->pti_enh_enable) { ++ memset(dwc_ep->pkt_info, 0, sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt); ++ } ++ ++ dwc_ep->cur_pkt = 0; ++ ++ SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); ++ ++ dwc_otg_iso_ep_start_transfer(core_if, dwc_ep); ++ ++ return 0; ++} ++ ++/** ++ * This function stops ISO EP Periodic Data Transfer. ++ */ ++static int dwc_otg_pcd_iso_ep_stop(struct usb_ep *usb_ep, struct usb_iso_request *req) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_pcd_t *pcd; ++ dwc_ep_t *dwc_ep; ++ unsigned long flags; ++ ++ ep = container_of(usb_ep, dwc_otg_pcd_ep_t, ep); ++ ++ if (!usb_ep || !ep->desc || ep->dwc_ep.num == 0) { ++ DWC_WARN("%s, bad ep\n", __func__); ++ return -EINVAL; ++ } ++ ++ pcd = ep->pcd; ++ ++ if (!pcd->driver || pcd->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", pcd->gadget.speed); ++ DWC_WARN("%s, bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ dwc_ep = &ep->dwc_ep; ++ ++ dwc_otg_iso_ep_stop_transfer(GET_CORE_IF(pcd), dwc_ep); ++ ++ kfree(dwc_ep->pkt_info); ++ ++ SPIN_LOCK_IRQSAVE(&pcd->lock, flags); ++ ++ if(ep->iso_req != req) { ++ return -EINVAL; ++ } ++ ++ req->status = -ECONNRESET; ++ ++ SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); ++ ++ ++ ep->iso_req = 0; ++ ++ return 0; ++} ++ ++/** ++ * This function is used for perodical data exchnage between PCD and gadget drivers. ++ * for Isochronous EPs ++ * ++ * - Every time a sync period completes this function is called to ++ * perform data exchange between PCD and gadget ++ */ ++void dwc_otg_iso_buffer_done(dwc_otg_pcd_ep_t *ep, dwc_otg_pcd_iso_request_t *req) ++{ ++ int i; ++ struct usb_gadget_iso_packet_descriptor *iso_packet; ++ dwc_ep_t *dwc_ep; ++ ++ dwc_ep = &ep->dwc_ep; ++ ++ if(ep->iso_req->status == -ECONNRESET) { ++ DWC_PRINT("Device has already disconnected\n"); ++ /*Device has been disconnected*/ ++ return; ++ } ++ ++ if(dwc_ep->proc_buf_num != 0) { ++ iso_packet = ep->iso_req->iso_packet_desc0; ++ } ++ ++ else { ++ iso_packet = ep->iso_req->iso_packet_desc1; ++ } ++ ++ /* Fill in ISOC packets descriptors & pass to gadget driver*/ ++ ++ for(i = 0; i < dwc_ep->pkt_cnt; ++i) { ++ iso_packet[i].status = dwc_ep->pkt_info[i].status; ++ iso_packet[i].offset = dwc_ep->pkt_info[i].offset; ++ iso_packet[i].actual_length = dwc_ep->pkt_info[i].length; ++ dwc_ep->pkt_info[i].status = 0; ++ dwc_ep->pkt_info[i].offset = 0; ++ dwc_ep->pkt_info[i].length = 0; ++ } ++ ++ /* Call callback function to process data buffer */ ++ ep->iso_req->status = 0;/* success */ ++ ++ SPIN_UNLOCK(&ep->pcd->lock); ++ ep->iso_req->process_buffer(&ep->ep, ep->iso_req); ++ SPIN_LOCK(&ep->pcd->lock); ++} ++ ++ ++static struct usb_iso_request *dwc_otg_pcd_alloc_iso_request(struct usb_ep *ep,int packets, ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ int gfp_flags ++#else ++ gfp_t gfp_flags ++#endif ++) ++{ ++ struct usb_iso_request *pReq = NULL; ++ uint32_t req_size; ++ ++ ++ req_size = sizeof(struct usb_iso_request); ++ req_size += (2 * packets * (sizeof(struct usb_gadget_iso_packet_descriptor))); ++ ++ ++ pReq = kmalloc(req_size, gfp_flags); ++ if (!pReq) { ++ DWC_WARN("%s, can't allocate Iso Request\n", __func__); ++ return 0; ++ } ++ pReq->iso_packet_desc0 = (void*) (pReq + 1); ++ ++ pReq->iso_packet_desc1 = pReq->iso_packet_desc0 + packets; ++ ++ return pReq; ++} ++ ++static void dwc_otg_pcd_free_iso_request(struct usb_ep *ep, struct usb_iso_request *req) ++{ ++ kfree(req); ++} ++ ++static struct usb_isoc_ep_ops dwc_otg_pcd_ep_ops = ++{ ++ .ep_ops = ++ { ++ .enable = dwc_otg_pcd_ep_enable, ++ .disable = dwc_otg_pcd_ep_disable, ++ ++ .alloc_request = dwc_otg_pcd_alloc_request, ++ .free_request = dwc_otg_pcd_free_request, ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) ++ .alloc_buffer = dwc_otg_pcd_alloc_buffer, ++ .free_buffer = dwc_otg_pcd_free_buffer, ++#endif ++ ++ .queue = dwc_otg_pcd_ep_queue, ++ .dequeue = dwc_otg_pcd_ep_dequeue, ++ ++ .set_halt = dwc_otg_pcd_ep_set_halt, ++ .fifo_status = 0, ++ .fifo_flush = 0, ++ }, ++ .iso_ep_start = dwc_otg_pcd_iso_ep_start, ++ .iso_ep_stop = dwc_otg_pcd_iso_ep_stop, ++ .alloc_iso_request = dwc_otg_pcd_alloc_iso_request, ++ .free_iso_request = dwc_otg_pcd_free_iso_request, ++}; ++ ++#else ++ ++ ++static struct usb_ep_ops dwc_otg_pcd_ep_ops = ++{ ++ .enable = dwc_otg_pcd_ep_enable, ++ .disable = dwc_otg_pcd_ep_disable, ++ ++ .alloc_request = dwc_otg_pcd_alloc_request, ++ .free_request = dwc_otg_pcd_free_request, ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23) ++ .alloc_buffer = dwc_otg_pcd_alloc_buffer, ++ .free_buffer = dwc_otg_pcd_free_buffer, ++#endif ++ ++ .queue = dwc_otg_pcd_ep_queue, ++ .dequeue = dwc_otg_pcd_ep_dequeue, ++ ++ .set_halt = dwc_otg_pcd_ep_set_halt, ++ .fifo_status = 0, ++ .fifo_flush = 0, ++ ++ ++}; ++ ++#endif /* DWC_EN_ISOC */ ++/* Gadget Operations */ ++/** ++ * The following gadget operations will be implemented in the DWC_otg ++ * PCD. Functions in the API that are not described below are not ++ * implemented. ++ * ++ * The Gadget API provides wrapper functions for each of the function ++ * pointers defined in usb_gadget_ops. The Gadget Driver calls the ++ * wrapper function, which then calls the underlying PCD function. The ++ * following sections are named according to the wrapper functions ++ * (except for ioctl, which doesn't have a wrapper function). Within ++ * each section, the corresponding DWC_otg PCD function name is ++ * specified. ++ * ++ */ ++ ++/** ++ *Gets the USB Frame number of the last SOF. ++ */ ++static int dwc_otg_pcd_get_frame(struct usb_gadget *gadget) ++{ ++ dwc_otg_pcd_t *pcd; ++ ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, gadget); ++ ++ if (gadget == 0) { ++ return -ENODEV; ++ } ++ else { ++ pcd = container_of(gadget, dwc_otg_pcd_t, gadget); ++ dwc_otg_get_frame_number(GET_CORE_IF(pcd)); ++ } ++ ++ return 0; ++} ++ ++void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t *pcd) ++{ ++ uint32_t *addr = (uint32_t *)&(GET_CORE_IF(pcd)->core_global_regs->gotgctl); ++ gotgctl_data_t mem; ++ gotgctl_data_t val; ++ ++ val.d32 = dwc_read_reg32(addr); ++ if (val.b.sesreq) { ++ DWC_ERROR("Session Request Already active!\n"); ++ return; ++ } ++ ++ DWC_NOTICE("Session Request Initated\n"); ++ mem.d32 = dwc_read_reg32(addr); ++ mem.b.sesreq = 1; ++ dwc_write_reg32(addr, mem.d32); ++ ++ /* Start the SRP timer */ ++ dwc_otg_pcd_start_srp_timer(pcd); ++ return; ++} ++ ++void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t *pcd, int set) ++{ ++ dctl_data_t dctl = {.d32=0}; ++ volatile uint32_t *addr = &(GET_CORE_IF(pcd)->dev_if->dev_global_regs->dctl); ++ ++ if (dwc_otg_is_device_mode(GET_CORE_IF(pcd))) { ++ if (pcd->remote_wakeup_enable) { ++ if (set) { ++ dctl.b.rmtwkupsig = 1; ++ dwc_modify_reg32(addr, 0, dctl.d32); ++ DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n"); ++ mdelay(1); ++ dwc_modify_reg32(addr, dctl.d32, 0); ++ DWC_DEBUGPL(DBG_PCD, "Clear Remote Wakeup\n"); ++ } ++ else { ++ } ++ } ++ else { ++ DWC_DEBUGPL(DBG_PCD, "Remote Wakeup is disabled\n"); ++ } ++ } ++ return; ++} ++ ++/** ++ * Initiates Session Request Protocol (SRP) to wakeup the host if no ++ * session is in progress. If a session is already in progress, but ++ * the device is suspended, remote wakeup signaling is started. ++ * ++ */ ++static int dwc_otg_pcd_wakeup(struct usb_gadget *gadget) ++{ ++ unsigned long flags; ++ dwc_otg_pcd_t *pcd; ++ dsts_data_t dsts; ++ gotgctl_data_t gotgctl; ++ ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, gadget); ++ ++ if (gadget == 0) { ++ return -ENODEV; ++ } ++ else { ++ pcd = container_of(gadget, dwc_otg_pcd_t, gadget); ++ } ++ SPIN_LOCK_IRQSAVE(&pcd->lock, flags); ++ ++ /* ++ * This function starts the Protocol if no session is in progress. If ++ * a session is already in progress, but the device is suspended, ++ * remote wakeup signaling is started. ++ */ ++ ++ /* Check if valid session */ ++ gotgctl.d32 = dwc_read_reg32(&(GET_CORE_IF(pcd)->core_global_regs->gotgctl)); ++ if (gotgctl.b.bsesvld) { ++ /* Check if suspend state */ ++ dsts.d32 = dwc_read_reg32(&(GET_CORE_IF(pcd)->dev_if->dev_global_regs->dsts)); ++ if (dsts.b.suspsts) { ++ dwc_otg_pcd_remote_wakeup(pcd, 1); ++ } ++ } ++ else { ++ dwc_otg_pcd_initiate_srp(pcd); ++ } ++ ++ SPIN_UNLOCK_IRQRESTORE(&pcd->lock, flags); ++ return 0; ++} ++ ++static const struct usb_gadget_ops dwc_otg_pcd_ops = ++{ ++ .get_frame = dwc_otg_pcd_get_frame, ++ .wakeup = dwc_otg_pcd_wakeup, ++ // current versions must always be self-powered ++}; ++ ++/** ++ * This function updates the otg values in the gadget structure. ++ */ ++void dwc_otg_pcd_update_otg(dwc_otg_pcd_t *pcd, const unsigned reset) ++{ ++ ++ if (!pcd->gadget.is_otg) ++ return; ++ ++ if (reset) { ++ pcd->b_hnp_enable = 0; ++ pcd->a_hnp_support = 0; ++ pcd->a_alt_hnp_support = 0; ++ } ++ ++ pcd->gadget.b_hnp_enable = pcd->b_hnp_enable; ++ pcd->gadget.a_hnp_support = pcd->a_hnp_support; ++ pcd->gadget.a_alt_hnp_support = pcd->a_alt_hnp_support; ++} ++ ++/** ++ * This function is the top level PCD interrupt handler. ++ */ ++static irqreturn_t dwc_otg_pcd_irq(int irq, void *dev ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) ++ , struct pt_regs *r ++#endif ++ ) ++{ ++ dwc_otg_pcd_t *pcd = dev; ++ int32_t retval = IRQ_NONE; ++ ++ retval = dwc_otg_pcd_handle_intr(pcd); ++ return IRQ_RETVAL(retval); ++} ++ ++/** ++ * PCD Callback function for initializing the PCD when switching to ++ * device mode. ++ * ++ * @param p void pointer to the dwc_otg_pcd_t ++ */ ++static int32_t dwc_otg_pcd_start_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p; ++ ++ /* ++ * Initialized the Core for Device mode. ++ */ ++ if (dwc_otg_is_device_mode(GET_CORE_IF(pcd))) { ++ dwc_otg_core_dev_init(GET_CORE_IF(pcd)); ++ } ++ return 1; ++} ++ ++/** ++ * PCD Callback function for stopping the PCD when switching to Host ++ * mode. ++ * ++ * @param p void pointer to the dwc_otg_pcd_t ++ */ ++static int32_t dwc_otg_pcd_stop_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p; ++ extern void dwc_otg_pcd_stop(dwc_otg_pcd_t *_pcd); ++ ++ dwc_otg_pcd_stop(pcd); ++ return 1; ++} ++ ++ ++/** ++ * PCD Callback function for notifying the PCD when resuming from ++ * suspend. ++ * ++ * @param p void pointer to the dwc_otg_pcd_t ++ */ ++static int32_t dwc_otg_pcd_suspend_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p; ++ ++ if (pcd->driver && pcd->driver->resume) { ++ SPIN_UNLOCK(&pcd->lock); ++ pcd->driver->suspend(&pcd->gadget); ++ SPIN_LOCK(&pcd->lock); ++ } ++ ++ return 1; ++} ++ ++ ++/** ++ * PCD Callback function for notifying the PCD when resuming from ++ * suspend. ++ * ++ * @param p void pointer to the dwc_otg_pcd_t ++ */ ++static int32_t dwc_otg_pcd_resume_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)p; ++ ++ if (pcd->driver && pcd->driver->resume) { ++ SPIN_UNLOCK(&pcd->lock); ++ pcd->driver->resume(&pcd->gadget); ++ SPIN_LOCK(&pcd->lock); ++ } ++ ++ /* Stop the SRP timeout timer. */ ++ if ((GET_CORE_IF(pcd)->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS) || ++ (!GET_CORE_IF(pcd)->core_params->i2c_enable)) { ++ if (GET_CORE_IF(pcd)->srp_timer_started) { ++ GET_CORE_IF(pcd)->srp_timer_started = 0; ++ del_timer(&pcd->srp_timer); ++ } ++ } ++ return 1; ++} ++ ++ ++/** ++ * PCD Callback structure for handling mode switching. ++ */ ++static dwc_otg_cil_callbacks_t pcd_callbacks = ++{ ++ .start = dwc_otg_pcd_start_cb, ++ .stop = dwc_otg_pcd_stop_cb, ++ .suspend = dwc_otg_pcd_suspend_cb, ++ .resume_wakeup = dwc_otg_pcd_resume_cb, ++ .p = 0, /* Set at registration */ ++}; ++ ++/** ++ * This function is called when the SRP timer expires. The SRP should ++ * complete within 6 seconds. ++ */ ++static void srp_timeout(unsigned long ptr) ++{ ++ gotgctl_data_t gotgctl; ++ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *)ptr; ++ volatile uint32_t *addr = &core_if->core_global_regs->gotgctl; ++ ++ gotgctl.d32 = dwc_read_reg32(addr); ++ ++ core_if->srp_timer_started = 0; ++ ++ if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) && ++ (core_if->core_params->i2c_enable)) { ++ DWC_PRINT("SRP Timeout\n"); ++ ++ if ((core_if->srp_success) && ++ (gotgctl.b.bsesvld)) { ++ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { ++ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); ++ } ++ ++ /* Clear Session Request */ ++ gotgctl.d32 = 0; ++ gotgctl.b.sesreq = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gotgctl, ++ gotgctl.d32, 0); ++ ++ core_if->srp_success = 0; ++ } ++ else { ++ DWC_ERROR("Device not connected/responding\n"); ++ gotgctl.b.sesreq = 0; ++ dwc_write_reg32(addr, gotgctl.d32); ++ } ++ } ++ else if (gotgctl.b.sesreq) { ++ DWC_PRINT("SRP Timeout\n"); ++ ++ DWC_ERROR("Device not connected/responding\n"); ++ gotgctl.b.sesreq = 0; ++ dwc_write_reg32(addr, gotgctl.d32); ++ } ++ else { ++ DWC_PRINT(" SRP GOTGCTL=%0x\n", gotgctl.d32); ++ } ++} ++ ++/** ++ * Start the SRP timer to detect when the SRP does not complete within ++ * 6 seconds. ++ * ++ * @param pcd the pcd structure. ++ */ ++void dwc_otg_pcd_start_srp_timer(dwc_otg_pcd_t *pcd) ++{ ++ struct timer_list *srp_timer = &pcd->srp_timer; ++ GET_CORE_IF(pcd)->srp_timer_started = 1; ++ init_timer(srp_timer); ++ srp_timer->function = srp_timeout; ++ srp_timer->data = (unsigned long)GET_CORE_IF(pcd); ++ srp_timer->expires = jiffies + (HZ*6); ++ add_timer(srp_timer); ++} ++ ++/** ++ * Tasklet ++ * ++ */ ++extern void start_next_request(dwc_otg_pcd_ep_t *ep); ++ ++static void start_xfer_tasklet_func (unsigned long data) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t*)data; ++ dwc_otg_core_if_t *core_if = pcd->otg_dev->core_if; ++ ++ int i; ++ depctl_data_t diepctl; ++ ++ DWC_DEBUGPL(DBG_PCDV, "Start xfer tasklet\n"); ++ ++ diepctl.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[0]->diepctl); ++ ++ if (pcd->ep0.queue_sof) { ++ pcd->ep0.queue_sof = 0; ++ start_next_request (&pcd->ep0); ++ // break; ++ } ++ ++ for (i=0; idev_if->num_in_eps; i++) ++ { ++ depctl_data_t diepctl; ++ diepctl.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[i]->diepctl); ++ ++ if (pcd->in_ep[i].queue_sof) { ++ pcd->in_ep[i].queue_sof = 0; ++ start_next_request (&pcd->in_ep[i]); ++ // break; ++ } ++ } ++ ++ return; ++} ++ ++ ++ ++ ++ ++ ++ ++static struct tasklet_struct start_xfer_tasklet = { ++ .next = NULL, ++ .state = 0, ++ .count = ATOMIC_INIT(0), ++ .func = start_xfer_tasklet_func, ++ .data = 0, ++}; ++/** ++ * This function initialized the pcd Dp structures to there default ++ * state. ++ * ++ * @param pcd the pcd structure. ++ */ ++void dwc_otg_pcd_reinit(dwc_otg_pcd_t *pcd) ++{ ++ static const char * names[] = ++ { ++ ++ "ep0", ++ "ep1in", ++ "ep2in", ++ "ep3in", ++ "ep4in", ++ "ep5in", ++ "ep6in", ++ "ep7in", ++ "ep8in", ++ "ep9in", ++ "ep10in", ++ "ep11in", ++ "ep12in", ++ "ep13in", ++ "ep14in", ++ "ep15in", ++ "ep1out", ++ "ep2out", ++ "ep3out", ++ "ep4out", ++ "ep5out", ++ "ep6out", ++ "ep7out", ++ "ep8out", ++ "ep9out", ++ "ep10out", ++ "ep11out", ++ "ep12out", ++ "ep13out", ++ "ep14out", ++ "ep15out" ++ ++ }; ++ ++ int i; ++ int in_ep_cntr, out_ep_cntr; ++ uint32_t hwcfg1; ++ uint32_t num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps; ++ uint32_t num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps; ++ dwc_otg_pcd_ep_t *ep; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, pcd); ++ ++ INIT_LIST_HEAD (&pcd->gadget.ep_list); ++ pcd->gadget.ep0 = &pcd->ep0.ep; ++ pcd->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ INIT_LIST_HEAD (&pcd->gadget.ep0->ep_list); ++ ++ /** ++ * Initialize the EP0 structure. ++ */ ++ ep = &pcd->ep0; ++ ++ /* Init EP structure */ ++ ep->desc = 0; ++ ep->pcd = pcd; ++ ep->stopped = 1; ++ ++ /* Init DWC ep structure */ ++ ep->dwc_ep.num = 0; ++ ep->dwc_ep.active = 0; ++ ep->dwc_ep.tx_fifo_num = 0; ++ /* Control until ep is actvated */ ++ ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; ++ ep->dwc_ep.maxpacket = MAX_PACKET_SIZE; ++ ep->dwc_ep.dma_addr = 0; ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = 0; ++ ep->queue_sof = 0; ++ ep->dwc_ep.desc_addr = 0; ++ ep->dwc_ep.dma_desc_addr = 0; ++ ++ ++ /* Init the usb_ep structure. */ ++ ep->ep.name = names[0]; ++ ep->ep.ops = (struct usb_ep_ops*)&dwc_otg_pcd_ep_ops; ++ ++ /** ++ * @todo NGS: What should the max packet size be set to ++ * here? Before EP type is set? ++ */ ++ ep->ep.maxpacket = MAX_PACKET_SIZE; ++ ++ list_add_tail (&ep->ep.ep_list, &pcd->gadget.ep_list); ++ ++ INIT_LIST_HEAD (&ep->queue); ++ /** ++ * Initialize the EP structures. ++ */ ++ in_ep_cntr = 0; ++ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 3; ++ ++ for (i = 1; in_ep_cntr < num_in_eps; i++) ++ { ++ if((hwcfg1 & 0x1) == 0) { ++ dwc_otg_pcd_ep_t *ep = &pcd->in_ep[in_ep_cntr]; ++ in_ep_cntr ++; ++ ++ /* Init EP structure */ ++ ep->desc = 0; ++ ep->pcd = pcd; ++ ep->stopped = 1; ++ ++ /* Init DWC ep structure */ ++ ep->dwc_ep.is_in = 1; ++ ep->dwc_ep.num = i; ++ ep->dwc_ep.active = 0; ++ ep->dwc_ep.tx_fifo_num = 0; ++ ++ /* Control until ep is actvated */ ++ ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; ++ ep->dwc_ep.maxpacket = MAX_PACKET_SIZE; ++ ep->dwc_ep.dma_addr = 0; ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = 0; ++ ep->queue_sof = 0; ++ ep->dwc_ep.desc_addr = 0; ++ ep->dwc_ep.dma_desc_addr = 0; ++ ++ /* Init the usb_ep structure. */ ++ ep->ep.name = names[i]; ++ ep->ep.ops = (struct usb_ep_ops*)&dwc_otg_pcd_ep_ops; ++ ++ /** ++ * @todo NGS: What should the max packet size be set to ++ * here? Before EP type is set? ++ */ ++ ep->ep.maxpacket = MAX_PACKET_SIZE; ++ ++ list_add_tail (&ep->ep.ep_list, &pcd->gadget.ep_list); ++ ++ INIT_LIST_HEAD (&ep->queue); ++ } ++ hwcfg1 >>= 2; ++ } ++ ++ out_ep_cntr = 0; ++ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 2; ++ ++ for (i = 1; out_ep_cntr < num_out_eps; i++) ++ { ++ if((hwcfg1 & 0x1) == 0) { ++ dwc_otg_pcd_ep_t *ep = &pcd->out_ep[out_ep_cntr]; ++ out_ep_cntr++; ++ ++ /* Init EP structure */ ++ ep->desc = 0; ++ ep->pcd = pcd; ++ ep->stopped = 1; ++ ++ /* Init DWC ep structure */ ++ ep->dwc_ep.is_in = 0; ++ ep->dwc_ep.num = i; ++ ep->dwc_ep.active = 0; ++ ep->dwc_ep.tx_fifo_num = 0; ++ /* Control until ep is actvated */ ++ ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; ++ ep->dwc_ep.maxpacket = MAX_PACKET_SIZE; ++ ep->dwc_ep.dma_addr = 0; ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = 0; ++ ep->queue_sof = 0; ++ ++ /* Init the usb_ep structure. */ ++ ep->ep.name = names[15 + i]; ++ ep->ep.ops = (struct usb_ep_ops*)&dwc_otg_pcd_ep_ops; ++ /** ++ * @todo NGS: What should the max packet size be set to ++ * here? Before EP type is set? ++ */ ++ ep->ep.maxpacket = MAX_PACKET_SIZE; ++ ++ list_add_tail (&ep->ep.ep_list, &pcd->gadget.ep_list); ++ ++ INIT_LIST_HEAD (&ep->queue); ++ } ++ hwcfg1 >>= 2; ++ } ++ ++ /* remove ep0 from the list. There is a ep0 pointer.*/ ++ list_del_init (&pcd->ep0.ep.ep_list); ++ ++ pcd->ep0state = EP0_DISCONNECT; ++ pcd->ep0.ep.maxpacket = MAX_EP0_SIZE; ++ pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE; ++ pcd->ep0.dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; ++} ++ ++/** ++ * This function releases the Gadget device. ++ * required by device_unregister(). ++ * ++ * @todo Should this do something? Should it free the PCD? ++ */ ++static void dwc_otg_pcd_gadget_release(struct device *dev) ++{ ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, dev); ++} ++ ++ ++ ++/** ++ * This function initialized the PCD portion of the driver. ++ * ++ */ ++ ++int dwc_otg_pcd_init(struct device *dev) ++{ ++ static char pcd_name[] = "dwc_otg_pcd"; ++ dwc_otg_pcd_t *pcd; ++ dwc_otg_core_if_t* core_if; ++ dwc_otg_dev_if_t* dev_if; ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(dev); ++ int retval = 0; ++ ++ ++ DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n",__func__, dev); ++ /* ++ * Allocate PCD structure ++ */ ++ pcd = kmalloc(sizeof(dwc_otg_pcd_t), GFP_KERNEL); ++ ++ if (pcd == 0) { ++ return -ENOMEM; ++ } ++ ++ memset(pcd, 0, sizeof(dwc_otg_pcd_t)); ++ spin_lock_init(&pcd->lock); ++ ++ otg_dev->pcd = pcd; ++ s_pcd = pcd; ++ pcd->gadget.name = pcd_name; ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30) ++ strcpy(pcd->gadget.dev.bus_id, "gadget"); ++#else ++ dev_set_name(&pcd->gadget.dev, "%s", "gadget"); ++#endif ++ ++ pcd->otg_dev = dev_get_drvdata(dev); ++ ++ pcd->gadget.dev.parent = dev; ++ pcd->gadget.dev.release = dwc_otg_pcd_gadget_release; ++ pcd->gadget.ops = &dwc_otg_pcd_ops; ++ ++ core_if = GET_CORE_IF(pcd); ++ dev_if = core_if->dev_if; ++ ++ if(core_if->hwcfg4.b.ded_fifo_en) { ++ DWC_PRINT("Dedicated Tx FIFOs mode\n"); ++ } ++ else { ++ DWC_PRINT("Shared Tx FIFO mode\n"); ++ } ++ ++ /* If the module is set to FS or if the PHY_TYPE is FS then the gadget ++ * should not report as dual-speed capable. replace the following line ++ * with the block of code below it once the software is debugged for ++ * this. If is_dualspeed = 0 then the gadget driver should not report ++ * a device qualifier descriptor when queried. */ ++ if ((GET_CORE_IF(pcd)->core_params->speed == DWC_SPEED_PARAM_FULL) || ++ ((GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == 2) && ++ (GET_CORE_IF(pcd)->hwcfg2.b.fs_phy_type == 1) && ++ (GET_CORE_IF(pcd)->core_params->ulpi_fs_ls))) { ++ pcd->gadget.is_dualspeed = 0; ++ } ++ else { ++ pcd->gadget.is_dualspeed = 1; ++ } ++ ++ if ((otg_dev->core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE) || ++ (otg_dev->core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST) || ++ (otg_dev->core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) || ++ (otg_dev->core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) { ++ pcd->gadget.is_otg = 0; ++ } ++ else { ++ pcd->gadget.is_otg = 1; ++ } ++ ++ ++ pcd->driver = 0; ++ /* Register the gadget device */ ++ retval = device_register(&pcd->gadget.dev); ++ if (retval != 0) { ++ kfree (pcd); ++ return retval; ++ } ++ ++ ++ /* ++ * Initialized the Core for Device mode. ++ */ ++ if (dwc_otg_is_device_mode(core_if)) { ++ dwc_otg_core_dev_init(core_if); ++ } ++ ++ /* ++ * Initialize EP structures ++ */ ++ dwc_otg_pcd_reinit(pcd); ++ ++ /* ++ * Register the PCD Callbacks. ++ */ ++ dwc_otg_cil_register_pcd_callbacks(otg_dev->core_if, &pcd_callbacks, ++ pcd); ++ /* ++ * Setup interupt handler ++ */ ++ DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", otg_dev->irq); ++ retval = request_irq(otg_dev->irq, dwc_otg_pcd_irq, ++ IRQF_SHARED, pcd->gadget.name, pcd); ++ if (retval != 0) { ++ DWC_ERROR("request of irq%d failed\n", otg_dev->irq); ++ device_unregister(&pcd->gadget.dev); ++ kfree (pcd); ++ return -EBUSY; ++ } ++ ++ /* ++ * Initialize the DMA buffer for SETUP packets ++ */ ++ if (GET_CORE_IF(pcd)->dma_enable) { ++ pcd->setup_pkt = dma_alloc_coherent (NULL, sizeof (*pcd->setup_pkt) * 5, &pcd->setup_pkt_dma_handle, 0); ++ if (pcd->setup_pkt == 0) { ++ free_irq(otg_dev->irq, pcd); ++ device_unregister(&pcd->gadget.dev); ++ kfree (pcd); ++ return -ENOMEM; ++ } ++ ++ pcd->status_buf = dma_alloc_coherent (NULL, sizeof (uint16_t), &pcd->status_buf_dma_handle, 0); ++ if (pcd->status_buf == 0) { ++ dma_free_coherent(NULL, sizeof(*pcd->setup_pkt), pcd->setup_pkt, pcd->setup_pkt_dma_handle); ++ free_irq(otg_dev->irq, pcd); ++ device_unregister(&pcd->gadget.dev); ++ kfree (pcd); ++ return -ENOMEM; ++ } ++ ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ dev_if->setup_desc_addr[0] = dwc_otg_ep_alloc_desc_chain(&dev_if->dma_setup_desc_addr[0], 1); ++ dev_if->setup_desc_addr[1] = dwc_otg_ep_alloc_desc_chain(&dev_if->dma_setup_desc_addr[1], 1); ++ dev_if->in_desc_addr = dwc_otg_ep_alloc_desc_chain(&dev_if->dma_in_desc_addr, 1); ++ dev_if->out_desc_addr = dwc_otg_ep_alloc_desc_chain(&dev_if->dma_out_desc_addr, 1); ++ ++ if(dev_if->setup_desc_addr[0] == 0 ++ || dev_if->setup_desc_addr[1] == 0 ++ || dev_if->in_desc_addr == 0 ++ || dev_if->out_desc_addr == 0 ) { ++ ++ if(dev_if->out_desc_addr) ++ dwc_otg_ep_free_desc_chain(dev_if->out_desc_addr, dev_if->dma_out_desc_addr, 1); ++ if(dev_if->in_desc_addr) ++ dwc_otg_ep_free_desc_chain(dev_if->in_desc_addr, dev_if->dma_in_desc_addr, 1); ++ if(dev_if->setup_desc_addr[1]) ++ dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[1], dev_if->dma_setup_desc_addr[1], 1); ++ if(dev_if->setup_desc_addr[0]) ++ dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[0], dev_if->dma_setup_desc_addr[0], 1); ++ ++ ++ dma_free_coherent(NULL, sizeof(*pcd->status_buf), pcd->status_buf, pcd->setup_pkt_dma_handle); ++ dma_free_coherent(NULL, sizeof(*pcd->setup_pkt), pcd->setup_pkt, pcd->setup_pkt_dma_handle); ++ ++ free_irq(otg_dev->irq, pcd); ++ device_unregister(&pcd->gadget.dev); ++ kfree (pcd); ++ ++ return -ENOMEM; ++ } ++ } ++ } ++ else { ++ pcd->setup_pkt = kmalloc (sizeof (*pcd->setup_pkt) * 5, GFP_KERNEL); ++ if (pcd->setup_pkt == 0) { ++ free_irq(otg_dev->irq, pcd); ++ device_unregister(&pcd->gadget.dev); ++ kfree (pcd); ++ return -ENOMEM; ++ } ++ ++ pcd->status_buf = kmalloc (sizeof (uint16_t), GFP_KERNEL); ++ if (pcd->status_buf == 0) { ++ kfree(pcd->setup_pkt); ++ free_irq(otg_dev->irq, pcd); ++ device_unregister(&pcd->gadget.dev); ++ kfree (pcd); ++ return -ENOMEM; ++ } ++ } ++ ++ ++ /* Initialize tasklet */ ++ start_xfer_tasklet.data = (unsigned long)pcd; ++ pcd->start_xfer_tasklet = &start_xfer_tasklet; ++ ++ return 0; ++} ++ ++/** ++ * Cleanup the PCD. ++ */ ++void dwc_otg_pcd_remove(struct device *dev) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(dev); ++ dwc_otg_pcd_t *pcd = otg_dev->pcd; ++ dwc_otg_dev_if_t* dev_if = GET_CORE_IF(pcd)->dev_if; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, dev); ++ ++ /* ++ * Free the IRQ ++ */ ++ free_irq(otg_dev->irq, pcd); ++ ++ /* start with the driver above us */ ++ if (pcd->driver) { ++ /* should have been done already by driver model core */ ++ DWC_WARN("driver '%s' is still registered\n", ++ pcd->driver->driver.name); ++ usb_gadget_unregister_driver(pcd->driver); ++ } ++ device_unregister(&pcd->gadget.dev); ++ ++ if (GET_CORE_IF(pcd)->dma_enable) { ++ dma_free_coherent (NULL, sizeof (*pcd->setup_pkt) * 5, pcd->setup_pkt, pcd->setup_pkt_dma_handle); ++ dma_free_coherent (NULL, sizeof (uint16_t), pcd->status_buf, pcd->status_buf_dma_handle); ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[0], dev_if->dma_setup_desc_addr[0], 1); ++ dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[1], dev_if->dma_setup_desc_addr[1], 1); ++ dwc_otg_ep_free_desc_chain(dev_if->in_desc_addr, dev_if->dma_in_desc_addr, 1); ++ dwc_otg_ep_free_desc_chain(dev_if->out_desc_addr, dev_if->dma_out_desc_addr, 1); ++ } ++ } ++ else { ++ kfree (pcd->setup_pkt); ++ kfree (pcd->status_buf); ++ } ++ ++ kfree(pcd); ++ otg_dev->pcd = 0; ++} ++ ++/** ++ * This function registers a gadget driver with the PCD. ++ * ++ * When a driver is successfully registered, it will receive control ++ * requests including set_configuration(), which enables non-control ++ * requests. then usb traffic follows until a disconnect is reported. ++ * then a host may connect again, or the driver might get unbound. ++ * ++ * @param driver The driver being registered ++ */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) ++int usb_gadget_probe_driver(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)) ++#else ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++#endif ++{ ++ int retval; ++ int (*d_bind)(struct usb_gadget *); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) ++ d_bind = bind; ++#else ++ d_bind = driver->bind; ++#endif ++ ++ DWC_DEBUGPL(DBG_PCD, "registering gadget driver '%s'\n", driver->driver.name); ++ ++ if (!driver || driver->speed == USB_SPEED_UNKNOWN || ++ !d_bind || ++ !driver->unbind || ++ !driver->disconnect || ++ !driver->setup) { ++ DWC_DEBUGPL(DBG_PCDV,"EINVAL\n"); ++ return -EINVAL; ++ } ++ if (s_pcd == 0) { ++ DWC_DEBUGPL(DBG_PCDV,"ENODEV\n"); ++ return -ENODEV; ++ } ++ if (s_pcd->driver != 0) { ++ DWC_DEBUGPL(DBG_PCDV,"EBUSY (%p)\n", s_pcd->driver); ++ return -EBUSY; ++ } ++ ++ /* hook up the driver */ ++ s_pcd->driver = driver; ++ s_pcd->gadget.dev.driver = &driver->driver; ++ ++ DWC_DEBUGPL(DBG_PCD, "bind to driver %s\n", driver->driver.name); ++ retval = d_bind(&s_pcd->gadget); ++ if (retval) { ++ DWC_ERROR("bind to driver %s --> error %d\n", ++ driver->driver.name, retval); ++ s_pcd->driver = 0; ++ s_pcd->gadget.dev.driver = 0; ++ return retval; ++ } ++ DWC_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n", ++ driver->driver.name); ++ return 0; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37) ++EXPORT_SYMBOL(usb_gadget_probe_driver); ++#else ++EXPORT_SYMBOL(usb_gadget_register_driver); ++#endif ++ ++/** ++ * This function unregisters a gadget driver ++ * ++ * @param driver The driver being unregistered ++ */ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ //DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, _driver); ++ ++ if (s_pcd == 0) { ++ DWC_DEBUGPL(DBG_ANY, "%s Return(%d): s_pcd==0\n", __func__, ++ -ENODEV); ++ return -ENODEV; ++ } ++ if (driver == 0 || driver != s_pcd->driver) { ++ DWC_DEBUGPL(DBG_ANY, "%s Return(%d): driver?\n", __func__, ++ -EINVAL); ++ return -EINVAL; ++ } ++ ++ driver->unbind(&s_pcd->gadget); ++ s_pcd->driver = 0; ++ ++ DWC_DEBUGPL(DBG_ANY, "unregistered driver '%s'\n", ++ driver->driver.name); ++ return 0; ++} ++EXPORT_SYMBOL(usb_gadget_unregister_driver); ++ ++#endif /* DWC_HOST_ONLY */ +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_pcd.h +@@ -0,0 +1,248 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.h $ ++ * $Revision: 1.2 $ ++ * $Date: 2008-11-21 05:39:15 $ ++ * $Change: 1103515 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++#if !defined(__DWC_PCD_H__) ++#define __DWC_PCD_H__ ++ ++#include ++#include ++#include ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++# include ++#else ++# include ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) ++#include ++#else ++#include ++#endif ++#include ++#include ++ ++struct dwc_otg_device; ++ ++#include "dwc_otg_cil.h" ++ ++/** ++ * @file ++ * ++ * This file contains the structures, constants, and interfaces for ++ * the Perpherial Contoller Driver (PCD). ++ * ++ * The Peripheral Controller Driver (PCD) for Linux will implement the ++ * Gadget API, so that the existing Gadget drivers can be used. For ++ * the Mass Storage Function driver the File-backed USB Storage Gadget ++ * (FBS) driver will be used. The FBS driver supports the ++ * Control-Bulk (CB), Control-Bulk-Interrupt (CBI), and Bulk-Only ++ * transports. ++ * ++ */ ++ ++/** Invalid DMA Address */ ++#define DMA_ADDR_INVALID (~(dma_addr_t)0) ++/** Maxpacket size for EP0 */ ++#define MAX_EP0_SIZE 64 ++/** Maxpacket size for any EP */ ++#define MAX_PACKET_SIZE 1024 ++ ++/** Max Transfer size for any EP */ ++#define MAX_TRANSFER_SIZE 65535 ++ ++/** Max DMA Descriptor count for any EP */ ++#define MAX_DMA_DESC_CNT 64 ++ ++/** ++ * Get the pointer to the core_if from the pcd pointer. ++ */ ++#define GET_CORE_IF( _pcd ) (_pcd->otg_dev->core_if) ++ ++/** ++ * States of EP0. ++ */ ++typedef enum ep0_state ++{ ++ EP0_DISCONNECT, /* no host */ ++ EP0_IDLE, ++ EP0_IN_DATA_PHASE, ++ EP0_OUT_DATA_PHASE, ++ EP0_IN_STATUS_PHASE, ++ EP0_OUT_STATUS_PHASE, ++ EP0_STALL, ++} ep0state_e; ++ ++/** Fordward declaration.*/ ++struct dwc_otg_pcd; ++ ++/** DWC_otg iso request structure. ++ * ++ */ ++typedef struct usb_iso_request dwc_otg_pcd_iso_request_t; ++ ++/** PCD EP structure. ++ * This structure describes an EP, there is an array of EPs in the PCD ++ * structure. ++ */ ++typedef struct dwc_otg_pcd_ep ++{ ++ /** USB EP data */ ++ struct usb_ep ep; ++ /** USB EP Descriptor */ ++ const struct usb_endpoint_descriptor *desc; ++ ++ /** queue of dwc_otg_pcd_requests. */ ++ struct list_head queue; ++ unsigned stopped : 1; ++ unsigned disabling : 1; ++ unsigned dma : 1; ++ unsigned queue_sof : 1; ++ ++#ifdef DWC_EN_ISOC ++ /** DWC_otg Isochronous Transfer */ ++ struct usb_iso_request* iso_req; ++#endif //DWC_EN_ISOC ++ ++ /** DWC_otg ep data. */ ++ dwc_ep_t dwc_ep; ++ ++ /** Pointer to PCD */ ++ struct dwc_otg_pcd *pcd; ++}dwc_otg_pcd_ep_t; ++ ++ ++ ++/** DWC_otg PCD Structure. ++ * This structure encapsulates the data for the dwc_otg PCD. ++ */ ++typedef struct dwc_otg_pcd ++{ ++ /** USB gadget */ ++ struct usb_gadget gadget; ++ /** USB gadget driver pointer*/ ++ struct usb_gadget_driver *driver; ++ /** The DWC otg device pointer. */ ++ struct dwc_otg_device *otg_dev; ++ ++ /** State of EP0 */ ++ ep0state_e ep0state; ++ /** EP0 Request is pending */ ++ unsigned ep0_pending : 1; ++ /** Indicates when SET CONFIGURATION Request is in process */ ++ unsigned request_config : 1; ++ /** The state of the Remote Wakeup Enable. */ ++ unsigned remote_wakeup_enable : 1; ++ /** The state of the B-Device HNP Enable. */ ++ unsigned b_hnp_enable : 1; ++ /** The state of A-Device HNP Support. */ ++ unsigned a_hnp_support : 1; ++ /** The state of the A-Device Alt HNP support. */ ++ unsigned a_alt_hnp_support : 1; ++ /** Count of pending Requests */ ++ unsigned request_pending; ++ ++ /** SETUP packet for EP0 ++ * This structure is allocated as a DMA buffer on PCD initialization ++ * with enough space for up to 3 setup packets. ++ */ ++ union ++ { ++ struct usb_ctrlrequest req; ++ uint32_t d32[2]; ++ } *setup_pkt; ++ ++ dma_addr_t setup_pkt_dma_handle; ++ ++ /** 2-byte dma buffer used to return status from GET_STATUS */ ++ uint16_t *status_buf; ++ dma_addr_t status_buf_dma_handle; ++ ++ /** EP0 */ ++ dwc_otg_pcd_ep_t ep0; ++ ++ /** Array of IN EPs. */ ++ dwc_otg_pcd_ep_t in_ep[ MAX_EPS_CHANNELS - 1]; ++ /** Array of OUT EPs. */ ++ dwc_otg_pcd_ep_t out_ep[ MAX_EPS_CHANNELS - 1]; ++ /** number of valid EPs in the above array. */ ++// unsigned num_eps : 4; ++ spinlock_t lock; ++ /** Timer for SRP. If it expires before SRP is successful ++ * clear the SRP. */ ++ struct timer_list srp_timer; ++ ++ /** Tasklet to defer starting of TEST mode transmissions until ++ * Status Phase has been completed. ++ */ ++ struct tasklet_struct test_mode_tasklet; ++ ++ /** Tasklet to delay starting of xfer in DMA mode */ ++ struct tasklet_struct *start_xfer_tasklet; ++ ++ /** The test mode to enter when the tasklet is executed. */ ++ unsigned test_mode; ++ ++} dwc_otg_pcd_t; ++ ++ ++/** DWC_otg request structure. ++ * This structure is a list of requests. ++ */ ++typedef struct ++{ ++ struct usb_request req; /**< USB Request. */ ++ struct list_head queue; /**< queue of these requests. */ ++} dwc_otg_pcd_request_t; ++ ++ ++extern int dwc_otg_pcd_init(struct device *dev); ++ ++//extern void dwc_otg_pcd_remove( struct dwc_otg_device *_otg_dev ); ++extern void dwc_otg_pcd_remove( struct device *dev); ++extern int32_t dwc_otg_pcd_handle_intr( dwc_otg_pcd_t *pcd ); ++extern void dwc_otg_pcd_start_srp_timer(dwc_otg_pcd_t *pcd ); ++ ++extern void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t *pcd); ++extern void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t *pcd, int set); ++ ++extern void dwc_otg_iso_buffer_done(dwc_otg_pcd_ep_t *ep, dwc_otg_pcd_iso_request_t *req); ++extern void dwc_otg_request_done(dwc_otg_pcd_ep_t *_ep, dwc_otg_pcd_request_t *req, ++ int status); ++extern void dwc_otg_request_nuke(dwc_otg_pcd_ep_t *_ep); ++extern void dwc_otg_pcd_update_otg(dwc_otg_pcd_t *_pcd, ++ const unsigned reset); ++ ++#endif ++#endif /* DWC_HOST_ONLY */ +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_pcd_intr.c +@@ -0,0 +1,3654 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_intr.c $ ++ * $Revision: 1.2 $ ++ * $Date: 2008-11-21 05:39:15 $ ++ * $Change: 1115682 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++#include ++#include ++#include ++ ++#include "dwc_otg_driver.h" ++#include "dwc_otg_pcd.h" ++ ++ ++#define DEBUG_EP0 ++ ++/* request functions defined in "dwc_otg_pcd.c" */ ++ ++/** @file ++ * This file contains the implementation of the PCD Interrupt handlers. ++ * ++ * The PCD handles the device interrupts. Many conditions can cause a ++ * device interrupt. When an interrupt occurs, the device interrupt ++ * service routine determines the cause of the interrupt and ++ * dispatches handling to the appropriate function. These interrupt ++ * handling functions are described below. ++ * All interrupt registers are processed from LSB to MSB. ++ */ ++ ++ ++/** ++ * This function prints the ep0 state for debug purposes. ++ */ ++static inline void print_ep0_state(dwc_otg_pcd_t *pcd) ++{ ++#ifdef DEBUG ++ char str[40]; ++ ++ switch (pcd->ep0state) { ++ case EP0_DISCONNECT: ++ strcpy(str, "EP0_DISCONNECT"); ++ break; ++ case EP0_IDLE: ++ strcpy(str, "EP0_IDLE"); ++ break; ++ case EP0_IN_DATA_PHASE: ++ strcpy(str, "EP0_IN_DATA_PHASE"); ++ break; ++ case EP0_OUT_DATA_PHASE: ++ strcpy(str, "EP0_OUT_DATA_PHASE"); ++ break; ++ case EP0_IN_STATUS_PHASE: ++ strcpy(str,"EP0_IN_STATUS_PHASE"); ++ break; ++ case EP0_OUT_STATUS_PHASE: ++ strcpy(str,"EP0_OUT_STATUS_PHASE"); ++ break; ++ case EP0_STALL: ++ strcpy(str,"EP0_STALL"); ++ break; ++ default: ++ strcpy(str,"EP0_INVALID"); ++ } ++ ++ DWC_DEBUGPL(DBG_ANY, "%s(%d)\n", str, pcd->ep0state); ++#endif ++} ++ ++/** ++ * This function returns pointer to in ep struct with number ep_num ++ */ ++static inline dwc_otg_pcd_ep_t* get_in_ep(dwc_otg_pcd_t *pcd, uint32_t ep_num) ++{ ++ int i; ++ int num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps; ++ if(ep_num == 0) { ++ return &pcd->ep0; ++ } ++ else { ++ for(i = 0; i < num_in_eps; ++i) ++ { ++ if(pcd->in_ep[i].dwc_ep.num == ep_num) ++ return &pcd->in_ep[i]; ++ } ++ return 0; ++ } ++} ++/** ++ * This function returns pointer to out ep struct with number ep_num ++ */ ++static inline dwc_otg_pcd_ep_t* get_out_ep(dwc_otg_pcd_t *pcd, uint32_t ep_num) ++{ ++ int i; ++ int num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps; ++ if(ep_num == 0) { ++ return &pcd->ep0; ++ } ++ else { ++ for(i = 0; i < num_out_eps; ++i) ++ { ++ if(pcd->out_ep[i].dwc_ep.num == ep_num) ++ return &pcd->out_ep[i]; ++ } ++ return 0; ++ } ++} ++/** ++ * This functions gets a pointer to an EP from the wIndex address ++ * value of the control request. ++ */ ++static dwc_otg_pcd_ep_t *get_ep_by_addr (dwc_otg_pcd_t *pcd, u16 wIndex) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ ++ if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) ++ return &pcd->ep0; ++ list_for_each_entry(ep, &pcd->gadget.ep_list, ep.ep_list) ++ { ++ u8 bEndpointAddress; ++ ++ if (!ep->desc) ++ continue; ++ ++ bEndpointAddress = ep->desc->bEndpointAddress; ++ if((wIndex & (USB_DIR_IN | USB_ENDPOINT_NUMBER_MASK)) ++ == (bEndpointAddress & (USB_DIR_IN | USB_ENDPOINT_NUMBER_MASK))) ++ return ep; ++ } ++ return NULL; ++} ++ ++/** ++ * This function checks the EP request queue, if the queue is not ++ * empty the next request is started. ++ */ ++void start_next_request(dwc_otg_pcd_ep_t *ep) ++{ ++ dwc_otg_pcd_request_t *req = 0; ++ uint32_t max_transfer = GET_CORE_IF(ep->pcd)->core_params->max_transfer_size; ++ ++ if (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, ++ dwc_otg_pcd_request_t, queue); ++ ++ /* Setup and start the Transfer */ ++ ep->dwc_ep.dma_addr = req->req.dma; ++ ep->dwc_ep.start_xfer_buff = req->req.buf; ++ ep->dwc_ep.xfer_buff = req->req.buf; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = req->req.length; ++ ep->dwc_ep.xfer_len = 0; ++ ep->dwc_ep.xfer_count = 0; ++ ++ if(max_transfer > MAX_TRANSFER_SIZE) { ++ ep->dwc_ep.maxxfer = max_transfer - (max_transfer % ep->dwc_ep.maxpacket); ++ } else { ++ ep->dwc_ep.maxxfer = max_transfer; ++ } ++ ++ if(req->req.zero) { ++ if((ep->dwc_ep.total_len % ep->dwc_ep.maxpacket == 0) ++ && (ep->dwc_ep.total_len != 0)) { ++ ep->dwc_ep.sent_zlp = 1; ++ } ++ ++ } ++ ++ dwc_otg_ep_start_transfer(GET_CORE_IF(ep->pcd), &ep->dwc_ep); ++ } ++} ++ ++/** ++ * This function handles the SOF Interrupts. At this time the SOF ++ * Interrupt is disabled. ++ */ ++int32_t dwc_otg_pcd_handle_sof_intr(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_PCD, "SOF\n"); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.sofintr = 1; ++ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++ ++/** ++ * This function handles the Rx Status Queue Level Interrupt, which ++ * indicates that there is a least one packet in the Rx FIFO. The ++ * packets are moved from the FIFO to memory, where they will be ++ * processed when the Endpoint Interrupt Register indicates Transfer ++ * Complete or SETUP Phase Done. ++ * ++ * Repeat the following until the Rx Status Queue is empty: ++ * -# Read the Receive Status Pop Register (GRXSTSP) to get Packet ++ * info ++ * -# If Receive FIFO is empty then skip to step Clear the interrupt ++ * and exit ++ * -# If SETUP Packet call dwc_otg_read_setup_packet to copy the ++ * SETUP data to the buffer ++ * -# If OUT Data Packet call dwc_otg_read_packet to copy the data ++ * to the destination buffer ++ */ ++int32_t dwc_otg_pcd_handle_rx_status_q_level_intr(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gintmsk_data_t gintmask = {.d32=0}; ++ device_grxsts_data_t status; ++ dwc_otg_pcd_ep_t *ep; ++ gintsts_data_t gintsts; ++#ifdef DEBUG ++ static char *dpid_str[] ={ "D0", "D2", "D1", "MDATA" }; ++#endif ++ ++ //DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _pcd); ++ /* Disable the Rx Status Queue Level interrupt */ ++ gintmask.b.rxstsqlvl= 1; ++ dwc_modify_reg32(&global_regs->gintmsk, gintmask.d32, 0); ++ ++ /* Get the Status from the top of the FIFO */ ++ status.d32 = dwc_read_reg32(&global_regs->grxstsp); ++ ++ DWC_DEBUGPL(DBG_PCD, "EP:%d BCnt:%d DPID:%s " ++ "pktsts:%x Frame:%d(0x%0x)\n", ++ status.b.epnum, status.b.bcnt, ++ dpid_str[status.b.dpid], ++ status.b.pktsts, status.b.fn, status.b.fn); ++ /* Get pointer to EP structure */ ++ ep = get_out_ep(pcd, status.b.epnum); ++ ++ switch (status.b.pktsts) { ++ case DWC_DSTS_GOUT_NAK: ++ DWC_DEBUGPL(DBG_PCDV, "Global OUT NAK\n"); ++ break; ++ case DWC_STS_DATA_UPDT: ++ DWC_DEBUGPL(DBG_PCDV, "OUT Data Packet\n"); ++ if (status.b.bcnt && ep->dwc_ep.xfer_buff) { ++ /** @todo NGS Check for buffer overflow? */ ++ dwc_otg_read_packet(core_if, ++ ep->dwc_ep.xfer_buff, ++ status.b.bcnt); ++ ep->dwc_ep.xfer_count += status.b.bcnt; ++ ep->dwc_ep.xfer_buff += status.b.bcnt; ++ } ++ break; ++ case DWC_STS_XFER_COMP: ++ DWC_DEBUGPL(DBG_PCDV, "OUT Complete\n"); ++ break; ++ case DWC_DSTS_SETUP_COMP: ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Setup Complete\n"); ++#endif ++ break; ++case DWC_DSTS_SETUP_UPDT: ++ dwc_otg_read_setup_packet(core_if, pcd->setup_pkt->d32); ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, ++ "SETUP PKT: %02x.%02x v%04x i%04x l%04x\n", ++ pcd->setup_pkt->req.bRequestType, ++ pcd->setup_pkt->req.bRequest, ++ pcd->setup_pkt->req.wValue, ++ pcd->setup_pkt->req.wIndex, ++ pcd->setup_pkt->req.wLength); ++#endif ++ ep->dwc_ep.xfer_count += status.b.bcnt; ++ break; ++ default: ++ DWC_DEBUGPL(DBG_PCDV, "Invalid Packet Status (0x%0x)\n", ++ status.b.pktsts); ++ break; ++ } ++ ++ /* Enable the Rx Status Queue Level interrupt */ ++ dwc_modify_reg32(&global_regs->gintmsk, 0, gintmask.d32); ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ dwc_write_reg32 (&global_regs->gintsts, gintsts.d32); ++ ++ //DWC_DEBUGPL(DBG_PCDV, "EXIT: %s\n", __func__); ++ return 1; ++} ++/** ++ * This function examines the Device IN Token Learning Queue to ++ * determine the EP number of the last IN token received. This ++ * implementation is for the Mass Storage device where there are only ++ * 2 IN EPs (Control-IN and BULK-IN). ++ * ++ * The EP numbers for the first six IN Tokens are in DTKNQR1 and there ++ * are 8 EP Numbers in each of the other possible DTKNQ Registers. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * ++ */ ++static inline int get_ep_of_last_in_token(dwc_otg_core_if_t *core_if) ++{ ++ dwc_otg_device_global_regs_t *dev_global_regs = ++ core_if->dev_if->dev_global_regs; ++ const uint32_t TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth; ++ /* Number of Token Queue Registers */ ++ const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8; ++ dtknq1_data_t dtknqr1; ++ uint32_t in_tkn_epnums[4]; ++ int ndx = 0; ++ int i = 0; ++ volatile uint32_t *addr = &dev_global_regs->dtknqr1; ++ int epnum = 0; ++ ++ //DWC_DEBUGPL(DBG_PCD,"dev_token_q_depth=%d\n",TOKEN_Q_DEPTH); ++ ++ ++ /* Read the DTKNQ Registers */ ++ for (i = 0; i < DTKNQ_REG_CNT; i++) ++ { ++ in_tkn_epnums[ i ] = dwc_read_reg32(addr); ++ DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i+1, ++ in_tkn_epnums[i]); ++ if (addr == &dev_global_regs->dvbusdis) { ++ addr = &dev_global_regs->dtknqr3_dthrctl; ++ } ++ else { ++ ++addr; ++ } ++ ++ } ++ ++ /* Copy the DTKNQR1 data to the bit field. */ ++ dtknqr1.d32 = in_tkn_epnums[0]; ++ /* Get the EP numbers */ ++ in_tkn_epnums[0] = dtknqr1.b.epnums0_5; ++ ndx = dtknqr1.b.intknwptr - 1; ++ ++ //DWC_DEBUGPL(DBG_PCDV,"ndx=%d\n",ndx); ++ if (ndx == -1) { ++ /** @todo Find a simpler way to calculate the max ++ * queue position.*/ ++ int cnt = TOKEN_Q_DEPTH; ++ if (TOKEN_Q_DEPTH <= 6) { ++ cnt = TOKEN_Q_DEPTH - 1; ++ } ++ else if (TOKEN_Q_DEPTH <= 14) { ++ cnt = TOKEN_Q_DEPTH - 7; ++ } ++ else if (TOKEN_Q_DEPTH <= 22) { ++ cnt = TOKEN_Q_DEPTH - 15; ++ } ++ else { ++ cnt = TOKEN_Q_DEPTH - 23; ++ } ++ epnum = (in_tkn_epnums[ DTKNQ_REG_CNT - 1 ] >> (cnt * 4)) & 0xF; ++ } ++ else { ++ if (ndx <= 5) { ++ epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF; ++ } ++ else if (ndx <= 13) { ++ ndx -= 6; ++ epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF; ++ } ++ else if (ndx <= 21) { ++ ndx -= 14; ++ epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF; ++ } ++ else if (ndx <= 29) { ++ ndx -= 22; ++ epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF; ++ } ++ } ++ //DWC_DEBUGPL(DBG_PCD,"epnum=%d\n",epnum); ++ return epnum; ++} ++ ++/** ++ * This interrupt occurs when the non-periodic Tx FIFO is half-empty. ++ * The active request is checked for the next packet to be loaded into ++ * the non-periodic Tx FIFO. ++ */ ++int32_t dwc_otg_pcd_handle_np_tx_fifo_empty_intr(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_core_global_regs_t *global_regs = ++ core_if->core_global_regs; ++ dwc_otg_dev_in_ep_regs_t *ep_regs; ++ gnptxsts_data_t txstatus = {.d32 = 0}; ++ gintsts_data_t gintsts; ++ ++ int epnum = 0; ++ dwc_otg_pcd_ep_t *ep = 0; ++ uint32_t len = 0; ++ int dwords; ++ ++ /* Get the epnum from the IN Token Learning Queue. */ ++ epnum = get_ep_of_last_in_token(core_if); ++ ep = get_in_ep(pcd, epnum); ++ ++ DWC_DEBUGPL(DBG_PCD, "NP TxFifo Empty: %s(%d) \n", ep->ep.name, epnum); ++ ep_regs = core_if->dev_if->in_ep_regs[epnum]; ++ ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ dwords = (len + 3)/4; ++ ++ ++ /* While there is space in the queue and space in the FIFO and ++ * More data to tranfer, Write packets to the Tx FIFO */ ++ txstatus.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_PCDV, "b4 GNPTXSTS=0x%08x\n",txstatus.d32); ++ ++ while (txstatus.b.nptxqspcavail > 0 && ++ txstatus.b.nptxfspcavail > dwords && ++ ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len) { ++ /* Write the FIFO */ ++ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0); ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ ++ dwords = (len + 3)/4; ++ txstatus.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_PCDV,"GNPTXSTS=0x%08x\n",txstatus.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n", ++ dwc_read_reg32(&global_regs->gnptxsts)); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.nptxfempty = 1; ++ dwc_write_reg32 (&global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This function is called when dedicated Tx FIFO Empty interrupt occurs. ++ * The active request is checked for the next packet to be loaded into ++ * apropriate Tx FIFO. ++ */ ++static int32_t write_empty_tx_fifo(dwc_otg_pcd_t *pcd, uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t* dev_if = core_if->dev_if; ++ dwc_otg_dev_in_ep_regs_t *ep_regs; ++ dtxfsts_data_t txstatus = {.d32 = 0}; ++ dwc_otg_pcd_ep_t *ep = 0; ++ uint32_t len = 0; ++ int dwords; ++ ++ ep = get_in_ep(pcd, epnum); ++ ++ DWC_DEBUGPL(DBG_PCD, "Dedicated TxFifo Empty: %s(%d) \n", ep->ep.name, epnum); ++ ++ ep_regs = core_if->dev_if->in_ep_regs[epnum]; ++ ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ ++ dwords = (len + 3)/4; ++ ++ /* While there is space in the queue and space in the FIFO and ++ * More data to tranfer, Write packets to the Tx FIFO */ ++ txstatus.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n",epnum,txstatus.d32); ++ ++ while (txstatus.b.txfspcavail > dwords && ++ ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len && ++ ep->dwc_ep.xfer_len != 0) { ++ /* Write the FIFO */ ++ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0); ++ ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ ++ dwords = (len + 3)/4; ++ txstatus.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV,"dtxfsts[%d]=0x%08x\n", epnum, txstatus.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n",epnum,dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dtxfsts)); ++ ++ return 1; ++} ++ ++ ++/** ++ * This function is called when the Device is disconnected. It stops ++ * any active requests and informs the Gadget driver of the ++ * disconnect. ++ */ ++void dwc_otg_pcd_stop(dwc_otg_pcd_t *pcd) ++{ ++ int i, num_in_eps, num_out_eps; ++ dwc_otg_pcd_ep_t *ep; ++ ++ gintmsk_data_t intr_mask = {.d32 = 0}; ++ ++ num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps; ++ num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s() \n", __func__); ++ /* don't disconnect drivers more than once */ ++ if (pcd->ep0state == EP0_DISCONNECT) { ++ DWC_DEBUGPL(DBG_ANY, "%s() Already Disconnected\n", __func__); ++ return; ++ } ++ pcd->ep0state = EP0_DISCONNECT; ++ ++ /* Reset the OTG state. */ ++ dwc_otg_pcd_update_otg(pcd, 1); ++ ++ /* Disable the NP Tx Fifo Empty Interrupt. */ ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Flush the FIFOs */ ++ /**@todo NGS Flush Periodic FIFOs */ ++ dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), 0x10); ++ dwc_otg_flush_rx_fifo(GET_CORE_IF(pcd)); ++ ++ /* prevent new request submissions, kill any outstanding requests */ ++ ep = &pcd->ep0; ++ dwc_otg_request_nuke(ep); ++ /* prevent new request submissions, kill any outstanding requests */ ++ for (i = 0; i < num_in_eps; i++) ++ { ++ dwc_otg_pcd_ep_t *ep = &pcd->in_ep[i]; ++ dwc_otg_request_nuke(ep); ++ } ++ /* prevent new request submissions, kill any outstanding requests */ ++ for (i = 0; i < num_out_eps; i++) ++ { ++ dwc_otg_pcd_ep_t *ep = &pcd->out_ep[i]; ++ dwc_otg_request_nuke(ep); ++ } ++ ++ /* report disconnect; the driver is already quiesced */ ++ if (pcd->driver && pcd->driver->disconnect) { ++ SPIN_UNLOCK(&pcd->lock); ++ pcd->driver->disconnect(&pcd->gadget); ++ SPIN_LOCK(&pcd->lock); ++ } ++} ++ ++/** ++ * This interrupt indicates that ... ++ */ ++int32_t dwc_otg_pcd_handle_i2c_intr(dwc_otg_pcd_t *pcd) ++{ ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ gintsts_data_t gintsts; ++ ++ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "i2cintr"); ++ intr_mask.b.i2cintr = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.i2cintr = 1; ++ dwc_write_reg32 (&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ return 1; ++} ++ ++ ++/** ++ * This interrupt indicates that ... ++ */ ++int32_t dwc_otg_pcd_handle_early_suspend_intr(dwc_otg_pcd_t *pcd) ++{ ++ gintsts_data_t gintsts; ++#if defined(VERBOSE) ++ DWC_PRINT("Early Suspend Detected\n"); ++#endif ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.erlysuspend = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ return 1; ++} ++ ++/** ++ * This function configures EPO to receive SETUP packets. ++ * ++ * @todo NGS: Update the comments from the HW FS. ++ * ++ * -# Program the following fields in the endpoint specific registers ++ * for Control OUT EP 0, in order to receive a setup packet ++ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back ++ * setup packets) ++ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back ++ * to back setup packets) ++ * - In DMA mode, DOEPDMA0 Register with a memory address to ++ * store any setup packets received ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param pcd Programming view of the PCD. ++ */ ++static inline void ep0_out_start(dwc_otg_core_if_t *core_if, dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ deptsiz0_data_t doeptsize0 = { .d32 = 0}; ++ dwc_otg_dma_desc_t* dma_desc; ++ depctl_data_t doepctl = { .d32 = 0 }; ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV,"%s() doepctl0=%0x\n", __func__, ++ dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl)); ++#endif ++ ++ doeptsize0.b.supcnt = 3; ++ doeptsize0.b.pktcnt = 1; ++ doeptsize0.b.xfersize = 8*3; ++ ++ ++ if (core_if->dma_enable) { ++ if (!core_if->dma_desc_enable) { ++ /** put here as for Hermes mode deptisz register should not be written */ ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doeptsiz, ++ doeptsize0.d32); ++ ++ /** @todo dma needs to handle multiple setup packets (up to 3) */ ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepdma, ++ pcd->setup_pkt_dma_handle); ++ } else { ++ dev_if->setup_desc_index = (dev_if->setup_desc_index + 1) & 1; ++ dma_desc = dev_if->setup_desc_addr[dev_if->setup_desc_index]; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.bytes = pcd->ep0.dwc_ep.maxpacket; ++ dma_desc->buf = pcd->setup_pkt_dma_handle; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DOEPDMA0 Register write */ ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepdma, dev_if->dma_setup_desc_addr[dev_if->setup_desc_index]); ++ } ++ ++ } else { ++ /** put here as for Hermes mode deptisz register should not be written */ ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doeptsiz, ++ doeptsize0.d32); ++ } ++ ++ /** DOEPCTL0 Register write */ ++ doepctl.b.epena = 1; ++ doepctl.b.cnak = 1; ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32); ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV,"doepctl0=%0x\n", ++ dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl)); ++ DWC_DEBUGPL(DBG_PCDV,"diepctl0=%0x\n", ++ dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl)); ++#endif ++} ++ ++ ++/** ++ * This interrupt occurs when a USB Reset is detected. When the USB ++ * Reset Interrupt occurs the device state is set to DEFAULT and the ++ * EP0 state is set to IDLE. ++ * -# Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1) ++ * -# Unmask the following interrupt bits ++ * - DAINTMSK.INEP0 = 1 (Control 0 IN endpoint) ++ * - DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint) ++ * - DOEPMSK.SETUP = 1 ++ * - DOEPMSK.XferCompl = 1 ++ * - DIEPMSK.XferCompl = 1 ++ * - DIEPMSK.TimeOut = 1 ++ * -# Program the following fields in the endpoint specific registers ++ * for Control OUT EP 0, in order to receive a setup packet ++ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back ++ * setup packets) ++ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back ++ * to back setup packets) ++ * - In DMA mode, DOEPDMA0 Register with a memory address to ++ * store any setup packets received ++ * At this point, all the required initialization, except for enabling ++ * the control 0 OUT endpoint is done, for receiving SETUP packets. ++ */ ++int32_t dwc_otg_pcd_handle_usb_reset_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ depctl_data_t doepctl = { .d32 = 0}; ++ ++ daint_data_t daintmsk = { .d32 = 0}; ++ doepmsk_data_t doepmsk = { .d32 = 0}; ++ diepmsk_data_t diepmsk = { .d32 = 0}; ++ ++ dcfg_data_t dcfg = { .d32=0 }; ++ grstctl_t resetctl = { .d32=0 }; ++ dctl_data_t dctl = {.d32=0}; ++ int i = 0; ++ gintsts_data_t gintsts; ++ ++ DWC_PRINT("USB RESET\n"); ++#ifdef DWC_EN_ISOC ++ for(i = 1;i < 16; ++i) ++ { ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ep = get_in_ep(pcd,i); ++ if(ep != 0){ ++ dwc_ep = &ep->dwc_ep; ++ dwc_ep->next_frame = 0xffffffff; ++ } ++ } ++#endif /* DWC_EN_ISOC */ ++ ++ /* reset the HNP settings */ ++ dwc_otg_pcd_update_otg(pcd, 1); ++ ++ /* Clear the Remote Wakeup Signalling */ ++ dctl.b.rmtwkupsig = 1; ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dctl, ++ dctl.d32, 0); ++ ++ /* Set NAK for all OUT EPs */ ++ doepctl.b.snak = 1; ++ for (i=0; i <= dev_if->num_out_eps; i++) ++ { ++ dwc_write_reg32(&dev_if->out_ep_regs[i]->doepctl, ++ doepctl.d32); ++ } ++ ++ /* Flush the NP Tx FIFO */ ++ dwc_otg_flush_tx_fifo(core_if, 0x10); ++ /* Flush the Learning Queue */ ++ resetctl.b.intknqflsh = 1; ++ dwc_write_reg32(&core_if->core_global_regs->grstctl, resetctl.d32); ++ ++ if(core_if->multiproc_int_enable) { ++ daintmsk.b.inep0 = 1; ++ daintmsk.b.outep0 = 1; ++ dwc_write_reg32(&dev_if->dev_global_regs->deachintmsk, daintmsk.d32); ++ ++ doepmsk.b.setup = 1; ++ doepmsk.b.xfercompl = 1; ++ doepmsk.b.ahberr = 1; ++ doepmsk.b.epdisabled = 1; ++ ++ if(core_if->dma_desc_enable) { ++ doepmsk.b.stsphsercvd = 1; ++ doepmsk.b.bna = 1; ++ } ++/* ++ doepmsk.b.babble = 1; ++ doepmsk.b.nyet = 1; ++ ++ if(core_if->dma_enable) { ++ doepmsk.b.nak = 1; ++ } ++*/ ++ dwc_write_reg32(&dev_if->dev_global_regs->doepeachintmsk[0], doepmsk.d32); ++ ++ diepmsk.b.xfercompl = 1; ++ diepmsk.b.timeout = 1; ++ diepmsk.b.epdisabled = 1; ++ diepmsk.b.ahberr = 1; ++ diepmsk.b.intknepmis = 1; ++ ++ if(core_if->dma_desc_enable) { ++ diepmsk.b.bna = 1; ++ } ++/* ++ if(core_if->dma_enable) { ++ diepmsk.b.nak = 1; ++ } ++*/ ++ dwc_write_reg32(&dev_if->dev_global_regs->diepeachintmsk[0], diepmsk.d32); ++ } else{ ++ daintmsk.b.inep0 = 1; ++ daintmsk.b.outep0 = 1; ++ dwc_write_reg32(&dev_if->dev_global_regs->daintmsk, daintmsk.d32); ++ ++ doepmsk.b.setup = 1; ++ doepmsk.b.xfercompl = 1; ++ doepmsk.b.ahberr = 1; ++ doepmsk.b.epdisabled = 1; ++ ++ if(core_if->dma_desc_enable) { ++ doepmsk.b.stsphsercvd = 1; ++ doepmsk.b.bna = 1; ++ } ++/* ++ doepmsk.b.babble = 1; ++ doepmsk.b.nyet = 1; ++ doepmsk.b.nak = 1; ++*/ ++ dwc_write_reg32(&dev_if->dev_global_regs->doepmsk, doepmsk.d32); ++ ++ diepmsk.b.xfercompl = 1; ++ diepmsk.b.timeout = 1; ++ diepmsk.b.epdisabled = 1; ++ diepmsk.b.ahberr = 1; ++ diepmsk.b.intknepmis = 1; ++ ++ if(core_if->dma_desc_enable) { ++ diepmsk.b.bna = 1; ++ } ++ ++// diepmsk.b.nak = 1; ++ ++ dwc_write_reg32(&dev_if->dev_global_regs->diepmsk, diepmsk.d32); ++ } ++ ++ /* Reset Device Address */ ++ dcfg.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dcfg); ++ dcfg.b.devaddr = 0; ++ dwc_write_reg32(&dev_if->dev_global_regs->dcfg, dcfg.d32); ++ ++ /* setup EP0 to receive SETUP packets */ ++ ep0_out_start(core_if, pcd); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.usbreset = 1; ++ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * Get the device speed from the device status register and convert it ++ * to USB speed constant. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static int get_device_speed(dwc_otg_core_if_t *core_if) ++{ ++ dsts_data_t dsts; ++ enum usb_device_speed speed = USB_SPEED_UNKNOWN; ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ switch (dsts.b.enumspd) { ++ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: ++ speed = USB_SPEED_HIGH; ++ break; ++ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: ++ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: ++ speed = USB_SPEED_FULL; ++ break; ++ ++ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ: ++ speed = USB_SPEED_LOW; ++ break; ++ } ++ ++ return speed; ++} ++ ++/** ++ * Read the device status register and set the device speed in the ++ * data structure. ++ * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate. ++ */ ++int32_t dwc_otg_pcd_handle_enum_done_intr(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ gintsts_data_t gintsts; ++ gusbcfg_data_t gusbcfg; ++ dwc_otg_core_global_regs_t *global_regs = ++ GET_CORE_IF(pcd)->core_global_regs; ++ uint8_t utmi16b, utmi8b; ++ DWC_DEBUGPL(DBG_PCD, "SPEED ENUM\n"); ++ ++ if (GET_CORE_IF(pcd)->snpsid >= 0x4F54260A) { ++ utmi16b = 6; ++ utmi8b = 9; ++ } else { ++ utmi16b = 4; ++ utmi8b = 8; ++ } ++ dwc_otg_ep0_activate(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ ++#ifdef DEBUG_EP0 ++ print_ep0_state(pcd); ++#endif ++ ++ if (pcd->ep0state == EP0_DISCONNECT) { ++ pcd->ep0state = EP0_IDLE; ++ } ++ else if (pcd->ep0state == EP0_STALL) { ++ pcd->ep0state = EP0_IDLE; ++ } ++ ++ pcd->ep0state = EP0_IDLE; ++ ++ ep0->stopped = 0; ++ ++ pcd->gadget.speed = get_device_speed(GET_CORE_IF(pcd)); ++ ++ /* Set USB turnaround time based on device speed and PHY interface. */ ++ gusbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ if (pcd->gadget.speed == USB_SPEED_HIGH) { ++ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == DWC_HWCFG2_HS_PHY_TYPE_ULPI) { ++ /* ULPI interface */ ++ gusbcfg.b.usbtrdtim = 9; ++ } ++ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == DWC_HWCFG2_HS_PHY_TYPE_UTMI) { ++ /* UTMI+ interface */ ++ if (GET_CORE_IF(pcd)->hwcfg4.b.utmi_phy_data_width == 0) { ++ gusbcfg.b.usbtrdtim = utmi8b; ++ } ++ else if (GET_CORE_IF(pcd)->hwcfg4.b.utmi_phy_data_width == 1) { ++ gusbcfg.b.usbtrdtim = utmi16b; ++ } ++ else if (GET_CORE_IF(pcd)->core_params->phy_utmi_width == 8) { ++ gusbcfg.b.usbtrdtim = utmi8b; ++ } ++ else { ++ gusbcfg.b.usbtrdtim = utmi16b; ++ } ++ } ++ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI) { ++ /* UTMI+ OR ULPI interface */ ++ if (gusbcfg.b.ulpi_utmi_sel == 1) { ++ /* ULPI interface */ ++ gusbcfg.b.usbtrdtim = 9; ++ } ++ else { ++ /* UTMI+ interface */ ++ if (GET_CORE_IF(pcd)->core_params->phy_utmi_width == 16) { ++ gusbcfg.b.usbtrdtim = utmi16b; ++ } ++ else { ++ gusbcfg.b.usbtrdtim = utmi8b; ++ } ++ } ++ } ++ } ++ else { ++ /* Full or low speed */ ++ gusbcfg.b.usbtrdtim = 9; ++ } ++ dwc_write_reg32(&global_regs->gusbcfg, gusbcfg.d32); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.enumdone = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that the ISO OUT Packet was dropped due to ++ * Rx FIFO full or Rx Status Queue Full. If this interrupt occurs ++ * read all the data from the Rx FIFO. ++ */ ++int32_t dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(dwc_otg_pcd_t *pcd) ++{ ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ gintsts_data_t gintsts; ++ ++ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", ++ "ISOC Out Dropped"); ++ ++ intr_mask.b.isooutdrop = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ ++ gintsts.d32 = 0; ++ gintsts.b.isooutdrop = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates the end of the portion of the micro-frame ++ * for periodic transactions. If there is a periodic transaction for ++ * the next frame, load the packets into the EP periodic Tx FIFO. ++ */ ++int32_t dwc_otg_pcd_handle_end_periodic_frame_intr(dwc_otg_pcd_t *pcd) ++{ ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ gintsts_data_t gintsts; ++ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "EOP"); ++ ++ intr_mask.b.eopframe = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.eopframe = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that EP of the packet on the top of the ++ * non-periodic Tx FIFO does not match EP of the IN Token received. ++ * ++ * The "Device IN Token Queue" Registers are read to determine the ++ * order the IN Tokens have been received. The non-periodic Tx FIFO ++ * is flushed, so it can be reloaded in the order seen in the IN Token ++ * Queue. ++ */ ++int32_t dwc_otg_pcd_handle_ep_mismatch_intr(dwc_otg_core_if_t *core_if) ++{ ++ gintsts_data_t gintsts; ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.epmismatch = 1; ++ dwc_write_reg32 (&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This funcion stalls EP0. ++ */ ++static inline void ep0_do_stall(dwc_otg_pcd_t *pcd, const int err_val) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ struct usb_ctrlrequest *ctrl = &pcd->setup_pkt->req; ++ DWC_WARN("req %02x.%02x protocol STALL; err %d\n", ++ ctrl->bRequestType, ctrl->bRequest, err_val); ++ ++ ep0->dwc_ep.is_in = 1; ++ dwc_otg_ep_set_stall(pcd->otg_dev->core_if, &ep0->dwc_ep); ++ pcd->ep0.stopped = 1; ++ pcd->ep0state = EP0_IDLE; ++ ep0_out_start(GET_CORE_IF(pcd), pcd); ++} ++ ++/** ++ * This functions delegates the setup command to the gadget driver. ++ */ ++static inline void do_gadget_setup(dwc_otg_pcd_t *pcd, ++ struct usb_ctrlrequest * ctrl) ++{ ++ int ret = 0; ++ if (pcd->driver && pcd->driver->setup) { ++ SPIN_UNLOCK(&pcd->lock); ++ ret = pcd->driver->setup(&pcd->gadget, ctrl); ++ SPIN_LOCK(&pcd->lock); ++ if (ret < 0) { ++ ep0_do_stall(pcd, ret); ++ } ++ ++ /** @todo This is a g_file_storage gadget driver specific ++ * workaround: a DELAYED_STATUS result from the fsg_setup ++ * routine will result in the gadget queueing a EP0 IN status ++ * phase for a two-stage control transfer. Exactly the same as ++ * a SET_CONFIGURATION/SET_INTERFACE except that this is a class ++ * specific request. Need a generic way to know when the gadget ++ * driver will queue the status phase. Can we assume when we ++ * call the gadget driver setup() function that it will always ++ * queue and require the following flag? Need to look into ++ * this. ++ */ ++ ++ if (ret == 256 + 999) { ++ pcd->request_config = 1; ++ } ++ } ++} ++ ++/** ++ * This function starts the Zero-Length Packet for the IN status phase ++ * of a 2 stage control transfer. ++ */ ++static inline void do_setup_in_status_phase(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ if (pcd->ep0state == EP0_STALL) { ++ return; ++ } ++ ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ ++ /* Prepare for more SETUP Packets */ ++ DWC_DEBUGPL(DBG_PCD, "EP0 IN ZLP\n"); ++ ep0->dwc_ep.xfer_len = 0; ++ ep0->dwc_ep.xfer_count = 0; ++ ep0->dwc_ep.is_in = 1; ++ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle; ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ ++ /* Prepare for more SETUP Packets */ ++// if(GET_CORE_IF(pcd)->dma_enable == 0) ep0_out_start(GET_CORE_IF(pcd), pcd); ++} ++ ++/** ++ * This function starts the Zero-Length Packet for the OUT status phase ++ * of a 2 stage control transfer. ++ */ ++static inline void do_setup_out_status_phase(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ if (pcd->ep0state == EP0_STALL) { ++ DWC_DEBUGPL(DBG_PCD, "EP0 STALLED\n"); ++ return; ++ } ++ pcd->ep0state = EP0_OUT_STATUS_PHASE; ++ ++ DWC_DEBUGPL(DBG_PCD, "EP0 OUT ZLP\n"); ++ ep0->dwc_ep.xfer_len = 0; ++ ep0->dwc_ep.xfer_count = 0; ++ ep0->dwc_ep.is_in = 0; ++ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle; ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ ++ /* Prepare for more SETUP Packets */ ++ if(GET_CORE_IF(pcd)->dma_enable == 0) { ++ ep0_out_start(GET_CORE_IF(pcd), pcd); ++ } ++} ++ ++/** ++ * Clear the EP halt (STALL) and if pending requests start the ++ * transfer. ++ */ ++static inline void pcd_clear_halt(dwc_otg_pcd_t *pcd, dwc_otg_pcd_ep_t *ep) ++{ ++ if(ep->dwc_ep.stall_clear_flag == 0) ++ dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep); ++ ++ /* Reactive the EP */ ++ dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep); ++ if (ep->stopped) { ++ ep->stopped = 0; ++ /* If there is a request in the EP queue start it */ ++ ++ /** @todo FIXME: this causes an EP mismatch in DMA mode. ++ * epmismatch not yet implemented. */ ++ ++ /* ++ * Above fixme is solved by implmenting a tasklet to call the ++ * start_next_request(), outside of interrupt context at some ++ * time after the current time, after a clear-halt setup packet. ++ * Still need to implement ep mismatch in the future if a gadget ++ * ever uses more than one endpoint at once ++ */ ++ ep->queue_sof = 1; ++ tasklet_schedule (pcd->start_xfer_tasklet); ++ } ++ /* Start Control Status Phase */ ++ do_setup_in_status_phase(pcd); ++} ++ ++/** ++ * This function is called when the SET_FEATURE TEST_MODE Setup packet ++ * is sent from the host. The Device Control register is written with ++ * the Test Mode bits set to the specified Test Mode. This is done as ++ * a tasklet so that the "Status" phase of the control transfer ++ * completes before transmitting the TEST packets. ++ * ++ * @todo This has not been tested since the tasklet struct was put ++ * into the PCD struct! ++ * ++ */ ++static void do_test_mode(unsigned long data) ++{ ++ dctl_data_t dctl; ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)data; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ int test_mode = pcd->test_mode; ++ ++ ++// DWC_WARN("%s() has not been tested since being rewritten!\n", __func__); ++ ++ dctl.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dctl); ++ switch (test_mode) { ++ case 1: // TEST_J ++ dctl.b.tstctl = 1; ++ break; ++ ++ case 2: // TEST_K ++ dctl.b.tstctl = 2; ++ break; ++ ++ case 3: // TEST_SE0_NAK ++ dctl.b.tstctl = 3; ++ break; ++ ++ case 4: // TEST_PACKET ++ dctl.b.tstctl = 4; ++ break; ++ ++ case 5: // TEST_FORCE_ENABLE ++ dctl.b.tstctl = 5; ++ break; ++ } ++ dwc_write_reg32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32); ++} ++ ++/** ++ * This function process the GET_STATUS Setup Commands. ++ */ ++static inline void do_get_status(dwc_otg_pcd_t *pcd) ++{ ++ struct usb_ctrlrequest ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ uint16_t *status = pcd->status_buf; ++ ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, ++ "GET_STATUS %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bRequestType, ctrl.bRequest, ++ ctrl.wValue, ctrl.wIndex, ctrl.wLength); ++#endif ++ ++ switch (ctrl.bRequestType & USB_RECIP_MASK) { ++ case USB_RECIP_DEVICE: ++ *status = 0x1; /* Self powered */ ++ *status |= pcd->remote_wakeup_enable << 1; ++ break; ++ ++ case USB_RECIP_INTERFACE: ++ *status = 0; ++ break; ++ ++ case USB_RECIP_ENDPOINT: ++ ep = get_ep_by_addr(pcd, ctrl.wIndex); ++ if (ep == 0 || ctrl.wLength > 2) { ++ ep0_do_stall(pcd, -EOPNOTSUPP); ++ return; ++ } ++ /** @todo check for EP stall */ ++ *status = ep->stopped; ++ break; ++ } ++ pcd->ep0_pending = 1; ++ ep0->dwc_ep.start_xfer_buff = (uint8_t *)status; ++ ep0->dwc_ep.xfer_buff = (uint8_t *)status; ++ ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle; ++ ep0->dwc_ep.xfer_len = 2; ++ ep0->dwc_ep.xfer_count = 0; ++ ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len; ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); ++} ++/** ++ * This function process the SET_FEATURE Setup Commands. ++ */ ++static inline void do_set_feature(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_core_global_regs_t *global_regs = ++ core_if->core_global_regs; ++ struct usb_ctrlrequest ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep = 0; ++ int32_t otg_cap_param = core_if->core_params->otg_cap; ++ gotgctl_data_t gotgctl = { .d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_PCD, "SET_FEATURE:%02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bRequestType, ctrl.bRequest, ++ ctrl.wValue, ctrl.wIndex, ctrl.wLength); ++ DWC_DEBUGPL(DBG_PCD,"otg_cap=%d\n", otg_cap_param); ++ ++ ++ switch (ctrl.bRequestType & USB_RECIP_MASK) { ++ case USB_RECIP_DEVICE: ++ switch (ctrl.wValue) { ++ case USB_DEVICE_REMOTE_WAKEUP: ++ pcd->remote_wakeup_enable = 1; ++ break; ++ ++ case USB_DEVICE_TEST_MODE: ++ /* Setup the Test Mode tasklet to do the Test ++ * Packet generation after the SETUP Status ++ * phase has completed. */ ++ ++ /** @todo This has not been tested since the ++ * tasklet struct was put into the PCD ++ * struct! */ ++ pcd->test_mode_tasklet.next = 0; ++ pcd->test_mode_tasklet.state = 0; ++ atomic_set(&pcd->test_mode_tasklet.count, 0); ++ pcd->test_mode_tasklet.func = do_test_mode; ++ pcd->test_mode_tasklet.data = (unsigned long)pcd; ++ pcd->test_mode = ctrl.wIndex >> 8; ++ tasklet_schedule(&pcd->test_mode_tasklet); ++ break; ++ ++ case USB_DEVICE_B_HNP_ENABLE: ++ DWC_DEBUGPL(DBG_PCDV, "SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); ++ ++ /* dev may initiate HNP */ ++ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { ++ pcd->b_hnp_enable = 1; ++ dwc_otg_pcd_update_otg(pcd, 0); ++ DWC_DEBUGPL(DBG_PCD, "Request B HNP\n"); ++ /**@todo Is the gotgctl.devhnpen cleared ++ * by a USB Reset? */ ++ gotgctl.b.devhnpen = 1; ++ gotgctl.b.hnpreq = 1; ++ dwc_write_reg32(&global_regs->gotgctl, gotgctl.d32); ++ } ++ else { ++ ep0_do_stall(pcd, -EOPNOTSUPP); ++ } ++ break; ++ ++ case USB_DEVICE_A_HNP_SUPPORT: ++ /* RH port supports HNP */ ++ DWC_DEBUGPL(DBG_PCDV, "SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n"); ++ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { ++ pcd->a_hnp_support = 1; ++ dwc_otg_pcd_update_otg(pcd, 0); ++ } ++ else { ++ ep0_do_stall(pcd, -EOPNOTSUPP); ++ } ++ break; ++ ++ case USB_DEVICE_A_ALT_HNP_SUPPORT: ++ /* other RH port does */ ++ DWC_DEBUGPL(DBG_PCDV, "SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); ++ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { ++ pcd->a_alt_hnp_support = 1; ++ dwc_otg_pcd_update_otg(pcd, 0); ++ } ++ else { ++ ep0_do_stall(pcd, -EOPNOTSUPP); ++ } ++ break; ++ } ++ do_setup_in_status_phase(pcd); ++ break; ++ ++ case USB_RECIP_INTERFACE: ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ ++ case USB_RECIP_ENDPOINT: ++ if (ctrl.wValue == USB_ENDPOINT_HALT) { ++ ep = get_ep_by_addr(pcd, ctrl.wIndex); ++ if (ep == 0) { ++ ep0_do_stall(pcd, -EOPNOTSUPP); ++ return; ++ } ++ ep->stopped = 1; ++ dwc_otg_ep_set_stall(core_if, &ep->dwc_ep); ++ } ++ do_setup_in_status_phase(pcd); ++ break; ++ } ++} ++ ++/** ++ * This function process the CLEAR_FEATURE Setup Commands. ++ */ ++static inline void do_clear_feature(dwc_otg_pcd_t *pcd) ++{ ++ struct usb_ctrlrequest ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep = 0; ++ ++ DWC_DEBUGPL(DBG_PCD, ++ "CLEAR_FEATURE:%02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bRequestType, ctrl.bRequest, ++ ctrl.wValue, ctrl.wIndex, ctrl.wLength); ++ ++ switch (ctrl.bRequestType & USB_RECIP_MASK) { ++ case USB_RECIP_DEVICE: ++ switch (ctrl.wValue) { ++ case USB_DEVICE_REMOTE_WAKEUP: ++ pcd->remote_wakeup_enable = 0; ++ break; ++ ++ case USB_DEVICE_TEST_MODE: ++ /** @todo Add CLEAR_FEATURE for TEST modes. */ ++ break; ++ } ++ do_setup_in_status_phase(pcd); ++ break; ++ ++ case USB_RECIP_ENDPOINT: ++ ep = get_ep_by_addr(pcd, ctrl.wIndex); ++ if (ep == 0) { ++ ep0_do_stall(pcd, -EOPNOTSUPP); ++ return; ++ } ++ ++ pcd_clear_halt(pcd, ep); ++ ++ break; ++ } ++} ++ ++/** ++ * This function process the SET_ADDRESS Setup Commands. ++ */ ++static inline void do_set_address(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if; ++ struct usb_ctrlrequest ctrl = pcd->setup_pkt->req; ++ ++ if (ctrl.bRequestType == USB_RECIP_DEVICE) { ++ dcfg_data_t dcfg = {.d32=0}; ++ ++#ifdef DEBUG_EP0 ++// DWC_DEBUGPL(DBG_PCDV, "SET_ADDRESS:%d\n", ctrl.wValue); ++#endif ++ dcfg.b.devaddr = ctrl.wValue; ++ dwc_modify_reg32(&dev_if->dev_global_regs->dcfg, 0, dcfg.d32); ++ do_setup_in_status_phase(pcd); ++ } ++} ++ ++/** ++ * This function processes SETUP commands. In Linux, the USB Command ++ * processing is done in two places - the first being the PCD and the ++ * second in the Gadget Driver (for example, the File-Backed Storage ++ * Gadget Driver). ++ * ++ *
Parameter NameMeaning
otg_capSpecifies the OTG capabilities. The driver will automatically detect the ++ value for this parameter if none is specified. ++ - 0: HNP and SRP capable (default, if available) ++ - 1: SRP Only capable ++ - 2: No HNP/SRP capable ++
dma_enableSpecifies whether to use slave or DMA mode for accessing the data FIFOs. ++ The driver will automatically detect the value for this parameter if none is ++ specified. ++ - 0: Slave ++ - 1: DMA (default, if available) ++
dma_burst_sizeThe DMA Burst size (applicable only for External DMA Mode). ++ - Values: 1, 4, 8 16, 32, 64, 128, 256 (default 32) ++
speedSpecifies the maximum speed of operation in host and device mode. The ++ actual speed depends on the speed of the attached device and the value of ++ phy_type. ++ - 0: High Speed (default) ++ - 1: Full Speed ++
host_support_fs_ls_low_powerSpecifies whether low power mode is supported when attached to a Full ++ Speed or Low Speed device in host mode. ++ - 0: Don't support low power mode (default) ++ - 1: Support low power mode ++
host_ls_low_power_phy_clkSpecifies the PHY clock rate in low power mode when connected to a Low ++ Speed device in host mode. This parameter is applicable only if ++ HOST_SUPPORT_FS_LS_LOW_POWER is enabled. ++ - 0: 48 MHz (default) ++ - 1: 6 MHz ++
enable_dynamic_fifo Specifies whether FIFOs may be resized by the driver software. ++ - 0: Use cC FIFO size parameters ++ - 1: Allow dynamic FIFO sizing (default) ++
data_fifo_sizeTotal number of 4-byte words in the data FIFO memory. This memory ++ includes the Rx FIFO, non-periodic Tx FIFO, and periodic Tx FIFOs. ++ - Values: 32 to 32768 (default 8192) ++ ++ Note: The total FIFO memory depth in the FPGA configuration is 8192. ++
dev_rx_fifo_sizeNumber of 4-byte words in the Rx FIFO in device mode when dynamic ++ FIFO sizing is enabled. ++ - Values: 16 to 32768 (default 1064) ++
dev_nperio_tx_fifo_sizeNumber of 4-byte words in the non-periodic Tx FIFO in device mode when ++ dynamic FIFO sizing is enabled. ++ - Values: 16 to 32768 (default 1024) ++
dev_perio_tx_fifo_size_n (n = 1 to 15)Number of 4-byte words in each of the periodic Tx FIFOs in device mode ++ when dynamic FIFO sizing is enabled. ++ - Values: 4 to 768 (default 256) ++
host_rx_fifo_sizeNumber of 4-byte words in the Rx FIFO in host mode when dynamic FIFO ++ sizing is enabled. ++ - Values: 16 to 32768 (default 1024) ++
host_nperio_tx_fifo_sizeNumber of 4-byte words in the non-periodic Tx FIFO in host mode when ++ dynamic FIFO sizing is enabled in the core. ++ - Values: 16 to 32768 (default 1024) ++
host_perio_tx_fifo_sizeNumber of 4-byte words in the host periodic Tx FIFO when dynamic FIFO ++ sizing is enabled. ++ - Values: 16 to 32768 (default 1024) ++
max_transfer_sizeThe maximum transfer size supported in bytes. ++ - Values: 2047 to 65,535 (default 65,535) ++
max_packet_countThe maximum number of packets in a transfer. ++ - Values: 15 to 511 (default 511) ++
host_channelsThe number of host channel registers to use. ++ - Values: 1 to 16 (default 12) ++ ++ Note: The FPGA configuration supports a maximum of 12 host channels. ++
dev_endpointsThe number of endpoints in addition to EP0 available for device mode ++ operations. ++ - Values: 1 to 15 (default 6 IN and OUT) ++ ++ Note: The FPGA configuration supports a maximum of 6 IN and OUT endpoints in ++ addition to EP0. ++
phy_typeSpecifies the type of PHY interface to use. By default, the driver will ++ automatically detect the phy_type. ++ - 0: Full Speed ++ - 1: UTMI+ (default, if available) ++ - 2: ULPI ++
phy_utmi_widthSpecifies the UTMI+ Data Width. This parameter is applicable for a ++ phy_type of UTMI+. Also, this parameter is applicable only if the ++ OTG_HSPHY_WIDTH cC parameter was set to "8 and 16 bits", meaning that the ++ core has been configured to work at either data path width. ++ - Values: 8 or 16 bits (default 16) ++
phy_ulpi_ddrSpecifies whether the ULPI operates at double or single data rate. This ++ parameter is only applicable if phy_type is ULPI. ++ - 0: single data rate ULPI interface with 8 bit wide data bus (default) ++ - 1: double data rate ULPI interface with 4 bit wide data bus ++
i2c_enableSpecifies whether to use the I2C interface for full speed PHY. This ++ parameter is only applicable if PHY_TYPE is FS. ++ - 0: Disabled (default) ++ - 1: Enabled ++
otg_en_multiple_tx_fifoSpecifies whether dedicatedto tx fifos are enabled for non periodic IN EPs. ++ The driver will automatically detect the value for this parameter if none is ++ specified. ++ - 0: Disabled ++ - 1: Enabled (default, if available) ++
dev_tx_fifo_size_n (n = 1 to 15)Number of 4-byte words in each of the Tx FIFOs in device mode ++ when dynamic FIFO sizing is enabled. ++ - Values: 4 to 768 (default 256) ++
++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ * ++ *
Command Driver Description
GET_STATUS PCD Command is processed as ++ * defined in chapter 9 of the USB 2.0 Specification chapter 9 ++ *
CLEAR_FEATURE PCD The Device and Endpoint ++ * requests are the ENDPOINT_HALT feature is procesed, all others the ++ * interface requests are ignored.
SET_FEATURE PCD The Device and Endpoint ++ * requests are processed by the PCD. Interface requests are passed ++ * to the Gadget Driver.
SET_ADDRESS PCD Program the DCFG reg, ++ * with device address received
GET_DESCRIPTOR Gadget Driver Return the ++ * requested descriptor
SET_DESCRIPTOR Gadget Driver Optional - ++ * not implemented by any of the existing Gadget Drivers.
SET_CONFIGURATION Gadget Driver Disable ++ * all EPs and enable EPs for new configuration.
GET_CONFIGURATION Gadget Driver Return ++ * the current configuration
SET_INTERFACE Gadget Driver Disable all ++ * EPs and enable EPs for new configuration.
GET_INTERFACE Gadget Driver Return the ++ * current interface.
SYNC_FRAME PCD Display debug ++ * message.
++ * ++ * When the SETUP Phase Done interrupt occurs, the PCD SETUP commands are ++ * processed by pcd_setup. Calling the Function Driver's setup function from ++ * pcd_setup processes the gadget SETUP commands. ++ */ ++static inline void pcd_setup(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ struct usb_ctrlrequest ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ ++ deptsiz0_data_t doeptsize0 = { .d32 = 0}; ++ ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "SETUP %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bRequestType, ctrl.bRequest, ++ ctrl.wValue, ctrl.wIndex, ctrl.wLength); ++#endif ++ ++ doeptsize0.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doeptsiz); ++ ++ /** @todo handle > 1 setup packet , assert error for now */ ++ ++ if (core_if->dma_enable && core_if->dma_desc_enable == 0 && (doeptsize0.b.supcnt < 2)) { ++ DWC_ERROR ("\n\n----------- CANNOT handle > 1 setup packet in DMA mode\n\n"); ++ } ++ ++ /* Clean up the request queue */ ++ dwc_otg_request_nuke(ep0); ++ ep0->stopped = 0; ++ ++ if (ctrl.bRequestType & USB_DIR_IN) { ++ ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_DATA_PHASE; ++ } ++ else { ++ ep0->dwc_ep.is_in = 0; ++ pcd->ep0state = EP0_OUT_DATA_PHASE; ++ } ++ ++ if(ctrl.wLength == 0) { ++ ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ } ++ ++ if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) { ++ /* handle non-standard (class/vendor) requests in the gadget driver */ ++ do_gadget_setup(pcd, &ctrl); ++ return; ++ } ++ ++ /** @todo NGS: Handle bad setup packet? */ ++ ++/////////////////////////////////////////// ++//// --- Standard Request handling --- //// ++ ++ switch (ctrl.bRequest) { ++ case USB_REQ_GET_STATUS: ++ do_get_status(pcd); ++ break; ++ ++ case USB_REQ_CLEAR_FEATURE: ++ do_clear_feature(pcd); ++ break; ++ ++ case USB_REQ_SET_FEATURE: ++ do_set_feature(pcd); ++ break; ++ ++ case USB_REQ_SET_ADDRESS: ++ do_set_address(pcd); ++ break; ++ ++ case USB_REQ_SET_INTERFACE: ++ case USB_REQ_SET_CONFIGURATION: ++// _pcd->request_config = 1; /* Configuration changed */ ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ ++ case USB_REQ_SYNCH_FRAME: ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ ++ default: ++ /* Call the Gadget Driver's setup functions */ ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ } ++} ++ ++/** ++ * This function completes the ep0 control transfer. ++ */ ++static int32_t ep0_complete_request(dwc_otg_pcd_ep_t *ep) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_dev_in_ep_regs_t *in_ep_regs = ++ dev_if->in_ep_regs[ep->dwc_ep.num]; ++#ifdef DEBUG_EP0 ++ dwc_otg_dev_out_ep_regs_t *out_ep_regs = ++ dev_if->out_ep_regs[ep->dwc_ep.num]; ++#endif ++ deptsiz0_data_t deptsiz; ++ desc_sts_data_t desc_sts; ++ dwc_otg_pcd_request_t *req; ++ int is_last = 0; ++ dwc_otg_pcd_t *pcd = ep->pcd; ++ ++ //DWC_DEBUGPL(DBG_PCDV, "%s() %s\n", __func__, _ep->ep.name); ++ ++ if (pcd->ep0_pending && list_empty(&ep->queue)) { ++ if (ep->dwc_ep.is_in) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Do setup OUT status phase\n"); ++#endif ++ do_setup_out_status_phase(pcd); ++ } ++ else { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Do setup IN status phase\n"); ++#endif ++ do_setup_in_status_phase(pcd); ++ } ++ pcd->ep0_pending = 0; ++ return 1; ++ } ++ ++ if (list_empty(&ep->queue)) { ++ return 0; ++ } ++ req = list_entry(ep->queue.next, dwc_otg_pcd_request_t, queue); ++ ++ ++ if (pcd->ep0state == EP0_OUT_STATUS_PHASE || pcd->ep0state == EP0_IN_STATUS_PHASE) { ++ is_last = 1; ++ } ++ else if (ep->dwc_ep.is_in) { ++ deptsiz.d32 = dwc_read_reg32(&in_ep_regs->dieptsiz); ++ if(core_if->dma_desc_enable != 0) ++ desc_sts.d32 = readl(dev_if->in_desc_addr); ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "%s len=%d xfersize=%d pktcnt=%d\n", ++ ep->ep.name, ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++#endif ++ ++ if (((core_if->dma_desc_enable == 0) && (deptsiz.b.xfersize == 0)) || ++ ((core_if->dma_desc_enable != 0) && (desc_sts.b.bytes == 0))) { ++ req->req.actual = ep->dwc_ep.xfer_count; ++ /* Is a Zero Len Packet needed? */ ++ if (req->req.zero) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "Setup Rx ZLP\n"); ++#endif ++ req->req.zero = 0; ++ } ++ do_setup_out_status_phase(pcd); ++ } ++ } ++ else { ++ /* ep0-OUT */ ++#ifdef DEBUG_EP0 ++ deptsiz.d32 = dwc_read_reg32(&out_ep_regs->doeptsiz); ++ DWC_DEBUGPL(DBG_PCDV, "%s len=%d xsize=%d pktcnt=%d\n", ++ ep->ep.name, ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, ++ deptsiz.b.pktcnt); ++#endif ++ req->req.actual = ep->dwc_ep.xfer_count; ++ /* Is a Zero Len Packet needed? */ ++ if (req->req.zero) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Setup Tx ZLP\n"); ++#endif ++ req->req.zero = 0; ++ } ++ if(core_if->dma_desc_enable == 0) ++ do_setup_in_status_phase(pcd); ++ } ++ ++ /* Complete the request */ ++ if (is_last) { ++ dwc_otg_request_done(ep, req, 0); ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ return 1; ++ } ++ return 0; ++} ++ ++/** ++ * This function completes the request for the EP. If there are ++ * additional requests for the EP in the queue they will be started. ++ */ ++static void complete_ep(dwc_otg_pcd_ep_t *ep) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_dev_in_ep_regs_t *in_ep_regs = ++ dev_if->in_ep_regs[ep->dwc_ep.num]; ++ deptsiz_data_t deptsiz; ++ desc_sts_data_t desc_sts; ++ dwc_otg_pcd_request_t *req = 0; ++ dwc_otg_dma_desc_t* dma_desc; ++ uint32_t byte_count = 0; ++ int is_last = 0; ++ int i; ++ ++ DWC_DEBUGPL(DBG_PCDV,"%s() %s-%s\n", __func__, ep->ep.name, ++ (ep->dwc_ep.is_in?"IN":"OUT")); ++ ++ /* Get any pending requests */ ++ if (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, dwc_otg_pcd_request_t, ++ queue); ++ if (!req) { ++ printk("complete_ep 0x%p, req = NULL!\n", ep); ++ return; ++ } ++ } ++ else { ++ printk("complete_ep 0x%p, ep->queue empty!\n", ep); ++ return; ++ } ++ DWC_DEBUGPL(DBG_PCD, "Requests %d\n", ep->pcd->request_pending); ++ ++ if (ep->dwc_ep.is_in) { ++ deptsiz.d32 = dwc_read_reg32(&in_ep_regs->dieptsiz); ++ ++ if (core_if->dma_enable) { ++ if(core_if->dma_desc_enable == 0) { ++ if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0) { ++ byte_count = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ ++ ep->dwc_ep.xfer_buff += byte_count; ++ ep->dwc_ep.dma_addr += byte_count; ++ ep->dwc_ep.xfer_count += byte_count; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s len=%d xfersize=%d pktcnt=%d\n", ++ ep->ep.name, ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ ++ ++ if(ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep); ++ } else if(ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length trasfer in case if it is queued ++ * a trasfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Desriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 legth. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer(core_if, &ep->dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } else { ++ DWC_WARN("Incomplete transfer (%s-%s [siz=%d pkt=%d])\n", ++ ep->ep.name, (ep->dwc_ep.is_in?"IN":"OUT"), ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ } ++ } else { ++ dma_desc = ep->dwc_ep.desc_addr; ++ byte_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ++ for(i = 0; i < ep->dwc_ep.desc_cnt; ++i) { ++ desc_sts.d32 = readl(dma_desc); ++ byte_count += desc_sts.b.bytes; ++ dma_desc++; ++ } ++ ++ if(byte_count == 0) { ++ ep->dwc_ep.xfer_count = ep->dwc_ep.total_len; ++ is_last = 1; ++ } else { ++ DWC_WARN("Incomplete transfer\n"); ++ } ++ } ++ } else { ++ if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0) { ++ /* Check if the whole transfer was completed, ++ * if no, setup transfer for next portion of data ++ */ ++ DWC_DEBUGPL(DBG_PCDV, "%s len=%d xfersize=%d pktcnt=%d\n", ++ ep->ep.name, ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ if(ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep); ++ } else if(ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length trasfer in case if it is queued ++ * a trasfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Desriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 legth. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer(core_if, &ep->dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } ++ else { ++ DWC_WARN("Incomplete transfer (%s-%s [siz=%d pkt=%d])\n", ++ ep->ep.name, (ep->dwc_ep.is_in?"IN":"OUT"), ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ } ++ } ++ } else { ++ dwc_otg_dev_out_ep_regs_t *out_ep_regs = ++ dev_if->out_ep_regs[ep->dwc_ep.num]; ++ desc_sts.d32 = 0; ++ if(core_if->dma_enable) { ++ if(core_if->dma_desc_enable) { ++ dma_desc = ep->dwc_ep.desc_addr; ++ byte_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ for(i = 0; i < ep->dwc_ep.desc_cnt; ++i) { ++ desc_sts.d32 = readl(dma_desc); ++ byte_count += desc_sts.b.bytes; ++ dma_desc++; ++ } ++ ++ ep->dwc_ep.xfer_count = ep->dwc_ep.total_len ++ - byte_count + ((4 - (ep->dwc_ep.total_len & 0x3)) & 0x3); ++ is_last = 1; ++ } else { ++ deptsiz.d32 = 0; ++ deptsiz.d32 = dwc_read_reg32(&out_ep_regs->doeptsiz); ++ ++ byte_count = (ep->dwc_ep.xfer_len - ++ ep->dwc_ep.xfer_count - deptsiz.b.xfersize); ++ ep->dwc_ep.xfer_buff += byte_count; ++ ep->dwc_ep.dma_addr += byte_count; ++ ep->dwc_ep.xfer_count += byte_count; ++ ++ /* Check if the whole transfer was completed, ++ * if no, setup transfer for next portion of data ++ */ ++ if(ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep); ++ } ++ else if(ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length trasfer in case if it is queued ++ * a trasfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Desriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 legth. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer(core_if, &ep->dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } ++ } else { ++ /* Check if the whole transfer was completed, ++ * if no, setup transfer for next portion of data ++ */ ++ if(ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep); ++ } ++ else if(ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length trasfer in case if it is queued ++ * a trasfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Desriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 legth. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer(core_if, &ep->dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } ++ ++#ifdef DEBUG ++ ++ DWC_DEBUGPL(DBG_PCDV, "addr %p, %s len=%d cnt=%d xsize=%d pktcnt=%d\n", ++ &out_ep_regs->doeptsiz, ep->ep.name, ep->dwc_ep.xfer_len, ++ ep->dwc_ep.xfer_count, ++ deptsiz.b.xfersize, ++ deptsiz.b.pktcnt); ++#endif ++ } ++ ++ /* Complete the request */ ++ if (is_last) { ++ req->req.actual = ep->dwc_ep.xfer_count; ++ ++ dwc_otg_request_done(ep, req, 0); ++ ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ ++ /* If there is a request in the queue start it.*/ ++ start_next_request(ep); ++ } ++} ++ ++ ++#ifdef DWC_EN_ISOC ++ ++/** ++ * This function BNA interrupt for Isochronous EPs ++ * ++ */ ++static void dwc_otg_pcd_handle_iso_bna(dwc_otg_pcd_ep_t *ep) ++{ ++ dwc_ep_t *dwc_ep = &ep->dwc_ep; ++ volatile uint32_t *addr; ++ depctl_data_t depctl = {.d32 = 0}; ++ dwc_otg_pcd_t *pcd = ep->pcd; ++ dwc_otg_dma_desc_t *dma_desc; ++ int i; ++ ++ dma_desc = dwc_ep->iso_desc_addr + dwc_ep->desc_cnt * (dwc_ep->proc_buf_num); ++ ++ if(dwc_ep->is_in) { ++ desc_sts_data_t sts = {.d32 = 0}; ++ for(i = 0;i < dwc_ep->desc_cnt; ++i, ++dma_desc) ++ { ++ sts.d32 = readl(&dma_desc->status); ++ sts.b_iso_in.bs = BS_HOST_READY; ++ writel(sts.d32,&dma_desc->status); ++ } ++ } ++ else { ++ desc_sts_data_t sts = {.d32 = 0}; ++ for(i = 0;i < dwc_ep->desc_cnt; ++i, ++dma_desc) ++ { ++ sts.d32 = readl(&dma_desc->status); ++ sts.b_iso_out.bs = BS_HOST_READY; ++ writel(sts.d32,&dma_desc->status); ++ } ++ } ++ ++ if(dwc_ep->is_in == 0){ ++ addr = &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep->num]->doepctl; ++ } ++ else{ ++ addr = &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl; ++ } ++ depctl.b.epena = 1; ++ dwc_modify_reg32(addr,depctl.d32,depctl.d32); ++} ++ ++/** ++ * This function sets latest iso packet information(non-PTI mode) ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void set_current_pkt_info(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ deptsiz_data_t deptsiz = { .d32 = 0 }; ++ dma_addr_t dma_addr; ++ uint32_t offset; ++ ++ if(ep->proc_buf_num) ++ dma_addr = ep->dma_addr1; ++ else ++ dma_addr = ep->dma_addr0; ++ ++ ++ if(ep->is_in) { ++ deptsiz.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[ep->num]->dieptsiz); ++ offset = ep->data_per_frame; ++ } else { ++ deptsiz.d32 = dwc_read_reg32(&core_if->dev_if->out_ep_regs[ep->num]->doeptsiz); ++ offset = ep->data_per_frame + (0x4 & (0x4 - (ep->data_per_frame & 0x3))); ++ } ++ ++ if(!deptsiz.b.xfersize) { ++ ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame; ++ ep->pkt_info[ep->cur_pkt].offset = ep->cur_pkt_dma_addr - dma_addr; ++ ep->pkt_info[ep->cur_pkt].status = 0; ++ } else { ++ ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame; ++ ep->pkt_info[ep->cur_pkt].offset = ep->cur_pkt_dma_addr - dma_addr; ++ ep->pkt_info[ep->cur_pkt].status = -ENODATA; ++ } ++ ep->cur_pkt_addr += offset; ++ ep->cur_pkt_dma_addr += offset; ++ ep->cur_pkt++; ++} ++ ++/** ++ * This function sets latest iso packet information(DDMA mode) ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP to start the transfer on. ++ * ++ */ ++static void set_ddma_iso_pkts_info(dwc_otg_core_if_t *core_if, dwc_ep_t *dwc_ep) ++{ ++ dwc_otg_dma_desc_t* dma_desc; ++ desc_sts_data_t sts = {.d32 = 0}; ++ iso_pkt_info_t *iso_packet; ++ uint32_t data_per_desc; ++ uint32_t offset; ++ int i, j; ++ ++ iso_packet = dwc_ep->pkt_info; ++ ++ /** Reinit closed DMA Descriptors*/ ++ /** ISO OUT EP */ ++ if(dwc_ep->is_in == 0) { ++ dma_desc = dwc_ep->iso_desc_addr + dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ offset = 0; ++ ++ for(i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; i+= dwc_ep->pkt_per_frm) ++ { ++ for(j = 0; j < dwc_ep->pkt_per_frm; ++j) ++ { ++ data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0; ++ ++ sts.d32 = readl(&dma_desc->status); ++ ++ /* Write status in iso_packet_decsriptor */ ++ iso_packet->status = sts.b_iso_out.rxsts + (sts.b_iso_out.bs^BS_DMA_DONE); ++ if(iso_packet->status) { ++ iso_packet->status = -ENODATA; ++ } ++ ++ /* Received data length */ ++ if(!sts.b_iso_out.rxbytes){ ++ iso_packet->length = data_per_desc - sts.b_iso_out.rxbytes; ++ } else { ++ iso_packet->length = data_per_desc - sts.b_iso_out.rxbytes + ++ (4 - dwc_ep->data_per_frame % 4); ++ } ++ ++ iso_packet->offset = offset; ++ ++ offset += data_per_desc; ++ dma_desc ++; ++ iso_packet ++; ++ } ++ } ++ ++ for(j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) ++ { ++ data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0; ++ ++ sts.d32 = readl(&dma_desc->status); ++ ++ /* Write status in iso_packet_decsriptor */ ++ iso_packet->status = sts.b_iso_out.rxsts + (sts.b_iso_out.bs^BS_DMA_DONE); ++ if(iso_packet->status) { ++ iso_packet->status = -ENODATA; ++ } ++ ++ /* Received data length */ ++ iso_packet->length = dwc_ep->data_per_frame - sts.b_iso_out.rxbytes; ++ ++ iso_packet->offset = offset; ++ ++ offset += data_per_desc; ++ iso_packet++; ++ dma_desc++; ++ } ++ ++ sts.d32 = readl(&dma_desc->status); ++ ++ /* Write status in iso_packet_decsriptor */ ++ iso_packet->status = sts.b_iso_out.rxsts + (sts.b_iso_out.bs^BS_DMA_DONE); ++ if(iso_packet->status) { ++ iso_packet->status = -ENODATA; ++ } ++ /* Received data length */ ++ if(!sts.b_iso_out.rxbytes){ ++ iso_packet->length = dwc_ep->data_per_frame - sts.b_iso_out.rxbytes; ++ } else { ++ iso_packet->length = dwc_ep->data_per_frame - sts.b_iso_out.rxbytes + ++ (4 - dwc_ep->data_per_frame % 4); ++ } ++ ++ iso_packet->offset = offset; ++ } ++ else /** ISO IN EP */ ++ { ++ dma_desc = dwc_ep->iso_desc_addr + dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ ++ for(i = 0; i < dwc_ep->desc_cnt - 1; i++) ++ { ++ sts.d32 = readl(&dma_desc->status); ++ ++ /* Write status in iso packet descriptor */ ++ iso_packet->status = sts.b_iso_in.txsts + (sts.b_iso_in.bs^BS_DMA_DONE); ++ if(iso_packet->status != 0) { ++ iso_packet->status = -ENODATA; ++ ++ } ++ /* Bytes has been transfered */ ++ iso_packet->length = dwc_ep->data_per_frame - sts.b_iso_in.txbytes; ++ ++ dma_desc ++; ++ iso_packet++; ++ } ++ ++ sts.d32 = readl(&dma_desc->status); ++ while(sts.b_iso_in.bs == BS_DMA_BUSY) { ++ sts.d32 = readl(&dma_desc->status); ++ } ++ ++ /* Write status in iso packet descriptor ??? do be done with ERROR codes*/ ++ iso_packet->status = sts.b_iso_in.txsts + (sts.b_iso_in.bs^BS_DMA_DONE); ++ if(iso_packet->status != 0) { ++ iso_packet->status = -ENODATA; ++ } ++ ++ /* Bytes has been transfered */ ++ iso_packet->length = dwc_ep->data_per_frame - sts.b_iso_in.txbytes; ++ } ++} ++ ++/** ++ * This function reinitialize DMA Descriptors for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP to start the transfer on. ++ * ++ */ ++static void reinit_ddma_iso_xfer(dwc_otg_core_if_t *core_if, dwc_ep_t *dwc_ep) ++{ ++ int i, j; ++ dwc_otg_dma_desc_t* dma_desc; ++ dma_addr_t dma_ad; ++ volatile uint32_t *addr; ++ desc_sts_data_t sts = { .d32 =0 }; ++ uint32_t data_per_desc; ++ ++ if(dwc_ep->is_in == 0) { ++ addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl; ++ } ++ else { ++ addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl; ++ } ++ ++ ++ if(dwc_ep->proc_buf_num == 0) { ++ /** Buffer 0 descriptors setup */ ++ dma_ad = dwc_ep->dma_addr0; ++ } ++ else { ++ /** Buffer 1 descriptors setup */ ++ dma_ad = dwc_ep->dma_addr1; ++ } ++ ++ ++ /** Reinit closed DMA Descriptors*/ ++ /** ISO OUT EP */ ++ if(dwc_ep->is_in == 0) { ++ dma_desc = dwc_ep->iso_desc_addr + dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ ++ sts.b_iso_out.bs = BS_HOST_READY; ++ sts.b_iso_out.rxsts = 0; ++ sts.b_iso_out.l = 0; ++ sts.b_iso_out.sp = 0; ++ sts.b_iso_out.ioc = 0; ++ sts.b_iso_out.pid = 0; ++ sts.b_iso_out.framenum = 0; ++ ++ for(i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; i+= dwc_ep->pkt_per_frm) ++ { ++ for(j = 0; j < dwc_ep->pkt_per_frm; ++j) ++ { ++ data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ ++ (uint32_t)dma_ad += data_per_desc; ++ dma_desc ++; ++ } ++ } ++ ++ for(j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) ++ { ++ ++ data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ ++ dma_desc++; ++ (uint32_t)dma_ad += data_per_desc; ++ } ++ ++ sts.b_iso_out.ioc = 1; ++ sts.b_iso_out.l = dwc_ep->proc_buf_num; ++ ++ data_per_desc = ((j + 1) * dwc_ep->maxpacket > dwc_ep->data_per_frame) ? ++ dwc_ep->data_per_frame - j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += (data_per_desc % 4) ? (4 - data_per_desc % 4):0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ } ++ else /** ISO IN EP */ ++ { ++ dma_desc = dwc_ep->iso_desc_addr + dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ ++ sts.b_iso_in.bs = BS_HOST_READY; ++ sts.b_iso_in.txsts = 0; ++ sts.b_iso_in.sp = 0; ++ sts.b_iso_in.ioc = 0; ++ sts.b_iso_in.pid = dwc_ep->pkt_per_frm; ++ sts.b_iso_in.framenum = dwc_ep->next_frame; ++ sts.b_iso_in.txbytes = dwc_ep->data_per_frame; ++ sts.b_iso_in.l = 0; ++ ++ for(i = 0; i < dwc_ep->desc_cnt - 1; i++) ++ { ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ ++ sts.b_iso_in.framenum += dwc_ep->bInterval; ++ (uint32_t)dma_ad += dwc_ep->data_per_frame; ++ dma_desc ++; ++ } ++ ++ sts.b_iso_in.ioc = 1; ++ sts.b_iso_in.l = dwc_ep->proc_buf_num; ++ ++ writel((uint32_t)dma_ad, &dma_desc->buf); ++ writel(sts.d32, &dma_desc->status); ++ ++ dwc_ep->next_frame = sts.b_iso_in.framenum + dwc_ep->bInterval * 1; ++ } ++ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1; ++} ++ ++ ++/** ++ * This function is to handle Iso EP transfer complete interrupt ++ * in case Iso out packet was dropped ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP for wihich transfer complete was asserted ++ * ++ */ ++static uint32_t handle_iso_out_pkt_dropped(dwc_otg_core_if_t *core_if, dwc_ep_t *dwc_ep) ++{ ++ uint32_t dma_addr; ++ uint32_t drp_pkt; ++ uint32_t drp_pkt_cnt; ++ deptsiz_data_t deptsiz = { .d32 = 0 }; ++ depctl_data_t depctl = { .d32 = 0 }; ++ int i; ++ ++ deptsiz.d32 = dwc_read_reg32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz); ++ ++ drp_pkt = dwc_ep->pkt_cnt - deptsiz.b.pktcnt; ++ drp_pkt_cnt = dwc_ep->pkt_per_frm - (drp_pkt % dwc_ep->pkt_per_frm); ++ ++ /* Setting dropped packets status */ ++ for(i = 0; i < drp_pkt_cnt; ++i) { ++ dwc_ep->pkt_info[drp_pkt].status = -ENODATA; ++ drp_pkt ++; ++ deptsiz.b.pktcnt--; ++ } ++ ++ ++ if(deptsiz.b.pktcnt > 0) { ++ deptsiz.b.xfersize = dwc_ep->xfer_len - (dwc_ep->pkt_cnt - deptsiz.b.pktcnt) * dwc_ep->maxpacket; ++ } else { ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 0; ++ } ++ ++ dwc_write_reg32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz, deptsiz.d32); ++ ++ if(deptsiz.b.pktcnt > 0) { ++ if(dwc_ep->proc_buf_num) { ++ dma_addr = dwc_ep->dma_addr1 + dwc_ep->xfer_len - deptsiz.b.xfersize; ++ } else { ++ dma_addr = dwc_ep->dma_addr0 + dwc_ep->xfer_len - deptsiz.b.xfersize;; ++ } ++ ++ dwc_write_reg32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepdma, dma_addr); ++ ++ /** Re-enable endpoint, clear nak */ ++ depctl.d32 = 0; ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ ++ dwc_modify_reg32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl, ++ depctl.d32,depctl.d32); ++ return 0; ++ } else { ++ return 1; ++ } ++} ++ ++/** ++ * This function sets iso packets information(PTI mode) ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++static uint32_t set_iso_pkts_info(dwc_otg_core_if_t *core_if, dwc_ep_t *ep) ++{ ++ int i, j; ++ dma_addr_t dma_ad; ++ iso_pkt_info_t *packet_info = ep->pkt_info; ++ uint32_t offset; ++ uint32_t frame_data; ++ deptsiz_data_t deptsiz; ++ ++ if(ep->proc_buf_num == 0) { ++ /** Buffer 0 descriptors setup */ ++ dma_ad = ep->dma_addr0; ++ } ++ else { ++ /** Buffer 1 descriptors setup */ ++ dma_ad = ep->dma_addr1; ++ } ++ ++ ++ if(ep->is_in) { ++ deptsiz.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[ep->num]->dieptsiz); ++ } else { ++ deptsiz.d32 = dwc_read_reg32(&core_if->dev_if->out_ep_regs[ep->num]->doeptsiz); ++ } ++ ++ if(!deptsiz.b.xfersize) { ++ offset = 0; ++ for(i = 0; i < ep->pkt_cnt; i += ep->pkt_per_frm) ++ { ++ frame_data = ep->data_per_frame; ++ for(j = 0; j < ep->pkt_per_frm; ++j) { ++ ++ /* Packet status - is not set as initially ++ * it is set to 0 and if packet was sent ++ successfully, status field will remain 0*/ ++ ++ ++ /* Bytes has been transfered */ ++ packet_info->length = (ep->maxpacket < frame_data) ? ++ ep->maxpacket : frame_data; ++ ++ /* Received packet offset */ ++ packet_info->offset = offset; ++ offset += packet_info->length; ++ frame_data -= packet_info->length; ++ ++ packet_info ++; ++ } ++ } ++ return 1; ++ } else { ++ /* This is a workaround for in case of Transfer Complete with ++ * PktDrpSts interrupts merging - in this case Transfer complete ++ * interrupt for Isoc Out Endpoint is asserted without PktDrpSts ++ * set and with DOEPTSIZ register non zero. Investigations showed, ++ * that this happens when Out packet is dropped, but because of ++ * interrupts merging during first interrupt handling PktDrpSts ++ * bit is cleared and for next merged interrupts it is not reset. ++ * In this case SW hadles the interrupt as if PktDrpSts bit is set. ++ */ ++ if(ep->is_in) { ++ return 1; ++ } else { ++ return handle_iso_out_pkt_dropped(core_if, ep); ++ } ++ } ++} ++ ++/** ++ * This function is to handle Iso EP transfer complete interrupt ++ * ++ * @param ep The EP for which transfer complete was asserted ++ * ++ */ ++static void complete_iso_ep(dwc_otg_pcd_ep_t *ep) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd); ++ dwc_ep_t *dwc_ep = &ep->dwc_ep; ++ uint8_t is_last = 0; ++ ++ if(core_if->dma_enable) { ++ if(core_if->dma_desc_enable) { ++ set_ddma_iso_pkts_info(core_if, dwc_ep); ++ reinit_ddma_iso_xfer(core_if, dwc_ep); ++ is_last = 1; ++ } else { ++ if(core_if->pti_enh_enable) { ++ if(set_iso_pkts_info(core_if, dwc_ep)) { ++ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ dwc_otg_iso_ep_start_buf_transfer(core_if, dwc_ep); ++ is_last = 1; ++ } ++ } else { ++ set_current_pkt_info(core_if, dwc_ep); ++ if(dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ is_last = 1; ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ if(dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr0; ++ } ++ ++ } ++ dwc_otg_iso_ep_start_frm_transfer(core_if, dwc_ep); ++ } ++ } ++ } else { ++ set_current_pkt_info(core_if, dwc_ep); ++ if(dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ is_last = 1; ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ if(dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr0; ++ } ++ ++ } ++ dwc_otg_iso_ep_start_frm_transfer(core_if, dwc_ep); ++ } ++ if(is_last) ++ dwc_otg_iso_buffer_done(ep, ep->iso_req); ++} ++ ++#endif //DWC_EN_ISOC ++ ++ ++/** ++ * This function handles EP0 Control transfers. ++ * ++ * The state of the control tranfers are tracked in ++ * ep0state. ++ */ ++static void handle_ep0(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ desc_sts_data_t desc_sts; ++ deptsiz0_data_t deptsiz; ++ uint32_t byte_count; ++ ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__); ++ print_ep0_state(pcd); ++#endif ++ ++ switch (pcd->ep0state) { ++ case EP0_DISCONNECT: ++ break; ++ ++ case EP0_IDLE: ++ pcd->request_config = 0; ++ ++ pcd_setup(pcd); ++ break; ++ ++ case EP0_IN_DATA_PHASE: ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "DATA_IN EP%d-%s: type=%d, mps=%d\n", ++ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ?"IN":"OUT"), ++ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket); ++#endif ++ ++ if (core_if->dma_enable != 0) { ++ /* ++ * For EP0 we can only program 1 packet at a time so we ++ * need to do the make calculations after each complete. ++ * Call write_packet to make the calculations, as in ++ * slave mode, and use those values to determine if we ++ * can complete. ++ */ ++ if(core_if->dma_desc_enable == 0) { ++ deptsiz.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[0]->dieptsiz); ++ byte_count = ep0->dwc_ep.xfer_len - deptsiz.b.xfersize; ++ } ++ else { ++ desc_sts.d32 = readl(core_if->dev_if->in_desc_addr); ++ byte_count = ep0->dwc_ep.xfer_len - desc_sts.b.bytes; ++ } ++ ep0->dwc_ep.xfer_count += byte_count; ++ ep0->dwc_ep.xfer_buff += byte_count; ++ ep0->dwc_ep.dma_addr += byte_count; ++ } ++ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) { ++ dwc_otg_ep0_continue_transfer (GET_CORE_IF(pcd), &ep0->dwc_ep); ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n"); ++ } ++ else if(ep0->dwc_ep.sent_zlp) { ++ dwc_otg_ep0_continue_transfer (GET_CORE_IF(pcd), &ep0->dwc_ep); ++ ep0->dwc_ep.sent_zlp = 0; ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n"); ++ } ++ else { ++ ep0_complete_request(ep0); ++ DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n"); ++ } ++ break; ++ case EP0_OUT_DATA_PHASE: ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "DATA_OUT EP%d-%s: type=%d, mps=%d\n", ++ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ?"IN":"OUT"), ++ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket); ++#endif ++ if (core_if->dma_enable != 0) { ++ if(core_if->dma_desc_enable == 0) { ++ deptsiz.d32 = dwc_read_reg32(&core_if->dev_if->out_ep_regs[0]->doeptsiz); ++ byte_count = ep0->dwc_ep.maxpacket - deptsiz.b.xfersize; ++ } ++ else { ++ desc_sts.d32 = readl(core_if->dev_if->out_desc_addr); ++ byte_count = ep0->dwc_ep.maxpacket - desc_sts.b.bytes; ++ } ++ ep0->dwc_ep.xfer_count += byte_count; ++ ep0->dwc_ep.xfer_buff += byte_count; ++ ep0->dwc_ep.dma_addr += byte_count; ++ } ++ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) { ++ dwc_otg_ep0_continue_transfer (GET_CORE_IF(pcd), &ep0->dwc_ep); ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n"); ++ } ++ else if(ep0->dwc_ep.sent_zlp) { ++ dwc_otg_ep0_continue_transfer (GET_CORE_IF(pcd), &ep0->dwc_ep); ++ ep0->dwc_ep.sent_zlp = 0; ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n"); ++ } ++ else { ++ ep0_complete_request(ep0); ++ DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n"); ++ } ++ break; ++ ++ ++ case EP0_IN_STATUS_PHASE: ++ case EP0_OUT_STATUS_PHASE: ++ DWC_DEBUGPL(DBG_PCD, "CASE: EP0_STATUS\n"); ++ ep0_complete_request(ep0); ++ pcd->ep0state = EP0_IDLE; ++ ep0->stopped = 1; ++ ep0->dwc_ep.is_in = 0; /* OUT for next SETUP */ ++ ++ /* Prepare for more SETUP Packets */ ++ if(core_if->dma_enable) { ++ ep0_out_start(core_if, pcd); ++ } ++ break; ++ ++ case EP0_STALL: ++ DWC_ERROR("EP0 STALLed, should not get here pcd_setup()\n"); ++ break; ++ } ++#ifdef DEBUG_EP0 ++ print_ep0_state(pcd); ++#endif ++} ++ ++ ++/** ++ * Restart transfer ++ */ ++static void restart_transfer(dwc_otg_pcd_t *pcd, const uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if; ++ dwc_otg_dev_if_t *dev_if; ++ deptsiz_data_t dieptsiz = {.d32=0}; ++ dwc_otg_pcd_ep_t *ep; ++ ++ ep = get_in_ep(pcd, epnum); ++ ++#ifdef DWC_EN_ISOC ++ if(ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { ++ return; ++ } ++#endif /* DWC_EN_ISOC */ ++ ++ core_if = GET_CORE_IF(pcd); ++ dev_if = core_if->dev_if; ++ ++ dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dieptsiz); ++ ++ DWC_DEBUGPL(DBG_PCD,"xfer_buff=%p xfer_count=%0x xfer_len=%0x" ++ " stopped=%d\n", ep->dwc_ep.xfer_buff, ++ ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len , ++ ep->stopped); ++ /* ++ * If xfersize is 0 and pktcnt in not 0, resend the last packet. ++ */ ++ if (dieptsiz.b.pktcnt && dieptsiz.b.xfersize == 0 && ++ ep->dwc_ep.start_xfer_buff != 0) { ++ if (ep->dwc_ep.total_len <= ep->dwc_ep.maxpacket) { ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.xfer_buff = ep->dwc_ep.start_xfer_buff; ++ ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count; ++ } ++ else { ++ ep->dwc_ep.xfer_count -= ep->dwc_ep.maxpacket; ++ /* convert packet size to dwords. */ ++ ep->dwc_ep.xfer_buff -= ep->dwc_ep.maxpacket; ++ ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count; ++ } ++ ep->stopped = 0; ++ DWC_DEBUGPL(DBG_PCD,"xfer_buff=%p xfer_count=%0x " ++ "xfer_len=%0x stopped=%d\n", ++ ep->dwc_ep.xfer_buff, ++ ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len , ++ ep->stopped ++ ); ++ if (epnum == 0) { ++ dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep); ++ } ++ else { ++ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep); ++ } ++ } ++} ++ ++ ++/** ++ * handle the IN EP disable interrupt. ++ */ ++static inline void handle_in_ep_disable_intr(dwc_otg_pcd_t *pcd, ++ const uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ deptsiz_data_t dieptsiz = {.d32=0}; ++ dctl_data_t dctl = {.d32=0}; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ++ ep = get_in_ep(pcd, epnum); ++ dwc_ep = &ep->dwc_ep; ++ ++ if(dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num); ++ return; ++ } ++ ++ DWC_DEBUGPL(DBG_PCD,"diepctl%d=%0x\n", epnum, ++ dwc_read_reg32(&dev_if->in_ep_regs[epnum]->diepctl)); ++ dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dieptsiz); ++ ++ DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n", ++ dieptsiz.b.pktcnt, ++ dieptsiz.b.xfersize); ++ ++ if (ep->stopped) { ++ /* Flush the Tx FIFO */ ++ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num); ++ /* Clear the Global IN NP NAK */ ++ dctl.d32 = 0; ++ dctl.b.cgnpinnak = 1; ++ dwc_modify_reg32(&dev_if->dev_global_regs->dctl, ++ dctl.d32, 0); ++ /* Restart the transaction */ ++ if (dieptsiz.b.pktcnt != 0 || ++ dieptsiz.b.xfersize != 0) { ++ restart_transfer(pcd, epnum); ++ } ++ } ++ else { ++ /* Restart the transaction */ ++ if (dieptsiz.b.pktcnt != 0 || ++ dieptsiz.b.xfersize != 0) { ++ restart_transfer(pcd, epnum); ++ } ++ DWC_DEBUGPL(DBG_ANY, "STOPPED!!!\n"); ++ } ++} ++ ++/** ++ * Handler for the IN EP timeout handshake interrupt. ++ */ ++static inline void handle_in_ep_timeout_intr(dwc_otg_pcd_t *pcd, ++ const uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ ++#ifdef DEBUG ++ deptsiz_data_t dieptsiz = {.d32=0}; ++ uint32_t num = 0; ++#endif ++ dctl_data_t dctl = {.d32=0}; ++ dwc_otg_pcd_ep_t *ep; ++ ++ gintmsk_data_t intr_mask = {.d32 = 0}; ++ ++ ep = get_in_ep(pcd, epnum); ++ ++ /* Disable the NP Tx Fifo Empty Interrrupt */ ++ if (!core_if->dma_enable) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, intr_mask.d32, 0); ++ } ++ /** @todo NGS Check EP type. ++ * Implement for Periodic EPs */ ++ /* ++ * Non-periodic EP ++ */ ++ /* Enable the Global IN NAK Effective Interrupt */ ++ intr_mask.b.ginnakeff = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, ++ 0, intr_mask.d32); ++ ++ /* Set Global IN NAK */ ++ dctl.b.sgnpinnak = 1; ++ dwc_modify_reg32(&dev_if->dev_global_regs->dctl, ++ dctl.d32, dctl.d32); ++ ++ ep->stopped = 1; ++ ++#ifdef DEBUG ++ dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[num]->dieptsiz); ++ DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n", ++ dieptsiz.b.pktcnt, ++ dieptsiz.b.xfersize); ++#endif ++ ++#ifdef DISABLE_PERIODIC_EP ++ /* ++ * Set the NAK bit for this EP to ++ * start the disable process. ++ */ ++ diepctl.d32 = 0; ++ diepctl.b.snak = 1; ++ dwc_modify_reg32(&dev_if->in_ep_regs[num]->diepctl, diepctl.d32, diepctl.d32); ++ ep->disabling = 1; ++ ep->stopped = 1; ++#endif ++} ++ ++/** ++ * Handler for the IN EP NAK interrupt. ++ */ ++static inline int32_t handle_in_ep_nak_intr(dwc_otg_pcd_t *pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t* core_if; ++ diepmsk_data_t intr_mask = { .d32 = 0}; ++ ++ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "IN EP NAK"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.nak = 1; ++ ++ if(core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->diepeachintmsk[epnum], ++ intr_mask.d32, 0); ++ } else { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->diepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++ ++/** ++ * Handler for the OUT EP Babble interrupt. ++ */ ++static inline int32_t handle_out_ep_babble_intr(dwc_otg_pcd_t *pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t* core_if; ++ doepmsk_data_t intr_mask = { .d32 = 0}; ++ ++ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "OUT EP Babble"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.babble = 1; ++ ++ if(core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepeachintmsk[epnum], ++ intr_mask.d32, 0); ++ } else { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++ ++/** ++ * Handler for the OUT EP NAK interrupt. ++ */ ++static inline int32_t handle_out_ep_nak_intr(dwc_otg_pcd_t *pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t* core_if; ++ doepmsk_data_t intr_mask = { .d32 = 0}; ++ ++ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "OUT EP NAK"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.nak = 1; ++ ++ if(core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepeachintmsk[epnum], ++ intr_mask.d32, 0); ++ } else { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++ ++/** ++ * Handler for the OUT EP NYET interrupt. ++ */ ++static inline int32_t handle_out_ep_nyet_intr(dwc_otg_pcd_t *pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t* core_if; ++ doepmsk_data_t intr_mask = { .d32 = 0}; ++ ++ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", "OUT EP NYET"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.nyet = 1; ++ ++ if(core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepeachintmsk[epnum], ++ intr_mask.d32, 0); ++ } else { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that an IN EP has a pending Interrupt. ++ * The sequence for handling the IN EP interrupt is shown below: ++ * -# Read the Device All Endpoint Interrupt register ++ * -# Repeat the following for each IN EP interrupt bit set (from ++ * LSB to MSB). ++ * -# Read the Device Endpoint Interrupt (DIEPINTn) register ++ * -# If "Transfer Complete" call the request complete function ++ * -# If "Endpoint Disabled" complete the EP disable procedure. ++ * -# If "AHB Error Interrupt" log error ++ * -# If "Time-out Handshake" log error ++ * -# If "IN Token Received when TxFIFO Empty" write packet to Tx ++ * FIFO. ++ * -# If "IN Token EP Mismatch" (disable, this is handled by EP ++ * Mismatch Interrupt) ++ */ ++static int32_t dwc_otg_pcd_handle_in_ep_intr(dwc_otg_pcd_t *pcd) ++{ ++#define CLEAR_IN_EP_INTR(__core_if,__epnum,__intr) \ ++do { \ ++ diepint_data_t diepint = {.d32=0}; \ ++ diepint.b.__intr = 1; \ ++ dwc_write_reg32(&__core_if->dev_if->in_ep_regs[__epnum]->diepint, \ ++ diepint.d32); \ ++} while (0) ++ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ diepint_data_t diepint = {.d32=0}; ++ dctl_data_t dctl = {.d32=0}; ++ depctl_data_t depctl = {.d32=0}; ++ uint32_t ep_intr; ++ uint32_t epnum = 0; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ gintmsk_data_t intr_mask = {.d32 = 0}; ++ ++ ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, pcd); ++ ++ /* Read in the device interrupt bits */ ++ ep_intr = dwc_otg_read_dev_all_in_ep_intr(core_if); ++ ++ /* Service the Device IN interrupts for each endpoint */ ++ while(ep_intr) { ++ if (ep_intr&0x1) { ++ uint32_t empty_msk; ++ /* Get EP pointer */ ++ ep = get_in_ep(pcd, epnum); ++ dwc_ep = &ep->dwc_ep; ++ ++ depctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->diepctl); ++ empty_msk = dwc_read_reg32(&dev_if->dev_global_regs->dtknqr4_fifoemptymsk); ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "IN EP INTERRUPT - %d\nepmty_msk - %8x diepctl - %8x\n", ++ epnum, ++ empty_msk, ++ depctl.d32); ++ ++ DWC_DEBUGPL(DBG_PCD, ++ "EP%d-%s: type=%d, mps=%d\n", ++ dwc_ep->num, (dwc_ep->is_in ?"IN":"OUT"), ++ dwc_ep->type, dwc_ep->maxpacket); ++ ++ diepint.d32 = dwc_otg_read_dev_in_ep_intr(core_if, dwc_ep); ++ ++ DWC_DEBUGPL(DBG_PCDV, "EP %d Interrupt Register - 0x%x\n", epnum, diepint.d32); ++ /* Transfer complete */ ++ if (diepint.b.xfercompl) { ++ /* Disable the NP Tx FIFO Empty ++ * Interrrupt */ ++ if(core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, intr_mask.d32, 0); ++ } ++ else { ++ /* Disable the Tx FIFO Empty Interrupt for this EP */ ++ uint32_t fifoemptymsk = 0x1 << dwc_ep->num; ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk, ++ fifoemptymsk, 0); ++ } ++ /* Clear the bit in DIEPINTn for this interrupt */ ++ CLEAR_IN_EP_INTR(core_if,epnum,xfercompl); ++ ++ /* Complete the transfer */ ++ if (epnum == 0) { ++ handle_ep0(pcd); ++ } ++#ifdef DWC_EN_ISOC ++ else if(dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ if(!ep->stopped) ++ complete_iso_ep(ep); ++ } ++#endif //DWC_EN_ISOC ++ else { ++ ++ complete_ep(ep); ++ } ++ } ++ /* Endpoint disable */ ++ if (diepint.b.epdisabled) { ++ DWC_DEBUGPL(DBG_ANY,"EP%d IN disabled\n", epnum); ++ handle_in_ep_disable_intr(pcd, epnum); ++ ++ /* Clear the bit in DIEPINTn for this interrupt */ ++ CLEAR_IN_EP_INTR(core_if,epnum,epdisabled); ++ } ++ /* AHB Error */ ++ if (diepint.b.ahberr) { ++ DWC_DEBUGPL(DBG_ANY,"EP%d IN AHB Error\n", epnum); ++ /* Clear the bit in DIEPINTn for this interrupt */ ++ CLEAR_IN_EP_INTR(core_if,epnum,ahberr); ++ } ++ /* TimeOUT Handshake (non-ISOC IN EPs) */ ++ if (diepint.b.timeout) { ++ DWC_DEBUGPL(DBG_ANY,"EP%d IN Time-out\n", epnum); ++ handle_in_ep_timeout_intr(pcd, epnum); ++ ++ CLEAR_IN_EP_INTR(core_if,epnum,timeout); ++ } ++ /** IN Token received with TxF Empty */ ++ if (diepint.b.intktxfemp) { ++ DWC_DEBUGPL(DBG_ANY,"EP%d IN TKN TxFifo Empty\n", ++ epnum); ++ if (!ep->stopped && epnum != 0) { ++ ++ diepmsk_data_t diepmsk = { .d32 = 0}; ++ diepmsk.b.intktxfemp = 1; ++ ++ if(core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&dev_if->dev_global_regs->diepeachintmsk[epnum], ++ diepmsk.d32, 0); ++ } else { ++ dwc_modify_reg32(&dev_if->dev_global_regs->diepmsk, diepmsk.d32, 0); ++ } ++ start_next_request(ep); ++ } ++ else if(core_if->dma_desc_enable && epnum == 0 && ++ pcd->ep0state == EP0_OUT_STATUS_PHASE) { ++ // EP0 IN set STALL ++ depctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->diepctl); ++ ++ /* set the disable and stall bits */ ++ if (depctl.b.epena) { ++ depctl.b.epdis = 1; ++ } ++ depctl.b.stall = 1; ++ dwc_write_reg32(&dev_if->in_ep_regs[epnum]->diepctl, depctl.d32); ++ } ++ CLEAR_IN_EP_INTR(core_if,epnum,intktxfemp); ++ } ++ /** IN Token Received with EP mismatch */ ++ if (diepint.b.intknepmis) { ++ DWC_DEBUGPL(DBG_ANY,"EP%d IN TKN EP Mismatch\n", epnum); ++ CLEAR_IN_EP_INTR(core_if,epnum,intknepmis); ++ } ++ /** IN Endpoint NAK Effective */ ++ if (diepint.b.inepnakeff) { ++ DWC_DEBUGPL(DBG_ANY,"EP%d IN EP NAK Effective\n", epnum); ++ /* Periodic EP */ ++ if (ep->disabling) { ++ depctl.d32 = 0; ++ depctl.b.snak = 1; ++ depctl.b.epdis = 1; ++ dwc_modify_reg32(&dev_if->in_ep_regs[epnum]->diepctl, depctl.d32, depctl.d32); ++ } ++ CLEAR_IN_EP_INTR(core_if,epnum,inepnakeff); ++ ++ } ++ ++ /** IN EP Tx FIFO Empty Intr */ ++ if (diepint.b.emptyintr) { ++ DWC_DEBUGPL(DBG_ANY,"EP%d Tx FIFO Empty Intr \n", epnum); ++ write_empty_tx_fifo(pcd, epnum); ++ ++ CLEAR_IN_EP_INTR(core_if,epnum,emptyintr); ++ ++ } ++ ++ /** IN EP BNA Intr */ ++ if (diepint.b.bna) { ++ CLEAR_IN_EP_INTR(core_if,epnum,bna); ++ if(core_if->dma_desc_enable) { ++#ifdef DWC_EN_ISOC ++ if(dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * This checking is performed to prevent first "false" BNA ++ * handling occuring right after reconnect ++ */ ++ if(dwc_ep->next_frame != 0xffffffff) ++ dwc_otg_pcd_handle_iso_bna(ep); ++ } ++ else ++#endif //DWC_EN_ISOC ++ { ++ dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl); ++ ++ /* If Global Continue on BNA is disabled - disable EP */ ++ if(!dctl.b.gcontbna) { ++ depctl.d32 = 0; ++ depctl.b.snak = 1; ++ depctl.b.epdis = 1; ++ dwc_modify_reg32(&dev_if->in_ep_regs[epnum]->diepctl, depctl.d32, depctl.d32); ++ } else { ++ start_next_request(ep); ++ } ++ } ++ } ++ } ++ /* NAK Interrutp */ ++ if (diepint.b.nak) { ++ DWC_DEBUGPL(DBG_ANY,"EP%d IN NAK Interrupt\n", epnum); ++ handle_in_ep_nak_intr(pcd, epnum); ++ ++ CLEAR_IN_EP_INTR(core_if,epnum,nak); ++ } ++ } ++ epnum++; ++ ep_intr >>=1; ++ } ++ ++ return 1; ++#undef CLEAR_IN_EP_INTR ++} ++ ++/** ++ * This interrupt indicates that an OUT EP has a pending Interrupt. ++ * The sequence for handling the OUT EP interrupt is shown below: ++ * -# Read the Device All Endpoint Interrupt register ++ * -# Repeat the following for each OUT EP interrupt bit set (from ++ * LSB to MSB). ++ * -# Read the Device Endpoint Interrupt (DOEPINTn) register ++ * -# If "Transfer Complete" call the request complete function ++ * -# If "Endpoint Disabled" complete the EP disable procedure. ++ * -# If "AHB Error Interrupt" log error ++ * -# If "Setup Phase Done" process Setup Packet (See Standard USB ++ * Command Processing) ++ */ ++static int32_t dwc_otg_pcd_handle_out_ep_intr(dwc_otg_pcd_t *pcd) ++{ ++#define CLEAR_OUT_EP_INTR(__core_if,__epnum,__intr) \ ++do { \ ++ doepint_data_t doepint = {.d32=0}; \ ++ doepint.b.__intr = 1; \ ++ dwc_write_reg32(&__core_if->dev_if->out_ep_regs[__epnum]->doepint, \ ++ doepint.d32); \ ++} while (0) ++ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ uint32_t ep_intr; ++ doepint_data_t doepint = {.d32=0}; ++ dctl_data_t dctl = {.d32=0}; ++ depctl_data_t doepctl = {.d32=0}; ++ uint32_t epnum = 0; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__); ++ ++ /* Read in the device interrupt bits */ ++ ep_intr = dwc_otg_read_dev_all_out_ep_intr(core_if); ++ ++ while(ep_intr) { ++ if (ep_intr&0x1) { ++ /* Get EP pointer */ ++ ep = get_out_ep(pcd, epnum); ++ dwc_ep = &ep->dwc_ep; ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, ++ "EP%d-%s: type=%d, mps=%d\n", ++ dwc_ep->num, (dwc_ep->is_in ?"IN":"OUT"), ++ dwc_ep->type, dwc_ep->maxpacket); ++#endif ++ doepint.d32 = dwc_otg_read_dev_out_ep_intr(core_if, dwc_ep); ++ ++ /* Transfer complete */ ++ if (doepint.b.xfercompl) { ++ ++ if (epnum == 0) { ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if,epnum,xfercompl); ++ if(core_if->dma_desc_enable == 0 || pcd->ep0state != EP0_IDLE) ++ handle_ep0(pcd); ++#ifdef DWC_EN_ISOC ++ } else if(dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ if (doepint.b.pktdrpsts == 0) { ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if,epnum,xfercompl); ++ complete_iso_ep(ep); ++ } else { ++ ++ doepint_data_t doepint = {.d32=0}; ++ doepint.b.xfercompl = 1; ++ doepint.b.pktdrpsts = 1; ++ dwc_write_reg32(&core_if->dev_if->out_ep_regs[epnum]->doepint, ++ doepint.d32); ++ if(handle_iso_out_pkt_dropped(core_if,dwc_ep)) { ++ complete_iso_ep(ep); ++ } ++ } ++#endif //DWC_EN_ISOC ++ } else { ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if,epnum,xfercompl); ++ complete_ep(ep); ++ } ++ ++ } ++ ++ /* Endpoint disable */ ++ if (doepint.b.epdisabled) { ++ ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if,epnum,epdisabled); ++ } ++ /* AHB Error */ ++ if (doepint.b.ahberr) { ++ DWC_DEBUGPL(DBG_PCD,"EP%d OUT AHB Error\n", epnum); ++ DWC_DEBUGPL(DBG_PCD,"EP DMA REG %d \n", core_if->dev_if->out_ep_regs[epnum]->doepdma); ++ CLEAR_OUT_EP_INTR(core_if,epnum,ahberr); ++ } ++ /* Setup Phase Done (contorl EPs) */ ++ if (doepint.b.setup) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD,"EP%d SETUP Done\n", ++ epnum); ++#endif ++ CLEAR_OUT_EP_INTR(core_if,epnum,setup); ++ ++ handle_ep0(pcd); ++ } ++ ++ /** OUT EP BNA Intr */ ++ if (doepint.b.bna) { ++ CLEAR_OUT_EP_INTR(core_if,epnum,bna); ++ if(core_if->dma_desc_enable) { ++#ifdef DWC_EN_ISOC ++ if(dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * This checking is performed to prevent first "false" BNA ++ * handling occuring right after reconnect ++ */ ++ if(dwc_ep->next_frame != 0xffffffff) ++ dwc_otg_pcd_handle_iso_bna(ep); ++ } ++ else ++#endif //DWC_EN_ISOC ++ { ++ dctl.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dctl); ++ ++ /* If Global Continue on BNA is disabled - disable EP*/ ++ if(!dctl.b.gcontbna) { ++ doepctl.d32 = 0; ++ doepctl.b.snak = 1; ++ doepctl.b.epdis = 1; ++ dwc_modify_reg32(&dev_if->out_ep_regs[epnum]->doepctl, doepctl.d32, doepctl.d32); ++ } else { ++ start_next_request(ep); ++ } ++ } ++ } ++ } ++ if (doepint.b.stsphsercvd) { ++ CLEAR_OUT_EP_INTR(core_if,epnum,stsphsercvd); ++ if(core_if->dma_desc_enable) { ++ do_setup_in_status_phase(pcd); ++ } ++ } ++ /* Babble Interrutp */ ++ if (doepint.b.babble) { ++ DWC_DEBUGPL(DBG_ANY,"EP%d OUT Babble\n", epnum); ++ handle_out_ep_babble_intr(pcd, epnum); ++ ++ CLEAR_OUT_EP_INTR(core_if,epnum,babble); ++ } ++ /* NAK Interrutp */ ++ if (doepint.b.nak) { ++ DWC_DEBUGPL(DBG_ANY,"EP%d OUT NAK\n", epnum); ++ handle_out_ep_nak_intr(pcd, epnum); ++ ++ CLEAR_OUT_EP_INTR(core_if,epnum,nak); ++ } ++ /* NYET Interrutp */ ++ if (doepint.b.nyet) { ++ DWC_DEBUGPL(DBG_ANY,"EP%d OUT NYET\n", epnum); ++ handle_out_ep_nyet_intr(pcd, epnum); ++ ++ CLEAR_OUT_EP_INTR(core_if,epnum,nyet); ++ } ++ } ++ ++ epnum++; ++ ep_intr >>=1; ++ } ++ ++ return 1; ++ ++#undef CLEAR_OUT_EP_INTR ++} ++ ++ ++/** ++ * Incomplete ISO IN Transfer Interrupt. ++ * This interrupt indicates one of the following conditions occurred ++ * while transmitting an ISOC transaction. ++ * - Corrupted IN Token for ISOC EP. ++ * - Packet not complete in FIFO. ++ * The follow actions will be taken: ++ * -# Determine the EP ++ * -# Set incomplete flag in dwc_ep structure ++ * -# Disable EP; when "Endpoint Disabled" interrupt is received ++ * Flush FIFO ++ */ ++int32_t dwc_otg_pcd_handle_incomplete_isoc_in_intr(dwc_otg_pcd_t *pcd) ++{ ++ gintsts_data_t gintsts; ++ ++ ++#ifdef DWC_EN_ISOC ++ dwc_otg_dev_if_t *dev_if; ++ deptsiz_data_t deptsiz = { .d32 = 0}; ++ depctl_data_t depctl = { .d32 = 0}; ++ dsts_data_t dsts = { .d32 = 0}; ++ dwc_ep_t *dwc_ep; ++ int i; ++ ++ dev_if = GET_CORE_IF(pcd)->dev_if; ++ ++ for(i = 1; i <= dev_if->num_in_eps; ++i) { ++ dwc_ep = &pcd->in_ep[i].dwc_ep; ++ if(dwc_ep->active && ++ dwc_ep->type == USB_ENDPOINT_XFER_ISOC) ++ { ++ deptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->dieptsiz); ++ depctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl); ++ ++ if(depctl.b.epdis && deptsiz.d32) { ++ set_current_pkt_info(GET_CORE_IF(pcd), dwc_ep); ++ if(dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ ++ if(dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr0; ++ } ++ ++ } ++ ++ dsts.d32 = dwc_read_reg32(&GET_CORE_IF(pcd)->dev_if->dev_global_regs->dsts); ++ dwc_ep->next_frame = dsts.b.soffn; ++ ++ dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF(pcd), dwc_ep); ++ } ++ } ++ } ++ ++#else ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", ++ "IN ISOC Incomplete"); ++ ++ intr_mask.b.incomplisoin = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++#endif //DWC_EN_ISOC ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.incomplisoin = 1; ++ dwc_write_reg32 (&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * Incomplete ISO OUT Transfer Interrupt. ++ * ++ * This interrupt indicates that the core has dropped an ISO OUT ++ * packet. The following conditions can be the cause: ++ * - FIFO Full, the entire packet would not fit in the FIFO. ++ * - CRC Error ++ * - Corrupted Token ++ * The follow actions will be taken: ++ * -# Determine the EP ++ * -# Set incomplete flag in dwc_ep structure ++ * -# Read any data from the FIFO ++ * -# Disable EP. when "Endpoint Disabled" interrupt is received ++ * re-enable EP. ++ */ ++int32_t dwc_otg_pcd_handle_incomplete_isoc_out_intr(dwc_otg_pcd_t *pcd) ++{ ++ /* @todo implement ISR */ ++ gintsts_data_t gintsts; ++ ++#ifdef DWC_EN_ISOC ++ dwc_otg_dev_if_t *dev_if; ++ deptsiz_data_t deptsiz = { .d32 = 0}; ++ depctl_data_t depctl = { .d32 = 0}; ++ dsts_data_t dsts = { .d32 = 0}; ++ dwc_ep_t *dwc_ep; ++ int i; ++ ++ dev_if = GET_CORE_IF(pcd)->dev_if; ++ ++ for(i = 1; i <= dev_if->num_out_eps; ++i) { ++ dwc_ep = &pcd->in_ep[i].dwc_ep; ++ if(pcd->out_ep[i].dwc_ep.active && ++ pcd->out_ep[i].dwc_ep.type == USB_ENDPOINT_XFER_ISOC) ++ { ++ deptsiz.d32 = dwc_read_reg32(&dev_if->out_ep_regs[i]->doeptsiz); ++ depctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[i]->doepctl); ++ ++ if(depctl.b.epdis && deptsiz.d32) { ++ set_current_pkt_info(GET_CORE_IF(pcd), &pcd->out_ep[i].dwc_ep); ++ if(dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ ++ if(dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr0; ++ } ++ ++ } ++ ++ dsts.d32 = dwc_read_reg32(&GET_CORE_IF(pcd)->dev_if->dev_global_regs->dsts); ++ dwc_ep->next_frame = dsts.b.soffn; ++ ++ dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF(pcd), dwc_ep); ++ } ++ } ++ } ++#else ++ /** @todo implement ISR */ ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ ++ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", ++ "OUT ISOC Incomplete"); ++ ++ intr_mask.b.incomplisoout = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++#endif // DWC_EN_ISOC ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.incomplisoout = 1; ++ dwc_write_reg32 (&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This function handles the Global IN NAK Effective interrupt. ++ * ++ */ ++int32_t dwc_otg_pcd_handle_in_nak_effective(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if; ++ depctl_data_t diepctl = { .d32 = 0}; ++ depctl_data_t diepctl_rd = { .d32 = 0}; ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ gintsts_data_t gintsts; ++ int i; ++ ++ DWC_DEBUGPL(DBG_PCD, "Global IN NAK Effective\n"); ++ ++ /* Disable all active IN EPs */ ++ diepctl.b.epdis = 1; ++ diepctl.b.snak = 1; ++ ++ for (i=0; i <= dev_if->num_in_eps; i++) ++ { ++ diepctl_rd.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl); ++ if (diepctl_rd.b.epena) { ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->diepctl, ++ diepctl.d32); ++ } ++ } ++ /* Disable the Global IN NAK Effective Interrupt */ ++ intr_mask.b.ginnakeff = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.ginnakeff = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * OUT NAK Effective. ++ * ++ */ ++int32_t dwc_otg_pcd_handle_out_nak_effective(dwc_otg_pcd_t *pcd) ++{ ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ gintsts_data_t gintsts; ++ ++ DWC_PRINT("INTERRUPT Handler not implemented for %s\n", ++ "Global IN NAK Effective\n"); ++ /* Disable the Global IN NAK Effective Interrupt */ ++ intr_mask.b.goutnakeff = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.goutnakeff = 1; ++ dwc_write_reg32 (&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++ ++/** ++ * PCD interrupt handler. ++ * ++ * The PCD handles the device interrupts. Many conditions can cause a ++ * device interrupt. When an interrupt occurs, the device interrupt ++ * service routine determines the cause of the interrupt and ++ * dispatches handling to the appropriate function. These interrupt ++ * handling functions are described below. ++ * ++ * All interrupt registers are processed from LSB to MSB. ++ * ++ */ ++int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t *pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++#ifdef VERBOSE ++ dwc_otg_core_global_regs_t *global_regs = ++ core_if->core_global_regs; ++#endif ++ gintsts_data_t gintr_status; ++ int32_t retval = 0; ++ ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_ANY, "%s() gintsts=%08x gintmsk=%08x\n", ++ __func__, ++ dwc_read_reg32(&global_regs->gintsts), ++ dwc_read_reg32(&global_regs->gintmsk)); ++#endif ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ SPIN_LOCK(&pcd->lock); ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%08x gintmsk=%08x\n", ++ __func__, ++ dwc_read_reg32(&global_regs->gintsts), ++ dwc_read_reg32(&global_regs->gintmsk)); ++#endif ++ ++ gintr_status.d32 = dwc_otg_read_core_intr(core_if); ++ ++/* ++ if (!gintr_status.d32) { ++ SPIN_UNLOCK(&pcd->lock); ++ return 0; ++ } ++*/ ++ DWC_DEBUGPL(DBG_PCDV, "%s: gintsts&gintmsk=%08x\n", ++ __func__, gintr_status.d32); ++ ++ if (gintr_status.b.sofintr) { ++ retval |= dwc_otg_pcd_handle_sof_intr(pcd); ++ } ++ if (gintr_status.b.rxstsqlvl) { ++ retval |= dwc_otg_pcd_handle_rx_status_q_level_intr(pcd); ++ } ++ if (gintr_status.b.nptxfempty) { ++ retval |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd); ++ } ++ if (gintr_status.b.ginnakeff) { ++ retval |= dwc_otg_pcd_handle_in_nak_effective(pcd); ++ } ++ if (gintr_status.b.goutnakeff) { ++ retval |= dwc_otg_pcd_handle_out_nak_effective(pcd); ++ } ++ if (gintr_status.b.i2cintr) { ++ retval |= dwc_otg_pcd_handle_i2c_intr(pcd); ++ } ++ if (gintr_status.b.erlysuspend) { ++ retval |= dwc_otg_pcd_handle_early_suspend_intr(pcd); ++ } ++ if (gintr_status.b.usbreset) { ++ retval |= dwc_otg_pcd_handle_usb_reset_intr(pcd); ++ } ++ if (gintr_status.b.enumdone) { ++ retval |= dwc_otg_pcd_handle_enum_done_intr(pcd); ++ } ++ if (gintr_status.b.isooutdrop) { ++ retval |= dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(pcd); ++ } ++ if (gintr_status.b.eopframe) { ++ retval |= dwc_otg_pcd_handle_end_periodic_frame_intr(pcd); ++ } ++ if (gintr_status.b.epmismatch) { ++ retval |= dwc_otg_pcd_handle_ep_mismatch_intr(core_if); ++ } ++ if (gintr_status.b.inepint) { ++ if(!core_if->multiproc_int_enable) { ++ retval |= dwc_otg_pcd_handle_in_ep_intr(pcd); ++ } ++ } ++ if (gintr_status.b.outepintr) { ++ if(!core_if->multiproc_int_enable) { ++ retval |= dwc_otg_pcd_handle_out_ep_intr(pcd); ++ } ++ } ++ if (gintr_status.b.incomplisoin) { ++ retval |= dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd); ++ } ++ if (gintr_status.b.incomplisoout) { ++ retval |= dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd); ++ } ++ ++ /* In MPI mode De vice Endpoints intterrupts are asserted ++ * without setting outepintr and inepint bits set, so these ++ * Interrupt handlers are called without checking these bit-fields ++ */ ++ if(core_if->multiproc_int_enable) { ++ retval |= dwc_otg_pcd_handle_in_ep_intr(pcd); ++ retval |= dwc_otg_pcd_handle_out_ep_intr(pcd); ++ } ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%0x\n", __func__, ++ dwc_read_reg32(&global_regs->gintsts)); ++#endif ++ SPIN_UNLOCK(&pcd->lock); ++ } ++ ++ S3C2410X_CLEAR_EINTPEND(); ++ ++ return retval; ++} ++ ++#endif /* DWC_HOST_ONLY */ +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_regs.h +@@ -0,0 +1,2075 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_regs.h $ ++ * $Revision: 1.2 $ ++ * $Date: 2008-11-21 05:39:15 $ ++ * $Change: 1099526 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef __DWC_OTG_REGS_H__ ++#define __DWC_OTG_REGS_H__ ++ ++/** ++ * @file ++ * ++ * This file contains the data structures for accessing the DWC_otg core registers. ++ * ++ * The application interfaces with the HS OTG core by reading from and ++ * writing to the Control and Status Register (CSR) space through the ++ * AHB Slave interface. These registers are 32 bits wide, and the ++ * addresses are 32-bit-block aligned. ++ * CSRs are classified as follows: ++ * - Core Global Registers ++ * - Device Mode Registers ++ * - Device Global Registers ++ * - Device Endpoint Specific Registers ++ * - Host Mode Registers ++ * - Host Global Registers ++ * - Host Port CSRs ++ * - Host Channel Specific Registers ++ * ++ * Only the Core Global registers can be accessed in both Device and ++ * Host modes. When the HS OTG core is operating in one mode, either ++ * Device or Host, the application must not access registers from the ++ * other mode. When the core switches from one mode to another, the ++ * registers in the new mode of operation must be reprogrammed as they ++ * would be after a power-on reset. ++ */ ++ ++/** Maximum number of Periodic FIFOs */ ++#define MAX_PERIO_FIFOS 15 ++/** Maximum number of Transmit FIFOs */ ++#define MAX_TX_FIFOS 15 ++ ++/** Maximum number of Endpoints/HostChannels */ ++#define MAX_EPS_CHANNELS 16 ++ ++/****************************************************************************/ ++/** DWC_otg Core registers . ++ * The dwc_otg_core_global_regs structure defines the size ++ * and relative field offsets for the Core Global registers. ++ */ ++typedef struct dwc_otg_core_global_regs ++{ ++ /** OTG Control and Status Register. Offset: 000h */ ++ volatile uint32_t gotgctl; ++ /** OTG Interrupt Register. Offset: 004h */ ++ volatile uint32_t gotgint; ++ /**Core AHB Configuration Register. Offset: 008h */ ++ volatile uint32_t gahbcfg; ++ ++#define DWC_GLBINTRMASK 0x0001 ++#define DWC_DMAENABLE 0x0020 ++#define DWC_NPTXEMPTYLVL_EMPTY 0x0080 ++#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000 ++#define DWC_PTXEMPTYLVL_EMPTY 0x0100 ++#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000 ++ ++ /**Core USB Configuration Register. Offset: 00Ch */ ++ volatile uint32_t gusbcfg; ++ /**Core Reset Register. Offset: 010h */ ++ volatile uint32_t grstctl; ++ /**Core Interrupt Register. Offset: 014h */ ++ volatile uint32_t gintsts; ++ /**Core Interrupt Mask Register. Offset: 018h */ ++ volatile uint32_t gintmsk; ++ /**Receive Status Queue Read Register (Read Only). Offset: 01Ch */ ++ volatile uint32_t grxstsr; ++ /**Receive Status Queue Read & POP Register (Read Only). Offset: 020h*/ ++ volatile uint32_t grxstsp; ++ /**Receive FIFO Size Register. Offset: 024h */ ++ volatile uint32_t grxfsiz; ++ /**Non Periodic Transmit FIFO Size Register. Offset: 028h */ ++ volatile uint32_t gnptxfsiz; ++ /**Non Periodic Transmit FIFO/Queue Status Register (Read ++ * Only). Offset: 02Ch */ ++ volatile uint32_t gnptxsts; ++ /**I2C Access Register. Offset: 030h */ ++ volatile uint32_t gi2cctl; ++ /**PHY Vendor Control Register. Offset: 034h */ ++ volatile uint32_t gpvndctl; ++ /**General Purpose Input/Output Register. Offset: 038h */ ++ volatile uint32_t ggpio; ++ /**User ID Register. Offset: 03Ch */ ++ volatile uint32_t guid; ++ /**Synopsys ID Register (Read Only). Offset: 040h */ ++ volatile uint32_t gsnpsid; ++ /**User HW Config1 Register (Read Only). Offset: 044h */ ++ volatile uint32_t ghwcfg1; ++ /**User HW Config2 Register (Read Only). Offset: 048h */ ++ volatile uint32_t ghwcfg2; ++#define DWC_SLAVE_ONLY_ARCH 0 ++#define DWC_EXT_DMA_ARCH 1 ++#define DWC_INT_DMA_ARCH 2 ++ ++#define DWC_MODE_HNP_SRP_CAPABLE 0 ++#define DWC_MODE_SRP_ONLY_CAPABLE 1 ++#define DWC_MODE_NO_HNP_SRP_CAPABLE 2 ++#define DWC_MODE_SRP_CAPABLE_DEVICE 3 ++#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4 ++#define DWC_MODE_SRP_CAPABLE_HOST 5 ++#define DWC_MODE_NO_SRP_CAPABLE_HOST 6 ++ ++ /**User HW Config3 Register (Read Only). Offset: 04Ch */ ++ volatile uint32_t ghwcfg3; ++ /**User HW Config4 Register (Read Only). Offset: 050h*/ ++ volatile uint32_t ghwcfg4; ++ /** Reserved Offset: 054h-0FFh */ ++ volatile uint32_t reserved[43]; ++ /** Host Periodic Transmit FIFO Size Register. Offset: 100h */ ++ volatile uint32_t hptxfsiz; ++ /** Device Periodic Transmit FIFO#n Register if dedicated fifos are disabled, ++ otherwise Device Transmit FIFO#n Register. ++ * Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15). */ ++ volatile uint32_t dptxfsiz_dieptxf[15]; ++} dwc_otg_core_global_regs_t; ++ ++/** ++ * This union represents the bit fields of the Core OTG Control ++ * and Status Register (GOTGCTL). Set the bits using the bit ++ * fields then write the d32 value to the register. ++ */ ++typedef union gotgctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned sesreqscs : 1; ++ unsigned sesreq : 1; ++ unsigned reserved2_7 : 6; ++ unsigned hstnegscs : 1; ++ unsigned hnpreq : 1; ++ unsigned hstsethnpen : 1; ++ unsigned devhnpen : 1; ++ unsigned reserved12_15 : 4; ++ unsigned conidsts : 1; ++ unsigned reserved17 : 1; ++ unsigned asesvld : 1; ++ unsigned bsesvld : 1; ++ unsigned currmod : 1; ++ unsigned reserved21_31 : 11; ++ } b; ++} gotgctl_data_t; ++ ++/** ++ * This union represents the bit fields of the Core OTG Interrupt Register ++ * (GOTGINT). Set/clear the bits using the bit fields then write the d32 ++ * value to the register. ++ */ ++typedef union gotgint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** Current Mode */ ++ unsigned reserved0_1 : 2; ++ ++ /** Session End Detected */ ++ unsigned sesenddet : 1; ++ ++ unsigned reserved3_7 : 5; ++ ++ /** Session Request Success Status Change */ ++ unsigned sesreqsucstschng : 1; ++ /** Host Negotiation Success Status Change */ ++ unsigned hstnegsucstschng : 1; ++ ++ unsigned reserver10_16 : 7; ++ ++ /** Host Negotiation Detected */ ++ unsigned hstnegdet : 1; ++ /** A-Device Timeout Change */ ++ unsigned adevtoutchng : 1; ++ /** Debounce Done */ ++ unsigned debdone : 1; ++ ++ unsigned reserved31_20 : 12; ++ ++ } b; ++} gotgint_data_t; ++ ++ ++/** ++ * This union represents the bit fields of the Core AHB Configuration ++ * Register (GAHBCFG). Set/clear the bits using the bit fields then ++ * write the d32 value to the register. ++ */ ++typedef union gahbcfg_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned glblintrmsk : 1; ++#define DWC_GAHBCFG_GLBINT_ENABLE 1 ++ ++ unsigned hburstlen : 4; ++#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE 0 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR 1 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR4 3 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR8 5 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR16 7 ++ ++ unsigned dmaenable : 1; ++#define DWC_GAHBCFG_DMAENABLE 1 ++ unsigned reserved : 1; ++ unsigned nptxfemplvl_txfemplvl : 1; ++ unsigned ptxfemplvl : 1; ++#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY 1 ++#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0 ++ unsigned reserved9_31 : 23; ++ } b; ++} gahbcfg_data_t; ++ ++/** ++ * This union represents the bit fields of the Core USB Configuration ++ * Register (GUSBCFG). Set the bits using the bit fields then write ++ * the d32 value to the register. ++ */ ++typedef union gusbcfg_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned toutcal : 3; ++ unsigned phyif : 1; ++ unsigned ulpi_utmi_sel : 1; ++ unsigned fsintf : 1; ++ unsigned physel : 1; ++ unsigned ddrsel : 1; ++ unsigned srpcap : 1; ++ unsigned hnpcap : 1; ++ unsigned usbtrdtim : 4; ++ unsigned nptxfrwnden : 1; ++ unsigned phylpwrclksel : 1; ++ unsigned otgutmifssel : 1; ++ unsigned ulpi_fsls : 1; ++ unsigned ulpi_auto_res : 1; ++ unsigned ulpi_clk_sus_m : 1; ++ unsigned ulpi_ext_vbus_drv : 1; ++ unsigned ulpi_int_vbus_indicator : 1; ++ unsigned term_sel_dl_pulse : 1; ++ unsigned reserved23_27 : 5; ++ unsigned tx_end_delay : 1; ++ unsigned reserved29_31 : 3; ++ } b; ++} gusbcfg_data_t; ++ ++/** ++ * This union represents the bit fields of the Core Reset Register ++ * (GRSTCTL). Set/clear the bits using the bit fields then write the ++ * d32 value to the register. ++ */ ++typedef union grstctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** Core Soft Reset (CSftRst) (Device and Host) ++ * ++ * The application can flush the control logic in the ++ * entire core using this bit. This bit resets the ++ * pipelines in the AHB Clock domain as well as the ++ * PHY Clock domain. ++ * ++ * The state machines are reset to an IDLE state, the ++ * control bits in the CSRs are cleared, all the ++ * transmit FIFOs and the receive FIFO are flushed. ++ * ++ * The status mask bits that control the generation of ++ * the interrupt, are cleared, to clear the ++ * interrupt. The interrupt status bits are not ++ * cleared, so the application can get the status of ++ * any events that occurred in the core after it has ++ * set this bit. ++ * ++ * Any transactions on the AHB are terminated as soon ++ * as possible following the protocol. Any ++ * transactions on the USB are terminated immediately. ++ * ++ * The configuration settings in the CSRs are ++ * unchanged, so the software doesn't have to ++ * reprogram these registers (Device ++ * Configuration/Host Configuration/Core System ++ * Configuration/Core PHY Configuration). ++ * ++ * The application can write to this bit, any time it ++ * wants to reset the core. This is a self clearing ++ * bit and the core clears this bit after all the ++ * necessary logic is reset in the core, which may ++ * take several clocks, depending on the current state ++ * of the core. ++ */ ++ unsigned csftrst : 1; ++ /** Hclk Soft Reset ++ * ++ * The application uses this bit to reset the control logic in ++ * the AHB clock domain. Only AHB clock domain pipelines are ++ * reset. ++ */ ++ unsigned hsftrst : 1; ++ /** Host Frame Counter Reset (Host Only)
++ * ++ * The application can reset the (micro)frame number ++ * counter inside the core, using this bit. When the ++ * (micro)frame counter is reset, the subsequent SOF ++ * sent out by the core, will have a (micro)frame ++ * number of 0. ++ */ ++ unsigned hstfrm : 1; ++ /** In Token Sequence Learning Queue Flush ++ * (INTknQFlsh) (Device Only) ++ */ ++ unsigned intknqflsh : 1; ++ /** RxFIFO Flush (RxFFlsh) (Device and Host) ++ * ++ * The application can flush the entire Receive FIFO ++ * using this bit.

The application must first ++ * ensure that the core is not in the middle of a ++ * transaction.

The application should write into ++ * this bit, only after making sure that neither the ++ * DMA engine is reading from the RxFIFO nor the MAC ++ * is writing the data in to the FIFO.

The ++ * application should wait until the bit is cleared ++ * before performing any other operations. This bit ++ * will takes 8 clocks (slowest of PHY or AHB clock) ++ * to clear. ++ */ ++ unsigned rxfflsh : 1; ++ /** TxFIFO Flush (TxFFlsh) (Device and Host). ++ * ++ * This bit is used to selectively flush a single or ++ * all transmit FIFOs. The application must first ++ * ensure that the core is not in the middle of a ++ * transaction.

The application should write into ++ * this bit, only after making sure that neither the ++ * DMA engine is writing into the TxFIFO nor the MAC ++ * is reading the data out of the FIFO.

The ++ * application should wait until the core clears this ++ * bit, before performing any operations. This bit ++ * will takes 8 clocks (slowest of PHY or AHB clock) ++ * to clear. ++ */ ++ unsigned txfflsh : 1; ++ ++ /** TxFIFO Number (TxFNum) (Device and Host). ++ * ++ * This is the FIFO number which needs to be flushed, ++ * using the TxFIFO Flush bit. This field should not ++ * be changed until the TxFIFO Flush bit is cleared by ++ * the core. ++ * - 0x0 : Non Periodic TxFIFO Flush ++ * - 0x1 : Periodic TxFIFO #1 Flush in device mode ++ * or Periodic TxFIFO in host mode ++ * - 0x2 : Periodic TxFIFO #2 Flush in device mode. ++ * - ... ++ * - 0xF : Periodic TxFIFO #15 Flush in device mode ++ * - 0x10: Flush all the Transmit NonPeriodic and ++ * Transmit Periodic FIFOs in the core ++ */ ++ unsigned txfnum : 5; ++ /** Reserved */ ++ unsigned reserved11_29 : 19; ++ /** DMA Request Signal. Indicated DMA request is in ++ * probress. Used for debug purpose. */ ++ unsigned dmareq : 1; ++ /** AHB Master Idle. Indicates the AHB Master State ++ * Machine is in IDLE condition. */ ++ unsigned ahbidle : 1; ++ } b; ++} grstctl_t; ++ ++ ++/** ++ * This union represents the bit fields of the Core Interrupt Mask ++ * Register (GINTMSK). Set/clear the bits using the bit fields then ++ * write the d32 value to the register. ++ */ ++typedef union gintmsk_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned reserved0 : 1; ++ unsigned modemismatch : 1; ++ unsigned otgintr : 1; ++ unsigned sofintr : 1; ++ unsigned rxstsqlvl : 1; ++ unsigned nptxfempty : 1; ++ unsigned ginnakeff : 1; ++ unsigned goutnakeff : 1; ++ unsigned reserved8 : 1; ++ unsigned i2cintr : 1; ++ unsigned erlysuspend : 1; ++ unsigned usbsuspend : 1; ++ unsigned usbreset : 1; ++ unsigned enumdone : 1; ++ unsigned isooutdrop : 1; ++ unsigned eopframe : 1; ++ unsigned reserved16 : 1; ++ unsigned epmismatch : 1; ++ unsigned inepintr : 1; ++ unsigned outepintr : 1; ++ unsigned incomplisoin : 1; ++ unsigned incomplisoout : 1; ++ unsigned reserved22_23 : 2; ++ unsigned portintr : 1; ++ unsigned hcintr : 1; ++ unsigned ptxfempty : 1; ++ unsigned reserved27 : 1; ++ unsigned conidstschng : 1; ++ unsigned disconnect : 1; ++ unsigned sessreqintr : 1; ++ unsigned wkupintr : 1; ++ } b; ++} gintmsk_data_t; ++/** ++ * This union represents the bit fields of the Core Interrupt Register ++ * (GINTSTS). Set/clear the bits using the bit fields then write the ++ * d32 value to the register. ++ */ ++typedef union gintsts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++#define DWC_SOF_INTR_MASK 0x0008 ++ /** register bits */ ++ struct ++ { ++#define DWC_HOST_MODE 1 ++ unsigned curmode : 1; ++ unsigned modemismatch : 1; ++ unsigned otgintr : 1; ++ unsigned sofintr : 1; ++ unsigned rxstsqlvl : 1; ++ unsigned nptxfempty : 1; ++ unsigned ginnakeff : 1; ++ unsigned goutnakeff : 1; ++ unsigned reserved8 : 1; ++ unsigned i2cintr : 1; ++ unsigned erlysuspend : 1; ++ unsigned usbsuspend : 1; ++ unsigned usbreset : 1; ++ unsigned enumdone : 1; ++ unsigned isooutdrop : 1; ++ unsigned eopframe : 1; ++ unsigned intokenrx : 1; ++ unsigned epmismatch : 1; ++ unsigned inepint: 1; ++ unsigned outepintr : 1; ++ unsigned incomplisoin : 1; ++ unsigned incomplisoout : 1; ++ unsigned reserved22_23 : 2; ++ unsigned portintr : 1; ++ unsigned hcintr : 1; ++ unsigned ptxfempty : 1; ++ unsigned reserved27 : 1; ++ unsigned conidstschng : 1; ++ unsigned disconnect : 1; ++ unsigned sessreqintr : 1; ++ unsigned wkupintr : 1; ++ } b; ++} gintsts_data_t; ++ ++ ++/** ++ * This union represents the bit fields in the Device Receive Status Read and ++ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the d32 ++ * element then read out the bits using the bit elements. ++ */ ++typedef union device_grxsts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned epnum : 4; ++ unsigned bcnt : 11; ++ unsigned dpid : 2; ++ ++#define DWC_STS_DATA_UPDT 0x2 // OUT Data Packet ++#define DWC_STS_XFER_COMP 0x3 // OUT Data Transfer Complete ++ ++#define DWC_DSTS_GOUT_NAK 0x1 // Global OUT NAK ++#define DWC_DSTS_SETUP_COMP 0x4 // Setup Phase Complete ++#define DWC_DSTS_SETUP_UPDT 0x6 // SETUP Packet ++ unsigned pktsts : 4; ++ unsigned fn : 4; ++ unsigned reserved : 7; ++ } b; ++} device_grxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Receive Status Read and ++ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the d32 ++ * element then read out the bits using the bit elements. ++ */ ++typedef union host_grxsts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned chnum : 4; ++ unsigned bcnt : 11; ++ unsigned dpid : 2; ++ ++ unsigned pktsts : 4; ++#define DWC_GRXSTS_PKTSTS_IN 0x2 ++#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP 0x3 ++#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR 0x5 ++#define DWC_GRXSTS_PKTSTS_CH_HALTED 0x7 ++ ++ unsigned reserved : 11; ++ } b; ++} host_grxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the FIFO Size Registers (HPTXFSIZ, ++ * GNPTXFSIZ, DPTXFSIZn, DIEPTXFn). Read the register into the d32 element then ++ * read out the bits using the bit elements. ++ */ ++typedef union fifosize_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned startaddr : 16; ++ unsigned depth : 16; ++ } b; ++} fifosize_data_t; ++ ++/** ++ * This union represents the bit fields in the Non-Periodic Transmit ++ * FIFO/Queue Status Register (GNPTXSTS). Read the register into the ++ * d32 element then read out the bits using the bit ++ * elements. ++ */ ++typedef union gnptxsts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned nptxfspcavail : 16; ++ unsigned nptxqspcavail : 8; ++ /** Top of the Non-Periodic Transmit Request Queue ++ * - bit 24 - Terminate (Last entry for the selected ++ * channel/EP) ++ * - bits 26:25 - Token Type ++ * - 2'b00 - IN/OUT ++ * - 2'b01 - Zero Length OUT ++ * - 2'b10 - PING/Complete Split ++ * - 2'b11 - Channel Halt ++ * - bits 30:27 - Channel/EP Number ++ */ ++ unsigned nptxqtop_terminate : 1; ++ unsigned nptxqtop_token : 2; ++ unsigned nptxqtop_chnep : 4; ++ unsigned reserved : 1; ++ } b; ++} gnptxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Transmit ++ * FIFO Status Register (DTXFSTS). Read the register into the ++ * d32 element then read out the bits using the bit ++ * elements. ++ */ ++typedef union dtxfsts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned txfspcavail : 16; ++ unsigned reserved : 16; ++ } b; ++} dtxfsts_data_t; ++ ++/** ++ * This union represents the bit fields in the I2C Control Register ++ * (I2CCTL). Read the register into the d32 element then read out the ++ * bits using the bit elements. ++ */ ++typedef union gi2cctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned rwdata : 8; ++ unsigned regaddr : 8; ++ unsigned addr : 7; ++ unsigned i2cen : 1; ++ unsigned ack : 1; ++ unsigned i2csuspctl : 1; ++ unsigned i2cdevaddr : 2; ++ unsigned reserved : 2; ++ unsigned rw : 1; ++ unsigned bsydne : 1; ++ } b; ++} gi2cctl_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config1 ++ * Register. Read the register into the d32 element then read ++ * out the bits using the bit elements. ++ */ ++typedef union hwcfg1_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned ep_dir0 : 2; ++ unsigned ep_dir1 : 2; ++ unsigned ep_dir2 : 2; ++ unsigned ep_dir3 : 2; ++ unsigned ep_dir4 : 2; ++ unsigned ep_dir5 : 2; ++ unsigned ep_dir6 : 2; ++ unsigned ep_dir7 : 2; ++ unsigned ep_dir8 : 2; ++ unsigned ep_dir9 : 2; ++ unsigned ep_dir10 : 2; ++ unsigned ep_dir11 : 2; ++ unsigned ep_dir12 : 2; ++ unsigned ep_dir13 : 2; ++ unsigned ep_dir14 : 2; ++ unsigned ep_dir15 : 2; ++ } b; ++} hwcfg1_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config2 ++ * Register. Read the register into the d32 element then read ++ * out the bits using the bit elements. ++ */ ++typedef union hwcfg2_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /* GHWCFG2 */ ++ unsigned op_mode : 3; ++#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0 ++#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1 ++#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2 ++#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 ++#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 ++#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 ++#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 ++ ++ unsigned architecture : 2; ++ unsigned point2point : 1; ++ unsigned hs_phy_type : 2; ++#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 ++#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1 ++#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2 ++#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 ++ ++ unsigned fs_phy_type : 2; ++ unsigned num_dev_ep : 4; ++ unsigned num_host_chan : 4; ++ unsigned perio_ep_supported : 1; ++ unsigned dynamic_fifo : 1; ++ unsigned multi_proc_int : 1; ++ unsigned reserved21 : 1; ++ unsigned nonperio_tx_q_depth : 2; ++ unsigned host_perio_tx_q_depth : 2; ++ unsigned dev_token_q_depth : 5; ++ unsigned reserved31 : 1; ++ } b; ++} hwcfg2_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config3 ++ * Register. Read the register into the d32 element then read ++ * out the bits using the bit elements. ++ */ ++typedef union hwcfg3_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /* GHWCFG3 */ ++ unsigned xfer_size_cntr_width : 4; ++ unsigned packet_size_cntr_width : 3; ++ unsigned otg_func : 1; ++ unsigned i2c : 1; ++ unsigned vendor_ctrl_if : 1; ++ unsigned optional_features : 1; ++ unsigned synch_reset_type : 1; ++ unsigned ahb_phy_clock_synch : 1; ++ unsigned reserved15_13 : 3; ++ unsigned dfifo_depth : 16; ++ } b; ++} hwcfg3_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config4 ++ * Register. Read the register into the d32 element then read ++ * out the bits using the bit elements. ++ */ ++typedef union hwcfg4_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned num_dev_perio_in_ep : 4; ++ unsigned power_optimiz : 1; ++ unsigned min_ahb_freq : 9; ++ unsigned utmi_phy_data_width : 2; ++ unsigned num_dev_mode_ctrl_ep : 4; ++ unsigned iddig_filt_en : 1; ++ unsigned vbus_valid_filt_en : 1; ++ unsigned a_valid_filt_en : 1; ++ unsigned b_valid_filt_en : 1; ++ unsigned session_end_filt_en : 1; ++ unsigned ded_fifo_en : 1; ++ unsigned num_in_eps : 4; ++ unsigned desc_dma : 1; ++ unsigned desc_dma_dyn : 1; ++ } b; ++} hwcfg4_data_t; ++ ++//////////////////////////////////////////// ++// Device Registers ++/** ++ * Device Global Registers. Offsets 800h-BFFh ++ * ++ * The following structures define the size and relative field offsets ++ * for the Device Mode Registers. ++ * ++ * These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown. ++ */ ++typedef struct dwc_otg_dev_global_regs ++{ ++ /** Device Configuration Register. Offset 800h */ ++ volatile uint32_t dcfg; ++ /** Device Control Register. Offset: 804h */ ++ volatile uint32_t dctl; ++ /** Device Status Register (Read Only). Offset: 808h */ ++ volatile uint32_t dsts; ++ /** Reserved. Offset: 80Ch */ ++ uint32_t unused; ++ /** Device IN Endpoint Common Interrupt Mask ++ * Register. Offset: 810h */ ++ volatile uint32_t diepmsk; ++ /** Device OUT Endpoint Common Interrupt Mask ++ * Register. Offset: 814h */ ++ volatile uint32_t doepmsk; ++ /** Device All Endpoints Interrupt Register. Offset: 818h */ ++ volatile uint32_t daint; ++ /** Device All Endpoints Interrupt Mask Register. Offset: ++ * 81Ch */ ++ volatile uint32_t daintmsk; ++ /** Device IN Token Queue Read Register-1 (Read Only). ++ * Offset: 820h */ ++ volatile uint32_t dtknqr1; ++ /** Device IN Token Queue Read Register-2 (Read Only). ++ * Offset: 824h */ ++ volatile uint32_t dtknqr2; ++ /** Device VBUS discharge Register. Offset: 828h */ ++ volatile uint32_t dvbusdis; ++ /** Device VBUS Pulse Register. Offset: 82Ch */ ++ volatile uint32_t dvbuspulse; ++ /** Device IN Token Queue Read Register-3 (Read Only). / ++ * Device Thresholding control register (Read/Write) ++ * Offset: 830h */ ++ volatile uint32_t dtknqr3_dthrctl; ++ /** Device IN Token Queue Read Register-4 (Read Only). / ++ * Device IN EPs empty Inr. Mask Register (Read/Write) ++ * Offset: 834h */ ++ volatile uint32_t dtknqr4_fifoemptymsk; ++ /** Device Each Endpoint Interrupt Register (Read Only). / ++ * Offset: 838h */ ++ volatile uint32_t deachint; ++ /** Device Each Endpoint Interrupt mask Register (Read/Write). / ++ * Offset: 83Ch */ ++ volatile uint32_t deachintmsk; ++ /** Device Each In Endpoint Interrupt mask Register (Read/Write). / ++ * Offset: 840h */ ++ volatile uint32_t diepeachintmsk[MAX_EPS_CHANNELS]; ++ /** Device Each Out Endpoint Interrupt mask Register (Read/Write). / ++ * Offset: 880h */ ++ volatile uint32_t doepeachintmsk[MAX_EPS_CHANNELS]; ++} dwc_otg_device_global_regs_t; ++ ++/** ++ * This union represents the bit fields in the Device Configuration ++ * Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. Write the ++ * d32 member to the dcfg register. ++ */ ++typedef union dcfg_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** Device Speed */ ++ unsigned devspd : 2; ++ /** Non Zero Length Status OUT Handshake */ ++ unsigned nzstsouthshk : 1; ++#define DWC_DCFG_SEND_STALL 1 ++ ++ unsigned reserved3 : 1; ++ /** Device Addresses */ ++ unsigned devaddr : 7; ++ /** Periodic Frame Interval */ ++ unsigned perfrint : 2; ++#define DWC_DCFG_FRAME_INTERVAL_80 0 ++#define DWC_DCFG_FRAME_INTERVAL_85 1 ++#define DWC_DCFG_FRAME_INTERVAL_90 2 ++#define DWC_DCFG_FRAME_INTERVAL_95 3 ++ ++ unsigned reserved13_17 : 5; ++ /** In Endpoint Mis-match count */ ++ unsigned epmscnt : 5; ++ /** Enable Descriptor DMA in Device mode */ ++ unsigned descdma : 1; ++ } b; ++} dcfg_data_t; ++ ++/** ++ * This union represents the bit fields in the Device Control ++ * Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. ++ */ ++typedef union dctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** Remote Wakeup */ ++ unsigned rmtwkupsig : 1; ++ /** Soft Disconnect */ ++ unsigned sftdiscon : 1; ++ /** Global Non-Periodic IN NAK Status */ ++ unsigned gnpinnaksts : 1; ++ /** Global OUT NAK Status */ ++ unsigned goutnaksts : 1; ++ /** Test Control */ ++ unsigned tstctl : 3; ++ /** Set Global Non-Periodic IN NAK */ ++ unsigned sgnpinnak : 1; ++ /** Clear Global Non-Periodic IN NAK */ ++ unsigned cgnpinnak : 1; ++ /** Set Global OUT NAK */ ++ unsigned sgoutnak : 1; ++ /** Clear Global OUT NAK */ ++ unsigned cgoutnak : 1; ++ ++ /** Power-On Programming Done */ ++ unsigned pwronprgdone : 1; ++ /** Global Continue on BNA */ ++ unsigned gcontbna : 1; ++ /** Global Multi Count */ ++ unsigned gmc : 2; ++ /** Ignore Frame Number for ISOC EPs */ ++ unsigned ifrmnum : 1; ++ /** NAK on Babble */ ++ unsigned nakonbble : 1; ++ ++ unsigned reserved16_31 : 16; ++ } b; ++} dctl_data_t; ++ ++/** ++ * This union represents the bit fields in the Device Status ++ * Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. ++ */ ++typedef union dsts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** Suspend Status */ ++ unsigned suspsts : 1; ++ /** Enumerated Speed */ ++ unsigned enumspd : 2; ++#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0 ++#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1 ++#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2 ++#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3 ++ /** Erratic Error */ ++ unsigned errticerr : 1; ++ unsigned reserved4_7: 4; ++ /** Frame or Microframe Number of the received SOF */ ++ unsigned soffn : 14; ++ unsigned reserved22_31 : 10; ++ } b; ++} dsts_data_t; ++ ++ ++/** ++ * This union represents the bit fields in the Device IN EP Interrupt ++ * Register and the Device IN EP Common Mask Register. ++ * ++ * - Read the register into the d32 member then set/clear the ++ * bits using the bit elements. ++ */ ++typedef union diepint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** Transfer complete mask */ ++ unsigned xfercompl : 1; ++ /** Endpoint disable mask */ ++ unsigned epdisabled : 1; ++ /** AHB Error mask */ ++ unsigned ahberr : 1; ++ /** TimeOUT Handshake mask (non-ISOC EPs) */ ++ unsigned timeout : 1; ++ /** IN Token received with TxF Empty mask */ ++ unsigned intktxfemp : 1; ++ /** IN Token Received with EP mismatch mask */ ++ unsigned intknepmis : 1; ++ /** IN Endpoint HAK Effective mask */ ++ unsigned inepnakeff : 1; ++ /** IN Endpoint HAK Effective mask */ ++ unsigned emptyintr : 1; ++ ++ unsigned txfifoundrn : 1; ++ ++ /** BNA Interrupt mask */ ++ unsigned bna : 1; ++ ++ unsigned reserved10_12 : 3; ++ /** BNA Interrupt mask */ ++ unsigned nak : 1; ++ ++ unsigned reserved14_31 : 18; ++ } b; ++} diepint_data_t; ++ ++/** ++ * This union represents the bit fields in the Device IN EP ++ * Common/Dedicated Interrupt Mask Register. ++ */ ++typedef union diepint_data diepmsk_data_t; ++ ++/** ++ * This union represents the bit fields in the Device OUT EP Interrupt ++ * Registerand Device OUT EP Common Interrupt Mask Register. ++ * ++ * - Read the register into the d32 member then set/clear the ++ * bits using the bit elements. ++ */ ++typedef union doepint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** Transfer complete */ ++ unsigned xfercompl : 1; ++ /** Endpoint disable */ ++ unsigned epdisabled : 1; ++ /** AHB Error */ ++ unsigned ahberr : 1; ++ /** Setup Phase Done (contorl EPs) */ ++ unsigned setup : 1; ++ /** OUT Token Received when Endpoint Disabled */ ++ unsigned outtknepdis : 1; ++ ++ unsigned stsphsercvd : 1; ++ /** Back-to-Back SETUP Packets Received */ ++ unsigned back2backsetup : 1; ++ ++ unsigned reserved7 : 1; ++ /** OUT packet Error */ ++ unsigned outpkterr : 1; ++ /** BNA Interrupt */ ++ unsigned bna : 1; ++ ++ unsigned reserved10 : 1; ++ /** Packet Drop Status */ ++ unsigned pktdrpsts : 1; ++ /** Babble Interrupt */ ++ unsigned babble : 1; ++ /** NAK Interrupt */ ++ unsigned nak : 1; ++ /** NYET Interrupt */ ++ unsigned nyet : 1; ++ ++ unsigned reserved15_31 : 17; ++ } b; ++} doepint_data_t; ++ ++/** ++ * This union represents the bit fields in the Device OUT EP ++ * Common/Dedicated Interrupt Mask Register. ++ */ ++typedef union doepint_data doepmsk_data_t; ++ ++/** ++ * This union represents the bit fields in the Device All EP Interrupt ++ * and Mask Registers. ++ * - Read the register into the d32 member then set/clear the ++ * bits using the bit elements. ++ */ ++typedef union daint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** IN Endpoint bits */ ++ unsigned in : 16; ++ /** OUT Endpoint bits */ ++ unsigned out : 16; ++ } ep; ++ struct ++ { ++ /** IN Endpoint bits */ ++ unsigned inep0 : 1; ++ unsigned inep1 : 1; ++ unsigned inep2 : 1; ++ unsigned inep3 : 1; ++ unsigned inep4 : 1; ++ unsigned inep5 : 1; ++ unsigned inep6 : 1; ++ unsigned inep7 : 1; ++ unsigned inep8 : 1; ++ unsigned inep9 : 1; ++ unsigned inep10 : 1; ++ unsigned inep11 : 1; ++ unsigned inep12 : 1; ++ unsigned inep13 : 1; ++ unsigned inep14 : 1; ++ unsigned inep15 : 1; ++ /** OUT Endpoint bits */ ++ unsigned outep0 : 1; ++ unsigned outep1 : 1; ++ unsigned outep2 : 1; ++ unsigned outep3 : 1; ++ unsigned outep4 : 1; ++ unsigned outep5 : 1; ++ unsigned outep6 : 1; ++ unsigned outep7 : 1; ++ unsigned outep8 : 1; ++ unsigned outep9 : 1; ++ unsigned outep10 : 1; ++ unsigned outep11 : 1; ++ unsigned outep12 : 1; ++ unsigned outep13 : 1; ++ unsigned outep14 : 1; ++ unsigned outep15 : 1; ++ } b; ++} daint_data_t; ++ ++/** ++ * This union represents the bit fields in the Device IN Token Queue ++ * Read Registers. ++ * - Read the register into the d32 member. ++ * - READ-ONLY Register ++ */ ++typedef union dtknq1_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** In Token Queue Write Pointer */ ++ unsigned intknwptr : 5; ++ /** Reserved */ ++ unsigned reserved05_06 : 2; ++ /** write pointer has wrapped. */ ++ unsigned wrap_bit : 1; ++ /** EP Numbers of IN Tokens 0 ... 4 */ ++ unsigned epnums0_5 : 24; ++ }b; ++} dtknq1_data_t; ++ ++/** ++ * This union represents Threshold control Register ++ * - Read and write the register into the d32 member. ++ * - READ-WRITABLE Register ++ */ ++typedef union dthrctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** non ISO Tx Thr. Enable */ ++ unsigned non_iso_thr_en : 1; ++ /** ISO Tx Thr. Enable */ ++ unsigned iso_thr_en : 1; ++ /** Tx Thr. Length */ ++ unsigned tx_thr_len : 9; ++ /** Reserved */ ++ unsigned reserved11_15 : 5; ++ /** Rx Thr. Enable */ ++ unsigned rx_thr_en : 1; ++ /** Rx Thr. Length */ ++ unsigned rx_thr_len : 9; ++ /** Reserved */ ++ unsigned reserved26_31 : 6; ++ }b; ++} dthrctl_data_t; ++ ++ ++/** ++ * Device Logical IN Endpoint-Specific Registers. Offsets ++ * 900h-AFCh ++ * ++ * There will be one set of endpoint registers per logical endpoint ++ * implemented. ++ * ++ * These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown. ++ */ ++typedef struct dwc_otg_dev_in_ep_regs ++{ ++ /** Device IN Endpoint Control Register. Offset:900h + ++ * (ep_num * 20h) + 00h */ ++ volatile uint32_t diepctl; ++ /** Reserved. Offset:900h + (ep_num * 20h) + 04h */ ++ uint32_t reserved04; ++ /** Device IN Endpoint Interrupt Register. Offset:900h + ++ * (ep_num * 20h) + 08h */ ++ volatile uint32_t diepint; ++ /** Reserved. Offset:900h + (ep_num * 20h) + 0Ch */ ++ uint32_t reserved0C; ++ /** Device IN Endpoint Transfer Size ++ * Register. Offset:900h + (ep_num * 20h) + 10h */ ++ volatile uint32_t dieptsiz; ++ /** Device IN Endpoint DMA Address Register. Offset:900h + ++ * (ep_num * 20h) + 14h */ ++ volatile uint32_t diepdma; ++ /** Device IN Endpoint Transmit FIFO Status Register. Offset:900h + ++ * (ep_num * 20h) + 18h */ ++ volatile uint32_t dtxfsts; ++ /** Device IN Endpoint DMA Buffer Register. Offset:900h + ++ * (ep_num * 20h) + 1Ch */ ++ volatile uint32_t diepdmab; ++} dwc_otg_dev_in_ep_regs_t; ++ ++/** ++ * Device Logical OUT Endpoint-Specific Registers. Offsets: ++ * B00h-CFCh ++ * ++ * There will be one set of endpoint registers per logical endpoint ++ * implemented. ++ * ++ * These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown. ++ */ ++typedef struct dwc_otg_dev_out_ep_regs ++{ ++ /** Device OUT Endpoint Control Register. Offset:B00h + ++ * (ep_num * 20h) + 00h */ ++ volatile uint32_t doepctl; ++ /** Device OUT Endpoint Frame number Register. Offset: ++ * B00h + (ep_num * 20h) + 04h */ ++ volatile uint32_t doepfn; ++ /** Device OUT Endpoint Interrupt Register. Offset:B00h + ++ * (ep_num * 20h) + 08h */ ++ volatile uint32_t doepint; ++ /** Reserved. Offset:B00h + (ep_num * 20h) + 0Ch */ ++ uint32_t reserved0C; ++ /** Device OUT Endpoint Transfer Size Register. Offset: ++ * B00h + (ep_num * 20h) + 10h */ ++ volatile uint32_t doeptsiz; ++ /** Device OUT Endpoint DMA Address Register. Offset:B00h ++ * + (ep_num * 20h) + 14h */ ++ volatile uint32_t doepdma; ++ /** Reserved. Offset:B00h + * (ep_num * 20h) + 1Ch */ ++ uint32_t unused; ++ /** Device OUT Endpoint DMA Buffer Register. Offset:B00h ++ * + (ep_num * 20h) + 1Ch */ ++ uint32_t doepdmab; ++} dwc_otg_dev_out_ep_regs_t; ++ ++/** ++ * This union represents the bit fields in the Device EP Control ++ * Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. ++ */ ++typedef union depctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** Maximum Packet Size ++ * IN/OUT EPn ++ * IN/OUT EP0 - 2 bits ++ * 2'b00: 64 Bytes ++ * 2'b01: 32 ++ * 2'b10: 16 ++ * 2'b11: 8 */ ++ unsigned mps : 11; ++#define DWC_DEP0CTL_MPS_64 0 ++#define DWC_DEP0CTL_MPS_32 1 ++#define DWC_DEP0CTL_MPS_16 2 ++#define DWC_DEP0CTL_MPS_8 3 ++ ++ /** Next Endpoint ++ * IN EPn/IN EP0 ++ * OUT EPn/OUT EP0 - reserved */ ++ unsigned nextep : 4; ++ ++ /** USB Active Endpoint */ ++ unsigned usbactep : 1; ++ ++ /** Endpoint DPID (INTR/Bulk IN and OUT endpoints) ++ * This field contains the PID of the packet going to ++ * be received or transmitted on this endpoint. The ++ * application should program the PID of the first ++ * packet going to be received or transmitted on this ++ * endpoint , after the endpoint is ++ * activated. Application use the SetD1PID and ++ * SetD0PID fields of this register to program either ++ * D0 or D1 PID. ++ * ++ * The encoding for this field is ++ * - 0: D0 ++ * - 1: D1 ++ */ ++ unsigned dpid : 1; ++ ++ /** NAK Status */ ++ unsigned naksts : 1; ++ ++ /** Endpoint Type ++ * 2'b00: Control ++ * 2'b01: Isochronous ++ * 2'b10: Bulk ++ * 2'b11: Interrupt */ ++ unsigned eptype : 2; ++ ++ /** Snoop Mode ++ * OUT EPn/OUT EP0 ++ * IN EPn/IN EP0 - reserved */ ++ unsigned snp : 1; ++ ++ /** Stall Handshake */ ++ unsigned stall : 1; ++ ++ /** Tx Fifo Number ++ * IN EPn/IN EP0 ++ * OUT EPn/OUT EP0 - reserved */ ++ unsigned txfnum : 4; ++ ++ /** Clear NAK */ ++ unsigned cnak : 1; ++ /** Set NAK */ ++ unsigned snak : 1; ++ /** Set DATA0 PID (INTR/Bulk IN and OUT endpoints) ++ * Writing to this field sets the Endpoint DPID (DPID) ++ * field in this register to DATA0. Set Even ++ * (micro)frame (SetEvenFr) (ISO IN and OUT Endpoints) ++ * Writing to this field sets the Even/Odd ++ * (micro)frame (EO_FrNum) field to even (micro) ++ * frame. ++ */ ++ unsigned setd0pid : 1; ++ /** Set DATA1 PID (INTR/Bulk IN and OUT endpoints) ++ * Writing to this field sets the Endpoint DPID (DPID) ++ * field in this register to DATA1 Set Odd ++ * (micro)frame (SetOddFr) (ISO IN and OUT Endpoints) ++ * Writing to this field sets the Even/Odd ++ * (micro)frame (EO_FrNum) field to odd (micro) frame. ++ */ ++ unsigned setd1pid : 1; ++ ++ /** Endpoint Disable */ ++ unsigned epdis : 1; ++ /** Endpoint Enable */ ++ unsigned epena : 1; ++ } b; ++} depctl_data_t; ++ ++/** ++ * This union represents the bit fields in the Device EP Transfer ++ * Size Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. ++ */ ++typedef union deptsiz_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Transfer size */ ++ unsigned xfersize : 19; ++ /** Packet Count */ ++ unsigned pktcnt : 10; ++ /** Multi Count - Periodic IN endpoints */ ++ unsigned mc : 2; ++ unsigned reserved : 1; ++ } b; ++} deptsiz_data_t; ++ ++/** ++ * This union represents the bit fields in the Device EP 0 Transfer ++ * Size Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. ++ */ ++typedef union deptsiz0_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Transfer size */ ++ unsigned xfersize : 7; ++ /** Reserved */ ++ unsigned reserved7_18 : 12; ++ /** Packet Count */ ++ unsigned pktcnt : 1; ++ /** Reserved */ ++ unsigned reserved20_28 : 9; ++ /**Setup Packet Count (DOEPTSIZ0 Only) */ ++ unsigned supcnt : 2; ++ unsigned reserved31; ++ } b; ++} deptsiz0_data_t; ++ ++ ++///////////////////////////////////////////////// ++// DMA Descriptor Specific Structures ++// ++ ++/** Buffer status definitions */ ++ ++#define BS_HOST_READY 0x0 ++#define BS_DMA_BUSY 0x1 ++#define BS_DMA_DONE 0x2 ++#define BS_HOST_BUSY 0x3 ++ ++/** Receive/Transmit status definitions */ ++ ++#define RTS_SUCCESS 0x0 ++#define RTS_BUFFLUSH 0x1 ++#define RTS_RESERVED 0x2 ++#define RTS_BUFERR 0x3 ++ ++ ++/** ++ * This union represents the bit fields in the DMA Descriptor ++ * status quadlet. Read the quadlet into the d32 member then ++ * set/clear the bits using the bit, b_iso_out and ++ * b_iso_in elements. ++ */ ++typedef union desc_sts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** quadlet bits */ ++ struct { ++ /** Received number of bytes */ ++ unsigned bytes : 16; ++ ++ unsigned reserved16_22 : 7; ++ /** Multiple Transfer - only for OUT EPs */ ++ unsigned mtrf : 1; ++ /** Setup Packet received - only for OUT EPs */ ++ unsigned sr : 1; ++ /** Interrupt On Complete */ ++ unsigned ioc : 1; ++ /** Short Packet */ ++ unsigned sp : 1; ++ /** Last */ ++ unsigned l : 1; ++ /** Receive Status */ ++ unsigned sts : 2; ++ /** Buffer Status */ ++ unsigned bs : 2; ++ } b; ++ ++#ifdef DWC_EN_ISOC ++ /** iso out quadlet bits */ ++ struct { ++ /** Received number of bytes */ ++ unsigned rxbytes : 11; ++ ++ unsigned reserved11 : 1; ++ /** Frame Number */ ++ unsigned framenum : 11; ++ /** Received ISO Data PID */ ++ unsigned pid : 2; ++ /** Interrupt On Complete */ ++ unsigned ioc : 1; ++ /** Short Packet */ ++ unsigned sp : 1; ++ /** Last */ ++ unsigned l : 1; ++ /** Receive Status */ ++ unsigned rxsts : 2; ++ /** Buffer Status */ ++ unsigned bs : 2; ++ } b_iso_out; ++ ++ /** iso in quadlet bits */ ++ struct { ++ /** Transmited number of bytes */ ++ unsigned txbytes : 12; ++ /** Frame Number */ ++ unsigned framenum : 11; ++ /** Transmited ISO Data PID */ ++ unsigned pid : 2; ++ /** Interrupt On Complete */ ++ unsigned ioc : 1; ++ /** Short Packet */ ++ unsigned sp : 1; ++ /** Last */ ++ unsigned l : 1; ++ /** Transmit Status */ ++ unsigned txsts : 2; ++ /** Buffer Status */ ++ unsigned bs : 2; ++ } b_iso_in; ++#endif //DWC_EN_ISOC ++} desc_sts_data_t; ++ ++/** ++ * DMA Descriptor structure ++ * ++ * DMA Descriptor structure contains two quadlets: ++ * Status quadlet and Data buffer pointer. ++ */ ++typedef struct dwc_otg_dma_desc ++{ ++ /** DMA Descriptor status quadlet */ ++ desc_sts_data_t status; ++ /** DMA Descriptor data buffer pointer */ ++ dma_addr_t buf; ++} dwc_otg_dma_desc_t; ++ ++/** ++ * The dwc_otg_dev_if structure contains information needed to manage ++ * the DWC_otg controller acting in device mode. It represents the ++ * programming view of the device-specific aspects of the controller. ++ */ ++typedef struct dwc_otg_dev_if ++{ ++ /** Pointer to device Global registers. ++ * Device Global Registers starting at offset 800h ++ */ ++ dwc_otg_device_global_regs_t *dev_global_regs; ++#define DWC_DEV_GLOBAL_REG_OFFSET 0x800 ++ ++ /** ++ * Device Logical IN Endpoint-Specific Registers 900h-AFCh ++ */ ++ dwc_otg_dev_in_ep_regs_t *in_ep_regs[MAX_EPS_CHANNELS]; ++#define DWC_DEV_IN_EP_REG_OFFSET 0x900 ++#define DWC_EP_REG_OFFSET 0x20 ++ ++ /** Device Logical OUT Endpoint-Specific Registers B00h-CFCh */ ++ dwc_otg_dev_out_ep_regs_t *out_ep_regs[MAX_EPS_CHANNELS]; ++#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00 ++ ++ /* Device configuration information*/ ++ uint8_t speed; /**< Device Speed 0: Unknown, 1: LS, 2:FS, 3: HS */ ++ uint8_t num_in_eps; /**< Number # of Tx EP range: 0-15 exept ep0 */ ++ uint8_t num_out_eps; /**< Number # of Rx EP range: 0-15 exept ep 0*/ ++ ++ /** Size of periodic FIFOs (Bytes) */ ++ uint16_t perio_tx_fifo_size[MAX_PERIO_FIFOS]; ++ ++ /** Size of Tx FIFOs (Bytes) */ ++ uint16_t tx_fifo_size[MAX_TX_FIFOS]; ++ ++ /** Thresholding enable flags and length varaiables **/ ++ uint16_t rx_thr_en; ++ uint16_t iso_tx_thr_en; ++ uint16_t non_iso_tx_thr_en; ++ ++ uint16_t rx_thr_length; ++ uint16_t tx_thr_length; ++ ++ /** ++ * Pointers to the DMA Descriptors for EP0 Control ++ * transfers (virtual and physical) ++ */ ++ ++ /** 2 descriptors for SETUP packets */ ++ uint32_t dma_setup_desc_addr[2]; ++ dwc_otg_dma_desc_t* setup_desc_addr[2]; ++ ++ /** Pointer to Descriptor with latest SETUP packet */ ++ dwc_otg_dma_desc_t* psetup; ++ ++ /** Index of current SETUP handler descriptor */ ++ uint32_t setup_desc_index; ++ ++ /** Descriptor for Data In or Status In phases */ ++ uint32_t dma_in_desc_addr; ++ dwc_otg_dma_desc_t* in_desc_addr;; ++ ++ /** Descriptor for Data Out or Status Out phases */ ++ uint32_t dma_out_desc_addr; ++ dwc_otg_dma_desc_t* out_desc_addr; ++ ++} dwc_otg_dev_if_t; ++ ++ ++ ++ ++///////////////////////////////////////////////// ++// Host Mode Register Structures ++// ++/** ++ * The Host Global Registers structure defines the size and relative ++ * field offsets for the Host Mode Global Registers. Host Global ++ * Registers offsets 400h-7FFh. ++*/ ++typedef struct dwc_otg_host_global_regs ++{ ++ /** Host Configuration Register. Offset: 400h */ ++ volatile uint32_t hcfg; ++ /** Host Frame Interval Register. Offset: 404h */ ++ volatile uint32_t hfir; ++ /** Host Frame Number / Frame Remaining Register. Offset: 408h */ ++ volatile uint32_t hfnum; ++ /** Reserved. Offset: 40Ch */ ++ uint32_t reserved40C; ++ /** Host Periodic Transmit FIFO/ Queue Status Register. Offset: 410h */ ++ volatile uint32_t hptxsts; ++ /** Host All Channels Interrupt Register. Offset: 414h */ ++ volatile uint32_t haint; ++ /** Host All Channels Interrupt Mask Register. Offset: 418h */ ++ volatile uint32_t haintmsk; ++} dwc_otg_host_global_regs_t; ++ ++/** ++ * This union represents the bit fields in the Host Configuration Register. ++ * Read the register into the d32 member then set/clear the bits using ++ * the bit elements. Write the d32 member to the hcfg register. ++ */ ++typedef union hcfg_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ /** FS/LS Phy Clock Select */ ++ unsigned fslspclksel : 2; ++#define DWC_HCFG_30_60_MHZ 0 ++#define DWC_HCFG_48_MHZ 1 ++#define DWC_HCFG_6_MHZ 2 ++ ++ /** FS/LS Only Support */ ++ unsigned fslssupp : 1; ++ } b; ++} hcfg_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Frame Remaing/Number ++ * Register. ++ */ ++typedef union hfir_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ unsigned frint : 16; ++ unsigned reserved : 16; ++ } b; ++} hfir_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Frame Remaing/Number ++ * Register. ++ */ ++typedef union hfnum_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ unsigned frnum : 16; ++#define DWC_HFNUM_MAX_FRNUM 0x3FFF ++ unsigned frrem : 16; ++ } b; ++} hfnum_data_t; ++ ++typedef union hptxsts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ unsigned ptxfspcavail : 16; ++ unsigned ptxqspcavail : 8; ++ /** Top of the Periodic Transmit Request Queue ++ * - bit 24 - Terminate (last entry for the selected channel) ++ * - bits 26:25 - Token Type ++ * - 2'b00 - Zero length ++ * - 2'b01 - Ping ++ * - 2'b10 - Disable ++ * - bits 30:27 - Channel Number ++ * - bit 31 - Odd/even microframe ++ */ ++ unsigned ptxqtop_terminate : 1; ++ unsigned ptxqtop_token : 2; ++ unsigned ptxqtop_chnum : 4; ++ unsigned ptxqtop_odd : 1; ++ } b; ++} hptxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Port Control and Status ++ * Register. Read the register into the d32 member then set/clear the ++ * bits using the bit elements. Write the d32 member to the ++ * hprt0 register. ++ */ ++typedef union hprt0_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned prtconnsts : 1; ++ unsigned prtconndet : 1; ++ unsigned prtena : 1; ++ unsigned prtenchng : 1; ++ unsigned prtovrcurract : 1; ++ unsigned prtovrcurrchng : 1; ++ unsigned prtres : 1; ++ unsigned prtsusp : 1; ++ unsigned prtrst : 1; ++ unsigned reserved9 : 1; ++ unsigned prtlnsts : 2; ++ unsigned prtpwr : 1; ++ unsigned prttstctl : 4; ++ unsigned prtspd : 2; ++#define DWC_HPRT0_PRTSPD_HIGH_SPEED 0 ++#define DWC_HPRT0_PRTSPD_FULL_SPEED 1 ++#define DWC_HPRT0_PRTSPD_LOW_SPEED 2 ++ unsigned reserved19_31 : 13; ++ } b; ++} hprt0_data_t; ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union haint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned ch0 : 1; ++ unsigned ch1 : 1; ++ unsigned ch2 : 1; ++ unsigned ch3 : 1; ++ unsigned ch4 : 1; ++ unsigned ch5 : 1; ++ unsigned ch6 : 1; ++ unsigned ch7 : 1; ++ unsigned ch8 : 1; ++ unsigned ch9 : 1; ++ unsigned ch10 : 1; ++ unsigned ch11 : 1; ++ unsigned ch12 : 1; ++ unsigned ch13 : 1; ++ unsigned ch14 : 1; ++ unsigned ch15 : 1; ++ unsigned reserved : 16; ++ } b; ++ ++ struct ++ { ++ unsigned chint : 16; ++ unsigned reserved : 16; ++ } b2; ++} haint_data_t; ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union haintmsk_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned ch0 : 1; ++ unsigned ch1 : 1; ++ unsigned ch2 : 1; ++ unsigned ch3 : 1; ++ unsigned ch4 : 1; ++ unsigned ch5 : 1; ++ unsigned ch6 : 1; ++ unsigned ch7 : 1; ++ unsigned ch8 : 1; ++ unsigned ch9 : 1; ++ unsigned ch10 : 1; ++ unsigned ch11 : 1; ++ unsigned ch12 : 1; ++ unsigned ch13 : 1; ++ unsigned ch14 : 1; ++ unsigned ch15 : 1; ++ unsigned reserved : 16; ++ } b; ++ ++ struct ++ { ++ unsigned chint : 16; ++ unsigned reserved : 16; ++ } b2; ++} haintmsk_data_t; ++ ++/** ++ * Host Channel Specific Registers. 500h-5FCh ++ */ ++typedef struct dwc_otg_hc_regs ++{ ++ /** Host Channel 0 Characteristic Register. Offset: 500h + (chan_num * 20h) + 00h */ ++ volatile uint32_t hcchar; ++ /** Host Channel 0 Split Control Register. Offset: 500h + (chan_num * 20h) + 04h */ ++ volatile uint32_t hcsplt; ++ /** Host Channel 0 Interrupt Register. Offset: 500h + (chan_num * 20h) + 08h */ ++ volatile uint32_t hcint; ++ /** Host Channel 0 Interrupt Mask Register. Offset: 500h + (chan_num * 20h) + 0Ch */ ++ volatile uint32_t hcintmsk; ++ /** Host Channel 0 Transfer Size Register. Offset: 500h + (chan_num * 20h) + 10h */ ++ volatile uint32_t hctsiz; ++ /** Host Channel 0 DMA Address Register. Offset: 500h + (chan_num * 20h) + 14h */ ++ volatile uint32_t hcdma; ++ /** Reserved. Offset: 500h + (chan_num * 20h) + 18h - 500h + (chan_num * 20h) + 1Ch */ ++ uint32_t reserved[2]; ++} dwc_otg_hc_regs_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Characteristics ++ * Register. Read the register into the d32 member then set/clear the ++ * bits using the bit elements. Write the d32 member to the ++ * hcchar register. ++ */ ++typedef union hcchar_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ /** Maximum packet size in bytes */ ++ unsigned mps : 11; ++ ++ /** Endpoint number */ ++ unsigned epnum : 4; ++ ++ /** 0: OUT, 1: IN */ ++ unsigned epdir : 1; ++ ++ unsigned reserved : 1; ++ ++ /** 0: Full/high speed device, 1: Low speed device */ ++ unsigned lspddev : 1; ++ ++ /** 0: Control, 1: Isoc, 2: Bulk, 3: Intr */ ++ unsigned eptype : 2; ++ ++ /** Packets per frame for periodic transfers. 0 is reserved. */ ++ unsigned multicnt : 2; ++ ++ /** Device address */ ++ unsigned devaddr : 7; ++ ++ /** ++ * Frame to transmit periodic transaction. ++ * 0: even, 1: odd ++ */ ++ unsigned oddfrm : 1; ++ ++ /** Channel disable */ ++ unsigned chdis : 1; ++ ++ /** Channel enable */ ++ unsigned chen : 1; ++ } b; ++} hcchar_data_t; ++ ++typedef union hcsplt_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ /** Port Address */ ++ unsigned prtaddr : 7; ++ ++ /** Hub Address */ ++ unsigned hubaddr : 7; ++ ++ /** Transaction Position */ ++ unsigned xactpos : 2; ++#define DWC_HCSPLIT_XACTPOS_MID 0 ++#define DWC_HCSPLIT_XACTPOS_END 1 ++#define DWC_HCSPLIT_XACTPOS_BEGIN 2 ++#define DWC_HCSPLIT_XACTPOS_ALL 3 ++ ++ /** Do Complete Split */ ++ unsigned compsplt : 1; ++ ++ /** Reserved */ ++ unsigned reserved : 14; ++ ++ /** Split Enble */ ++ unsigned spltena : 1; ++ } b; ++} hcsplt_data_t; ++ ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union hcint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** Transfer Complete */ ++ unsigned xfercomp : 1; ++ /** Channel Halted */ ++ unsigned chhltd : 1; ++ /** AHB Error */ ++ unsigned ahberr : 1; ++ /** STALL Response Received */ ++ unsigned stall : 1; ++ /** NAK Response Received */ ++ unsigned nak : 1; ++ /** ACK Response Received */ ++ unsigned ack : 1; ++ /** NYET Response Received */ ++ unsigned nyet : 1; ++ /** Transaction Err */ ++ unsigned xacterr : 1; ++ /** Babble Error */ ++ unsigned bblerr : 1; ++ /** Frame Overrun */ ++ unsigned frmovrun : 1; ++ /** Data Toggle Error */ ++ unsigned datatglerr : 1; ++ /** Reserved */ ++ unsigned reserved : 21; ++ } b; ++} hcint_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Transfer Size ++ * Register. Read the register into the d32 member then set/clear the ++ * bits using the bit elements. Write the d32 member to the ++ * hcchar register. ++ */ ++typedef union hctsiz_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ /** Total transfer size in bytes */ ++ unsigned xfersize : 19; ++ ++ /** Data packets to transfer */ ++ unsigned pktcnt : 10; ++ ++ /** ++ * Packet ID for next data packet ++ * 0: DATA0 ++ * 1: DATA2 ++ * 2: DATA1 ++ * 3: MDATA (non-Control), SETUP (Control) ++ */ ++ unsigned pid : 2; ++#define DWC_HCTSIZ_DATA0 0 ++#define DWC_HCTSIZ_DATA1 2 ++#define DWC_HCTSIZ_DATA2 1 ++#define DWC_HCTSIZ_MDATA 3 ++#define DWC_HCTSIZ_SETUP 3 ++ ++ /** Do PING protocol when 1 */ ++ unsigned dopng : 1; ++ } b; ++} hctsiz_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Interrupt Mask ++ * Register. Read the register into the d32 member then set/clear the ++ * bits using the bit elements. Write the d32 member to the ++ * hcintmsk register. ++ */ ++typedef union hcintmsk_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ unsigned xfercompl : 1; ++ unsigned chhltd : 1; ++ unsigned ahberr : 1; ++ unsigned stall : 1; ++ unsigned nak : 1; ++ unsigned ack : 1; ++ unsigned nyet : 1; ++ unsigned xacterr : 1; ++ unsigned bblerr : 1; ++ unsigned frmovrun : 1; ++ unsigned datatglerr : 1; ++ unsigned reserved : 21; ++ } b; ++} hcintmsk_data_t; ++ ++/** OTG Host Interface Structure. ++ * ++ * The OTG Host Interface Structure structure contains information ++ * needed to manage the DWC_otg controller acting in host mode. It ++ * represents the programming view of the host-specific aspects of the ++ * controller. ++ */ ++typedef struct dwc_otg_host_if ++{ ++ /** Host Global Registers starting at offset 400h.*/ ++ dwc_otg_host_global_regs_t *host_global_regs; ++#define DWC_OTG_HOST_GLOBAL_REG_OFFSET 0x400 ++ ++ /** Host Port 0 Control and Status Register */ ++ volatile uint32_t *hprt0; ++#define DWC_OTG_HOST_PORT_REGS_OFFSET 0x440 ++ ++ ++ /** Host Channel Specific Registers at offsets 500h-5FCh. */ ++ dwc_otg_hc_regs_t *hc_regs[MAX_EPS_CHANNELS]; ++#define DWC_OTG_HOST_CHAN_REGS_OFFSET 0x500 ++#define DWC_OTG_CHAN_REGS_OFFSET 0x20 ++ ++ ++ /* Host configuration information */ ++ /** Number of Host Channels (range: 1-16) */ ++ uint8_t num_host_channels; ++ /** Periodic EPs supported (0: no, 1: yes) */ ++ uint8_t perio_eps_supported; ++ /** Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */ ++ uint16_t perio_tx_fifo_size; ++ ++} dwc_otg_host_if_t; ++ ++ ++/** ++ * This union represents the bit fields in the Power and Clock Gating Control ++ * Register. Read the register into the d32 member then set/clear the ++ * bits using the bit elements. ++ */ ++typedef union pcgcctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ /** Stop Pclk */ ++ unsigned stoppclk : 1; ++ /** Gate Hclk */ ++ unsigned gatehclk : 1; ++ /** Power Clamp */ ++ unsigned pwrclmp : 1; ++ /** Reset Power Down Modules */ ++ unsigned rstpdwnmodule : 1; ++ /** PHY Suspended */ ++ unsigned physuspended : 1; ++ ++ unsigned reserved : 27; ++ } b; ++} pcgcctl_data_t; ++ ++ ++#endif +--- /dev/null ++++ b/drivers/usb/dwc_otg/linux/dwc_otg_plat.h +@@ -0,0 +1,260 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/platform/dwc_otg_plat.h $ ++ * $Revision: 1.2 $ ++ * $Date: 2008-11-21 05:39:16 $ ++ * $Change: 1064915 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_OTG_PLAT_H__) ++#define __DWC_OTG_PLAT_H__ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * @file ++ * ++ * This file contains the Platform Specific constants, interfaces ++ * (functions and macros) for Linux. ++ * ++ */ ++//#if !defined(__LINUX_ARM_ARCH__) ++//#error "The contents of this file is Linux specific!!!" ++//#endif ++ ++/** ++ * Reads the content of a register. ++ * ++ * @param reg address of register to read. ++ * @return contents of the register. ++ * ++ ++ * Usage:
++ * uint32_t dev_ctl = dwc_read_reg32(&dev_regs->dctl); ++ */ ++static __inline__ uint32_t dwc_read_reg32( volatile uint32_t *reg) ++{ ++ return readl(reg); ++}; ++ ++/** ++ * Writes a register with a 32 bit value. ++ * ++ * @param reg address of register to read. ++ * @param value to write to _reg. ++ * ++ * Usage:
++ * dwc_write_reg32(&dev_regs->dctl, 0); ++ */ ++static __inline__ void dwc_write_reg32( volatile uint32_t *reg, const uint32_t value) ++{ ++ writel( value, reg ); ++}; ++ ++/** ++ * This function modifies bit values in a register. Using the ++ * algorithm: (reg_contents & ~clear_mask) | set_mask. ++ * ++ * @param reg address of register to read. ++ * @param clear_mask bit mask to be cleared. ++ * @param set_mask bit mask to be set. ++ * ++ * Usage:
++ * // Clear the SOF Interrupt Mask bit and
++ * // set the OTG Interrupt mask bit, leaving all others as they were. ++ * dwc_modify_reg32(&dev_regs->gintmsk, DWC_SOF_INT, DWC_OTG_INT);
++ */ ++static __inline__ ++ void dwc_modify_reg32( volatile uint32_t *reg, const uint32_t clear_mask, const uint32_t set_mask) ++{ ++ writel( (readl(reg) & ~clear_mask) | set_mask, reg ); ++}; ++ ++ ++/** ++ * Wrapper for the OS micro-second delay function. ++ * @param[in] usecs Microseconds of delay ++ */ ++static __inline__ void UDELAY( const uint32_t usecs ) ++{ ++ udelay( usecs ); ++} ++ ++/** ++ * Wrapper for the OS milli-second delay function. ++ * @param[in] msecs milliseconds of delay ++ */ ++static __inline__ void MDELAY( const uint32_t msecs ) ++{ ++ mdelay( msecs ); ++} ++ ++/** ++ * Wrapper for the Linux spin_lock. On the ARM (Integrator) ++ * spin_lock() is a nop. ++ * ++ * @param lock Pointer to the spinlock. ++ */ ++static __inline__ void SPIN_LOCK( spinlock_t *lock ) ++{ ++ spin_lock(lock); ++} ++ ++/** ++ * Wrapper for the Linux spin_unlock. On the ARM (Integrator) ++ * spin_lock() is a nop. ++ * ++ * @param lock Pointer to the spinlock. ++ */ ++static __inline__ void SPIN_UNLOCK( spinlock_t *lock ) ++{ ++ spin_unlock(lock); ++} ++ ++/** ++ * Wrapper (macro) for the Linux spin_lock_irqsave. On the ARM ++ * (Integrator) spin_lock() is a nop. ++ * ++ * @param l Pointer to the spinlock. ++ * @param f unsigned long for irq flags storage. ++ */ ++#define SPIN_LOCK_IRQSAVE( l, f ) spin_lock_irqsave(l,f); ++ ++/** ++ * Wrapper (macro) for the Linux spin_unlock_irqrestore. On the ARM ++ * (Integrator) spin_lock() is a nop. ++ * ++ * @param l Pointer to the spinlock. ++ * @param f unsigned long for irq flags storage. ++ */ ++#define SPIN_UNLOCK_IRQRESTORE( l,f ) spin_unlock_irqrestore(l,f); ++ ++/* ++ * Debugging support vanishes in non-debug builds. ++ */ ++ ++ ++/** ++ * The Debug Level bit-mask variable. ++ */ ++extern uint32_t g_dbg_lvl; ++/** ++ * Set the Debug Level variable. ++ */ ++static inline uint32_t SET_DEBUG_LEVEL( const uint32_t new ) ++{ ++ uint32_t old = g_dbg_lvl; ++ g_dbg_lvl = new; ++ return old; ++} ++ ++/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */ ++#define DBG_CIL (0x2) ++/** When debug level has the DBG_CILV bit set, display CIL Verbose debug ++ * messages */ ++#define DBG_CILV (0x20) ++/** When debug level has the DBG_PCD bit set, display PCD (Device) debug ++ * messages */ ++#define DBG_PCD (0x4) ++/** When debug level has the DBG_PCDV set, display PCD (Device) Verbose debug ++ * messages */ ++#define DBG_PCDV (0x40) ++/** When debug level has the DBG_HCD bit set, display Host debug messages */ ++#define DBG_HCD (0x8) ++/** When debug level has the DBG_HCDV bit set, display Verbose Host debug ++ * messages */ ++#define DBG_HCDV (0x80) ++/** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host ++ * mode. */ ++#define DBG_HCD_URB (0x800) ++ ++/** When debug level has any bit set, display debug messages */ ++#define DBG_ANY (0xFF) ++ ++/** All debug messages off */ ++#define DBG_OFF 0 ++ ++/** Prefix string for DWC_DEBUG print macros. */ ++#define USB_DWC "dwc_otg: " ++ ++/** ++ * Print a debug message when the Global debug level variable contains ++ * the bit defined in lvl. ++ * ++ * @param[in] lvl - Debug level, use one of the DBG_ constants above. ++ * @param[in] x - like printf ++ * ++ * Example:

++ * ++ * DWC_DEBUGPL( DBG_ANY, "%s(%p)\n", __func__, _reg_base_addr); ++ * ++ *
++ * results in:
++ * ++ * usb-DWC_otg: dwc_otg_cil_init(ca867000) ++ * ++ */ ++#ifdef DEBUG ++ ++# define DWC_DEBUGPL(lvl, x...) do{ if ((lvl)&g_dbg_lvl)printk( KERN_DEBUG USB_DWC x ); }while(0) ++# define DWC_DEBUGP(x...) DWC_DEBUGPL(DBG_ANY, x ) ++ ++# define CHK_DEBUG_LEVEL(level) ((level) & g_dbg_lvl) ++ ++#else ++ ++# define DWC_DEBUGPL(lvl, x...) do{}while(0) ++# define DWC_DEBUGP(x...) ++ ++# define CHK_DEBUG_LEVEL(level) (0) ++ ++#endif /*DEBUG*/ ++ ++/** ++ * Print an Error message. ++ */ ++#define DWC_ERROR(x...) printk( KERN_ERR USB_DWC x ) ++/** ++ * Print a Warning message. ++ */ ++#define DWC_WARN(x...) printk( KERN_WARNING USB_DWC x ) ++/** ++ * Print a notice (normal but significant message). ++ */ ++#define DWC_NOTICE(x...) printk( KERN_NOTICE USB_DWC x ) ++/** ++ * Basic message printing. ++ */ ++#define DWC_PRINT(x...) printk( KERN_INFO USB_DWC x ) ++ ++#endif ++ diff --git a/target/linux/ramips/patches-3.9/0205-owrt-MIPS-add-OWRTDTB-secion.patch b/target/linux/ramips/patches-3.9/0205-owrt-MIPS-add-OWRTDTB-secion.patch new file mode 100644 index 0000000000..b1e8058c7b --- /dev/null +++ b/target/linux/ramips/patches-3.9/0205-owrt-MIPS-add-OWRTDTB-secion.patch @@ -0,0 +1,52 @@ +From 6beb1af1b1475478c8f275b9579c9ebe4dad2904 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 19 Mar 2013 10:16:42 +0100 +Subject: [PATCH 205/208] owrt: MIPS: add OWRTDTB secion + +Signed-off-by: John Crispin +--- + arch/mips/kernel/head.S | 3 +++ + arch/mips/ralink/Makefile | 2 +- + arch/mips/ralink/of.c | 4 +++- + 3 files changed, 7 insertions(+), 2 deletions(-) + +--- a/arch/mips/kernel/head.S ++++ b/arch/mips/kernel/head.S +@@ -146,6 +146,9 @@ EXPORT(__image_cmdline) + .fill 0x400 + #endif /* CONFIG_IMAGE_CMDLINE_HACK */ + ++ .ascii "OWRTDTB:" ++ EXPORT(__image_dtb) ++ .fill 0x4000 + __REF + + NESTED(kernel_entry, 16, sp) # kernel entry point +--- a/arch/mips/ralink/Makefile ++++ b/arch/mips/ralink/Makefile +@@ -15,4 +15,4 @@ obj-$(CONFIG_SOC_MT7620) += mt7620.o + + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + +-obj-y += dts/ ++#obj-y += dts/ +--- a/arch/mips/ralink/of.c ++++ b/arch/mips/ralink/of.c +@@ -77,6 +77,8 @@ void __init device_tree_init(void) + free_bootmem(base, size); + } + ++extern struct boot_param_header __image_dtb; ++ + void __init plat_mem_setup(void) + { + set_io_port_base(KSEG1); +@@ -85,7 +87,7 @@ void __init plat_mem_setup(void) + * Load the builtin devicetree. This causes the chosen node to be + * parsed resulting in our memory appearing + */ +- __dt_setup_arch(&__dtb_start); ++ __dt_setup_arch(&__image_dtb); + + if (soc_info.mem_size) + add_memory_region(soc_info.mem_base, soc_info.mem_size, diff --git a/target/linux/ramips/patches-3.9/0206-owrt-MIPS-ralink-add-pseudo-pwm-led-trigger-based-on.patch b/target/linux/ramips/patches-3.9/0206-owrt-MIPS-ralink-add-pseudo-pwm-led-trigger-based-on.patch new file mode 100644 index 0000000000..8e62d13bf1 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0206-owrt-MIPS-ralink-add-pseudo-pwm-led-trigger-based-on.patch @@ -0,0 +1,301 @@ +From b35a0a294d39316c20f85004335c02f33a70ab68 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 24 Mar 2013 17:17:17 +0100 +Subject: [PATCH 206/208] owrt: MIPS: ralink: add pseudo pwm led trigger based + on timer0 + +Signed-off-by: John Crispin +--- + arch/mips/ralink/timer.c | 213 ++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 197 insertions(+), 16 deletions(-) + +--- a/arch/mips/ralink/timer.c ++++ b/arch/mips/ralink/timer.c +@@ -12,6 +12,8 @@ + #include + #include + #include ++#include ++#include + + #include + +@@ -23,16 +25,34 @@ + + #define TMR0CTL_ENABLE BIT(7) + #define TMR0CTL_MODE_PERIODIC BIT(4) +-#define TMR0CTL_PRESCALER 1 ++#define TMR0CTL_PRESCALER 2 + #define TMR0CTL_PRESCALE_VAL (0xf - TMR0CTL_PRESCALER) + #define TMR0CTL_PRESCALE_DIV (65536 / BIT(TMR0CTL_PRESCALER)) + ++struct rt_timer_gpio { ++ struct list_head list; ++ struct led_classdev *led; ++}; ++ + struct rt_timer { +- struct device *dev; +- void __iomem *membase; +- int irq; +- unsigned long timer_freq; +- unsigned long timer_div; ++ struct device *dev; ++ void __iomem *membase; ++ int irq; ++ ++ unsigned long timer_freq; ++ unsigned long timer_div; ++ ++ struct list_head gpios; ++ struct led_trigger led_trigger; ++ unsigned int duty_cycle; ++ unsigned int duty; ++ ++ unsigned int fade; ++ unsigned int fade_min; ++ unsigned int fade_max; ++ unsigned int fade_speed; ++ unsigned int fade_dir; ++ unsigned int fade_count; + }; + + static inline void rt_timer_w32(struct rt_timer *rt, u8 reg, u32 val) +@@ -48,18 +68,46 @@ static inline u32 rt_timer_r32(struct rt + static irqreturn_t rt_timer_irq(int irq, void *_rt) + { + struct rt_timer *rt = (struct rt_timer *) _rt; ++ struct rt_timer_gpio *gpio; ++ unsigned int val; + +- rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); ++ if (rt->fade && (rt->fade_count++ > rt->fade_speed)) { ++ rt->fade_count = 0; ++ if (rt->duty_cycle <= rt->fade_min) ++ rt->fade_dir = 1; ++ else if (rt->duty_cycle >= rt->fade_max) ++ rt->fade_dir = 0; ++ ++ if (rt->fade_dir) ++ rt->duty_cycle += 1; ++ else ++ rt->duty_cycle -= 1; ++ ++ } ++ ++ val = rt->timer_freq / rt->timer_div; ++ if (rt->duty) ++ val *= rt->duty_cycle; ++ else ++ val *= (100 - rt->duty_cycle); ++ val /= 100; ++ ++ if (!list_empty(&rt->gpios)) ++ list_for_each_entry(gpio, &rt->gpios, list) ++ led_set_brightness(gpio->led, !!rt->duty); ++ ++ rt->duty = !rt->duty; ++ ++ rt_timer_w32(rt, TIMER_REG_TMR0LOAD, val + 1); + rt_timer_w32(rt, TIMER_REG_TMRSTAT, TMRSTAT_TMR0INT); + + return IRQ_HANDLED; + } + +- + static int rt_timer_request(struct rt_timer *rt) + { +- int err = request_irq(rt->irq, rt_timer_irq, IRQF_DISABLED, +- dev_name(rt->dev), rt); ++ int err = devm_request_irq(rt->dev, rt->irq, rt_timer_irq, ++ IRQF_DISABLED, dev_name(rt->dev), rt); + if (err) { + dev_err(rt->dev, "failed to request irq\n"); + } else { +@@ -81,8 +129,6 @@ static int rt_timer_config(struct rt_tim + else + rt->timer_div = divisor; + +- rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div); +- + return 0; + } + +@@ -108,11 +154,128 @@ static void rt_timer_disable(struct rt_t + rt_timer_w32(rt, TIMER_REG_TMR0CTL, t); + } + ++static ssize_t led_fade_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ ++ return sprintf(buf, "speed: %d, min: %d, max: %d\n", rt->fade_speed, rt->fade_min, rt->fade_max); ++} ++ ++static ssize_t led_fade_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ unsigned int speed = 0, min = 0, max = 0; ++ ssize_t ret = -EINVAL; ++ ++ ret = sscanf(buf, "%u %u %u", &speed, &min, &max); ++ ++ if (ret == 3) { ++ rt->fade_speed = speed; ++ rt->fade_min = min; ++ rt->fade_max = max; ++ rt->fade = 1; ++ } else { ++ rt->fade = 0; ++ } ++ ++ return size; ++} ++ ++static DEVICE_ATTR(fade, 0644, led_fade_show, led_fade_store); ++ ++static ssize_t led_duty_cycle_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ ++ return sprintf(buf, "%u\n", rt->duty_cycle); ++} ++ ++static ssize_t led_duty_cycle_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct led_classdev *led_cdev = dev_get_drvdata(dev); ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ unsigned long state; ++ ssize_t ret = -EINVAL; ++ ++ ret = kstrtoul(buf, 10, &state); ++ if (ret) ++ return ret; ++ ++ if (state <= 100) ++ rt->duty_cycle = state; ++ else ++ rt->duty_cycle = 100; ++ ++ rt->fade = 0; ++ ++ return size; ++} ++ ++static DEVICE_ATTR(duty_cycle, 0644, led_duty_cycle_show, led_duty_cycle_store); ++ ++static void rt_timer_trig_activate(struct led_classdev *led_cdev) ++{ ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ struct rt_timer_gpio *gpio_data; ++ int rc; ++ ++ led_cdev->trigger_data = NULL; ++ gpio_data = kzalloc(sizeof(*gpio_data), GFP_KERNEL); ++ if (!gpio_data) ++ return; ++ ++ rc = device_create_file(led_cdev->dev, &dev_attr_duty_cycle); ++ if (rc) ++ goto err_gpio; ++ rc = device_create_file(led_cdev->dev, &dev_attr_fade); ++ if (rc) ++ goto err_out_duty_cycle; ++ ++ led_cdev->activated = true; ++ led_cdev->trigger_data = gpio_data; ++ gpio_data->led = led_cdev; ++ list_add(&gpio_data->list, &rt->gpios); ++ led_cdev->trigger_data = gpio_data; ++ rt_timer_enable(rt); ++ return; ++ ++err_out_duty_cycle: ++ device_remove_file(led_cdev->dev, &dev_attr_duty_cycle); ++ ++err_gpio: ++ kfree(gpio_data); ++} ++ ++static void rt_timer_trig_deactivate(struct led_classdev *led_cdev) ++{ ++ struct rt_timer *rt = container_of(led_cdev->trigger, struct rt_timer, led_trigger); ++ struct rt_timer_gpio *gpio_data = (struct rt_timer_gpio*) led_cdev->trigger_data; ++ ++ if (led_cdev->activated) { ++ device_remove_file(led_cdev->dev, &dev_attr_duty_cycle); ++ device_remove_file(led_cdev->dev, &dev_attr_fade); ++ led_cdev->activated = false; ++ } ++ ++ list_del(&gpio_data->list); ++ rt_timer_disable(rt); ++ led_set_brightness(led_cdev, LED_OFF); ++} ++ + static int rt_timer_probe(struct platform_device *pdev) + { + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ const __be32 *divisor; + struct rt_timer *rt; + struct clk *clk; ++ int ret; + + if (!res) { + dev_err(&pdev->dev, "no memory resource found\n"); +@@ -147,12 +310,29 @@ static int rt_timer_probe(struct platfor + if (!rt->timer_freq) + return -EINVAL; + ++ rt->duty_cycle = 100; + rt->dev = &pdev->dev; + platform_set_drvdata(pdev, rt); + +- rt_timer_request(rt); +- rt_timer_config(rt, 2); +- rt_timer_enable(rt); ++ ret = rt_timer_request(rt); ++ if (ret) ++ return ret; ++ ++ divisor = of_get_property(pdev->dev.of_node, "ralink,divisor", NULL); ++ if (divisor) ++ rt_timer_config(rt, be32_to_cpu(*divisor)); ++ else ++ rt_timer_config(rt, 200); ++ ++ rt->led_trigger.name = "pwmtimer", ++ rt->led_trigger.activate = rt_timer_trig_activate, ++ rt->led_trigger.deactivate = rt_timer_trig_deactivate, ++ ++ ret = led_trigger_register(&rt->led_trigger); ++ if (ret) ++ return ret; ++ ++ INIT_LIST_HEAD(&rt->gpios); + + dev_info(&pdev->dev, "maximum frequncy is %luHz\n", rt->timer_freq); + +@@ -163,6 +343,7 @@ static int rt_timer_remove(struct platfo + { + struct rt_timer *rt = platform_get_drvdata(pdev); + ++ led_trigger_unregister(&rt->led_trigger); + rt_timer_disable(rt); + rt_timer_free(rt); + +@@ -187,6 +368,6 @@ static struct platform_driver rt_timer_d + + module_platform_driver(rt_timer_driver); + +-MODULE_DESCRIPTION("Ralink RT2880 timer"); ++MODULE_DESCRIPTION("Ralink RT2880 timer / pseudo pwm"); + MODULE_AUTHOR("John Crispin +Date: Fri, 3 Aug 2012 10:27:13 +0200 +Subject: [PATCH 19/25] owrt mtd split + +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 1 + + arch/mips/lantiq/setup.c | 7 + + drivers/mtd/Kconfig | 4 + + drivers/mtd/mtdpart.c | 173 +++++++++++++++++++- + 4 files changed, 184 insertions(+), 1 deletions(-) + +--- a/drivers/mtd/Kconfig ++++ b/drivers/mtd/Kconfig +@@ -31,6 +31,10 @@ config MTD_ROOTFS_SPLIT + bool "Automatically split 'rootfs' partition for squashfs" + default y + ++config MTD_UIMAGE_SPLIT ++ bool "Automatically split 'linux' partition into 'kernel' and 'rootfs'" ++ default y ++ + config MTD_REDBOOT_PARTS + tristate "RedBoot partition table parsing" + ---help--- +--- a/drivers/mtd/mtdpart.c ++++ b/drivers/mtd/mtdpart.c +@@ -833,6 +833,99 @@ static int refresh_rootfs_split(struct m + } + #endif /* CONFIG_MTD_ROOTFS_SPLIT */ + ++#ifdef CONFIG_MTD_UIMAGE_SPLIT ++static unsigned long find_uimage_size(struct mtd_info *mtd, ++ unsigned long offset) ++{ ++#define UBOOT_MAGIC 0x56190527 ++ unsigned long magic = 0; ++ unsigned long temp; ++ size_t len; ++ int ret; ++ ++ ret = mtd_read(mtd, offset, 4, &len, (void *)&magic); ++ if (ret || len != sizeof(magic)) ++ return 0; ++ ++ if (le32_to_cpu(magic) != UBOOT_MAGIC) ++ return 0; ++ ++ ret = mtd_read(mtd, offset + 12, 4, &len, (void *)&temp); ++ if (ret || len != sizeof(temp)) ++ return 0; ++ ++ return be32_to_cpu(temp) + 0x40; ++} ++ ++static int detect_squashfs_partition(struct mtd_info *mtd, unsigned long offset) ++{ ++ unsigned long temp; ++ size_t len; ++ int ret; ++ ++ ret = mtd_read(mtd, offset, 4, &len, (void *)&temp); ++ if (ret || len != sizeof(temp)) ++ return 0; ++ ++ return le32_to_cpu(temp) == SQUASHFS_MAGIC; ++} ++ ++static unsigned long find_squashfs_offset(struct mtd_info *mtd, unsigned long _offset) ++{ ++ /* scan the first 2MB at 64K offsets */ ++ int i; ++ ++ for (i = 0; i < 32; i++) { ++ unsigned long offset = i * 64 * 1024; ++ if (detect_squashfs_partition(mtd, _offset + offset)) ++ return offset; ++ } ++ return 0; ++} ++ ++static int split_uimage(struct mtd_info *mtd, ++ const struct mtd_partition *part) ++{ ++ static struct mtd_partition split_partitions[] = { ++ { ++ .name = "kernel", ++ .offset = 0x0, ++ .size = 0x0, ++ }, { ++ .name = "rootfs", ++ .offset = 0x0, ++ .size = 0x0, ++ }, ++ }; ++ ++ split_partitions[0].size = find_uimage_size(mtd, part->offset); ++ if (!split_partitions[0].size) { ++ split_partitions[0].size = find_squashfs_offset(mtd, part->offset); ++ if (!split_partitions[0].size) { ++ pr_err("failed to split firmware partition\n"); ++ return -1; ++ } ++ } ++ ++ if (!detect_squashfs_partition(mtd, ++ part->offset ++ + split_partitions[0].size)) { ++ split_partitions[0].size &= ~(mtd->erasesize - 1); ++ split_partitions[0].size += mtd->erasesize; ++ } else { ++ pr_info("found squashfs behind kernel\n"); ++ } ++ ++ split_partitions[0].offset = part->offset; ++ split_partitions[1].offset = part->offset + split_partitions[0].size; ++ split_partitions[1].size = part->size - split_partitions[0].size; ++ ++ add_mtd_partitions(mtd, split_partitions, 2); ++ ++ return 0; ++} ++#endif ++ + /* + * This function, given a master MTD object and a partition table, creates + * and registers slave MTD objects which are bound to the master according to +@@ -849,7 +942,7 @@ int add_mtd_partitions(struct mtd_info * + struct mtd_part *slave; + uint64_t cur_offset = 0; + int i; +-#ifdef CONFIG_MTD_ROOTFS_SPLIT ++#if defined(CONFIG_MTD_ROOTFS_SPLIT) || defined(CONFIG_MTD_UIMAGE_SPLIT) + int ret; + #endif + +@@ -866,6 +959,14 @@ int add_mtd_partitions(struct mtd_info * + + add_mtd_device(&slave->mtd); + ++#ifdef CONFIG_MTD_UIMAGE_SPLIT ++ if (!strcmp(parts[i].name, "firmware")) { ++ ret = split_uimage(master, &parts[i]); ++ if (ret) ++ printk(KERN_WARNING "Can't split firmware partition\n"); ++ } ++#endif ++ + if (!strcmp(parts[i].name, "rootfs")) { + #ifdef CONFIG_MTD_ROOTFS_ROOT_DEV + if (ROOT_DEV == 0) { diff --git a/target/linux/ramips/patches-3.9/0210-mtd_fix_cfi_cmdset_0002_erase_status_check.patch b/target/linux/ramips/patches-3.9/0210-mtd_fix_cfi_cmdset_0002_erase_status_check.patch new file mode 100644 index 0000000000..91b887f922 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0210-mtd_fix_cfi_cmdset_0002_erase_status_check.patch @@ -0,0 +1,20 @@ +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -1939,7 +1939,7 @@ static int __xipram do_erase_chip(struct + chip->erase_suspended = 0; + } + +- if (chip_ready(map, adr)) ++ if (chip_good(map, adr, map_word_ff(map))) + break; + + if (time_after(jiffies, timeo)) { +@@ -2028,7 +2028,7 @@ static int __xipram do_erase_oneblock(st + chip->erase_suspended = 0; + } + +- if (chip_ready(map, adr)) { ++ if (chip_good(map, adr, map_word_ff(map))) { + xip_enable(map, chip, adr); + break; + } diff --git a/target/linux/ramips/patches-3.9/0211-mtd-cfi_cmdset_0002-force-word-write.patch b/target/linux/ramips/patches-3.9/0211-mtd-cfi_cmdset_0002-force-word-write.patch new file mode 100644 index 0000000000..25ae351e65 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0211-mtd-cfi_cmdset_0002-force-word-write.patch @@ -0,0 +1,61 @@ +--- a/drivers/mtd/chips/cfi_cmdset_0002.c ++++ b/drivers/mtd/chips/cfi_cmdset_0002.c +@@ -39,7 +39,7 @@ + #include + + #define AMD_BOOTLOC_BUG +-#define FORCE_WORD_WRITE 0 ++#define FORCE_WORD_WRITE 1 + + #define MAX_WORD_RETRIES 3 + +@@ -50,7 +50,9 @@ + + static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); ++#if !FORCE_WORD_WRITE + static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); ++#endif + static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *); + static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); + static void cfi_amdstd_sync (struct mtd_info *); +@@ -186,6 +188,7 @@ static void fixup_amd_bootblock(struct m + } + #endif + ++#if !FORCE_WORD_WRITE + static void fixup_use_write_buffers(struct mtd_info *mtd) + { + struct map_info *map = mtd->priv; +@@ -195,6 +198,7 @@ static void fixup_use_write_buffers(stru + mtd->_write = cfi_amdstd_write_buffers; + } + } ++#endif /* !FORCE_WORD_WRITE */ + + /* Atmel chips don't use the same PRI format as AMD chips */ + static void fixup_convert_atmel_pri(struct mtd_info *mtd) +@@ -1443,6 +1447,7 @@ static int cfi_amdstd_write_words(struct + /* + * FIXME: interleaved mode not tested, and probably not supported! + */ ++#if !FORCE_WORD_WRITE + static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, + unsigned long adr, const u_char *buf, + int len) +@@ -1567,7 +1572,6 @@ static int __xipram do_write_buffer(stru + return ret; + } + +- + static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) + { +@@ -1642,6 +1646,7 @@ static int cfi_amdstd_write_buffers(stru + + return 0; + } ++#endif /* !FORCE_WORD_WRITE */ + + /* + * Wait for the flash chip to become ready to write data diff --git a/target/linux/ramips/patches-3.9/0999-net.patch b/target/linux/ramips/patches-3.9/0999-net.patch new file mode 100644 index 0000000000..954c747022 --- /dev/null +++ b/target/linux/ramips/patches-3.9/0999-net.patch @@ -0,0 +1,160 @@ +From 194f30e932952d74438fce68006f30f5a180459b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 22 Jun 2013 20:36:21 +0200 +Subject: [PATCH] net + +--- + drivers/net/ethernet/ralink/mdio_rt2880.c | 48 ++++++++++++++++++++++++-- + drivers/net/ethernet/ralink/mdio_rt2880.h | 3 +- + drivers/net/ethernet/ralink/ralink_soc_eth.h | 2 ++ + drivers/net/ethernet/ralink/soc_rt3883.c | 3 +- + 4 files changed, 51 insertions(+), 5 deletions(-) + +Index: linux-3.9.6/drivers/net/ethernet/ralink/mdio_rt2880.c +=================================================================== +--- linux-3.9.6.orig/drivers/net/ethernet/ralink/mdio_rt2880.c 2013-06-22 22:46:13.596610936 +0200 ++++ linux-3.9.6/drivers/net/ethernet/ralink/mdio_rt2880.c 2013-06-22 23:25:29.536713089 +0200 +@@ -32,6 +32,7 @@ + + #include "ralink_soc_eth.h" + #include "mdio_rt2880.h" ++#include "mdio.h" + + #define FE_MDIO_RETRY 1000 + +@@ -49,7 +50,7 @@ + return "?"; + } + +-void rt2880_mdio_link_adjust(struct fe_priv *priv) ++void rt2880_mdio_link_adjust(struct fe_priv *priv, int port) + { + u32 mdio_cfg; + +@@ -66,10 +67,10 @@ + if (priv->phy->duplex[0] == DUPLEX_FULL) + mdio_cfg |= FE_MDIO_CFG_GP1_DUPLEX; + +- if (priv->phy->tx_fc) ++ if (priv->phy->tx_fc[0]) + mdio_cfg |= FE_MDIO_CFG_GP1_FC_TX; + +- if (priv->phy->rx_fc) ++ if (priv->phy->rx_fc[0]) + mdio_cfg |= FE_MDIO_CFG_GP1_FC_RX; + + switch (priv->phy->speed[0]) { +@@ -161,3 +162,71 @@ + + return rt2880_mdio_wait_ready(priv); + } ++ ++void rt2880_port_init(struct fe_priv *priv, struct device_node *np) ++{ ++ const __be32 *id = of_get_property(np, "reg", NULL); ++ const __be32 *link; ++ int size; ++ int phy_mode; ++ ++ if (!id || (be32_to_cpu(*id) != 0)) { ++ pr_err("%s: invalid port id\n", np->name); ++ return; ++ } ++ ++ priv->phy->phy_fixed[0] = of_get_property(np, "ralink,fixed-link", &size); ++ if (priv->phy->phy_fixed[0] && (size != (4 * sizeof(*priv->phy->phy_fixed[0])))) { ++ pr_err("%s: invalid fixed link property\n", np->name); ++ priv->phy->phy_fixed[0] = NULL; ++ return; ++ } ++ ++ phy_mode = of_get_phy_mode(np); ++ switch (phy_mode) { ++ case PHY_INTERFACE_MODE_RGMII: ++ break; ++ case PHY_INTERFACE_MODE_MII: ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ break; ++ default: ++ if (!priv->phy->phy_fixed[0]) ++ dev_err(priv->device, "port %d - invalid phy mode\n", priv->phy->speed[0]); ++ break; ++ } ++ ++ priv->phy->phy_node[0] = of_parse_phandle(np, "phy-handle", 0); ++ if (!priv->phy->phy_node[0] && !priv->phy->phy_fixed[0]) ++ return; ++ ++ if (priv->phy->phy_fixed[0]) { ++ link = priv->phy->phy_fixed[0]; ++ priv->phy->speed[0] = be32_to_cpup(link++); ++ priv->phy->duplex[0] = be32_to_cpup(link++); ++ priv->phy->tx_fc[0] = be32_to_cpup(link++); ++ priv->phy->rx_fc[0] = be32_to_cpup(link++); ++ ++ priv->link[0] = 1; ++ switch (priv->phy->speed[0]) { ++ case SPEED_10: ++ break; ++ case SPEED_100: ++ break; ++ case SPEED_1000: ++ break; ++ default: ++ dev_err(priv->device, "invalid link speed: %d\n", priv->phy->speed[0]); ++ priv->phy->phy_fixed[0] = 0; ++ return; ++ } ++ dev_info(priv->device, "using fixed link parameters\n"); ++ rt2880_mdio_link_adjust(priv, 0); ++ return; ++ } ++ if (priv->phy->phy_node[0] && priv->mii_bus->phy_map[0]) { ++ fe_connect_phy_node(priv, priv->phy->phy_node[0]); ++ } ++ ++ return; ++} +Index: linux-3.9.6/drivers/net/ethernet/ralink/mdio_rt2880.h +=================================================================== +--- linux-3.9.6.orig/drivers/net/ethernet/ralink/mdio_rt2880.h 2013-06-22 22:46:13.600610937 +0200 ++++ linux-3.9.6/drivers/net/ethernet/ralink/mdio_rt2880.h 2013-06-22 22:46:13.744610945 +0200 +@@ -18,8 +18,9 @@ + #ifndef _RALINK_MDIO_RT2880_H__ + #define _RALINK_MDIO_RT2880_H__ + +-void rt2880_mdio_link_adjust(struct fe_priv *priv); ++void rt2880_mdio_link_adjust(struct fe_priv *priv, int port); + int rt2880_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg); + int rt2880_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val); ++void rt2880_port_init(struct fe_priv *priv, struct device_node *np); + + #endif +Index: linux-3.9.6/drivers/net/ethernet/ralink/ralink_soc_eth.h +=================================================================== +--- linux-3.9.6.orig/drivers/net/ethernet/ralink/ralink_soc_eth.h 2013-06-22 22:46:13.600610937 +0200 ++++ linux-3.9.6/drivers/net/ethernet/ralink/ralink_soc_eth.h 2013-06-22 22:46:13.744610945 +0200 +@@ -301,6 +301,8 @@ + const __be32 *phy_fixed[8]; + int duplex[8]; + int speed[8]; ++ int tx_fc[8]; ++ int rx_fc[8]; + spinlock_t lock; + + int (*connect)(struct fe_priv *priv); +Index: linux-3.9.6/drivers/net/ethernet/ralink/soc_rt3883.c +=================================================================== +--- linux-3.9.6.orig/drivers/net/ethernet/ralink/soc_rt3883.c 2013-06-22 22:46:13.600610937 +0200 ++++ linux-3.9.6/drivers/net/ethernet/ralink/soc_rt3883.c 2013-06-22 22:46:13.744610945 +0200 +@@ -47,7 +47,8 @@ + .checksum_bit = RX_DMA_L4VALID, + .mdio_read = rt2880_mdio_read, + .mdio_write = rt2880_mdio_write, +- .mdio_link_adjust = rt2880_mdio_link_adjust, ++ .mdio_adjust_link = rt2880_mdio_link_adjust, ++ .port_init = rt2880_port_init, + }; + + const struct of_device_id of_fe_match[] = { diff --git a/target/linux/ramips/rt288x/config-3.9 b/target/linux/ramips/rt288x/config-3.9 new file mode 100644 index 0000000000..28e573349b --- /dev/null +++ b/target/linux/ramips/rt288x/config-3.9 @@ -0,0 +1,149 @@ +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_CEVT_R4K=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CMDLINE="rootfstype=squashfs,jffs2" +CONFIG_CMDLINE_BOOL=y +# CONFIG_CMDLINE_OVERRIDE is not set +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_MIPS32=y +# CONFIG_CPU_MIPS32_R1 is not set +CONFIG_CPU_MIPS32_R2=y +CONFIG_CPU_MIPSR2=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_R4K_FPU=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CSRC_R4K=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DMA_NONCOHERENT=y +# CONFIG_DTB_RT2880_EVAL is not set +# CONFIG_DTB_RT305X_EVAL is not set +CONFIG_DTB_RT_NONE=y +CONFIG_DTC=y +CONFIG_EARLY_PRINTK=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_IO=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_RALINK=y +CONFIG_GPIO_SYSFS=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y +CONFIG_HAVE_CLK=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_HAVE_GENERIC_HARDIRQS=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_IRQ_WORK=y +CONFIG_HAVE_MACH_CLKDEV=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_HAVE_NET_DSA=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HW_HAS_PCI=y +CONFIG_HW_RANDOM=m +CONFIG_IMAGE_CMDLINE_HACK=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQ_CPU=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_M25PXX_USE_FAST_READ=y +CONFIG_MDIO_BOARDINFO=y +# CONFIG_MII is not set +CONFIG_MIPS=y +# CONFIG_MIPS_HUGE_TLB_SUPPORT is not set +CONFIG_MIPS_L1_CACHE_SHIFT=4 +# CONFIG_MIPS_MACHINE is not set +CONFIG_MIPS_MT_DISABLED=y +CONFIG_MODULES_USE_ELF_REL=y +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_OF_PARTS=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_UIMAGE_SPLIT=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_RAMIPS=y +# CONFIG_NET_RAMIPS_DEBUG is not set +# CONFIG_NET_RAMIPS_DEBUG_FS is not set +CONFIG_NET_SCH_FQ_CODEL=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_DEVICE=y +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_MDIO=y +CONFIG_OF_MTD=y +CONFIG_OF_NET=y +CONFIG_PAGEFLAGS_EXTENDED=y +# CONFIG_PCI is not set +CONFIG_PERCPU_RWSEM=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PHYLIB=y +# CONFIG_PREEMPT_RCU is not set +CONFIG_RALINK=y +CONFIG_RALINK_WDT=y +# CONFIG_SCSI_DMA is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RT288X=y +CONFIG_SERIAL_OF_PLATFORM=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SOC_MT7620 is not set +CONFIG_SOC_RT288X=y +# CONFIG_SOC_RT305X is not set +# CONFIG_SOC_RT3883 is not set +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_RALINK=y +CONFIG_SWCONFIG=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_HAS_EARLY_PRINTK=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_UIDGID_CONVERTED=y +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB_ARCH_HAS_HCD is not set +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/ramips/rt305x/config-3.8 b/target/linux/ramips/rt305x/config-3.8 index e87d7607cf..bb81bc9bcb 100644 --- a/target/linux/ramips/rt305x/config-3.8 +++ b/target/linux/ramips/rt305x/config-3.8 @@ -1,6 +1,7 @@ CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y CONFIG_ARCH_DISCARD_MEMBLOCK=y CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_ARCH_HAS_RESET_CONTROLLER=y CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y CONFIG_ARCH_HIBERNATION_POSSIBLE=y CONFIG_ARCH_REQUIRE_GPIOLIB=y @@ -9,6 +10,9 @@ CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_CEVT_R4K=y CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKEVT_RT3352=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLKSRC_OF=y CONFIG_CMDLINE="rootfstype=squashfs,jffs2" CONFIG_CMDLINE_BOOL=y # CONFIG_CMDLINE_OVERRIDE is not set @@ -27,8 +31,9 @@ CONFIG_CPU_SUPPORTS_HIGHMEM=y CONFIG_CSRC_R4K=y CONFIG_DECOMPRESS_LZMA=y CONFIG_DMA_NONCOHERENT=y -CONFIG_DTB_RT305X_EVAL=y -# CONFIG_DTB_RT_NONE is not set +# CONFIG_DTB_RT305X_EVAL is not set +# CONFIG_DTB_RT5350_EVAL is not set +CONFIG_DTB_RT_NONE=y CONFIG_DTC=y # CONFIG_DWC_OTG is not set CONFIG_EARLY_PRINTK=y @@ -95,9 +100,8 @@ CONFIG_MTD_PHYSMAP_OF=y CONFIG_MTD_UIMAGE_SPLIT=y CONFIG_NEED_DMA_MAP_STATE=y CONFIG_NEED_PER_CPU_KM=y -CONFIG_NET_RAMIPS=y -# CONFIG_NET_RAMIPS_DEBUG is not set -# CONFIG_NET_RAMIPS_DEBUG_FS is not set +CONFIG_NET_RALINK=y +CONFIG_NET_RALINK_ESW_RT3052=y CONFIG_OF=y CONFIG_OF_ADDRESS=y CONFIG_OF_DEVICE=y @@ -114,7 +118,9 @@ CONFIG_PERF_USE_VMALLOC=y CONFIG_PHYLIB=y # CONFIG_PREEMPT_RCU is not set CONFIG_RALINK=y +CONFIG_RALINK_USBPHY=y CONFIG_RALINK_WDT=y +CONFIG_RESET_CONTROLLER=y # CONFIG_SCSI_DMA is not set CONFIG_SERIAL_8250_NR_UARTS=4 CONFIG_SERIAL_8250_RT288X=y @@ -138,6 +144,8 @@ CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y CONFIG_TICK_CPU_ACCOUNTING=y CONFIG_UIDGID_CONVERTED=y # CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_OTG_UTILS=y CONFIG_USB_SUPPORT=y CONFIG_USE_OF=y +CONFIG_WATCHDOG_CORE=y CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/ramips/rt305x/config-3.9 b/target/linux/ramips/rt305x/config-3.9 new file mode 100644 index 0000000000..72c6082437 --- /dev/null +++ b/target/linux/ramips/rt305x/config-3.9 @@ -0,0 +1,157 @@ +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_CEVT_R4K=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLKEVT_RT3352=y +CONFIG_CLKSRC_MMIO=y +CONFIG_CLKSRC_OF=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CMDLINE="rootfstype=squashfs,jffs2" +CONFIG_CMDLINE_BOOL=y +# CONFIG_CMDLINE_OVERRIDE is not set +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_MIPS32=y +# CONFIG_CPU_MIPS32_R1 is not set +CONFIG_CPU_MIPS32_R2=y +CONFIG_CPU_MIPSR2=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_R4K_FPU=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CSRC_R4K=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DMA_NONCOHERENT=y +# CONFIG_DTB_RT305X_EVAL is not set +# CONFIG_DTB_RT5350_EVAL is not set +CONFIG_DTB_RT_NONE=y +CONFIG_DTC=y +# CONFIG_DWC_OTG is not set +CONFIG_EARLY_PRINTK=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_IO=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_DEVRES=y +CONFIG_GPIO_RALINK=y +CONFIG_GPIO_SYSFS=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_HAVE_CLK=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_HAVE_GENERIC_HARDIRQS=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_MACH_CLKDEV=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_HAVE_NET_DSA=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HW_RANDOM=m +CONFIG_IMAGE_CMDLINE_HACK=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_CPU=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +CONFIG_M25PXX_USE_FAST_READ=y +CONFIG_MDIO_BOARDINFO=y +# CONFIG_MII is not set +CONFIG_MIPS=y +# CONFIG_MIPS_HUGE_TLB_SUPPORT is not set +CONFIG_MIPS_L1_CACHE_SHIFT=5 +# CONFIG_MIPS_MACHINE is not set +CONFIG_MIPS_MT_DISABLED=y +CONFIG_MODULES_USE_ELF_REL=y +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_OF_PARTS=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_UIMAGE_SPLIT=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_RALINK=y +CONFIG_NET_RALINK_ESW_RT3052=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_DEVICE=y +# CONFIG_OF_DISPLAY_TIMING is not set +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_MDIO=y +CONFIG_OF_MTD=y +CONFIG_OF_NET=y +# CONFIG_OF_VIDEOMODE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PHYLIB=y +# CONFIG_PREEMPT_RCU is not set +CONFIG_RALINK=y +CONFIG_RALINK_USBPHY=y +CONFIG_RALINK_WDT=y +# CONFIG_RCU_STALL_COMMON is not set +CONFIG_RESET_CONTROLLER=y +# CONFIG_SCSI_DMA is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RT288X=y +CONFIG_SERIAL_OF_PLATFORM=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SOC_MT7620 is not set +# CONFIG_SOC_RT288X is not set +CONFIG_SOC_RT305X=y +# CONFIG_SOC_RT3883 is not set +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_RALINK=y +CONFIG_SWCONFIG=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_HAS_EARLY_PRINTK=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_UIDGID_CONVERTED=y +# CONFIG_USB_ARCH_HAS_XHCI is not set +CONFIG_USB_OTG_UTILS=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_WATCHDOG_CORE=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/ramips/rt3883/config-3.9 b/target/linux/ramips/rt3883/config-3.9 new file mode 100644 index 0000000000..e8dab5859c --- /dev/null +++ b/target/linux/ramips/rt3883/config-3.9 @@ -0,0 +1,164 @@ +CONFIG_AR8216_PHY=y +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_ARCH_HAS_RESET_CONTROLLER=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_CEVT_R4K=y +CONFIG_CLKDEV_LOOKUP=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CMDLINE="rootfstype=squashfs,jffs2" +CONFIG_CMDLINE_BOOL=y +# CONFIG_CMDLINE_OVERRIDE is not set +CONFIG_CPU_GENERIC_DUMP_TLB=y +CONFIG_CPU_HAS_PREFETCH=y +CONFIG_CPU_HAS_SYNC=y +CONFIG_CPU_LITTLE_ENDIAN=y +CONFIG_CPU_MIPS32=y +# CONFIG_CPU_MIPS32_R1 is not set +CONFIG_CPU_MIPS32_R2=y +CONFIG_CPU_MIPSR2=y +CONFIG_CPU_R4K_CACHE_TLB=y +CONFIG_CPU_R4K_FPU=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CSRC_R4K=y +CONFIG_DECOMPRESS_LZMA=y +CONFIG_DMA_NONCOHERENT=y +# CONFIG_DTB_RT3883_EVAL is not set +CONFIG_DTB_RT_NONE=y +CONFIG_DTC=y +CONFIG_EARLY_PRINTK=y +CONFIG_ETHERNET_PACKET_MANGLE=y +CONFIG_GENERIC_ATOMIC64=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_GENERIC_CMOS_UPDATE=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_IO=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_PCI_IOMAP=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_DEVRES=y +CONFIG_GPIO_RALINK=y +CONFIG_GPIO_SYSFS=y +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_HAVE_CLK=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DEBUG_KMEMLEAK=y +CONFIG_HAVE_DMA_API_DEBUG=y +CONFIG_HAVE_DMA_ATTRS=y +CONFIG_HAVE_DYNAMIC_FTRACE=y +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +CONFIG_HAVE_FUNCTION_TRACER=y +CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y +CONFIG_HAVE_GENERIC_DMA_COHERENT=y +CONFIG_HAVE_GENERIC_HARDIRQS=y +CONFIG_HAVE_IDE=y +CONFIG_HAVE_MACH_CLKDEV=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_HAVE_NET_DSA=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_PERF_EVENTS=y +CONFIG_HW_HAS_PCI=y +CONFIG_HW_RANDOM=m +CONFIG_IMAGE_CMDLINE_HACK=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_IRQCHIP=y +CONFIG_IRQ_CPU=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +CONFIG_M25PXX_USE_FAST_READ=y +CONFIG_MDIO_BOARDINFO=y +# CONFIG_MII is not set +CONFIG_MIPS=y +# CONFIG_MIPS_HUGE_TLB_SUPPORT is not set +CONFIG_MIPS_L1_CACHE_SHIFT=5 +# CONFIG_MIPS_MACHINE is not set +CONFIG_MIPS_MT_DISABLED=y +CONFIG_MODULES_USE_ELF_REL=y +# CONFIG_MTD_CFI_INTELEXT is not set +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_M25P80=y +CONFIG_MTD_OF_PARTS=y +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_OF=y +CONFIG_MTD_UIMAGE_SPLIT=y +CONFIG_NEED_DMA_MAP_STATE=y +CONFIG_NEED_PER_CPU_KM=y +CONFIG_NET_RALINK=y +CONFIG_NET_RALINK_MDIO=y +CONFIG_NET_RALINK_MDIO_RT2880=y +CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y +CONFIG_OF=y +CONFIG_OF_ADDRESS=y +CONFIG_OF_DEVICE=y +# CONFIG_OF_DISPLAY_TIMING is not set +CONFIG_OF_EARLY_FLATTREE=y +CONFIG_OF_FLATTREE=y +CONFIG_OF_GPIO=y +CONFIG_OF_IRQ=y +CONFIG_OF_MDIO=y +CONFIG_OF_MTD=y +CONFIG_OF_NET=y +CONFIG_OF_PCI=y +CONFIG_OF_PCI_IRQ=y +# CONFIG_OF_VIDEOMODE is not set +CONFIG_PAGEFLAGS_EXTENDED=y +CONFIG_PCI=y +CONFIG_PCI_DOMAINS=y +CONFIG_PERF_USE_VMALLOC=y +CONFIG_PHYLIB=y +# CONFIG_PREEMPT_RCU is not set +CONFIG_RALINK=y +# CONFIG_RALINK_USBPHY is not set +CONFIG_RALINK_WDT=y +# CONFIG_RCU_STALL_COMMON is not set +CONFIG_RESET_CONTROLLER=y +CONFIG_RTL8366_SMI=y +CONFIG_RTL8367B_PHY=y +CONFIG_RTL8367_PHY=y +# CONFIG_SCSI_DMA is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RT288X=y +CONFIG_SERIAL_OF_PLATFORM=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SOC_MT7620 is not set +# CONFIG_SOC_RT288X is not set +# CONFIG_SOC_RT305X is not set +CONFIG_SOC_RT3883=y +CONFIG_SPI=y +CONFIG_SPI_MASTER=y +CONFIG_SPI_RALINK=y +CONFIG_SWCONFIG=y +CONFIG_SYS_HAS_CPU_MIPS32_R1=y +CONFIG_SYS_HAS_CPU_MIPS32_R2=y +CONFIG_SYS_HAS_EARLY_PRINTK=y +CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y +CONFIG_SYS_SUPPORTS_ARBIT_HZ=y +CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y +CONFIG_TICK_CPU_ACCOUNTING=y +CONFIG_UIDGID_CONVERTED=y +CONFIG_USB_ARCH_HAS_XHCI=y +CONFIG_USB_SUPPORT=y +CONFIG_USE_OF=y +CONFIG_WATCHDOG_CORE=y +CONFIG_ZONE_DMA_FLAG=0