Skip to content

Commit

Permalink
mtd: spi-nor / spi / MFD: Convert intel-spi to SPI MEM
Browse files Browse the repository at this point in the history
Merge series from Mika Westerberg <[email protected]>:

Based on discussion on the patch I sent some time ago here:

  http://lists.infradead.org/pipermail/linux-mtd/2021-June/086867.html

it turns out that the preferred way to deal with the SPI flash controller
drivers is through SPI MEM which is part of Linux SPI subsystem.

This series does that for the intel-spi driver. This also renames the
driver to follow the convention used in the SPI subsystem. The first patch
improves the write protection handling to be slightly more safer. The
following two patches do the conversion itself. Note the Intel SPI flash
controller only allows commands such as read, write and so on and it
internally uses whatever addressing etc. it figured from the SFDP on the
flash device.

base-commit: e783362
  • Loading branch information
broonie committed Feb 15, 2022
2 parents f48dc6b + 4ab1348 commit 0177212
Show file tree
Hide file tree
Showing 14 changed files with 719 additions and 405 deletions.
2 changes: 1 addition & 1 deletion Documentation/driver-api/mtd/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ Memory Technology Device (MTD)
.. toctree::
:maxdepth: 1

intel-spi
spi-intel
nand_ecc
spi-nor
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
==============================
Upgrading BIOS using intel-spi
Upgrading BIOS using spi-intel
==============================

Many Intel CPUs like Baytrail and Braswell include SPI serial flash host
Expand All @@ -11,12 +11,12 @@ avoid accidental (or on purpose) overwrite of the content.
Not all manufacturers protect the SPI serial flash, mainly because it
allows upgrading the BIOS image directly from an OS.

The intel-spi driver makes it possible to read and write the SPI serial
The spi-intel driver makes it possible to read and write the SPI serial
flash, if certain protection bits are not set and locked. If it finds
any of them set, the whole MTD device is made read-only to prevent
partial overwrites. By default the driver exposes SPI serial flash
contents as read-only but it can be changed from kernel command line,
passing "intel-spi.writeable=1".
passing "spi_intel.writeable=1".

Please keep in mind that overwriting the BIOS image on SPI serial flash
might render the machine unbootable and requires special equipment like
Expand All @@ -32,7 +32,7 @@ Linux.
serial flash. Distros like Debian and Fedora have this prepackaged with
name "mtd-utils".

3) Add "intel-spi.writeable=1" to the kernel command line and reboot
3) Add "spi_intel.writeable=1" to the kernel command line and reboot
the board (you can also reload the driver passing "writeable=1" as
module parameter to modprobe).

Expand Down
59 changes: 54 additions & 5 deletions drivers/mfd/lpc_ich.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
#define SPIBASE_BYT 0x54
#define SPIBASE_BYT_SZ 512
#define SPIBASE_BYT_EN BIT(1)
#define BYT_BCR 0xfc
#define BYT_BCR_WPD BIT(0)

#define SPIBASE_LPT 0x3800
#define SPIBASE_LPT_SZ 512
Expand Down Expand Up @@ -1084,12 +1086,57 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
return ret;
}

static bool lpc_ich_byt_set_writeable(void __iomem *base, void *data)
{
u32 val;

val = readl(base + BYT_BCR);
if (!(val & BYT_BCR_WPD)) {
val |= BYT_BCR_WPD;
writel(val, base + BYT_BCR);
val = readl(base + BYT_BCR);
}

return val & BYT_BCR_WPD;
}

static bool lpc_ich_lpt_set_writeable(void __iomem *base, void *data)
{
struct pci_dev *pdev = data;
u32 bcr;

pci_read_config_dword(pdev, BCR, &bcr);
if (!(bcr & BCR_WPD)) {
bcr |= BCR_WPD;
pci_write_config_dword(pdev, BCR, bcr);
pci_read_config_dword(pdev, BCR, &bcr);
}

return bcr & BCR_WPD;
}

static bool lpc_ich_bxt_set_writeable(void __iomem *base, void *data)
{
unsigned int spi = PCI_DEVFN(13, 2);
struct pci_bus *bus = data;
u32 bcr;

pci_bus_read_config_dword(bus, spi, BCR, &bcr);
if (!(bcr & BCR_WPD)) {
bcr |= BCR_WPD;
pci_bus_write_config_dword(bus, spi, BCR, bcr);
pci_bus_read_config_dword(bus, spi, BCR, &bcr);
}

return bcr & BCR_WPD;
}

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;
u32 spi_base, rcba;

info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
if (!info)
Expand All @@ -1103,6 +1150,8 @@ static int lpc_ich_init_spi(struct pci_dev *dev)
if (spi_base & SPIBASE_BYT_EN) {
res->start = spi_base & ~(SPIBASE_BYT_SZ - 1);
res->end = res->start + SPIBASE_BYT_SZ - 1;

info->set_writeable = lpc_ich_byt_set_writeable;
}
break;

Expand All @@ -1113,8 +1162,8 @@ static int lpc_ich_init_spi(struct pci_dev *dev)
res->start = spi_base + SPIBASE_LPT;
res->end = res->start + SPIBASE_LPT_SZ - 1;

pci_read_config_dword(dev, BCR, &bcr);
info->writeable = !!(bcr & BCR_WPD);
info->set_writeable = lpc_ich_lpt_set_writeable;
info->data = dev;
}
break;

Expand All @@ -1135,8 +1184,8 @@ static int lpc_ich_init_spi(struct pci_dev *dev)
res->start = spi_base & 0xfffffff0;
res->end = res->start + SPIBASE_APL_SZ - 1;

pci_bus_read_config_dword(bus, spi, BCR, &bcr);
info->writeable = !!(bcr & BCR_WPD);
info->set_writeable = lpc_ich_bxt_set_writeable;
info->data = bus;
}

pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x1);
Expand Down
36 changes: 0 additions & 36 deletions drivers/mtd/spi-nor/controllers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -26,39 +26,3 @@ config SPI_NXP_SPIFI
SPIFI is a specialized controller for connecting serial SPI
Flash. Enable this option if you have a device with a SPIFI
controller and want to access the Flash as a mtd device.

config SPI_INTEL_SPI
tristate

config SPI_INTEL_SPI_PCI
tristate "Intel PCH/PCU SPI flash PCI driver (DANGEROUS)"
depends on X86 && PCI
select SPI_INTEL_SPI
help
This enables PCI support for the Intel PCH/PCU SPI controller in
master mode. This controller is present in modern Intel hardware
and is used to hold BIOS and other persistent settings. Using
this driver it is possible to upgrade BIOS directly from Linux.

Say N here unless you know what you are doing. Overwriting the
SPI flash may render the system unbootable.

To compile this driver as a module, choose M here: the module
will be called intel-spi-pci.

config SPI_INTEL_SPI_PLATFORM
tristate "Intel PCH/PCU SPI flash platform driver (DANGEROUS)"
depends on X86
select SPI_INTEL_SPI
help
This enables platform support for the Intel PCH/PCU SPI
controller in master mode. This controller is present in modern
Intel hardware and is used to hold BIOS and other persistent
settings. Using this driver it is possible to upgrade BIOS
directly from Linux.

Say N here unless you know what you are doing. Overwriting the
SPI flash may render the system unbootable.

To compile this driver as a module, choose M here: the module
will be called intel-spi-platform.
3 changes: 0 additions & 3 deletions drivers/mtd/spi-nor/controllers/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,3 @@
obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o
obj-$(CONFIG_SPI_INTEL_SPI_PCI) += intel-spi-pci.o
obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o
21 changes: 0 additions & 21 deletions drivers/mtd/spi-nor/controllers/intel-spi.h

This file was deleted.

39 changes: 39 additions & 0 deletions drivers/spi/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,45 @@ config SPI_INGENIC
To compile this driver as a module, choose M here: the module
will be called spi-ingenic.

config SPI_INTEL
tristate

config SPI_INTEL_PCI
tristate "Intel PCH/PCU SPI flash PCI driver (DANGEROUS)"
depends on PCI
depends on X86 || COMPILE_TEST
depends on SPI_MEM
select SPI_INTEL
help
This enables PCI support for the Intel PCH/PCU SPI controller in
master mode. This controller is present in modern Intel hardware
and is used to hold BIOS and other persistent settings. Using
this driver it is possible to upgrade BIOS directly from Linux.

Say N here unless you know what you are doing. Overwriting the
SPI flash may render the system unbootable.

To compile this driver as a module, choose M here: the module
will be called spi-intel-pci.

config SPI_INTEL_PLATFORM
tristate "Intel PCH/PCU SPI flash platform driver (DANGEROUS)"
depends on X86 || COMPILE_TEST
depends on SPI_MEM
select SPI_INTEL
help
This enables platform support for the Intel PCH/PCU SPI
controller in master mode. This controller is present in modern
Intel hardware and is used to hold BIOS and other persistent
settings. Using this driver it is possible to upgrade BIOS
directly from Linux.

Say N here unless you know what you are doing. Overwriting the
SPI flash may render the system unbootable.

To compile this driver as a module, choose M here: the module
will be called spi-intel-platform.

config SPI_JCORE
tristate "J-Core SPI Master"
depends on OF && (SUPERH || COMPILE_TEST)
Expand Down
3 changes: 3 additions & 0 deletions drivers/spi/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ obj-$(CONFIG_SPI_HISI_SFC_V3XX) += spi-hisi-sfc-v3xx.o
obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o
obj-$(CONFIG_SPI_IMX) += spi-imx.o
obj-$(CONFIG_SPI_INGENIC) += spi-ingenic.o
obj-$(CONFIG_SPI_INTEL) += spi-intel.o
obj-$(CONFIG_SPI_INTEL_PCI) += spi-intel-pci.o
obj-$(CONFIG_SPI_INTEL_PLATFORM) += spi-intel-platform.o
obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o
obj-$(CONFIG_SPI_JCORE) += spi-jcore.o
obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,48 @@
/*
* Intel PCH/PCU SPI flash PCI driver.
*
* Copyright (C) 2016, Intel Corporation
* Copyright (C) 2016 - 2022, Intel Corporation
* Author: Mika Westerberg <[email protected]>
*/

#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>

#include "intel-spi.h"
#include "spi-intel.h"

#define BCR 0xdc
#define BCR_WPD BIT(0)

static bool intel_spi_pci_set_writeable(void __iomem *base, void *data)
{
struct pci_dev *pdev = data;
u32 bcr;

/* Try to make the chip read/write */
pci_read_config_dword(pdev, BCR, &bcr);
if (!(bcr & BCR_WPD)) {
bcr |= BCR_WPD;
pci_write_config_dword(pdev, BCR, bcr);
pci_read_config_dword(pdev, BCR, &bcr);
}

return bcr & BCR_WPD;
}

static const struct intel_spi_boardinfo bxt_info = {
.type = INTEL_SPI_BXT,
.set_writeable = intel_spi_pci_set_writeable,
};

static const struct intel_spi_boardinfo cnl_info = {
.type = INTEL_SPI_CNL,
.set_writeable = intel_spi_pci_set_writeable,
};

static int intel_spi_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct intel_spi_boardinfo *info;
struct intel_spi *ispi;
u32 bcr;
int ret;

ret = pcim_enable_device(pdev);
Expand All @@ -41,26 +55,8 @@ static int intel_spi_pci_probe(struct pci_dev *pdev,
if (!info)
return -ENOMEM;

/* Try to make the chip read/write */
pci_read_config_dword(pdev, BCR, &bcr);
if (!(bcr & BCR_WPD)) {
bcr |= BCR_WPD;
pci_write_config_dword(pdev, BCR, bcr);
pci_read_config_dword(pdev, BCR, &bcr);
}
info->writeable = !!(bcr & BCR_WPD);

ispi = intel_spi_probe(&pdev->dev, &pdev->resource[0], info);
if (IS_ERR(ispi))
return PTR_ERR(ispi);

pci_set_drvdata(pdev, ispi);
return 0;
}

static void intel_spi_pci_remove(struct pci_dev *pdev)
{
intel_spi_remove(pci_get_drvdata(pdev));
info->data = pdev;
return intel_spi_probe(&pdev->dev, &pdev->resource[0], info);
}

static const struct pci_device_id intel_spi_pci_ids[] = {
Expand Down Expand Up @@ -89,7 +85,6 @@ static struct pci_driver intel_spi_pci_driver = {
.name = "intel-spi",
.id_table = intel_spi_pci_ids,
.probe = intel_spi_pci_probe,
.remove = intel_spi_pci_remove,
};

module_pci_driver(intel_spi_pci_driver);
Expand Down
Loading

0 comments on commit 0177212

Please sign in to comment.