Commit ce1bf979 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-20170212' of git://git.infradead.org/linux-mtd

Pull MTD updates from Brian Norris:
 "MTD updates for 4.11:

  General:
   - new kernel API for retrieving max bad blocks per die (not used yet)
   - track (and expose via sysfs) a partition's device tree node
   - support a "label" device tree property for naming an MTD

  NAND:
   - sunxi: avoid busy-waiting for NAND events
   - ifc: fix ECC handling on IFC v1.0
   - OX820: add explicit dependency on ARCH_OXNAS in Kconfig
   - core: add a new manufacture ID and fix a kernel-doc warning
   - fsmc: kill pdata support
   - lpc32xx_slc: remove unneeded NULL check
   - support dynamic "max bad blocks" detection via ONFI

  SPI NOR:
   - add support for the 4-byte address instruction set
   - add support for new memory parts
   - add support to S3AN memories
   - add support to the Intel SPI controller
   - add support to the Aspeed AST2400 and AST2550 controllers
   - support max SPI message sizes in m25p80_read()
   - fixes for the Candence and Freescale QSPI drivers

  Other:
   - add support for Gemini flash probing
   - bcm47xxsflash: add support for reading outside memory-mapped window
   - bcm47xxparts: extend to support multiple TRX partitions
   - misc fixes and typos

  Extra note: we've pulled in an MFD subtree from Lee Jones as a
  dependency for a new Intel SPI NOR driver"

[ Kudos to Brian for sending pull request a week early:

   "I refuse to acknowledge the existence of 4.10-rc8 and am therefore
    sending our MTD changes for 4.11 now"

  which is all good ]

* tag 'for-linus-20170212' of git://git.infradead.org/linux-mtd: (52 commits)
  mtd: aspeed: remove redundant dev_err call in aspeed_smc_probe()
  dt-bindings: mtd: add a common label property to all mtd devices
  mtd: name the mtd device with an optional label property
  mtd: physmap_of: fixup gemini/versatile dependencies
  mtd: spi-nor: cqspi: remove redundant dead code on error return check
  Documentation: mtk-quadspi: update DT bindings
  mtd: fsl-quadspi: Rename SEQID_QUAD_READ to SEQID_READ
  mtd:fsl-quadspi:use the property fields of SPI-NOR
  mtd: spi-nor: Add support for gd25q16
  mtd: spi-nor: Fix S3AN addressing calculation
  mtd: aspeed: fix compile warning in aspeed_smc_read_from_ahb()
  mtd: spi-nor: add dt support for Everspin MRAMs
  mtd: spi-nor: Add lock/unlock support for f25l32pa
  mtd: spi-nor: add a stateless method to support memory size above 128Mib
  mtd: spi-nor: rename SPINOR_OP_* macros of the 4-byte address op codes
  mtd: m25p80: consider max message size in m25p80_read
  mtd: spi-nor: bindings for the Aspeed memory controllers
  mtd: aspeed: add memory controllers for the Aspeed AST2400 SoC
  mtd: spi-nor: add memory controllers for the Aspeed AST2500 SoC
  mtd: spi-nor: remove WARN_ONCE() message in spi_nor_write()
  ...
parents c470abd4 d91f6cee
* Aspeed Firmware Memory controller
* Aspeed SPI Flash Memory Controller
The Firmware Memory Controller in the Aspeed AST2500 SoC supports
three chip selects, two of which are always of SPI type and the third
can be SPI or NOR type flash. These bindings only describe SPI.
The two SPI flash memory controllers in the AST2500 each support two
chip selects.
Required properties:
- compatible : Should be one of
"aspeed,ast2400-fmc" for the AST2400 Firmware Memory Controller
"aspeed,ast2400-spi" for the AST2400 SPI Flash memory Controller
"aspeed,ast2500-fmc" for the AST2500 Firmware Memory Controller
"aspeed,ast2500-spi" for the AST2500 SPI flash memory controllers
- reg : the first contains the control register location and length,
the second contains the memory window mapping address and length
- #address-cells : must be 1 corresponding to chip select child binding
- #size-cells : must be 0 corresponding to chip select child binding
Optional properties:
- interrupts : Should contain the interrupt for the dma device if an
FMC
The child nodes are the SPI flash modules which must have a compatible
property as specified in bindings/mtd/jedec,spi-nor.txt
Optionally, the child node can contain properties for SPI mode (may be
ignored):
- spi-max-frequency - max frequency of spi bus
Example:
fmc: fmc@1e620000 {
compatible = "aspeed,ast2500-fmc";
reg = < 0x1e620000 0x94
0x20000000 0x02000000 >;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <19>;
flash@0 {
reg = < 0 >;
compatible = "jedec,spi-nor";
/* spi-max-frequency = <>; */
/* m25p,fast-read; */
#address-cells = <1>;
#size-cells = <1>;
};
};
* Common properties of all MTD devices
Optional properties:
- label: user-defined MTD device name. Can be used to assign user
friendly names to MTD devices (instead of the flash model or flash
controller based name) in order to ease flash device identification
and/or describe what they are used for.
Example:
flash@0 {
label = "System-firmware";
/* flash type specific properties */
};
Flash device on Cortina Systems Gemini SoC
This flash is regular CFI compatible (Intel or AMD extended) flash chips with
some special bits that can be controlled by the machine's system controller.
Required properties:
- compatible : must be "cortina,gemini-flash", "cfi-flash";
- reg : memory address for the flash chip
- syscon : must be a phandle to the system controller
- bank-width : width in bytes of flash interface, should be <2>
For the rest of the properties, see mtd-physmap.txt.
The device tree may optionally contain sub-nodes describing partitions of the
address space. See partition.txt for more detail.
Example:
flash@30000000 {
compatible = "cortina,gemini-flash", "cfi-flash";
reg = <0x30000000 0x01000000>;
syscon = <&syscon>;
bank-width = <2>;
};
...@@ -14,6 +14,8 @@ Required properties: ...@@ -14,6 +14,8 @@ Required properties:
at25df641 at25df641
at26df081a at26df081a
mr25h256 mr25h256
mr25h10
mr25h40
mx25l4005a mx25l4005a
mx25l1606e mx25l1606e
mx25l6405d mx25l6405d
......
* Serial NOR flash controller for MTK MT81xx (and similar) * Serial NOR flash controller for MTK MT81xx (and similar)
Required properties: Required properties:
- compatible: should be "mediatek,mt8173-nor"; - compatible: The possible values are:
"mediatek,mt2701-nor"
"mediatek,mt7623-nor"
"mediatek,mt8173-nor"
For mt8173, compatible should be "mediatek,mt8173-nor".
For every other SoC, should contain both the SoC-specific compatible string
and "mediatek,mt8173-nor".
- reg: physical base address and length of the controller's register - reg: physical base address and length of the controller's register
- clocks: the phandle of the clocks needed by the nor controller - clocks: the phandle of the clocks needed by the nor controller
- clock-names: the names of the clocks - clock-names: the names of the clocks
......
Upgrading BIOS using intel-spi
------------------------------
Many Intel CPUs like Baytrail and Braswell include SPI serial flash host
controller which is used to hold BIOS and other platform specific data.
Since contents of the SPI serial flash is crucial for machine to function,
it is typically protected by different hardware protection mechanisms to
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
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".
Please keep in mind that overwriting the BIOS image on SPI serial flash
might render the machine unbootable and requires special equipment like
Dediprog to revive. You have been warned!
Below are the steps how to upgrade MinnowBoard MAX BIOS directly from
Linux.
1) Download and extract the latest Minnowboard MAX BIOS SPI image
[1]. At the time writing this the latest image is v92.
2) Install mtd-utils package [2]. We need this in order to erase the SPI
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
the board (you can also reload the driver passing "writeable=1" as
module parameter to modprobe).
4) Once the board is up and running again, find the right MTD partition
(it is named as "BIOS"):
# cat /proc/mtd
dev: size erasesize name
mtd0: 00800000 00001000 "BIOS"
So here it will be /dev/mtd0 but it may vary.
5) Make backup of the existing image first:
# dd if=/dev/mtd0ro of=bios.bak
16384+0 records in
16384+0 records out
8388608 bytes (8.4 MB) copied, 10.0269 s, 837 kB/s
6) Verify the backup
# sha1sum /dev/mtd0ro bios.bak
fdbb011920572ca6c991377c4b418a0502668b73 /dev/mtd0ro
fdbb011920572ca6c991377c4b418a0502668b73 bios.bak
The SHA1 sums must match. Otherwise do not continue any further!
7) Erase the SPI serial flash. After this step, do not reboot the
board! Otherwise it will not start anymore.
# flash_erase /dev/mtd0 0 0
Erasing 4 Kibyte @ 7ff000 -- 100 % complete
8) Once completed without errors you can write the new BIOS image:
# dd if=MNW2MAX1.X64.0092.R01.1605221712.bin of=/dev/mtd0
9) Verify that the new content of the SPI serial flash matches the new
BIOS image:
# sha1sum /dev/mtd0ro MNW2MAX1.X64.0092.R01.1605221712.bin
9b4df9e4be2057fceec3a5529ec3d950836c87a2 /dev/mtd0ro
9b4df9e4be2057fceec3a5529ec3d950836c87a2 MNW2MAX1.X64.0092.R01.1605221712.bin
The SHA1 sums should match.
10) Now you can reboot your board and observe the new BIOS starting up
properly.
References
----------
[1] https://firmware.intel.com/sites/default/files/MinnowBoard.MAX_.X64.92.R01.zip
[2] http://www.linux-mtd.infradead.org/
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
* document number TBD : Wildcat Point-LP * document number TBD : Wildcat Point-LP
* document number TBD : 9 Series * document number TBD : 9 Series
* document number TBD : Lewisburg * document number TBD : Lewisburg
* document number TBD : Apollo Lake SoC
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
...@@ -83,6 +84,17 @@ ...@@ -83,6 +84,17 @@
#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 SPIBASE_APL_SZ 4096
#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 +145,12 @@ static struct resource gpio_ich_res[] = { ...@@ -133,6 +145,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 +165,14 @@ static struct mfd_cell lpc_ich_gpio_cell = { ...@@ -147,6 +165,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 */
...@@ -216,6 +242,7 @@ enum lpc_chipsets { ...@@ -216,6 +242,7 @@ enum lpc_chipsets {
LPC_BRASWELL, /* Braswell SoC */ LPC_BRASWELL, /* Braswell SoC */
LPC_LEWISBURG, /* Lewisburg */ LPC_LEWISBURG, /* Lewisburg */
LPC_9S, /* 9 Series */ LPC_9S, /* 9 Series */
LPC_APL, /* Apollo Lake SoC */
}; };
static struct lpc_ich_info lpc_chipset_info[] = { static struct lpc_ich_info lpc_chipset_info[] = {
...@@ -494,10 +521,12 @@ static struct lpc_ich_info lpc_chipset_info[] = { ...@@ -494,10 +521,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 +540,7 @@ static struct lpc_ich_info lpc_chipset_info[] = { ...@@ -511,6 +540,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 +549,12 @@ static struct lpc_ich_info lpc_chipset_info[] = { ...@@ -519,10 +549,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",
...@@ -533,6 +565,10 @@ static struct lpc_ich_info lpc_chipset_info[] = { ...@@ -533,6 +565,10 @@ static struct lpc_ich_info lpc_chipset_info[] = {
.iTCO_version = 2, .iTCO_version = 2,
.gpio_version = ICH_V5_GPIO, .gpio_version = ICH_V5_GPIO,
}, },
[LPC_APL] = {
.name = "Apollo Lake SoC",
.spi_type = INTEL_SPI_BXT,
},
}; };
/* /*
...@@ -681,6 +717,7 @@ static const struct pci_device_id lpc_ich_ids[] = { ...@@ -681,6 +717,7 @@ static const struct pci_device_id lpc_ich_ids[] = {
{ PCI_VDEVICE(INTEL, 0x3b14), LPC_3420}, { PCI_VDEVICE(INTEL, 0x3b14), LPC_3420},
{ PCI_VDEVICE(INTEL, 0x3b16), LPC_3450}, { PCI_VDEVICE(INTEL, 0x3b16), LPC_3450},
{ PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579}, { PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579},
{ PCI_VDEVICE(INTEL, 0x5ae8), LPC_APL},
{ PCI_VDEVICE(INTEL, 0x8c40), LPC_LPT}, { PCI_VDEVICE(INTEL, 0x8c40), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c41), LPC_LPT}, { PCI_VDEVICE(INTEL, 0x8c41), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c42), LPC_LPT}, { PCI_VDEVICE(INTEL, 0x8c42), LPC_LPT},
...@@ -1056,6 +1093,94 @@ static int lpc_ich_init_wdt(struct pci_dev *dev) ...@@ -1056,6 +1093,94 @@ static int lpc_ich_init_wdt(struct pci_dev *dev)
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;
case INTEL_SPI_BXT: {
unsigned int p2sb = PCI_DEVFN(13, 0);
unsigned int spi = PCI_DEVFN(13, 2);
struct pci_bus *bus = dev->bus;
/*
* The P2SB is hidden by BIOS and we need to unhide it in
* order to read BAR of the SPI flash device. Once that is
* done we hide it again.
*/
pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x0);
pci_bus_read_config_dword(bus, spi, PCI_BASE_ADDRESS_0,
&spi_base);
if (spi_base != ~0) {
res->start = spi_base & 0xfffffff0;
res->end = res->start + SPIBASE_APL_SZ - 1;
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);
}
info->writeable = !!(bcr & BCR_WPD);
}
pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x1);
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 +1224,12 @@ static int lpc_ich_probe(struct pci_dev *dev, ...@@ -1099,6 +1224,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.
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* *
*/ */
#include <linux/bcm47xx_nvram.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -83,6 +84,91 @@ static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master, ...@@ -83,6 +84,91 @@ static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master,
return "rootfs"; return "rootfs";
} }
static int bcm47xxpart_parse_trx(struct mtd_info *master,
struct mtd_partition *trx,
struct mtd_partition *parts,
size_t parts_len)
{
struct trx_header header;
size_t bytes_read;
int curr_part = 0;
int i, err;
if (parts_len < 3) {
pr_warn("No enough space to add TRX partitions!\n");
return -ENOMEM;
}
err = mtd_read(master, trx->offset, sizeof(header), &bytes_read,
(uint8_t *)&header);
if (err && !mtd_is_bitflip(err)) {
pr_err("mtd_read error while reading TRX header: %d\n", err);
return err;
}
i = 0;
/* We have LZMA loader if offset[2] points to sth */
if (header.offset[2]) {
bcm47xxpart_add_part(&parts[curr_part++], "loader",
trx->offset + header.offset[i], 0);
i++;
}
if (header.offset[i]) {
bcm47xxpart_add_part(&parts[curr_part++], "linux",
trx->offset + header.offset[i], 0);
i++;
}
if (header.offset[i]) {
size_t offset = trx->offset + header.offset[i];
const char *name = bcm47xxpart_trx_data_part_name(master,
offset);
bcm47xxpart_add_part(&parts[curr_part++], name, offset, 0);
i++;
}
/*
* Assume that every partition ends at the beginning of the one it is
* followed by.
*/
for (i = 0; i < curr_part; i++) {
u64 next_part_offset = (i < curr_part - 1) ?
parts[i + 1].offset :
trx->offset + trx->size;
parts[i].size = next_part_offset - parts[i].offset;
}
return curr_part;
}
/**
* bcm47xxpart_bootpartition - gets index of TRX partition used by bootloader
*
* Some devices may have more than one TRX partition. In such case one of them
* is the main one and another a failsafe one. Bootloader may fallback to the
* failsafe firmware if it detects corruption of the main image.
*
* This function provides info about currently used TRX partition. It's the one
* containing kernel started by the bootloader.
*/
static int bcm47xxpart_bootpartition(void)
{
char buf[4];
int bootpartition;
/* Check CFE environment variable */
if (bcm47xx_nvram_getenv("bootpartition", buf, sizeof(buf)) > 0) {
if (!kstrtoint(buf, 0, &bootpartition))
return bootpartition;
}
return 0;
}
static int bcm47xxpart_parse(struct mtd_info *master, static int bcm47xxpart_parse(struct mtd_info *master,
const struct mtd_partition **pparts, const struct mtd_partition **pparts,
struct mtd_part_parser_data *data) struct mtd_part_parser_data *data)
...@@ -93,9 +179,8 @@ static int bcm47xxpart_parse(struct mtd_info *master, ...@@ -93,9 +179,8 @@ static int bcm47xxpart_parse(struct mtd_info *master,
size_t bytes_read; size_t bytes_read;
uint32_t offset; uint32_t offset;
uint32_t blocksize = master->erasesize; uint32_t blocksize = master->erasesize;
struct trx_header *trx; int trx_parts[2]; /* Array with indexes of TRX partitions */
int trx_part = -1; int trx_num = 0; /* Number of found TRX partitions */
int last_trx_part = -1;
int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, }; int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
int err; int err;
...@@ -182,54 +267,18 @@ static int bcm47xxpart_parse(struct mtd_info *master, ...@@ -182,54 +267,18 @@ static int bcm47xxpart_parse(struct mtd_info *master,
/* TRX */ /* TRX */
if (buf[0x000 / 4] == TRX_MAGIC) { if (buf[0x000 / 4] == TRX_MAGIC) {
if (BCM47XXPART_MAX_PARTS - curr_part < 4) { struct trx_header *trx;
pr_warn("Not enough partitions left to register trx, scanning stopped!\n");
break;
}
trx = (struct trx_header *)buf;
trx_part = curr_part; if (trx_num >= ARRAY_SIZE(trx_parts))
pr_warn("No enough space to store another TRX found at 0x%X\n",
offset);
else
trx_parts[trx_num++] = curr_part;
bcm47xxpart_add_part(&parts[curr_part++], "firmware", bcm47xxpart_add_part(&parts[curr_part++], "firmware",
offset, 0); offset, 0);
i = 0;
/* We have LZMA loader if offset[2] points to sth */
if (trx->offset[2]) {
bcm47xxpart_add_part(&parts[curr_part++],
"loader",
offset + trx->offset[i],
0);
i++;
}
if (trx->offset[i]) {
bcm47xxpart_add_part(&parts[curr_part++],
"linux",
offset + trx->offset[i],
0);
i++;
}
/*
* Pure rootfs size is known and can be calculated as:
* trx->length - trx->offset[i]. We don't fill it as
* we want to have jffs2 (overlay) in the same mtd.
*/
if (trx->offset[i]) {
const char *name;
name = bcm47xxpart_trx_data_part_name(master, offset + trx->offset[i]);
bcm47xxpart_add_part(&parts[curr_part++],
name,
offset + trx->offset[i],
0);
i++;
}
last_trx_part = curr_part - 1;
/* Jump to the end of TRX */ /* Jump to the end of TRX */
trx = (struct trx_header *)buf;
offset = roundup(offset + trx->length, blocksize); offset = roundup(offset + trx->length, blocksize);
/* Next loop iteration will increase the offset */ /* Next loop iteration will increase the offset */
offset -= blocksize; offset -= blocksize;
...@@ -307,9 +356,23 @@ static int bcm47xxpart_parse(struct mtd_info *master, ...@@ -307,9 +356,23 @@ static int bcm47xxpart_parse(struct mtd_info *master,
parts[i + 1].offset : master->size; parts[i + 1].offset : master->size;
parts[i].size = next_part_offset - parts[i].offset; parts[i].size = next_part_offset - parts[i].offset;
if (i == last_trx_part && trx_part >= 0) }
parts[trx_part].size = next_part_offset -
parts[trx_part].offset; /* If there was TRX parse it now */
for (i = 0; i < trx_num; i++) {
struct mtd_partition *trx = &parts[trx_parts[i]];
if (i == bcm47xxpart_bootpartition()) {
int num_parts;
num_parts = bcm47xxpart_parse_trx(master, trx,
parts + curr_part,
BCM47XXPART_MAX_PARTS - curr_part);
if (num_parts > 0)
curr_part += num_parts;
} else {
trx->name = "failsafe";
}
} }
*pparts = parts; *pparts = parts;
......
...@@ -105,15 +105,33 @@ static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -105,15 +105,33 @@ static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf)
{ {
struct bcm47xxsflash *b47s = mtd->priv; struct bcm47xxsflash *b47s = mtd->priv;
size_t orig_len = len;
/* Check address range */ /* Check address range */
if ((from + len) > mtd->size) if ((from + len) > mtd->size)
return -EINVAL; return -EINVAL;
memcpy_fromio(buf, b47s->window + from, len); /* Read as much as possible using fast MMIO window */
*retlen = len; if (from < BCM47XXSFLASH_WINDOW_SZ) {
size_t memcpy_len;
return len; memcpy_len = min(len, (size_t)(BCM47XXSFLASH_WINDOW_SZ - from));
memcpy_fromio(buf, b47s->window + from, memcpy_len);
from += memcpy_len;
len -= memcpy_len;
buf += memcpy_len;
}
/* Use indirect access for content out of the window */
for (; len; len--) {
b47s->cc_write(b47s, BCMA_CC_FLASHADDR, from++);
bcm47xxsflash_cmd(b47s, OPCODE_ST_READ4B);
*buf++ = b47s->cc_read(b47s, BCMA_CC_FLASHDATA);
}
*retlen = orig_len;
return orig_len;
} }
static int bcm47xxsflash_write_st(struct mtd_info *mtd, u32 offset, size_t len, static int bcm47xxsflash_write_st(struct mtd_info *mtd, u32 offset, size_t len,
...@@ -284,7 +302,6 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) ...@@ -284,7 +302,6 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
b47s = devm_kzalloc(dev, sizeof(*b47s), GFP_KERNEL); b47s = devm_kzalloc(dev, sizeof(*b47s), GFP_KERNEL);
if (!b47s) if (!b47s)
return -ENOMEM; return -ENOMEM;
sflash->priv = b47s;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) { if (!res) {
...@@ -334,6 +351,8 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) ...@@ -334,6 +351,8 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
b47s->size = sflash->size; b47s->size = sflash->size;
bcm47xxsflash_fill_mtd(b47s, &pdev->dev); bcm47xxsflash_fill_mtd(b47s, &pdev->dev);
platform_set_drvdata(pdev, b47s);
err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0); err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
if (err) { if (err) {
pr_err("Failed to register MTD device: %d\n", err); pr_err("Failed to register MTD device: %d\n", err);
...@@ -349,8 +368,7 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) ...@@ -349,8 +368,7 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev)
static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) static int bcm47xxsflash_bcma_remove(struct platform_device *pdev)
{ {
struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); struct bcm47xxsflash *b47s = platform_get_drvdata(pdev);
struct bcm47xxsflash *b47s = sflash->priv;
mtd_device_unregister(&b47s->mtd); mtd_device_unregister(&b47s->mtd);
iounmap(b47s->window); iounmap(b47s->window);
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#define BCM47XXSFLASH_WINDOW_SZ SZ_16M
/* Used for ST flashes only. */ /* Used for ST flashes only. */
#define OPCODE_ST_WREN 0x0006 /* Write Enable */ #define OPCODE_ST_WREN 0x0006 /* Write Enable */
#define OPCODE_ST_WRDIS 0x0004 /* Write Disable */ #define OPCODE_ST_WRDIS 0x0004 /* Write Disable */
...@@ -16,6 +18,7 @@ ...@@ -16,6 +18,7 @@
#define OPCODE_ST_RES 0x03ab /* Read Electronic Signature */ #define OPCODE_ST_RES 0x03ab /* Read Electronic Signature */
#define OPCODE_ST_CSA 0x1000 /* Keep chip select asserted */ #define OPCODE_ST_CSA 0x1000 /* Keep chip select asserted */
#define OPCODE_ST_SSE 0x0220 /* Sub-sector Erase */ #define OPCODE_ST_SSE 0x0220 /* Sub-sector Erase */
#define OPCODE_ST_READ4B 0x6313 /* Read Data Bytes in 4Byte addressing mode */
/* Used for Atmel flashes only. */ /* Used for Atmel flashes only. */
#define OPCODE_AT_READ 0x07e8 #define OPCODE_AT_READ 0x07e8
......
...@@ -172,7 +172,8 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len, ...@@ -172,7 +172,8 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
t[1].rx_buf = buf; t[1].rx_buf = buf;
t[1].rx_nbits = m25p80_rx_nbits(nor); t[1].rx_nbits = m25p80_rx_nbits(nor);
t[1].len = min(len, spi_max_transfer_size(spi)); t[1].len = min3(len, spi_max_transfer_size(spi),
spi_max_message_size(spi) - t[0].len);
spi_message_add_tail(&t[1], &m); spi_message_add_tail(&t[1], &m);
ret = spi_sync(spi, &m); ret = spi_sync(spi, &m);
...@@ -288,7 +289,6 @@ static const struct spi_device_id m25p_ids[] = { ...@@ -288,7 +289,6 @@ static const struct spi_device_id m25p_ids[] = {
* should be kept for backward compatibility. * should be kept for backward compatibility.
*/ */
{"at25df321a"}, {"at25df641"}, {"at26df081a"}, {"at25df321a"}, {"at25df641"}, {"at26df081a"},
{"mr25h256"},
{"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"}, {"mx25l4005a"}, {"mx25l1606e"}, {"mx25l6405d"}, {"mx25l12805d"},
{"mx25l25635e"},{"mx66l51235l"}, {"mx25l25635e"},{"mx66l51235l"},
{"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"}, {"n25q064"}, {"n25q128a11"}, {"n25q128a13"}, {"n25q512a"},
...@@ -305,6 +305,11 @@ static const struct spi_device_id m25p_ids[] = { ...@@ -305,6 +305,11 @@ static const struct spi_device_id m25p_ids[] = {
{"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"}, {"m25p40-nonjedec"}, {"m25p80-nonjedec"}, {"m25p16-nonjedec"},
{"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"}, {"m25p32-nonjedec"}, {"m25p64-nonjedec"}, {"m25p128-nonjedec"},
/* Everspin MRAMs (non-JEDEC) */
{ "mr25h256" }, /* 256 Kib, 40 MHz */
{ "mr25h10" }, /* 1 Mib, 40 MHz */
{ "mr25h40" }, /* 4 Mib, 40 MHz */
{ }, { },
}; };
MODULE_DEVICE_TABLE(spi, m25p_ids); MODULE_DEVICE_TABLE(spi, m25p_ids);
......
...@@ -18,19 +18,12 @@ ...@@ -18,19 +18,12 @@
#define SPINOR_OP_RDVCR 0x85 #define SPINOR_OP_RDVCR 0x85
/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */ /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
#define SPINOR_OP_READ_1_2_2 0xbb /* DUAL I/O READ */
#define SPINOR_OP_READ_1_4_4 0xeb /* QUAD I/O READ */
#define SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */ #define SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */
#define SPINOR_OP_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */ #define SPINOR_OP_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
#define SPINOR_OP_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */ #define SPINOR_OP_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
#define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */ #define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
#define SPINOR_OP_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */ #define SPINOR_OP_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
/* READ commands with 32-bit addressing */
#define SPINOR_OP_READ4_1_2_2 0xbc
#define SPINOR_OP_READ4_1_4_4 0xec
/* Configuration flags */ /* Configuration flags */
#define FLASH_FLAG_SINGLE 0x000000ff #define FLASH_FLAG_SINGLE 0x000000ff
#define FLASH_FLAG_READ_WRITE 0x00000001 #define FLASH_FLAG_READ_WRITE 0x00000001
......
...@@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_configs[] = { ...@@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_configs[] = {
* - 'FAST' variants configured for 8 dummy cycles (see note above.) * - 'FAST' variants configured for 8 dummy cycles (see note above.)
*/ */
static struct seq_rw_config n25q_read4_configs[] = { static struct seq_rw_config n25q_read4_configs[] = {
{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8}, {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 0, 8},
{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8}, {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8}, {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 0, 8},
{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8}, {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
{FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST_4B, 0, 1, 1, 0x00, 0, 8},
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0}, {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B, 0, 1, 1, 0x00, 0, 0},
{0x00, 0, 0, 0, 0, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0},
}; };
/* /*
...@@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq) ...@@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
* entering a state that is incompatible with the SPIBoot Controller. * entering a state that is incompatible with the SPIBoot Controller.
*/ */
static struct seq_rw_config stfsm_s25fl_read4_configs[] = { static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4}, {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 2, 4},
{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8}, {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0}, {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 4, 0},
{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8}, {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
{FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8}, {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST_4B, 0, 1, 1, 0x00, 0, 8},
{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0}, {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B, 0, 1, 1, 0x00, 0, 0},
{0x00, 0, 0, 0, 0, 0x00, 0, 0}, {0x00, 0, 0, 0, 0, 0x00, 0, 0},
}; };
static struct seq_rw_config stfsm_s25fl_write4_configs[] = { static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
......
...@@ -75,7 +75,7 @@ config MTD_PHYSMAP_OF ...@@ -75,7 +75,7 @@ config MTD_PHYSMAP_OF
taken from OF device tree. taken from OF device tree.
config MTD_PHYSMAP_OF_VERSATILE config MTD_PHYSMAP_OF_VERSATILE
bool "Support ARM Versatile physmap OF" bool "ARM Versatile OF-based physical memory map handling"
depends on MTD_PHYSMAP_OF depends on MTD_PHYSMAP_OF
depends on MFD_SYSCON depends on MFD_SYSCON
default y if (ARCH_INTEGRATOR || ARCH_VERSATILE || ARCH_REALVIEW) default y if (ARCH_INTEGRATOR || ARCH_VERSATILE || ARCH_REALVIEW)
...@@ -84,6 +84,16 @@ config MTD_PHYSMAP_OF_VERSATILE ...@@ -84,6 +84,16 @@ config MTD_PHYSMAP_OF_VERSATILE
platforms, basically to add a VPP (write protection) callback so platforms, basically to add a VPP (write protection) callback so
the flash can be taken out of write protection. the flash can be taken out of write protection.
config MTD_PHYSMAP_OF_GEMINI
bool "Cortina Gemini OF-based physical memory map handling"
depends on MTD_PHYSMAP_OF
depends on MFD_SYSCON
default ARCH_GEMINI
help
This provides some extra DT physmap parsing for the Gemini
platforms, some detection and setting up parallel mode on the
external interface.
config MTD_PMC_MSP_EVM config MTD_PMC_MSP_EVM
tristate "CFI Flash device mapped on PMC-Sierra MSP" tristate "CFI Flash device mapped on PMC-Sierra MSP"
depends on PMC_MSP && MTD_CFI depends on PMC_MSP && MTD_CFI
......
...@@ -17,10 +17,13 @@ obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o ...@@ -17,10 +17,13 @@ obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
ifdef CONFIG_MTD_PHYSMAP_OF_VERSATILE ifdef CONFIG_MTD_PHYSMAP_OF_VERSATILE
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of_versatile.o physmap_of-objs += physmap_of_versatile.o
endif
ifdef CONFIG_MTD_PHYSMAP_OF_GEMINI
physmap_of-objs += physmap_of_gemini.o
endif endif
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
obj-$(CONFIG_MTD_PISMO) += pismo.o obj-$(CONFIG_MTD_PISMO) += pismo.o
obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
......
...@@ -57,10 +57,12 @@ static void ichxrom_cleanup(struct ichxrom_window *window) ...@@ -57,10 +57,12 @@ static void ichxrom_cleanup(struct ichxrom_window *window)
{ {
struct ichxrom_map_info *map, *scratch; struct ichxrom_map_info *map, *scratch;
u16 word; u16 word;
int ret;
/* Disable writes through the rom window */ /* Disable writes through the rom window */
pci_read_config_word(window->pdev, BIOS_CNTL, &word); ret = pci_read_config_word(window->pdev, BIOS_CNTL, &word);
pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1); if (!ret)
pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1);
pci_dev_put(window->pdev); pci_dev_put(window->pdev);
/* Free all of the mtd devices */ /* Free all of the mtd devices */
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* by the Free Software Foundation. * by the Free Software Foundation.
* *
* Copyright (C) 2004 Liu Peng Infineon IFAP DC COM CPE * Copyright (C) 2004 Liu Peng Infineon IFAP DC COM CPE
* Copyright (C) 2010 John Crispin <blogic@openwrt.org> * Copyright (C) 2010 John Crispin <john@phrozen.org>
*/ */
#include <linux/err.h> #include <linux/err.h>
...@@ -209,5 +209,5 @@ static struct platform_driver ltq_mtd_driver = { ...@@ -209,5 +209,5 @@ static struct platform_driver ltq_mtd_driver = {
module_platform_driver(ltq_mtd_driver); module_platform_driver(ltq_mtd_driver);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Crispin <blogic@openwrt.org>"); MODULE_AUTHOR("John Crispin <john@phrozen.org>");
MODULE_DESCRIPTION("Lantiq SoC NOR"); MODULE_DESCRIPTION("Lantiq SoC NOR");
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "physmap_of_gemini.h"
#include "physmap_of_versatile.h" #include "physmap_of_versatile.h"
struct of_flash_list { struct of_flash_list {
...@@ -241,11 +242,13 @@ static int of_flash_probe(struct platform_device *dev) ...@@ -241,11 +242,13 @@ static int of_flash_probe(struct platform_device *dev)
info->list[i].map.size = res_size; info->list[i].map.size = res_size;
info->list[i].map.bankwidth = be32_to_cpup(width); info->list[i].map.bankwidth = be32_to_cpup(width);
info->list[i].map.device_node = dp; info->list[i].map.device_node = dp;
err = of_flash_probe_gemini(dev, dp, &info->list[i].map);
if (err)
return err;
err = of_flash_probe_versatile(dev, dp, &info->list[i].map); err = of_flash_probe_versatile(dev, dp, &info->list[i].map);
if (err) { if (err)
dev_err(&dev->dev, "Can't probe Versatile VPP\n");
return err; return err;
}
err = -ENOMEM; err = -ENOMEM;
info->list[i].map.virt = ioremap(info->list[i].map.phys, info->list[i].map.virt = ioremap(info->list[i].map.phys,
......
/*
* Cortina Systems Gemini OF physmap add-on
* Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
*
* This SoC has an elaborate flash control register, so we need to
* detect and set it up when booting on this platform.
*/
#include <linux/export.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/mtd/map.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/bitops.h>
#include "physmap_of_gemini.h"
/*
* The Flash-relevant parts of the global status register
* These would also be relevant for a NAND driver.
*/
#define GLOBAL_STATUS 0x04
#define FLASH_TYPE_MASK (0x3 << 24)
#define FLASH_TYPE_NAND_2K (0x3 << 24)
#define FLASH_TYPE_NAND_512 (0x2 << 24)
#define FLASH_TYPE_PARALLEL (0x1 << 24)
#define FLASH_TYPE_SERIAL (0x0 << 24)
/* if parallel */
#define FLASH_WIDTH_16BIT (1 << 23) /* else 8 bit */
/* if serial */
#define FLASH_ATMEL (1 << 23) /* else STM */
#define FLASH_SIZE_MASK (0x3 << 21)
#define NAND_256M (0x3 << 21) /* and more */
#define NAND_128M (0x2 << 21)
#define NAND_64M (0x1 << 21)
#define NAND_32M (0x0 << 21)
#define ATMEL_16M (0x3 << 21) /* and more */
#define ATMEL_8M (0x2 << 21)
#define ATMEL_4M_2M (0x1 << 21)
#define ATMEL_1M (0x0 << 21) /* and less */
#define STM_32M (1 << 22) /* and more */
#define STM_16M (0 << 22) /* and less */
#define FLASH_PARALLEL_HIGH_PIN_CNT (1 << 20) /* else low pin cnt */
/* Miscellaneous Control Register */
#define GLOBAL_MISC_CTRL 0x30
#define FLASH_PADS_MASK 0x07
#define NAND_PADS_DISABLE BIT(2)
#define PFLASH_PADS_DISABLE BIT(1)
#define SFLASH_PADS_DISABLE BIT(0)
static const struct of_device_id syscon_match[] = {
{ .compatible = "cortina,gemini-syscon" },
{ },
};
int of_flash_probe_gemini(struct platform_device *pdev,
struct device_node *np,
struct map_info *map)
{
static struct regmap *rmap;
struct device *dev = &pdev->dev;
u32 val;
int ret;
/* Multiplatform guard */
if (!of_device_is_compatible(np, "cortina,gemini-flash"))
return 0;
rmap = syscon_regmap_lookup_by_phandle(np, "syscon");
if (IS_ERR(rmap)) {
dev_err(dev, "no syscon\n");
return PTR_ERR(rmap);
}
ret = regmap_read(rmap, GLOBAL_STATUS, &val);
if (ret) {
dev_err(dev, "failed to read global status register\n");
return -ENODEV;
}
dev_dbg(dev, "global status reg: %08x\n", val);
/*
* It would be contradictory if a physmap flash was NOT parallel.
*/
if ((val & FLASH_TYPE_MASK) != FLASH_TYPE_PARALLEL) {
dev_err(dev, "flash is not parallel\n");
return -ENODEV;
}
/*
* Complain if DT data and hardware definition is different.
*/
if (val & FLASH_WIDTH_16BIT) {
if (map->bankwidth != 2)
dev_warn(dev, "flash hardware say flash is 16 bit wide but DT says it is %d bits wide\n",
map->bankwidth * 8);
} else {
if (map->bankwidth != 1)
dev_warn(dev, "flash hardware say flash is 8 bit wide but DT says it is %d bits wide\n",
map->bankwidth * 8);
}
/* Activate parallel (NOR flash) mode */
ret = regmap_update_bits(rmap, GLOBAL_MISC_CTRL,
FLASH_PADS_MASK,
SFLASH_PADS_DISABLE | NAND_PADS_DISABLE);
if (ret) {
dev_err(dev, "unable to set up physmap pads\n");
return -ENODEV;
}
dev_info(&pdev->dev, "initialized Gemini-specific physmap control\n");
return 0;
}
#include <linux/of.h>
#include <linux/mtd/map.h>
#ifdef CONFIG_MTD_PHYSMAP_OF_GEMINI
int of_flash_probe_gemini(struct platform_device *pdev,
struct device_node *np,
struct map_info *map);
#else
static inline
int of_flash_probe_gemini(struct platform_device *pdev,
struct device_node *np,
struct map_info *map)
{
return 0;
}
#endif
...@@ -252,4 +252,3 @@ int of_flash_probe_versatile(struct platform_device *pdev, ...@@ -252,4 +252,3 @@ int of_flash_probe_versatile(struct platform_device *pdev,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(of_flash_probe_versatile);
...@@ -139,15 +139,13 @@ static int __init init_msp_flash(void) ...@@ -139,15 +139,13 @@ static int __init init_msp_flash(void)
} }
msp_maps[i].bankwidth = 1; msp_maps[i].bankwidth = 1;
msp_maps[i].name = kmalloc(7, GFP_KERNEL); msp_maps[i].name = kstrndup(flash_name, 7, GFP_KERNEL);
if (!msp_maps[i].name) { if (!msp_maps[i].name) {
iounmap(msp_maps[i].virt); iounmap(msp_maps[i].virt);
kfree(msp_parts[i]); kfree(msp_parts[i]);
goto cleanup_loop; goto cleanup_loop;
} }
msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7);
for (j = 0; j < pcnt; j++) { for (j = 0; j < pcnt; j++) {
part_name[5] = '0' + i; part_name[5] = '0' + i;
part_name[7] = '0' + j; part_name[7] = '0' + j;
......
...@@ -451,7 +451,7 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd, ...@@ -451,7 +451,7 @@ static int mtdchar_readoob(struct file *file, struct mtd_info *mtd,
* data. For our userspace tools it is important to dump areas * data. For our userspace tools it is important to dump areas
* with ECC errors! * with ECC errors!
* For kernel internal usage it also might return -EUCLEAN * For kernel internal usage it also might return -EUCLEAN
* to signal the caller that a bitflip has occured and has * to signal the caller that a bitflip has occurred and has
* been corrected by the ECC algorithm. * been corrected by the ECC algorithm.
* *
* Note: currently the standard NAND function, nand_read_oob_std, * Note: currently the standard NAND function, nand_read_oob_std,
......
...@@ -1128,7 +1128,7 @@ EXPORT_SYMBOL_GPL(mtd_write_oob); ...@@ -1128,7 +1128,7 @@ EXPORT_SYMBOL_GPL(mtd_write_oob);
* @oobecc: OOB region struct filled with the appropriate ECC position * @oobecc: OOB region struct filled with the appropriate ECC position
* information * information
* *
* This functions return ECC section information in the OOB area. I you want * This function returns ECC section information in the OOB area. If you want
* to get all the ECC bytes information, then you should call * to get all the ECC bytes information, then you should call
* mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE. * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE.
* *
...@@ -1160,7 +1160,7 @@ EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc); ...@@ -1160,7 +1160,7 @@ EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
* @oobfree: OOB region struct filled with the appropriate free position * @oobfree: OOB region struct filled with the appropriate free position
* information * information
* *
* This functions return free bytes position in the OOB area. I you want * This function returns free bytes position in the OOB area. If you want
* to get all the free bytes information, then you should call * to get all the free bytes information, then you should call
* mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE. * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE.
* *
...@@ -1190,7 +1190,7 @@ EXPORT_SYMBOL_GPL(mtd_ooblayout_free); ...@@ -1190,7 +1190,7 @@ EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
* @iter: iterator function. Should be either mtd_ooblayout_free or * @iter: iterator function. Should be either mtd_ooblayout_free or
* mtd_ooblayout_ecc depending on the region type you're searching for * mtd_ooblayout_ecc depending on the region type you're searching for
* *
* This functions returns the section id and oobregion information of a * This function returns the section id and oobregion information of a
* specific byte. For example, say you want to know where the 4th ECC byte is * specific byte. For example, say you want to know where the 4th ECC byte is
* stored, you'll use: * stored, you'll use:
* *
......
...@@ -349,6 +349,14 @@ static const struct mtd_ooblayout_ops part_ooblayout_ops = { ...@@ -349,6 +349,14 @@ static const struct mtd_ooblayout_ops part_ooblayout_ops = {
.free = part_ooblayout_free, .free = part_ooblayout_free,
}; };
static int part_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->master->_max_bad_blocks(part->master,
ofs + part->offset, len);
}
static inline void free_partition(struct mtd_part *p) static inline void free_partition(struct mtd_part *p)
{ {
kfree(p->mtd.name); kfree(p->mtd.name);
...@@ -424,6 +432,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, ...@@ -424,6 +432,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) ? slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) ?
&master->dev : &master->dev :
master->dev.parent; master->dev.parent;
slave->mtd.dev.of_node = part->of_node;
slave->mtd._read = part_read; slave->mtd._read = part_read;
slave->mtd._write = part_write; slave->mtd._write = part_write;
...@@ -475,6 +484,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, ...@@ -475,6 +484,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *master,
slave->mtd._block_isbad = part_block_isbad; slave->mtd._block_isbad = part_block_isbad;
if (master->_block_markbad) if (master->_block_markbad)
slave->mtd._block_markbad = part_block_markbad; slave->mtd._block_markbad = part_block_markbad;
if (master->_max_bad_blocks)
slave->mtd._max_bad_blocks = part_max_bad_blocks;
if (master->_get_device) if (master->_get_device)
slave->mtd._get_device = part_get_device; slave->mtd._get_device = part_get_device;
......
...@@ -426,6 +426,7 @@ config MTD_NAND_ORION ...@@ -426,6 +426,7 @@ config MTD_NAND_ORION
config MTD_NAND_OXNAS config MTD_NAND_OXNAS
tristate "NAND Flash support for Oxford Semiconductor SoC" tristate "NAND Flash support for Oxford Semiconductor SoC"
depends on ARCH_OXNAS || COMPILE_TEST
depends on HAS_IOMEM depends on HAS_IOMEM
help help
This enables the NAND flash controller on Oxford Semiconductor SoCs. This enables the NAND flash controller on Oxford Semiconductor SoCs.
...@@ -535,6 +536,7 @@ config MTD_NAND_JZ4780 ...@@ -535,6 +536,7 @@ config MTD_NAND_JZ4780
config MTD_NAND_FSMC config MTD_NAND_FSMC
tristate "Support for NAND on ST Micros FSMC" tristate "Support for NAND on ST Micros FSMC"
depends on OF
depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300 depends on PLAT_SPEAR || ARCH_NOMADIK || ARCH_U8500 || MACH_U300
help help
Enables support for NAND Flash chips on the ST Microelectronics Enables support for NAND Flash chips on the ST Microelectronics
......
...@@ -258,9 +258,15 @@ static void fsl_ifc_run_command(struct mtd_info *mtd) ...@@ -258,9 +258,15 @@ static void fsl_ifc_run_command(struct mtd_info *mtd)
int bufnum = nctrl->page & priv->bufnum_mask; int bufnum = nctrl->page & priv->bufnum_mask;
int sector = bufnum * chip->ecc.steps; int sector = bufnum * chip->ecc.steps;
int sector_end = sector + chip->ecc.steps - 1; int sector_end = sector + chip->ecc.steps - 1;
__be32 *eccstat_regs;
if (ctrl->version >= FSL_IFC_VERSION_2_0_0)
eccstat_regs = ifc->ifc_nand.v2_nand_eccstat;
else
eccstat_regs = ifc->ifc_nand.v1_nand_eccstat;
for (i = sector / 4; i <= sector_end / 4; i++) for (i = sector / 4; i <= sector_end / 4; i++)
eccstat[i] = ifc_in32(&ifc->ifc_nand.nand_eccstat[i]); eccstat[i] = ifc_in32(&eccstat_regs[i]);
for (i = sector; i <= sector_end; i++) { for (i = sector; i <= sector_end; i++) {
errors = check_read_ecc(mtd, ctrl, eccstat, i); errors = check_read_ecc(mtd, ctrl, eccstat, i);
......
...@@ -35,10 +35,133 @@ ...@@ -35,10 +35,133 @@
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/mtd/fsmc.h>
#include <linux/amba/bus.h> #include <linux/amba/bus.h>
#include <mtd/mtd-abi.h> #include <mtd/mtd-abi.h>
#define FSMC_NAND_BW8 1
#define FSMC_NAND_BW16 2
#define FSMC_MAX_NOR_BANKS 4
#define FSMC_MAX_NAND_BANKS 4
#define FSMC_FLASH_WIDTH8 1
#define FSMC_FLASH_WIDTH16 2
/* fsmc controller registers for NOR flash */
#define CTRL 0x0
/* ctrl register definitions */
#define BANK_ENABLE (1 << 0)
#define MUXED (1 << 1)
#define NOR_DEV (2 << 2)
#define WIDTH_8 (0 << 4)
#define WIDTH_16 (1 << 4)
#define RSTPWRDWN (1 << 6)
#define WPROT (1 << 7)
#define WRT_ENABLE (1 << 12)
#define WAIT_ENB (1 << 13)
#define CTRL_TIM 0x4
/* ctrl_tim register definitions */
#define FSMC_NOR_BANK_SZ 0x8
#define FSMC_NOR_REG_SIZE 0x40
#define FSMC_NOR_REG(base, bank, reg) (base + \
FSMC_NOR_BANK_SZ * (bank) + \
reg)
/* fsmc controller registers for NAND flash */
#define PC 0x00
/* pc register definitions */
#define FSMC_RESET (1 << 0)
#define FSMC_WAITON (1 << 1)
#define FSMC_ENABLE (1 << 2)
#define FSMC_DEVTYPE_NAND (1 << 3)
#define FSMC_DEVWID_8 (0 << 4)
#define FSMC_DEVWID_16 (1 << 4)
#define FSMC_ECCEN (1 << 6)
#define FSMC_ECCPLEN_512 (0 << 7)
#define FSMC_ECCPLEN_256 (1 << 7)
#define FSMC_TCLR_1 (1)
#define FSMC_TCLR_SHIFT (9)
#define FSMC_TCLR_MASK (0xF)
#define FSMC_TAR_1 (1)
#define FSMC_TAR_SHIFT (13)
#define FSMC_TAR_MASK (0xF)
#define STS 0x04
/* sts register definitions */
#define FSMC_CODE_RDY (1 << 15)
#define COMM 0x08
/* comm register definitions */
#define FSMC_TSET_0 0
#define FSMC_TSET_SHIFT 0
#define FSMC_TSET_MASK 0xFF
#define FSMC_TWAIT_6 6
#define FSMC_TWAIT_SHIFT 8
#define FSMC_TWAIT_MASK 0xFF
#define FSMC_THOLD_4 4
#define FSMC_THOLD_SHIFT 16
#define FSMC_THOLD_MASK 0xFF
#define FSMC_THIZ_1 1
#define FSMC_THIZ_SHIFT 24
#define FSMC_THIZ_MASK 0xFF
#define ATTRIB 0x0C
#define IOATA 0x10
#define ECC1 0x14
#define ECC2 0x18
#define ECC3 0x1C
#define FSMC_NAND_BANK_SZ 0x20
#define FSMC_NAND_REG(base, bank, reg) (base + FSMC_NOR_REG_SIZE + \
(FSMC_NAND_BANK_SZ * (bank)) + \
reg)
#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
struct fsmc_nand_timings {
uint8_t tclr;
uint8_t tar;
uint8_t thiz;
uint8_t thold;
uint8_t twait;
uint8_t tset;
};
enum access_mode {
USE_DMA_ACCESS = 1,
USE_WORD_ACCESS,
};
/**
* fsmc_nand_platform_data - platform specific NAND controller config
* @nand_timings: timing setup for the physical NAND interface
* @partitions: partition table for the platform, use a default fallback
* if this is NULL
* @nr_partitions: the number of partitions in the previous entry
* @options: different options for the driver
* @width: bus width
* @bank: default bank
* @select_bank: callback to select a certain bank, this is
* platform-specific. If the controller only supports one bank
* this may be set to NULL
*/
struct fsmc_nand_platform_data {
struct fsmc_nand_timings *nand_timings;
struct mtd_partition *partitions;
unsigned int nr_partitions;
unsigned int options;
unsigned int width;
unsigned int bank;
enum access_mode mode;
void (*select_bank)(uint32_t bank, uint32_t busw);
/* priv structures for dma accesses */
void *read_dma_priv;
void *write_dma_priv;
};
static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section, static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion) struct mtd_oob_region *oobregion)
{ {
...@@ -714,7 +837,6 @@ static bool filter(struct dma_chan *chan, void *slave) ...@@ -714,7 +837,6 @@ static bool filter(struct dma_chan *chan, void *slave)
return true; return true;
} }
#ifdef CONFIG_OF
static int fsmc_nand_probe_config_dt(struct platform_device *pdev, static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
struct device_node *np) struct device_node *np)
{ {
...@@ -757,13 +879,6 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev, ...@@ -757,13 +879,6 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
} }
return 0; return 0;
} }
#else
static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
struct device_node *np)
{
return -ENOSYS;
}
#endif
/* /*
* fsmc_nand_probe - Probe function * fsmc_nand_probe - Probe function
...@@ -782,19 +897,15 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) ...@@ -782,19 +897,15 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
u32 pid; u32 pid;
int i; int i;
if (np) { pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata)
pdev->dev.platform_data = pdata; return -ENOMEM;
ret = fsmc_nand_probe_config_dt(pdev, np);
if (ret) {
dev_err(&pdev->dev, "no platform data\n");
return -ENODEV;
}
}
if (!pdata) { pdev->dev.platform_data = pdata;
dev_err(&pdev->dev, "platform data is NULL\n"); ret = fsmc_nand_probe_config_dt(pdev, np);
return -EINVAL; if (ret) {
dev_err(&pdev->dev, "no platform data\n");
return -ENODEV;
} }
/* Allocate memory for the device structure (and zero it) */ /* Allocate memory for the device structure (and zero it) */
......
...@@ -797,22 +797,17 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) ...@@ -797,22 +797,17 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
struct resource *rc; struct resource *rc;
int res; int res;
rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (rc == NULL) {
dev_err(&pdev->dev, "No memory resource found for device\n");
return -EBUSY;
}
/* Allocate memory for the device structure (and zero it) */ /* Allocate memory for the device structure (and zero it) */
host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
if (!host) if (!host)
return -ENOMEM; return -ENOMEM;
host->io_base_dma = rc->start;
rc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->io_base = devm_ioremap_resource(&pdev->dev, rc); host->io_base = devm_ioremap_resource(&pdev->dev, rc);
if (IS_ERR(host->io_base)) if (IS_ERR(host->io_base))
return PTR_ERR(host->io_base); return PTR_ERR(host->io_base);
host->io_base_dma = rc->start;
if (pdev->dev.of_node) if (pdev->dev.of_node)
host->ncfg = lpc32xx_parse_dt(&pdev->dev); host->ncfg = lpc32xx_parse_dt(&pdev->dev);
if (!host->ncfg) { if (!host->ncfg) {
......
...@@ -1383,7 +1383,6 @@ static int mtk_nfc_probe(struct platform_device *pdev) ...@@ -1383,7 +1383,6 @@ static int mtk_nfc_probe(struct platform_device *pdev)
nfc->regs = devm_ioremap_resource(dev, res); nfc->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(nfc->regs)) { if (IS_ERR(nfc->regs)) {
ret = PTR_ERR(nfc->regs); ret = PTR_ERR(nfc->regs);
dev_err(dev, "no nfi base\n");
goto release_ecc; goto release_ecc;
} }
......
...@@ -3262,6 +3262,42 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) ...@@ -3262,6 +3262,42 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
return nand_block_markbad_lowlevel(mtd, ofs); return nand_block_markbad_lowlevel(mtd, ofs);
} }
/**
* nand_max_bad_blocks - [MTD Interface] Max number of bad blocks for an mtd
* @mtd: MTD device structure
* @ofs: offset relative to mtd start
* @len: length of mtd
*/
static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
{
struct nand_chip *chip = mtd_to_nand(mtd);
u32 part_start_block;
u32 part_end_block;
u32 part_start_die;
u32 part_end_die;
/*
* max_bb_per_die and blocks_per_die used to determine
* the maximum bad block count.
*/
if (!chip->max_bb_per_die || !chip->blocks_per_die)
return -ENOTSUPP;
/* Get the start and end of the partition in erase blocks. */
part_start_block = mtd_div_by_eb(ofs, mtd);
part_end_block = mtd_div_by_eb(len, mtd) + part_start_block - 1;
/* Get the start and end LUNs of the partition. */
part_start_die = part_start_block / chip->blocks_per_die;
part_end_die = part_end_block / chip->blocks_per_die;
/*
* Look up the bad blocks per unit and multiply by the number of units
* that the partition spans.
*/
return chip->max_bb_per_die * (part_end_die - part_start_die + 1);
}
/** /**
* nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand * nand_onfi_set_features- [REPLACEABLE] set features for ONFI nand
* @mtd: MTD device structure * @mtd: MTD device structure
...@@ -3592,6 +3628,9 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -3592,6 +3628,9 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
chip->bits_per_cell = p->bits_per_cell; chip->bits_per_cell = p->bits_per_cell;
chip->max_bb_per_die = le16_to_cpu(p->bb_per_lun);
chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun);
if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS)
*busw = NAND_BUSWIDTH_16; *busw = NAND_BUSWIDTH_16;
else else
...@@ -4815,6 +4854,7 @@ int nand_scan_tail(struct mtd_info *mtd) ...@@ -4815,6 +4854,7 @@ int nand_scan_tail(struct mtd_info *mtd)
mtd->_block_isreserved = nand_block_isreserved; mtd->_block_isreserved = nand_block_isreserved;
mtd->_block_isbad = nand_block_isbad; mtd->_block_isbad = nand_block_isbad;
mtd->_block_markbad = nand_block_markbad; mtd->_block_markbad = nand_block_markbad;
mtd->_max_bad_blocks = nand_max_bad_blocks;
mtd->writebufsize = mtd->writesize; mtd->writebufsize = mtd->writesize;
/* /*
......
...@@ -185,6 +185,7 @@ struct nand_manufacturers nand_manuf_ids[] = { ...@@ -185,6 +185,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_SANDISK, "SanDisk"}, {NAND_MFR_SANDISK, "SanDisk"},
{NAND_MFR_INTEL, "Intel"}, {NAND_MFR_INTEL, "Intel"},
{NAND_MFR_ATO, "ATO"}, {NAND_MFR_ATO, "ATO"},
{NAND_MFR_WINBOND, "Winbond"},
{0x0, "Unknown"} {0x0, "Unknown"}
}; };
......
...@@ -321,6 +321,10 @@ static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events, ...@@ -321,6 +321,10 @@ static int sunxi_nfc_wait_events(struct sunxi_nfc *nfc, u32 events,
ret = wait_for_completion_timeout(&nfc->complete, ret = wait_for_completion_timeout(&nfc->complete,
msecs_to_jiffies(timeout_ms)); msecs_to_jiffies(timeout_ms));
if (!ret)
ret = -ETIMEDOUT;
else
ret = 0;
writel(0, nfc->regs + NFC_REG_INT); writel(0, nfc->regs + NFC_REG_INT);
} else { } else {
...@@ -518,6 +522,8 @@ static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ...@@ -518,6 +522,8 @@ static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
u32 tmp; u32 tmp;
while (len > offs) { while (len > offs) {
bool poll = false;
cnt = min(len - offs, NFC_SRAM_SIZE); cnt = min(len - offs, NFC_SRAM_SIZE);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
...@@ -528,7 +534,11 @@ static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ...@@ -528,7 +534,11 @@ static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD; tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
writel(tmp, nfc->regs + NFC_REG_CMD); writel(tmp, nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); /* Arbitrary limit for polling mode */
if (cnt < 64)
poll = true;
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
if (ret) if (ret)
break; break;
...@@ -551,6 +561,8 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, ...@@ -551,6 +561,8 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
u32 tmp; u32 tmp;
while (len > offs) { while (len > offs) {
bool poll = false;
cnt = min(len - offs, NFC_SRAM_SIZE); cnt = min(len - offs, NFC_SRAM_SIZE);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc); ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
...@@ -563,7 +575,11 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, ...@@ -563,7 +575,11 @@ static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
NFC_ACCESS_DIR; NFC_ACCESS_DIR;
writel(tmp, nfc->regs + NFC_REG_CMD); writel(tmp, nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); /* Arbitrary limit for polling mode */
if (cnt < 64)
poll = true;
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, poll, 0);
if (ret) if (ret)
break; break;
...@@ -588,10 +604,6 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, ...@@ -588,10 +604,6 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
int ret; int ret;
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
return;
if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) && if (dat == NAND_CMD_NONE && (ctrl & NAND_NCE) &&
!(ctrl & (NAND_CLE | NAND_ALE))) { !(ctrl & (NAND_CLE | NAND_ALE))) {
u32 cmd = 0; u32 cmd = 0;
...@@ -621,6 +633,10 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, ...@@ -621,6 +633,10 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
writel(sunxi_nand->addr[1], writel(sunxi_nand->addr[1],
nfc->regs + NFC_REG_ADDR_HIGH); nfc->regs + NFC_REG_ADDR_HIGH);
ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
if (ret)
return;
writel(cmd, nfc->regs + NFC_REG_CMD); writel(cmd, nfc->regs + NFC_REG_CMD);
sunxi_nand->addr[0] = 0; sunxi_nand->addr[0] = 0;
sunxi_nand->addr[1] = 0; sunxi_nand->addr[1] = 0;
...@@ -957,7 +973,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, ...@@ -957,7 +973,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd,
writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP,
nfc->regs + NFC_REG_CMD); nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
sunxi_nfc_randomizer_disable(mtd); sunxi_nfc_randomizer_disable(mtd);
if (ret) if (ret)
return ret; return ret;
...@@ -1069,7 +1085,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct mtd_info *mtd, uint8_t *buf, ...@@ -1069,7 +1085,7 @@ static int sunxi_nfc_hw_ecc_read_chunks_dma(struct mtd_info *mtd, uint8_t *buf,
writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS, writel(NFC_PAGE_OP | NFC_DATA_SWAP_METHOD | NFC_DATA_TRANS,
nfc->regs + NFC_REG_CMD); nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
if (ret) if (ret)
dmaengine_terminate_all(nfc->dmac); dmaengine_terminate_all(nfc->dmac);
...@@ -1189,7 +1205,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, ...@@ -1189,7 +1205,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd,
NFC_ACCESS_DIR | NFC_ECC_OP, NFC_ACCESS_DIR | NFC_ECC_OP,
nfc->regs + NFC_REG_CMD); nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
sunxi_nfc_randomizer_disable(mtd); sunxi_nfc_randomizer_disable(mtd);
if (ret) if (ret)
return ret; return ret;
...@@ -1428,7 +1444,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd, ...@@ -1428,7 +1444,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd,
NFC_DATA_TRANS | NFC_ACCESS_DIR, NFC_DATA_TRANS | NFC_ACCESS_DIR,
nfc->regs + NFC_REG_CMD); nfc->regs + NFC_REG_CMD);
ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, true, 0); ret = sunxi_nfc_wait_events(nfc, NFC_CMD_INT_FLAG, false, 0);
if (ret) if (ret)
dmaengine_terminate_all(nfc->dmac); dmaengine_terminate_all(nfc->dmac);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* under the terms of the GNU General Public License version 2 as published * under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation. * by the Free Software Foundation.
* *
* Copyright © 2012 John Crispin <blogic@openwrt.org> * Copyright © 2012 John Crispin <john@phrozen.org>
* Copyright © 2016 Hauke Mehrtens <hauke@hauke-m.de> * Copyright © 2016 Hauke Mehrtens <hauke@hauke-m.de>
*/ */
......
...@@ -108,6 +108,7 @@ static int parse_ofpart_partitions(struct mtd_info *master, ...@@ -108,6 +108,7 @@ static int parse_ofpart_partitions(struct mtd_info *master,
parts[i].offset = of_read_number(reg, a_cells); parts[i].offset = of_read_number(reg, a_cells);
parts[i].size = of_read_number(reg + a_cells, s_cells); parts[i].size = of_read_number(reg + a_cells, s_cells);
parts[i].of_node = pp;
partname = of_get_property(pp, "label", &len); partname = of_get_property(pp, "label", &len);
if (!partname) if (!partname)
......
...@@ -29,6 +29,16 @@ config MTD_SPI_NOR_USE_4K_SECTORS ...@@ -29,6 +29,16 @@ config MTD_SPI_NOR_USE_4K_SECTORS
Please note that some tools/drivers/filesystems may not work with Please note that some tools/drivers/filesystems may not work with
4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum). 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum).
config SPI_ASPEED_SMC
tristate "Aspeed flash controllers in SPI mode"
depends on ARCH_ASPEED || COMPILE_TEST
depends on HAS_IOMEM && OF
help
This enables support for the Firmware Memory controller (FMC)
in the Aspeed AST2500/AST2400 SoCs when attached to SPI NOR chips,
and support for the SPI flash memory controller (SPI) for
the host firmware. The implementation only supports SPI NOR.
config SPI_ATMEL_QUADSPI config SPI_ATMEL_QUADSPI
tristate "Atmel Quad SPI Controller" tristate "Atmel Quad SPI Controller"
depends on ARCH_AT91 || (ARM && COMPILE_TEST) depends on ARCH_AT91 || (ARM && COMPILE_TEST)
...@@ -40,7 +50,7 @@ config SPI_ATMEL_QUADSPI ...@@ -40,7 +50,7 @@ config SPI_ATMEL_QUADSPI
config SPI_CADENCE_QUADSPI config SPI_CADENCE_QUADSPI
tristate "Cadence Quad SPI controller" tristate "Cadence Quad SPI controller"
depends on OF && ARM depends on OF && (ARM || COMPILE_TEST)
help help
Enable support for the Cadence Quad SPI Flash controller. Enable support for the Cadence Quad SPI Flash controller.
...@@ -76,4 +86,24 @@ config SPI_NXP_SPIFI ...@@ -76,4 +86,24 @@ config SPI_NXP_SPIFI
Flash. Enable this option if you have a device with a SPIFI Flash. Enable this option if you have a device with a SPIFI
controller and want to access the Flash as a mtd device. controller and want to access the Flash as a mtd device.
config SPI_INTEL_SPI
tristate
config SPI_INTEL_SPI_PLATFORM
tristate "Intel PCH/PCU SPI flash platform driver" if EXPERT
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.
endif # MTD_SPI_NOR endif # MTD_SPI_NOR
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o
obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o obj-$(CONFIG_MTD_MT81xx_NOR) += mtk-quadspi.o
obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o
obj-$(CONFIG_SPI_INTEL_SPI) += intel-spi.o
obj-$(CONFIG_SPI_INTEL_SPI_PLATFORM) += intel-spi-platform.o
/*
* ASPEED Static Memory Controller driver
*
* Copyright (c) 2015-2016, IBM Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/spi-nor.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/sysfs.h>
#define DEVICE_NAME "aspeed-smc"
/*
* The driver only support SPI flash
*/
enum aspeed_smc_flash_type {
smc_type_nor = 0,
smc_type_nand = 1,
smc_type_spi = 2,
};
struct aspeed_smc_chip;
struct aspeed_smc_info {
u32 maxsize; /* maximum size of chip window */
u8 nce; /* number of chip enables */
bool hastype; /* flash type field exists in config reg */
u8 we0; /* shift for write enable bit for CE0 */
u8 ctl0; /* offset in regs of ctl for CE0 */
void (*set_4b)(struct aspeed_smc_chip *chip);
};
static void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip);
static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip);
static const struct aspeed_smc_info fmc_2400_info = {
.maxsize = 64 * 1024 * 1024,
.nce = 5,
.hastype = true,
.we0 = 16,
.ctl0 = 0x10,
.set_4b = aspeed_smc_chip_set_4b,
};
static const struct aspeed_smc_info spi_2400_info = {
.maxsize = 64 * 1024 * 1024,
.nce = 1,
.hastype = false,
.we0 = 0,
.ctl0 = 0x04,
.set_4b = aspeed_smc_chip_set_4b_spi_2400,
};
static const struct aspeed_smc_info fmc_2500_info = {
.maxsize = 256 * 1024 * 1024,
.nce = 3,
.hastype = true,
.we0 = 16,
.ctl0 = 0x10,
.set_4b = aspeed_smc_chip_set_4b,
};
static const struct aspeed_smc_info spi_2500_info = {
.maxsize = 128 * 1024 * 1024,
.nce = 2,
.hastype = false,
.we0 = 16,
.ctl0 = 0x10,
.set_4b = aspeed_smc_chip_set_4b,
};
enum aspeed_smc_ctl_reg_value {
smc_base, /* base value without mode for other commands */
smc_read, /* command reg for (maybe fast) reads */
smc_write, /* command reg for writes */
smc_max,
};
struct aspeed_smc_controller;
struct aspeed_smc_chip {
int cs;
struct aspeed_smc_controller *controller;
void __iomem *ctl; /* control register */
void __iomem *ahb_base; /* base of chip window */
u32 ctl_val[smc_max]; /* control settings */
enum aspeed_smc_flash_type type; /* what type of flash */
struct spi_nor nor;
};
struct aspeed_smc_controller {
struct device *dev;
struct mutex mutex; /* controller access mutex */
const struct aspeed_smc_info *info; /* type info of controller */
void __iomem *regs; /* controller registers */
void __iomem *ahb_base; /* per-chip windows resource */
struct aspeed_smc_chip *chips[0]; /* pointers to attached chips */
};
/*
* SPI Flash Configuration Register (AST2500 SPI)
* or
* Type setting Register (AST2500 FMC).
* CE0 and CE1 can only be of type SPI. CE2 can be of type NOR but the
* driver does not support it.
*/
#define CONFIG_REG 0x0
#define CONFIG_DISABLE_LEGACY BIT(31) /* 1 */
#define CONFIG_CE2_WRITE BIT(18)
#define CONFIG_CE1_WRITE BIT(17)
#define CONFIG_CE0_WRITE BIT(16)
#define CONFIG_CE2_TYPE BIT(4) /* AST2500 FMC only */
#define CONFIG_CE1_TYPE BIT(2) /* AST2500 FMC only */
#define CONFIG_CE0_TYPE BIT(0) /* AST2500 FMC only */
/*
* CE Control Register
*/
#define CE_CONTROL_REG 0x4
/*
* CEx Control Register
*/
#define CONTROL_AAF_MODE BIT(31)
#define CONTROL_IO_MODE_MASK GENMASK(30, 28)
#define CONTROL_IO_DUAL_DATA BIT(29)
#define CONTROL_IO_DUAL_ADDR_DATA (BIT(29) | BIT(28))
#define CONTROL_IO_QUAD_DATA BIT(30)
#define CONTROL_IO_QUAD_ADDR_DATA (BIT(30) | BIT(28))
#define CONTROL_CE_INACTIVE_SHIFT 24
#define CONTROL_CE_INACTIVE_MASK GENMASK(27, \
CONTROL_CE_INACTIVE_SHIFT)
/* 0 = 16T ... 15 = 1T T=HCLK */
#define CONTROL_COMMAND_SHIFT 16
#define CONTROL_DUMMY_COMMAND_OUT BIT(15)
#define CONTROL_IO_DUMMY_HI BIT(14)
#define CONTROL_IO_DUMMY_HI_SHIFT 14
#define CONTROL_CLK_DIV4 BIT(13) /* others */
#define CONTROL_IO_ADDRESS_4B BIT(13) /* AST2400 SPI */
#define CONTROL_RW_MERGE BIT(12)
#define CONTROL_IO_DUMMY_LO_SHIFT 6
#define CONTROL_IO_DUMMY_LO GENMASK(7, \
CONTROL_IO_DUMMY_LO_SHIFT)
#define CONTROL_IO_DUMMY_MASK (CONTROL_IO_DUMMY_HI | \
CONTROL_IO_DUMMY_LO)
#define CONTROL_IO_DUMMY_SET(dummy) \
(((((dummy) >> 2) & 0x1) << CONTROL_IO_DUMMY_HI_SHIFT) | \
(((dummy) & 0x3) << CONTROL_IO_DUMMY_LO_SHIFT))
#define CONTROL_CLOCK_FREQ_SEL_SHIFT 8
#define CONTROL_CLOCK_FREQ_SEL_MASK GENMASK(11, \
CONTROL_CLOCK_FREQ_SEL_SHIFT)
#define CONTROL_LSB_FIRST BIT(5)
#define CONTROL_CLOCK_MODE_3 BIT(4)
#define CONTROL_IN_DUAL_DATA BIT(3)
#define CONTROL_CE_STOP_ACTIVE_CONTROL BIT(2)
#define CONTROL_COMMAND_MODE_MASK GENMASK(1, 0)
#define CONTROL_COMMAND_MODE_NORMAL 0
#define CONTROL_COMMAND_MODE_FREAD 1
#define CONTROL_COMMAND_MODE_WRITE 2
#define CONTROL_COMMAND_MODE_USER 3
#define CONTROL_KEEP_MASK \
(CONTROL_AAF_MODE | CONTROL_CE_INACTIVE_MASK | CONTROL_CLK_DIV4 | \
CONTROL_IO_DUMMY_MASK | CONTROL_CLOCK_FREQ_SEL_MASK | \
CONTROL_LSB_FIRST | CONTROL_CLOCK_MODE_3)
/*
* The Segment Register uses a 8MB unit to encode the start address
* and the end address of the mapping window of a flash SPI slave :
*
* | byte 1 | byte 2 | byte 3 | byte 4 |
* +--------+--------+--------+--------+
* | end | start | 0 | 0 |
*/
#define SEGMENT_ADDR_REG0 0x30
#define SEGMENT_ADDR_START(_r) ((((_r) >> 16) & 0xFF) << 23)
#define SEGMENT_ADDR_END(_r) ((((_r) >> 24) & 0xFF) << 23)
/*
* In user mode all data bytes read or written to the chip decode address
* range are transferred to or from the SPI bus. The range is treated as a
* fifo of arbitratry 1, 2, or 4 byte width but each write has to be aligned
* to its size. The address within the multiple 8kB range is ignored when
* sending bytes to the SPI bus.
*
* On the arm architecture, as of Linux version 4.3, memcpy_fromio and
* memcpy_toio on little endian targets use the optimized memcpy routines
* that were designed for well behavied memory storage. These routines
* have a stutter if the source and destination are not both word aligned,
* once with a duplicate access to the source after aligning to the
* destination to a word boundary, and again with a duplicate access to
* the source when the final byte count is not word aligned.
*
* When writing or reading the fifo this stutter discards data or sends
* too much data to the fifo and can not be used by this driver.
*
* While the low level io string routines that implement the insl family do
* the desired accesses and memory increments, the cross architecture io
* macros make them essentially impossible to use on a memory mapped address
* instead of a a token from the call to iomap of an io port.
*
* These fifo routines use readl and friends to a constant io port and update
* the memory buffer pointer and count via explicit code. The final updates
* to len are optimistically suppressed.
*/
static int aspeed_smc_read_from_ahb(void *buf, void __iomem *src, size_t len)
{
size_t offset = 0;
if (IS_ALIGNED((uintptr_t)src, sizeof(uintptr_t)) &&
IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) {
ioread32_rep(src, buf, len >> 2);
offset = len & ~0x3;
len -= offset;
}
ioread8_rep(src, (u8 *)buf + offset, len);
return 0;
}
static int aspeed_smc_write_to_ahb(void __iomem *dst, const void *buf,
size_t len)
{
size_t offset = 0;
if (IS_ALIGNED((uintptr_t)dst, sizeof(uintptr_t)) &&
IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) {
iowrite32_rep(dst, buf, len >> 2);
offset = len & ~0x3;
len -= offset;
}
iowrite8_rep(dst, (const u8 *)buf + offset, len);
return 0;
}
static inline u32 aspeed_smc_chip_write_bit(struct aspeed_smc_chip *chip)
{
return BIT(chip->controller->info->we0 + chip->cs);
}
static void aspeed_smc_chip_check_config(struct aspeed_smc_chip *chip)
{
struct aspeed_smc_controller *controller = chip->controller;
u32 reg;
reg = readl(controller->regs + CONFIG_REG);
if (reg & aspeed_smc_chip_write_bit(chip))
return;
dev_dbg(controller->dev, "config write is not set ! @%p: 0x%08x\n",
controller->regs + CONFIG_REG, reg);
reg |= aspeed_smc_chip_write_bit(chip);
writel(reg, controller->regs + CONFIG_REG);
}
static void aspeed_smc_start_user(struct spi_nor *nor)
{
struct aspeed_smc_chip *chip = nor->priv;
u32 ctl = chip->ctl_val[smc_base];
/*
* When the chip is controlled in user mode, we need write
* access to send the opcodes to it. So check the config.
*/
aspeed_smc_chip_check_config(chip);
ctl |= CONTROL_COMMAND_MODE_USER |
CONTROL_CE_STOP_ACTIVE_CONTROL;
writel(ctl, chip->ctl);
ctl &= ~CONTROL_CE_STOP_ACTIVE_CONTROL;
writel(ctl, chip->ctl);
}
static void aspeed_smc_stop_user(struct spi_nor *nor)
{
struct aspeed_smc_chip *chip = nor->priv;
u32 ctl = chip->ctl_val[smc_read];
u32 ctl2 = ctl | CONTROL_COMMAND_MODE_USER |
CONTROL_CE_STOP_ACTIVE_CONTROL;
writel(ctl2, chip->ctl); /* stop user CE control */
writel(ctl, chip->ctl); /* default to fread or read mode */
}
static int aspeed_smc_prep(struct spi_nor *nor, enum spi_nor_ops ops)
{
struct aspeed_smc_chip *chip = nor->priv;
mutex_lock(&chip->controller->mutex);
return 0;
}
static void aspeed_smc_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
{
struct aspeed_smc_chip *chip = nor->priv;
mutex_unlock(&chip->controller->mutex);
}
static int aspeed_smc_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
struct aspeed_smc_chip *chip = nor->priv;
aspeed_smc_start_user(nor);
aspeed_smc_write_to_ahb(chip->ahb_base, &opcode, 1);
aspeed_smc_read_from_ahb(buf, chip->ahb_base, len);
aspeed_smc_stop_user(nor);
return 0;
}
static int aspeed_smc_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
int len)
{
struct aspeed_smc_chip *chip = nor->priv;
aspeed_smc_start_user(nor);
aspeed_smc_write_to_ahb(chip->ahb_base, &opcode, 1);
aspeed_smc_write_to_ahb(chip->ahb_base, buf, len);
aspeed_smc_stop_user(nor);
return 0;
}
static void aspeed_smc_send_cmd_addr(struct spi_nor *nor, u8 cmd, u32 addr)
{
struct aspeed_smc_chip *chip = nor->priv;
__be32 temp;
u32 cmdaddr;
switch (nor->addr_width) {
default:
WARN_ONCE(1, "Unexpected address width %u, defaulting to 3\n",
nor->addr_width);
/* FALLTHROUGH */
case 3:
cmdaddr = addr & 0xFFFFFF;
cmdaddr |= cmd << 24;
temp = cpu_to_be32(cmdaddr);
aspeed_smc_write_to_ahb(chip->ahb_base, &temp, 4);
break;
case 4:
temp = cpu_to_be32(addr);
aspeed_smc_write_to_ahb(chip->ahb_base, &cmd, 1);
aspeed_smc_write_to_ahb(chip->ahb_base, &temp, 4);
break;
}
}
static ssize_t aspeed_smc_read_user(struct spi_nor *nor, loff_t from,
size_t len, u_char *read_buf)
{
struct aspeed_smc_chip *chip = nor->priv;
int i;
u8 dummy = 0xFF;
aspeed_smc_start_user(nor);
aspeed_smc_send_cmd_addr(nor, nor->read_opcode, from);
for (i = 0; i < chip->nor.read_dummy / 8; i++)
aspeed_smc_write_to_ahb(chip->ahb_base, &dummy, sizeof(dummy));
aspeed_smc_read_from_ahb(read_buf, chip->ahb_base, len);
aspeed_smc_stop_user(nor);
return len;
}
static ssize_t aspeed_smc_write_user(struct spi_nor *nor, loff_t to,
size_t len, const u_char *write_buf)
{
struct aspeed_smc_chip *chip = nor->priv;
aspeed_smc_start_user(nor);
aspeed_smc_send_cmd_addr(nor, nor->program_opcode, to);
aspeed_smc_write_to_ahb(chip->ahb_base, write_buf, len);
aspeed_smc_stop_user(nor);
return len;
}
static int aspeed_smc_unregister(struct aspeed_smc_controller *controller)
{
struct aspeed_smc_chip *chip;
int n;
for (n = 0; n < controller->info->nce; n++) {
chip = controller->chips[n];
if (chip)
mtd_device_unregister(&chip->nor.mtd);
}
return 0;
}
static int aspeed_smc_remove(struct platform_device *dev)
{
return aspeed_smc_unregister(platform_get_drvdata(dev));
}
static const struct of_device_id aspeed_smc_matches[] = {
{ .compatible = "aspeed,ast2400-fmc", .data = &fmc_2400_info },
{ .compatible = "aspeed,ast2400-spi", .data = &spi_2400_info },
{ .compatible = "aspeed,ast2500-fmc", .data = &fmc_2500_info },
{ .compatible = "aspeed,ast2500-spi", .data = &spi_2500_info },
{ }
};
MODULE_DEVICE_TABLE(of, aspeed_smc_matches);
/*
* Each chip has a mapping window defined by a segment address
* register defining a start and an end address on the AHB bus. These
* addresses can be configured to fit the chip size and offer a
* contiguous memory region across chips. For the moment, we only
* check that each chip segment is valid.
*/
static void __iomem *aspeed_smc_chip_base(struct aspeed_smc_chip *chip,
struct resource *res)
{
struct aspeed_smc_controller *controller = chip->controller;
u32 offset = 0;
u32 reg;
if (controller->info->nce > 1) {
reg = readl(controller->regs + SEGMENT_ADDR_REG0 +
chip->cs * 4);
if (SEGMENT_ADDR_START(reg) >= SEGMENT_ADDR_END(reg))
return NULL;
offset = SEGMENT_ADDR_START(reg) - res->start;
}
return controller->ahb_base + offset;
}
static void aspeed_smc_chip_enable_write(struct aspeed_smc_chip *chip)
{
struct aspeed_smc_controller *controller = chip->controller;
u32 reg;
reg = readl(controller->regs + CONFIG_REG);
reg |= aspeed_smc_chip_write_bit(chip);
writel(reg, controller->regs + CONFIG_REG);
}
static void aspeed_smc_chip_set_type(struct aspeed_smc_chip *chip, int type)
{
struct aspeed_smc_controller *controller = chip->controller;
u32 reg;
chip->type = type;
reg = readl(controller->regs + CONFIG_REG);
reg &= ~(3 << (chip->cs * 2));
reg |= chip->type << (chip->cs * 2);
writel(reg, controller->regs + CONFIG_REG);
}
/*
* The AST2500 FMC flash controller should be strapped by hardware, or
* autodetected, but the AST2500 SPI flash needs to be set.
*/
static void aspeed_smc_chip_set_4b(struct aspeed_smc_chip *chip)
{
struct aspeed_smc_controller *controller = chip->controller;
u32 reg;
if (chip->controller->info == &spi_2500_info) {
reg = readl(controller->regs + CE_CONTROL_REG);
reg |= 1 << chip->cs;
writel(reg, controller->regs + CE_CONTROL_REG);
}
}
/*
* The AST2400 SPI flash controller does not have a CE Control
* register. It uses the CE0 control register to set 4Byte mode at the
* controller level.
*/
static void aspeed_smc_chip_set_4b_spi_2400(struct aspeed_smc_chip *chip)
{
chip->ctl_val[smc_base] |= CONTROL_IO_ADDRESS_4B;
chip->ctl_val[smc_read] |= CONTROL_IO_ADDRESS_4B;
}
static int aspeed_smc_chip_setup_init(struct aspeed_smc_chip *chip,
struct resource *res)
{
struct aspeed_smc_controller *controller = chip->controller;
const struct aspeed_smc_info *info = controller->info;
u32 reg, base_reg;
/*
* Always turn on the write enable bit to allow opcodes to be
* sent in user mode.
*/
aspeed_smc_chip_enable_write(chip);
/* The driver only supports SPI type flash */
if (info->hastype)
aspeed_smc_chip_set_type(chip, smc_type_spi);
/*
* Configure chip base address in memory
*/
chip->ahb_base = aspeed_smc_chip_base(chip, res);
if (!chip->ahb_base) {
dev_warn(chip->nor.dev, "CE segment window closed.\n");
return -EINVAL;
}
/*
* Get value of the inherited control register. U-Boot usually
* does some timing calibration on the FMC chip, so it's good
* to keep them. In the future, we should handle calibration
* from Linux.
*/
reg = readl(chip->ctl);
dev_dbg(controller->dev, "control register: %08x\n", reg);
base_reg = reg & CONTROL_KEEP_MASK;
if (base_reg != reg) {
dev_dbg(controller->dev,
"control register changed to: %08x\n",
base_reg);
}
chip->ctl_val[smc_base] = base_reg;
/*
* Retain the prior value of the control register as the
* default if it was normal access mode. Otherwise start with
* the sanitized base value set to read mode.
*/
if ((reg & CONTROL_COMMAND_MODE_MASK) ==
CONTROL_COMMAND_MODE_NORMAL)
chip->ctl_val[smc_read] = reg;
else
chip->ctl_val[smc_read] = chip->ctl_val[smc_base] |
CONTROL_COMMAND_MODE_NORMAL;
dev_dbg(controller->dev, "default control register: %08x\n",
chip->ctl_val[smc_read]);
return 0;
}
static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
{
struct aspeed_smc_controller *controller = chip->controller;
const struct aspeed_smc_info *info = controller->info;
u32 cmd;
if (chip->nor.addr_width == 4 && info->set_4b)
info->set_4b(chip);
/*
* base mode has not been optimized yet. use it for writes.
*/
chip->ctl_val[smc_write] = chip->ctl_val[smc_base] |
chip->nor.program_opcode << CONTROL_COMMAND_SHIFT |
CONTROL_COMMAND_MODE_WRITE;
dev_dbg(controller->dev, "write control register: %08x\n",
chip->ctl_val[smc_write]);
/*
* TODO: Adjust clocks if fast read is supported and interpret
* SPI-NOR flags to adjust controller settings.
*/
switch (chip->nor.flash_read) {
case SPI_NOR_NORMAL:
cmd = CONTROL_COMMAND_MODE_NORMAL;
break;
case SPI_NOR_FAST:
cmd = CONTROL_COMMAND_MODE_FREAD;
break;
default:
dev_err(chip->nor.dev, "unsupported SPI read mode\n");
return -EINVAL;
}
chip->ctl_val[smc_read] |= cmd |
CONTROL_IO_DUMMY_SET(chip->nor.read_dummy / 8);
dev_dbg(controller->dev, "base control register: %08x\n",
chip->ctl_val[smc_read]);
return 0;
}
static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
struct device_node *np, struct resource *r)
{
const struct aspeed_smc_info *info = controller->info;
struct device *dev = controller->dev;
struct device_node *child;
unsigned int cs;
int ret = -ENODEV;
for_each_available_child_of_node(np, child) {
struct aspeed_smc_chip *chip;
struct spi_nor *nor;
struct mtd_info *mtd;
/* This driver does not support NAND or NOR flash devices. */
if (!of_device_is_compatible(child, "jedec,spi-nor"))
continue;
ret = of_property_read_u32(child, "reg", &cs);
if (ret) {
dev_err(dev, "Couldn't not read chip select.\n");
break;
}
if (cs >= info->nce) {
dev_err(dev, "Chip select %d out of range.\n",
cs);
ret = -ERANGE;
break;
}
if (controller->chips[cs]) {
dev_err(dev, "Chip select %d already in use by %s\n",
cs, dev_name(controller->chips[cs]->nor.dev));
ret = -EBUSY;
break;
}
chip = devm_kzalloc(controller->dev, sizeof(*chip), GFP_KERNEL);
if (!chip) {
ret = -ENOMEM;
break;
}
chip->controller = controller;
chip->ctl = controller->regs + info->ctl0 + cs * 4;
chip->cs = cs;
nor = &chip->nor;
mtd = &nor->mtd;
nor->dev = dev;
nor->priv = chip;
spi_nor_set_flash_node(nor, child);
nor->read = aspeed_smc_read_user;
nor->write = aspeed_smc_write_user;
nor->read_reg = aspeed_smc_read_reg;
nor->write_reg = aspeed_smc_write_reg;
nor->prepare = aspeed_smc_prep;
nor->unprepare = aspeed_smc_unprep;
ret = aspeed_smc_chip_setup_init(chip, r);
if (ret)
break;
/*
* TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
* attach when board support is present as determined
* by of property.
*/
ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
if (ret)
break;
ret = aspeed_smc_chip_setup_finish(chip);
if (ret)
break;
ret = mtd_device_register(mtd, NULL, 0);
if (ret)
break;
controller->chips[cs] = chip;
}
if (ret)
aspeed_smc_unregister(controller);
return ret;
}
static int aspeed_smc_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct aspeed_smc_controller *controller;
const struct of_device_id *match;
const struct aspeed_smc_info *info;
struct resource *res;
int ret;
match = of_match_device(aspeed_smc_matches, &pdev->dev);
if (!match || !match->data)
return -ENODEV;
info = match->data;
controller = devm_kzalloc(&pdev->dev, sizeof(*controller) +
info->nce * sizeof(controller->chips[0]), GFP_KERNEL);
if (!controller)
return -ENOMEM;
controller->info = info;
controller->dev = dev;
mutex_init(&controller->mutex);
platform_set_drvdata(pdev, controller);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
controller->regs = devm_ioremap_resource(dev, res);
if (IS_ERR(controller->regs))
return PTR_ERR(controller->regs);
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
controller->ahb_base = devm_ioremap_resource(dev, res);
if (IS_ERR(controller->ahb_base))
return PTR_ERR(controller->ahb_base);
ret = aspeed_smc_setup_flash(controller, np, res);
if (ret)
dev_err(dev, "Aspeed SMC probe failed %d\n", ret);
return ret;
}
static struct platform_driver aspeed_smc_driver = {
.probe = aspeed_smc_probe,
.remove = aspeed_smc_remove,
.driver = {
.name = DEVICE_NAME,
.of_match_table = aspeed_smc_matches,
}
};
module_platform_driver(aspeed_smc_driver);
MODULE_DESCRIPTION("ASPEED Static Memory Controller Driver");
MODULE_AUTHOR("Cedric Le Goater <clg@kaod.org>");
MODULE_LICENSE("GPL v2");
...@@ -526,7 +526,8 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor, ...@@ -526,7 +526,8 @@ static int cqspi_indirect_read_execute(struct spi_nor *nor,
bytes_to_read *= cqspi->fifo_width; bytes_to_read *= cqspi->fifo_width;
bytes_to_read = bytes_to_read > remaining ? bytes_to_read = bytes_to_read > remaining ?
remaining : bytes_to_read; remaining : bytes_to_read;
readsl(ahb_base, rxbuf, DIV_ROUND_UP(bytes_to_read, 4)); ioread32_rep(ahb_base, rxbuf,
DIV_ROUND_UP(bytes_to_read, 4));
rxbuf += bytes_to_read; rxbuf += bytes_to_read;
remaining -= bytes_to_read; remaining -= bytes_to_read;
bytes_to_read = cqspi_get_rd_sram_level(cqspi); bytes_to_read = cqspi_get_rd_sram_level(cqspi);
...@@ -610,7 +611,8 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor, ...@@ -610,7 +611,8 @@ static int cqspi_indirect_write_execute(struct spi_nor *nor,
while (remaining > 0) { while (remaining > 0) {
write_bytes = remaining > page_size ? page_size : remaining; write_bytes = remaining > page_size ? page_size : remaining;
writesl(cqspi->ahb_base, txbuf, DIV_ROUND_UP(write_bytes, 4)); iowrite32_rep(cqspi->ahb_base, txbuf,
DIV_ROUND_UP(write_bytes, 4));
ret = wait_for_completion_timeout(&cqspi->transfer_complete, ret = wait_for_completion_timeout(&cqspi->transfer_complete,
msecs_to_jiffies msecs_to_jiffies
...@@ -891,7 +893,7 @@ static ssize_t cqspi_write(struct spi_nor *nor, loff_t to, ...@@ -891,7 +893,7 @@ static ssize_t cqspi_write(struct spi_nor *nor, loff_t to,
if (ret) if (ret)
return ret; return ret;
return (ret < 0) ? ret : len; return len;
} }
static ssize_t cqspi_read(struct spi_nor *nor, loff_t from, static ssize_t cqspi_read(struct spi_nor *nor, loff_t from,
...@@ -911,7 +913,7 @@ static ssize_t cqspi_read(struct spi_nor *nor, loff_t from, ...@@ -911,7 +913,7 @@ static ssize_t cqspi_read(struct spi_nor *nor, loff_t from,
if (ret) if (ret)
return ret; return ret;
return (ret < 0) ? ret : len; return len;
} }
static int cqspi_erase(struct spi_nor *nor, loff_t offs) static int cqspi_erase(struct spi_nor *nor, loff_t offs)
......
...@@ -193,7 +193,7 @@ ...@@ -193,7 +193,7 @@
#define QUADSPI_LUT_NUM 64 #define QUADSPI_LUT_NUM 64
/* SEQID -- we can have 16 seqids at most. */ /* SEQID -- we can have 16 seqids at most. */
#define SEQID_QUAD_READ 0 #define SEQID_READ 0
#define SEQID_WREN 1 #define SEQID_WREN 1
#define SEQID_WRDI 2 #define SEQID_WRDI 2
#define SEQID_RDSR 3 #define SEQID_RDSR 3
...@@ -373,32 +373,26 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) ...@@ -373,32 +373,26 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
void __iomem *base = q->iobase; void __iomem *base = q->iobase;
int rxfifo = q->devtype_data->rxfifo; int rxfifo = q->devtype_data->rxfifo;
u32 lut_base; u32 lut_base;
u8 cmd, addrlen, dummy;
int i; int i;
struct spi_nor *nor = &q->nor[0];
u8 addrlen = (nor->addr_width == 3) ? ADDR24BIT : ADDR32BIT;
u8 read_op = nor->read_opcode;
u8 read_dm = nor->read_dummy;
fsl_qspi_unlock_lut(q); fsl_qspi_unlock_lut(q);
/* Clear all the LUT table */ /* Clear all the LUT table */
for (i = 0; i < QUADSPI_LUT_NUM; i++) for (i = 0; i < QUADSPI_LUT_NUM; i++)
qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4); qspi_writel(q, 0, base + QUADSPI_LUT_BASE + i * 4);
/* Quad Read */ /* Read */
lut_base = SEQID_QUAD_READ * 4; lut_base = SEQID_READ * 4;
if (q->nor_size <= SZ_16M) {
cmd = SPINOR_OP_READ_1_1_4;
addrlen = ADDR24BIT;
dummy = 8;
} else {
/* use the 4-byte address */
cmd = SPINOR_OP_READ_1_1_4;
addrlen = ADDR32BIT;
dummy = 8;
}
qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen), qspi_writel(q, LUT0(CMD, PAD1, read_op) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
qspi_writel(q, LUT0(DUMMY, PAD1, dummy) | LUT1(FSL_READ, PAD4, rxfifo), qspi_writel(q, LUT0(DUMMY, PAD1, read_dm) |
LUT1(FSL_READ, PAD4, rxfifo),
base + QUADSPI_LUT(lut_base + 1)); base + QUADSPI_LUT(lut_base + 1));
/* Write enable */ /* Write enable */
...@@ -409,16 +403,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) ...@@ -409,16 +403,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
/* Page Program */ /* Page Program */
lut_base = SEQID_PP * 4; lut_base = SEQID_PP * 4;
if (q->nor_size <= SZ_16M) { qspi_writel(q, LUT0(CMD, PAD1, nor->program_opcode) |
cmd = SPINOR_OP_PP; LUT1(ADDR, PAD1, addrlen),
addrlen = ADDR24BIT;
} else {
/* use the 4-byte address */
cmd = SPINOR_OP_PP;
addrlen = ADDR32BIT;
}
qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
qspi_writel(q, LUT0(FSL_WRITE, PAD1, 0), qspi_writel(q, LUT0(FSL_WRITE, PAD1, 0),
base + QUADSPI_LUT(lut_base + 1)); base + QUADSPI_LUT(lut_base + 1));
...@@ -432,10 +418,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q) ...@@ -432,10 +418,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
/* Erase a sector */ /* Erase a sector */
lut_base = SEQID_SE * 4; lut_base = SEQID_SE * 4;
cmd = q->nor[0].erase_opcode; qspi_writel(q, LUT0(CMD, PAD1, nor->erase_opcode) |
addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT; LUT1(ADDR, PAD1, addrlen),
qspi_writel(q, LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
base + QUADSPI_LUT(lut_base)); base + QUADSPI_LUT(lut_base));
/* Erase the whole chip */ /* Erase the whole chip */
...@@ -484,7 +468,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd) ...@@ -484,7 +468,7 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
{ {
switch (cmd) { switch (cmd) {
case SPINOR_OP_READ_1_1_4: case SPINOR_OP_READ_1_1_4:
return SEQID_QUAD_READ; return SEQID_READ;
case SPINOR_OP_WREN: case SPINOR_OP_WREN:
return SEQID_WREN; return SEQID_WREN;
case SPINOR_OP_WRDI: case SPINOR_OP_WRDI:
......
/*
* Intel PCH/PCU SPI flash platform driver.
*
* Copyright (C) 2016, Intel Corporation
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "intel-spi.h"
static int intel_spi_platform_probe(struct platform_device *pdev)
{
struct intel_spi_boardinfo *info;
struct intel_spi *ispi;
struct resource *mem;
info = dev_get_platdata(&pdev->dev);
if (!info)
return -EINVAL;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ispi = intel_spi_probe(&pdev->dev, mem, info);
if (IS_ERR(ispi))
return PTR_ERR(ispi);
platform_set_drvdata(pdev, ispi);
return 0;
}
static int intel_spi_platform_remove(struct platform_device *pdev)
{
struct intel_spi *ispi = platform_get_drvdata(pdev);
return intel_spi_remove(ispi);
}
static struct platform_driver intel_spi_platform_driver = {
.probe = intel_spi_platform_probe,
.remove = intel_spi_platform_remove,
.driver = {
.name = "intel-spi",
},
};
module_platform_driver(intel_spi_platform_driver);
MODULE_DESCRIPTION("Intel PCH/PCU SPI flash platform driver");
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:intel-spi");
/*
* Intel PCH/PCU SPI flash driver.
*
* Copyright (C) 2016, Intel Corporation
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/sizes.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/spi-nor.h>
#include <linux/platform_data/intel-spi.h>
#include "intel-spi.h"
/* Offsets are from @ispi->base */
#define BFPREG 0x00
#define HSFSTS_CTL 0x04
#define HSFSTS_CTL_FSMIE BIT(31)
#define HSFSTS_CTL_FDBC_SHIFT 24
#define HSFSTS_CTL_FDBC_MASK (0x3f << HSFSTS_CTL_FDBC_SHIFT)
#define HSFSTS_CTL_FCYCLE_SHIFT 17
#define HSFSTS_CTL_FCYCLE_MASK (0x0f << HSFSTS_CTL_FCYCLE_SHIFT)
/* HW sequencer opcodes */
#define HSFSTS_CTL_FCYCLE_READ (0x00 << HSFSTS_CTL_FCYCLE_SHIFT)
#define HSFSTS_CTL_FCYCLE_WRITE (0x02 << HSFSTS_CTL_FCYCLE_SHIFT)
#define HSFSTS_CTL_FCYCLE_ERASE (0x03 << HSFSTS_CTL_FCYCLE_SHIFT)
#define HSFSTS_CTL_FCYCLE_ERASE_64K (0x04 << HSFSTS_CTL_FCYCLE_SHIFT)
#define HSFSTS_CTL_FCYCLE_RDID (0x06 << HSFSTS_CTL_FCYCLE_SHIFT)
#define HSFSTS_CTL_FCYCLE_WRSR (0x07 << HSFSTS_CTL_FCYCLE_SHIFT)
#define HSFSTS_CTL_FCYCLE_RDSR (0x08 << HSFSTS_CTL_FCYCLE_SHIFT)
#define HSFSTS_CTL_FGO BIT(16)
#define HSFSTS_CTL_FLOCKDN BIT(15)
#define HSFSTS_CTL_FDV BIT(14)
#define HSFSTS_CTL_SCIP BIT(5)
#define HSFSTS_CTL_AEL BIT(2)
#define HSFSTS_CTL_FCERR BIT(1)
#define HSFSTS_CTL_FDONE BIT(0)
#define FADDR 0x08
#define DLOCK 0x0c
#define FDATA(n) (0x10 + ((n) * 4))
#define FRACC 0x50
#define FREG(n) (0x54 + ((n) * 4))
#define FREG_BASE_MASK 0x3fff
#define FREG_LIMIT_SHIFT 16
#define FREG_LIMIT_MASK (0x03fff << FREG_LIMIT_SHIFT)
/* Offset is from @ispi->pregs */
#define PR(n) ((n) * 4)
#define PR_WPE BIT(31)
#define PR_LIMIT_SHIFT 16
#define PR_LIMIT_MASK (0x3fff << PR_LIMIT_SHIFT)
#define PR_RPE BIT(15)
#define PR_BASE_MASK 0x3fff
/* Last PR is GPR0 */
#define PR_NUM (5 + 1)
/* Offsets are from @ispi->sregs */
#define SSFSTS_CTL 0x00
#define SSFSTS_CTL_FSMIE BIT(23)
#define SSFSTS_CTL_DS BIT(22)
#define SSFSTS_CTL_DBC_SHIFT 16
#define SSFSTS_CTL_SPOP BIT(11)
#define SSFSTS_CTL_ACS BIT(10)
#define SSFSTS_CTL_SCGO BIT(9)
#define SSFSTS_CTL_COP_SHIFT 12
#define SSFSTS_CTL_FRS BIT(7)
#define SSFSTS_CTL_DOFRS BIT(6)
#define SSFSTS_CTL_AEL BIT(4)
#define SSFSTS_CTL_FCERR BIT(3)
#define SSFSTS_CTL_FDONE BIT(2)
#define SSFSTS_CTL_SCIP BIT(0)
#define PREOP_OPTYPE 0x04
#define OPMENU0 0x08
#define OPMENU1 0x0c
/* CPU specifics */
#define BYT_PR 0x74
#define BYT_SSFSTS_CTL 0x90
#define BYT_BCR 0xfc
#define BYT_BCR_WPD BIT(0)
#define BYT_FREG_NUM 5
#define LPT_PR 0x74
#define LPT_SSFSTS_CTL 0x90
#define LPT_FREG_NUM 5
#define BXT_PR 0x84
#define BXT_SSFSTS_CTL 0xa0
#define BXT_FREG_NUM 12
#define INTEL_SPI_TIMEOUT 5000 /* ms */
#define INTEL_SPI_FIFO_SZ 64
/**
* struct intel_spi - Driver private data
* @dev: Device pointer
* @info: Pointer to board specific info
* @nor: SPI NOR layer structure
* @base: Beginning of MMIO space
* @pregs: Start of protection registers
* @sregs: Start of software sequencer registers
* @nregions: Maximum number of regions
* @writeable: Is the chip writeable
* @swseq: Use SW sequencer in register reads/writes
* @erase_64k: 64k erase supported
* @opcodes: Opcodes which are supported. This are programmed by BIOS
* before it locks down the controller.
* @preopcodes: Preopcodes which are supported.
*/
struct intel_spi {
struct device *dev;
const struct intel_spi_boardinfo *info;
struct spi_nor nor;
void __iomem *base;
void __iomem *pregs;
void __iomem *sregs;
size_t nregions;
bool writeable;
bool swseq;
bool erase_64k;
u8 opcodes[8];
u8 preopcodes[2];
};
static bool writeable;
module_param(writeable, bool, 0);
MODULE_PARM_DESC(writeable, "Enable write access to SPI flash chip (default=0)");
static void intel_spi_dump_regs(struct intel_spi *ispi)
{
u32 value;
int i;
dev_dbg(ispi->dev, "BFPREG=0x%08x\n", readl(ispi->base + BFPREG));
value = readl(ispi->base + HSFSTS_CTL);
dev_dbg(ispi->dev, "HSFSTS_CTL=0x%08x\n", value);
if (value & HSFSTS_CTL_FLOCKDN)
dev_dbg(ispi->dev, "-> Locked\n");
dev_dbg(ispi->dev, "FADDR=0x%08x\n", readl(ispi->base + FADDR));
dev_dbg(ispi->dev, "DLOCK=0x%08x\n", readl(ispi->base + DLOCK));
for (i = 0; i < 16; i++)
dev_dbg(ispi->dev, "FDATA(%d)=0x%08x\n",
i, readl(ispi->base + FDATA(i)));
dev_dbg(ispi->dev, "FRACC=0x%08x\n", readl(ispi->base + FRACC));
for (i = 0; i < ispi->nregions; i++)
dev_dbg(ispi->dev, "FREG(%d)=0x%08x\n", i,
readl(ispi->base + FREG(i)));
for (i = 0; i < PR_NUM; i++)
dev_dbg(ispi->dev, "PR(%d)=0x%08x\n", i,
readl(ispi->pregs + PR(i)));
value = readl(ispi->sregs + SSFSTS_CTL);
dev_dbg(ispi->dev, "SSFSTS_CTL=0x%08x\n", value);
dev_dbg(ispi->dev, "PREOP_OPTYPE=0x%08x\n",
readl(ispi->sregs + PREOP_OPTYPE));
dev_dbg(ispi->dev, "OPMENU0=0x%08x\n", readl(ispi->sregs + OPMENU0));
dev_dbg(ispi->dev, "OPMENU1=0x%08x\n", readl(ispi->sregs + OPMENU1));
if (ispi->info->type == INTEL_SPI_BYT)
dev_dbg(ispi->dev, "BCR=0x%08x\n", readl(ispi->base + BYT_BCR));
dev_dbg(ispi->dev, "Protected regions:\n");
for (i = 0; i < PR_NUM; i++) {
u32 base, limit;
value = readl(ispi->pregs + PR(i));
if (!(value & (PR_WPE | PR_RPE)))
continue;
limit = (value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT;
base = value & PR_BASE_MASK;
dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x [%c%c]\n",
i, base << 12, (limit << 12) | 0xfff,
value & PR_WPE ? 'W' : '.',
value & PR_RPE ? 'R' : '.');
}
dev_dbg(ispi->dev, "Flash regions:\n");
for (i = 0; i < ispi->nregions; i++) {
u32 region, base, limit;
region = readl(ispi->base + FREG(i));
base = region & FREG_BASE_MASK;
limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT;
if (base >= limit || (i > 0 && limit == 0))
dev_dbg(ispi->dev, " %02d disabled\n", i);
else
dev_dbg(ispi->dev, " %02d base: 0x%08x limit: 0x%08x\n",
i, base << 12, (limit << 12) | 0xfff);
}
dev_dbg(ispi->dev, "Using %cW sequencer for register access\n",
ispi->swseq ? 'S' : 'H');
}
/* Reads max INTEL_SPI_FIFO_SZ bytes from the device fifo */
static int intel_spi_read_block(struct intel_spi *ispi, void *buf, size_t size)
{
size_t bytes;
int i = 0;
if (size > INTEL_SPI_FIFO_SZ)
return -EINVAL;
while (size > 0) {
bytes = min_t(size_t, size, 4);
memcpy_fromio(buf, ispi->base + FDATA(i), bytes);
size -= bytes;
buf += bytes;
i++;
}
return 0;
}
/* Writes max INTEL_SPI_FIFO_SZ bytes to the device fifo */
static int intel_spi_write_block(struct intel_spi *ispi, const void *buf,
size_t size)
{
size_t bytes;
int i = 0;
if (size > INTEL_SPI_FIFO_SZ)
return -EINVAL;
while (size > 0) {
bytes = min_t(size_t, size, 4);
memcpy_toio(ispi->base + FDATA(i), buf, bytes);
size -= bytes;
buf += bytes;
i++;
}
return 0;
}
static int intel_spi_wait_hw_busy(struct intel_spi *ispi)
{
u32 val;
return readl_poll_timeout(ispi->base + HSFSTS_CTL, val,
!(val & HSFSTS_CTL_SCIP), 0,
INTEL_SPI_TIMEOUT * 1000);
}
static int intel_spi_wait_sw_busy(struct intel_spi *ispi)
{
u32 val;
return readl_poll_timeout(ispi->sregs + SSFSTS_CTL, val,
!(val & SSFSTS_CTL_SCIP), 0,
INTEL_SPI_TIMEOUT * 1000);
}
static int intel_spi_init(struct intel_spi *ispi)
{
u32 opmenu0, opmenu1, val;
int i;
switch (ispi->info->type) {
case INTEL_SPI_BYT:
ispi->sregs = ispi->base + BYT_SSFSTS_CTL;
ispi->pregs = ispi->base + BYT_PR;
ispi->nregions = BYT_FREG_NUM;
if (writeable) {
/* Disable write protection */
val = readl(ispi->base + BYT_BCR);
if (!(val & BYT_BCR_WPD)) {
val |= BYT_BCR_WPD;
writel(val, ispi->base + BYT_BCR);
val = readl(ispi->base + BYT_BCR);
}
ispi->writeable = !!(val & BYT_BCR_WPD);
}
break;
case INTEL_SPI_LPT:
ispi->sregs = ispi->base + LPT_SSFSTS_CTL;
ispi->pregs = ispi->base + LPT_PR;
ispi->nregions = LPT_FREG_NUM;
break;
case INTEL_SPI_BXT:
ispi->sregs = ispi->base + BXT_SSFSTS_CTL;
ispi->pregs = ispi->base + BXT_PR;
ispi->nregions = BXT_FREG_NUM;
ispi->erase_64k = true;
break;
default:
return -EINVAL;
}
/* Disable #SMI generation */
val = readl(ispi->base + HSFSTS_CTL);
val &= ~HSFSTS_CTL_FSMIE;
writel(val, ispi->base + HSFSTS_CTL);
/*
* BIOS programs allowed opcodes and then locks down the register.
* So read back what opcodes it decided to support. That's the set
* we are going to support as well.
*/
opmenu0 = readl(ispi->sregs + OPMENU0);
opmenu1 = readl(ispi->sregs + OPMENU1);
/*
* Some controllers can only do basic operations using hardware
* sequencer. All other operations are supposed to be carried out
* using software sequencer. If we find that BIOS has programmed
* opcodes for the software sequencer we use that over the hardware
* sequencer.
*/
if (opmenu0 && opmenu1) {
for (i = 0; i < ARRAY_SIZE(ispi->opcodes) / 2; i++) {
ispi->opcodes[i] = opmenu0 >> i * 8;
ispi->opcodes[i + 4] = opmenu1 >> i * 8;
}
val = readl(ispi->sregs + PREOP_OPTYPE);
ispi->preopcodes[0] = val;
ispi->preopcodes[1] = val >> 8;
/* Disable #SMI generation from SW sequencer */
val = readl(ispi->sregs + SSFSTS_CTL);
val &= ~SSFSTS_CTL_FSMIE;
writel(val, ispi->sregs + SSFSTS_CTL);
ispi->swseq = true;
}
intel_spi_dump_regs(ispi);
return 0;
}
static int intel_spi_opcode_index(struct intel_spi *ispi, u8 opcode)
{
int i;
for (i = 0; i < ARRAY_SIZE(ispi->opcodes); i++)
if (ispi->opcodes[i] == opcode)
return i;
return -EINVAL;
}
static int intel_spi_hw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf,
int len)
{
u32 val, status;
int ret;
val = readl(ispi->base + HSFSTS_CTL);
val &= ~(HSFSTS_CTL_FCYCLE_MASK | HSFSTS_CTL_FDBC_MASK);
switch (opcode) {
case SPINOR_OP_RDID:
val |= HSFSTS_CTL_FCYCLE_RDID;
break;
case SPINOR_OP_WRSR:
val |= HSFSTS_CTL_FCYCLE_WRSR;
break;
case SPINOR_OP_RDSR:
val |= HSFSTS_CTL_FCYCLE_RDSR;
break;
default:
return -EINVAL;
}
val |= (len - 1) << HSFSTS_CTL_FDBC_SHIFT;
val |= HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
val |= HSFSTS_CTL_FGO;
writel(val, ispi->base + HSFSTS_CTL);
ret = intel_spi_wait_hw_busy(ispi);
if (ret)
return ret;
status = readl(ispi->base + HSFSTS_CTL);
if (status & HSFSTS_CTL_FCERR)
return -EIO;
else if (status & HSFSTS_CTL_AEL)
return -EACCES;
return 0;
}
static int intel_spi_sw_cycle(struct intel_spi *ispi, u8 opcode, u8 *buf,
int len)
{
u32 val, status;
int ret;
ret = intel_spi_opcode_index(ispi, opcode);
if (ret < 0)
return ret;
val = (len << SSFSTS_CTL_DBC_SHIFT) | SSFSTS_CTL_DS;
val |= ret << SSFSTS_CTL_COP_SHIFT;
val |= SSFSTS_CTL_FCERR | SSFSTS_CTL_FDONE;
val |= SSFSTS_CTL_SCGO;
writel(val, ispi->sregs + SSFSTS_CTL);
ret = intel_spi_wait_sw_busy(ispi);
if (ret)
return ret;
status = readl(ispi->base + SSFSTS_CTL);
if (status & SSFSTS_CTL_FCERR)
return -EIO;
else if (status & SSFSTS_CTL_AEL)
return -EACCES;
return 0;
}
static int intel_spi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
struct intel_spi *ispi = nor->priv;
int ret;
/* Address of the first chip */
writel(0, ispi->base + FADDR);
if (ispi->swseq)
ret = intel_spi_sw_cycle(ispi, opcode, buf, len);
else
ret = intel_spi_hw_cycle(ispi, opcode, buf, len);
if (ret)
return ret;
return intel_spi_read_block(ispi, buf, len);
}
static int intel_spi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
struct intel_spi *ispi = nor->priv;
int ret;
/*
* This is handled with atomic operation and preop code in Intel
* controller so skip it here now.
*/
if (opcode == SPINOR_OP_WREN)
return 0;
writel(0, ispi->base + FADDR);
/* Write the value beforehand */
ret = intel_spi_write_block(ispi, buf, len);
if (ret)
return ret;
if (ispi->swseq)
return intel_spi_sw_cycle(ispi, opcode, buf, len);
return intel_spi_hw_cycle(ispi, opcode, buf, len);
}
static ssize_t intel_spi_read(struct spi_nor *nor, loff_t from, size_t len,
u_char *read_buf)
{
struct intel_spi *ispi = nor->priv;
size_t block_size, retlen = 0;
u32 val, status;
ssize_t ret;
switch (nor->read_opcode) {
case SPINOR_OP_READ:
case SPINOR_OP_READ_FAST:
break;
default:
return -EINVAL;
}
while (len > 0) {
block_size = min_t(size_t, len, INTEL_SPI_FIFO_SZ);
writel(from, ispi->base + FADDR);
val = readl(ispi->base + HSFSTS_CTL);
val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT;
val |= HSFSTS_CTL_FCYCLE_READ;
val |= HSFSTS_CTL_FGO;
writel(val, ispi->base + HSFSTS_CTL);
ret = intel_spi_wait_hw_busy(ispi);
if (ret)
return ret;
status = readl(ispi->base + HSFSTS_CTL);
if (status & HSFSTS_CTL_FCERR)
ret = -EIO;
else if (status & HSFSTS_CTL_AEL)
ret = -EACCES;
if (ret < 0) {
dev_err(ispi->dev, "read error: %llx: %#x\n", from,
status);
return ret;
}
ret = intel_spi_read_block(ispi, read_buf, block_size);
if (ret)
return ret;
len -= block_size;
from += block_size;
retlen += block_size;
read_buf += block_size;
}
return retlen;
}
static ssize_t intel_spi_write(struct spi_nor *nor, loff_t to, size_t len,
const u_char *write_buf)
{
struct intel_spi *ispi = nor->priv;
size_t block_size, retlen = 0;
u32 val, status;
ssize_t ret;
while (len > 0) {
block_size = min_t(size_t, len, INTEL_SPI_FIFO_SZ);
writel(to, ispi->base + FADDR);
val = readl(ispi->base + HSFSTS_CTL);
val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
val |= (block_size - 1) << HSFSTS_CTL_FDBC_SHIFT;
val |= HSFSTS_CTL_FCYCLE_WRITE;
/* Write enable */
if (ispi->preopcodes[1] == SPINOR_OP_WREN)
val |= SSFSTS_CTL_SPOP;
val |= SSFSTS_CTL_ACS;
writel(val, ispi->base + HSFSTS_CTL);
ret = intel_spi_write_block(ispi, write_buf, block_size);
if (ret) {
dev_err(ispi->dev, "failed to write block\n");
return ret;
}
/* Start the write now */
val = readl(ispi->base + HSFSTS_CTL);
writel(val | HSFSTS_CTL_FGO, ispi->base + HSFSTS_CTL);
ret = intel_spi_wait_hw_busy(ispi);
if (ret) {
dev_err(ispi->dev, "timeout\n");
return ret;
}
status = readl(ispi->base + HSFSTS_CTL);
if (status & HSFSTS_CTL_FCERR)
ret = -EIO;
else if (status & HSFSTS_CTL_AEL)
ret = -EACCES;
if (ret < 0) {
dev_err(ispi->dev, "write error: %llx: %#x\n", to,
status);
return ret;
}
len -= block_size;
to += block_size;
retlen += block_size;
write_buf += block_size;
}
return retlen;
}
static int intel_spi_erase(struct spi_nor *nor, loff_t offs)
{
size_t erase_size, len = nor->mtd.erasesize;
struct intel_spi *ispi = nor->priv;
u32 val, status, cmd;
int ret;
/* If the hardware can do 64k erase use that when possible */
if (len >= SZ_64K && ispi->erase_64k) {
cmd = HSFSTS_CTL_FCYCLE_ERASE_64K;
erase_size = SZ_64K;
} else {
cmd = HSFSTS_CTL_FCYCLE_ERASE;
erase_size = SZ_4K;
}
while (len > 0) {
writel(offs, ispi->base + FADDR);
val = readl(ispi->base + HSFSTS_CTL);
val &= ~(HSFSTS_CTL_FDBC_MASK | HSFSTS_CTL_FCYCLE_MASK);
val |= HSFSTS_CTL_AEL | HSFSTS_CTL_FCERR | HSFSTS_CTL_FDONE;
val |= cmd;
val |= HSFSTS_CTL_FGO;
writel(val, ispi->base + HSFSTS_CTL);
ret = intel_spi_wait_hw_busy(ispi);
if (ret)
return ret;
status = readl(ispi->base + HSFSTS_CTL);
if (status & HSFSTS_CTL_FCERR)
return -EIO;
else if (status & HSFSTS_CTL_AEL)
return -EACCES;
offs += erase_size;
len -= erase_size;
}
return 0;
}
static bool intel_spi_is_protected(const struct intel_spi *ispi,
unsigned int base, unsigned int limit)
{
int i;
for (i = 0; i < PR_NUM; i++) {
u32 pr_base, pr_limit, pr_value;
pr_value = readl(ispi->pregs + PR(i));
if (!(pr_value & (PR_WPE | PR_RPE)))
continue;
pr_limit = (pr_value & PR_LIMIT_MASK) >> PR_LIMIT_SHIFT;
pr_base = pr_value & PR_BASE_MASK;
if (pr_base >= base && pr_limit <= limit)
return true;
}
return false;
}
/*
* There will be a single partition holding all enabled flash regions. We
* call this "BIOS".
*/
static void intel_spi_fill_partition(struct intel_spi *ispi,
struct mtd_partition *part)
{
u64 end;
int i;
memset(part, 0, sizeof(*part));
/* Start from the mandatory descriptor region */
part->size = 4096;
part->name = "BIOS";
/*
* Now try to find where this partition ends based on the flash
* region registers.
*/
for (i = 1; i < ispi->nregions; i++) {
u32 region, base, limit;
region = readl(ispi->base + FREG(i));
base = region & FREG_BASE_MASK;
limit = (region & FREG_LIMIT_MASK) >> FREG_LIMIT_SHIFT;
if (base >= limit || limit == 0)
continue;
/*
* If any of the regions have protection bits set, make the
* whole partition read-only to be on the safe side.
*/
if (intel_spi_is_protected(ispi, base, limit))
ispi->writeable = 0;
end = (limit << 12) + 4096;
if (end > part->size)
part->size = end;
}
}
struct intel_spi *intel_spi_probe(struct device *dev,
struct resource *mem, const struct intel_spi_boardinfo *info)
{
struct mtd_partition part;
struct intel_spi *ispi;
int ret;
if (!info || !mem)
return ERR_PTR(-EINVAL);
ispi = devm_kzalloc(dev, sizeof(*ispi), GFP_KERNEL);
if (!ispi)
return ERR_PTR(-ENOMEM);
ispi->base = devm_ioremap_resource(dev, mem);
if (IS_ERR(ispi->base))
return ispi->base;
ispi->dev = dev;
ispi->info = info;
ispi->writeable = info->writeable;
ret = intel_spi_init(ispi);
if (ret)
return ERR_PTR(ret);
ispi->nor.dev = ispi->dev;
ispi->nor.priv = ispi;
ispi->nor.read_reg = intel_spi_read_reg;
ispi->nor.write_reg = intel_spi_write_reg;
ispi->nor.read = intel_spi_read;
ispi->nor.write = intel_spi_write;
ispi->nor.erase = intel_spi_erase;
ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL);
if (ret) {
dev_info(dev, "failed to locate the chip\n");
return ERR_PTR(ret);
}
intel_spi_fill_partition(ispi, &part);
/* Prevent writes if not explicitly enabled */
if (!ispi->writeable || !writeable)
ispi->nor.mtd.flags &= ~MTD_WRITEABLE;
ret = mtd_device_parse_register(&ispi->nor.mtd, NULL, NULL, &part, 1);
if (ret)
return ERR_PTR(ret);
return ispi;
}
EXPORT_SYMBOL_GPL(intel_spi_probe);
int intel_spi_remove(struct intel_spi *ispi)
{
return mtd_device_unregister(&ispi->nor.mtd);
}
EXPORT_SYMBOL_GPL(intel_spi_remove);
MODULE_DESCRIPTION("Intel PCH/PCU SPI flash core driver");
MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
MODULE_LICENSE("GPL v2");
/*
* Intel PCH/PCU SPI flash driver.
*
* Copyright (C) 2016, Intel Corporation
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef INTEL_SPI_H
#define INTEL_SPI_H
#include <linux/platform_data/intel-spi.h>
struct intel_spi;
struct resource;
struct intel_spi *intel_spi_probe(struct device *dev,
struct resource *mem, const struct intel_spi_boardinfo *info);
int intel_spi_remove(struct intel_spi *ispi);
#endif /* INTEL_SPI_H */
...@@ -75,6 +75,16 @@ struct flash_info { ...@@ -75,6 +75,16 @@ struct flash_info {
* bit. Must be used with * bit. Must be used with
* SPI_NOR_HAS_LOCK. * SPI_NOR_HAS_LOCK.
*/ */
#define SPI_S3AN BIT(10) /*
* Xilinx Spartan 3AN In-System Flash
* (MFR cannot be used for probing
* because it has the same value as
* ATMEL flashes)
*/
#define SPI_NOR_4B_OPCODES BIT(11) /*
* Use dedicated 4byte address op codes
* to support memory size above 128Mib.
*/
}; };
#define JEDEC_MFR(info) ((info)->id[0]) #define JEDEC_MFR(info) ((info)->id[0])
...@@ -122,7 +132,7 @@ static int read_fsr(struct spi_nor *nor) ...@@ -122,7 +132,7 @@ static int read_fsr(struct spi_nor *nor)
/* /*
* Read configuration register, returning its value in the * Read configuration register, returning its value in the
* location. Return the configuration register value. * location. Return the configuration register value.
* Returns negative if error occured. * Returns negative if error occurred.
*/ */
static int read_cr(struct spi_nor *nor) static int read_cr(struct spi_nor *nor)
{ {
...@@ -188,6 +198,78 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd) ...@@ -188,6 +198,78 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
return mtd->priv; return mtd->priv;
} }
static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
{
size_t i;
for (i = 0; i < size; i++)
if (table[i][0] == opcode)
return table[i][1];
/* No conversion found, keep input op code. */
return opcode;
}
static inline u8 spi_nor_convert_3to4_read(u8 opcode)
{
static const u8 spi_nor_3to4_read[][2] = {
{ SPINOR_OP_READ, SPINOR_OP_READ_4B },
{ SPINOR_OP_READ_FAST, SPINOR_OP_READ_FAST_4B },
{ SPINOR_OP_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B },
{ SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B },
{ SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B },
{ SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B },
};
return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
ARRAY_SIZE(spi_nor_3to4_read));
}
static inline u8 spi_nor_convert_3to4_program(u8 opcode)
{
static const u8 spi_nor_3to4_program[][2] = {
{ SPINOR_OP_PP, SPINOR_OP_PP_4B },
{ SPINOR_OP_PP_1_1_4, SPINOR_OP_PP_1_1_4_4B },
{ SPINOR_OP_PP_1_4_4, SPINOR_OP_PP_1_4_4_4B },
};
return spi_nor_convert_opcode(opcode, spi_nor_3to4_program,
ARRAY_SIZE(spi_nor_3to4_program));
}
static inline u8 spi_nor_convert_3to4_erase(u8 opcode)
{
static const u8 spi_nor_3to4_erase[][2] = {
{ SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B },
{ SPINOR_OP_BE_32K, SPINOR_OP_BE_32K_4B },
{ SPINOR_OP_SE, SPINOR_OP_SE_4B },
};
return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase,
ARRAY_SIZE(spi_nor_3to4_erase));
}
static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
const struct flash_info *info)
{
/* Do some manufacturer fixups first */
switch (JEDEC_MFR(info)) {
case SNOR_MFR_SPANSION:
/* No small sector erase for 4-byte command set */
nor->erase_opcode = SPINOR_OP_SE;
nor->mtd.erasesize = info->sector_size;
break;
default:
break;
}
nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
}
/* Enable/disable 4-byte addressing mode. */ /* Enable/disable 4-byte addressing mode. */
static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
int enable) int enable)
...@@ -217,6 +299,21 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, ...@@ -217,6 +299,21 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1); return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
} }
} }
static int s3an_sr_ready(struct spi_nor *nor)
{
int ret;
u8 val;
ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);
if (ret < 0) {
dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
return ret;
}
return !!(val & XSR_RDY);
}
static inline int spi_nor_sr_ready(struct spi_nor *nor) static inline int spi_nor_sr_ready(struct spi_nor *nor)
{ {
int sr = read_sr(nor); int sr = read_sr(nor);
...@@ -238,7 +335,11 @@ static inline int spi_nor_fsr_ready(struct spi_nor *nor) ...@@ -238,7 +335,11 @@ static inline int spi_nor_fsr_ready(struct spi_nor *nor)
static int spi_nor_ready(struct spi_nor *nor) static int spi_nor_ready(struct spi_nor *nor)
{ {
int sr, fsr; int sr, fsr;
sr = spi_nor_sr_ready(nor);
if (nor->flags & SNOR_F_READY_XSR_RDY)
sr = s3an_sr_ready(nor);
else
sr = spi_nor_sr_ready(nor);
if (sr < 0) if (sr < 0)
return sr; return sr;
fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1; fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
...@@ -319,6 +420,27 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) ...@@ -319,6 +420,27 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
mutex_unlock(&nor->lock); mutex_unlock(&nor->lock);
} }
/*
* This code converts an address to the Default Address Mode, that has non
* power of two page sizes. We must support this mode because it is the default
* mode supported by Xilinx tools, it can access the whole flash area and
* changing over to the Power-of-two mode is irreversible and corrupts the
* original data.
* Addr can safely be unsigned int, the biggest S3AN device is smaller than
* 4 MiB.
*/
static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr)
{
unsigned int offset;
unsigned int page;
offset = addr % nor->page_size;
page = addr / nor->page_size;
page <<= (nor->page_size > 512) ? 10 : 9;
return page | offset;
}
/* /*
* Initiate the erasure of a single sector * Initiate the erasure of a single sector
*/ */
...@@ -327,6 +449,9 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) ...@@ -327,6 +449,9 @@ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
int i; int i;
if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
addr = spi_nor_s3an_addr_convert(nor, addr);
if (nor->erase) if (nor->erase)
return nor->erase(nor, addr); return nor->erase(nor, addr);
...@@ -368,7 +493,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -368,7 +493,7 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
return ret; return ret;
/* whole-chip erase? */ /* whole-chip erase? */
if (len == mtd->size) { if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
unsigned long timeout; unsigned long timeout;
write_enable(nor); write_enable(nor);
...@@ -782,6 +907,19 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) ...@@ -782,6 +907,19 @@ static int spi_nor_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
.addr_width = (_addr_width), \ .addr_width = (_addr_width), \
.flags = (_flags), .flags = (_flags),
#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \
.id = { \
((_jedec_id) >> 16) & 0xff, \
((_jedec_id) >> 8) & 0xff, \
(_jedec_id) & 0xff \
}, \
.id_len = 3, \
.sector_size = (8*_page_size), \
.n_sectors = (_n_sectors), \
.page_size = _page_size, \
.addr_width = 3, \
.flags = SPI_NOR_NO_FR | SPI_S3AN,
/* NOTE: double check command sets and memory organization when you add /* NOTE: double check command sets and memory organization when you add
* more nor chips. This current list focusses on newer chips, which * more nor chips. This current list focusses on newer chips, which
* have been converging on command sets which including JEDEC ID. * have been converging on command sets which including JEDEC ID.
...@@ -821,7 +959,7 @@ static const struct flash_info spi_nor_ids[] = { ...@@ -821,7 +959,7 @@ static const struct flash_info spi_nor_ids[] = {
{ "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) }, { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) },
/* ESMT */ /* ESMT */
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) },
/* Everspin */ /* Everspin */
{ "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
...@@ -832,6 +970,11 @@ static const struct flash_info spi_nor_ids[] = { ...@@ -832,6 +970,11 @@ static const struct flash_info spi_nor_ids[] = {
{ "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) }, { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) },
/* GigaDevice */ /* GigaDevice */
{
"gd25q16", INFO(0xc84015, 0, 64 * 1024, 32,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{ {
"gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
...@@ -1014,6 +1157,13 @@ static const struct flash_info spi_nor_ids[] = { ...@@ -1014,6 +1157,13 @@ static const struct flash_info spi_nor_ids[] = {
{ "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
{ "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) }, { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
/* Xilinx S3AN Internal Flash */
{ "3S50AN", S3AN_INFO(0x1f2200, 64, 264) },
{ "3S200AN", S3AN_INFO(0x1f2400, 256, 264) },
{ "3S400AN", S3AN_INFO(0x1f2400, 256, 264) },
{ "3S700AN", S3AN_INFO(0x1f2500, 512, 264) },
{ "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) },
{ }, { },
}; };
...@@ -1054,7 +1204,12 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -1054,7 +1204,12 @@ static int spi_nor_read(struct mtd_info *mtd, loff_t from, size_t len,
return ret; return ret;
while (len) { while (len) {
ret = nor->read(nor, from, len, buf); loff_t addr = from;
if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
addr = spi_nor_s3an_addr_convert(nor, addr);
ret = nor->read(nor, addr, len, buf);
if (ret == 0) { if (ret == 0) {
/* We shouldn't see 0-length reads */ /* We shouldn't see 0-length reads */
ret = -EIO; ret = -EIO;
...@@ -1175,17 +1330,32 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -1175,17 +1330,32 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
for (i = 0; i < len; ) { for (i = 0; i < len; ) {
ssize_t written; ssize_t written;
loff_t addr = to + i;
/*
* If page_size is a power of two, the offset can be quickly
* calculated with an AND operation. On the other cases we
* need to do a modulus operation (more expensive).
* Power of two numbers have only one bit set and we can use
* the instruction hweight32 to detect if we need to do a
* modulus (do_div()) or not.
*/
if (hweight32(nor->page_size) == 1) {
page_offset = addr & (nor->page_size - 1);
} else {
uint64_t aux = addr;
page_offset = (to + i) & (nor->page_size - 1); page_offset = do_div(aux, nor->page_size);
WARN_ONCE(page_offset, }
"Writing at offset %zu into a NOR page. Writing partial pages may decrease reliability and increase wear of NOR flash.",
page_offset);
/* the size of data remaining on the first page */ /* the size of data remaining on the first page */
page_remain = min_t(size_t, page_remain = min_t(size_t,
nor->page_size - page_offset, len - i); nor->page_size - page_offset, len - i);
if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
addr = spi_nor_s3an_addr_convert(nor, addr);
write_enable(nor); write_enable(nor);
ret = nor->write(nor, to + i, page_remain, buf + i); ret = nor->write(nor, addr, page_remain, buf + i);
if (ret < 0) if (ret < 0)
goto write_err; goto write_err;
written = ret; written = ret;
...@@ -1216,6 +1386,9 @@ static int macronix_quad_enable(struct spi_nor *nor) ...@@ -1216,6 +1386,9 @@ static int macronix_quad_enable(struct spi_nor *nor)
val = read_sr(nor); val = read_sr(nor);
if (val < 0) if (val < 0)
return val; return val;
if (val & SR_QUAD_EN_MX)
return 0;
write_enable(nor); write_enable(nor);
write_sr(nor, val | SR_QUAD_EN_MX); write_sr(nor, val | SR_QUAD_EN_MX);
...@@ -1236,7 +1409,7 @@ static int macronix_quad_enable(struct spi_nor *nor) ...@@ -1236,7 +1409,7 @@ static int macronix_quad_enable(struct spi_nor *nor)
* Write status Register and configuration register with 2 bytes * Write status Register and configuration register with 2 bytes
* The first byte will be written to the status register, while the * The first byte will be written to the status register, while the
* second byte will be written to the configuration register. * second byte will be written to the configuration register.
* Return negative if error occured. * Return negative if error occurred.
*/ */
static int write_sr_cr(struct spi_nor *nor, u16 val) static int write_sr_cr(struct spi_nor *nor, u16 val)
{ {
...@@ -1312,6 +1485,47 @@ static int spi_nor_check(struct spi_nor *nor) ...@@ -1312,6 +1485,47 @@ static int spi_nor_check(struct spi_nor *nor)
return 0; return 0;
} }
static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
{
int ret;
u8 val;
ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);
if (ret < 0) {
dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
return ret;
}
nor->erase_opcode = SPINOR_OP_XSE;
nor->program_opcode = SPINOR_OP_XPP;
nor->read_opcode = SPINOR_OP_READ;
nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
/*
* This flashes have a page size of 264 or 528 bytes (known as
* Default addressing mode). It can be changed to a more standard
* Power of two mode where the page size is 256/512. This comes
* with a price: there is 3% less of space, the data is corrupted
* and the page size cannot be changed back to default addressing
* mode.
*
* The current addressing mode can be read from the XRDSR register
* and should not be changed, because is a destructive operation.
*/
if (val & XSR_PAGESIZE) {
/* Flash in Power of 2 mode */
nor->page_size = (nor->page_size == 264) ? 256 : 512;
nor->mtd.writebufsize = nor->page_size;
nor->mtd.size = 8 * nor->page_size * info->n_sectors;
nor->mtd.erasesize = 8 * nor->page_size;
} else {
/* Flash in Default addressing mode */
nor->flags |= SNOR_F_S3AN_ADDR_DEFAULT;
}
return 0;
}
int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
{ {
const struct flash_info *info = NULL; const struct flash_info *info = NULL;
...@@ -1359,6 +1573,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) ...@@ -1359,6 +1573,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
mutex_init(&nor->lock); mutex_init(&nor->lock);
/*
* Make sure the XSR_RDY flag is set before calling
* spi_nor_wait_till_ready(). Xilinx S3AN share MFR
* with Atmel spi-nor
*/
if (info->flags & SPI_S3AN)
nor->flags |= SNOR_F_READY_XSR_RDY;
/* /*
* Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
* with the software protection bits set * with the software protection bits set
...@@ -1483,27 +1705,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) ...@@ -1483,27 +1705,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
else if (mtd->size > 0x1000000) { else if (mtd->size > 0x1000000) {
/* enable 4-byte addressing if the device exceeds 16MiB */ /* enable 4-byte addressing if the device exceeds 16MiB */
nor->addr_width = 4; nor->addr_width = 4;
if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) { if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
/* Dedicated 4-byte command set */ info->flags & SPI_NOR_4B_OPCODES)
switch (nor->flash_read) { spi_nor_set_4byte_opcodes(nor, info);
case SPI_NOR_QUAD: else
nor->read_opcode = SPINOR_OP_READ4_1_1_4;
break;
case SPI_NOR_DUAL:
nor->read_opcode = SPINOR_OP_READ4_1_1_2;
break;
case SPI_NOR_FAST:
nor->read_opcode = SPINOR_OP_READ4_FAST;
break;
case SPI_NOR_NORMAL:
nor->read_opcode = SPINOR_OP_READ4;
break;
}
nor->program_opcode = SPINOR_OP_PP_4B;
/* No small sector erase for 4-byte command set */
nor->erase_opcode = SPINOR_OP_SE_4B;
mtd->erasesize = info->sector_size;
} else
set_4byte(nor, info, 1); set_4byte(nor, info, 1);
} else { } else {
nor->addr_width = 3; nor->addr_width = 3;
...@@ -1517,6 +1722,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) ...@@ -1517,6 +1722,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
nor->read_dummy = spi_nor_read_dummy_cycles(nor); nor->read_dummy = spi_nor_read_dummy_cycles(nor);
if (info->flags & SPI_S3AN) {
ret = s3an_nor_scan(info, nor);
if (ret)
return ret;
}
dev_info(dev, "%s (%lld Kbytes)\n", info->name, dev_info(dev, "%s (%lld Kbytes)\n", info->name,
(long long)mtd->size >> 10); (long long)mtd->size >> 10);
......
...@@ -371,7 +371,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width, ...@@ -371,7 +371,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width,
/* default mode, does not need flex_cmd */ /* default mode, does not need flex_cmd */
flex_mode = 0; flex_mode = 0;
else else
command = SPINOR_OP_READ4_FAST; command = SPINOR_OP_READ_FAST_4B;
break; break;
case SPI_NBITS_DUAL: case SPI_NBITS_DUAL:
bpc = 0x00000001; bpc = 0x00000001;
...@@ -384,7 +384,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width, ...@@ -384,7 +384,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width,
} else { } else {
command = SPINOR_OP_READ_1_1_2; command = SPINOR_OP_READ_1_1_2;
if (spans_4byte) if (spans_4byte)
command = SPINOR_OP_READ4_1_1_2; command = SPINOR_OP_READ_1_1_2_4B;
} }
break; break;
case SPI_NBITS_QUAD: case SPI_NBITS_QUAD:
...@@ -399,7 +399,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width, ...@@ -399,7 +399,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi, int width,
} else { } else {
command = SPINOR_OP_READ_1_1_4; command = SPINOR_OP_READ_1_1_4;
if (spans_4byte) if (spans_4byte)
command = SPINOR_OP_READ4_1_1_4; command = SPINOR_OP_READ_1_1_4_4B;
} }
break; break;
default: default:
......
...@@ -593,9 +593,6 @@ struct bcma_sflash { ...@@ -593,9 +593,6 @@ struct bcma_sflash {
u32 blocksize; u32 blocksize;
u16 numblocks; u16 numblocks;
u32 size; u32 size;
struct mtd_info *mtd;
void *priv;
}; };
#endif #endif
......
...@@ -733,8 +733,12 @@ struct fsl_ifc_nand { ...@@ -733,8 +733,12 @@ struct fsl_ifc_nand {
__be32 nand_erattr1; __be32 nand_erattr1;
u32 res19[0x10]; u32 res19[0x10];
__be32 nand_fsr; __be32 nand_fsr;
u32 res20[0x3]; u32 res20;
__be32 nand_eccstat[6]; /* The V1 nand_eccstat is actually 4 words that overlaps the
* V2 nand_eccstat.
*/
__be32 v1_nand_eccstat[2];
__be32 v2_nand_eccstat[6];
u32 res21[0x1c]; u32 res21[0x1c];
__be32 nanndcr; __be32 nanndcr;
u32 res22[0x2]; u32 res22[0x2];
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#ifndef LPC_ICH_H #ifndef LPC_ICH_H
#define LPC_ICH_H #define LPC_ICH_H
#include <linux/platform_data/intel-spi.h>
/* GPIO resources */ /* GPIO resources */
#define ICH_RES_GPIO 0 #define ICH_RES_GPIO 0
#define ICH_RES_GPE0 1 #define ICH_RES_GPE0 1
...@@ -40,6 +42,7 @@ struct lpc_ich_info { ...@@ -40,6 +42,7 @@ struct lpc_ich_info {
char name[32]; char name[32];
unsigned int iTCO_version; unsigned int iTCO_version;
unsigned int gpio_version; unsigned int gpio_version;
enum intel_spi_type spi_type;
u8 use_gpio; u8 use_gpio;
}; };
......
/*
* incude/mtd/fsmc.h
*
* ST Microelectronics
* Flexible Static Memory Controller (FSMC)
* platform data interface and header file
*
* Copyright © 2010 ST Microelectronics
* Vipin Kumar <vipin.kumar@st.com>
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#ifndef __MTD_FSMC_H
#define __MTD_FSMC_H
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/mtd/physmap.h>
#include <linux/types.h>
#include <linux/mtd/partitions.h>
#include <asm/param.h>
#define FSMC_NAND_BW8 1
#define FSMC_NAND_BW16 2
#define FSMC_MAX_NOR_BANKS 4
#define FSMC_MAX_NAND_BANKS 4
#define FSMC_FLASH_WIDTH8 1
#define FSMC_FLASH_WIDTH16 2
/* fsmc controller registers for NOR flash */
#define CTRL 0x0
/* ctrl register definitions */
#define BANK_ENABLE (1 << 0)
#define MUXED (1 << 1)
#define NOR_DEV (2 << 2)
#define WIDTH_8 (0 << 4)
#define WIDTH_16 (1 << 4)
#define RSTPWRDWN (1 << 6)
#define WPROT (1 << 7)
#define WRT_ENABLE (1 << 12)
#define WAIT_ENB (1 << 13)
#define CTRL_TIM 0x4
/* ctrl_tim register definitions */
#define FSMC_NOR_BANK_SZ 0x8
#define FSMC_NOR_REG_SIZE 0x40
#define FSMC_NOR_REG(base, bank, reg) (base + \
FSMC_NOR_BANK_SZ * (bank) + \
reg)
/* fsmc controller registers for NAND flash */
#define PC 0x00
/* pc register definitions */
#define FSMC_RESET (1 << 0)
#define FSMC_WAITON (1 << 1)
#define FSMC_ENABLE (1 << 2)
#define FSMC_DEVTYPE_NAND (1 << 3)
#define FSMC_DEVWID_8 (0 << 4)
#define FSMC_DEVWID_16 (1 << 4)
#define FSMC_ECCEN (1 << 6)
#define FSMC_ECCPLEN_512 (0 << 7)
#define FSMC_ECCPLEN_256 (1 << 7)
#define FSMC_TCLR_1 (1)
#define FSMC_TCLR_SHIFT (9)
#define FSMC_TCLR_MASK (0xF)
#define FSMC_TAR_1 (1)
#define FSMC_TAR_SHIFT (13)
#define FSMC_TAR_MASK (0xF)
#define STS 0x04
/* sts register definitions */
#define FSMC_CODE_RDY (1 << 15)
#define COMM 0x08
/* comm register definitions */
#define FSMC_TSET_0 0
#define FSMC_TSET_SHIFT 0
#define FSMC_TSET_MASK 0xFF
#define FSMC_TWAIT_6 6
#define FSMC_TWAIT_SHIFT 8
#define FSMC_TWAIT_MASK 0xFF
#define FSMC_THOLD_4 4
#define FSMC_THOLD_SHIFT 16
#define FSMC_THOLD_MASK 0xFF
#define FSMC_THIZ_1 1
#define FSMC_THIZ_SHIFT 24
#define FSMC_THIZ_MASK 0xFF
#define ATTRIB 0x0C
#define IOATA 0x10
#define ECC1 0x14
#define ECC2 0x18
#define ECC3 0x1C
#define FSMC_NAND_BANK_SZ 0x20
#define FSMC_NAND_REG(base, bank, reg) (base + FSMC_NOR_REG_SIZE + \
(FSMC_NAND_BANK_SZ * (bank)) + \
reg)
#define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
struct fsmc_nand_timings {
uint8_t tclr;
uint8_t tar;
uint8_t thiz;
uint8_t thold;
uint8_t twait;
uint8_t tset;
};
enum access_mode {
USE_DMA_ACCESS = 1,
USE_WORD_ACCESS,
};
/**
* fsmc_nand_platform_data - platform specific NAND controller config
* @nand_timings: timing setup for the physical NAND interface
* @partitions: partition table for the platform, use a default fallback
* if this is NULL
* @nr_partitions: the number of partitions in the previous entry
* @options: different options for the driver
* @width: bus width
* @bank: default bank
* @select_bank: callback to select a certain bank, this is
* platform-specific. If the controller only supports one bank
* this may be set to NULL
*/
struct fsmc_nand_platform_data {
struct fsmc_nand_timings *nand_timings;
struct mtd_partition *partitions;
unsigned int nr_partitions;
unsigned int options;
unsigned int width;
unsigned int bank;
enum access_mode mode;
void (*select_bank)(uint32_t bank, uint32_t busw);
/* priv structures for dma accesses */
void *read_dma_priv;
void *write_dma_priv;
};
extern int __init fsmc_nor_init(struct platform_device *pdev,
unsigned long base, uint32_t bank, uint32_t width);
extern void __init fsmc_init_board_info(struct platform_device *pdev,
struct mtd_partition *partitions, unsigned int nr_partitions,
unsigned int width);
#endif /* __MTD_FSMC_H */
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/of.h>
#include <mtd/mtd-abi.h> #include <mtd/mtd-abi.h>
...@@ -322,6 +323,7 @@ struct mtd_info { ...@@ -322,6 +323,7 @@ struct mtd_info {
int (*_block_isreserved) (struct mtd_info *mtd, loff_t ofs); int (*_block_isreserved) (struct mtd_info *mtd, loff_t ofs);
int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs); int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs); int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
int (*_max_bad_blocks) (struct mtd_info *mtd, loff_t ofs, size_t len);
int (*_suspend) (struct mtd_info *mtd); int (*_suspend) (struct mtd_info *mtd);
void (*_resume) (struct mtd_info *mtd); void (*_resume) (struct mtd_info *mtd);
void (*_reboot) (struct mtd_info *mtd); void (*_reboot) (struct mtd_info *mtd);
...@@ -385,6 +387,8 @@ static inline void mtd_set_of_node(struct mtd_info *mtd, ...@@ -385,6 +387,8 @@ static inline void mtd_set_of_node(struct mtd_info *mtd,
struct device_node *np) struct device_node *np)
{ {
mtd->dev.of_node = np; mtd->dev.of_node = np;
if (!mtd->name)
of_property_read_string(np, "label", &mtd->name);
} }
static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd) static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
...@@ -397,6 +401,18 @@ static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops) ...@@ -397,6 +401,18 @@ static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops)
return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize; return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
} }
static inline int mtd_max_bad_blocks(struct mtd_info *mtd,
loff_t ofs, size_t len)
{
if (!mtd->_max_bad_blocks)
return -ENOTSUPP;
if (mtd->size < (len + ofs) || ofs < 0)
return -EINVAL;
return mtd->_max_bad_blocks(mtd, ofs, len);
}
int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit, int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
struct mtd_pairing_info *info); struct mtd_pairing_info *info);
int mtd_pairing_info_to_wunit(struct mtd_info *mtd, int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
......
...@@ -615,7 +615,7 @@ struct nand_buffers { ...@@ -615,7 +615,7 @@ struct nand_buffers {
* @tALS_min: ALE setup time * @tALS_min: ALE setup time
* @tAR_min: ALE to RE# delay * @tAR_min: ALE to RE# delay
* @tCEA_max: CE# access time * @tCEA_max: CE# access time
* @tCEH_min: * @tCEH_min: CE# high hold time
* @tCH_min: CE# hold time * @tCH_min: CE# hold time
* @tCHZ_max: CE# high to output hi-Z * @tCHZ_max: CE# high to output hi-Z
* @tCLH_min: CLE hold time * @tCLH_min: CLE hold time
...@@ -801,6 +801,10 @@ nand_get_sdr_timings(const struct nand_data_interface *conf) ...@@ -801,6 +801,10 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)
* supported, 0 otherwise. * supported, 0 otherwise.
* @jedec_params: [INTERN] holds the JEDEC parameter page when JEDEC is * @jedec_params: [INTERN] holds the JEDEC parameter page when JEDEC is
* supported, 0 otherwise. * supported, 0 otherwise.
* @max_bb_per_die: [INTERN] the max number of bad blocks each die of a
* this nand device will encounter their life times.
* @blocks_per_die: [INTERN] The number of PEBs in a die
* @data_interface: [INTERN] NAND interface timing information
* @read_retries: [INTERN] the number of read retry modes supported * @read_retries: [INTERN] the number of read retry modes supported
* @onfi_set_features: [REPLACEABLE] set the features for ONFI nand * @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
* @onfi_get_features: [REPLACEABLE] get the features for ONFI nand * @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
...@@ -883,6 +887,8 @@ struct nand_chip { ...@@ -883,6 +887,8 @@ struct nand_chip {
struct nand_onfi_params onfi_params; struct nand_onfi_params onfi_params;
struct nand_jedec_params jedec_params; struct nand_jedec_params jedec_params;
}; };
u16 max_bb_per_die;
u32 blocks_per_die;
struct nand_data_interface *data_interface; struct nand_data_interface *data_interface;
...@@ -958,6 +964,7 @@ static inline void nand_set_controller_data(struct nand_chip *chip, void *priv) ...@@ -958,6 +964,7 @@ static inline void nand_set_controller_data(struct nand_chip *chip, void *priv)
#define NAND_MFR_SANDISK 0x45 #define NAND_MFR_SANDISK 0x45
#define NAND_MFR_INTEL 0x89 #define NAND_MFR_INTEL 0x89
#define NAND_MFR_ATO 0x9b #define NAND_MFR_ATO 0x9b
#define NAND_MFR_WINBOND 0xef
/* The maximum expected count of bytes in the NAND ID sequence */ /* The maximum expected count of bytes in the NAND ID sequence */
#define NAND_MAX_ID_LEN 8 #define NAND_MAX_ID_LEN 8
......
...@@ -41,6 +41,7 @@ struct mtd_partition { ...@@ -41,6 +41,7 @@ struct mtd_partition {
uint64_t size; /* partition size */ uint64_t size; /* partition size */
uint64_t offset; /* offset within the master MTD space */ uint64_t offset; /* offset within the master MTD space */
uint32_t mask_flags; /* master MTD flags to mask out for this partition */ uint32_t mask_flags; /* master MTD flags to mask out for this partition */
struct device_node *of_node;
}; };
#define MTDPART_OFS_RETAIN (-3) #define MTDPART_OFS_RETAIN (-3)
......
...@@ -43,9 +43,13 @@ ...@@ -43,9 +43,13 @@
#define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */ #define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
#define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */ #define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
#define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */ #define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */ #define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */
#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ #define SPINOR_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */
#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Output SPI) */
#define SPINOR_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */ #define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
#define SPINOR_OP_PP_1_1_4 0x32 /* Quad page program */
#define SPINOR_OP_PP_1_4_4 0x38 /* Quad page program */
#define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */ #define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */
#define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ #define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
#define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */ #define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */
...@@ -56,11 +60,17 @@ ...@@ -56,11 +60,17 @@
#define SPINOR_OP_RDFSR 0x70 /* Read flag status register */ #define SPINOR_OP_RDFSR 0x70 /* Read flag status register */
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */ #define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */ #define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */
#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */ #define SPINOR_OP_READ_1_1_2_4B 0x3c /* Read data bytes (Dual Output SPI) */
#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */ #define SPINOR_OP_READ_1_2_2_4B 0xbc /* Read data bytes (Dual I/O SPI) */
#define SPINOR_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */
#define SPINOR_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */
#define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ #define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
#define SPINOR_OP_PP_1_1_4_4B 0x34 /* Quad page program */
#define SPINOR_OP_PP_1_4_4_4B 0x3e /* Quad page program */
#define SPINOR_OP_BE_4K_4B 0x21 /* Erase 4KiB block */
#define SPINOR_OP_BE_32K_4B 0x5c /* Erase 32KiB block */
#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */
/* Used for SST flashes only. */ /* Used for SST flashes only. */
...@@ -68,6 +78,15 @@ ...@@ -68,6 +78,15 @@
#define SPINOR_OP_WRDI 0x04 /* Write disable */ #define SPINOR_OP_WRDI 0x04 /* Write disable */
#define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */ #define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */
/* Used for S3AN flashes only */
#define SPINOR_OP_XSE 0x50 /* Sector erase */
#define SPINOR_OP_XPP 0x82 /* Page program */
#define SPINOR_OP_XRDSR 0xd7 /* Read status register */
#define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */
#define XSR_RDY BIT(7) /* Ready */
/* Used for Macronix and Winbond flashes. */ /* Used for Macronix and Winbond flashes. */
#define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
#define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
...@@ -119,6 +138,9 @@ enum spi_nor_ops { ...@@ -119,6 +138,9 @@ enum spi_nor_ops {
enum spi_nor_option_flags { enum spi_nor_option_flags {
SNOR_F_USE_FSR = BIT(0), SNOR_F_USE_FSR = BIT(0),
SNOR_F_HAS_SR_TB = BIT(1), SNOR_F_HAS_SR_TB = BIT(1),
SNOR_F_NO_OP_CHIP_ERASE = BIT(2),
SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
SNOR_F_READY_XSR_RDY = BIT(4),
}; };
/** /**
......
/*
* Intel PCH/PCU SPI flash driver.
*
* Copyright (C) 2016, Intel Corporation
* Author: Mika Westerberg <mika.westerberg@linux.intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef INTEL_SPI_PDATA_H
#define INTEL_SPI_PDATA_H
enum intel_spi_type {
INTEL_SPI_BYT = 1,
INTEL_SPI_LPT,
INTEL_SPI_BXT,
};
/**
* struct intel_spi_boardinfo - Board specific data for Intel SPI driver
* @type: Type which this controller is compatible with
* @writeable: The chip is writeable
*/
struct intel_spi_boardinfo {
enum intel_spi_type type;
bool writeable;
};
#endif /* INTEL_SPI_PDATA_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment