[U-Boot] [PATCH 23/33] x86: ivybridge: Add SATA init
Bin Meng
bmeng.cn at gmail.com
Wed Nov 12 09:03:10 CET 2014
Hi Simon,
On Wed, Nov 12, 2014 at 8:18 AM, Simon Glass <sjg at chromium.org> wrote:
> Add code to set up the SATA interfaces on boot.
>
> Signed-off-by: Simon Glass <sjg at chromium.org>
> ---
>
> arch/x86/cpu/ivybridge/Makefile | 1 +
> arch/x86/cpu/ivybridge/bd82x6x.c | 20 +++
> arch/x86/cpu/ivybridge/sata.c | 243 ++++++++++++++++++++++++++
> arch/x86/include/asm/arch-ivybridge/bd82x6x.h | 2 +
> arch/x86/include/asm/arch-ivybridge/pch.h | 58 ++++++
> doc/device-tree-bindings/ata/intel-sata.txt | 26 +++
> include/fdtdec.h | 1 +
> lib/fdtdec.c | 1 +
> 8 files changed, 352 insertions(+)
> create mode 100644 arch/x86/cpu/ivybridge/sata.c
> create mode 100644 doc/device-tree-bindings/ata/intel-sata.txt
>
> diff --git a/arch/x86/cpu/ivybridge/Makefile b/arch/x86/cpu/ivybridge/Makefile
> index 479e446..c6342cd 100644
> --- a/arch/x86/cpu/ivybridge/Makefile
> +++ b/arch/x86/cpu/ivybridge/Makefile
> @@ -15,4 +15,5 @@ obj-y += microcode_intel.o
> obj-y += pch.o
> obj-y += pci.o
> obj-y += report_platform.o
> +obj-y += sata.o
> obj-y += sdram.o
> diff --git a/arch/x86/cpu/ivybridge/bd82x6x.c b/arch/x86/cpu/ivybridge/bd82x6x.c
> index be4db74..b54f5c7 100644
> --- a/arch/x86/cpu/ivybridge/bd82x6x.c
> +++ b/arch/x86/cpu/ivybridge/bd82x6x.c
> @@ -88,18 +88,38 @@ void bd82x6x_pci_bus_enable_resources(pci_dev_t dev)
>
> int bd82x6x_init_pci_devices(void)
> {
> + const void *blob = gd->fdt_blob;
> struct pci_controller *hose;
> + int sata_node;
>
> hose = pci_bus_to_hose(0);
> lpc_enable(PCH_LPC_DEV);
> lpc_init(hose, PCH_LPC_DEV);
> + sata_node = fdtdec_next_compatible(blob, 0,
> + COMPAT_INTEL_PANTHERPOINT_AHCI);
> + if (sata_node < 0) {
> + debug("%s: Cannot find SATA node\n", __func__);
> + return -EINVAL;
> + }
> + bd82x6x_sata_init(PCH_SATA_DEV, blob, sata_node);
>
> return 0;
> }
>
> int bd82x6x_init(void)
> {
> + const void *blob = gd->fdt_blob;
> + int sata_node;
> +
> + sata_node = fdtdec_next_compatible(blob, 0,
> + COMPAT_INTEL_PANTHERPOINT_AHCI);
> + if (sata_node < 0) {
> + debug("%s: Cannot find SATA node\n", __func__);
> + return -EINVAL;
> + }
> +
> bd82x6x_pci_init(PCH_DEV);
> + bd82x6x_sata_enable(PCH_SATA_DEV, blob, sata_node);
>
> return 0;
> }
> diff --git a/arch/x86/cpu/ivybridge/sata.c b/arch/x86/cpu/ivybridge/sata.c
> new file mode 100644
> index 0000000..134afb9
> --- /dev/null
> +++ b/arch/x86/cpu/ivybridge/sata.c
> @@ -0,0 +1,243 @@
> +/*
> + * From Coreboot
> + * Copyright (C) 2008-2009 coresystems GmbH
> + *
> + * SPDX-License-Identifier: GPL-2.0
> + */
> +
> +#include <common.h>
> +#include <fdtdec.h>
> +#include <asm/io.h>
> +#include <asm/pci.h>
> +#include <asm/arch/pch.h>
> +#include <asm/arch/bd82x6x.h>
> +
> +static inline u32 sir_read(pci_dev_t dev, int idx)
> +{
> + pci_write_config32(dev, SATA_SIRI, idx);
> + return pci_read_config32(dev, SATA_SIRD);
> +}
> +
> +static inline void sir_write(pci_dev_t dev, int idx, u32 value)
> +{
> + pci_write_config32(dev, SATA_SIRI, idx);
> + pci_write_config32(dev, SATA_SIRD, value);
> +}
> +
> +void bd82x6x_sata_init(pci_dev_t dev, const void *blob, int node)
> +{
> + unsigned int port_map, speed_support, port_tx;
> + const char *mode;
> + u32 reg32;
> + u16 reg16;
> +
> + debug("SATA: Initializing...\n");
> +
> + /* SATA configuration */
> + port_map = fdtdec_get_int(blob, node, "intel,sata-port-map", 0);
> + speed_support = fdtdec_get_int(blob, node,
> + "sata_interface_speed_support", 0);
> +
> + /* Enable BARs */
> + pci_write_config16(dev, PCI_COMMAND, 0x0007);
> +
> + mode = fdt_getprop(blob, node, "intel,sata-mode", NULL);
> + if (!mode || !strcmp(mode, "ahci")) {
> + u32 abar;
> +
> + debug("SATA: Controller in AHCI mode\n");
> +
> + /* Set Interrupt Line, Interrupt Pin is set by D31IP.PIP */
> + pci_write_config8(dev, INTR_LN, 0x0a);
> +
> + /* Set timings */
> + pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
> + IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
> + IDE_PPE0 | IDE_IE0 | IDE_TIME0);
> + pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
> + IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS);
> +
> + /* Sync DMA */
> + pci_write_config16(dev, IDE_SDMA_CNT, IDE_PSDE0);
> + pci_write_config16(dev, IDE_SDMA_TIM, 0x0001);
> +
> + /* Set IDE I/O Configuration */
> + reg32 = SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 |
> + PCB0;
> + pci_write_config32(dev, IDE_CONFIG, reg32);
> +
> + /* for AHCI, Port Enable is managed in memory-mapped space */
> + reg16 = pci_read_config16(dev, 0x92);
> + reg16 &= ~0x3f; /* 6 ports SKU + ORM */
> + reg16 |= 0x8000 | port_map;
> + pci_write_config16(dev, 0x92, reg16);
> +
> + /* SATA Initialization register */
> + pci_write_config32(dev, 0x94,
> + ((port_map ^ 0x3f) << 24) | 0x183);
> +
> + /* Initialize AHCI memory-mapped space */
> + abar = pci_read_config32(dev, PCI_BASE_ADDRESS_5) & ~1;
> + debug("ABAR: %08X\n", abar);
> + /* CAP (HBA Capabilities) : enable power management */
> + reg32 = readl(abar + 0x00);
> + reg32 |= 0x0c006000; /* set PSC+SSC+SALP+SSS */
> + reg32 &= ~0x00020060; /* clear SXS+EMS+PMS */
> + /* Set ISS, if available */
> + if (speed_support) {
> + reg32 &= ~0x00f00000;
> + reg32 |= (speed_support & 0x03) << 20;
> + }
> + writel(reg32, abar + 0x00);
> + /* PI (Ports implemented) */
> + writel(port_map, abar + 0x0c);
> + (void) readl(abar + 0x0c); /* Read back 1 */
> + (void) readl(abar + 0x0c); /* Read back 2 */
> + /* CAP2 (HBA Capabilities Extended)*/
> + reg32 = readl(abar + 0x24);
> + reg32 &= ~0x00000002;
> + writel(reg32, abar + 0x24);
> + /* VSP (Vendor Specific Register */
> + reg32 = readl(abar + 0xa0);
> + reg32 &= ~0x00000005;
> + writel(reg32, abar + 0xa0);
> + } else if (!strcmp(mode, "combined")) {
> + debug("SATA: Controller in combined mode\n");
> +
> + /* No AHCI: clear AHCI base */
> + pci_write_config32(dev, 0x24, 0x00000000);
> + /* And without AHCI BAR no memory decoding */
> + reg16 = pci_read_config16(dev, PCI_COMMAND);
> + reg16 &= ~PCI_COMMAND_MEMORY;
> + pci_write_config16(dev, PCI_COMMAND, reg16);
> +
> + pci_write_config8(dev, 0x09, 0x80);
> +
> + /* Set timings */
> + pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
> + IDE_ISP_5_CLOCKS | IDE_RCT_4_CLOCKS);
> + pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
> + IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
> + IDE_PPE0 | IDE_IE0 | IDE_TIME0);
> +
> + /* Sync DMA */
> + pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0);
> + pci_write_config16(dev, IDE_SDMA_TIM, 0x0200);
> +
> + /* Set IDE I/O Configuration */
> + reg32 = SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 |
> + PCB0;
> + pci_write_config32(dev, IDE_CONFIG, reg32);
> +
> + /* Port enable */
> + reg16 = pci_read_config16(dev, 0x92);
> + reg16 &= ~0x3f;
> + reg16 |= port_map;
> + pci_write_config16(dev, 0x92, reg16);
> +
> + /* SATA Initialization register */
> + pci_write_config32(dev, 0x94,
> + ((port_map ^ 0x3f) << 24) | 0x183);
> + } else {
> + debug("SATA: Controller in plain-ide mode\n");
> +
> + /* No AHCI: clear AHCI base */
> + pci_write_config32(dev, 0x24, 0x00000000);
> +
> + /* And without AHCI BAR no memory decoding */
> + reg16 = pci_read_config16(dev, PCI_COMMAND);
> + reg16 &= ~PCI_COMMAND_MEMORY;
> + pci_write_config16(dev, PCI_COMMAND, reg16);
> +
> + /*
> + * Native mode capable on both primary and secondary (0xa)
> + * OR'ed with enabled (0x50) = 0xf
> + */
> + pci_write_config8(dev, 0x09, 0x8f);
> +
> + /* Set Interrupt Line */
> + /* Interrupt Pin is set by D31IP.PIP */
> + pci_write_config8(dev, INTR_LN, 0xff);
> +
> + /* Set timings */
> + pci_write_config16(dev, IDE_TIM_PRI, IDE_DECODE_ENABLE |
> + IDE_ISP_3_CLOCKS | IDE_RCT_1_CLOCKS |
> + IDE_PPE0 | IDE_IE0 | IDE_TIME0);
> + pci_write_config16(dev, IDE_TIM_SEC, IDE_DECODE_ENABLE |
> + IDE_SITRE | IDE_ISP_3_CLOCKS |
> + IDE_RCT_1_CLOCKS | IDE_IE0 | IDE_TIME0);
> +
> + /* Sync DMA */
> + pci_write_config16(dev, IDE_SDMA_CNT, IDE_SSDE0 | IDE_PSDE0);
> + pci_write_config16(dev, IDE_SDMA_TIM, 0x0201);
> +
> + /* Set IDE I/O Configuration */
> + reg32 = SIG_MODE_PRI_NORMAL | FAST_PCB1 | FAST_PCB0 | PCB1 |
> + PCB0;
> + pci_write_config32(dev, IDE_CONFIG, reg32);
> +
> + /* Port enable */
> + reg16 = pci_read_config16(dev, 0x92);
> + reg16 &= ~0x3f;
> + reg16 |= port_map;
> + pci_write_config16(dev, 0x92, reg16);
> +
> + /* SATA Initialization register */
> + pci_write_config32(dev, 0x94,
> + ((port_map ^ 0x3f) << 24) | 0x183);
> + }
> +
> + /* Set Gen3 Transmitter settings if needed */
> + port_tx = fdtdec_get_int(blob, node, "intel,sata-port0-gen3-tx", 0);
> + if (port_tx)
> + pch_iobp_update(SATA_IOBP_SP0G3IR, 0, port_tx);
> +
> + port_tx = fdtdec_get_int(blob, node, "intel,sata-port1-gen3-tx", 0);
> + if (port_tx)
> + pch_iobp_update(SATA_IOBP_SP1G3IR, 0, port_tx);
> +
> + /* Additional Programming Requirements */
> + sir_write(dev, 0x04, 0x00001600);
> + sir_write(dev, 0x28, 0xa0000033);
> + reg32 = sir_read(dev, 0x54);
> + reg32 &= 0xff000000;
> + reg32 |= 0x5555aa;
> + sir_write(dev, 0x54, reg32);
> + sir_write(dev, 0x64, 0xcccc8484);
> + reg32 = sir_read(dev, 0x68);
> + reg32 &= 0xffff0000;
> + reg32 |= 0xcccc;
> + sir_write(dev, 0x68, reg32);
> + reg32 = sir_read(dev, 0x78);
> + reg32 &= 0x0000ffff;
> + reg32 |= 0x88880000;
> + sir_write(dev, 0x78, reg32);
> + sir_write(dev, 0x84, 0x001c7000);
> + sir_write(dev, 0x88, 0x88338822);
> + sir_write(dev, 0xa0, 0x001c7000);
> + sir_write(dev, 0xc4, 0x0c0c0c0c);
> + sir_write(dev, 0xc8, 0x0c0c0c0c);
> + sir_write(dev, 0xd4, 0x10000000);
> +
> + pch_iobp_update(0xea004001, 0x3fffffff, 0xc0000000);
> + pch_iobp_update(0xea00408a, 0xfffffcff, 0x00000100);
There are many magic numbers here.
> +}
> +
> +void bd82x6x_sata_enable(pci_dev_t dev, const void *blob, int node)
> +{
> + unsigned port_map;
> + const char *mode;
> + u16 map = 0;
> +
> + /*
> + * Set SATA controller mode early so the resource allocator can
> + * properly assign IO/Memory resources for the controller.
> + */
> + mode = fdt_getprop(blob, node, "intel,sata-mode", NULL);
> + if (mode && !strcmp(mode, "ahci"))
> + map = 0x0060;
> + port_map = fdtdec_get_int(blob, node, "intel,sata-port-map", 0);
> +
> + map |= (port_map ^ 0x3f) << 8;
> + pci_write_config16(dev, 0x90, map);
> +}
> diff --git a/arch/x86/include/asm/arch-ivybridge/bd82x6x.h b/arch/x86/include/asm/arch-ivybridge/bd82x6x.h
> index e02520c..644755f 100644
> --- a/arch/x86/include/asm/arch-ivybridge/bd82x6x.h
> +++ b/arch/x86/include/asm/arch-ivybridge/bd82x6x.h
> @@ -7,6 +7,8 @@
> #ifndef _ASM_ARCH_BD82X6X_H
> #define _ASM_ARCH_BD82X6X_H
>
> +void bd82x6x_sata_init(pci_dev_t dev, const void *blob, int node);
> +void bd82x6x_sata_enable(pci_dev_t dev, const void *blob, int node);
> void bd82x6x_pci_init(pci_dev_t dev);
> int bd82x6x_init_pci_devices(void);
> int bd82x6x_init(void);
> diff --git a/arch/x86/include/asm/arch-ivybridge/pch.h b/arch/x86/include/asm/arch-ivybridge/pch.h
> index b3ec7c5..21df083 100644
> --- a/arch/x86/include/asm/arch-ivybridge/pch.h
> +++ b/arch/x86/include/asm/arch-ivybridge/pch.h
> @@ -122,6 +122,64 @@ void pch_iobp_update(u32 address, u32 andvalue, u32 orvalue);
> #define LPC_GEN4_DEC 0x90 /* LPC IF Generic Decode Range 4 */
> #define LPC_GENX_DEC(x) (0x84 + 4 * (x))
>
> +/* PCI Configuration Space (D31:F1): IDE */
> +#define PCH_IDE_DEV PCI_BDF(0, 0x1f, 1)
> +#define PCH_SATA_DEV PCI_BDF(0, 0x1f, 2)
> +#define PCH_SATA2_DEV PCI_BDF(0, 0x1f, 5)
> +
> +#define INTR_LN 0x3c
> +#define IDE_TIM_PRI 0x40 /* IDE timings, primary */
> +#define IDE_DECODE_ENABLE (1 << 15)
> +#define IDE_SITRE (1 << 14)
> +#define IDE_ISP_5_CLOCKS (0 << 12)
> +#define IDE_ISP_4_CLOCKS (1 << 12)
> +#define IDE_ISP_3_CLOCKS (2 << 12)
> +#define IDE_RCT_4_CLOCKS (0 << 8)
> +#define IDE_RCT_3_CLOCKS (1 << 8)
> +#define IDE_RCT_2_CLOCKS (2 << 8)
> +#define IDE_RCT_1_CLOCKS (3 << 8)
> +#define IDE_DTE1 (1 << 7)
> +#define IDE_PPE1 (1 << 6)
> +#define IDE_IE1 (1 << 5)
> +#define IDE_TIME1 (1 << 4)
> +#define IDE_DTE0 (1 << 3)
> +#define IDE_PPE0 (1 << 2)
> +#define IDE_IE0 (1 << 1)
> +#define IDE_TIME0 (1 << 0)
> +#define IDE_TIM_SEC 0x42 /* IDE timings, secondary */
> +
> +#define IDE_SDMA_CNT 0x48 /* Synchronous DMA control */
> +#define IDE_SSDE1 (1 << 3)
> +#define IDE_SSDE0 (1 << 2)
> +#define IDE_PSDE1 (1 << 1)
> +#define IDE_PSDE0 (1 << 0)
> +
> +#define IDE_SDMA_TIM 0x4a
> +
> +#define IDE_CONFIG 0x54 /* IDE I/O Configuration Register */
> +#define SIG_MODE_SEC_NORMAL (0 << 18)
> +#define SIG_MODE_SEC_TRISTATE (1 << 18)
> +#define SIG_MODE_SEC_DRIVELOW (2 << 18)
> +#define SIG_MODE_PRI_NORMAL (0 << 16)
> +#define SIG_MODE_PRI_TRISTATE (1 << 16)
> +#define SIG_MODE_PRI_DRIVELOW (2 << 16)
> +#define FAST_SCB1 (1 << 15)
> +#define FAST_SCB0 (1 << 14)
> +#define FAST_PCB1 (1 << 13)
> +#define FAST_PCB0 (1 << 12)
> +#define SCB1 (1 << 3)
> +#define SCB0 (1 << 2)
> +#define PCB1 (1 << 1)
> +#define PCB0 (1 << 0)
> +
> +#define SATA_SIRI 0xa0 /* SATA Indexed Register Index */
> +#define SATA_SIRD 0xa4 /* SATA Indexed Register Data */
> +#define SATA_SP 0xd0 /* Scratchpad */
> +
> +/* SATA IOBP Registers */
> +#define SATA_IOBP_SP0G3IR 0xea000151
> +#define SATA_IOBP_SP1G3IR 0xea000051
> +
> /* PCI Configuration Space (D31:F3): SMBus */
> #define PCH_SMBUS_DEV PCI_BDF(0, 0x1f, 3)
> #define SMB_BASE 0x20
> diff --git a/doc/device-tree-bindings/ata/intel-sata.txt b/doc/device-tree-bindings/ata/intel-sata.txt
> new file mode 100644
> index 0000000..5e4da83
> --- /dev/null
> +++ b/doc/device-tree-bindings/ata/intel-sata.txt
> @@ -0,0 +1,26 @@
> +Intel Pantherpoint SATA Device Binding
> +======================================
> +
> +The device tree node which describes the operation of the Intel Pantherpoint
> +SATA device is as follows:
> +
> +Required properties :
> +- compatible = "intel,pantherpoint-ahci"
> +- intel,sata-mode : string, one of:
> + "ahci" : Use AHCI mode (default)
> + "combined" : Use combined IDE + legacy mode
> + "plain-ide" : Use plain IDE mode
> +- intel,sata-port-map : Which SATA ports are enabled, bit 0=enable first port,
> + bit 1=enable second port, etc.
> +- intel,sata-port0-gen3-tx : Value for the IOBP_SP0G3IR register
> +- intel,sata-port1-gen3-tx : Value for the IOBP_SP1G3IR register
> +
> +Example
> +-------
> +
> +sata {
> + compatible = "intel,pantherpoint-ahci";
> + intel,sata-mode = "ahci";
> + intel,sata-port-map = <1>;
> + intel,sata-port0-gen3-tx = <0x00880a7f>;
> +};
> diff --git a/include/fdtdec.h b/include/fdtdec.h
> index abfd678..0a0afce 100644
> --- a/include/fdtdec.h
> +++ b/include/fdtdec.h
> @@ -120,6 +120,7 @@ enum fdt_compat_id {
> COMPAT_INTEL_LPC, /* Intel Low Pin Count I/F */
> COMPAT_INTEL_MICROCODE, /* Intel microcode update */
> COMPAT_MEMORY_SPD, /* Memory SPD information */
> + COMPAT_INTEL_PANTHERPOINT_AHCI, /* Intel Pantherpoint AHCI */
I have not started to look into the new driver model and the
of_control device tree stuff. One questoin that comes just off the top
of my head is that: why should we add the PCI device into the device
tree? The device tree spec is designed to describe the devices which
cannot be probed by some mechanism. For devices under such buses like
PCI/USB, we can easily identify the device via vendor id and device
id, thus there is no need to describe them in the device tree. I keep
seeing we update the fdt_compat_id which is a central database, but
eventually someday when all U-Boot drivers are converted to driver
model, it will become a huge list and unmaintainable.
>
> COMPAT_COUNT,
> };
> diff --git a/lib/fdtdec.c b/lib/fdtdec.c
> index aafc4f9..8865a26 100644
> --- a/lib/fdtdec.c
> +++ b/lib/fdtdec.c
> @@ -75,6 +75,7 @@ static const char * const compat_names[COMPAT_COUNT] = {
> COMPAT(COMPAT_INTEL_LPC, "intel,lpc"),
> COMPAT(INTEL_MICROCODE, "intel,microcode"),
> COMPAT(MEMORY_SPD, "memory-spd"),
> + COMPAT(INTEL_PANTHERPOINT_AHCI, "intel,pantherpoint-ahci"),
> };
>
> const char *fdtdec_get_compatible(enum fdt_compat_id id)
> --
Regards,
Bin
More information about the U-Boot
mailing list