From: Ian Campbell Date: Fri, 18 Jul 2014 19:38:41 +0000 (+0100) Subject: ahci: provide sunxi SATA driver using AHCI platform framework X-Git-Url: http://git.dujemihanovic.xyz/?a=commitdiff_plain;h=a6e50a88d8d3724fc75f1c6959b80a6c7c69cbad;p=u-boot.git ahci: provide sunxi SATA driver using AHCI platform framework 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 Acked-by: Hans de Goede Signed-off-by: Hans de Goede --- diff --git a/arch/arm/cpu/armv7/sunxi/clock_sun4i.c b/arch/arm/cpu/armv7/sunxi/clock_sun4i.c index b8b16cff95..ecbdb0162b 100644 --- a/arch/arm/cpu/armv7/sunxi/clock_sun4i.c +++ b/arch/arm/cpu/armv7/sunxi/clock_sun4i.c @@ -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 diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h index 928f3f2670..2531cbdcd5 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h @@ -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) diff --git a/board/sunxi/Makefile b/board/sunxi/Makefile index 62acb8ff27..03f55cc49d 100644 --- a/board/sunxi/Makefile +++ b/board/sunxi/Makefile @@ -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 index 0000000000..0c262eabb7 --- /dev/null +++ b/board/sunxi/ahci.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/configs/Cubieboard2_FEL_defconfig b/configs/Cubieboard2_FEL_defconfig index 08f31591c2..4eddd08969 100644 --- a/configs/Cubieboard2_FEL_defconfig +++ b/configs/Cubieboard2_FEL_defconfig @@ -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 diff --git a/configs/Cubieboard2_defconfig b/configs/Cubieboard2_defconfig index 122dac94fb..ce06fe9ab1 100644 --- a/configs/Cubieboard2_defconfig +++ b/configs/Cubieboard2_defconfig @@ -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 diff --git a/configs/Cubieboard_defconfig b/configs/Cubieboard_defconfig index 29bf8361e1..7646ea4a37 100644 --- a/configs/Cubieboard_defconfig +++ b/configs/Cubieboard_defconfig @@ -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 diff --git a/configs/Cubietruck_FEL_defconfig b/configs/Cubietruck_FEL_defconfig index b95c5fab28..73867f725f 100644 --- a/configs/Cubietruck_FEL_defconfig +++ b/configs/Cubietruck_FEL_defconfig @@ -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 diff --git a/configs/Cubietruck_defconfig b/configs/Cubietruck_defconfig index 4c1e9a3987..eb460ac9b1 100644 --- a/configs/Cubietruck_defconfig +++ b/configs/Cubietruck_defconfig @@ -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 diff --git a/drivers/block/ahci.c b/drivers/block/ahci.c index 4df804671a..dce99adc6b 100644 --- a/drivers/block/ahci.c +++ b/drivers/block/ahci.c @@ -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); diff --git a/include/ahci.h b/include/ahci.h index 90e850929b..35b8a8c09b 100644 --- a/include/ahci.h +++ b/include/ahci.h @@ -58,6 +58,10 @@ #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 */ diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index 8ab6429e48..1505510cda 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -57,6 +57,18 @@ #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