summaryrefslogtreecommitdiffstats
path: root/target/linux/apm821xx/patches-4.4
diff options
context:
space:
mode:
authorChristian Lamparter <chunkeey@googlemail.com>2016-07-20 15:44:57 +0200
committerFelix Fietkau <nbd@nbd.name>2016-07-22 09:48:12 +0200
commit9e0fd1b52ad1f805a308bf6a5a13236f352fd962 (patch)
treec01eaf08bb04ea37b07070469e8077c402a5d111 /target/linux/apm821xx/patches-4.4
parent47eeb9f857044de6e79c79577a1a7d6a57ac5046 (diff)
downloadmaster-31e0f0ae-9e0fd1b52ad1f805a308bf6a5a13236f352fd962.tar.gz
master-31e0f0ae-9e0fd1b52ad1f805a308bf6a5a13236f352fd962.tar.bz2
master-31e0f0ae-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/apm821xx/patches-4.4')
-rw-r--r--target/linux/apm821xx/patches-4.4/202-add-netgear-wndr4700-support.patch32
-rw-r--r--target/linux/apm821xx/patches-4.4/301-fix-memory-map-wndr4700.patch14
-rw-r--r--target/linux/apm821xx/patches-4.4/702-powerpc_ibm_phy_add_dt_parser.patch335
-rw-r--r--target/linux/apm821xx/patches-4.4/800-usb-dwc2-add-wndr4700-otg.patch48
-rw-r--r--target/linux/apm821xx/patches-4.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch550
-rw-r--r--target/linux/apm821xx/patches-4.4/802-usb-xhci-force-msi-renesas-xhci.patch57
6 files changed, 1036 insertions, 0 deletions
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 = &params_bcm2835 },
+ { .compatible = "rockchip,rk3066-usb", .data = &params_rk3066 },
++ { .compatible = "netgear,wndr4700-usb", .data = &params_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;