diff options
author | Christian Lamparter <chunkeey@googlemail.com> | 2016-07-20 15:44:57 +0200 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2016-07-22 09:48:12 +0200 |
commit | 9e0fd1b52ad1f805a308bf6a5a13236f352fd962 (patch) | |
tree | c01eaf08bb04ea37b07070469e8077c402a5d111 /target/linux | |
parent | 47eeb9f857044de6e79c79577a1a7d6a57ac5046 (diff) | |
download | upstream-9e0fd1b52ad1f805a308bf6a5a13236f352fd962.tar.gz upstream-9e0fd1b52ad1f805a308bf6a5a13236f352fd962.tar.bz2 upstream-9e0fd1b52ad1f805a308bf6a5a13236f352fd962.zip |
apm821xx: add support for the Netgear Centria N900 WNDR4700/WNDR4720
This patch adds support for Netgear Centria N900 WNDR4700/WNDR4720
hardware highlights:
CPU: AMCC PowerPC APM82181 Rev. E at 1000 MHz (PLB=166, OPB=83, EBC=83 MHz)
Security support, Boot ROM Location NAND wo/ECC 2k page (8 bits)
32 kB I-Cache 32 kB D-Cache, 256 kB L2-Cache, 32 kB OnChip Memory
Board: AMCC APM82181 Evaluation Board, PCIE0/SATA1, 1*USB OTG
DRAM: 256 MB (ECC not enabled, 500 Mb/s, 32-bit, CL3)
NAND: 128 MiB (SLC, erase size: 128 KiB, page size: 2048, OOB size: 64)
ETH: Atheros AR8327N Gigabit Switch (4 x LAN, 1 x WAN)
USB: 2 x 3.0 (Renesas uPD720202K8-711-BAA-A, firmware not included)
SATA: 1 x SATA-II 3.5" Hard Drive Bay for HDDs (DesignWare SATA).
WLAN1: Atheros AR9380 5GHz 802.11an 3:3x3
WLAN2: Atheros AR9581 2.4GHz 802.11bgn 3:3x3
SDCARD: GL827L SD/MMC/MS Flash Card Reader (on internal dwc2 USB 2.0 host)
I2C: GMT G781 (i2c-0 @ 0x4d - lm90 compatible temperature sensor)
TC654 (i2c-0 @ 0x1b - Dual PWM fan Speed controller)
WARNING: The serial port needs a TTL/RS-232 v3.3 level converter!
INFO: Since this device only has a NAND chip. I opted for going with
root.squashfs in a UBI volume. There's no squashfs/jffs2 image.
This target produces three images.
a. netgear factory image
This image can be used to flash the Netgear WNDR4700 via the
firmware recovery mechanism and the web admin site.
The bootloader can be instructed to do a firmware recovery via the
# fw_recovery
command. It will start a tftp server and listen on 192.168.1.1
(the ipaddr variable in u-boot) for incoming, binary tftp clients.
The firmware recovery mechanism is also started if any of the flash
content which contains the kernel, device-tree definitions or the
(fake)rootfs fails to verify or load.
b. sysupgrade.tar image for sysupgrade
An sysupgrade will replace the entire current LEDE installation
with a newer version. This does include the kernel and the ubi rootfs
partition. The configuration can be carried over automatically as well
if desired.
simply copy the sysupgrade.tar to a the WNDR4700 running LEDE and run:
root@lede:~# sysupgrade sysupgrade.tar
and let it reboot.
Note: The devicetree flash area is NOT updated. Until the devicetree
definition is stable, this can lead to all sorts of hardware
detection problems! So make sure, if you experience issues: try
the fw_recovery. If you are unsure whenever this affects you:
test if you can reproduce your issue with the initramfs method.
As it will always have up-to-date device-tree definitions.
c. initramfs image for TFTP (for development and testing)
To use the initramfs method, follow the following steps:
1) Move the "lede-apm821xx-netgear-WNDR4700-initramfs-kernel.bin"
file to to the root directory of your TFTP server.
2) rename it to wndr4700.bin
3) On the WNDR4700 - Hit Enter during u-boot and insert:
# tftp 400000 wndr4700.bin; run addtty; bootm 400000 -
This will boot the LEDE image.
Note: The default tftp server is 192.168.1.7, if you want to change it:
# setenv serverip 192.168.8.7;
Note2: The default address for the WNDR4700 is 192.168.1.1:
# setenv ipaddr 192.168.8.8;
Note: Connect you tftp server on the last LAN port (not the WAN)
Note: The firmware for the USB 3.0 Host chip is not included anymore.
Therefore the two USB 3.0 ports will not work without the
uPD7070x-firmware package installed.
Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
Diffstat (limited to 'target/linux')
17 files changed, 2129 insertions, 1 deletions
diff --git a/target/linux/apm821xx/base-files/etc/board.d/01_leds b/target/linux/apm821xx/base-files/etc/board.d/01_leds index bcc7a9f101..48fe139ce9 100755 --- a/target/linux/apm821xx/base-files/etc/board.d/01_leds +++ b/target/linux/apm821xx/base-files/etc/board.d/01_leds @@ -19,6 +19,14 @@ mr24) mbl) ;; +wndr4700) + ucidef_set_led_netdev "wan" "WAN (green)" "wndr4700:green:wan" "eth0.2" + ucidef_set_led_usbdev "usb3-1" "USB3-1" "wndr4700:blue:usb" "2-1" + ucidef_set_led_usbdev "usb3-2" "USB3-2" "wndr4700:blue:usb" "3-1" + ucidef_set_led_wlan "wlan2g" "WLAN2G" "wndr4700:blue:wlan" "phy0tpt" + ucidef_set_led_wlan "wlan5g" "WLAN5G" "wndr4700:blue:wlan" "phy1tpt" + ;; + *) ;; esac diff --git a/target/linux/apm821xx/base-files/etc/board.d/02_network b/target/linux/apm821xx/base-files/etc/board.d/02_network index 9525d8c5f2..3a25709a2a 100755 --- a/target/linux/apm821xx/base-files/etc/board.d/02_network +++ b/target/linux/apm821xx/base-files/etc/board.d/02_network @@ -13,6 +13,12 @@ mbl | \ mr24) ucidef_set_interface_lan "eth0" ;; + +wndr4700) + ucidef_add_switch "switch0" \ + "0@eth0" "4:lan" "3:lan" "2:lan" "1:lan" "5:wan" + ;; + *) ucidef_set_interfaces_lan_wan "eth0" "eth1" ;; diff --git a/target/linux/apm821xx/base-files/etc/diag.sh b/target/linux/apm821xx/base-files/etc/diag.sh index bea66eb14d..3ddd21d844 100755 --- a/target/linux/apm821xx/base-files/etc/diag.sh +++ b/target/linux/apm821xx/base-files/etc/diag.sh @@ -13,6 +13,10 @@ get_status_led() { status_led="mr24:green:power" ;; + wndr4700) + status_led="wndr4700:green:power" + ;; + *) ;; esac diff --git a/target/linux/apm821xx/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/apm821xx/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom new file mode 100644 index 0000000000..dfdc548d63 --- /dev/null +++ b/target/linux/apm821xx/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -0,0 +1,89 @@ +#!/bin/sh + +[ -e /lib/firmware/$FIRMWARE ] && exit 0 + +. /lib/apm821xx.sh +. /lib/functions.sh +. /lib/functions/system.sh + +ath9k_eeprom_die() { + echo "ath9k eeprom: " "$*" + exit 1 +} + +ath9k_eeprom_extract() { + local part=$1 + local offset=$2 + local count=$3 + local mtd + + mtd=$(find_mtd_chardev $part) + [ -n "$mtd" ] || \ + ath9k_eeprom_die "no mtd device found for partition $part" + + dd if=$mtd of=/lib/firmware/$FIRMWARE bs=1 skip=$offset count=$count 2>/dev/null || \ + ath9k_eeprom_die "failed to extract from $mtd" +} + +ath9k_ubi_eeprom_extract() { + local part=$1 + local offset=$2 + local count=$3 + local ubidev=$(nand_find_ubi $CI_UBIPART) + local ubi + + ubi=$(nand_find_volume $ubidev $part) + [ -n "$ubi" ] || \ + ath9k_eeprom_die "no UBI volume found for $part" + + dd if=/dev/$ubi of=/lib/firmware/$FIRMWARE bs=1 skip=$offset count=$count 2>/dev/null || \ + ath9k_eeprom_die "failed to extract from $ubi" +} + +ath9k_patch_firmware_mac() { + local mac=$1 + + [ -z "$mac" ] && return + + macaddr_2bin $mac | dd of=/lib/firmware/$FIRMWARE conv=notrunc bs=1 seek=2 count=6 +} + +board=$(apm821xx_board_name) + +case "$FIRMWARE" in +"pci_wmac0.eeprom") + case $board in + wndr4700) + . /lib/upgrade/nand.sh + + if [ -n "$(nand_find_volume ubi0 caldata)" ]; then + ath9k_ubi_eeprom_extract "caldata" 20480 4096 + else + ath9k_eeprom_extract "wifi_data" 20480 4096 + ath9k_patch_firmware_mac $(mtd_get_mac_binary wifi_data 12) + fi + ;; + *) + ath9k_eeprom_die "board $board is not supported yet" + ;; + esac + ;; + +"pci_wmac1.eeprom") + case $board in + wndr4700) + . /lib/upgrade/nand.sh + + if [ -n "$(nand_find_volume ubi0 caldata)" ]; then + ath9k_ubi_eeprom_extract "caldata" 4096 4096 + else + ath9k_eeprom_extract "wifi_data" 4096 4096 + ath9k_patch_firmware_mac $(mtd_get_mac_binary wifi_data 0) + fi + ;; + *) + ath9k_eeprom_die "board $board is not supported yet" + ;; + esac + ;; +esac diff --git a/target/linux/apm821xx/base-files/lib/apm821xx.sh b/target/linux/apm821xx/base-files/lib/apm821xx.sh index 98e88cff6e..78fe452184 100755 --- a/target/linux/apm821xx/base-files/lib/apm821xx.sh +++ b/target/linux/apm821xx/base-files/lib/apm821xx.sh @@ -18,6 +18,10 @@ apm821xx_board_detect() { name="mbl" ;; + *"Netgear WNDR4700/WNDR4720 Series") + name="wndr4700" + ;; + *) name="unknown" ;; diff --git a/target/linux/apm821xx/base-files/lib/upgrade/platform.sh b/target/linux/apm821xx/base-files/lib/upgrade/platform.sh index d5b0986dd5..5559767a26 100755 --- a/target/linux/apm821xx/base-files/lib/upgrade/platform.sh +++ b/target/linux/apm821xx/base-files/lib/upgrade/platform.sh @@ -21,6 +21,11 @@ platform_check_image() { return $?; ;; + wndr4700) + nand_do_platform_check $board "$1" + return $?; + ;; + *) ;; esac @@ -37,6 +42,10 @@ platform_pre_upgrade() { merakinand_do_upgrade "$1" ;; + wndr4700) + nand_do_upgrade "$1" + ;; + *) ;; esac diff --git a/target/linux/apm821xx/config-4.4 b/target/linux/apm821xx/config-4.4 index 804e7e852f..1515dda73b 100644 --- a/target/linux/apm821xx/config-4.4 +++ b/target/linux/apm821xx/config-4.4 @@ -319,6 +319,7 @@ CONFIG_TICK_CPU_ACCOUNTING=y CONFIG_USB_SUPPORT=y CONFIG_VDSO32=y # CONFIG_WARP is not set +# CONFIG_WNDR4700 is not set CONFIG_WORD_SIZE=32 # CONFIG_XILINX_SYSACE is not set # CONFIG_XILINX_VIRTEX440_GENERIC_BOARD is not set @@ -328,3 +329,4 @@ CONFIG_XZ_DEC_POWERPC=y CONFIG_ZLIB_DEFLATE=y CONFIG_ZLIB_INFLATE=y # CONFIG_JFFS2_FS is not set +# CONFIG_SENSORS_TC654 is not set diff --git a/target/linux/apm821xx/dts/wndr4700.dts b/target/linux/apm821xx/dts/wndr4700.dts new file mode 100644 index 0000000000..9a2ceb9d75 --- /dev/null +++ b/target/linux/apm821xx/dts/wndr4700.dts @@ -0,0 +1,762 @@ +/* + * Device Tree Source for Netgear WNDR4700/WNDR4720 Series + * + * Copyright 2008 DENX Software Engineering, Stefan Roese <sr@denx.de> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without + * any warranty of any kind, whether express or implied. + */ + +/dts-v1/; + +#include <dt-bindings/thermal/thermal.h> + +/ { + #address-cells = <2>; + #size-cells = <1>; + model = "Netgear WNDR4700/WNDR4720 Series"; + compatible = "netgear,wndr4700"; + dcr-parent = <&{/cpus/cpu@0}>; + + aliases { + ethernet0 = &EMAC0; + serial0 = &UART0; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu0: cpu@0 { + device_type = "cpu"; + model = "PowerPC,apm82181"; + reg = <0x00000000>; + clock-frequency = <0>; /* Filled in by U-Boot */ + timebase-frequency = <0>; /* Filled in by U-Boot */ + i-cache-line-size = <32>; + d-cache-line-size = <32>; + i-cache-size = <32768>; + d-cache-size = <32768>; + dcr-controller; + dcr-access-method = "native"; + next-level-cache = <&L2C0>; + }; + }; + + memory { + device_type = "memory"; + reg = <0x00000000 0x00000000 0x00000000>; /* Filled in by U-Boot */ + }; + + UIC0: interrupt-controller0 { + compatible = "ibm,uic-460ex","ibm,uic"; + interrupt-controller; + cell-index = <0>; + dcr-reg = <0x0c0 0x009>; + #address-cells = <0>; + #size-cells = <0>; + #interrupt-cells = <2>; + }; + + UIC1: interrupt-controller1 { + compatible = "ibm,uic-460ex","ibm,uic"; + interrupt-controller; + cell-index = <1>; + dcr-reg = <0x0d0 0x009>; + #address-cells = <0>; + #size-cells = <0>; + #interrupt-cells = <2>; + interrupts = <0x1e 0x4 0x1f 0x4>; /* cascade */ + interrupt-parent = <&UIC0>; + }; + + UIC2: interrupt-controller2 { + compatible = "ibm,uic-460ex","ibm,uic"; + interrupt-controller; + cell-index = <2>; + dcr-reg = <0x0e0 0x009>; + #address-cells = <0>; + #size-cells = <0>; + #interrupt-cells = <2>; + interrupts = <0xa 0x4 0xb 0x4>; /* cascade */ + interrupt-parent = <&UIC0>; + }; + + UIC3: interrupt-controller3 { + compatible = "ibm,uic-460ex","ibm,uic"; + interrupt-controller; + cell-index = <3>; + dcr-reg = <0x0f0 0x009>; + #address-cells = <0>; + #size-cells = <0>; + #interrupt-cells = <2>; + interrupts = <0x10 0x4 0x11 0x4>; /* cascade */ + interrupt-parent = <&UIC0>; + }; + + OCM1: ocm@400040000 { + compatible = "ibm,ocm"; + status = "okay"; + cell-index = <1>; + /* configured in U-Boot */ + reg = <4 0x00040000 0x8000>; /* 32K */ + }; + + SDR0: sdr { + compatible = "ibm,sdr-460ex"; + dcr-reg = <0x00e 0x002>; + }; + + CPR0: cpr { + compatible = "ibm,cpr-460ex"; + dcr-reg = <0x00c 0x002>; + }; + + L2C0: l2c { + compatible = "ibm,l2-cache-apm82181", "ibm,l2-cache"; + dcr-reg = <0x020 0x008 + 0x030 0x008>; + cache-line-size = <32>; + cache-size = <262144>; + interrupt-parent = <&UIC1>; + interrupts = <11 1>; + }; + + CPM0: cpm { + compatible = "ibm,cpm-apm821xx", "ibm,cpm"; + cell-index = <0>; + dcr-reg = <0x160 0x003>; + pm-cpu = <0x02000000>; + pm-doze = <0x302570F0>; + pm-nap = <0x302570F0>; + pm-deepsleep = <0x302570F0>; + pm-iic-device = <&IIC0>; + pm-emac-device = <&EMAC0>; + }; + + plb { + compatible = "ibm,plb-460ex", "ibm,plb4"; + #address-cells = <2>; + #size-cells = <1>; + ranges; + clock-frequency = <0>; /* Filled in by U-Boot */ + + SDRAM0: sdram { + compatible = "ibm,sdram-460ex", "ibm,sdram-405gp"; + dcr-reg = <0x010 0x002>; + }; + + RTC: rtc { + compatible = "ibm,rtc"; + dcr-reg = <0x240 0x009>; + interrupts = <0x1a 0x4>; + interrupt-parent = <&UIC2>; + + }; + + CRYPTO: crypto@180000 { + compatible = "amcc,ppc460ex-crypto", "amcc,ppc4xx-crypto"; + reg = <4 0x00180000 0x80400>; + interrupt-parent = <&UIC0>; + interrupts = <0x1d 0x4>; + }; + + PKA: pka@114000 { + device_type = "pka"; + compatible = "ppc4xx-pka", "amcc,ppc4xx-pka", "amcc, ppc4xx-pka"; + reg = <4 0x00114000 0x4000>; + interrupt-parent = <&UIC0>; + interrupts = <0x14 0x1>; + }; + + TRNG: trng@110000 { + device_type = "trng"; + compatible = "amcc,ppc460ex-rng", "ppc4xx-rng", "amcc, ppc4xx-trng"; + reg = <4 0x00110000 0x100>; + interrupt-parent = <&UIC1>; + interrupts = <0x3 0x4>; + }; + + MAL0: mcmal { + compatible = "ibm,mcmal-460ex", "ibm,mcmal2"; + descriptor-memory = "ocm"; + dcr-reg = <0x180 0x062>; + num-tx-chans = <1>; + num-rx-chans = <1>; + #address-cells = <0>; + #size-cells = <0>; + interrupt-parent = <&UIC2>; + interrupts = < /*TXEOB*/ 0x6 0x4 + /*RXEOB*/ 0x7 0x4 + /*SERR*/ 0x3 0x4 + /*TXDE*/ 0x4 0x4 + /*RXDE*/ 0x5 0x4 + /*TX0 COAL*/ 0x8 0x2 + /*TX1 COAL 0x9 0x2*/ + /*RX0 COAL*/ 0xc 0x2 + /*RX1 COAL 0xd 0x2*/>; + }; + + AHBDMA: dma@bffd0800 { + compatible = "snps,dma-spear1340"; + reg = <4 0xbffd0800 0x400>; + interrupt-parent = <&UIC0>; + interrupts = <25 4>; + #dma-cells = <3>; + /* use autoconfiguration for the dma setup */ + }; + + SATA0: sata@bffd1000 { + compatible = "amcc,sata-460ex"; + reg = <4 0xbffd1000 0x800>; + interrupt-parent = <&UIC0>; + interrupts = <26 4>; + dmas = <&AHBDMA 0 0 1>; + dma-names = "sata-dma"; + status = "disabled"; /* disabled by uboot */ + }; + + SATA1: sata@bffd1800 { + compatible = "amcc,sata-460ex"; + reg = <4 0xbffd1800 0x800>; + interrupt-parent = <&UIC0>; + interrupts = <27 4>; + dmas = <&AHBDMA 1 0 2>; + dma-names = "sata-dma"; + }; + + USBOTG0: usbotg@bff80000 { + compatible = "netgear,wndr4700-usb"; + reg = <4 0xbff80000 0x10000>; + interrupt-parent = <&USBOTG0>; + interrupts = <0 1 2>; + #interrupt-cells = <1>; + #address-cells = <0>; + #size-cells = <0>; + interrupt-map = </* USB-OTG */ 0 &UIC2 0x1c 4 + /* HIGH-POWER */ 1 &UIC1 0x1a 8 + /* DMA */ 2 &UIC0 0xc 4>; + dr_mode = "host"; + }; + + POB0: opb { + compatible = "ibm,opb-460ex", "ibm,opb"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0xb0000000 0x00000004 0xb0000000 0x50000000>; + clock-frequency = <0>; /* Filled in by U-Boot */ + + EBC0: ebc { + compatible = "ibm,ebc-460ex", "ibm,ebc"; + dcr-reg = <0x012 0x002>; + #address-cells = <2>; + #size-cells = <1>; + clock-frequency = <0>; /* Filled in by U-Boot */ + /* ranges property is supplied by U-Boot */ + ranges = < 0x00000003 0x00000000 0xe0000000 0x8000000>; + interrupts = <0x6 0x4>; + interrupt-parent = <&UIC1>; + + nor_flash@0,0 { + compatible = "amd,s29gl512n", "cfi-flash"; + bank-width = <1>; + reg = <0x00000000 0x00000000 0x00100000>; + #address-cells = <1>; + #size-cells = <1>; + status = "disabled"; + + partition@0 { + label = "back-up"; + reg = <0x00000000 0x00080000>; + }; + partition@1 { + label = "u-boot"; + reg = <0x00080000 0x0080000>; + }; + }; + ndfc@1,0 { + compatible = "ibm,ndfc"; + reg = <00000003 00000000 00002000>; + ccr = <0x00001000>; + bank-settings = <0x80002222>; + #address-cells = <1>; + #size-cells = <1>; + /*128 MiB Nand Flash*/ + nand { + #address-cells = <1>; + #size-cells = <1>; + + partition0,0@0x00000000 { + label = "NAND 128MiB 3,3V 8-bit"; + reg = <0x00000000 0x08000000>; + read-only; + }; + + partition0,1@0x00000000 { + label = "uboot"; + reg = <0x00000000 0x00180000>; + read-only; + }; + + partition0,2@0x00180000 { + label = "device-tree"; + reg = <0x00180000 0x00020000>; + read-only; + }; + + partition0,3@0x001a0000 { + label = "kernel"; + reg = <0x001a0000 0x001e0000>; + /* + * will also contain a fake/empty + * rootfs to fool Netgear's uboot + * rootfs integrety checks. + */ + }; + + partition0,4@0x00380000 { + label = "ubi"; + reg = <0x00380000 0x01660000>; + }; + + partition0,5@0x019e0000 { + label = "config"; + reg = <0x019e0000 0x00080000>; + read-only; + }; + + partition0,6@0x01a60000 { + label = "pot"; + reg = <0x01a60000 0x00080000>; + read-only; + }; + + partition0,7@0x01ae0000 { + label = "traffic_meter"; + reg = <0x01ae0000 0x00300000>; + read-only; + }; + + partition0,8@0x01de0000 { + label = "language"; + reg = <0x01de0000 0x001c0000>; + read-only; + }; + + partition0,9@0x01fa0000 { + label = "ecos"; + reg = <0x01fa0000 0x06020000>; + read-only; + }; + + partition0,10@0x07fc0000 { + label = "wifi_data"; + reg = <0x07fc0000 0x00040000>; + read-only; + }; + + partition0,11@0x00180000 { + label = "firmware"; + reg = <0x00180000 0x01860000>; + read-only; + }; + }; + }; + }; + + UART0: serial@ef600300 { + device_type = "serial"; + compatible = "ns16550"; + reg = <0xef600300 0x00000008>; + virtual-reg = <0xef600300>; + clock-frequency = <0>; /* Filled in by U-Boot */ + current-speed = <0>; /* Filled in by U-Boot */ + interrupt-parent = <&UIC1>; + interrupts = <0x1 0x4>; + }; + + GPIO0: gpio@ef600b00 { + compatible = "ibm,ppc4xx-gpio"; + reg = <0xef600b00 0x00000048>; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + + interrupts-extended = <&UIC1 0x14>, + <&UIC1 0x1e>, + <&UIC1 0x1f>, + <&UIC2 0x19>; + }; + + gpio_keys_polled { + compatible = "gpio-keys-polled"; + #address-cells = <1>; + #size-cells = <0>; + #interrupt-cells = <2>; + autorepeat; + poll-interval = <60>; /* 3 * 20 = 60ms */ + + reset { + label = "Reset button"; + linux,code = <0x198>; /* KEY_RESTART */ + gpios = <&GPIO0 15 0>; + interrupt-parent = <&UIC1>; + interrupts = <0x14 0x2>; + }; + + backup_hd { + label = "Backup HD button"; + gpios = <&GPIO0 19 0>; + linux,code = <0x100>; /* BTN_0 */ + interrupt-parent = <&UIC1>; + interrupts = <0x1e 0x2>; + }; + + rfkill { + label = "RFKILL button"; + gpios = <&GPIO0 20 0>; + linux,code = <0xf7>; /* KEY_RFKILL */ + interrupt-parent = <&UIC1>; + interrupts = <0x1f 0x2>; + }; + + wps { + label = "WPS button"; + gpios = <&GPIO0 23 0>; + linux,code = <0x211>; /* KEY_WPS_BUTTON */ + interrupt-parent = <&UIC2>; + interrupts = <0x19 0x2>; + }; + + sdcard { + label = "SDCard inserted"; + gpios = <&GPIO0 7 0>; + linux,code = <0x101>; /* BTN_1 */ + }; + }; + + gpio-leds { + compatible = "gpio-leds"; + power-green { + label = "wndr4700:green:power"; + gpios = <&GPIO0 8 0>; + }; + + power-orange { + label = "wndr4700:orange:power"; + gpios = <&GPIO0 9 1>; + linux,default-trigger = "panic"; + }; + + usb-blue { + label = "wndr4700:blue:usb"; + gpios = <&GPIO0 10 0>; + }; + + logo-white { + label = "wndr4700:white:logo"; + gpios = <&GPIO0 11 0>; + }; + + wan-yellow { + label = "wndr4700:yellow:wan"; + gpios = <&GPIO0 3 0>; + }; + + wan-green { + label = "wndr4700:green:wan"; + gpios = <&GPIO0 12 0>; + }; + + hd-green { + label = "wndr4700:green:hd"; + gpios = <&GPIO0 14 0>; + }; + + hd-red { + label = "wndr4700:red:hd"; + gpios = <&GPIO0 17 0>; + }; + + wlan-blue { + label = "wndr4700:blue:wlan"; + gpios = <&GPIO0 18 0>; + }; + }; + + IIC0: i2c@ef600700 { + compatible = "ibm,iic-460ex", "ibm,iic"; + reg = <0xef600700 0x00000014>; + interrupt-parent = <&UIC0>; + interrupts = <0x2 0x4>; + #address-cells = <1>; + #size-cells = <0>; + + fan0: fan@1b { + compatible = "microchip,tc654"; + reg = <0x1b>; + cooling-min-level = <0>; + cooling-max-level = <255>; + #cooling-cells = <2>; /* min followed by max */ + + gpios = <&GPIO0 16 1>; /* fan status */ + alarm-gpios = <&GPIO0 5 1>; /* fault */ + interrupt-parent = <&UIC3>; + interrupts = <0x16 0x2>; /* fault */ + }; + + temp0: temp@4d { + compatible = "gmt,g781"; + reg = <0x4d>; + #thermal-sensor-cells = <1>; + + /* + * The LM90 has two sensors: + * temp0 -> internal to LM90 + * temp1 -> external NTC near CPU + */ + }; + }; + + IIC1: i2c@ef600800 { + compatible = "ibm,iic-460ex", "ibm,iic"; + reg = <0xef600800 0x00000014>; + interrupt-parent = <&UIC0>; + interrupts = <0x3 0x4>; + }; + + RGMII0: emac-rgmii@ef601500 { + compatible = "ibm,rgmii-405ex", "ibm,rgmii"; + reg = <0xef601500 0x00000008>; + has-mdio; + }; + + TAH0: emac-tah@ef601350 { + compatible = "ibm,tah-460ex", "ibm,tah"; + reg = <0xef601350 0x00000030>; + }; + + EMAC0: ethernet@ef600c00 { + device_type = "network"; + compatible = "ibm,emac-apm821xx", "ibm,emac4sync"; + interrupt-parent = <&EMAC0>; + interrupts = <0x0 0x1>; + #interrupt-cells = <1>; + #address-cells = <0>; + #size-cells = <0>; + interrupt-map = </*Status*/ 0x0 &UIC2 0x10 0x4 + /*Wake*/ 0x1 &UIC2 0x14 0x4>; + reg = <0xef600c00 0x000000c4>; + local-mac-address = [000000000000]; /* Filled in by U-Boot */ + mal-device = <&MAL0>; + mal-tx-channel = <0>; + mal-rx-channel = <0>; + cell-index = <0>; + max-frame-size = <9000>; + rx-fifo-size = <16384>; + tx-fifo-size = <2048>; + fifo-entry-size = <10>; + phy-mode = "rgmii"; + phy-handle = <&phy0>; + phy-map = <0x00000000>; + rgmii-device = <&RGMII0>; + rgmii-channel = <0>; + tah-device = <&TAH0>; + tah-channel = <0>; + has-inverted-stacr-oc; + has-new-stacr-staopc; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + device_type = "ethernet-phy"; + reg = <0>; + qca,ar8327-initvals = < + 0x0010 0x40000000 + 0x0624 0x007f7f7f + 0x0004 0x07a00000 /* PAD0_MODE */ + 0x000c 0x01000000 /* PAD6_MODE */ + 0x007c 0x0000007e /* PORT0_STATUS */ + >; + }; + }; + }; + }; + + PCIE0: pciex@d00000000 { + device_type = "pci"; + #interrupt-cells = <1>; + #size-cells = <2>; + #address-cells = <3>; + compatible = "ibm,plb-pciex-apm821xx", "ibm,plb-pciex"; + primary; + port = <0x0>; /* port number */ + reg = <0x0000000d 0x00000000 0x20000000 /* Config space access */ + 0x0000000c 0x08010000 0x00001000>; /* Registers */ + dcr-reg = <0x100 0x020>; + sdr-base = <0x300>; + + /* Outbound ranges, one memory and one IO, + * later cannot be changed + */ + ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x00000000 0x00000000 0x80000000 + 0x02000000 0x00000000 0x00000000 0x0000000f 0x00000000 0x00000000 0x00100000 + 0x01000000 0x00000000 0x00000000 0x0000000f 0x80000000 0x00000000 0x00010000>; + + /* Inbound 2GB range starting at 0 */ + dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>; + + /* This drives busses 40 to 0x7f */ + bus-range = <0x40 0x7f>; + + /* Legacy interrupts (note the weird polarity, the bridge seems + * to invert PCIe legacy interrupts). + * We are de-swizzling here because the numbers are actually for + * port of the root complex virtual P2P bridge. But I want + * to avoid putting a node for it in the tree, so the numbers + * below are basically de-swizzled numbers. + * The real slot is on idsel 0, so the swizzling is 1:1 + */ + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = < + 0x0 0x0 0x0 0x1 &UIC3 0xc 0x4 /* swizzled int A */ + 0x0 0x0 0x0 0x2 &UIC3 0xd 0x4 /* swizzled int B */ + 0x0 0x0 0x0 0x3 &UIC3 0xe 0x4 /* swizzled int C */ + 0x0 0x0 0x0 0x4 &UIC3 0xf 0x4 /* swizzled int D */>; + }; + + MSI: ppc4xx-msi@C10000000 { + compatible = "amcc,ppc4xx-msi", "ppc4xx-msi"; + reg = < 0xC 0x10000000 0x100 + 0xC 0x10000000 0x100>; + sdr-base = <0x36C>; + msi-data = <0x00004440>; + msi-mask = <0x0000ffe0>; + interrupts =<0 1 2 3 4 5 6 7>; + interrupt-parent = <&MSI>; + #interrupt-cells = <1>; + #address-cells = <0>; + #size-cells = <0>; + msi-available-ranges = <0x0 0x100>; + interrupt-map = < + 0 &UIC3 0x18 1 + 1 &UIC3 0x19 1 + 2 &UIC3 0x1A 1 + 3 &UIC3 0x1B 1 + 4 &UIC3 0x1C 1 + 5 &UIC3 0x1D 1 + 6 &UIC3 0x1E 1 + 7 &UIC3 0x1F 1 + >; + }; + }; + + chosen { + linux,stdout-path = "/plb/opb/serial@ef600300"; + }; + + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <10000>; /* milliseconds */ + polling-delay = <20000>; /* milliseconds */ + + thermal-sensors = <&temp0 1>; + + trips { + /* + * Once the thermal governers are a bit smarter + * and do hysteresis properly, we can disable + * the fan when the HDD and CPU has < 39 C. + */ + cpu_alert0: cpu-alert0 { + temperature = <25000>; + hysteresis = <2000>; + type = "active"; + }; + + cpu_alert1: cpu-alert1 { + temperature = <27000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "active"; + }; + + cpu_alert2: cpu-alert2 { + temperature = <65000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "active"; + }; + + cpu_alert3: cpu-alert3 { + temperature = <70000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "active"; + }; + + cpu_alert4: cpu-alert4 { + temperature = <75000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "active"; + }; + + cpu_alert5: cpu-alert5 { + temperature = <80000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "active"; + }; + + cpu_alert6: cpu-alert6 { + temperature = <850000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "active"; + }; + + cpu_crit: cpu-crit { + temperature = <90000>; /* millicelsius */ + hysteresis = <2000>; /* millicelsius */ + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu_alert0>; + cooling-device = <&fan0 THERMAL_NO_LIMIT 0>; + }; + + map1 { + trip = <&cpu_alert1>; + cooling-device = <&fan0 1 87>; + }; + + map2 { + trip = <&cpu_alert2>; + cooling-device = <&fan0 88 100>; + }; + + map3 { + trip = <&cpu_alert3>; + cooling-device = <&fan0 101 147>; + }; + + map4 { + trip = <&cpu_alert4>; + cooling-device = <&fan0 148 207>; + }; + + map5 { + trip = <&cpu_alert5>; + cooling-device = <&fan0 208 231>; + }; + + map6 { + trip = <&cpu_alert6>; + cooling-device =<&fan0 232 THERMAL_NO_LIMIT>; + }; + }; + }; + }; +}; diff --git a/target/linux/apm821xx/files/arch/powerpc/platforms/44x/wndr4700.c b/target/linux/apm821xx/files/arch/powerpc/platforms/44x/wndr4700.c new file mode 100644 index 0000000000..975ac461fe --- /dev/null +++ b/target/linux/apm821xx/files/arch/powerpc/platforms/44x/wndr4700.c @@ -0,0 +1,96 @@ +/* + * Netgear Centria N900 WNDR4700/WNDR4720 platform support + * + * 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 implemention is based on the simple platform support for the + * PowerPC 44x chips. + */ + +#include <asm/machdep.h> +#include <asm/pci-bridge.h> +#include <asm/ppc4xx.h> +#include <asm/prom.h> +#include <asm/time.h> +#include <asm/udbg.h> +#include <asm/uic.h> + +#include <linux/init.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/ath9k_platform.h> +#include <linux/firmware.h> +#include <linux/delay.h> +#include <asm/unaligned.h> + +static const struct of_device_id ppc44x_of_bus[] __initconst = { + { .compatible = "ibm,plb4", }, + { .compatible = "ibm,opb", }, + { .compatible = "ibm,ebc", }, + { .compatible = "simple-bus", }, + {}, +}; + +static int __init ppc44x_device_probe(void) +{ + of_platform_bus_probe(NULL, ppc44x_of_bus, NULL); + + return 0; +} +machine_device_initcall(wndr4700, ppc44x_device_probe); + +static char *board[] __initdata = { + "netgear,wndr4700", +}; + +static int __init ppc44x_probe(void) +{ + unsigned long root = of_get_flat_dt_root(); + int i = 0; + + pcie_bus_config = PCIE_BUS_PEER2PEER; /* force 128 Byte MPS */ + + for (i = 0; i < ARRAY_SIZE(board); i++) { + if (of_flat_dt_is_compatible(root, board[i])) { + pci_set_flags(PCI_REASSIGN_ALL_RSRC); /* PCI_PROBE_ONLY */ + return 1; + } + } + + return 0; +} + +define_machine(wndr4700) { + .name = "WNDR4700 Platform", + .probe = ppc44x_probe, + .progress = udbg_progress, + .init_IRQ = uic_init_tree, + .get_irq = uic_get_irq, + .restart = ppc4xx_reset_system, + .calibrate_decr = generic_calibrate_decr, +}; + +static struct ath9k_platform_data ar9380_wmac0_data = { + .led_pin = -1, + .eeprom_name = "pci_wmac1.eeprom", +}; +static struct ath9k_platform_data ar9580_wmac1_data = { + .led_pin = -1, + .eeprom_name = "pci_wmac0.eeprom", +}; + +static void load_eeprom_ar9380(struct pci_dev *dev) +{ + dev->dev.platform_data = &ar9380_wmac0_data; +} + +static void load_eeprom_ar9580(struct pci_dev *dev) +{ + dev->dev.platform_data = &ar9580_wmac1_data; +} + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATHEROS, 0x0030, load_eeprom_ar9380); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATHEROS, 0x0033, load_eeprom_ar9580); diff --git a/target/linux/apm821xx/image/Makefile b/target/linux/apm821xx/image/Makefile index 9ba43d9db2..08173c603f 100644 --- a/target/linux/apm821xx/image/Makefile +++ b/target/linux/apm821xx/image/Makefile @@ -78,6 +78,88 @@ define Device/mr24 endef TARGET_DEVICES += mr24 +define Build/create-uImage-dtb + # flat_dt target expect FIT image - which WNDR4700's uboot doesn't support + -$(STAGING_DIR_HOST)/bin/mkimage -A $(LINUX_KARCH) \ + -O linux -T kernel -C none \ + -n '$(call toupper,$(LINUX_KARCH)) LEDE Linux-$(LINUX_VERSION)' \ + -d $@.dtb $@.new + @mv $@.new $@ +endef + +define Build/append-fakerootfs + rm -rf $@.fakerootsquashfs $@.fakefs + + # append a fake/empty rootfs to fool netgear's uboot + # CHECK_DNI_FIRMWARE_ROOTFS_INTEGRITY in do_chk_dniimg() + dd if=/dev/zero of=$@.fakerd bs=16 count=1 conv=sync + + -$(STAGING_DIR_HOST)/bin/mkimage \ + -A $(LINUX_KARCH) -O linux -T filesystem -C none \ + -a 0x00000000 -e 0x00000000 \ + -n '$(DEVICE_PROFILE) fakerootfs' \ + -d $@.fakerd $@.fakefs + + cat $@.fakefs >> $@ + rm -rf $@.fakerootsquashfs $@.fakefs +endef + +define Build/wndr4700-specialImage + rm -rf $@.fakerd $@.new + + dd if=/dev/zero of=$@.fakerd bs=32 count=1 conv=sync + + # Netgear used an old uboot that doesn't have FIT support. + # So we are stuck with either a full ext2/4 fs in a initrd. + # ... or we try to make the "multi" image approach to work + # for us. + # + # Sadly, the "multi" image has to consists of three + # "fixed" parts in the following "fixed" order: + # 1. The kernel which is in $@ + # 2. The (fake) initrd which is in $@.fakerd + # 3. The device tree binary which is in $@.dtb + # + # Now, given that we use the function for the kernel which + # already has a initramfs image inside, we still have to + # add a "fake" initrd (which a mkimage header) in the second + # part of the legacy multi image. Since we need to put the + # device tree stuff into part 3. + + -$(STAGING_DIR_HOST)/bin/mkimage -A $(LINUX_KARCH) -O linux -T multi \ + -C $(1) -a $(KERNEL_LOADADDR) -e $(KERNEL_ENTRY) \ + -n '$(DEVICE_PROFILE) initramfs' -d $@:$@.fakerd:$@.dtb $@.new + mv $@.new $@ + rm -rf $@.fakerd +endef + +define Device/WNDR4700 + DEVICE_TITLE := Netgear Centria N900 WNDR4700/WNDR4720 + DEVICE_PACKAGES := badblocks block-mount e2fsprogs \ + kmod-ath9k kmod-dm kmod-fs-ext4 kmod-fs-vfat kmod-ledtrig-usbdev \ + kmod-md-mod kmod-nls-cp437 kmod-nls-iso8859-1 kmod-nls-iso8859-15 \ + kmod-nls-utf8 kmod-usb3 kmod-usb-dwc2 kmod-usb-storage \ + partx-utils swconfig wpad-mini + DEVICE_NAME := wndr4700 + DEVICE_PROFILE := wndr4700 + DEVICE_DTS := wndr4700 + PAGESIZE := 2048 + SUBPAGESIZE := 512 + BLOCKSIZE := 131072 + DTB_SIZE := 131008 + IMAGE_SIZE:=25559040 + IMAGES := factory.img sysupgrade.tar + KERNEL_SIZE := 1920k + KERNEL := kernel-bin | lzma | uImage lzma | pad-offset $$(BLOCKSIZE) 64 | append-fakerootfs + KERNEL_INITRAMFS := kernel-bin | gzip | dtb | wndr4700-specialImage gzip + IMAGE/factory.img := dtb | create-uImage-dtb | append-kernel | pad-to 2M | append-ubi | \ + netgear-dni | check-size $$$$(IMAGE_SIZE) + IMAGE/sysupgrade.tar := sysupgrade-nand + NETGEAR_BOARD_ID := WNDR4700 + NETGEAR_HW_ID := 29763875+128+256 +endef +TARGET_DEVICES += WNDR4700 + endif ifeq ($(SUBTARGET),sata) @@ -161,4 +243,3 @@ TARGET_DEVICES += MyBookLiveDuo endif $(eval $(call BuildImage)) - diff --git a/target/linux/apm821xx/nand/config-default b/target/linux/apm821xx/nand/config-default index 582d1295bc..9c93680f5f 100644 --- a/target/linux/apm821xx/nand/config-default +++ b/target/linux/apm821xx/nand/config-default @@ -1,4 +1,32 @@ +CONFIG_AR8216_PHY=y +CONFIG_DMADEVICES=y +CONFIG_DMA_ENGINE=y +CONFIG_DW_DMAC_CORE=y +CONFIG_DW_DMAC=y +# CONFIG_SATA_DWC_OLD_DMA is not set +# CONFIG_DW_DMAC_PCI is not set CONFIG_IKAREM=y +CONFIG_ATA=y +CONFIG_ATA_SFF=y +CONFIG_ATA_BMDMA=y +CONFIG_SATA_PMP=y +CONFIG_GENERIC_PHY=y +CONFIG_SATA_DWC=y +# CONFIG_SATA_DWC_DEBUG is not set +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_GENERIC=y +CONFIG_GPIO_GENERIC_PLATFORM=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_M48T86=y +CONFIG_THERMAL=y +CONFIG_THERMAL_OF=y +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y +CONFIG_THERMAL_HWMON=y +CONFIG_HWMON=y +CONFIG_SENSORS_GPIO_FAN=y +CONFIG_CMDLINE="rootfstype=squashfs noinitrd" CONFIG_MTD_UBI=y CONFIG_MTD_UBI_BEB_LIMIT=20 CONFIG_MTD_UBI_BLOCK=y @@ -7,3 +35,6 @@ CONFIG_MTD_UBI_BLOCK=y CONFIG_MTD_UBI_WL_THRESHOLD=4096 CONFIG_UBIFS_FS=y # CONFIG_UBIFS_FS_ADVANCED_COMPR is not set +CONFIG_SENSORS_LM90=y +CONFIG_SENSORS_TC654=y +CONFIG_WNDR4700=y diff --git a/target/linux/apm821xx/patches-4.4/202-add-netgear-wndr4700-support.patch b/target/linux/apm821xx/patches-4.4/202-add-netgear-wndr4700-support.patch new file mode 100644 index 0000000000..81531915ba --- /dev/null +++ b/target/linux/apm821xx/patches-4.4/202-add-netgear-wndr4700-support.patch @@ -0,0 +1,32 @@ +--- a/arch/powerpc/platforms/44x/Makefile 2016-05-31 19:28:28.825973250 +0200 ++++ b/arch/powerpc/platforms/44x/Makefile 2016-05-31 19:28:22.135960329 +0200 +@@ -3,6 +3,7 @@ ifneq ($(CONFIG_PPC4xx_CPM),y) + obj-$(CONFIG_44x) += idle.o + endif + obj-$(CONFIG_PPC44x_SIMPLE) += ppc44x_simple.o ++obj-$(CONFIG_WNDR4700) += wndr4700.o + obj-$(CONFIG_EBONY) += ebony.o + obj-$(CONFIG_SAM440EP) += sam440ep.o + obj-$(CONFIG_WARP) += warp.o +--- a/arch/powerpc/platforms/44x/Kconfig 2016-05-31 19:33:57.049940191 +0200 ++++ b/arch/powerpc/platforms/44x/Kconfig 2016-05-31 19:30:01.699485861 +0200 +@@ -260,6 +260,19 @@ config ICON + help + This option enables support for the AMCC PPC440SPe evaluation board. + ++config WNDR4700 ++ bool "WNDR4700" ++ depends on 44x ++ default n ++ select APM821xx ++ select PCI_MSI ++ select PPC4xx_MSI ++ select PPC4xx_PCI_EXPRESS ++ select IBM_EMAC_RGMII ++ select 460EX ++ help ++ This option enables support for the Netgear WNDR4700/WNDR4720 board. ++ + config XILINX_VIRTEX440_GENERIC_BOARD + bool "Generic Xilinx Virtex 5 FXT board support" + depends on 44x diff --git a/target/linux/apm821xx/patches-4.4/301-fix-memory-map-wndr4700.patch b/target/linux/apm821xx/patches-4.4/301-fix-memory-map-wndr4700.patch new file mode 100644 index 0000000000..b44f8bba8a --- /dev/null +++ b/target/linux/apm821xx/patches-4.4/301-fix-memory-map-wndr4700.patch @@ -0,0 +1,14 @@ +--- a/arch/powerpc/sysdev/ppc4xx_pci.c 2016-05-30 17:57:30.125498459 +0200 ++++ b/arch/powerpc/sysdev/ppc4xx_pci.c 2016-05-30 18:00:39.236007798 +0200 +@@ -1913,9 +1913,9 @@ static void __init ppc4xx_configure_pcie + * if it works + */ + out_le32(mbase + PECFG_PIM0LAL, 0x00000000); +- out_le32(mbase + PECFG_PIM0LAH, 0x00000000); ++ out_le32(mbase + PECFG_PIM0LAH, 0x00000008); + out_le32(mbase + PECFG_PIM1LAL, 0x00000000); +- out_le32(mbase + PECFG_PIM1LAH, 0x00000000); ++ out_le32(mbase + PECFG_PIM1LAH, 0x0000000c); + out_le32(mbase + PECFG_PIM01SAH, 0xffff0000); + out_le32(mbase + PECFG_PIM01SAL, 0x00000000); + diff --git a/target/linux/apm821xx/patches-4.4/702-powerpc_ibm_phy_add_dt_parser.patch b/target/linux/apm821xx/patches-4.4/702-powerpc_ibm_phy_add_dt_parser.patch new file mode 100644 index 0000000000..a1ca7c610e --- /dev/null +++ b/target/linux/apm821xx/patches-4.4/702-powerpc_ibm_phy_add_dt_parser.patch @@ -0,0 +1,335 @@ +From 59b394d0d2b2e11687e757820c52d6470d15a9c5 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter <chunkeey@gmail.com> +Date: Mon, 13 Jun 2016 15:42:21 +0200 +Subject: [PATCH] phy device tree support for emac + +--- + drivers/net/ethernet/ibm/emac/core.c | 261 +++++++++++++++++++++++++++++++++++ + drivers/net/ethernet/ibm/emac/core.h | 4 + + 2 files changed, 265 insertions(+) + +diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c +index 4c9771d..5a8a26c 100644 +--- a/drivers/net/ethernet/ibm/emac/core.c ++++ b/drivers/net/ethernet/ibm/emac/core.c +@@ -42,6 +42,7 @@ + #include <linux/of_address.h> + #include <linux/of_irq.h> + #include <linux/of_net.h> ++#include <linux/of_mdio.h> + #include <linux/slab.h> + + #include <asm/processor.h> +@@ -2383,6 +2384,246 @@ static int emac_read_uint_prop(struct device_node *np, const char *name, + return 0; + } + ++static void emac_adjust_link(struct net_device *ndev) ++{ ++ struct emac_instance *dev = netdev_priv(ndev); ++ struct phy_device *phy = dev->phy_dev; ++ ++ dev->phy.autoneg = phy->autoneg; ++ dev->phy.speed = phy->speed; ++ dev->phy.duplex = phy->duplex; ++ dev->phy.pause = phy->pause; ++ dev->phy.asym_pause = phy->asym_pause; ++ dev->phy.advertising = phy->advertising; ++} ++ ++static int emac_mii_bus_read(struct mii_bus *bus, int addr, int regnum) ++{ ++ return emac_mdio_read(bus->priv, addr, regnum); ++} ++ ++static int emac_mii_bus_write(struct mii_bus *bus, int addr, int regnum, u16 val) ++{ ++ emac_mdio_write(bus->priv, addr, regnum, val); ++ return 0; ++} ++ ++static int emac_mii_bus_reset(struct mii_bus *bus) ++{ ++ struct emac_instance *dev = netdev_priv(bus->priv); ++ ++ emac_mii_reset_phy(&dev->phy); ++ return 0; ++} ++ ++static int emac_mdio_setup_aneg(struct mii_phy *phy, u32 advertise) ++{ ++ struct net_device *ndev = phy->dev; ++ struct emac_instance *dev = netdev_priv(ndev); ++ ++ dev->phy.autoneg = AUTONEG_ENABLE; ++ dev->phy.speed = SPEED_1000; ++ dev->phy.duplex = DUPLEX_FULL; ++ dev->phy.advertising = advertise; ++ phy->autoneg = AUTONEG_ENABLE; ++ phy->speed = dev->phy.speed; ++ phy->duplex = dev->phy.duplex; ++ phy->advertising = advertise; ++ return phy_start_aneg(dev->phy_dev); ++} ++ ++static int emac_mdio_setup_forced(struct mii_phy *phy, int speed, int fd) ++{ ++ struct net_device *ndev = phy->dev; ++ struct emac_instance *dev = netdev_priv(ndev); ++ ++ dev->phy.autoneg = AUTONEG_DISABLE; ++ dev->phy.speed = speed; ++ dev->phy.duplex = fd; ++ phy->autoneg = AUTONEG_DISABLE; ++ phy->speed = speed; ++ phy->duplex = fd; ++ return phy_start_aneg(dev->phy_dev); ++} ++ ++static int emac_mdio_poll_link(struct mii_phy *phy) ++{ ++ struct net_device *ndev = phy->dev; ++ struct emac_instance *dev = netdev_priv(ndev); ++ int res; ++ ++ res = phy_read_status(dev->phy_dev); ++ if (res) { ++ dev_err(&dev->ndev->dev, "link update failed (%d).", res); ++ return ethtool_op_get_link(ndev); ++ } ++ ++ return dev->phy_dev->link; ++} ++ ++static int emac_mdio_read_link(struct mii_phy *phy) ++{ ++ struct net_device *ndev = phy->dev; ++ struct emac_instance *dev = netdev_priv(ndev); ++ int res; ++ ++ res = phy_read_status(dev->phy_dev); ++ if (res) ++ return res; ++ ++ dev->phy.speed = phy->speed; ++ dev->phy.duplex = phy->duplex; ++ dev->phy.pause = phy->pause; ++ dev->phy.asym_pause = phy->asym_pause; ++ return 0; ++} ++ ++static int emac_mdio_init_phy(struct mii_phy *phy) ++{ ++ struct net_device *ndev = phy->dev; ++ struct emac_instance *dev = netdev_priv(ndev); ++ ++ phy_start(dev->phy_dev); ++ dev->phy.autoneg = phy->autoneg; ++ dev->phy.speed = phy->speed; ++ dev->phy.duplex = phy->duplex; ++ dev->phy.advertising = phy->advertising; ++ dev->phy.pause = phy->pause; ++ dev->phy.asym_pause = phy->asym_pause; ++ ++ return phy_init_hw(dev->phy_dev); ++} ++ ++static const struct mii_phy_ops emac_dt_mdio_phy_ops = { ++ .init = emac_mdio_init_phy, ++ .setup_aneg = emac_mdio_setup_aneg, ++ .setup_forced = emac_mdio_setup_forced, ++ .poll_link = emac_mdio_poll_link, ++ .read_link = emac_mdio_read_link, ++}; ++ ++static void emac_dt_phy_mdio_cleanup(struct emac_instance *dev) ++{ ++ if (dev->mii_bus) { ++ if (dev->mii_bus->state == MDIOBUS_REGISTERED) ++ mdiobus_unregister(dev->mii_bus); ++ mdiobus_free(dev->mii_bus); ++ dev->mii_bus = NULL; ++ } ++ kfree(dev->phy.def); ++ dev->phy.def = NULL; ++} ++ ++static int emac_dt_mdio_probe(struct emac_instance *dev) ++{ ++ struct device_node *mii_np; ++ int res; ++ ++ mii_np = of_get_child_by_name(dev->ofdev->dev.of_node, "mdio"); ++ if (!mii_np) { ++ dev_err(&dev->ndev->dev, "no mdio definition found."); ++ return -ENODEV; ++ } ++ ++ if (!of_device_is_available(mii_np)) { ++ res = 1; ++ goto err_put_node; ++ } ++ ++ dev->mii_bus = mdiobus_alloc(); ++ if (!dev->mii_bus) { ++ res = -ENOMEM; ++ goto err_cleanup_mdio; ++ } ++ ++ dev->mii_bus->priv = dev->ndev; ++ dev->mii_bus->parent = dev->ndev->dev.parent; ++ dev->mii_bus->name = "emac_mdio"; ++ dev->mii_bus->read = &emac_mii_bus_read; ++ dev->mii_bus->write = &emac_mii_bus_write; ++ dev->mii_bus->reset = &emac_mii_bus_reset; ++ snprintf(dev->mii_bus->id, MII_BUS_ID_SIZE, "%s", dev->mii_bus->name); ++ ++ res = of_mdiobus_register(dev->mii_bus, mii_np); ++ if (res) { ++ dev_err(&dev->ndev->dev, "cannot register MDIO bus %s (%d)", ++ dev->mii_bus->name, res); ++ goto err_cleanup_mdio; ++ } ++ of_node_put(mii_np); ++ return 0; ++ ++ err_cleanup_mdio: ++ emac_dt_phy_mdio_cleanup(dev); ++ err_put_node: ++ of_node_put(mii_np); ++ return res; ++} ++ ++static int emac_dt_phy_probe(struct emac_instance *dev, ++ struct device_node *phy_handle) ++{ ++ u32 phy_flags = 0; ++ int res; ++ ++ res = of_property_read_u32(phy_handle, "phy-flags", &phy_flags); ++ if (res < 0 && res != -EINVAL) ++ return res; ++ ++ dev->phy.def = kzalloc(sizeof(*dev->phy.def), GFP_KERNEL); ++ if (!dev->phy.def) ++ return -ENOMEM; ++ ++ dev->phy_dev = of_phy_connect(dev->ndev, phy_handle, ++ &emac_adjust_link, phy_flags, ++ PHY_INTERFACE_MODE_RGMII); ++ if (!dev->phy_dev) { ++ dev_err(&dev->ndev->dev, "failed to connect to PHY."); ++ kfree(dev->phy.dev); ++ dev->phy.dev = NULL; ++ return -ENODEV; ++ } ++ ++ dev->phy.def->phy_id = dev->phy_dev->drv->phy_id; ++ dev->phy.def->phy_id_mask = dev->phy_dev->drv->phy_id_mask; ++ dev->phy.def->name = dev->phy_dev->drv->name; ++ dev->phy.def->ops = &emac_dt_mdio_phy_ops; ++ dev->phy.features = dev->phy_dev->supported; ++ dev->phy.address = dev->phy_dev->addr; ++ dev->phy.mode = dev->phy_dev->interface; ++ return 0; ++} ++ ++static int emac_probe_dt_phy(struct emac_instance *dev) ++{ ++ struct device_node *np = dev->ofdev->dev.of_node; ++ struct device_node *phy_handle; ++ int res = 0; ++ ++ phy_handle = of_parse_phandle(np, "phy-handle", 0); ++ ++ if (phy_handle) { ++ res = emac_dt_mdio_probe(dev); ++ if (res) ++ goto out; ++ ++ res = emac_dt_phy_probe(dev, phy_handle); ++ if (res) { ++ emac_dt_phy_mdio_cleanup(dev); ++ goto out; ++ } ++ ++ return 1; ++ } ++ ++ out: ++ of_node_put(phy_handle); ++ /* if no phy device was specifie in the device tree, then we fallback ++ * to the old emac_phy.c probe code for compatibility reasons. ++ */ ++ return res; ++} ++ + static int emac_init_phy(struct emac_instance *dev) + { + struct device_node *np = dev->ofdev->dev.of_node; +@@ -2453,6 +2694,18 @@ static int emac_init_phy(struct emac_instance *dev) + + emac_configure(dev); + ++ if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII)) { ++ int res = emac_probe_dt_phy(dev); ++ ++ if (res == 1) ++ goto init_phy; ++ if (res < 0) ++ dev_err(&dev->ndev->dev, "failed to attach dt phy (%d).", ++ res); ++ ++ /* continue with old code */ ++ } ++ + if (dev->phy_address != 0xffffffff) + phy_map = ~(1 << dev->phy_address); + +@@ -2480,6 +2733,7 @@ static int emac_init_phy(struct emac_instance *dev) + return -ENXIO; + } + ++ init_phy: + /* Init PHY */ + if (dev->phy.def->ops->init) + dev->phy.def->ops->init(&dev->phy); +@@ -2898,6 +3152,8 @@ static int emac_probe(struct platform_device *ofdev) + /* I have a bad feeling about this ... */ + + err_detach_tah: ++ emac_dt_phy_mdio_cleanup(dev); ++ + if (emac_has_feature(dev, EMAC_FTR_HAS_TAH)) + tah_detach(dev->tah_dev, dev->tah_port); + err_detach_rgmii: +@@ -2948,6 +3204,11 @@ static int emac_remove(struct platform_device *ofdev) + if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII)) + zmii_detach(dev->zmii_dev, dev->zmii_port); + ++ if (dev->phy_dev) ++ phy_disconnect(dev->phy_dev); ++ ++ emac_dt_phy_mdio_cleanup(dev); ++ + busy_phy_map &= ~(1 << dev->phy.address); + DBG(dev, "busy_phy_map now %#x" NL, busy_phy_map); + +diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h +index 93ae114..0710a66 100644 +--- a/drivers/net/ethernet/ibm/emac/core.h ++++ b/drivers/net/ethernet/ibm/emac/core.h +@@ -199,6 +199,10 @@ struct emac_instance { + struct emac_instance *mdio_instance; + struct mutex mdio_lock; + ++ /* Device-tree based phy configuration */ ++ struct mii_bus *mii_bus; ++ struct phy_device *phy_dev; ++ + /* ZMII infos if any */ + u32 zmii_ph; + u32 zmii_port; +-- +2.1.4 + diff --git a/target/linux/apm821xx/patches-4.4/800-usb-dwc2-add-wndr4700-otg.patch b/target/linux/apm821xx/patches-4.4/800-usb-dwc2-add-wndr4700-otg.patch new file mode 100644 index 0000000000..24d267ef4a --- /dev/null +++ b/target/linux/apm821xx/patches-4.4/800-usb-dwc2-add-wndr4700-otg.patch @@ -0,0 +1,48 @@ +--- a/drivers/usb/dwc2/platform.c 2016-05-26 21:39:41.347838639 +0200 ++++ b/drivers/usb/dwc2/platform.c 2016-05-26 21:44:01.554907417 +0200 +@@ -115,6 +115,37 @@ static const struct dwc2_core_params par + .hibernation = -1, + }; + ++static const struct dwc2_core_params params_wndr4700 = { ++ .otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE, ++ .otg_ver = -1, ++ .dma_enable = -1, ++ .dma_desc_enable = -1, ++ .speed = -1, ++ .enable_dynamic_fifo = -1, ++ .en_multiple_tx_fifo = -1, ++ .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, ++ .phy_type = -1, ++ .phy_utmi_width = -1, ++ .phy_ulpi_ddr = -1, ++ .phy_ulpi_ext_vbus = -1, ++ .i2c_enable = -1, ++ .ulpi_fs_ls = -1, ++ .host_support_fs_ls_low_power = -1, ++ .host_ls_low_power_phy_clk = -1, ++ .ts_dline = -1, ++ .reload_ctl = -1, ++ .ahbcfg = GAHBCFG_HBSTLEN_INCR16 << ++ GAHBCFG_HBSTLEN_SHIFT, ++ .uframe_sched = -1, ++ .external_id_pin_ctl = -1, ++ .hibernation = -1, ++}; ++ + static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) + { + struct platform_device *pdev = to_platform_device(hsotg->dev); +@@ -309,6 +340,7 @@ static int dwc2_driver_remove(struct pla + static const struct of_device_id dwc2_of_match_table[] = { + { .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 }, + { .compatible = "rockchip,rk3066-usb", .data = ¶ms_rk3066 }, ++ { .compatible = "netgear,wndr4700-usb", .data = ¶ms_wndr4700 }, + { .compatible = "snps,dwc2", .data = NULL }, + { .compatible = "samsung,s3c6400-hsotg", .data = NULL}, + {}, diff --git a/target/linux/apm821xx/patches-4.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch b/target/linux/apm821xx/patches-4.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch new file mode 100644 index 0000000000..e56e95576d --- /dev/null +++ b/target/linux/apm821xx/patches-4.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch @@ -0,0 +1,550 @@ +From 419992bae5aaa4e06402e0b7c79fcf7bcb6b4764 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter <chunkeey@googlemail.com> +Date: Thu, 2 Jun 2016 00:48:46 +0200 +Subject: [PATCH] usb: xhci: add firmware loader for uPD720201 and uPD720202 + w/o ROM + +This patch adds a firmware loader for the uPD720201K8-711-BAC-A +and uPD720202K8-711-BAA-A variant. Both of these chips are listed +in Renesas' R19UH0078EJ0500 Rev.5.00 "User's Manual: Hardware" as +devices which need the firmware loader on page 2 in order to +work as they "do not support the External ROM". + +The "Firmware Download Sequence" is describe in chapter +"7.1 FW Download Interface" R19UH0078EJ0500 Rev.5.00 page 131. + +The firmware "K2013080.mem" is available from a USB3.0 Host to +PCIe Adapter (PP2U-E card) "Firmware download" archive. An +alternative version can be sourced from Netgear's WNDR4700 GPL +archives. + +The release notes of the PP2U-E's "Firmware Download" ver 2.0.1.3 +(2012-06-15) state that the firmware is for the following devices: + - uPD720201 ES 2.0 sample whose revision ID is 2. + - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. + - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. + +If someone from Renesas is listening: It would be great, if these +firmwares could be added to linux-firmware.git. + +Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com> +Signed-off-by: Christian Lamparter <chunkeey@googlemail.com> +--- + drivers/usb/host/xhci-pci.c | 492 ++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 492 insertions(+) + +diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c +index 48672fa..328c891 100644 +--- a/drivers/usb/host/xhci-pci.c ++++ b/drivers/usb/host/xhci-pci.c +@@ -24,6 +24,8 @@ + #include <linux/slab.h> + #include <linux/module.h> + #include <linux/acpi.h> ++#include <linux/firmware.h> ++#include <asm/unaligned.h> + + #include "xhci.h" + #include "xhci-trace.h" +@@ -207,6 +209,458 @@ static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) + static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { } + #endif /* CONFIG_ACPI */ + ++static const struct renesas_fw_entry { ++ const char *firmware_name; ++ u16 device; ++ u8 revision; ++ u16 expected_version; ++} renesas_fw_table[] = { ++ /* ++ * Only the uPD720201K8-711-BAC-A or uPD720202K8-711-BAA-A ++ * are listed in R19UH0078EJ0500 Rev.5.00 as devices which ++ * need the software loader. ++ * ++ * PP2U/ReleaseNote_USB3-201-202-FW.txt: ++ * ++ * Note: This firmware is for the following devices. ++ * - uPD720201 ES 2.0 sample whose revision ID is 2. ++ * - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3. ++ * - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2. ++ */ ++ { "K2013080.mem", 0x0014, 0x02, 0x2013 }, ++ { "K2013080.mem", 0x0014, 0x03, 0x2013 }, ++ { "K2013080.mem", 0x0015, 0x02, 0x2013 }, ++}; ++ ++static const struct renesas_fw_entry *renesas_needs_fw_dl(struct pci_dev *dev) ++{ ++ const struct renesas_fw_entry *entry; ++ size_t i; ++ ++ /* This loader will only work with a RENESAS device. */ ++ if (!(dev->vendor == PCI_VENDOR_ID_RENESAS)) ++ return NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(renesas_fw_table); i++) { ++ entry = &renesas_fw_table[i]; ++ if (entry->device == dev->device && ++ entry->revision == dev->revision) ++ return entry; ++ } ++ ++ return NULL; ++} ++ ++static int renesas_fw_download_image(struct pci_dev *dev, ++ const u32 *fw, ++ size_t step) ++{ ++ size_t i; ++ int err; ++ u8 fw_status; ++ bool data0_or_data1; ++ ++ /* ++ * The hardware does alternate between two 32-bit pages. ++ * (This is because each row of the firmware is 8 bytes). ++ * ++ * for even steps we use DATA0, for odd steps DATA1. ++ */ ++ data0_or_data1 = (step & 1) == 1; ++ ++ /* step+1. Read "Set DATAX" and confirm it is cleared. */ ++ for (i = 0; i < 10000; i++) { ++ err = pci_read_config_byte(dev, 0xF5, &fw_status); ++ if (err) ++ return pcibios_err_to_errno(err); ++ if (!(fw_status & BIT(data0_or_data1))) ++ break; ++ ++ udelay(1); ++ } ++ if (i == 10000) ++ return -ETIMEDOUT; ++ ++ /* ++ * step+2. Write FW data to "DATAX". ++ * "LSB is left" => force little endian ++ */ ++ err = pci_write_config_dword(dev, data0_or_data1 ? 0xFC : 0xF8, ++ (__force u32) cpu_to_le32(fw[step])); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ udelay(100); ++ ++ /* step+3. Set "Set DATAX". */ ++ err = pci_write_config_byte(dev, 0xF5, BIT(data0_or_data1)); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ return 0; ++} ++ ++static int renesas_fw_verify(struct pci_dev *dev, ++ const void *fw_data, ++ size_t length) ++{ ++ const struct renesas_fw_entry *entry = renesas_needs_fw_dl(dev); ++ u16 fw_version_pointer; ++ u16 fw_version; ++ ++ if (!entry) ++ return -EINVAL; ++ ++ /* ++ * The Firmware's Data Format is describe in ++ * "6.3 Data Format" R19UH0078EJ0500 Rev.5.00 page 124 ++ */ ++ ++ /* "Each row is 8 bytes". => firmware size must be a multiple of 8. */ ++ if (length % 8 != 0) { ++ dev_err(&dev->dev, "firmware size is not a multipe of 8."); ++ return -EINVAL; ++ } ++ ++ /* ++ * The bootrom chips of the big brother have sizes up to 64k, let's ++ * assume that's the biggest the firmware can get. ++ */ ++ if (length < 0x1000 || length >= 0x10000) { ++ dev_err(&dev->dev, "firmware is size %zd is not (4k - 64k).", ++ length); ++ return -EINVAL; ++ } ++ ++ /* The First 2 bytes are fixed value (55aa). "LSB on Left" */ ++ if (get_unaligned_le16(fw_data) != 0x55aa) { ++ dev_err(&dev->dev, "no valid firmware header found."); ++ return -EINVAL; ++ } ++ ++ /* verify the firmware version position and print it. */ ++ fw_version_pointer = get_unaligned_le16(fw_data + 4); ++ if (fw_version_pointer + 2 >= length) { ++ dev_err(&dev->dev, "firmware version pointer is outside of the firmware image."); ++ return -EINVAL; ++ } ++ ++ fw_version = get_unaligned_le16(fw_data + fw_version_pointer); ++ dev_dbg(&dev->dev, "got firmware version: %02x.", fw_version); ++ ++ if (fw_version != entry->expected_version) { ++ dev_err(&dev->dev, "firmware version mismatch, expected version: %02x.", ++ entry->expected_version); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int renesas_fw_check_running(struct pci_dev *pdev) ++{ ++ int err; ++ u8 fw_state; ++ ++ /* ++ * Test if the device is actually needing the firmware. As most ++ * BIOSes will initialize the device for us. If the device is ++ * initialized. ++ */ ++ err = pci_read_config_byte(pdev, 0xF4, &fw_state); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ /* ++ * Check if "FW Download Lock" is locked. If it is and the FW is ++ * ready we can simply continue. If the FW is not ready, we have ++ * to give up. ++ */ ++ if (fw_state & BIT(1)) { ++ dev_dbg(&pdev->dev, "FW Download Lock is engaged."); ++ ++ if (fw_state & BIT(4)) ++ return 0; ++ ++ dev_err(&pdev->dev, "FW Download Lock is set and FW is not ready. Giving Up."); ++ return -EIO; ++ } ++ ++ /* ++ * Check if "FW Download Enable" is set. If someone (us?) tampered ++ * with it and it can't be resetted, we have to give up too... and ++ * ask for a forgiveness and a reboot. ++ */ ++ if (fw_state & BIT(0)) { ++ dev_err(&pdev->dev, "FW Download Enable is stale. Giving Up (poweroff/reboot needed)."); ++ return -EIO; ++ } ++ ++ /* Otherwise, Check the "Result Code" Bits (6:4) and act accordingly */ ++ switch ((fw_state & 0x70)) { ++ case 0: /* No result yet */ ++ dev_dbg(&pdev->dev, "FW is not ready/loaded yet."); ++ ++ /* tell the caller, that this device needs the firmware. */ ++ return 1; ++ ++ case BIT(4): /* Success, device should be working. */ ++ dev_dbg(&pdev->dev, "FW is ready."); ++ return 0; ++ ++ case BIT(5): /* Error State */ ++ dev_err(&pdev->dev, "hardware is in an error state. Giving up (poweroff/reboot needed)."); ++ return -ENODEV; ++ ++ default: /* All other states are marked as "Reserved states" */ ++ dev_err(&pdev->dev, "hardware is in an invalid state %x. Giving up (poweroff/reboot needed).", ++ (fw_state & 0x70) >> 4); ++ return -EINVAL; ++ } ++} ++ ++static int renesas_hw_check_run_stop_busy(struct pci_dev *pdev) ++{ ++#if 0 ++ u32 val; ++ ++ /* ++ * 7.1.3 Note 3: "... must not set 'FW Download Enable' when ++ * 'RUN/STOP' of USBCMD Register is set" ++ */ ++ val = readl(hcd->regs + 0x20); ++ if (val & BIT(0)) { ++ dev_err(&pdev->dev, "hardware is busy and can't receive a FW."); ++ return -EBUSY; ++ } ++#endif ++ return 0; ++} ++ ++static int renesas_fw_download(struct pci_dev *pdev, ++ const struct firmware *fw, unsigned int retry_counter) ++{ ++ const u32 *fw_data = (const u32 *) fw->data; ++ size_t i; ++ int err; ++ u8 fw_status; ++ ++ /* ++ * For more information and the big picture: please look at the ++ * "Firmware Download Sequence" in "7.1 FW Download Interface" ++ * of R19UH0078EJ0500 Rev.5.00 page 131 ++ */ ++ err = renesas_hw_check_run_stop_busy(pdev); ++ if (err) ++ return err; ++ ++ /* ++ * 0. Set "FW Download Enable" bit in the ++ * "FW Download Control & Status Register" at 0xF4 ++ */ ++ err = pci_write_config_byte(pdev, 0xF4, BIT(0)); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ /* 1 - 10 follow one step after the other. */ ++ for (i = 0; i < fw->size / 4; i++) { ++ err = renesas_fw_download_image(pdev, fw_data, i); ++ if (err) { ++ dev_err(&pdev->dev, "Firmware Download Step %zd failed at position %zd bytes with (%d).", ++ i, i * 4, err); ++ return err; ++ } ++ } ++ ++ /* ++ * This sequence continues until the last data is written to ++ * "DATA0" or "DATA1". Naturally, we wait until "SET DATA0/1" ++ * is cleared by the hardware beforehand. ++ */ ++ for (i = 0; i < 10000; i++) { ++ err = pci_read_config_byte(pdev, 0xF5, &fw_status); ++ if (err) ++ return pcibios_err_to_errno(err); ++ if (!(fw_status & (BIT(0) | BIT(1)))) ++ break; ++ ++ udelay(1); ++ } ++ if (i == 10000) ++ dev_warn(&pdev->dev, "Final Firmware Download step timed out."); ++ ++ /* ++ * 11. After finishing writing the last data of FW, the ++ * System Software must clear "FW Download Enable" ++ */ ++ err = pci_write_config_byte(pdev, 0xF4, 0); ++ if (err) ++ return pcibios_err_to_errno(err); ++ ++ /* 12. Read "Result Code" and confirm it is good. */ ++ for (i = 0; i < 10000; i++) { ++ err = pci_read_config_byte(pdev, 0xF4, &fw_status); ++ if (err) ++ return pcibios_err_to_errno(err); ++ if (fw_status & BIT(4)) ++ break; ++ ++ udelay(1); ++ } ++ if (i == 10000) { ++ /* Timed out / Error - let's see if we can fix this */ ++ err = renesas_fw_check_running(pdev); ++ switch (err) { ++ case 0: /* ++ * we shouldn't end up here. ++ * maybe it took a little bit longer. ++ * But all should be well? ++ */ ++ break; ++ ++ case 1: /* (No result yet? - we can try to retry) */ ++ if (retry_counter < 10) { ++ retry_counter++; ++ dev_warn(&pdev->dev, "Retry Firmware download: %d try.", ++ retry_counter); ++ return renesas_fw_download(pdev, fw, ++ retry_counter); ++ } ++ return -ETIMEDOUT; ++ ++ default: ++ return err; ++ } ++ } ++ /* ++ * Optional last step: Engage Firmware Lock ++ * ++ * err = pci_write_config_byte(pdev, 0xF4, BIT(2)); ++ * if (err) ++ * return pcibios_err_to_errno(err); ++ */ ++ ++ return 0; ++} ++ ++struct renesas_fw_ctx { ++ struct pci_dev *pdev; ++ const struct pci_device_id *id; ++ bool resume; ++}; ++ ++static int xhci_pci_probe(struct pci_dev *pdev, ++ const struct pci_device_id *id); ++ ++static void renesas_fw_callback(const struct firmware *fw, ++ void *context) ++{ ++ struct renesas_fw_ctx *ctx = context; ++ struct pci_dev *pdev = ctx->pdev; ++ struct device *parent = pdev->dev.parent; ++ int err = -ENOENT; ++ ++ if (fw) { ++ err = renesas_fw_verify(pdev, fw->data, fw->size); ++ if (!err) { ++ err = renesas_fw_download(pdev, fw, 0); ++ release_firmware(fw); ++ if (!err) { ++ if (ctx->resume) ++ return; ++ ++ err = xhci_pci_probe(pdev, ctx->id); ++ if (!err) { ++ /* everything worked */ ++ devm_kfree(&pdev->dev, ctx); ++ return; ++ } ++ ++ /* in case of an error - fall through */ ++ } else { ++ dev_err(&pdev->dev, "firmware failed to download (%d).", ++ err); ++ } ++ } ++ } else { ++ dev_err(&pdev->dev, "firmware failed to load (%d).", err); ++ } ++ ++ dev_info(&pdev->dev, "Unloading driver"); ++ ++ if (parent) ++ device_lock(parent); ++ ++ device_release_driver(&pdev->dev); ++ ++ if (parent) ++ device_unlock(parent); ++ ++ pci_dev_put(pdev); ++} ++ ++static int renesas_fw_alive_check(struct pci_dev *pdev) ++{ ++ const struct renesas_fw_entry *entry; ++ int err; ++ ++ /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ ++ entry = renesas_needs_fw_dl(pdev); ++ if (!entry) ++ return 0; ++ ++ err = renesas_fw_check_running(pdev); ++ /* Also go ahead, if the firmware is running */ ++ if (err == 0) ++ return 0; ++ ++ /* At this point, we can be sure that the FW isn't ready. */ ++ return err; ++} ++ ++static int renesas_fw_download_to_hw(struct pci_dev *pdev, ++ const struct pci_device_id *id, ++ bool do_resume) ++{ ++ const struct renesas_fw_entry *entry; ++ struct renesas_fw_ctx *ctx; ++ int err; ++ ++ /* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */ ++ entry = renesas_needs_fw_dl(pdev); ++ if (!entry) ++ return 0; ++ ++ err = renesas_fw_check_running(pdev); ++ /* Continue ahead, if the firmware is already running. */ ++ if (err == 0) ++ return 0; ++ ++ if (err != 1) ++ return err; ++ ++ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); ++ if (!ctx) ++ return -ENOMEM; ++ ctx->pdev = pdev; ++ ctx->resume = do_resume; ++ ctx->id = id; ++ ++ pci_dev_get(pdev); ++ err = request_firmware_nowait(THIS_MODULE, 1, entry->firmware_name, ++ &pdev->dev, GFP_KERNEL, ctx, renesas_fw_callback); ++ if (err) { ++ pci_dev_put(pdev); ++ return err; ++ } ++ ++ /* ++ * The renesas_fw_callback() callback will continue the probe ++ * process, once it aquires the firmware. ++ */ ++ return 1; ++} ++ + /* called during probe() after chip reset completes */ + static int xhci_pci_setup(struct usb_hcd *hcd) + { +@@ -246,6 +700,22 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) + struct hc_driver *driver; + struct usb_hcd *hcd; + ++ /* ++ * Check if this device is a RENESAS uPD720201/2 device. ++ * Otherwise, we can continue with xhci_pci_probe as usual. ++ */ ++ retval = renesas_fw_download_to_hw(dev, id, false); ++ switch (retval) { ++ case 0: ++ break; ++ ++ case 1: /* let it load the firmware and recontinue the probe. */ ++ return 0; ++ ++ default: ++ return retval; ++ }; ++ + driver = (struct hc_driver *)id->driver_data; + + /* Prevent runtime suspending between USB-2 and USB-3 initialization */ +@@ -303,6 +773,16 @@ static void xhci_pci_remove(struct pci_dev *dev) + { + struct xhci_hcd *xhci; + ++ if (renesas_fw_alive_check(dev)) { ++ /* ++ * bail out early, if this was a renesas device w/o FW. ++ * Else we might hit the NMI watchdog in xhci_handsake ++ * during xhci_reset as part of the driver's unloading. ++ * which we forced in the renesas_fw_callback(). ++ */ ++ return; ++ } ++ + xhci = hcd_to_xhci(pci_get_drvdata(dev)); + xhci->xhc_state |= XHCI_STATE_REMOVING; + if (xhci->shared_hcd) { +-- +2.8.1 + diff --git a/target/linux/apm821xx/patches-4.4/802-usb-xhci-force-msi-renesas-xhci.patch b/target/linux/apm821xx/patches-4.4/802-usb-xhci-force-msi-renesas-xhci.patch new file mode 100644 index 0000000000..a09e9dc41c --- /dev/null +++ b/target/linux/apm821xx/patches-4.4/802-usb-xhci-force-msi-renesas-xhci.patch @@ -0,0 +1,57 @@ +From a0dc613140bab907a3d5787a7ae7b0638bf674d0 Mon Sep 17 00:00:00 2001 +From: Christian Lamparter <chunkeey@gmail.com> +Date: Thu, 23 Jun 2016 20:28:20 +0200 +Subject: [PATCH] usb: xhci: force MSI for uPD720201 and + uPD720202 + +The APM82181 does not support MSI-X. When probed, it will +produce a noisy warning. + +--- + drivers/usb/host/pci-quirks.c | 362 ++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 362 insertions(+) + +diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c +index 1c4d89e..555bd3f 100644 +--- a/drivers/usb/host/xhci-pci.c ++++ b/drivers/usb/host/xhci-pci.c +@@ -172,7 +172,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) + } + if (pdev->vendor == PCI_VENDOR_ID_RENESAS && + pdev->device == 0x0015) +- xhci->quirks |= XHCI_RESET_ON_RESUME; ++ xhci->quirks |= XHCI_RESET_ON_RESUME | XHCI_FORCE_MSI; + if (pdev->vendor == PCI_VENDOR_ID_VIA) + xhci->quirks |= XHCI_RESET_ON_RESUME; + +diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c +index 9e71c96..27cfcb9 100644 +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -389,10 +389,14 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) + free_irq(hcd->irq, hcd); + hcd->irq = 0; + +- ret = xhci_setup_msix(xhci); +- if (ret) +- /* fall back to msi*/ ++ if (xhci->quirks & XHCI_FORCE_MSI) { + ret = xhci_setup_msi(xhci); ++ } else { ++ ret = xhci_setup_msix(xhci); ++ if (ret) ++ /* fall back to msi*/ ++ ret = xhci_setup_msi(xhci); ++ } + + if (!ret) + /* hcd->irq is 0, we have MSI */ +diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h +index 6b085aa..514dc3f 100644 +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -1649,3 +1649,4 @@ struct xhci_hcd { + #define XHCI_BROKEN_STREAMS (1 << 19) + #define XHCI_PME_STUCK_QUIRK (1 << 20) ++#define XHCI_FORCE_MSI (1 << 24) + unsigned int num_active_eps; |