]> git.ipfire.org Git - people/ms/u-boot.git/commitdiff
ahci: provide sunxi SATA driver using AHCI platform framework
authorIan Campbell <ijc@hellion.org.uk>
Fri, 18 Jul 2014 19:38:41 +0000 (20:38 +0100)
committerHans de Goede <hdegoede@redhat.com>
Thu, 31 Jul 2014 13:37:22 +0000 (15:37 +0200)
This enables the necessary clocks, in AHB0 and in PLL6_CFG. This is done
for sun7i only since I don't have access to any other sunxi platforms
with sata included.

The PHY setup is derived from the Alwinner releases and Linux, but is mostly
undocumented.

The Allwinner AHCI controller also requires some magic (and, again,
undocumented) DMA initialisation when starting a port.  This is added under a
suitable ifdef.

This option is enabled for Cubieboard, Cubieboard2 and Cubietruck based on
contents of Linux DTS files, including SATA power pin config taken from the
DTS. All build tested, but runtime tested on Cubieboard2 and Cubietruck only.

Signed-off-by: Ian Campbell <ijc@hellion.org.uk>
Acked-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
12 files changed:
arch/arm/cpu/armv7/sunxi/clock_sun4i.c
arch/arm/include/asm/arch-sunxi/clock_sun4i.h
board/sunxi/Makefile
board/sunxi/ahci.c [new file with mode: 0644]
configs/Cubieboard2_FEL_defconfig
configs/Cubieboard2_defconfig
configs/Cubieboard_defconfig
configs/Cubietruck_FEL_defconfig
configs/Cubietruck_defconfig
drivers/block/ahci.c
include/ahci.h
include/configs/sunxi-common.h

index b8b16cff951aee8a8ac1d65d27a0aeda3bbd1310..ecbdb0162b2efcd17f6c6ee2a357543e2a71cab2 100644 (file)
@@ -39,6 +39,10 @@ void clock_init_safe(void)
        setbits_le32(&ccm->ahb_gate0, 0x1 << AHB_GATE_OFFSET_DMA);
 #endif
        writel(PLL6_CFG_DEFAULT, &ccm->pll6_cfg);
+#ifdef CONFIG_SUNXI_AHCI
+       setbits_le32(&ccm->ahb_gate0, 0x1 << AHB_GATE_OFFSET_SATA);
+       setbits_le32(&ccm->pll6_cfg, 0x1 << CCM_PLL6_CTRL_SATA_EN_SHIFT);
+#endif
 }
 #endif
 
index 928f3f2670997285bdeb34aaa749e00d44a9d9fb..2531cbdcd595b66195111420dc6b5d1e77e5e0eb 100644 (file)
@@ -218,10 +218,13 @@ struct sunxi_ccm_reg {
 #define CCM_PLL5_CTRL_BYPASS (0x1 << 30)
 #define CCM_PLL5_CTRL_EN (0x1 << 31)
 
-#define CCM_PLL6_CTRL_N_SHIFT  8
-#define CCM_PLL6_CTRL_N_MASK   (0x1f << CCM_PLL6_CTRL_N_SHIFT)
-#define CCM_PLL6_CTRL_K_SHIFT  4
-#define CCM_PLL6_CTRL_K_MASK   (0x3 << CCM_PLL6_CTRL_K_SHIFT)
+#define CCM_PLL6_CTRL_EN               31
+#define CCM_PLL6_CTRL_BYPASS_EN                30
+#define CCM_PLL6_CTRL_SATA_EN_SHIFT    14
+#define CCM_PLL6_CTRL_N_SHIFT          8
+#define CCM_PLL6_CTRL_N_MASK           (0x1f << CCM_PLL6_CTRL_N_SHIFT)
+#define CCM_PLL6_CTRL_K_SHIFT          4
+#define CCM_PLL6_CTRL_K_MASK           (0x3 << CCM_PLL6_CTRL_K_SHIFT)
 
 #define CCM_GPS_CTRL_RESET (0x1 << 0)
 #define CCM_GPS_CTRL_GATE (0x1 << 1)
index 62acb8ff27a272aec8f2fbb8d1d923df5afc3b3c..03f55cc49d1345c943fe72d18e290a81f815b032 100644 (file)
@@ -10,6 +10,7 @@
 #
 obj-y  += board.o
 obj-$(CONFIG_SUNXI_GMAC)       += gmac.o
+obj-$(CONFIG_SUNXI_AHCI)       += ahci.o
 obj-$(CONFIG_A13_OLINUXINOM)   += dram_a13_oli_micro.o
 obj-$(CONFIG_CUBIEBOARD)       += dram_cubieboard.o
 obj-$(CONFIG_CUBIEBOARD2)      += dram_cubieboard2.o
diff --git a/board/sunxi/ahci.c b/board/sunxi/ahci.c
new file mode 100644 (file)
index 0000000..0c262ea
--- /dev/null
@@ -0,0 +1,84 @@
+#include <common.h>
+#include <ahci.h>
+#include <scsi.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+#define AHCI_PHYCS0R 0x00c0
+#define AHCI_PHYCS1R 0x00c4
+#define AHCI_PHYCS2R 0x00c8
+#define AHCI_RWCR    0x00fc
+
+/* This magic PHY initialisation was taken from the Allwinner releases
+ * and Linux driver, but is completely undocumented.
+ */
+static int sunxi_ahci_phy_init(u32 base)
+{
+       u8 *reg_base = (u8 *)base;
+       u32 reg_val;
+       int timeout;
+
+       writel(0, reg_base + AHCI_RWCR);
+       mdelay(5);
+
+       setbits_le32(reg_base + AHCI_PHYCS1R, 0x1 << 19);
+       clrsetbits_le32(reg_base + AHCI_PHYCS0R,
+                       (0x7 << 24),
+                       (0x5 << 24) | (0x1 << 23) | (0x1 << 18));
+       clrsetbits_le32(reg_base + AHCI_PHYCS1R,
+                       (0x3 << 16) | (0x1f << 8) | (0x3 << 6),
+                       (0x2 << 16) | (0x6 << 8) | (0x2 << 6));
+       setbits_le32(reg_base + AHCI_PHYCS1R, (0x1 << 28) | (0x1 << 15));
+       clrbits_le32(reg_base + AHCI_PHYCS1R, (0x1 << 19));
+       clrsetbits_le32(reg_base + AHCI_PHYCS0R, (0x7 << 20), (0x3 << 20));
+       clrsetbits_le32(reg_base + AHCI_PHYCS2R, (0x1f << 5), (0x19 << 5));
+       mdelay(5);
+
+       setbits_le32(reg_base + AHCI_PHYCS0R, (0x1 << 19));
+
+       timeout = 250; /* Power up takes approx 50 us */
+       for (;;) {
+               reg_val = readl(reg_base + AHCI_PHYCS0R) & (0x7 << 28);
+               if (reg_val == (0x2 << 28))
+                       break;
+               if (--timeout == 0) {
+                       printf("AHCI PHY power up failed.\n");
+                       return -EIO;
+               }
+               udelay(1);
+       };
+
+       setbits_le32(reg_base + AHCI_PHYCS2R, (0x1 << 24));
+
+       timeout = 100; /* Calibration takes approx 10 us */
+       for (;;) {
+               reg_val = readl(reg_base + AHCI_PHYCS2R) & (0x1 << 24);
+               if (reg_val == 0x0)
+                       break;
+               if (--timeout == 0) {
+                       printf("AHCI PHY calibration failed.\n");
+                       return -EIO;
+               }
+               udelay(1);
+       }
+
+       mdelay(15);
+
+       writel(0x7, reg_base + AHCI_RWCR);
+
+       return 0;
+}
+
+void scsi_init(void)
+{
+       printf("SUNXI SCSI INIT\n");
+#ifdef CONFIG_SATAPWR
+       gpio_direction_output(CONFIG_SATAPWR, 1);
+#endif
+
+       if (sunxi_ahci_phy_init(SUNXI_SATA_BASE) < 0)
+               return;
+
+       ahci_init(SUNXI_SATA_BASE);
+}
index 08f31591c2a983748484d4da12e3ff869a373207..4eddd089691c0d2df5ee6a3bdc8e05c0cd0091c6 100644 (file)
@@ -1,4 +1,4 @@
 CONFIG_SPL=y
-CONFIG_SYS_EXTRA_OPTIONS="CUBIEBOARD2,SPL_FEL,SUNXI_GMAC"
+CONFIG_SYS_EXTRA_OPTIONS="CUBIEBOARD2,SPL_FEL,SUNXI_GMAC,AHCI,SATAPWR=SUNXI_GPB(8)"
 +S:CONFIG_ARM=y
 +S:CONFIG_TARGET_SUN7I=y
index 122dac94fb4c9519edc01276b9e895b12dae50b9..ce06fe9ab13f7cc4c715423a4da4ad0793983615 100644 (file)
@@ -1,4 +1,4 @@
 CONFIG_SPL=y
-CONFIG_SYS_EXTRA_OPTIONS="CUBIEBOARD2,SPL,SUNXI_GMAC"
+CONFIG_SYS_EXTRA_OPTIONS="CUBIEBOARD2,SPL,SUNXI_GMAC,AHCI,SATAPWR=SUNXI_GPB(8)"
 +S:CONFIG_ARM=y
 +S:CONFIG_TARGET_SUN7I=y
index 29bf8361e1bbccee3b76b2a4c2c59fb30b48fe8b..7646ea4a37adff9568fce37015de880bc6f4b724 100644 (file)
@@ -1,4 +1,4 @@
 CONFIG_SPL=y
-CONFIG_SYS_EXTRA_OPTIONS="CUBIEBOARD,SPL,AXP209_POWER,SUNXI_EMAC"
+CONFIG_SYS_EXTRA_OPTIONS="CUBIEBOARD,SPL,AXP209_POWER,SUNXI_EMAC,AHCI,SATAPWR=SUNXI_GPB(8)"
 +S:CONFIG_ARM=y
 +S:CONFIG_TARGET_SUN4I=y
index b95c5fab28b4ef06fb2cd4f2db2acf79bb3a8466..73867f725fa6610779fff46b359f617b2d44710e 100644 (file)
@@ -1,4 +1,4 @@
 CONFIG_SPL=y
-CONFIG_SYS_EXTRA_OPTIONS="CUBIETRUCK,SPL_FEL,AXP209_POWER,SUNXI_GMAC,RGMII"
+CONFIG_SYS_EXTRA_OPTIONS="CUBIETRUCK,SPL_FEL,AXP209_POWER,SUNXI_GMAC,RGMII,AHCI,SATAPWR=SUNXI_GPH(12)"
 +S:CONFIG_ARM=y
 +S:CONFIG_TARGET_SUN7I=y
index 4c1e9a3987bf8ec50c4564c00a61b06be0443a18..eb460ac9b1bdde1de43b4ea2c7fead0dd9ae1d3f 100644 (file)
@@ -1,4 +1,4 @@
 CONFIG_SPL=y
-CONFIG_SYS_EXTRA_OPTIONS="CUBIETRUCK,SPL,AXP209_POWER,SUNXI_GMAC,RGMII"
+CONFIG_SYS_EXTRA_OPTIONS="CUBIETRUCK,SPL,AXP209_POWER,SUNXI_GMAC,RGMII,AHCI,SATAPWR=SUNXI_GPH(12)"
 +S:CONFIG_ARM=y
 +S:CONFIG_TARGET_SUN7I=y
index 4df804671a8b513c6125ac6919384df6cd8170c4..dce99adc6b58cfbcf00530c0d84c1963357a5aaf 100644 (file)
@@ -129,6 +129,14 @@ int __weak ahci_link_up(struct ahci_probe_ent *probe_ent, u8 port)
        return 1;
 }
 
+#ifdef CONFIG_SUNXI_AHCI
+/* The sunxi AHCI controller requires this undocumented setup */
+static void sunxi_dma_init(volatile u8 *port_mmio)
+{
+       clrsetbits_le32(port_mmio + PORT_P0DMACR, 0x0000ff00, 0x00004400);
+}
+#endif
+
 static int ahci_host_init(struct ahci_probe_ent *probe_ent)
 {
 #ifndef CONFIG_SCSI_AHCI_PLAT
@@ -213,6 +221,10 @@ static int ahci_host_init(struct ahci_probe_ent *probe_ent)
                        msleep(500);
                }
 
+#ifdef CONFIG_SUNXI_AHCI
+               sunxi_dma_init(port_mmio);
+#endif
+
                /* Add the spinup command to whatever mode bits may
                 * already be on in the command register.
                 */
@@ -545,6 +557,10 @@ static int ahci_port_start(u8 port)
 
        writel_with_flush(pp->rx_fis, port_mmio + PORT_FIS_ADDR);
 
+#ifdef CONFIG_SUNXI_AHCI
+       sunxi_dma_init(port_mmio);
+#endif
+
        writel_with_flush(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
                          PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
                          PORT_CMD_START, port_mmio + PORT_CMD);
index 90e850929b7d4c625dee3baeb7ed4307af2fe0cc..35b8a8c09b1879530e971f9583e40e253a980f9a 100644 (file)
 #define PORT_SCR_ERR           0x30 /* SATA phy register: SError */
 #define PORT_SCR_ACT           0x34 /* SATA phy register: SActive */
 
+#ifdef CONFIG_SUNXI_AHCI
+#define PORT_P0DMACR           0x70 /* SUNXI specific "DMA register" */
+#endif
+
 /* PORT_IRQ_{STAT,MASK} bits */
 #define PORT_IRQ_COLD_PRES     (1 << 31) /* cold presence detect */
 #define PORT_IRQ_TF_ERR                (1 << 30) /* task file error */
index 8ab6429e482abaac92383964a46c6c035917c607..1505510cda86b3aeb738e5ae1572f8574bf4ebf4 100644 (file)
 #define PHYS_SDRAM_0                   CONFIG_SYS_SDRAM_BASE
 #define PHYS_SDRAM_0_SIZE              0x80000000 /* 2 GiB */
 
+#ifdef CONFIG_AHCI
+#define CONFIG_LIBATA
+#define CONFIG_SCSI_AHCI
+#define CONFIG_SCSI_AHCI_PLAT
+#define CONFIG_SUNXI_AHCI
+#define CONFIG_SYS_SCSI_MAX_SCSI_ID    1
+#define CONFIG_SYS_SCSI_MAX_LUN                1
+#define CONFIG_SYS_SCSI_MAX_DEVICE     (CONFIG_SYS_SCSI_MAX_SCSI_ID * \
+                                        CONFIG_SYS_SCSI_MAX_LUN)
+#define CONFIG_CMD_SCSI
+#endif
+
 #define CONFIG_CMD_MEMORY
 #define CONFIG_CMD_SETEXPR