summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2015-09-01 09:43:54 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2015-09-01 09:43:54 +0200
commit60fc3e99b52e7ac3282e80ed4124a9a6d1276b23 (patch)
tree85eff24b87bbf4da2d584b7f58a54a2e946595ff
parent69dccb494a0ee5fececc67f2b7afa7b79368a991 (diff)
parent746a5530bee37c8d2ffc75609447641b3f3cb55b (diff)
downloadbarebox-60fc3e99b52e7ac3282e80ed4124a9a6d1276b23.tar.gz
barebox-60fc3e99b52e7ac3282e80ed4124a9a6d1276b23.tar.xz
Merge branch 'for-next/mtd'
-rw-r--r--Documentation/devicetree/bindings/mtd/m25p80.rst10
-rw-r--r--drivers/mtd/devices/m25p80.c6
-rw-r--r--drivers/mtd/spi-nor/cadence-quadspi.c2
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c81
-rw-r--r--drivers/spi/imx_spi.c124
-rw-r--r--include/linux/mtd/spi-nor.h4
-rw-r--r--include/spi/imx-spi.h4
7 files changed, 138 insertions, 93 deletions
diff --git a/Documentation/devicetree/bindings/mtd/m25p80.rst b/Documentation/devicetree/bindings/mtd/m25p80.rst
new file mode 100644
index 0000000000..d7c8914ec5
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/m25p80.rst
@@ -0,0 +1,10 @@
+MTD SPI driver for ST M25Pxx (and similar) serial flash chips
+=============================================================
+
+Additionally to the Linux bindings in ``dts/Bindings/mtd/m25p80.txt``
+the barebox driver has the following optional properties:
+
+- use-large-blocks : Use large blocks rather than the 4K blocks some devices
+ support. 4K erase blocks do not work with UBIFS which needs
+ a minimum erase block size of 15360 bytes. Also bigger sectors
+ are faster to erase.
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 794c9dbd82..d627690080 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -235,6 +235,7 @@ static int m25p_probe(struct device_d *dev)
enum read_mode mode = SPI_NOR_NORMAL;
const char *flash_name = NULL;
int device_id;
+ bool use_large_blocks;
int ret;
data = dev->platform_data;
@@ -272,7 +273,10 @@ static int m25p_probe(struct device_d *dev)
else
flash_name = NULL; /* auto-detect */
- ret = spi_nor_scan(nor, flash_name, mode);
+ use_large_blocks = of_property_read_bool(dev->device_node,
+ "use-large-blocks");
+
+ ret = spi_nor_scan(nor, flash_name, mode, use_large_blocks);
if (ret)
return ret;
diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c
index dce29ca0ea..ff7bb7a5d8 100644
--- a/drivers/mtd/spi-nor/cadence-quadspi.c
+++ b/drivers/mtd/spi-nor/cadence-quadspi.c
@@ -1078,7 +1078,7 @@ static int cqspi_setup_flash(struct device_d *dev,
nor->write = cqspi_write;
nor->erase = cqspi_erase;
- ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
+ ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD, false);
if (ret)
goto probe_failed;
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index 9435424085..b357e5adb4 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -370,85 +370,27 @@ erase_err:
static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
- struct spi_nor *nor = mtd_to_spi_nor(mtd);
- uint32_t offset = ofs;
- uint8_t status_old, status_new;
- int ret = 0;
-
- ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
- if (ret)
- return ret;
-
- status_old = read_sr(nor);
-
- if (offset < mtd->size - (mtd->size / 2))
- status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0;
- else if (offset < mtd->size - (mtd->size / 4))
- status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
- else if (offset < mtd->size - (mtd->size / 8))
- status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
- else if (offset < mtd->size - (mtd->size / 16))
- status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2;
- else if (offset < mtd->size - (mtd->size / 32))
- status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
- else if (offset < mtd->size - (mtd->size / 64))
- status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1;
- else
- status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0;
-
- /* Only modify protection if it will not unlock other areas */
- if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) >
- (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
- write_enable(nor);
- ret = write_sr(nor, status_new);
- if (ret)
- goto err;
- }
-
-err:
- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
- return ret;
+ return 0;
}
static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
- uint32_t offset = ofs;
- uint8_t status_old, status_new;
- int ret = 0;
+ uint8_t status;
+ int ret;
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);
if (ret)
return ret;
- status_old = read_sr(nor);
-
- if (offset+len > mtd->size - (mtd->size / 64))
- status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0);
- else if (offset+len > mtd->size - (mtd->size / 32))
- status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0;
- else if (offset+len > mtd->size - (mtd->size / 16))
- status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1;
- else if (offset+len > mtd->size - (mtd->size / 8))
- status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
- else if (offset+len > mtd->size - (mtd->size / 4))
- status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2;
- else if (offset+len > mtd->size - (mtd->size / 2))
- status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
- else
- status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
+ status = read_sr(nor);
+ status &= ~(SR_BP2 | SR_BP1 | SR_BP0);
+ write_enable(nor);
- /* Only modify protection if it will not lock other areas */
- if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) <
- (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
- write_enable(nor);
- ret = write_sr(nor, status_new);
- if (ret)
- goto err;
- }
+ ret = write_sr(nor, status);
-err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
+
return ret;
}
@@ -928,7 +870,8 @@ static int spi_nor_check(struct spi_nor *nor)
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,
+ bool use_large_blocks)
{
const struct spi_device_id *id = NULL;
struct flash_info *info;
@@ -1017,10 +960,10 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
/* prefer "small sector" erase if possible */
- if (info->flags & SECT_4K) {
+ if (info->flags & SECT_4K && !use_large_blocks) {
nor->erase_opcode = SPINOR_OP_BE_4K;
mtd->erasesize = 4096;
- } else if (info->flags & SECT_4K_PMC) {
+ } else if (info->flags & SECT_4K_PMC && !use_large_blocks) {
nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
mtd->erasesize = 4096;
} else
diff --git a/drivers/spi/imx_spi.c b/drivers/spi/imx_spi.c
index 6805d22a75..80dfc7819a 100644
--- a/drivers/spi/imx_spi.c
+++ b/drivers/spi/imx_spi.c
@@ -39,11 +39,18 @@ struct imx_spi {
struct clk *clk;
unsigned int (*xchg_single)(struct imx_spi *imx, u32 data);
+ void (*do_transfer)(struct spi_device *spi);
void (*chipselect)(struct spi_device *spi, int active);
+
+ const void *tx_buf;
+ void *rx_buf;
+ int xfer_len;
+ int bits_per_word;
};
struct spi_imx_devtype_data {
unsigned int (*xchg_single)(struct imx_spi *imx, u32 data);
+ void (*do_transfer)(struct spi_device *spi);
void (*chipselect)(struct spi_device *spi, int active);
void (*init)(struct imx_spi *imx);
};
@@ -230,14 +237,8 @@ static unsigned int cspi_2_3_xchg_single(struct imx_spi *imx, unsigned int data)
{
void __iomem *base = imx->regs;
- unsigned int cfg_reg = readl(base + CSPI_2_3_CTRL);
-
writel(data, base + CSPI_2_3_TXDATA);
- cfg_reg |= CSPI_2_3_CTRL_XCH;
-
- writel(cfg_reg, base + CSPI_2_3_CTRL);
-
while (!(readl(base + CSPI_2_3_STAT) & CSPI_2_3_STAT_RR));
return readl(base + CSPI_2_3_RXDATA);
@@ -306,6 +307,8 @@ static void cspi_2_3_chipselect(struct spi_device *spi, int is_active)
ctrl |= (spi->bits_per_word - 1) << CSPI_2_3_CTRL_BL_OFFSET;
+ ctrl |= CSPI_2_3_CTRL_SMC;
+
cfg |= CSPI_2_3_CONFIG_SBBCTRL(cs);
if (spi->mode & SPI_CPHA)
@@ -336,38 +339,39 @@ static u32 imx_xchg_single(struct spi_device *spi, u32 tx_val)
return imx_spi_maybe_reverse_bits(spi, rx_val);
}
-static void imx_spi_do_transfer(struct spi_device *spi, struct spi_transfer *t)
+static void imx_spi_do_transfer(struct spi_device *spi)
{
+ struct imx_spi *imx = container_of(spi->master, struct imx_spi, master);
unsigned i;
- if (spi->bits_per_word <= 8) {
- const u8 *tx_buf = t->tx_buf;
- u8 *rx_buf = t->rx_buf;
+ if (imx->bits_per_word <= 8) {
+ const u8 *tx_buf = imx->tx_buf;
+ u8 *rx_buf = imx->rx_buf;
u8 rx_val;
- for (i = 0; i < t->len; i++) {
+ for (i = 0; i < imx->xfer_len; i++) {
rx_val = imx_xchg_single(spi, tx_buf ? tx_buf[i] : 0);
if (rx_buf)
rx_buf[i] = rx_val;
}
- } else if (spi->bits_per_word <= 16) {
- const u16 *tx_buf = t->tx_buf;
- u16 *rx_buf = t->rx_buf;
+ } else if (imx->bits_per_word <= 16) {
+ const u16 *tx_buf = imx->tx_buf;
+ u16 *rx_buf = imx->rx_buf;
u16 rx_val;
- for (i = 0; i < t->len >> 1; i++) {
+ for (i = 0; i < imx->xfer_len >> 1; i++) {
rx_val = imx_xchg_single(spi, tx_buf ? tx_buf[i] : 0);
if (rx_buf)
rx_buf[i] = rx_val;
}
- } else if (spi->bits_per_word <= 32) {
- const u32 *tx_buf = t->tx_buf;
- u32 *rx_buf = t->rx_buf;
+ } else if (imx->bits_per_word <= 32) {
+ const u32 *tx_buf = imx->tx_buf;
+ u32 *rx_buf = imx->rx_buf;
u32 rx_val;
- for (i = 0; i < t->len >> 2; i++) {
+ for (i = 0; i < imx->xfer_len >> 2; i++) {
rx_val = imx_xchg_single(spi, tx_buf ? tx_buf[i] : 0);
if (rx_buf)
@@ -376,6 +380,77 @@ static void imx_spi_do_transfer(struct spi_device *spi, struct spi_transfer *t)
}
}
+static int cspi_2_3_xchg_burst(struct spi_device *spi)
+{
+ struct imx_spi *imx = container_of(spi->master, struct imx_spi, master);
+ int now, txlen, rxlen;
+ u32 ctrl;
+ void __iomem *base = imx->regs;
+
+ now = min(imx->xfer_len, 512);
+ now >>= 2;
+
+ if (!now)
+ return 0;
+
+ txlen = rxlen = now;
+
+ ctrl = readl(base + CSPI_2_3_CTRL);
+ ctrl &= ~(0xfff << CSPI_2_3_CTRL_BL_OFFSET);
+ ctrl |= ((txlen * 32) - 1) << CSPI_2_3_CTRL_BL_OFFSET;
+ ctrl |= 1 << 3;
+ writel(ctrl, base + CSPI_2_3_CTRL);
+
+ while (txlen || rxlen) {
+ u32 status = readl(base + CSPI_2_3_STAT);
+
+ if (txlen && !(status & CSPI_2_3_STAT_TF)) {
+ if (imx->tx_buf) {
+ u32 data = swab32(*(u32 *)imx->tx_buf);
+ writel(data, base + CSPI_2_3_TXDATA);
+ imx->tx_buf += sizeof(u32);
+ } else {
+ writel(0, base + CSPI_2_3_TXDATA);
+ }
+ txlen--;
+ }
+
+ if (rxlen && (status & CSPI_2_3_STAT_RR)) {
+ u32 data = readl(base + CSPI_2_3_RXDATA);
+
+ if (imx->rx_buf) {
+ *(u32 *)imx->rx_buf = swab32(data);
+ imx->rx_buf += sizeof(u32);
+ }
+
+ rxlen--;
+ }
+ }
+
+ imx->xfer_len -= now * 4;
+
+ return now;
+}
+
+static void cspi_2_3_do_transfer(struct spi_device *spi)
+{
+ struct imx_spi *imx = container_of(spi->master, struct imx_spi, master);
+ u32 ctrl;
+
+ if (imx->bits_per_word == 8 || imx->bits_per_word == 16 || imx->bits_per_word == 32)
+ while (cspi_2_3_xchg_burst(spi) > 0);
+
+ if (!imx->xfer_len)
+ return;
+
+ ctrl = readl(imx->regs + CSPI_2_3_CTRL);
+ ctrl &= ~(0xfff << CSPI_2_3_CTRL_BL_OFFSET);
+ ctrl |= (spi->bits_per_word - 1) << CSPI_2_3_CTRL_BL_OFFSET;
+ writel(ctrl, imx->regs + CSPI_2_3_CTRL);
+
+ imx_spi_do_transfer(spi);
+}
+
static int imx_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
{
struct imx_spi *imx = container_of(spi->master, struct imx_spi, master);
@@ -399,7 +474,12 @@ static int imx_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
cs_change = t->cs_change;
- imx_spi_do_transfer(spi, t);
+ imx->tx_buf = t->tx_buf;
+ imx->rx_buf = t->rx_buf;
+ imx->xfer_len = t->len;
+ imx->bits_per_word = spi->bits_per_word;
+ imx->do_transfer(spi);
+
mesg->actual_length += t->len;
if (cs_change)
@@ -415,17 +495,20 @@ static int imx_spi_transfer(struct spi_device *spi, struct spi_message *mesg)
static __maybe_unused struct spi_imx_devtype_data spi_imx_devtype_data_0_0 = {
.chipselect = cspi_0_0_chipselect,
.xchg_single = cspi_0_0_xchg_single,
+ .do_transfer = imx_spi_do_transfer,
.init = cspi_0_0_init,
};
static __maybe_unused struct spi_imx_devtype_data spi_imx_devtype_data_0_7 = {
.chipselect = cspi_0_7_chipselect,
.xchg_single = cspi_0_7_xchg_single,
+ .do_transfer = imx_spi_do_transfer,
.init = cspi_0_7_init,
};
static __maybe_unused struct spi_imx_devtype_data spi_imx_devtype_data_2_3 = {
.chipselect = cspi_2_3_chipselect,
+ .do_transfer = cspi_2_3_do_transfer,
.xchg_single = cspi_2_3_xchg_single,
};
@@ -490,6 +573,7 @@ static int imx_spi_probe(struct device_d *dev)
imx->chipselect = devdata->chipselect;
imx->xchg_single = devdata->xchg_single;
+ imx->do_transfer = devdata->do_transfer;
imx->regs = dev_request_mem_region(dev, 0);
if (devdata->init)
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index f099406c53..bd2b16dd2a 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -190,6 +190,7 @@ struct spi_nor {
* @nor: the spi_nor structure
* @name: the chip type name
* @mode: the read mode supported by the driver
+ * @use_large_blocks: prefer large blocks even if 4k blocks are supported
*
* The drivers can use this fuction to scan the SPI NOR.
* In the scanning, it will try to get all the necessary information to
@@ -199,6 +200,7 @@ struct spi_nor {
*
* Return: 0 for success, others for failure.
*/
-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,
+ bool use_large_blocks);
#endif
diff --git a/include/spi/imx-spi.h b/include/spi/imx-spi.h
index 560b092bd2..221c66502f 100644
--- a/include/spi/imx-spi.h
+++ b/include/spi/imx-spi.h
@@ -61,6 +61,7 @@
#define CSPI_2_3_CTRL 0x08
#define CSPI_2_3_CTRL_ENABLE (1 << 0)
#define CSPI_2_3_CTRL_XCH (1 << 2)
+#define CSPI_2_3_CTRL_SMC (1 << 3)
#define CSPI_2_3_CTRL_MODE(cs) (1 << ((cs) + 4))
#define CSPI_2_3_CTRL_POSTDIV_OFFSET 8
#define CSPI_2_3_CTRL_PREDIV_OFFSET 12
@@ -78,6 +79,7 @@
#define CSPI_2_3_INT_RREN (1 << 3)
#define CSPI_2_3_STAT 0x18
-#define CSPI_2_3_STAT_RR (1 << 3)
+#define CSPI_2_3_STAT_TF (1 << 2)
+#define CSPI_2_3_STAT_RR (1 << 3)
#endif /* __SPI_IMX_SPI_H */