[U-Boot] [PATCH 23/33] x86: ivybridge: Add SATA init
Simon Glass
sjg at chromium.org
Wed Nov 12 16:23:37 CET 2014
Hi Bin,
On 12 November 2014 01:03, Bin Meng <bmeng.cn at gmail.com> wrote:
> 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.
We want to indicate which devices should be enabled and which should
not be. Also we want to provide parameters to say how a particular
device should be set up. In the case of SATA, we need to know if it
should be SATA or legacy ATA, for example. If you look at the
chromebook_link.dts file you will see quite a few settings.
The fdt_compat_id will go away as things are converted to driver
model. It was a stop-gap so that we didn't forget all the places that
need to be changed.
I have formed the view that we need to convert PCI to driver model
before long, but it is of course much easier to come up with a
suitable uclass when you have a body of code to convert. I think the
x86 ivybridge port will provide a reasonable base for that.
Regards,
Simon
More information about the U-Boot
mailing list