[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