|
@@ -83,6 +83,15 @@
|
|
#define ACPIBASE_GCS_OFF 0x3410
|
|
#define ACPIBASE_GCS_OFF 0x3410
|
|
#define ACPIBASE_GCS_END 0x3414
|
|
#define ACPIBASE_GCS_END 0x3414
|
|
|
|
|
|
|
|
+#define SPIBASE_BYT 0x54
|
|
|
|
+#define SPIBASE_BYT_SZ 512
|
|
|
|
+#define SPIBASE_BYT_EN BIT(1)
|
|
|
|
+
|
|
|
|
+#define SPIBASE_LPT 0x3800
|
|
|
|
+#define SPIBASE_LPT_SZ 512
|
|
|
|
+#define BCR 0xdc
|
|
|
|
+#define BCR_WPD BIT(0)
|
|
|
|
+
|
|
#define GPIOBASE_ICH0 0x58
|
|
#define GPIOBASE_ICH0 0x58
|
|
#define GPIOCTRL_ICH0 0x5C
|
|
#define GPIOCTRL_ICH0 0x5C
|
|
#define GPIOBASE_ICH6 0x48
|
|
#define GPIOBASE_ICH6 0x48
|
|
@@ -133,6 +142,12 @@ static struct resource gpio_ich_res[] = {
|
|
},
|
|
},
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static struct resource intel_spi_res[] = {
|
|
|
|
+ {
|
|
|
|
+ .flags = IORESOURCE_MEM,
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+
|
|
static struct mfd_cell lpc_ich_wdt_cell = {
|
|
static struct mfd_cell lpc_ich_wdt_cell = {
|
|
.name = "iTCO_wdt",
|
|
.name = "iTCO_wdt",
|
|
.num_resources = ARRAY_SIZE(wdt_ich_res),
|
|
.num_resources = ARRAY_SIZE(wdt_ich_res),
|
|
@@ -147,6 +162,14 @@ static struct mfd_cell lpc_ich_gpio_cell = {
|
|
.ignore_resource_conflicts = true,
|
|
.ignore_resource_conflicts = true,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+
|
|
|
|
+static struct mfd_cell lpc_ich_spi_cell = {
|
|
|
|
+ .name = "intel-spi",
|
|
|
|
+ .num_resources = ARRAY_SIZE(intel_spi_res),
|
|
|
|
+ .resources = intel_spi_res,
|
|
|
|
+ .ignore_resource_conflicts = true,
|
|
|
|
+};
|
|
|
|
+
|
|
/* chipset related info */
|
|
/* chipset related info */
|
|
enum lpc_chipsets {
|
|
enum lpc_chipsets {
|
|
LPC_ICH = 0, /* ICH */
|
|
LPC_ICH = 0, /* ICH */
|
|
@@ -494,10 +517,12 @@ static struct lpc_ich_info lpc_chipset_info[] = {
|
|
.name = "Lynx Point",
|
|
.name = "Lynx Point",
|
|
.iTCO_version = 2,
|
|
.iTCO_version = 2,
|
|
.gpio_version = ICH_V5_GPIO,
|
|
.gpio_version = ICH_V5_GPIO,
|
|
|
|
+ .spi_type = INTEL_SPI_LPT,
|
|
},
|
|
},
|
|
[LPC_LPT_LP] = {
|
|
[LPC_LPT_LP] = {
|
|
.name = "Lynx Point_LP",
|
|
.name = "Lynx Point_LP",
|
|
.iTCO_version = 2,
|
|
.iTCO_version = 2,
|
|
|
|
+ .spi_type = INTEL_SPI_LPT,
|
|
},
|
|
},
|
|
[LPC_WBG] = {
|
|
[LPC_WBG] = {
|
|
.name = "Wellsburg",
|
|
.name = "Wellsburg",
|
|
@@ -511,6 +536,7 @@ static struct lpc_ich_info lpc_chipset_info[] = {
|
|
[LPC_BAYTRAIL] = {
|
|
[LPC_BAYTRAIL] = {
|
|
.name = "Bay Trail SoC",
|
|
.name = "Bay Trail SoC",
|
|
.iTCO_version = 3,
|
|
.iTCO_version = 3,
|
|
|
|
+ .spi_type = INTEL_SPI_BYT,
|
|
},
|
|
},
|
|
[LPC_COLETO] = {
|
|
[LPC_COLETO] = {
|
|
.name = "Coleto Creek",
|
|
.name = "Coleto Creek",
|
|
@@ -519,10 +545,12 @@ static struct lpc_ich_info lpc_chipset_info[] = {
|
|
[LPC_WPT_LP] = {
|
|
[LPC_WPT_LP] = {
|
|
.name = "Wildcat Point_LP",
|
|
.name = "Wildcat Point_LP",
|
|
.iTCO_version = 2,
|
|
.iTCO_version = 2,
|
|
|
|
+ .spi_type = INTEL_SPI_LPT,
|
|
},
|
|
},
|
|
[LPC_BRASWELL] = {
|
|
[LPC_BRASWELL] = {
|
|
.name = "Braswell SoC",
|
|
.name = "Braswell SoC",
|
|
.iTCO_version = 3,
|
|
.iTCO_version = 3,
|
|
|
|
+ .spi_type = INTEL_SPI_BYT,
|
|
},
|
|
},
|
|
[LPC_LEWISBURG] = {
|
|
[LPC_LEWISBURG] = {
|
|
.name = "Lewisburg",
|
|
.name = "Lewisburg",
|
|
@@ -1056,6 +1084,64 @@ wdt_done:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int lpc_ich_init_spi(struct pci_dev *dev)
|
|
|
|
+{
|
|
|
|
+ struct lpc_ich_priv *priv = pci_get_drvdata(dev);
|
|
|
|
+ struct resource *res = &intel_spi_res[0];
|
|
|
|
+ struct intel_spi_boardinfo *info;
|
|
|
|
+ u32 spi_base, rcba, bcr;
|
|
|
|
+
|
|
|
|
+ info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
|
|
|
|
+ if (!info)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ info->type = lpc_chipset_info[priv->chipset].spi_type;
|
|
|
|
+
|
|
|
|
+ switch (info->type) {
|
|
|
|
+ case INTEL_SPI_BYT:
|
|
|
|
+ pci_read_config_dword(dev, SPIBASE_BYT, &spi_base);
|
|
|
|
+ if (spi_base & SPIBASE_BYT_EN) {
|
|
|
|
+ res->start = spi_base & ~(SPIBASE_BYT_SZ - 1);
|
|
|
|
+ res->end = res->start + SPIBASE_BYT_SZ - 1;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case INTEL_SPI_LPT:
|
|
|
|
+ pci_read_config_dword(dev, RCBABASE, &rcba);
|
|
|
|
+ if (rcba & 1) {
|
|
|
|
+ spi_base = round_down(rcba, SPIBASE_LPT_SZ);
|
|
|
|
+ res->start = spi_base + SPIBASE_LPT;
|
|
|
|
+ res->end = res->start + SPIBASE_LPT_SZ - 1;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Try to make the flash chip writeable now by
|
|
|
|
+ * setting BCR_WPD. It it fails we tell the driver
|
|
|
|
+ * that it can only read the chip.
|
|
|
|
+ */
|
|
|
|
+ pci_read_config_dword(dev, BCR, &bcr);
|
|
|
|
+ if (!(bcr & BCR_WPD)) {
|
|
|
|
+ bcr |= BCR_WPD;
|
|
|
|
+ pci_write_config_dword(dev, BCR, bcr);
|
|
|
|
+ pci_read_config_dword(dev, BCR, &bcr);
|
|
|
|
+ }
|
|
|
|
+ info->writeable = !!(bcr & BCR_WPD);
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ default:
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!res->start)
|
|
|
|
+ return -ENODEV;
|
|
|
|
+
|
|
|
|
+ lpc_ich_spi_cell.platform_data = info;
|
|
|
|
+ lpc_ich_spi_cell.pdata_size = sizeof(*info);
|
|
|
|
+
|
|
|
|
+ return mfd_add_devices(&dev->dev, PLATFORM_DEVID_NONE,
|
|
|
|
+ &lpc_ich_spi_cell, 1, NULL, 0, NULL);
|
|
|
|
+}
|
|
|
|
+
|
|
static int lpc_ich_probe(struct pci_dev *dev,
|
|
static int lpc_ich_probe(struct pci_dev *dev,
|
|
const struct pci_device_id *id)
|
|
const struct pci_device_id *id)
|
|
{
|
|
{
|
|
@@ -1099,6 +1185,12 @@ static int lpc_ich_probe(struct pci_dev *dev,
|
|
cell_added = true;
|
|
cell_added = true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (lpc_chipset_info[priv->chipset].spi_type) {
|
|
|
|
+ ret = lpc_ich_init_spi(dev);
|
|
|
|
+ if (!ret)
|
|
|
|
+ cell_added = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* We only care if at least one or none of the cells registered
|
|
* We only care if at least one or none of the cells registered
|
|
* successfully.
|
|
* successfully.
|