diff --git a/debian/changelog b/debian/changelog index 5176df6d3..842abb79d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,7 @@ linux (3.14.1-1~exp2) UNRELEASED; urgency=medium [ Ian Campbell ] * [armhf] Drop suffix from kernel udeb. + * [armhf] Backport sunxi AHCI and GMAC drivers from v3.15-rc1 [ Ben Hutchings ] * [x86] Enable X86_INTEL_LPSS (Closes: #745331) diff --git a/debian/config/armhf/config.armmp b/debian/config/armhf/config.armmp index 7a37df077..c45b1e19d 100644 --- a/debian/config/armhf/config.armmp +++ b/debian/config/armhf/config.armmp @@ -118,6 +118,7 @@ CONFIG_OMAP_PM_NOOP=y ## CONFIG_SATA_AHCI_PLATFORM=m CONFIG_AHCI_IMX=m +CONFIG_AHCI_SUNXI=m CONFIG_SATA_HIGHBANK=m CONFIG_PATA_IMX=m CONFIG_PATA_PLATFORM=m @@ -431,6 +432,11 @@ CONFIG_SMC91X=m CONFIG_SMC911X=m CONFIG_SMSC911X=m +## +## file: drivers/net/ethernet/stmicro/stmmac/Kconfig +## +CONFIG_STMMAC_ETH=m + ## ## file: drivers/net/ethernet/ti/Kconfig ## diff --git a/debian/installer/armhf/modules/armhf-armmp/nic-modules b/debian/installer/armhf/modules/armhf-armmp/nic-modules index 817104055..3b3f35ba7 100644 --- a/debian/installer/armhf/modules/armhf-armmp/nic-modules +++ b/debian/installer/armhf/modules/armhf-armmp/nic-modules @@ -2,5 +2,6 @@ mvneta mvmdio smsc911x +stmmac sun4i-emac xgmac diff --git a/debian/installer/armhf/modules/armhf-armmp/sata-modules b/debian/installer/armhf/modules/armhf-armmp/sata-modules index f70cd04a0..cc7f38160 100644 --- a/debian/installer/armhf/modules/armhf-armmp/sata-modules +++ b/debian/installer/armhf/modules/armhf-armmp/sata-modules @@ -1,5 +1,6 @@ #include ahci_platform ahci_imx +ahci_sunxi sata_highbank diff --git a/debian/patches/features/all/ARM-sunxi-ahci-and-gmac.patch b/debian/patches/features/all/ARM-sunxi-ahci-and-gmac.patch new file mode 100644 index 000000000..204defd81 --- /dev/null +++ b/debian/patches/features/all/ARM-sunxi-ahci-and-gmac.patch @@ -0,0 +1,2547 @@ +commit 15d6fcb8b18bcd251139f2f557067411f4c9500a +Author: Hans de Goede +Date: Sat Mar 1 20:26:20 2014 +0100 + + ARM: sunxi: dt: Add sunxi-common-regulators include file + + Most sunxi boards with a sata connector also have a gpio controlled connector + for sata target power and almost all sunxi boards have a gpio controlled vbus + for usb1 and usb2. + + This commit adds an include file for the regulators representing these + supplies, avoiding the need to copy and paste the regulator code to allmost + all sunxi board dts files. + + Signed-off-by: Hans de Goede + Signed-off-by: Maxime Ripard + (cherry picked from commit 25ff22a532f1124397ece327e675aabfef4fa7b4) + +commit cef54381f3fe3b9968dc0944eda347b734ec0aeb +Author: Emilio López +Date: Wed Mar 19 15:19:32 2014 -0300 + + clk: sunxi: fix some calculations + + Some divisor calculations were misrounded, causing higher than requested + rates on some clocks. Fix them up using DIV_ROUND_UP, and replace one + homebrew instance of it as well with the right macro. + + Reported-by: Boris BREZILLON + Signed-off-by: Emilio López + Signed-off-by: Mike Turquette + (cherry picked from commit 2226013972da1ec0a2aeb13a684180bb2b50e0f3) + +commit f1096cc726d521c62a36ef4cfd7d31845d2a3f01 +Author: Chen-Yu Tsai +Date: Mon Feb 10 18:35:47 2014 +0800 + + clk: sunxi: Add Allwinner A20/A31 GMAC clock unit + + The Allwinner A20/A31 clock module controls the transmit clock source + and interface type of the GMAC ethernet controller. Model this as + a single clock for GMAC drivers to use. + + Signed-off-by: Chen-Yu Tsai + Acked-by: Maxime Ripard + Signed-off-by: Emilio López + (cherry picked from commit e4c6d6c11bee5ff11feb837a0a76103b3eba252f) + + Conflicts: + Documentation/devicetree/bindings/clock/sunxi.txt + +commit 5732a8aedf1e58bf154942d35d6675beead2cc58 +Author: Maxime Ripard +Date: Wed Feb 5 14:05:03 2014 +0100 + + clk: sunxi: Add support for PLL6 on the A31 + + The A31 has a slightly different PLL6 clock. Add support for this new clock in + our driver. + + Signed-off-by: Maxime Ripard + Signed-off-by: Emilio López + (cherry picked from commit 92ef67c53ad92487c3c8de75e7940384c2edd793) + +commit e50895e6c5f5a0677f6a65ccea8934897604c7da +Author: Chen-Yu Tsai +Date: Mon Feb 3 09:51:40 2014 +0800 + + clk: sunxi: get divs parent clock name from parent factor clock + + Divs clocks consist of a parent factor clock with multiple outputs, + and seperate clocks for each output. Get the name of the parent + clock from the parent factor clock, instead of the DT node name. + + Signed-off-by: Chen-Yu Tsai + Acked-by: Maxime Ripard + Acked-by: Mike Turquette + Signed-off-by: Emilio López + (cherry picked from commit 97e36b3ce3106988b82e1ca53b1d1c872bde855a) + +commit 1daa0121a7be5f38adcdfbb6e6bf42d9d6df5737 +Author: Chen-Yu Tsai +Date: Mon Feb 3 09:51:39 2014 +0800 + + clk: sunxi: add names for pll5, pll6 parent clocks to factors_data + + Some factor clocks, such as the parent clock of pll5 and pll6, have + multiple output names. Add the corresponding names to factors_data + tied to compatible string. + + Signed-off-by: Chen-Yu Tsai + Acked-by: Maxime Ripard + Acked-by: Mike Turquette + Signed-off-by: Emilio López + (cherry picked from commit 667f542db542fddc62d1299b17451d7cae84f6e1) + +commit 66d96749ad78255d0903f67497fdfe5bd2161bf3 +Author: Chen-Yu Tsai +Date: Mon Feb 3 09:51:37 2014 +0800 + + clk: sunxi: add clock-output-names dt property support + + sunxi clock drivers use dt node name as clock name, but clock + nodes should be named clk@X, so the names would be the same. + Let the drivers read clock names from dt clock-output-names + property. + + Signed-off-by: Chen-Yu Tsai + Acked-by: Maxime Ripard + Acked-by: Mike Turquette + Signed-off-by: Emilio López + (cherry picked from commit f64111ebaf6776558f0e60d0ea8c7a9c579b9436) + +commit ba9d3543becbab5cb2582e81c4ee15afc19f720c +Author: Hans de Goede +Date: Sat Mar 1 20:26:22 2014 +0100 + + ARM: sun7i: dt: Add ahci / sata support + + This patch adds sunxi sata support to A20 boards that have such a connector. + Some boards also feature a regulator via a GPIO and support for this is also + added. + + Signed-off-by: Olliver Schinagl + Signed-off-by: Hans de Goede + Signed-off-by: Maxime Ripard + (cherry picked from commit 902febf9ea856dc9a9376bcdc70dfaa9b3ad5a74) + + Conflicts: + arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts + arch/arm/boot/dts/sun7i-a20.dtsi + +commit 089eb5838989a6f0cab56cb259637d8ff145f3bc +Author: Oliver Schinagl +Date: Sat Mar 1 20:26:21 2014 +0100 + + ARM: sun4i: dt: Add ahci / sata support + + This patch adds sunxi sata support to A10 boards that have such a connector. + Some boards also feature a regulator via a GPIO and support for this is also + added. + + Signed-off-by: Olliver Schinagl + Signed-off-by: Hans de Goede + Signed-off-by: Maxime Ripard + (cherry picked from commit 248bd1e228eb9cc7ff9577d45b5d1b6d52c43cd9) + + Conflicts: + arch/arm/boot/dts/sun4i-a10.dtsi + +commit 7a25c91712808d75e7e504487bb94620f12ce0b8 +Author: Marc Zyngier +Date: Tue Feb 18 14:04:44 2014 +0000 + + ARM: sun7i: add arch timer node + + The Allwinner A20 SoC is built around a pair of Cortex-A7 cores, + which have the usual generic timers. Report this in the DT. + + Signed-off-by: Marc Zyngier + Signed-off-by: Maxime Ripard + (cherry picked from commit 7902763e4a4d04a96406447f935a8f676e73e0ce) + +commit 7ffc006a340f7f2dcd4949462e3aa7b22f59ef62 +Author: Chen-Yu Tsai +Date: Mon Feb 10 18:35:54 2014 +0800 + + ARM: dts: sun7i: Add ethernet alias for GMAC + + All Allwinner A20 boards we support can only use either EMAC or GMAC, + as they share the same pins. As we have switched all supported to + GMAC, we should alias GMAC (the active controller) as ethernet0, + so u-boot will insert the MAC address for the correct controller. + + Signed-off-by: Chen-Yu Tsai + Signed-off-by: Maxime Ripard + (cherry picked from commit 18428f7782920171fbcf0ccedcf6a146e7cc2e6f) + +commit 5ccc8c209394c5217cf1e4195997d9f936f5bd63 +Author: Maxime Ripard +Date: Thu Jan 2 22:05:04 2014 +0100 + + ARM: sun7i: Add missing serial aliases + + Some UART aliases have been defined, but not all of them. Add the remaining + ones to be consistent and to ease the parsing of the DT by the bootloaders. + + Signed-off-by: Maxime Ripard + (cherry picked from commit 4566b4beafe4582488907b84cb04e6c0efba384a) + +commit ee1689f3993061de7757eab84e95c70f497c6886 +Author: Maxime Ripard +Date: Thu Jan 2 22:05:04 2014 +0100 + + ARM: sun6i: Add missing serial aliases + + Some UART aliases have been defined, but not all of them. Add the remaining + ones to be consistent and to ease the parsing of the DT by the bootloaders. + + Signed-off-by: Maxime Ripard + (cherry picked from commit 54428d4025c2ce1f26bd6f677edaa7170a974787) + +commit d8183f5ad12bd23962b89063387899abdc717648 +Author: Maxime Ripard +Date: Thu Jan 2 22:05:04 2014 +0100 + + ARM: sun5i: a10s: Add missing serial aliases + + Some UART aliases have been defined, but not all of them. Add the remaining + ones to be consistent and to ease the parsing of the DT by the bootloaders. + + Signed-off-by: Maxime Ripard + (cherry picked from commit 4dd4065f80ccd09e336388e7ff8e3a4a1dcf8e83) + +commit 5dc99b5e04e8048d550634ea56bf699e02c147f1 +Author: Maxime Ripard +Date: Thu Jan 2 22:05:04 2014 +0100 + + ARM: sun4i: a10: Add missing serial aliases + + Some UART aliases have been defined, but not all of them. Add the remaining + ones to be consistent and to ease the parsing of the DT by the bootloaders. + + Signed-off-by: Maxime Ripard + (cherry picked from commit 143b13d6e603abb0dc374cf31ec22f5dd28500eb) + +commit c67f298a228e6614a5394f831d17ad5f8a29d186 +Author: Chen-Yu Tsai +Date: Mon Feb 10 18:35:53 2014 +0800 + + ARM: dts: sun7i: a20-olinuxino-micro: Enable GMAC instead of EMAC + + GMAC has better performance and fewer hardware issues. + Use the GMAC in MII mode for ethernet instead of the EMAC. + + Signed-off-by: Chen-Yu Tsai + Signed-off-by: Maxime Ripard + (cherry picked from commit 7164318297f7f2567d4ca44a5e4affc910b1b979) + +commit 08f1be55ac881ca708f0d21d4ab5244cc99dc3ed +Author: Chen-Yu Tsai +Date: Mon Feb 10 18:35:52 2014 +0800 + + ARM: dts: sun7i: cubieboard2: Enable GMAC instead of EMAC + + GMAC has better performance and fewer hardware issues. + Use the GMAC in MII mode for ethernet instead of the EMAC. + + Signed-off-by: Chen-Yu Tsai + Signed-off-by: Maxime Ripard + (cherry picked from commit 5e71892f01d07a68bab0529f64cb8cd06bf94fd4) + +commit fb227cb85a9fb05925b7d2dbfc91ee0469523c05 +Author: Chen-Yu Tsai +Date: Mon Feb 10 18:35:51 2014 +0800 + + ARM: dts: sun7i: cubietruck: Enable the GMAC + + The CubieTruck uses the GMAC with an RGMII phy. + + Signed-off-by: Chen-Yu Tsai + Signed-off-by: Maxime Ripard + (cherry picked from commit 67073d97672d03cc29ba252235e31c7e54ea9b62) + +commit 7dc7a78b507297af1ced79170c4b5f9a3b559c76 +Author: Chen-Yu Tsai +Date: Mon Feb 10 18:35:50 2014 +0800 + + ARM: dts: sun7i: Add pin muxing options for the GMAC + + The A20 has EMAC and GMAC muxed on the same pins. + Add pin sets with gmac function for MII and RGMII mode to the DTSI. + + Signed-off-by: Chen-Yu Tsai + Signed-off-by: Maxime Ripard + (cherry picked from commit 129ccbcd6fc704f3a0df892240f3aeb463d461f5) + +commit e7609b19b3f95a7415acb249b5ede9ae5b74caa0 +Author: Chen-Yu Tsai +Date: Mon Feb 10 18:35:49 2014 +0800 + + ARM: dts: sun7i: Add GMAC controller node to sun7i DTSI + + Signed-off-by: Chen-Yu Tsai + Signed-off-by: Maxime Ripard + (cherry picked from commit c40b8d5858f6396b11d7f859a76bef9b82a65743) + +commit 9fd91d26fb223ca444d7232aae39bc402f7c0c67 +Author: Chen-Yu Tsai +Date: Mon Feb 10 18:35:48 2014 +0800 + + ARM: dts: sun7i: Add GMAC clock node to sun7i DTSI + + The GMAC uses 1 of 2 sources for its transmit clock, depending on the + PHY interface mode. Add both sources as dummy clocks, and as parents + to the GMAC clock node. + + Signed-off-by: Chen-Yu Tsai + Signed-off-by: Maxime Ripard + (cherry picked from commit daed5a8163dcc9ff63e4fde8f7e8ce885ba4c34b) + +commit 732a0e04187ae4e216b9b3f2add1e8af1491b9f2 +Author: Hans de Goede +Date: Sun Feb 23 12:52:41 2014 +0100 + + ahci_sunxi: Use msleep instead of mdelay + + ahci_sunxi_phy_init is called from the probe and resume code paths, and + sleeping is safe in both, so use msleep instead of mdelay. + + Signed-off-by: Hans de Goede + Signed-off-by: Tejun Heo + (cherry picked from commit d2ec147a76d0e051db19d378cac3ee7721877717) + +commit e47a4a22ce18b11ae8108cb66579bdbf27a3c6b6 +Author: Bartlomiej Zolnierkiewicz +Date: Mon Mar 17 14:08:12 2014 +0100 + + ata: ahci_sunxi: fix code formatting + + Signed-off-by: Bartlomiej Zolnierkiewicz + Signed-off-by: Tejun Heo + Acked-by: Hans de Goede + (cherry picked from commit cdf457a4fe30980f7c15a894af2f954f85cd71d2) + +commit 55f1e3fa16e761e5d9f69bfd45fbf94f74182356 +Author: Bartlomiej Zolnierkiewicz +Date: Mon Mar 17 14:06:57 2014 +0100 + + ata: ahci_sunxi: make ahci_sunxi_resume() static + + Signed-off-by: Bartlomiej Zolnierkiewicz + Signed-off-by: Tejun Heo + Acked-by: Hans de Goede + (cherry picked from commit 1bf9d885658cbee1bc8e4324d0e27b02b1540d58) + +commit 98e351d4a64434059defa66fb463c42cc3b91ae5 +Author: Roger Quadros +Date: Sat Feb 22 16:53:40 2014 +0100 + + ata: ahci_platform: Manage SATA PHY + + Some platforms have a PHY hooked up to the SATA controller. The PHY + needs to be initialized and powered up for SATA to work. We do that + using the PHY framework. + + tj: Minor comment formatting updates. + + CC: Balaji T K + Signed-off-by: Roger Quadros + Signed-off-by: Hans de Goede + Signed-off-by: Tejun Heo + (cherry picked from commit 21b5faeec229d4f70a7f60a7b0b065c98198f491) + +commit b86357997ea09c7201e532809fb8a4a3ac809280 +Author: Olliver Schinagl +Date: Sat Feb 22 16:53:36 2014 +0100 + + ARM: sunxi: Add support for Allwinner SUNXi SoCs sata to ahci_platform + + This patch adds support for the ahci sata controler found on Allwinner A10 + and A20 SoCs to the ahci_platform driver. + + Orignally written by Olliver Schinagl using the approach of having a platform + device which probe method creates a new child platform device which gets + driven by ahci_platform.c, as done by ahci_imx.c . + + Refactored by Hans de Goede to add most of the non sunxi specific functionality + to ahci_platform.c and use a platform_data pointer from of_device_id for the + sunxi specific bits. + + Signed-off-by: Olliver Schinagl + Signed-off-by: Hans de Goede + Signed-off-by: Tejun Heo + (cherry picked from commit c5754b5220f01e8722799d35c04a76e82c62d7d8) + +commit 510b793cdc550ff40fdd51cb59ad081e89217a86 +Author: Hans de Goede +Date: Sat Feb 22 16:53:35 2014 +0100 + + ahci-platform: "Library-ise" suspend / resume functionality + + Split suspend / resume code into host suspend / resume functionality and + resource enable / disabling phases, and export the new suspend_ / resume_host + functions. + + tj: Minor comment formatting updates. + + Signed-off-by: Hans de Goede + Signed-off-by: Tejun Heo + (cherry picked from commit 648cb6fd83b97f0f772db783a280af300fa9f2bc) + +commit f86ef8cec39c7bfc27f128ae1325ba51892bff0f +Author: Hans de Goede +Date: Sat Feb 22 16:53:34 2014 +0100 + + ahci-platform: "Library-ise" ahci_probe functionality + + ahci_probe consists of 3 steps: + 1) Get resources (get mmio, clks, regulator) + 2) Enable resources, handled by ahci_platform_enable_resouces + 3) The more or less standard ahci-host controller init sequence + + This commit refactors step 1 and 3 into separate functions, so the platform + drivers for AHCI implementations which need a specific order in step 2, + and / or need to do some custom register poking at some time, can re-use + ahci-platform.c code without needing to copy and paste it. + + Note that ahci_platform_init_host's prototype takes the 3 non function + members of ahci_platform_data as arguments, the idea is that drivers using + the new exported utility functions will not use ahci_platform_data at all, + and hopefully in the future ahci_platform_data can go away entirely. + + tj: Minor comment formatting updates. + + Signed-off-by: Hans de Goede + Signed-off-by: Tejun Heo + (cherry picked from commit 23b07d4cb3c0c850055cf968af44019b8da185fb) + +commit d2892d30b6752365f821ba67d0352125ec683d39 +Author: Hans de Goede +Date: Sat Feb 22 16:53:33 2014 +0100 + + ahci-platform: Add enable_ / disable_resources helper functions + + tj: Minor comment formatting updates. + + Signed-off-by: Hans de Goede + Signed-off-by: Tejun Heo + (cherry picked from commit 96a01ba52c60fdd74dd6e8cf06645d06515b1396) + +commit 7114f980aefb057ab24921130e14d92ac76e44c3 +Author: Hans de Goede +Date: Sat Feb 22 16:53:32 2014 +0100 + + ahci-platform: Add support for an optional regulator for sata-target power + + Signed-off-by: Hans de Goede + Signed-off-by: Tejun Heo + (cherry picked from commit 4b3e603a298db26c6c37e8b08adcce24d014df13) + +commit 0e457adc70a512a892727895a4758fadd3d567ee +Author: Hans de Goede +Date: Sat Feb 22 16:53:31 2014 +0100 + + ahci-platform: Add support for devices with more then 1 clock + + The allwinner-sun4i AHCI controller needs 2 clocks to be enabled and the + imx AHCI controller needs 3 clocks to be enabled. + + tj: Minor comment formatting updates. + + Signed-off-by: Hans de Goede + Signed-off-by: Tejun Heo + (cherry picked from commit 156c5887948cd191417f18026aab9ce26e5a95da) + +commit 3535037fafc980139b3c6ae31b58455660600195 +Author: Hans de Goede +Date: Sat Feb 22 16:53:30 2014 +0100 + + libahci: Allow drivers to override start_engine + + Allwinner A10 and A20 ARM SoCs have an AHCI sata controller which needs a + special register to be poked before starting the DMA engine. + + This register gets reset on an ahci_stop_engine call, so there is no other + place then ahci_start_engine where this poking can be done. + + This commit allows drivers to override ahci_start_engine behavior for use by + the Allwinner AHCI driver (and potentially other drivers in the future). + + Signed-off-by: Hans de Goede + Signed-off-by: Tejun Heo + (cherry picked from commit 039ece38da45f5e6a94be3aa7611cf3634bc2461) +diff --git a/Documentation/devicetree/bindings/ata/ahci-platform.txt b/Documentation/devicetree/bindings/ata/ahci-platform.txt +index 89de156..499bfed 100644 +--- a/Documentation/devicetree/bindings/ata/ahci-platform.txt ++++ b/Documentation/devicetree/bindings/ata/ahci-platform.txt +@@ -4,17 +4,28 @@ SATA nodes are defined to describe on-chip Serial ATA controllers. + Each SATA controller should have its own node. + + Required properties: +-- compatible : compatible list, contains "snps,spear-ahci" ++- compatible : compatible list, one of "snps,spear-ahci", ++ "snps,exynos5440-ahci", "ibm,476gtr-ahci", or ++ "allwinner,sun4i-a10-ahci" + - interrupts : + - reg : + + Optional properties: + - dma-coherent : Present if dma operations are coherent ++- clocks : a list of phandle + clock specifier pairs ++- target-supply : regulator for SATA target power + +-Example: ++Examples: + sata@ffe08000 { + compatible = "snps,spear-ahci"; + reg = <0xffe08000 0x1000>; + interrupts = <115>; +- + }; ++ ++ ahci: sata@01c18000 { ++ compatible = "allwinner,sun4i-a10-ahci"; ++ reg = <0x01c18000 0x1000>; ++ interrupts = <56>; ++ clocks = <&pll6 0>, <&ahb_gates 25>; ++ target-supply = <®_ahci_5v>; ++ }; +diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt +index c2cb762..5865acd 100644 +--- a/Documentation/devicetree/bindings/clock/sunxi.txt ++++ b/Documentation/devicetree/bindings/clock/sunxi.txt +@@ -11,6 +11,7 @@ Required properties: + "allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31 + "allwinner,sun4i-pll5-clk" - for the PLL5 clock + "allwinner,sun4i-pll6-clk" - for the PLL6 clock ++ "allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31 + "allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock + "allwinner,sun4i-axi-clk" - for the AXI clock + "allwinner,sun4i-axi-gates-clk" - for the AXI gates +@@ -37,6 +38,7 @@ Required properties: + "allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31 + "allwinner,sun4i-mod0-clk" - for the module 0 family of clocks + "allwinner,sun7i-a20-out-clk" - for the external output clocks ++ "allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31 + + Required properties for all clocks: + - reg : shall be the control register address for the clock. +@@ -49,6 +51,9 @@ Required properties for all clocks: + Additionally, "allwinner,*-gates-clk" clocks require: + - clock-output-names : the corresponding gate names that the clock controls + ++For "allwinner,sun7i-a20-gmac-clk", the parent clocks shall be fixed rate ++dummy clocks at 25 MHz and 125 MHz, respectively. See example. ++ + Clock consumers should specify the desired clocks they use with a + "clocks" phandle cell. Consumers that are using a gated clock should + provide an additional ID in their clock property. This ID is the +@@ -76,3 +81,29 @@ cpu: cpu@01c20054 { + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>; + }; ++ ++mii_phy_tx_clk: clk@2 { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <25000000>; ++ clock-output-names = "mii_phy_tx"; ++}; ++ ++gmac_int_tx_clk: clk@3 { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <125000000>; ++ clock-output-names = "gmac_int_tx"; ++}; ++ ++gmac_clk: clk@01c20164 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun7i-a20-gmac-clk"; ++ reg = <0x01c20164 0x4>; ++ /* ++ * The first clock must be fixed at 25MHz; ++ * the second clock must be fixed at 125MHz ++ */ ++ clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>; ++ clock-output-names = "gmac"; ++}; +diff --git a/arch/arm/boot/dts/sun4i-a10-a1000.dts b/arch/arm/boot/dts/sun4i-a10-a1000.dts +index d4b081d..027a0ed 100644 +--- a/arch/arm/boot/dts/sun4i-a10-a1000.dts ++++ b/arch/arm/boot/dts/sun4i-a10-a1000.dts +@@ -35,6 +35,10 @@ + }; + }; + ++ ahci: sata@01c18000 { ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + emac_power_pin_a1000: emac_power_pin@0 { + allwinner,pins = "PH15"; +diff --git a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +index b139ee6..20407ac 100644 +--- a/arch/arm/boot/dts/sun4i-a10-cubieboard.dts ++++ b/arch/arm/boot/dts/sun4i-a10-cubieboard.dts +@@ -12,6 +12,7 @@ + + /dts-v1/; + /include/ "sun4i-a10.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "Cubietech Cubieboard"; +@@ -33,6 +34,11 @@ + }; + }; + ++ ahci: sata@01c18000 { ++ target-supply = <®_ahci_5v>; ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { + led_pins_cubieboard: led_pins@0 { + allwinner,pins = "PH20", "PH21"; +@@ -77,4 +83,8 @@ + linux,default-trigger = "heartbeat"; + }; + }; ++ ++ reg_ahci_5v: ahci-5v { ++ status = "okay"; ++ }; + }; +diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi +index d4d2763..796e59b 100644 +--- a/arch/arm/boot/dts/sun4i-a10.dtsi ++++ b/arch/arm/boot/dts/sun4i-a10.dtsi +@@ -19,6 +19,12 @@ + ethernet0 = &emac; + serial0 = &uart0; + serial1 = &uart1; ++ serial2 = &uart2; ++ serial3 = &uart3; ++ serial4 = &uart4; ++ serial5 = &uart5; ++ serial6 = &uart6; ++ serial7 = &uart7; + }; + + cpus { +@@ -330,6 +336,14 @@ + #size-cells = <0>; + }; + ++ ahci: sata@01c18000 { ++ compatible = "allwinner,sun4i-a10-ahci"; ++ reg = <0x01c18000 0x1000>; ++ interrupts = <56>; ++ clocks = <&pll6 0>, <&ahb_gates 25>; ++ status = "disabled"; ++ }; ++ + intc: interrupt-controller@01c20400 { + compatible = "allwinner,sun4i-ic"; + reg = <0x01c20400 0x400>; +diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi +index 79fd412..848baaa 100644 +--- a/arch/arm/boot/dts/sun5i-a10s.dtsi ++++ b/arch/arm/boot/dts/sun5i-a10s.dtsi +@@ -18,6 +18,10 @@ + + aliases { + ethernet0 = &emac; ++ serial0 = &uart0; ++ serial1 = &uart1; ++ serial2 = &uart2; ++ serial3 = &uart3; + }; + + cpus { +diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi +index 5256ad9..092bf97 100644 +--- a/arch/arm/boot/dts/sun6i-a31.dtsi ++++ b/arch/arm/boot/dts/sun6i-a31.dtsi +@@ -16,6 +16,16 @@ + / { + interrupt-parent = <&gic>; + ++ aliases { ++ serial0 = &uart0; ++ serial1 = &uart1; ++ serial2 = &uart2; ++ serial3 = &uart3; ++ serial4 = &uart4; ++ serial5 = &uart5; ++ }; ++ ++ + cpus { + #address-cells = <1>; + #size-cells = <0>; +diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +index 5c51cb8..4bed115 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts +@@ -13,25 +13,16 @@ + + /dts-v1/; + /include/ "sun7i-a20.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "Cubietech Cubieboard2"; + compatible = "cubietech,cubieboard2", "allwinner,sun7i-a20"; + + soc@01c00000 { +- emac: ethernet@01c0b000 { +- pinctrl-names = "default"; +- pinctrl-0 = <&emac_pins_a>; +- phy = <&phy1>; +- status = "okay"; +- }; +- +- mdio@01c0b080 { ++ ahci: sata@01c18000 { ++ target-supply = <®_ahci_5v>; + status = "okay"; +- +- phy1: ethernet-phy@1 { +- reg = <1>; +- }; + }; + + pinctrl@01c20800 { +@@ -60,6 +51,18 @@ + pinctrl-0 = <&i2c1_pins_a>; + status = "okay"; + }; ++ ++ gmac: ethernet@01c50000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&gmac_pins_mii_a>; ++ phy = <&phy1>; ++ phy-mode = "mii"; ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ }; + }; + + leds { +@@ -77,4 +80,8 @@ + gpios = <&pio 7 20 0>; + }; + }; ++ ++ reg_ahci_5v: ahci-5v { ++ status = "okay"; ++ }; + }; +diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +index f9dcb61..ef5fed8 100644 +--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts ++++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts +@@ -13,13 +13,26 @@ + + /dts-v1/; + /include/ "sun7i-a20.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "Cubietech Cubietruck"; + compatible = "cubietech,cubietruck", "allwinner,sun7i-a20"; + + soc@01c00000 { ++ ahci: sata@01c18000 { ++ target-supply = <®_ahci_5v>; ++ status = "okay"; ++ }; ++ + pinctrl@01c20800 { ++ ahci_pwr_pin_cubietruck: ahci_pwr_pin@1 { ++ allwinner,pins = "PH12"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ + led_pins_cubietruck: led_pins@0 { + allwinner,pins = "PH7", "PH11", "PH20", "PH21"; + allwinner,function = "gpio_out"; +@@ -51,6 +64,18 @@ + pinctrl-0 = <&i2c2_pins_a>; + status = "okay"; + }; ++ ++ gmac: ethernet@01c50000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&gmac_pins_rgmii_a>; ++ phy = <&phy1>; ++ phy-mode = "rgmii"; ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ }; + }; + + leds { +@@ -78,4 +103,10 @@ + gpios = <&pio 7 7 0>; + }; + }; ++ ++ reg_ahci_5v: ahci-5v { ++ pinctrl-0 = <&ahci_pwr_pin_cubietruck>; ++ gpio = <&pio 7 12 0>; ++ status = "okay"; ++ }; + }; +diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +index ead3013..9b104ad 100644 +--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts ++++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts +@@ -13,25 +13,16 @@ + + /dts-v1/; + /include/ "sun7i-a20.dtsi" ++/include/ "sunxi-common-regulators.dtsi" + + / { + model = "Olimex A20-Olinuxino Micro"; + compatible = "olimex,a20-olinuxino-micro", "allwinner,sun7i-a20"; + + soc@01c00000 { +- emac: ethernet@01c0b000 { +- pinctrl-names = "default"; +- pinctrl-0 = <&emac_pins_a>; +- phy = <&phy1>; +- status = "okay"; +- }; +- +- mdio@01c0b080 { ++ ahci: sata@01c18000 { ++ target-supply = <®_ahci_5v>; + status = "okay"; +- +- phy1: ethernet-phy@1 { +- reg = <1>; +- }; + }; + + pinctrl@01c20800 { +@@ -78,6 +69,18 @@ + pinctrl-0 = <&i2c2_pins_a>; + status = "okay"; + }; ++ ++ gmac: ethernet@01c50000 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&gmac_pins_mii_a>; ++ phy = <&phy1>; ++ phy-mode = "mii"; ++ status = "okay"; ++ ++ phy1: ethernet-phy@1 { ++ reg = <1>; ++ }; ++ }; + }; + + leds { +@@ -91,4 +94,8 @@ + default-state = "on"; + }; + }; ++ ++ reg_ahci_5v: ahci-5v { ++ status = "okay"; ++ }; + }; +diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi +index 6f25cf5..c16ef91 100644 +--- a/arch/arm/boot/dts/sun7i-a20.dtsi ++++ b/arch/arm/boot/dts/sun7i-a20.dtsi +@@ -17,7 +17,15 @@ + interrupt-parent = <&gic>; + + aliases { +- ethernet0 = &emac; ++ ethernet0 = &gmac; ++ serial0 = &uart0; ++ serial1 = &uart1; ++ serial2 = &uart2; ++ serial3 = &uart3; ++ serial4 = &uart4; ++ serial5 = &uart5; ++ serial6 = &uart6; ++ serial7 = &uart7; + }; + + cpus { +@@ -41,6 +49,14 @@ + reg = <0x40000000 0x80000000>; + }; + ++ timer { ++ compatible = "arm,armv7-timer"; ++ interrupts = <1 13 0xf08>, ++ <1 14 0xf08>, ++ <1 11 0xf08>, ++ <1 10 0xf08>; ++ }; ++ + clocks { + #address-cells = <1>; + #size-cells = <1>; +@@ -305,6 +321,34 @@ + }; + + /* ++ * The following two are dummy clocks, placeholders used in the gmac_tx ++ * clock. The gmac driver will choose one parent depending on the PHY ++ * interface mode, using clk_set_rate auto-reparenting. ++ * The actual TX clock rate is not controlled by the gmac_tx clock. ++ */ ++ mii_phy_tx_clk: clk@2 { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <25000000>; ++ clock-output-names = "mii_phy_tx"; ++ }; ++ ++ gmac_int_tx_clk: clk@3 { ++ #clock-cells = <0>; ++ compatible = "fixed-clock"; ++ clock-frequency = <125000000>; ++ clock-output-names = "gmac_int_tx"; ++ }; ++ ++ gmac_tx_clk: clk@01c20164 { ++ #clock-cells = <0>; ++ compatible = "allwinner,sun7i-a20-gmac-clk"; ++ reg = <0x01c20164 0x4>; ++ clocks = <&mii_phy_tx_clk>, <&gmac_int_tx_clk>; ++ clock-output-names = "gmac_tx"; ++ }; ++ ++ /* + * Dummy clock used by output clocks + */ + osc24M_32k: clk@1 { +@@ -355,6 +399,14 @@ + #size-cells = <0>; + }; + ++ ahci: sata@01c18000 { ++ compatible = "allwinner,sun4i-a10-ahci"; ++ reg = <0x01c18000 0x1000>; ++ interrupts = <0 56 4>; ++ clocks = <&pll6 0>, <&ahb_gates 25>; ++ status = "disabled"; ++ }; ++ + pio: pinctrl@01c20800 { + compatible = "allwinner,sun7i-a20-pinctrl"; + reg = <0x01c20800 0x400>; +@@ -432,6 +484,32 @@ + allwinner,drive = <0>; + allwinner,pull = <0>; + }; ++ ++ gmac_pins_mii_a: gmac_mii@0 { ++ allwinner,pins = "PA0", "PA1", "PA2", ++ "PA3", "PA4", "PA5", "PA6", ++ "PA7", "PA8", "PA9", "PA10", ++ "PA11", "PA12", "PA13", "PA14", ++ "PA15", "PA16"; ++ allwinner,function = "gmac"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ ++ gmac_pins_rgmii_a: gmac_rgmii@0 { ++ allwinner,pins = "PA0", "PA1", "PA2", ++ "PA3", "PA4", "PA5", "PA6", ++ "PA7", "PA8", "PA10", ++ "PA11", "PA12", "PA13", ++ "PA15", "PA16"; ++ allwinner,function = "gmac"; ++ /* ++ * data lines in RGMII mode use DDR mode ++ * and need a higher signal drive strength ++ */ ++ allwinner,drive = <3>; ++ allwinner,pull = <0>; ++ }; + }; + + timer@01c20c00 { +@@ -593,6 +671,21 @@ + status = "disabled"; + }; + ++ gmac: ethernet@01c50000 { ++ compatible = "allwinner,sun7i-a20-gmac"; ++ reg = <0x01c50000 0x10000>; ++ interrupts = <0 85 4>; ++ interrupt-names = "macirq"; ++ clocks = <&ahb_gates 49>, <&gmac_tx_clk>; ++ clock-names = "stmmaceth", "allwinner_gmac_tx"; ++ snps,pbl = <2>; ++ snps,fixed-burst; ++ snps,force_sf_dma_mode; ++ status = "disabled"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ }; ++ + hstimer@01c60000 { + compatible = "allwinner,sun7i-a20-hstimer"; + reg = <0x01c60000 0x1000>; +diff --git a/arch/arm/boot/dts/sunxi-common-regulators.dtsi b/arch/arm/boot/dts/sunxi-common-regulators.dtsi +new file mode 100644 +index 0000000..18eeac0 +--- /dev/null ++++ b/arch/arm/boot/dts/sunxi-common-regulators.dtsi +@@ -0,0 +1,75 @@ ++/* ++ * sunxi boards common regulator (ahci target power supply, usb-vbus) code ++ * ++ * Copyright 2014 - Hans de Goede ++ * ++ * The code contained herein is licensed under the GNU General Public ++ * License. You may obtain a copy of the GNU General Public License ++ * Version 2 or later at the following locations: ++ * ++ * http://www.opensource.org/licenses/gpl-license.html ++ * http://www.gnu.org/copyleft/gpl.html ++ */ ++ ++/ { ++ soc@01c00000 { ++ pio: pinctrl@01c20800 { ++ ahci_pwr_pin_a: ahci_pwr_pin@0 { ++ allwinner,pins = "PB8"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ ++ usb1_vbus_pin_a: usb1_vbus_pin@0 { ++ allwinner,pins = "PH6"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ ++ usb2_vbus_pin_a: usb2_vbus_pin@0 { ++ allwinner,pins = "PH3"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = <0>; ++ allwinner,pull = <0>; ++ }; ++ }; ++ }; ++ ++ reg_ahci_5v: ahci-5v { ++ compatible = "regulator-fixed"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&ahci_pwr_pin_a>; ++ regulator-name = "ahci-5v"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ gpio = <&pio 1 8 0>; ++ status = "disabled"; ++ }; ++ ++ reg_usb1_vbus: usb1-vbus { ++ compatible = "regulator-fixed"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usb1_vbus_pin_a>; ++ regulator-name = "usb1-vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ gpio = <&pio 7 6 0>; ++ status = "disabled"; ++ }; ++ ++ reg_usb2_vbus: usb2-vbus { ++ compatible = "regulator-fixed"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&usb2_vbus_pin_a>; ++ regulator-name = "usb2-vbus"; ++ regulator-min-microvolt = <5000000>; ++ regulator-max-microvolt = <5000000>; ++ enable-active-high; ++ gpio = <&pio 7 3 0>; ++ status = "disabled"; ++ }; ++}; +diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig +index 868429a..10a9c25 100644 +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -106,6 +106,15 @@ config AHCI_IMX + + If unsure, say N. + ++config AHCI_SUNXI ++ tristate "Allwinner sunxi AHCI SATA support" ++ depends on ARCH_SUNXI && SATA_AHCI_PLATFORM ++ help ++ This option enables support for the Allwinner sunxi SoC's ++ onboard AHCI SATA. ++ ++ If unsure, say N. ++ + config SATA_FSL + tristate "Freescale 3.0Gbps SATA support" + depends on FSL_SOC +diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile +index 46518c6..246050b 100644 +--- a/drivers/ata/Makefile ++++ b/drivers/ata/Makefile +@@ -11,6 +11,7 @@ obj-$(CONFIG_SATA_SIL24) += sata_sil24.o + obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o + obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o + obj-$(CONFIG_AHCI_IMX) += ahci_imx.o ++obj-$(CONFIG_AHCI_SUNXI) += ahci_sunxi.o + + # SFF w/ custom DMA + obj-$(CONFIG_PDC_ADMA) += pdc_adma.o +diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c +index c81d809..8bfc477 100644 +--- a/drivers/ata/ahci.c ++++ b/drivers/ata/ahci.c +@@ -578,6 +578,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) + { + struct ata_port *ap = link->ap; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + bool online; + int rc; + +@@ -588,7 +589,7 @@ static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), + deadline, &online, NULL); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); + +@@ -603,6 +604,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, + { + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -618,7 +620,7 @@ static int ahci_p5wdh_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, sata_ehc_deb_timing(&link->eh_context), + deadline, &online, NULL); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* The pseudo configuration device on SIMG4726 attached to + * ASUS P5W-DH Deluxe doesn't send signature FIS after +diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h +index 2289efd..3ab7ac9 100644 +--- a/drivers/ata/ahci.h ++++ b/drivers/ata/ahci.h +@@ -37,6 +37,8 @@ + + #include + #include ++#include ++#include + + /* Enclosure Management Control */ + #define EM_CTRL_MSG_TYPE 0x000f0000 +@@ -51,6 +53,7 @@ + + enum { + AHCI_MAX_PORTS = 32, ++ AHCI_MAX_CLKS = 3, + AHCI_MAX_SG = 168, /* hardware max is 64K */ + AHCI_DMA_BOUNDARY = 0xffffffff, + AHCI_MAX_CMDS = 32, +@@ -321,8 +324,16 @@ struct ahci_host_priv { + u32 em_loc; /* enclosure management location */ + u32 em_buf_sz; /* EM buffer size in byte */ + u32 em_msg_type; /* EM message type */ +- struct clk *clk; /* Only for platforms supporting clk */ ++ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ ++ struct regulator *target_pwr; /* Optional */ ++ struct phy *phy; /* If platform uses phy */ + void *plat_data; /* Other platform data */ ++ /* ++ * Optional ahci_start_engine override, if not set this gets set to the ++ * default ahci_start_engine during ahci_save_initial_config, this can ++ * be overridden anytime before the host is activated. ++ */ ++ void (*start_engine)(struct ata_port *ap); + }; + + extern int ahci_ignore_sss; +diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c +index 4b231ba..ecacd4d 100644 +--- a/drivers/ata/ahci_platform.c ++++ b/drivers/ata/ahci_platform.c +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include "ahci.h" + + static void ahci_host_stop(struct ata_host *host); +@@ -87,78 +88,275 @@ static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT("ahci_platform"), + }; + +-static int ahci_probe(struct platform_device *pdev) ++/** ++ * ahci_platform_enable_clks - Enable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all the clks found in hpriv->clks, starting at ++ * index 0. If any clk fails to enable it disables all the clks already ++ * enabled in reverse order, and then returns an error. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c, rc; ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { ++ rc = clk_prepare_enable(hpriv->clks[c]); ++ if (rc) ++ goto disable_unprepare_clk; ++ } ++ return 0; ++ ++disable_unprepare_clk: ++ while (--c >= 0) ++ clk_disable_unprepare(hpriv->clks[c]); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); ++ ++/** ++ * ahci_platform_disable_clks - Disable platform clocks ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all the clks found in hpriv->clks, in reverse ++ * order of ahci_platform_enable_clks (starting at the end of the array). ++ */ ++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) ++{ ++ int c; ++ ++ for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) ++ if (hpriv->clks[c]) ++ clk_disable_unprepare(hpriv->clks[c]); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); ++ ++/** ++ * ahci_platform_enable_resources - Enable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function enables all ahci_platform managed resources in the ++ * following order: ++ * 1) Regulator ++ * 2) Clocks (through ahci_platform_enable_clks) ++ * 3) Phy ++ * ++ * If resource enabling fails at any point the previous enabled resources ++ * are disabled in reverse order. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) + { +- struct device *dev = &pdev->dev; +- struct ahci_platform_data *pdata = dev_get_platdata(dev); +- const struct platform_device_id *id = platform_get_device_id(pdev); +- struct ata_port_info pi = ahci_port_info[id ? id->driver_data : 0]; +- const struct ata_port_info *ppi[] = { &pi, NULL }; +- struct ahci_host_priv *hpriv; +- struct ata_host *host; +- struct resource *mem; +- int irq; +- int n_ports; +- int i; + int rc; + +- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!mem) { +- dev_err(dev, "no mmio space\n"); +- return -EINVAL; ++ if (hpriv->target_pwr) { ++ rc = regulator_enable(hpriv->target_pwr); ++ if (rc) ++ return rc; + } + +- irq = platform_get_irq(pdev, 0); +- if (irq <= 0) { +- dev_err(dev, "no irq\n"); +- return -EINVAL; ++ rc = ahci_platform_enable_clks(hpriv); ++ if (rc) ++ goto disable_regulator; ++ ++ if (hpriv->phy) { ++ rc = phy_init(hpriv->phy); ++ if (rc) ++ goto disable_clks; ++ ++ rc = phy_power_on(hpriv->phy); ++ if (rc) { ++ phy_exit(hpriv->phy); ++ goto disable_clks; ++ } + } + +- if (pdata && pdata->ata_port_info) +- pi = *pdata->ata_port_info; ++ return 0; + +- hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); +- if (!hpriv) { +- dev_err(dev, "can't alloc ahci_host_priv\n"); +- return -ENOMEM; ++disable_clks: ++ ahci_platform_disable_clks(hpriv); ++ ++disable_regulator: ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); ++ ++/** ++ * ahci_platform_disable_resources - Disable platform resources ++ * @hpriv: host private area to store config values ++ * ++ * This function disables all ahci_platform managed resources in the ++ * following order: ++ * 1) Phy ++ * 2) Clocks (through ahci_platform_disable_clks) ++ * 3) Regulator ++ */ ++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) ++{ ++ if (hpriv->phy) { ++ phy_power_off(hpriv->phy); ++ phy_exit(hpriv->phy); + } + +- hpriv->flags |= (unsigned long)pi.private_data; ++ ahci_platform_disable_clks(hpriv); + +- hpriv->mmio = devm_ioremap(dev, mem->start, resource_size(mem)); ++ if (hpriv->target_pwr) ++ regulator_disable(hpriv->target_pwr); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); ++ ++static void ahci_platform_put_resources(struct device *dev, void *res) ++{ ++ struct ahci_host_priv *hpriv = res; ++ int c; ++ ++ for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) ++ clk_put(hpriv->clks[c]); ++} ++ ++/** ++ * ahci_platform_get_resources - Get platform resources ++ * @pdev: platform device to get resources for ++ * ++ * This function allocates an ahci_host_priv struct, and gets the following ++ * resources, storing a reference to them inside the returned struct: ++ * ++ * 1) mmio registers (IORESOURCE_MEM 0, mandatory) ++ * 2) regulator for controlling the targets power (optional) ++ * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, ++ * or for non devicetree enabled platforms a single clock ++ * 4) phy (optional) ++ * ++ * RETURNS: ++ * The allocated ahci_host_priv on success, otherwise an ERR_PTR value ++ */ ++struct ahci_host_priv *ahci_platform_get_resources( ++ struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ahci_host_priv *hpriv; ++ struct clk *clk; ++ int i, rc = -ENOMEM; ++ ++ if (!devres_open_group(dev, NULL, GFP_KERNEL)) ++ return ERR_PTR(-ENOMEM); ++ ++ hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), ++ GFP_KERNEL); ++ if (!hpriv) ++ goto err_out; ++ ++ devres_add(dev, hpriv); ++ ++ hpriv->mmio = devm_ioremap_resource(dev, ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); + if (!hpriv->mmio) { +- dev_err(dev, "can't map %pR\n", mem); +- return -ENOMEM; ++ dev_err(dev, "no mmio space\n"); ++ goto err_out; + } + +- hpriv->clk = clk_get(dev, NULL); +- if (IS_ERR(hpriv->clk)) { +- dev_err(dev, "can't get clock\n"); +- } else { +- rc = clk_prepare_enable(hpriv->clk); +- if (rc) { +- dev_err(dev, "clock prepare enable failed"); +- goto free_clk; ++ hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); ++ if (IS_ERR(hpriv->target_pwr)) { ++ rc = PTR_ERR(hpriv->target_pwr); ++ if (rc == -EPROBE_DEFER) ++ goto err_out; ++ hpriv->target_pwr = NULL; ++ } ++ ++ for (i = 0; i < AHCI_MAX_CLKS; i++) { ++ /* ++ * For now we must use clk_get(dev, NULL) for the first clock, ++ * because some platforms (da850, spear13xx) are not yet ++ * converted to use devicetree for clocks. For new platforms ++ * this is equivalent to of_clk_get(dev->of_node, 0). ++ */ ++ if (i == 0) ++ clk = clk_get(dev, NULL); ++ else ++ clk = of_clk_get(dev->of_node, i); ++ ++ if (IS_ERR(clk)) { ++ rc = PTR_ERR(clk); ++ if (rc == -EPROBE_DEFER) ++ goto err_out; ++ break; + } ++ hpriv->clks[i] = clk; + } + +- /* +- * Some platforms might need to prepare for mmio region access, +- * which could be done in the following init call. So, the mmio +- * region shouldn't be accessed before init (if provided) has +- * returned successfully. +- */ +- if (pdata && pdata->init) { +- rc = pdata->init(dev, hpriv->mmio); +- if (rc) +- goto disable_unprepare_clk; ++ hpriv->phy = devm_phy_get(dev, "sata-phy"); ++ if (IS_ERR(hpriv->phy)) { ++ rc = PTR_ERR(hpriv->phy); ++ switch (rc) { ++ case -ENODEV: ++ case -ENOSYS: ++ /* continue normally */ ++ hpriv->phy = NULL; ++ break; ++ ++ case -EPROBE_DEFER: ++ goto err_out; ++ ++ default: ++ dev_err(dev, "couldn't get sata-phy\n"); ++ goto err_out; ++ } + } + +- ahci_save_initial_config(dev, hpriv, +- pdata ? pdata->force_port_map : 0, +- pdata ? pdata->mask_port_map : 0); ++ devres_remove_group(dev, NULL); ++ return hpriv; ++ ++err_out: ++ devres_release_group(dev, NULL); ++ return ERR_PTR(rc); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_get_resources); ++ ++/** ++ * ahci_platform_init_host - Bring up an ahci-platform host ++ * @pdev: platform device pointer for the host ++ * @hpriv: ahci-host private data for the host ++ * @pi_template: template for the ata_port_info to use ++ * @force_port_map: param passed to ahci_save_initial_config ++ * @mask_port_map: param passed to ahci_save_initial_config ++ * ++ * This function does all the usual steps needed to bring up an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_init_host(struct platform_device *pdev, ++ struct ahci_host_priv *hpriv, ++ const struct ata_port_info *pi_template, ++ unsigned int force_port_map, ++ unsigned int mask_port_map) ++{ ++ struct device *dev = &pdev->dev; ++ struct ata_port_info pi = *pi_template; ++ const struct ata_port_info *ppi[] = { &pi, NULL }; ++ struct ata_host *host; ++ int i, irq, n_ports, rc; ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq <= 0) { ++ dev_err(dev, "no irq\n"); ++ return -EINVAL; ++ } + + /* prepare host */ ++ hpriv->flags |= (unsigned long)pi.private_data; ++ ++ ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); ++ + if (hpriv->cap & HOST_CAP_NCQ) + pi.flags |= ATA_FLAG_NCQ; + +@@ -175,10 +373,8 @@ static int ahci_probe(struct platform_device *pdev) + n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); + + host = ata_host_alloc_pinfo(dev, ppi, n_ports); +- if (!host) { +- rc = -ENOMEM; +- goto pdata_exit; +- } ++ if (!host) ++ return -ENOMEM; + + host->private_data = hpriv; + +@@ -193,7 +389,8 @@ static int ahci_probe(struct platform_device *pdev) + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + +- ata_port_desc(ap, "mmio %pR", mem); ++ ata_port_desc(ap, "mmio %pR", ++ platform_get_resource(pdev, IORESOURCE_MEM, 0)); + ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); + + /* set enclosure management message type */ +@@ -207,13 +404,53 @@ static int ahci_probe(struct platform_device *pdev) + + rc = ahci_reset_controller(host); + if (rc) +- goto pdata_exit; ++ return rc; + + ahci_init_controller(host); + ahci_print_info(host, "platform"); + +- rc = ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, +- &ahci_platform_sht); ++ return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, ++ &ahci_platform_sht); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_init_host); ++ ++static int ahci_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ const struct platform_device_id *id = platform_get_device_id(pdev); ++ const struct ata_port_info *pi_template; ++ struct ahci_host_priv *hpriv; ++ int rc; ++ ++ hpriv = ahci_platform_get_resources(pdev); ++ if (IS_ERR(hpriv)) ++ return PTR_ERR(hpriv); ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ /* ++ * Some platforms might need to prepare for mmio region access, ++ * which could be done in the following init call. So, the mmio ++ * region shouldn't be accessed before init (if provided) has ++ * returned successfully. ++ */ ++ if (pdata && pdata->init) { ++ rc = pdata->init(dev, hpriv->mmio); ++ if (rc) ++ goto disable_resources; ++ } ++ ++ if (pdata && pdata->ata_port_info) ++ pi_template = pdata->ata_port_info; ++ else ++ pi_template = &ahci_port_info[id ? id->driver_data : 0]; ++ ++ rc = ahci_platform_init_host(pdev, hpriv, pi_template, ++ pdata ? pdata->force_port_map : 0, ++ pdata ? pdata->mask_port_map : 0); + if (rc) + goto pdata_exit; + +@@ -221,12 +458,8 @@ static int ahci_probe(struct platform_device *pdev) + pdata_exit: + if (pdata && pdata->exit) + pdata->exit(dev); +-disable_unprepare_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); +-free_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_put(hpriv->clk); ++disable_resources: ++ ahci_platform_disable_resources(hpriv); + return rc; + } + +@@ -239,21 +472,27 @@ static void ahci_host_stop(struct ata_host *host) + if (pdata && pdata->exit) + pdata->exit(dev); + +- if (!IS_ERR(hpriv->clk)) { +- clk_disable_unprepare(hpriv->clk); +- clk_put(hpriv->clk); +- } ++ ahci_platform_disable_resources(hpriv); + } + + #ifdef CONFIG_PM_SLEEP +-static int ahci_suspend(struct device *dev) ++/** ++ * ahci_platform_suspend_host - Suspend an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to suspend an ++ * ahci-platform host, note any necessary resources (ie clks, phy, etc.) ++ * must be disabled after calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend_host(struct device *dev) + { +- struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = hpriv->mmio; + u32 ctl; +- int rc; + + if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { + dev_err(dev, "firmware update required for suspend/resume\n"); +@@ -270,61 +509,113 @@ static int ahci_suspend(struct device *dev) + writel(ctl, mmio + HOST_CTL); + readl(mmio + HOST_CTL); /* flush */ + +- rc = ata_host_suspend(host, PMSG_SUSPEND); ++ return ata_host_suspend(host, PMSG_SUSPEND); ++} ++EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); ++ ++/** ++ * ahci_platform_resume_host - Resume an ahci-platform host ++ * @dev: device pointer for the host ++ * ++ * This function does all the usual steps needed to resume an ahci-platform ++ * host, note any necessary resources (ie clks, phy, etc.) must be ++ * initialized / enabled before calling this. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume_host(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ int rc; ++ ++ if (dev->power.power_state.event == PM_EVENT_SUSPEND) { ++ rc = ahci_reset_controller(host); ++ if (rc) ++ return rc; ++ ++ ahci_init_controller(host); ++ } ++ ++ ata_host_resume(host); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(ahci_platform_resume_host); ++ ++/** ++ * ahci_platform_suspend - Suspend an ahci-platform device ++ * @dev: the platform device to suspend ++ * ++ * This function suspends the host associated with the device, followed by ++ * disabling all the resources of the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_suspend(struct device *dev) ++{ ++ struct ahci_platform_data *pdata = dev_get_platdata(dev); ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_suspend_host(dev); + if (rc) + return rc; + + if (pdata && pdata->suspend) + return pdata->suspend(dev); + +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); ++ ahci_platform_disable_resources(hpriv); + + return 0; + } ++EXPORT_SYMBOL_GPL(ahci_platform_suspend); + +-static int ahci_resume(struct device *dev) ++/** ++ * ahci_platform_resume - Resume an ahci-platform device ++ * @dev: the platform device to resume ++ * ++ * This function enables all the resources of the device followed by ++ * resuming the host associated with the device. ++ * ++ * RETURNS: ++ * 0 on success otherwise a negative error code ++ */ ++int ahci_platform_resume(struct device *dev) + { + struct ahci_platform_data *pdata = dev_get_platdata(dev); + struct ata_host *host = dev_get_drvdata(dev); + struct ahci_host_priv *hpriv = host->private_data; + int rc; + +- if (!IS_ERR(hpriv->clk)) { +- rc = clk_prepare_enable(hpriv->clk); +- if (rc) { +- dev_err(dev, "clock prepare enable failed"); +- return rc; +- } +- } ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; + + if (pdata && pdata->resume) { + rc = pdata->resume(dev); + if (rc) +- goto disable_unprepare_clk; +- } +- +- if (dev->power.power_state.event == PM_EVENT_SUSPEND) { +- rc = ahci_reset_controller(host); +- if (rc) +- goto disable_unprepare_clk; +- +- ahci_init_controller(host); ++ goto disable_resources; + } + +- ata_host_resume(host); ++ rc = ahci_platform_resume_host(dev); ++ if (rc) ++ goto disable_resources; + + return 0; + +-disable_unprepare_clk: +- if (!IS_ERR(hpriv->clk)) +- clk_disable_unprepare(hpriv->clk); ++disable_resources: ++ ahci_platform_disable_resources(hpriv); + + return rc; + } ++EXPORT_SYMBOL_GPL(ahci_platform_resume); + #endif + +-static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_suspend, ahci_resume); ++static SIMPLE_DEV_PM_OPS(ahci_pm_ops, ahci_platform_suspend, ++ ahci_platform_resume); + + static const struct of_device_id ahci_of_match[] = { + { .compatible = "snps,spear-ahci", }, +diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c +new file mode 100644 +index 0000000..42d3f64 +--- /dev/null ++++ b/drivers/ata/ahci_sunxi.c +@@ -0,0 +1,249 @@ ++/* ++ * Allwinner sunxi AHCI SATA platform driver ++ * Copyright 2013 Olliver Schinagl ++ * Copyright 2014 Hans de Goede ++ * ++ * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov ++ * Based on code from Allwinner Technology Co., Ltd. , ++ * Daniel Wang ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "ahci.h" ++ ++#define AHCI_BISTAFR 0x00a0 ++#define AHCI_BISTCR 0x00a4 ++#define AHCI_BISTFCTR 0x00a8 ++#define AHCI_BISTSR 0x00ac ++#define AHCI_BISTDECR 0x00b0 ++#define AHCI_DIAGNR0 0x00b4 ++#define AHCI_DIAGNR1 0x00b8 ++#define AHCI_OOBR 0x00bc ++#define AHCI_PHYCS0R 0x00c0 ++#define AHCI_PHYCS1R 0x00c4 ++#define AHCI_PHYCS2R 0x00c8 ++#define AHCI_TIMER1MS 0x00e0 ++#define AHCI_GPARAM1R 0x00e8 ++#define AHCI_GPARAM2R 0x00ec ++#define AHCI_PPARAMR 0x00f0 ++#define AHCI_TESTR 0x00f4 ++#define AHCI_VERSIONR 0x00f8 ++#define AHCI_IDR 0x00fc ++#define AHCI_RWCR 0x00fc ++#define AHCI_P0DMACR 0x0170 ++#define AHCI_P0PHYCR 0x0178 ++#define AHCI_P0PHYSR 0x017c ++ ++static void sunxi_clrbits(void __iomem *reg, u32 clr_val) ++{ ++ u32 reg_val; ++ ++ reg_val = readl(reg); ++ reg_val &= ~(clr_val); ++ writel(reg_val, reg); ++} ++ ++static void sunxi_setbits(void __iomem *reg, u32 set_val) ++{ ++ u32 reg_val; ++ ++ reg_val = readl(reg); ++ reg_val |= set_val; ++ writel(reg_val, reg); ++} ++ ++static void sunxi_clrsetbits(void __iomem *reg, u32 clr_val, u32 set_val) ++{ ++ u32 reg_val; ++ ++ reg_val = readl(reg); ++ reg_val &= ~(clr_val); ++ reg_val |= set_val; ++ writel(reg_val, reg); ++} ++ ++static u32 sunxi_getbits(void __iomem *reg, u8 mask, u8 shift) ++{ ++ return (readl(reg) >> shift) & mask; ++} ++ ++static int ahci_sunxi_phy_init(struct device *dev, void __iomem *reg_base) ++{ ++ u32 reg_val; ++ int timeout; ++ ++ /* This magic is from the original code */ ++ writel(0, reg_base + AHCI_RWCR); ++ msleep(5); ++ ++ sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(19)); ++ sunxi_clrsetbits(reg_base + AHCI_PHYCS0R, ++ (0x7 << 24), ++ (0x5 << 24) | BIT(23) | BIT(18)); ++ sunxi_clrsetbits(reg_base + AHCI_PHYCS1R, ++ (0x3 << 16) | (0x1f << 8) | (0x3 << 6), ++ (0x2 << 16) | (0x6 << 8) | (0x2 << 6)); ++ sunxi_setbits(reg_base + AHCI_PHYCS1R, BIT(28) | BIT(15)); ++ sunxi_clrbits(reg_base + AHCI_PHYCS1R, BIT(19)); ++ sunxi_clrsetbits(reg_base + AHCI_PHYCS0R, ++ (0x7 << 20), (0x3 << 20)); ++ sunxi_clrsetbits(reg_base + AHCI_PHYCS2R, ++ (0x1f << 5), (0x19 << 5)); ++ msleep(5); ++ ++ sunxi_setbits(reg_base + AHCI_PHYCS0R, (0x1 << 19)); ++ ++ timeout = 250; /* Power up takes aprox 50 us */ ++ do { ++ reg_val = sunxi_getbits(reg_base + AHCI_PHYCS0R, 0x7, 28); ++ if (reg_val == 0x02) ++ break; ++ ++ if (--timeout == 0) { ++ dev_err(dev, "PHY power up failed.\n"); ++ return -EIO; ++ } ++ udelay(1); ++ } while (1); ++ ++ sunxi_setbits(reg_base + AHCI_PHYCS2R, (0x1 << 24)); ++ ++ timeout = 100; /* Calibration takes aprox 10 us */ ++ do { ++ reg_val = sunxi_getbits(reg_base + AHCI_PHYCS2R, 0x1, 24); ++ if (reg_val == 0x00) ++ break; ++ ++ if (--timeout == 0) { ++ dev_err(dev, "PHY calibration failed.\n"); ++ return -EIO; ++ } ++ udelay(1); ++ } while (1); ++ ++ msleep(15); ++ ++ writel(0x7, reg_base + AHCI_RWCR); ++ ++ return 0; ++} ++ ++static void ahci_sunxi_start_engine(struct ata_port *ap) ++{ ++ void __iomem *port_mmio = ahci_port_base(ap); ++ struct ahci_host_priv *hpriv = ap->host->private_data; ++ ++ /* Setup DMA before DMA start */ ++ sunxi_clrsetbits(hpriv->mmio + AHCI_P0DMACR, 0x0000ff00, 0x00004400); ++ ++ /* Start DMA */ ++ sunxi_setbits(port_mmio + PORT_CMD, PORT_CMD_START); ++} ++ ++static const struct ata_port_info ahci_sunxi_port_info = { ++ AHCI_HFLAGS(AHCI_HFLAG_32BIT_ONLY | AHCI_HFLAG_NO_MSI | ++ AHCI_HFLAG_NO_PMP | AHCI_HFLAG_YES_NCQ), ++ .flags = AHCI_FLAG_COMMON | ATA_FLAG_NCQ, ++ .pio_mask = ATA_PIO4, ++ .udma_mask = ATA_UDMA6, ++ .port_ops = &ahci_platform_ops, ++}; ++ ++static int ahci_sunxi_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ahci_host_priv *hpriv; ++ int rc; ++ ++ hpriv = ahci_platform_get_resources(pdev); ++ if (IS_ERR(hpriv)) ++ return PTR_ERR(hpriv); ++ ++ hpriv->start_engine = ahci_sunxi_start_engine; ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ rc = ahci_sunxi_phy_init(dev, hpriv->mmio); ++ if (rc) ++ goto disable_resources; ++ ++ rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info, 0, 0); ++ if (rc) ++ goto disable_resources; ++ ++ return 0; ++ ++disable_resources: ++ ahci_platform_disable_resources(hpriv); ++ return rc; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int ahci_sunxi_resume(struct device *dev) ++{ ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct ahci_host_priv *hpriv = host->private_data; ++ int rc; ++ ++ rc = ahci_platform_enable_resources(hpriv); ++ if (rc) ++ return rc; ++ ++ rc = ahci_sunxi_phy_init(dev, hpriv->mmio); ++ if (rc) ++ goto disable_resources; ++ ++ rc = ahci_platform_resume_host(dev); ++ if (rc) ++ goto disable_resources; ++ ++ return 0; ++ ++disable_resources: ++ ahci_platform_disable_resources(hpriv); ++ return rc; ++} ++#endif ++ ++static SIMPLE_DEV_PM_OPS(ahci_sunxi_pm_ops, ahci_platform_suspend, ++ ahci_sunxi_resume); ++ ++static const struct of_device_id ahci_sunxi_of_match[] = { ++ { .compatible = "allwinner,sun4i-a10-ahci", }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(of, ahci_sunxi_of_match); ++ ++static struct platform_driver ahci_sunxi_driver = { ++ .probe = ahci_sunxi_probe, ++ .remove = ata_platform_remove_one, ++ .driver = { ++ .name = "ahci-sunxi", ++ .owner = THIS_MODULE, ++ .of_match_table = ahci_sunxi_of_match, ++ .pm = &ahci_sunxi_pm_ops, ++ }, ++}; ++module_platform_driver(ahci_sunxi_driver); ++ ++MODULE_DESCRIPTION("Allwinner sunxi AHCI SATA driver"); ++MODULE_AUTHOR("Olliver Schinagl "); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c +index 36605ab..f839bb3 100644 +--- a/drivers/ata/libahci.c ++++ b/drivers/ata/libahci.c +@@ -394,6 +394,9 @@ static ssize_t ahci_show_em_supported(struct device *dev, + * + * If inconsistent, config values are fixed up by this function. + * ++ * If it is not set already this function sets hpriv->start_engine to ++ * ahci_start_engine. ++ * + * LOCKING: + * None. + */ +@@ -500,6 +503,9 @@ void ahci_save_initial_config(struct device *dev, + hpriv->cap = cap; + hpriv->cap2 = cap2; + hpriv->port_map = port_map; ++ ++ if (!hpriv->start_engine) ++ hpriv->start_engine = ahci_start_engine; + } + EXPORT_SYMBOL_GPL(ahci_save_initial_config); + +@@ -766,7 +772,7 @@ static void ahci_start_port(struct ata_port *ap) + + /* enable DMA */ + if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE)) +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* turn on LEDs */ + if (ap->flags & ATA_FLAG_EM) { +@@ -1234,7 +1240,7 @@ int ahci_kick_engine(struct ata_port *ap) + + /* restart engine */ + out_restart: +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + return rc; + } + EXPORT_SYMBOL_GPL(ahci_kick_engine); +@@ -1426,6 +1432,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, + const unsigned long *timing = sata_ehc_deb_timing(&link->eh_context); + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -1443,7 +1450,7 @@ static int ahci_hardreset(struct ata_link *link, unsigned int *class, + rc = sata_link_hardreset(link, timing, deadline, &online, + ahci_check_ready); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); +@@ -2007,10 +2014,12 @@ static void ahci_thaw(struct ata_port *ap) + + void ahci_error_handler(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; ++ + if (!(ap->pflags & ATA_PFLAG_FROZEN)) { + /* restart engine */ + ahci_stop_engine(ap); +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + sata_pmp_error_handler(ap); +@@ -2031,6 +2040,7 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) + + static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + struct ata_device *dev = ap->link.device; + u32 devslp, dm, dito, mdat, deto; +@@ -2094,7 +2104,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + PORT_DEVSLP_ADSE); + writel(devslp, port_mmio + PORT_DEVSLP); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + /* enable device sleep feature for the drive */ + err_mask = ata_dev_set_feature(dev, +@@ -2106,6 +2116,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) + + static void ahci_enable_fbs(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; +@@ -2134,11 +2145,12 @@ static void ahci_enable_fbs(struct ata_port *ap) + } else + dev_err(ap->host->dev, "Failed to enable FBS\n"); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + static void ahci_disable_fbs(struct ata_port *ap) + { ++ struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; +@@ -2166,7 +2178,7 @@ static void ahci_disable_fbs(struct ata_port *ap) + pp->fbs_enabled = false; + } + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + } + + static void ahci_pmp_attach(struct ata_port *ap) +diff --git a/drivers/ata/sata_highbank.c b/drivers/ata/sata_highbank.c +index 870b11e..b3b18d1 100644 +--- a/drivers/ata/sata_highbank.c ++++ b/drivers/ata/sata_highbank.c +@@ -403,6 +403,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, + static const unsigned long timing[] = { 5, 100, 500}; + struct ata_port *ap = link->ap; + struct ahci_port_priv *pp = ap->private_data; ++ struct ahci_host_priv *hpriv = ap->host->private_data; + u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; + struct ata_taskfile tf; + bool online; +@@ -431,7 +432,7 @@ static int ahci_highbank_hardreset(struct ata_link *link, unsigned int *class, + break; + } while (!online && retry--); + +- ahci_start_engine(ap); ++ hpriv->start_engine(ap); + + if (online) + *class = ahci_dev_classify(ap); +diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c +index abb6c5a..a4b5025 100644 +--- a/drivers/clk/sunxi/clk-sunxi.c ++++ b/drivers/clk/sunxi/clk-sunxi.c +@@ -51,6 +51,8 @@ static void __init sun4i_osc_clk_setup(struct device_node *node) + if (!gate) + goto err_free_fixed; + ++ of_property_read_string(node, "clock-output-names", &clk_name); ++ + /* set up gate and fixed rate properties */ + gate->reg = of_iomap(node, 0); + gate->bit_idx = SUNXI_OSC24M_GATE; +@@ -249,7 +251,38 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate, + *n = DIV_ROUND_UP(div, (*k+1)); + } + ++/** ++ * sun6i_a31_get_pll6_factors() - calculates n, k factors for A31 PLL6 ++ * PLL6 rate is calculated as follows ++ * rate = parent_rate * n * (k + 1) / 2 ++ * parent_rate is always 24Mhz ++ */ ++ ++static void sun6i_a31_get_pll6_factors(u32 *freq, u32 parent_rate, ++ u8 *n, u8 *k, u8 *m, u8 *p) ++{ ++ u8 div; ++ ++ /* ++ * We always have 24MHz / 2, so we can just say that our ++ * parent clock is 12MHz. ++ */ ++ parent_rate = parent_rate / 2; ++ ++ /* Normalize value to a parent_rate multiple (24M / 2) */ ++ div = *freq / parent_rate; ++ *freq = parent_rate * div; + ++ /* we were called to round the frequency, we can now return */ ++ if (n == NULL) ++ return; ++ ++ *k = div / 32; ++ if (*k > 3) ++ *k = 3; ++ ++ *n = DIV_ROUND_UP(div, (*k+1)); ++} + + /** + * sun4i_get_apb1_factors() - calculates m, p factors for APB1 +@@ -265,7 +298,7 @@ static void sun4i_get_apb1_factors(u32 *freq, u32 parent_rate, + if (parent_rate < *freq) + *freq = parent_rate; + +- parent_rate = (parent_rate + (*freq - 1)) / *freq; ++ parent_rate = DIV_ROUND_UP(parent_rate, *freq); + + /* Invalid rate! */ + if (parent_rate > 32) +@@ -310,7 +343,7 @@ static void sun4i_get_mod0_factors(u32 *freq, u32 parent_rate, + if (*freq > parent_rate) + *freq = parent_rate; + +- div = parent_rate / *freq; ++ div = DIV_ROUND_UP(parent_rate, *freq); + + if (div < 16) + calcp = 0; +@@ -351,7 +384,7 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, + if (*freq > parent_rate) + *freq = parent_rate; + +- div = parent_rate / *freq; ++ div = DIV_ROUND_UP(parent_rate, *freq); + + if (div < 32) + calcp = 0; +@@ -377,6 +410,102 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate, + + + /** ++ * sun7i_a20_gmac_clk_setup - Setup function for A20/A31 GMAC clock module ++ * ++ * This clock looks something like this ++ * ________________________ ++ * MII TX clock from PHY >-----|___________ _________|----> to GMAC core ++ * GMAC Int. RGMII TX clk >----|___________\__/__gate---|----> to PHY ++ * Ext. 125MHz RGMII TX clk >--|__divider__/ | ++ * |________________________| ++ * ++ * The external 125 MHz reference is optional, i.e. GMAC can use its ++ * internal TX clock just fine. The A31 GMAC clock module does not have ++ * the divider controls for the external reference. ++ * ++ * To keep it simple, let the GMAC use either the MII TX clock for MII mode, ++ * and its internal TX clock for GMII and RGMII modes. The GMAC driver should ++ * select the appropriate source and gate/ungate the output to the PHY. ++ * ++ * Only the GMAC should use this clock. Altering the clock so that it doesn't ++ * match the GMAC's operation parameters will result in the GMAC not being ++ * able to send traffic out. The GMAC driver should set the clock rate and ++ * enable/disable this clock to configure the required state. The clock ++ * driver then responds by auto-reparenting the clock. ++ */ ++ ++#define SUN7I_A20_GMAC_GPIT 2 ++#define SUN7I_A20_GMAC_MASK 0x3 ++#define SUN7I_A20_GMAC_PARENTS 2 ++ ++static void __init sun7i_a20_gmac_clk_setup(struct device_node *node) ++{ ++ struct clk *clk; ++ struct clk_mux *mux; ++ struct clk_gate *gate; ++ const char *clk_name = node->name; ++ const char *parents[SUN7I_A20_GMAC_PARENTS]; ++ void *reg; ++ ++ if (of_property_read_string(node, "clock-output-names", &clk_name)) ++ return; ++ ++ /* allocate mux and gate clock structs */ ++ mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL); ++ if (!mux) ++ return; ++ ++ gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); ++ if (!gate) ++ goto free_mux; ++ ++ /* gmac clock requires exactly 2 parents */ ++ parents[0] = of_clk_get_parent_name(node, 0); ++ parents[1] = of_clk_get_parent_name(node, 1); ++ if (!parents[0] || !parents[1]) ++ goto free_gate; ++ ++ reg = of_iomap(node, 0); ++ if (!reg) ++ goto free_gate; ++ ++ /* set up gate and fixed rate properties */ ++ gate->reg = reg; ++ gate->bit_idx = SUN7I_A20_GMAC_GPIT; ++ gate->lock = &clk_lock; ++ mux->reg = reg; ++ mux->mask = SUN7I_A20_GMAC_MASK; ++ mux->flags = CLK_MUX_INDEX_BIT; ++ mux->lock = &clk_lock; ++ ++ clk = clk_register_composite(NULL, clk_name, ++ parents, SUN7I_A20_GMAC_PARENTS, ++ &mux->hw, &clk_mux_ops, ++ NULL, NULL, ++ &gate->hw, &clk_gate_ops, ++ 0); ++ ++ if (IS_ERR(clk)) ++ goto iounmap_reg; ++ ++ of_clk_add_provider(node, of_clk_src_simple_get, clk); ++ clk_register_clkdev(clk, clk_name, NULL); ++ ++ return; ++ ++iounmap_reg: ++ iounmap(reg); ++free_gate: ++ kfree(gate); ++free_mux: ++ kfree(mux); ++} ++CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk", ++ sun7i_a20_gmac_clk_setup); ++ ++ ++ ++/** + * sunxi_factors_clk_setup() - Setup function for factor clocks + */ + +@@ -387,6 +516,7 @@ struct factors_data { + int mux; + struct clk_factors_config *table; + void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); ++ const char *name; + }; + + static struct clk_factors_config sun4i_pll1_config = { +@@ -416,6 +546,13 @@ static struct clk_factors_config sun4i_pll5_config = { + .kwidth = 2, + }; + ++static struct clk_factors_config sun6i_a31_pll6_config = { ++ .nshift = 8, ++ .nwidth = 5, ++ .kshift = 4, ++ .kwidth = 2, ++}; ++ + static struct clk_factors_config sun4i_apb1_config = { + .mshift = 0, + .mwidth = 5, +@@ -455,6 +592,20 @@ static const struct factors_data sun4i_pll5_data __initconst = { + .enable = 31, + .table = &sun4i_pll5_config, + .getter = sun4i_get_pll5_factors, ++ .name = "pll5", ++}; ++ ++static const struct factors_data sun4i_pll6_data __initconst = { ++ .enable = 31, ++ .table = &sun4i_pll5_config, ++ .getter = sun4i_get_pll5_factors, ++ .name = "pll6", ++}; ++ ++static const struct factors_data sun6i_a31_pll6_data __initconst = { ++ .enable = 31, ++ .table = &sun6i_a31_pll6_config, ++ .getter = sun6i_a31_get_pll6_factors, + }; + + static const struct factors_data sun4i_apb1_data __initconst = { +@@ -497,14 +648,14 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node, + (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + +- /* Nodes should be providing the name via clock-output-names +- * but originally our dts didn't, and so we used node->name. +- * The new, better nodes look like clk@deadbeef, so we pull the +- * name just in this case */ +- if (!strcmp("clk", clk_name)) { +- of_property_read_string_index(node, "clock-output-names", +- 0, &clk_name); +- } ++ /* ++ * some factor clocks, such as pll5 and pll6, may have multiple ++ * outputs, and have their name designated in factors_data ++ */ ++ if (data->name) ++ clk_name = data->name; ++ else ++ of_property_read_string(node, "clock-output-names", &clk_name); + + factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); + if (!factors) +@@ -601,6 +752,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, + (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + ++ of_property_read_string(node, "clock-output-names", &clk_name); ++ + clk = clk_register_mux(NULL, clk_name, parents, i, + CLK_SET_RATE_NO_REPARENT, reg, + data->shift, SUNXI_MUX_GATE_WIDTH, +@@ -660,6 +813,8 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, + + clk_parent = of_clk_get_parent_name(node, 0); + ++ of_property_read_string(node, "clock-output-names", &clk_name); ++ + clk = clk_register_divider(NULL, clk_name, clk_parent, 0, + reg, data->shift, data->width, + data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, +@@ -832,7 +987,7 @@ static const struct divs_data pll5_divs_data __initconst = { + }; + + static const struct divs_data pll6_divs_data __initconst = { +- .factors = &sun4i_pll5_data, ++ .factors = &sun4i_pll6_data, + .div = { + { .shift = 0, .table = pll6_sata_tbl, .gate = 14 }, /* M, SATA */ + { .fixed = 2 }, /* P, other */ +@@ -854,7 +1009,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, + struct divs_data *data) + { + struct clk_onecell_data *clk_data; +- const char *parent = node->name; ++ const char *parent; + const char *clk_name; + struct clk **clks, *pclk; + struct clk_hw *gate_hw, *rate_hw; +@@ -868,6 +1023,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node, + + /* Set up factor clock that we will be dividing */ + pclk = sunxi_factors_clk_setup(node, data->factors); ++ parent = __clk_get_name(pclk); + + reg = of_iomap(node, 0); + +@@ -972,6 +1128,7 @@ free_clkdata: + static const struct of_device_id clk_factors_match[] __initconst = { + {.compatible = "allwinner,sun4i-pll1-clk", .data = &sun4i_pll1_data,}, + {.compatible = "allwinner,sun6i-a31-pll1-clk", .data = &sun6i_a31_pll1_data,}, ++ {.compatible = "allwinner,sun6i-a31-pll6-clk", .data = &sun6i_a31_pll6_data,}, + {.compatible = "allwinner,sun4i-apb1-clk", .data = &sun4i_apb1_data,}, + {.compatible = "allwinner,sun4i-mod0-clk", .data = &sun4i_mod0_data,}, + {.compatible = "allwinner,sun7i-a20-out-clk", .data = &sun7i_a20_out_data,}, +diff --git a/include/linux/ahci_platform.h b/include/linux/ahci_platform.h +index 73a2500..542f268 100644 +--- a/include/linux/ahci_platform.h ++++ b/include/linux/ahci_platform.h +@@ -19,7 +19,15 @@ + + struct device; + struct ata_port_info; ++struct ahci_host_priv; ++struct platform_device; + ++/* ++ * Note ahci_platform_data is deprecated, it is only kept around for use ++ * by the old da850 and spear13xx ahci code. ++ * New drivers should instead declare their own platform_driver struct, and ++ * use ahci_platform* functions in their own probe, suspend and resume methods. ++ */ + struct ahci_platform_data { + int (*init)(struct device *dev, void __iomem *addr); + void (*exit)(struct device *dev); +@@ -30,4 +38,21 @@ struct ahci_platform_data { + unsigned int mask_port_map; + }; + ++int ahci_platform_enable_clks(struct ahci_host_priv *hpriv); ++void ahci_platform_disable_clks(struct ahci_host_priv *hpriv); ++int ahci_platform_enable_resources(struct ahci_host_priv *hpriv); ++void ahci_platform_disable_resources(struct ahci_host_priv *hpriv); ++struct ahci_host_priv *ahci_platform_get_resources( ++ struct platform_device *pdev); ++int ahci_platform_init_host(struct platform_device *pdev, ++ struct ahci_host_priv *hpriv, ++ const struct ata_port_info *pi_template, ++ unsigned int force_port_map, ++ unsigned int mask_port_map); ++ ++int ahci_platform_suspend_host(struct device *dev); ++int ahci_platform_resume_host(struct device *dev); ++int ahci_platform_suspend(struct device *dev); ++int ahci_platform_resume(struct device *dev); ++ + #endif /* _AHCI_PLATFORM_H */ diff --git a/debian/patches/series b/debian/patches/series index 14c46b37d..ab0eb16d0 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -73,3 +73,4 @@ features/all/mvsas-Recognise-device-subsystem-9485-9485-as-88SE94.patch bugfix/arm/bfa-Replace-large-udelay-with-mdelay.patch features/all/support-Thinkpad-X1-Carbon-2nd-generation-s-adaptive.patch features/all/save-and-restore-adaptive-keyboard-mode-for-suspend-.patch +features/all/ARM-sunxi-ahci-and-gmac.patch