diff options
author | Marek Vasut <marex@denx.de> | 2016-06-20 15:28:36 +0200 |
---|---|---|
committer | Zoltan HERPAI <wigyori@uid0.hu> | 2016-06-20 15:28:36 +0200 |
commit | 870678baa7f8b9a65945237704d84d7724f42459 (patch) | |
tree | 29dba8a076d9e53752b02f768dcdbe15fa9271cd | |
parent | 8864b97350bde1fffe614df3c0bfc0cb34fb40d4 (diff) | |
download | master-187ad058-870678baa7f8b9a65945237704d84d7724f42459.tar.gz master-187ad058-870678baa7f8b9a65945237704d84d7724f42459.tar.bz2 master-187ad058-870678baa7f8b9a65945237704d84d7724f42459.zip |
target: socfpga: Add support for QSPI NOR boot
Add necessary kernel backports to support the Cadence QSPI controller
present on the Altera SoCFPGA SoC.
Signed-off-by: Marek Vasut <marex@denx.de>
34 files changed, 4760 insertions, 16 deletions
diff --git a/target/linux/socfpga/config-4.4 b/target/linux/socfpga/config-4.4 index 15cac52fa8..b68bf7a9b0 100644 --- a/target/linux/socfpga/config-4.4 +++ b/target/linux/socfpga/config-4.4 @@ -416,6 +416,7 @@ CONFIG_MMC_DW_PLTFM=y CONFIG_MODULES_TREE_LOOKUP=y CONFIG_MODULES_USE_ELF_REL=y CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_M25P80=y CONFIG_MTD_SPI_NOR=y CONFIG_MTD_UBI=y CONFIG_MTD_UBI_BEB_LIMIT=20 @@ -621,6 +622,7 @@ CONFIG_SPARSE_IRQ=y CONFIG_SPI=y CONFIG_SPI_ALTERA=y CONFIG_SPI_BITBANG=y +CONFIG_SPI_CADENCE_QUADSPI=y CONFIG_SPI_DESIGNWARE=y CONFIG_SPI_DW_MMIO=y CONFIG_SPI_MASTER=y diff --git a/target/linux/socfpga/patches-4.4/0001-dt-bindings-gpio-altera-Fix-altr-interrupt-type-prop.patch b/target/linux/socfpga/patches-4.4/0001-dt-bindings-gpio-altera-Fix-altr-interrupt-type-prop.patch index b89793a162..bf658eb734 100644 --- a/target/linux/socfpga/patches-4.4/0001-dt-bindings-gpio-altera-Fix-altr-interrupt-type-prop.patch +++ b/target/linux/socfpga/patches-4.4/0001-dt-bindings-gpio-altera-Fix-altr-interrupt-type-prop.patch @@ -1,7 +1,7 @@ -From b32732e51a774e8514f40975f2600f02ef9db0b4 Mon Sep 17 00:00:00 2001 +From 39c5b88eedd3e99c57aeaf7d7d61a830a4ef4b80 Mon Sep 17 00:00:00 2001 From: Marek Vasut <marex@denx.de> Date: Mon, 29 Feb 2016 17:19:59 +0100 -Subject: [PATCH 1/5] dt-bindings: gpio: altera: Fix altr,interrupt-type +Subject: [PATCH 01/33] dt-bindings: gpio: altera: Fix altr,interrupt-type property The altr,interrupt-trigger property is not used by the driver. @@ -41,5 +41,5 @@ index 12f5014..826a720 100644 gpio-controller; #interrupt-cells = <1>; -- -2.7.0 +2.8.1 diff --git a/target/linux/socfpga/patches-4.4/0002-usb-dwc2-gadget-Repair-DSTS-register-decoding.patch b/target/linux/socfpga/patches-4.4/0002-usb-dwc2-gadget-Repair-DSTS-register-decoding.patch index 9be3834055..2a2b2ba8c6 100644 --- a/target/linux/socfpga/patches-4.4/0002-usb-dwc2-gadget-Repair-DSTS-register-decoding.patch +++ b/target/linux/socfpga/patches-4.4/0002-usb-dwc2-gadget-Repair-DSTS-register-decoding.patch @@ -1,7 +1,7 @@ -From e5cbd23e4f40181c907a1abc136b17de8cb86809 Mon Sep 17 00:00:00 2001 +From f7697963e1b0fc1496709f84c00da5cb144a58a3 Mon Sep 17 00:00:00 2001 From: Marek Vasut <marex@denx.de> Date: Thu, 17 Dec 2015 23:42:35 +0100 -Subject: [PATCH 2/5] usb: dwc2: gadget: Repair DSTS register decoding +Subject: [PATCH 02/33] usb: dwc2: gadget: Repair DSTS register decoding The "enumspd" field is located in register DSTS[2:1], but the code which checks the bitfield does not shift the value accordingly. This @@ -32,5 +32,5 @@ index 0abf73c..48e47c1 100644 case DSTS_ENUMSPD_FS48: hsotg->gadget.speed = USB_SPEED_FULL; -- -2.7.0 +2.8.1 diff --git a/target/linux/socfpga/patches-4.4/0003-ARM-socfpga-dts-Enable-MMC-support-at-correct-place-.patch b/target/linux/socfpga/patches-4.4/0003-ARM-socfpga-dts-Enable-MMC-support-at-correct-place-.patch index b12de6dcf9..5dec33881e 100644 --- a/target/linux/socfpga/patches-4.4/0003-ARM-socfpga-dts-Enable-MMC-support-at-correct-place-.patch +++ b/target/linux/socfpga/patches-4.4/0003-ARM-socfpga-dts-Enable-MMC-support-at-correct-place-.patch @@ -1,8 +1,8 @@ -From 6b8c64eb90e5d958f32524ff2d0571b3b6ac92df Mon Sep 17 00:00:00 2001 +From 23d14e4479c925904bc7620a55c66eb8babacbb9 Mon Sep 17 00:00:00 2001 From: Marek Vasut <marex@denx.de> Date: Mon, 21 Dec 2015 00:42:01 -0600 -Subject: [PATCH 3/5] ARM: socfpga: dts: Enable MMC support at correct place in - the DT +Subject: [PATCH 03/33] ARM: socfpga: dts: Enable MMC support at correct place + in the DT The socfpga.dtsi explicitly enabled MMC support, but not all boards are equiped with an MMC card. There are setups which only have QSPI NOR. @@ -86,5 +86,5 @@ index 48bf651..b61f22f 100644 &usb1 { -- -2.7.0 +2.8.1 diff --git a/target/linux/socfpga/patches-4.4/0004-ARM-socfpga-Add-support-for-HPS-LEDs-on-SoCKit.patch b/target/linux/socfpga/patches-4.4/0004-ARM-socfpga-Add-support-for-HPS-LEDs-on-SoCKit.patch index 954f03e990..f3155c5b2c 100644 --- a/target/linux/socfpga/patches-4.4/0004-ARM-socfpga-Add-support-for-HPS-LEDs-on-SoCKit.patch +++ b/target/linux/socfpga/patches-4.4/0004-ARM-socfpga-Add-support-for-HPS-LEDs-on-SoCKit.patch @@ -1,7 +1,7 @@ -From e56e545745dc42cba743dab549d0afb1a39d14b4 Mon Sep 17 00:00:00 2001 +From 2088bf920b5ff60ab14e9fca940b4ae28e8b88d3 Mon Sep 17 00:00:00 2001 From: Marek Vasut <marex@denx.de> Date: Mon, 22 Jun 2015 23:37:47 +0200 -Subject: [PATCH 4/5] ARM: socfpga: Add support for HPS LEDs on SoCKit +Subject: [PATCH 04/33] ARM: socfpga: Add support for HPS LEDs on SoCKit Add support for the blue LEDs on the SoCFPGA SoCkit board. @@ -62,5 +62,5 @@ index b61f22f..1461690 100644 status = "okay"; }; -- -2.7.0 +2.8.1 diff --git a/target/linux/socfpga/patches-4.4/0005-ARM-socfpga-Add-support-for-HPS-KEYs-SWs-on-SoCKit.patch b/target/linux/socfpga/patches-4.4/0005-ARM-socfpga-Add-support-for-HPS-KEYs-SWs-on-SoCKit.patch index a5e53f5d6c..8e24b6c1e1 100644 --- a/target/linux/socfpga/patches-4.4/0005-ARM-socfpga-Add-support-for-HPS-KEYs-SWs-on-SoCKit.patch +++ b/target/linux/socfpga/patches-4.4/0005-ARM-socfpga-Add-support-for-HPS-KEYs-SWs-on-SoCKit.patch @@ -1,7 +1,7 @@ -From a953c0800246e99c9b449bd9ec0b26682a82700c Mon Sep 17 00:00:00 2001 +From 7e990b69f2331daf7847ddb82bb5da9623f24f4f Mon Sep 17 00:00:00 2001 From: Marek Vasut <marex@denx.de> Date: Tue, 23 Jun 2015 00:41:08 +0200 -Subject: [PATCH 5/5] ARM: socfpga: Add support for HPS KEYs/SWs on SoCKit +Subject: [PATCH 05/33] ARM: socfpga: Add support for HPS KEYs/SWs on SoCKit Add support for the keys and flip-switches on the SoCFPGA SoCkit board. @@ -96,5 +96,5 @@ index 1461690..02e22f5 100644 }; -- -2.7.0 +2.8.1 diff --git a/target/linux/socfpga/patches-4.4/0006-mtd-add-get-set-of_node-flash_node-helpers.patch b/target/linux/socfpga/patches-4.4/0006-mtd-add-get-set-of_node-flash_node-helpers.patch new file mode 100644 index 0000000000..c3415d316c --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0006-mtd-add-get-set-of_node-flash_node-helpers.patch @@ -0,0 +1,87 @@ +From cb0416f4be60979f3fd3e99a9baff17ac9b8381f Mon Sep 17 00:00:00 2001 +From: Brian Norris <computersforpeace@gmail.com> +Date: Fri, 30 Oct 2015 20:33:20 -0700 +Subject: [PATCH 06/33] mtd: add get/set of_node/flash_node helpers + +We are going to begin using the mtd->dev.of_node field for MTD device +nodes, so let's add helpers for it. Also, we'll be making some +conversions on spi_nor (and nand_chip eventually) too, so get that ready +with their own helpers. + +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com> +--- + include/linux/mtd/mtd.h | 11 +++++++++++ + include/linux/mtd/nand.h | 11 +++++++++++ + include/linux/mtd/spi-nor.h | 11 +++++++++++ + 3 files changed, 33 insertions(+) + +diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h +index f17fa75..cc84923 100644 +--- a/include/linux/mtd/mtd.h ++++ b/include/linux/mtd/mtd.h +@@ -254,6 +254,17 @@ struct mtd_info { + int usecount; + }; + ++static inline void mtd_set_of_node(struct mtd_info *mtd, ++ struct device_node *np) ++{ ++ mtd->dev.of_node = np; ++} ++ ++static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd) ++{ ++ return mtd->dev.of_node; ++} ++ + int mtd_erase(struct mtd_info *mtd, struct erase_info *instr); + int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, + void **virt, resource_size_t *phys); +diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h +index 5a9d1d4..4f7c9b9 100644 +--- a/include/linux/mtd/nand.h ++++ b/include/linux/mtd/nand.h +@@ -719,6 +719,17 @@ struct nand_chip { + void *priv; + }; + ++static inline void nand_set_flash_node(struct nand_chip *chip, ++ struct device_node *np) ++{ ++ chip->flash_node = np; ++} ++ ++static inline struct device_node *nand_get_flash_node(struct nand_chip *chip) ++{ ++ return chip->flash_node; ++} ++ + /* + * NAND Flash Manufacturer ID Codes + */ +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index 12a4611..823c5381 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -184,6 +184,17 @@ struct spi_nor { + void *priv; + }; + ++static inline void spi_nor_set_flash_node(struct spi_nor *nor, ++ struct device_node *np) ++{ ++ nor->flash_node = np; ++} ++ ++static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor) ++{ ++ return nor->flash_node; ++} ++ + /** + * spi_nor_scan() - scan the SPI NOR + * @nor: the spi_nor structure +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0007-mtd-nand-spi-nor-assign-MTD-of_node.patch b/target/linux/socfpga/patches-4.4/0007-mtd-nand-spi-nor-assign-MTD-of_node.patch new file mode 100644 index 0000000000..c556a640a4 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0007-mtd-nand-spi-nor-assign-MTD-of_node.patch @@ -0,0 +1,44 @@ +From 0b3e7c875f3f6ac9127b55eb99817c2f412e164e Mon Sep 17 00:00:00 2001 +From: Brian Norris <computersforpeace@gmail.com> +Date: Fri, 30 Oct 2015 20:33:22 -0700 +Subject: [PATCH 07/33] mtd: {nand,spi-nor}: assign MTD of_node + +We should pass along our flash DT node to the MTD layer, so it can set +up ofpart for us. + +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com> +--- + drivers/mtd/nand/nand_base.c | 3 +++ + drivers/mtd/spi-nor/spi-nor.c | 1 + + 2 files changed, 4 insertions(+) + +diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c +index 3ff583f..1f30656 100644 +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -3990,6 +3990,9 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, + int ret; + + if (chip->flash_node) { ++ /* MTD can automatically handle DT partitions, etc. */ ++ mtd_set_of_node(mtd, chip->flash_node); ++ + ret = nand_dt_init(mtd, chip, chip->flash_node); + if (ret) + return ret; +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 32477c4..24ad373 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1256,6 +1256,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + mtd->flags |= MTD_NO_ERASE; + + mtd->dev.parent = dev; ++ mtd_set_of_node(mtd, np); + nor->page_size = info->page_size; + mtd->writebufsize = nor->page_size; + +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0008-mtd-spi-nor-convert-to-spi_nor_-get-set-_flash_node.patch b/target/linux/socfpga/patches-4.4/0008-mtd-spi-nor-convert-to-spi_nor_-get-set-_flash_node.patch new file mode 100644 index 0000000000..0f4093f424 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0008-mtd-spi-nor-convert-to-spi_nor_-get-set-_flash_node.patch @@ -0,0 +1,91 @@ +From bf8554bc3b6b699e9c5b990f63b286b31cf7e80b Mon Sep 17 00:00:00 2001 +From: Brian Norris <computersforpeace@gmail.com> +Date: Fri, 30 Oct 2015 20:33:24 -0700 +Subject: [PATCH 08/33] mtd: spi-nor: convert to spi_nor_{get, + set}_flash_node() + +Used semantic patch with 'make coccicheck MODE=patch COCCI=script.cocci': + +---8<---- +virtual patch + +@@ +struct spi_nor b; +struct spi_nor *c; +expression d; +@@ +( +-(b).flash_node = (d) ++spi_nor_set_flash_node(&b, d) +| +-(c)->flash_node = (d) ++spi_nor_set_flash_node(c, d) +) +---8<---- + +And a manual conversion for the one use of spi_nor_get_flash_node(). + +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com> +--- + drivers/mtd/devices/m25p80.c | 2 +- + drivers/mtd/spi-nor/fsl-quadspi.c | 2 +- + drivers/mtd/spi-nor/nxp-spifi.c | 2 +- + drivers/mtd/spi-nor/spi-nor.c | 2 +- + 4 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c +index fe9ceb7..bc7a802 100644 +--- a/drivers/mtd/devices/m25p80.c ++++ b/drivers/mtd/devices/m25p80.c +@@ -199,7 +199,7 @@ static int m25p_probe(struct spi_device *spi) + nor->read_reg = m25p80_read_reg; + + nor->dev = &spi->dev; +- nor->flash_node = spi->dev.of_node; ++ spi_nor_set_flash_node(nor, spi->dev.of_node); + nor->priv = flash; + + spi_set_drvdata(spi, flash); +diff --git a/drivers/mtd/spi-nor/fsl-quadspi.c b/drivers/mtd/spi-nor/fsl-quadspi.c +index 7b10ed4..8f4d920 100644 +--- a/drivers/mtd/spi-nor/fsl-quadspi.c ++++ b/drivers/mtd/spi-nor/fsl-quadspi.c +@@ -1013,7 +1013,7 @@ static int fsl_qspi_probe(struct platform_device *pdev) + mtd = &nor->mtd; + + nor->dev = dev; +- nor->flash_node = np; ++ spi_nor_set_flash_node(nor, np); + nor->priv = q; + + /* fill the hooks */ +diff --git a/drivers/mtd/spi-nor/nxp-spifi.c b/drivers/mtd/spi-nor/nxp-spifi.c +index 9e82098..4524b28 100644 +--- a/drivers/mtd/spi-nor/nxp-spifi.c ++++ b/drivers/mtd/spi-nor/nxp-spifi.c +@@ -330,7 +330,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi, + writel(ctrl, spifi->io_base + SPIFI_CTRL); + + spifi->nor.dev = spifi->dev; +- spifi->nor.flash_node = np; ++ spi_nor_set_flash_node(&spifi->nor, np); + spifi->nor.priv = spifi; + spifi->nor.read = nxp_spifi_read; + spifi->nor.write = nxp_spifi_write; +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 24ad373..b76090e 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1151,7 +1151,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + const struct flash_info *info = NULL; + struct device *dev = nor->dev; + struct mtd_info *mtd = &nor->mtd; +- struct device_node *np = nor->flash_node; ++ struct device_node *np = spi_nor_get_flash_node(nor); + int ret; + int i; + +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0009-mtd-spi-nor-drop-flash_node-field.patch b/target/linux/socfpga/patches-4.4/0009-mtd-spi-nor-drop-flash_node-field.patch new file mode 100644 index 0000000000..1da6c949fb --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0009-mtd-spi-nor-drop-flash_node-field.patch @@ -0,0 +1,64 @@ +From 1348b9e2300f66a4ffcb5b467f4c99cfb958ffca Mon Sep 17 00:00:00 2001 +From: Brian Norris <computersforpeace@gmail.com> +Date: Fri, 30 Oct 2015 20:33:27 -0700 +Subject: [PATCH 09/33] mtd: spi-nor: drop flash_node field + +We can just alias to the MTD of_node. + +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 1 - + include/linux/mtd/spi-nor.h | 6 ++---- + 2 files changed, 2 insertions(+), 5 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index b76090e..3e06d5b 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1256,7 +1256,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + mtd->flags |= MTD_NO_ERASE; + + mtd->dev.parent = dev; +- mtd_set_of_node(mtd, np); + nor->page_size = info->page_size; + mtd->writebufsize = nor->page_size; + +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index 823c5381..592420b 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -123,7 +123,6 @@ enum spi_nor_option_flags { + * @mtd: point to a mtd_info structure + * @lock: the lock for the read/write/erase/lock/unlock operations + * @dev: point to a spi device, or a spi nor controller device. +- * @flash_node: point to a device node describing this flash instance. + * @page_size: the page size of the SPI NOR + * @addr_width: number of address bytes + * @erase_opcode: the opcode for erasing a sector +@@ -154,7 +153,6 @@ struct spi_nor { + struct mtd_info mtd; + struct mutex lock; + struct device *dev; +- struct device_node *flash_node; + u32 page_size; + u8 addr_width; + u8 erase_opcode; +@@ -187,12 +185,12 @@ struct spi_nor { + static inline void spi_nor_set_flash_node(struct spi_nor *nor, + struct device_node *np) + { +- nor->flash_node = np; ++ mtd_set_of_node(&nor->mtd, np); + } + + static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor) + { +- return nor->flash_node; ++ return mtd_get_of_node(&nor->mtd); + } + + /** +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0010-mtd-spi-nor-remove-unnecessary-leading-space-from-db.patch b/target/linux/socfpga/patches-4.4/0010-mtd-spi-nor-remove-unnecessary-leading-space-from-db.patch new file mode 100644 index 0000000000..19b22c5018 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0010-mtd-spi-nor-remove-unnecessary-leading-space-from-db.patch @@ -0,0 +1,32 @@ +From f8c5645dd28440380622c2ad3744de0b55bd0bdf Mon Sep 17 00:00:00 2001 +From: Brian Norris <computersforpeace@gmail.com> +Date: Fri, 30 Oct 2015 12:56:22 -0700 +Subject: [PATCH 10/33] mtd: spi-nor: remove unnecessary leading space from dbg + print + +As Cyrille noted [1], this line is wrong. + +[1] http://lists.infradead.org/pipermail/linux-mtd/2015-September/061725.html + +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +Cc: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 3e06d5b..107571e 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -856,7 +856,7 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) + + tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); + if (tmp < 0) { +- dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp); ++ dev_dbg(nor->dev, "error %d reading JEDEC ID\n", tmp); + return ERR_PTR(tmp); + } + +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0011-mtd-spi-nor-provide-default-erase_sector-implementat.patch b/target/linux/socfpga/patches-4.4/0011-mtd-spi-nor-provide-default-erase_sector-implementat.patch new file mode 100644 index 0000000000..666b069451 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0011-mtd-spi-nor-provide-default-erase_sector-implementat.patch @@ -0,0 +1,112 @@ +From 29675718c3880cbe7a8d8c6819c07dcec656c544 Mon Sep 17 00:00:00 2001 +From: Brian Norris <computersforpeace@gmail.com> +Date: Tue, 10 Nov 2015 12:15:27 -0800 +Subject: [PATCH 11/33] mtd: spi-nor: provide default erase_sector + implementation + +Some spi-nor drivers perform sector erase by duplicating their +write_reg() command. Let's not require that the driver fill this out, +and provide a default instead. + +Tested on m25p80.c and Medatek's MT8173 SPI NOR flash driver. + +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 37 +++++++++++++++++++++++++++++++++---- + include/linux/mtd/spi-nor.h | 3 ++- + 2 files changed, 35 insertions(+), 5 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 107571e..0d2be38 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -38,6 +38,7 @@ + #define CHIP_ERASE_2MB_READY_WAIT_JIFFIES (40UL * HZ) + + #define SPI_NOR_MAX_ID_LEN 6 ++#define SPI_NOR_MAX_ADDR_WIDTH 4 + + struct flash_info { + char *name; +@@ -313,6 +314,29 @@ static void spi_nor_unlock_and_unprep(struct spi_nor *nor, enum spi_nor_ops ops) + } + + /* ++ * Initiate the erasure of a single sector ++ */ ++static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr) ++{ ++ u8 buf[SPI_NOR_MAX_ADDR_WIDTH]; ++ int i; ++ ++ if (nor->erase) ++ return nor->erase(nor, addr); ++ ++ /* ++ * Default implementation, if driver doesn't have a specialized HW ++ * control ++ */ ++ for (i = nor->addr_width - 1; i >= 0; i--) { ++ buf[i] = addr & 0xff; ++ addr >>= 8; ++ } ++ ++ return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width); ++} ++ ++/* + * Erase an address range on the nor chip. The address range may extend + * one or more erase sectors. Return an error is there is a problem erasing. + */ +@@ -371,10 +395,9 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) + while (len) { + write_enable(nor); + +- if (nor->erase(nor, addr)) { +- ret = -EIO; ++ ret = spi_nor_erase_sector(nor, addr); ++ if (ret) + goto erase_err; +- } + + addr += mtd->erasesize; + len -= mtd->erasesize; +@@ -1138,7 +1161,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + static int spi_nor_check(struct spi_nor *nor) + { + if (!nor->dev || !nor->read || !nor->write || +- !nor->read_reg || !nor->write_reg || !nor->erase) { ++ !nor->read_reg || !nor->write_reg) { + pr_err("spi-nor: please fill all the necessary fields!\n"); + return -EINVAL; + } +@@ -1338,6 +1361,12 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + nor->addr_width = 3; + } + ++ if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { ++ dev_err(dev, "address width is too large: %u\n", ++ nor->addr_width); ++ return -EINVAL; ++ } ++ + nor->read_dummy = spi_nor_read_dummy_cycles(nor); + + dev_info(dev, "%s (%lld Kbytes)\n", info->name, +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index 592420b..62356d5 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -142,7 +142,8 @@ enum spi_nor_option_flags { + * @read: [DRIVER-SPECIFIC] read data from the SPI NOR + * @write: [DRIVER-SPECIFIC] write data to the SPI NOR + * @erase: [DRIVER-SPECIFIC] erase a sector of the SPI NOR +- * at the offset @offs ++ * at the offset @offs; if not provided by the driver, ++ * spi-nor will send the erase opcode via write_reg() + * @flash_lock: [FLASH-SPECIFIC] lock a region of the SPI NOR + * @flash_unlock: [FLASH-SPECIFIC] unlock a region of the SPI NOR + * @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0012-mtd-spi-nor-mx25l3205d-mx25l6405d-append-SECT_4K.patch b/target/linux/socfpga/patches-4.4/0012-mtd-spi-nor-mx25l3205d-mx25l6405d-append-SECT_4K.patch new file mode 100644 index 0000000000..720d687dff --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0012-mtd-spi-nor-mx25l3205d-mx25l6405d-append-SECT_4K.patch @@ -0,0 +1,32 @@ +From 655585649ba3f4675b4386afc1feb22c6a880eb8 Mon Sep 17 00:00:00 2001 +From: Andreas Fenkart <afenkart@gmail.com> +Date: Thu, 5 Nov 2015 10:04:23 +0100 +Subject: [PATCH 12/33] mtd: spi-nor: mx25l3205d/mx25l6405d: append SECT_4K + +according datasheet both chips can erase 4kByte sectors individually + +Signed-off-by: Andreas Fenkart <andreas.fenkart@dev.digitalstrom.org> +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 0d2be38..c5bc927 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -738,9 +738,9 @@ static const struct flash_info spi_nor_ids[] = { + { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, + { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, + { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, +- { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, ++ { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, SECT_4K) }, + { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, +- { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, ++ { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, SECT_4K) }, + { "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) }, + { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, + { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0013-mtd-spi-nor-Fix-error-message-with-unrecognized-JEDE.patch b/target/linux/socfpga/patches-4.4/0013-mtd-spi-nor-Fix-error-message-with-unrecognized-JEDE.patch new file mode 100644 index 0000000000..1fd89554b7 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0013-mtd-spi-nor-Fix-error-message-with-unrecognized-JEDE.patch @@ -0,0 +1,35 @@ +From 14b9112f7d20455ffef7d796317e7d08c6545b41 Mon Sep 17 00:00:00 2001 +From: Ricardo Ribalda <ricardo.ribalda@gmail.com> +Date: Mon, 30 Nov 2015 20:41:17 +0100 +Subject: [PATCH 13/33] mtd: spi-nor: Fix error message with unrecognized JEDEC + +The error message was: + +m25p80 spi32766.0: unrecognized JEDEC id bytes: 00, 0, 0 + +The new error message: + +m25p80 spi32766.0: unrecognized JEDEC id bytes: 00, 00, 00 + +Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com> +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index c5bc927..3a50eea 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -890,7 +890,7 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) + return &spi_nor_ids[tmp]; + } + } +- dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %2x, %2x\n", ++ dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %02x, %02x\n", + id[0], id[1], id[2]); + return ERR_PTR(-ENODEV); + } +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0014-mtd-spi-nor-fix-error-handling-in-spi_nor_erase.patch b/target/linux/socfpga/patches-4.4/0014-mtd-spi-nor-fix-error-handling-in-spi_nor_erase.patch new file mode 100644 index 0000000000..c9f48eaa5b --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0014-mtd-spi-nor-fix-error-handling-in-spi_nor_erase.patch @@ -0,0 +1,44 @@ +From 93aa2e2f5af5b8e766fc22c3ff83a1642462025f Mon Sep 17 00:00:00 2001 +From: Heiner Kallweit <hkallweit1@gmail.com> +Date: Tue, 17 Nov 2015 20:18:54 +0100 +Subject: [PATCH 14/33] mtd: spi-nor: fix error handling in spi_nor_erase + +The documenting comment of mtd_erase in mtdcore.c states: +Device drivers are supposed to call instr->callback() whenever +the operation completes, even if it completes with a failure. + +Currently the callback isn't called in case of failure. Fix this. + +Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 3a50eea..43e00e2 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -410,17 +410,13 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr) + + write_disable(nor); + ++erase_err: + spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); + +- instr->state = MTD_ERASE_DONE; ++ instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return ret; +- +-erase_err: +- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE); +- instr->state = MTD_ERASE_FAILED; +- return ret; + } + + static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0015-mtd-spi-nor-Check-the-return-value-from-read_sr.patch b/target/linux/socfpga/patches-4.4/0015-mtd-spi-nor-Check-the-return-value-from-read_sr.patch new file mode 100644 index 0000000000..bfb5a97ac8 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0015-mtd-spi-nor-Check-the-return-value-from-read_sr.patch @@ -0,0 +1,60 @@ +From 8f5f914b1b6d70d6d7455e0f89df84b3377677f9 Mon Sep 17 00:00:00 2001 +From: Fabio Estevam <fabio.estevam@freescale.com> +Date: Fri, 20 Nov 2015 16:26:11 -0200 +Subject: [PATCH 15/33] mtd: spi-nor: Check the return value from read_sr() + +We should better check the return value from read_sr() and +propagate it in the case of error. + +Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com> +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 43e00e2..f8f36d4 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -478,11 +478,13 @@ static int stm_is_locked_sr(struct spi_nor *nor, loff_t ofs, uint64_t len, + static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) + { + struct mtd_info *mtd = &nor->mtd; +- u8 status_old, status_new; ++ int status_old, status_new; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 shift = ffs(mask) - 1, pow, val; + + status_old = read_sr(nor); ++ if (status_old < 0) ++ return status_old; + + /* SPI NOR always locks to the end */ + if (ofs + len != mtd->size) { +@@ -528,11 +530,13 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) + static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) + { + struct mtd_info *mtd = &nor->mtd; +- uint8_t status_old, status_new; ++ int status_old, status_new; + u8 mask = SR_BP2 | SR_BP1 | SR_BP0; + u8 shift = ffs(mask) - 1, pow, val; + + status_old = read_sr(nor); ++ if (status_old < 0) ++ return status_old; + + /* Cannot unlock; would unlock larger region than requested */ + if (stm_is_locked_sr(nor, ofs - mtd->erasesize, mtd->erasesize, +@@ -1032,6 +1036,8 @@ static int macronix_quad_enable(struct spi_nor *nor) + int ret, val; + + val = read_sr(nor); ++ if (val < 0) ++ return val; + write_enable(nor); + + write_sr(nor, val | SR_QUAD_EN_MX); +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0016-mtd-spi-nor-remove-micron_quad_enable.patch b/target/linux/socfpga/patches-4.4/0016-mtd-spi-nor-remove-micron_quad_enable.patch new file mode 100644 index 0000000000..48ceb8ef52 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0016-mtd-spi-nor-remove-micron_quad_enable.patch @@ -0,0 +1,110 @@ +From 246e2a5cc60e2179bf8849310b7af9eaa5c505f9 Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:02:13 +0100 +Subject: [PATCH 16/33] mtd: spi-nor: remove micron_quad_enable() + +This patch remove the micron_quad_enable() function which force the Quad +SPI mode. However, once this mode is enabled, the Micron memory expect ALL +commands to use the SPI 4-4-4 protocol. Hence a failure does occur when +calling spi_nor_wait_till_ready() right after the update of the Enhanced +Volatile Configuration Register (EVCR) in the micron_quad_enable() as +the SPI controller driver is not aware about the protocol change. + +Since there is almost no performance increase using Fast Read 4-4-4 +commands instead of Fast Read 1-1-4 commands, we rather keep on using the +Extended SPI mode than enabling the Quad SPI mode. + +Let's take the example of the pretty standard use of 8 dummy cycles during +Fast Read operations on 64KB erase sectors: + +Fast Read 1-1-4 requires 8 cycles for the command, then 24 cycles for the +3byte address followed by 8 dummy clock cycles and finally 65536*2 cycles +for the read data; so 131112 clock cycles. + +On the other hand the Fast Read 4-4-4 would require 2 cycles for the +command, then 6 cycles for the 3byte address followed by 8 dummy clock +cycles and finally 65536*2 cycles for the read data. So 131088 clock +cycles. The theorical bandwidth increase is 0.0%. + +Now using Fast Read operations on 512byte pages: +Fast Read 1-1-4 needs 8+24+8+(512*2) = 1064 clock cycles whereas Fast +Read 4-4-4 would requires 2+6+8+(512*2) = 1040 clock cycles. Hence the +theorical bandwidth increase is 2.3%. +Consecutive reads for non sequential pages is not a relevant use case so +The Quad SPI mode is not worth it. + +mtd_speedtest seems to confirm these figures. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Fixes: 548cd3ab54da ("mtd: spi-nor: Add quad I/O support for Micron SPI NOR") +--- + drivers/mtd/spi-nor/spi-nor.c | 46 +------------------------------------------ + 1 file changed, 1 insertion(+), 45 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index f8f36d4..6e72e96 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1092,45 +1092,6 @@ static int spansion_quad_enable(struct spi_nor *nor) + return 0; + } + +-static int micron_quad_enable(struct spi_nor *nor) +-{ +- int ret; +- u8 val; +- +- ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1); +- if (ret < 0) { +- dev_err(nor->dev, "error %d reading EVCR\n", ret); +- return ret; +- } +- +- write_enable(nor); +- +- /* set EVCR, enable quad I/O */ +- nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON; +- ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1); +- if (ret < 0) { +- dev_err(nor->dev, "error while writing EVCR register\n"); +- return ret; +- } +- +- ret = spi_nor_wait_till_ready(nor); +- if (ret) +- return ret; +- +- /* read EVCR and check it */ +- ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1); +- if (ret < 0) { +- dev_err(nor->dev, "error %d reading EVCR\n", ret); +- return ret; +- } +- if (val & EVCR_QUAD_EN_MICRON) { +- dev_err(nor->dev, "Micron EVCR Quad bit not clear\n"); +- return -EINVAL; +- } +- +- return 0; +-} +- + static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + { + int status; +@@ -1144,12 +1105,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + } + return status; + case SNOR_MFR_MICRON: +- status = micron_quad_enable(nor); +- if (status) { +- dev_err(nor->dev, "Micron quad-read not enabled\n"); +- return -EINVAL; +- } +- return status; ++ return 0; + default: + status = spansion_quad_enable(nor); + if (status) { +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0017-mtd-spi-nor-properly-detect-the-memory-when-it-boots.patch b/target/linux/socfpga/patches-4.4/0017-mtd-spi-nor-properly-detect-the-memory-when-it-boots.patch new file mode 100644 index 0000000000..63cd47e006 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0017-mtd-spi-nor-properly-detect-the-memory-when-it-boots.patch @@ -0,0 +1,244 @@ +From 06883bee58d1beedef783f7f5662367736d90924 Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:02:14 +0100 +Subject: [PATCH 17/33] mtd: spi-nor: properly detect the memory when it boots + in Quad or Dual mode + +The quad (or dual) mode of a spi-nor memory may be enabled at boot time by +non-volatile bits in some setting register. Also such a mode may have +already been enabled at early stage by some boot loader. + +Hence, we should not guess the spi-nor memory is always configured for the +regular SPI 1-1-1 protocol. + +Micron and Macronix memories, once their Quad (or dual for Micron) mode +enabled, no longer process the regular JEDEC Read ID (0x9f) command but +instead reply to a new command: JEDEC Read ID Multiple I/O (0xaf). +Besides, in Quad mode both memory manufacturers expect ALL commands to +use the SPI 4-4-4 protocol. For Micron memories, enabling their Dual mode +implies to use the SPI 2-2-2 protocol for ALL commands. + +Winbond memories, once their Quad mode enabled, expect ALL commands to use +the SPI 4-4-4 protocol. Unlike Micron and Macronix memories, they still +reply to the regular JEDEC Read ID (0x9f) command. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 83 ++++++++++++++++++++++++++++++++++++++++--- + include/linux/mtd/spi-nor.h | 50 ++++++++++++++++++++++++-- + 2 files changed, 127 insertions(+), 6 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 6e72e96..9ad2d40 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -73,6 +73,12 @@ struct flash_info { + + #define JEDEC_MFR(info) ((info)->id[0]) + ++struct read_id_config { ++ enum read_mode mode; ++ enum spi_nor_protocol proto; ++ u8 opcode; ++}; ++ + static const struct flash_info *spi_nor_match_id(const char *name); + + /* +@@ -871,11 +877,22 @@ static const struct flash_info spi_nor_ids[] = { + { }, + }; + +-static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) ++static const struct flash_info *spi_nor_read_id(struct spi_nor *nor, ++ enum read_mode mode) + { +- int tmp; ++ int i, tmp; + u8 id[SPI_NOR_MAX_ID_LEN]; + const struct flash_info *info; ++ static const struct read_id_config configs[] = { ++ /* Winbond QPI mode */ ++ {SPI_NOR_QUAD, SNOR_PROTO_4_4_4, SPINOR_OP_RDID}, ++ ++ /* Micron Quad mode & Macronix QPI mode */ ++ {SPI_NOR_QUAD, SNOR_PROTO_4_4_4, SPINOR_OP_MIO_RDID}, ++ ++ /* Micron Dual mode */ ++ {SPI_NOR_DUAL, SNOR_PROTO_2_2_2, SPINOR_OP_MIO_RDID} ++ }; + + tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN); + if (tmp < 0) { +@@ -883,6 +900,58 @@ static const struct flash_info *spi_nor_read_id(struct spi_nor *nor) + return ERR_PTR(tmp); + } + ++ /* ++ * Check whether the SPI NOR memory has already been configured (at ++ * reset or by some bootloader) to use a protocol other than SPI 1-1-1. ++ */ ++ for (i = 0; i < ARRAY_SIZE(configs); ++i) { ++ int len = SPI_NOR_MAX_ID_LEN; ++ bool is_multi = false; ++ ++ /* ++ * Check the latest read Manufacturer ID + Device ID (3 bytes): ++ * if they are different from both 0x000000 and 0xffffff, we ++ * assume that we succeeded in reading a valid JEDEC ID so we ++ * don't need to try other SPI protocols. ++ * Indeed when either the protocol or the op code are not valid, ++ * the SPI NOR memory should not reply to the command. Hence the ++ * SPI I/O lines remain in their default state: 1 when connected ++ * to pull-up resistors or 0 with pull-down. ++ */ ++ if (!((id[0] == 0xff && id[1] == 0xff && id[2] == 0xff) || ++ (id[0] == 0x00 && id[1] == 0x00 && id[2] == 0x00))) ++ break; ++ ++ /* Only try protocols supported by the user. */ ++ if (configs[i].mode != mode) ++ continue; ++ ++ /* Set this protocol for all commands. */ ++ nor->reg_proto = configs[i].proto; ++ nor->read_proto = configs[i].proto; ++ nor->write_proto = configs[i].proto; ++ nor->erase_proto = configs[i].proto; ++ ++ /* ++ * Multiple I/O Read ID only returns the Manufacturer ID ++ * (1 byte) and the Device ID (2 bytes). So we reset the ++ * remaining bytes. ++ */ ++ if (configs[i].opcode == SPINOR_OP_MIO_RDID) { ++ is_multi = true; ++ len = 3; ++ memset(id + len, 0, sizeof(id) - len); ++ } ++ ++ tmp = nor->read_reg(nor, configs[i].opcode, id, len); ++ if (tmp < 0) { ++ dev_dbg(nor->dev, ++ "error %d reading JEDEC ID%s\n", ++ tmp, (is_multi ? " Multi I/O" : "")); ++ return ERR_PTR(tmp); ++ } ++ } ++ + for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) { + info = &spi_nor_ids[tmp]; + if (info->id_len) { +@@ -1140,11 +1209,17 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + if (ret) + return ret; + ++ /* Reset SPI protocol for all commands */ ++ nor->erase_proto = SNOR_PROTO_1_1_1; ++ nor->read_proto = SNOR_PROTO_1_1_1; ++ nor->write_proto = SNOR_PROTO_1_1_1; ++ nor->reg_proto = SNOR_PROTO_1_1_1; ++ + if (name) + info = spi_nor_match_id(name); + /* Try to auto-detect if chip name wasn't specified or not found */ + if (!info) +- info = spi_nor_read_id(nor); ++ info = spi_nor_read_id(nor, mode); + if (IS_ERR_OR_NULL(info)) + return -ENOENT; + +@@ -1155,7 +1230,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + if (name && info->id_len) { + const struct flash_info *jinfo; + +- jinfo = spi_nor_read_id(nor); ++ jinfo = spi_nor_read_id(nor, mode); + if (IS_ERR(jinfo)) { + return PTR_ERR(jinfo); + } else if (jinfo != info) { +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index 62356d5..53932c8 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -75,8 +75,9 @@ + #define SPINOR_OP_BRWR 0x17 /* Bank register write */ + + /* Used for Micron flashes only. */ +-#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ +-#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ ++#define SPINOR_OP_MIO_RDID 0xaf /* Multiple I/O Read JEDEC ID */ ++#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ ++#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ + + /* Status Register bits. */ + #define SR_WIP BIT(0) /* Write in progress */ +@@ -105,6 +106,43 @@ enum read_mode { + SPI_NOR_QUAD, + }; + ++ ++#define SNOR_PROTO_CMD_OFF 8 ++#define SNOR_PROTO_CMD_MASK GENMASK(11, 8) ++#define SNOR_PROTO_CMD_TO_PROTO(cmd) \ ++ (((cmd) << SNOR_PROTO_CMD_OFF) & SNOR_PROTO_CMD_MASK) ++#define SNOR_PROTO_CMD_FROM_PROTO(proto) \ ++ ((((u32)(proto)) & SNOR_PROTO_CMD_MASK) >> SNOR_PROTO_CMD_OFF) ++ ++#define SNOR_PROTO_ADDR_OFF 4 ++#define SNOR_PROTO_ADDR_MASK GENMASK(7, 4) ++#define SNOR_PROTO_ADDR_TO_PROTO(addr) \ ++ (((addr) << SNOR_PROTO_ADDR_OFF) & SNOR_PROTO_ADDR_MASK) ++#define SNOR_PROTO_ADDR_FROM_PROTO(proto) \ ++ ((((u32)(proto)) & SNOR_PROTO_ADDR_MASK) >> SNOR_PROTO_ADDR_OFF) ++ ++#define SNOR_PROTO_DATA_OFF 0 ++#define SNOR_PROTO_DATA_MASK GENMASK(3, 0) ++#define SNOR_PROTO_DATA_TO_PROTO(data) \ ++ (((data) << SNOR_PROTO_DATA_OFF) & SNOR_PROTO_DATA_MASK) ++#define SNOR_PROTO_DATA_FROM_PROTO(proto) \ ++ ((((u32)(proto)) & SNOR_PROTO_DATA_MASK) >> SNOR_PROTO_DATA_OFF) ++ ++#define SNOR_PROTO(cmd, addr, data) \ ++ (SNOR_PROTO_CMD_TO_PROTO(cmd) | \ ++ SNOR_PROTO_ADDR_TO_PROTO(addr) | \ ++ SNOR_PROTO_DATA_TO_PROTO(data)) ++ ++enum spi_nor_protocol { ++ SNOR_PROTO_1_1_1 = SNOR_PROTO(1, 1, 1), /* SPI */ ++ SNOR_PROTO_1_1_2 = SNOR_PROTO(1, 1, 2), /* Dual Output */ ++ SNOR_PROTO_1_1_4 = SNOR_PROTO(1, 1, 4), /* Quad Output */ ++ SNOR_PROTO_1_2_2 = SNOR_PROTO(1, 2, 2), /* Dual IO */ ++ SNOR_PROTO_1_4_4 = SNOR_PROTO(1, 4, 4), /* Quad IO */ ++ SNOR_PROTO_2_2_2 = SNOR_PROTO(2, 2, 2), /* Dual Command */ ++ SNOR_PROTO_4_4_4 = SNOR_PROTO(4, 4, 4), /* Quad Command */ ++}; ++ + #define SPI_NOR_MAX_CMD_SIZE 8 + enum spi_nor_ops { + SPI_NOR_OPS_READ = 0, +@@ -132,6 +170,10 @@ enum spi_nor_option_flags { + * @flash_read: the mode of the read + * @sst_write_second: used by the SST write operation + * @flags: flag options for the current SPI-NOR (SNOR_F_*) ++ * @erase_proto: the SPI protocol used by erase operations ++ * @read_proto: the SPI protocol used by read operations ++ * @write_proto: the SPI protocol used by write operations ++ * @reg_proto the SPI protocol used by read_reg/write_reg operations + * @cmd_buf: used by the write_reg + * @prepare: [OPTIONAL] do some preparations for the + * read/write/erase/lock/unlock operations +@@ -160,6 +202,10 @@ struct spi_nor { + u8 read_opcode; + u8 read_dummy; + u8 program_opcode; ++ enum spi_nor_protocol erase_proto; ++ enum spi_nor_protocol read_proto; ++ enum spi_nor_protocol write_proto; ++ enum spi_nor_protocol reg_proto; + enum read_mode flash_read; + bool sst_write_second; + u32 flags; +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0018-mtd-spi-nor-select-op-codes-and-SPI-NOR-protocols-by.patch b/target/linux/socfpga/patches-4.4/0018-mtd-spi-nor-select-op-codes-and-SPI-NOR-protocols-by.patch new file mode 100644 index 0000000000..5adef1eb80 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0018-mtd-spi-nor-select-op-codes-and-SPI-NOR-protocols-by.patch @@ -0,0 +1,203 @@ +From a27dc343ea2de9283ca057fbcafa12a279a19b7b Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:02:15 +0100 +Subject: [PATCH 18/33] mtd: spi-nor: select op codes and SPI NOR protocols by + manufacturer + +This is a transitional patch to prepare the split by Manufacturer of the +support of Single/Dual/Quad SPI modes. + +Indeed every QSPI NOR manufacturer (Spansion, Micron, Macronix, Winbond) +supports Dual or Quad SPI modes on its way. Especially the Fast Read op +code and the SPI NOR protocols to use are not quite the same depending on +the manufacturer. + +For instance Quad commands can use either the SPI 1-1-4, 1-4-4 or 4-4-4 +protocol. + +This is why this patch will be completed by fixes for each manufacturer. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 110 ++++++++++++++++++++++++++++++++---------- + include/linux/mtd/spi-nor.h | 12 +++-- + 2 files changed, 92 insertions(+), 30 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 9ad2d40..889af12 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1172,17 +1172,61 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + dev_err(nor->dev, "Macronix quad-read not enabled\n"); + return -EINVAL; + } +- return status; ++ /* Check whether Macronix QPI mode is enabled. */ ++ if (nor->read_proto != SNOR_PROTO_4_4_4) ++ nor->read_proto = SNOR_PROTO_1_1_4; ++ break; ++ + case SNOR_MFR_MICRON: +- return 0; +- default: ++ /* Check whether Micron Quad mode is enabled. */ ++ if (nor->read_proto != SNOR_PROTO_4_4_4) ++ nor->read_proto = SNOR_PROTO_1_1_4; ++ break; ++ ++ case SNOR_MFR_SPANSION: + status = spansion_quad_enable(nor); + if (status) { + dev_err(nor->dev, "Spansion quad-read not enabled\n"); + return -EINVAL; + } +- return status; ++ nor->read_proto = SNOR_PROTO_1_1_4; ++ break; ++ ++ default: ++ return -EINVAL; + } ++ ++ nor->read_opcode = SPINOR_OP_READ_1_1_4; ++ return 0; ++} ++ ++static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info) ++{ ++ switch (JEDEC_MFR(info)) { ++ case SNOR_MFR_MICRON: ++ /* Check whether Micron Dual mode is enabled. */ ++ if (nor->read_proto != SNOR_PROTO_2_2_2) ++ nor->read_proto = SNOR_PROTO_1_1_2; ++ break; ++ ++ default: ++ nor->read_proto = SNOR_PROTO_1_1_2; ++ break; ++ } ++ ++ nor->read_opcode = SPINOR_OP_READ_1_1_2; ++ return 0; ++} ++ ++static int set_single_mode(struct spi_nor *nor, const struct flash_info *info) ++{ ++ switch (JEDEC_MFR(info)) { ++ default: ++ nor->read_proto = SNOR_PROTO_1_1_1; ++ break; ++ } ++ ++ return 0; + } + + static int spi_nor_check(struct spi_nor *nor) +@@ -1330,7 +1374,30 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + if (info->flags & SPI_NOR_NO_FR) + nor->flash_read = SPI_NOR_NORMAL; + +- /* Quad/Dual-read mode takes precedence over fast/normal */ ++ /* Default commands */ ++ if (nor->flash_read == SPI_NOR_NORMAL) ++ nor->read_opcode = SPINOR_OP_READ; ++ else ++ nor->read_opcode = SPINOR_OP_READ_FAST; ++ ++ nor->program_opcode = SPINOR_OP_PP; ++ ++ /* ++ * Quad/Dual-read mode takes precedence over fast/normal. ++ * ++ * Manufacturer specific modes are discovered when reading the JEDEC ID ++ * and are reported by the nor->read_proto value: ++ * - SNOR_PROTO_4_4_4 is either: ++ * + Micron Quad mode enabled ++ * + Macronix/Winbond QPI mode enabled ++ * - SNOR_PROTO_2_2_2 is either: ++ * + Micron Dual mode enabled ++ * ++ * The opcodes and the protocols are updated depending on the ++ * manufacturer. ++ * The read opcode and protocol should be updated by the relevant ++ * function when entering Quad or Dual mode. ++ */ + if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) { + ret = set_quad_mode(nor, info); + if (ret) { +@@ -1339,30 +1406,21 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + } + nor->flash_read = SPI_NOR_QUAD; + } else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) { ++ ret = set_dual_mode(nor, info); ++ if (ret) { ++ dev_err(dev, "dual mode not supported\n"); ++ return ret; ++ } + nor->flash_read = SPI_NOR_DUAL; ++ } else if (info->flags & (SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)) { ++ /* We may need to leave a Quad or Dual mode */ ++ ret = set_single_mode(nor, info); ++ if (ret) { ++ dev_err(dev, "failed to switch back to single mode\n"); ++ return ret; ++ } + } + +- /* Default commands */ +- switch (nor->flash_read) { +- case SPI_NOR_QUAD: +- nor->read_opcode = SPINOR_OP_READ_1_1_4; +- break; +- case SPI_NOR_DUAL: +- nor->read_opcode = SPINOR_OP_READ_1_1_2; +- break; +- case SPI_NOR_FAST: +- nor->read_opcode = SPINOR_OP_READ_FAST; +- break; +- case SPI_NOR_NORMAL: +- nor->read_opcode = SPINOR_OP_READ; +- break; +- default: +- dev_err(dev, "No Read opcode defined\n"); +- return -EINVAL; +- } +- +- nor->program_opcode = SPINOR_OP_PP; +- + if (info->addr_width) + nor->addr_width = info->addr_width; + else if (mtd->size > 0x1000000) { +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index 53932c8..89e3228 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -42,8 +42,10 @@ + #define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */ + #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_1_1_2 0x3b /* Read data bytes (Dual SPI) */ +-#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */ ++#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output 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_BE_4K 0x20 /* Erase 4KiB block */ + #define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ +@@ -57,8 +59,10 @@ + /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ + #define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */ + #define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */ +-#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */ +-#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */ ++#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual Output SPI) */ ++#define SPINOR_OP_READ4_1_2_2 0xbc /* Read data bytes (Dual I/O SPI) */ ++#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad Output SPI) */ ++#define SPINOR_OP_READ4_1_4_4 0xec /* Read data bytes (Quad I/O SPI) */ + #define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */ + #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */ + +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0019-mtd-spi-nor-fix-support-of-Macronix-memories.patch b/target/linux/socfpga/patches-4.4/0019-mtd-spi-nor-fix-support-of-Macronix-memories.patch new file mode 100644 index 0000000000..087b6715d0 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0019-mtd-spi-nor-fix-support-of-Macronix-memories.patch @@ -0,0 +1,139 @@ +From 4e094634d1995e279f8bc5eb92463295cb894c76 Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:02:16 +0100 +Subject: [PATCH 19/33] mtd: spi-nor: fix support of Macronix memories + +This patch fixes the support of Macronix memories. Especially we avoid +updating the Status Register when not needed as the Quad Enable (QE) bit +is a non-volatile bit. + +Also we add comments to explain why we use some Fast Read op codes rather +than others. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 81 ++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 72 insertions(+), 9 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 889af12..1b1f5a6 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1107,6 +1107,11 @@ static int macronix_quad_enable(struct spi_nor *nor) + val = read_sr(nor); + if (val < 0) + return val; ++ ++ if (likely(val & SR_QUAD_EN_MX)) ++ return 0; ++ dev_warn(nor->dev, "Macronix Quad mode disabled, enable it\n"); ++ + write_enable(nor); + + write_sr(nor, val | SR_QUAD_EN_MX); +@@ -1161,21 +1166,73 @@ static int spansion_quad_enable(struct spi_nor *nor) + return 0; + } + ++static int macronix_set_quad_mode(struct spi_nor *nor) ++{ ++ int status; ++ ++ /* Check whether the QPI mode is enabled. */ ++ if (nor->read_proto == SNOR_PROTO_4_4_4) { ++ /* ++ * Since the QPI mode is enabled, the Quad Enabled (QE) ++ * non-volatile bit is already set. ++ * However in QPI mode, only the Fast Read 1-4-4 (0xeb) ++ * op code is supported. ++ * WARNING: we should take care about the performance ++ * enhance toggling bits P0-P7 written during the ++ * dummy/mode cycles to avoid entering the continuous ++ * read (performance enhance) mode by mistake! ++ */ ++ nor->read_opcode = SPINOR_OP_READ_1_4_4; ++ return 0; ++ } ++ ++ /* ++ * The QPI mode is disabled but we still need to set the QE bit: ++ * this disables the reset and write protect features and ++ * frees the associated pins so they can be used as the 3rd ++ * and 4th I/O lines required by Quad SPI commands. ++ * Also we'd rather use the Fast Read 1-1-4 (0x6b) op code than ++ * the Fast Read 1-4-4 (0xeb) op code so we don't care about ++ * entering the continuous read mode by mistake if some ++ * performance enhance toggling bits P0-P7 were written during ++ * dummy/mode cycles. ++ */ ++ status = macronix_quad_enable(nor); ++ if (status) { ++ dev_err(nor->dev, "Macronix quad-read not enabled\n"); ++ return status; ++ } ++ nor->read_proto = SNOR_PROTO_1_1_4; ++ nor->read_opcode = SPINOR_OP_READ_1_1_4; ++ return 0; ++} ++ ++/* ++ * For both Macronix Dual and Single modes, we don't care about the value of ++ * the Quad Enabled (QE) bit since the memory still replies to Dual or Single ++ * SPI commands. ++ */ ++ ++static int macronix_set_dual_mode(struct spi_nor *nor) ++{ ++ nor->read_proto = SNOR_PROTO_1_1_2; ++ nor->read_opcode = SPINOR_OP_READ_1_1_2; ++ return 0; ++} ++ ++static int macronix_set_single_mode(struct spi_nor *nor) ++{ ++ nor->read_proto = SNOR_PROTO_1_1_1; ++ return 0; ++} ++ + static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + { + int status; + + switch (JEDEC_MFR(info)) { + case SNOR_MFR_MACRONIX: +- status = macronix_quad_enable(nor); +- if (status) { +- dev_err(nor->dev, "Macronix quad-read not enabled\n"); +- return -EINVAL; +- } +- /* Check whether Macronix QPI mode is enabled. */ +- if (nor->read_proto != SNOR_PROTO_4_4_4) +- nor->read_proto = SNOR_PROTO_1_1_4; +- break; ++ return macronix_set_quad_mode(nor); + + case SNOR_MFR_MICRON: + /* Check whether Micron Quad mode is enabled. */ +@@ -1203,6 +1260,9 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info) + { + switch (JEDEC_MFR(info)) { ++ case SNOR_MFR_MACRONIX: ++ return macronix_set_dual_mode(nor); ++ + case SNOR_MFR_MICRON: + /* Check whether Micron Dual mode is enabled. */ + if (nor->read_proto != SNOR_PROTO_2_2_2) +@@ -1221,6 +1281,9 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info) + static int set_single_mode(struct spi_nor *nor, const struct flash_info *info) + { + switch (JEDEC_MFR(info)) { ++ case SNOR_MFR_MACRONIX: ++ return macronix_set_single_mode(nor); ++ + default: + nor->read_proto = SNOR_PROTO_1_1_1; + break; +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0020-mtd-spi-nor-fix-support-of-Winbond-memories.patch b/target/linux/socfpga/patches-4.4/0020-mtd-spi-nor-fix-support-of-Winbond-memories.patch new file mode 100644 index 0000000000..e41fd3fff7 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0020-mtd-spi-nor-fix-support-of-Winbond-memories.patch @@ -0,0 +1,179 @@ +From 96b232d03a0c4462eacf879ed80b0cfd235e65c4 Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:02:17 +0100 +Subject: [PATCH 20/33] mtd: spi-nor: fix support of Winbond memories + +This patch fixes the support of Winbond memories. Indeed, before +performing any Quad SPI command, the Quad Enable (QE) non-volatile bit +MUST be set in the Status Register 2. + +According to the w25q16fw datasheet from Winbond: +"When QE=1, the /WP pin becomes IO2 and /HOLD pin becomes IO3." + +Quad SPI instructions requires the bidirectional IO2 and IO3 pins. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 100 ++++++++++++++++++++++++++++++++++++++++++ + include/linux/mtd/spi-nor.h | 6 +++ + 2 files changed, 106 insertions(+) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 1b1f5a6..aa7d26d 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1166,6 +1166,40 @@ static int spansion_quad_enable(struct spi_nor *nor) + return 0; + } + ++static int winbond_quad_enable(struct spi_nor *nor) ++{ ++ int ret; ++ u8 sr2; ++ ++ ret = nor->read_reg(nor, SPINOR_OP_RDSR2_WINB, &sr2, 1); ++ if (ret < 0) ++ return ret; ++ ++ if (likely(sr2 & SR2_QUAD_EN_WINB)) ++ return 0; ++ dev_warn(nor->dev, "Winbond Quad mode disabled, enable it\n"); ++ ++ write_enable(nor); ++ ++ sr2 |= SR2_QUAD_EN_WINB; ++ ret = nor->write_reg(nor, SPINOR_OP_WRSR2_WINB, &sr2, 1); ++ if (ret < 0) ++ return ret; ++ ++ if (spi_nor_wait_till_ready(nor)) ++ return -EIO; ++ ++ ret = nor->read_reg(nor, SPINOR_OP_RDSR2_WINB, &sr2, 1); ++ if (ret < 0) ++ return ret; ++ if (!(sr2 & SR2_QUAD_EN_WINB)) { ++ dev_err(nor->dev, "Winbond Quad bit not set\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + static int macronix_set_quad_mode(struct spi_nor *nor) + { + int status; +@@ -1226,6 +1260,63 @@ static int macronix_set_single_mode(struct spi_nor *nor) + return 0; + } + ++static int winbond_set_quad_mode(struct spi_nor *nor) ++{ ++ int status; ++ ++ /* Check whether the QPI mode is enabled. */ ++ if (nor->read_proto == SNOR_PROTO_4_4_4) { ++ /* Since the QPI mode is enabled, the Quad Enabled (QE) ++ * non-volatile bit is already set. ++ * If the Fast Read 1-4-4 (0xeb) were used, we should ++ * take care about the value M7-M0 written during ++ * dummy/mode cycles to avoid entering the continuous ++ * read mode by mistake. ++ * Also the Fast Read 1-1-4 (0x6b) op code is not ++ * supported in QPI mode. ++ * Hence the Fast Read 1-1-1 (0x0b) op code is chosen. ++ */ ++ nor->read_opcode = SPINOR_OP_READ_FAST; ++ return 0; ++ } ++ ++ /* ++ * The QPI mode is disabled but we still need to set the QE bit: ++ * when QE=1, the /WP pin becomes IO2 and /HOLD pin becomes IO3. ++ * If the Fast Read 1-4-4 (0xeb) were used, we should take care ++ * about the value M7-M0 written during dummy/mode cycles to ++ * avoid entering the continuous read mode by mistake. ++ * Hence the Fast Read 1-1-4 (0x6b) op code is preferred. ++ */ ++ status = winbond_quad_enable(nor); ++ if (status) { ++ dev_err(nor->dev, "Winbond quad-read nor enabled\n"); ++ return status; ++ } ++ nor->read_proto = SNOR_PROTO_1_1_4; ++ nor->read_opcode = SPINOR_OP_READ_1_1_4; ++ return 0; ++} ++ ++/* ++ * For both Winbond Dual and Single modes, we don't care about the value of ++ * the Quad Enabled (QE) bit since the memory still replies to Dual or Single ++ * SPI commands. ++ */ ++ ++static int winbond_set_dual_mode(struct spi_nor *nor) ++{ ++ nor->read_proto = SNOR_PROTO_1_1_2; ++ nor->read_opcode = SPINOR_OP_READ_1_1_2; ++ return 0; ++} ++ ++static int winbond_set_single_mode(struct spi_nor *nor) ++{ ++ nor->read_proto = SNOR_PROTO_1_1_1; ++ return 0; ++} ++ + static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + { + int status; +@@ -1234,6 +1325,9 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + case SNOR_MFR_MACRONIX: + return macronix_set_quad_mode(nor); + ++ case SNOR_MFR_WINBOND: ++ return winbond_set_quad_mode(nor); ++ + case SNOR_MFR_MICRON: + /* Check whether Micron Quad mode is enabled. */ + if (nor->read_proto != SNOR_PROTO_4_4_4) +@@ -1263,6 +1357,9 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info) + case SNOR_MFR_MACRONIX: + return macronix_set_dual_mode(nor); + ++ case SNOR_MFR_WINBOND: ++ return winbond_set_dual_mode(nor); ++ + case SNOR_MFR_MICRON: + /* Check whether Micron Dual mode is enabled. */ + if (nor->read_proto != SNOR_PROTO_2_2_2) +@@ -1284,6 +1381,9 @@ static int set_single_mode(struct spi_nor *nor, const struct flash_info *info) + case SNOR_MFR_MACRONIX: + return macronix_set_single_mode(nor); + ++ case SNOR_MFR_WINBOND: ++ return winbond_set_single_mode(nor); ++ + default: + nor->read_proto = SNOR_PROTO_1_1_1; + break; +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index 89e3228..46343f5 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -75,6 +75,12 @@ + #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ + #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ + ++/* Used for Winbond flashes only. */ ++#define SPINOR_OP_RDSR2_WINB 0x35 /* Read status register 2 */ ++#define SPINOR_OP_WRSR2_WINB 0x31 /* Write status register 2 */ ++ ++#define SR2_QUAD_EN_WINB BIT(1) /* Quad Enable bit */ ++ + /* Used for Spansion flashes only. */ + #define SPINOR_OP_BRWR 0x17 /* Bank register write */ + +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0021-mtd-spi-nor-fix-support-of-Micron-memories.patch b/target/linux/socfpga/patches-4.4/0021-mtd-spi-nor-fix-support-of-Micron-memories.patch new file mode 100644 index 0000000000..83d79ab90d --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0021-mtd-spi-nor-fix-support-of-Micron-memories.patch @@ -0,0 +1,224 @@ +From 0cd0df6b3583920ab9231035e533560b58e71a50 Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:02:18 +0100 +Subject: [PATCH 21/33] mtd: spi-nor: fix support of Micron memories + +This patch adds missing mode transitions. Indeed depending on both the +current memory mode and the new protocol wanted by the user, we may need +to perform a switch back to the Extended SPI mode. + +However when the current mode is the Quad mode and the user has asked for +a Quad SPI protocol, we'd rather stay in Quad mode and use the Fast Read +4-4-4 command than switch to the Extended SPI mode and use the Fast Read +1-1-4 command. + +Also we'd rather stay in Dual mode than swith to the Extended SPI mode +whenever the user has asked for Dual SPI protocol. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 154 +++++++++++++++++++++++++++++++++++++++--- + include/linux/mtd/spi-nor.h | 1 + + 2 files changed, 147 insertions(+), 8 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index aa7d26d..ae2cbac 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1317,6 +1317,147 @@ static int winbond_set_single_mode(struct spi_nor *nor) + return 0; + } + ++static int micron_set_protocol(struct spi_nor *nor, u8 mask, u8 val, ++ enum spi_nor_protocol proto) ++{ ++ u8 evcr; ++ int ret; ++ ++ /* Read the Enhanced Volatile Configuration Register (EVCR). */ ++ ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &evcr, 1); ++ if (ret < 0) { ++ dev_err(nor->dev, "error while reading EVCR register\n"); ++ return ret; ++ } ++ ++ /* Check whether we need to update the protocol bits. */ ++ if ((evcr & mask) == val) ++ return 0; ++ ++ /* Set EVCR protocol bits. */ ++ write_enable(nor); ++ evcr = (evcr & ~mask) | val; ++ ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, &evcr, 1); ++ if (ret < 0) { ++ dev_err(nor->dev, "error while writing EVCR register\n"); ++ return ret; ++ } ++ ++ /* Switch reg protocol now before accessing any other registers. */ ++ nor->reg_proto = proto; ++ ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ return ret; ++ ++ /* Read EVCR and check it. */ ++ ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &evcr, 1); ++ if (ret < 0 || (evcr & mask) != val) { ++ dev_err(nor->dev, "Micron EVCR protocol bits not updated\n"); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int micron_set_extended_spi_protocol(struct spi_nor *nor) ++{ ++ int ret; ++ ++ /* Set Quad/Dual bits to 11 to select the Extended SPI mode */ ++ ret = micron_set_protocol(nor, ++ EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON, ++ EVCR_QUAD_EN_MICRON | EVCR_DUAL_EN_MICRON, ++ SNOR_PROTO_1_1_1); ++ if (ret) { ++ dev_err(nor->dev, "Failed to set Micron Extended SPI mode\n"); ++ return ret; ++ } ++ ++ nor->write_proto = SNOR_PROTO_1_1_1; ++ nor->erase_proto = SNOR_PROTO_1_1_1; ++ return 0; ++} ++ ++static int micron_set_quad_mode(struct spi_nor *nor) ++{ ++ /* Check whether the Dual SPI mode is enabled. */ ++ if (unlikely(nor->read_proto == SNOR_PROTO_2_2_2)) { ++ int ret; ++ ++ /* ++ * Exit Micron Dual mode and switch to the Extended SPI mode: ++ * we can change the mode safely as we write into a volatile ++ * register. ++ * Also the Quad mode is not worth it for MTD usages: it ++ * should only be relevant for eXecution In Place (XIP) usages, ++ * which are out of the scope of the spi-nor framework. ++ */ ++ ret = micron_set_extended_spi_protocol(nor); ++ if (ret) ++ return ret; ++ } ++ ++ /* ++ * Whatever the Quad mode is enabled or not, the ++ * Fast Read Quad Output 1-1-4 (0x6b) op code is supported. ++ */ ++ if (nor->read_proto != SNOR_PROTO_4_4_4) ++ nor->read_proto = SNOR_PROTO_1_1_4; ++ nor->read_opcode = SPINOR_OP_READ_1_1_4; ++ return 0; ++} ++ ++static int micron_set_dual_mode(struct spi_nor *nor) ++{ ++ /* Check whether Quad mode is enabled. */ ++ if (unlikely(nor->read_proto == SNOR_PROTO_4_4_4)) { ++ int ret; ++ ++ /* ++ * Exit Micron Quad mode and switch to the Extended SPI mode: ++ * we can change the mode safely as we write into a volatile ++ * register. ++ * Also the Dual mode is not worth it for MTD usages: it ++ * should only be relevant for eXecution In Place (XIP) usages, ++ * which are out of the scope of the spi-nor framework. ++ */ ++ ret = micron_set_extended_spi_protocol(nor); ++ if (ret) ++ return ret; ++ } ++ ++ /* ++ * Whatever the Dual mode is enabled or not, the ++ * Fast Read Dual Output 1-1-2 (0x3b) op code is supported. ++ */ ++ if (nor->read_proto != SNOR_PROTO_2_2_2) ++ nor->read_proto = SNOR_PROTO_1_1_2; ++ nor->read_opcode = SPINOR_OP_READ_1_1_2; ++ return 0; ++} ++ ++static int micron_set_single_mode(struct spi_nor *nor) ++{ ++ /* Check whether either the Dual or Quad mode is enabled. */ ++ if (unlikely(nor->read_proto != SNOR_PROTO_1_1_1)) { ++ int ret; ++ ++ /* ++ * Exit Micron Dual or Quad mode and switch to the Extended SPI ++ * mode: we can change the mode safely as we write into a ++ * volatile register. ++ */ ++ ret = micron_set_extended_spi_protocol(nor); ++ if (ret) ++ return ret; ++ ++ nor->read_proto = SNOR_PROTO_1_1_1; ++ } ++ ++ return 0; ++} ++ + static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + { + int status; +@@ -1329,10 +1470,7 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + return winbond_set_quad_mode(nor); + + case SNOR_MFR_MICRON: +- /* Check whether Micron Quad mode is enabled. */ +- if (nor->read_proto != SNOR_PROTO_4_4_4) +- nor->read_proto = SNOR_PROTO_1_1_4; +- break; ++ return micron_set_quad_mode(nor); + + case SNOR_MFR_SPANSION: + status = spansion_quad_enable(nor); +@@ -1361,10 +1499,7 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info) + return winbond_set_dual_mode(nor); + + case SNOR_MFR_MICRON: +- /* Check whether Micron Dual mode is enabled. */ +- if (nor->read_proto != SNOR_PROTO_2_2_2) +- nor->read_proto = SNOR_PROTO_1_1_2; +- break; ++ return micron_set_dual_mode(nor); + + default: + nor->read_proto = SNOR_PROTO_1_1_2; +@@ -1384,6 +1519,9 @@ static int set_single_mode(struct spi_nor *nor, const struct flash_info *info) + case SNOR_MFR_WINBOND: + return winbond_set_single_mode(nor); + ++ case SNOR_MFR_MICRON: ++ return micron_set_single_mode(nor); ++ + default: + nor->read_proto = SNOR_PROTO_1_1_1; + break; +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index 46343f5..d0a6f34 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -102,6 +102,7 @@ + + /* Enhanced Volatile Configuration Register bits */ + #define EVCR_QUAD_EN_MICRON BIT(7) /* Micron Quad I/O */ ++#define EVCR_DUAL_EN_MICRON BIT(6) /* Micron Dual I/O */ + + /* Flag Status Register bits */ + #define FSR_READY BIT(7) +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0022-mtd-spi-nor-fix-support-of-Spansion-memories.patch b/target/linux/socfpga/patches-4.4/0022-mtd-spi-nor-fix-support-of-Spansion-memories.patch new file mode 100644 index 0000000000..a7a8f4bbc5 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0022-mtd-spi-nor-fix-support-of-Spansion-memories.patch @@ -0,0 +1,116 @@ +From 4774693a681539f1e890164acc2d99fede2aa35e Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:02:19 +0100 +Subject: [PATCH 22/33] mtd: spi-nor: fix support of Spansion memories + +This patch is only a transitional one. It concludes the series of patches +to select op codes and protocols by manufacturer. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 53 ++++++++++++++++++++++++++++++------------- + 1 file changed, 37 insertions(+), 16 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index ae2cbac..8a042ab 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1458,10 +1458,35 @@ static int micron_set_single_mode(struct spi_nor *nor) + return 0; + } + +-static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) ++static int spansion_set_quad_mode(struct spi_nor *nor) + { + int status; + ++ status = spansion_quad_enable(nor); ++ if (status) { ++ dev_err(nor->dev, "Spansion quad-read not enabled\n"); ++ return -EINVAL; ++ } ++ nor->read_proto = SNOR_PROTO_1_1_4; ++ nor->read_opcode = SPINOR_OP_READ_1_1_4; ++ return 0; ++} ++ ++static int spansion_set_dual_mode(struct spi_nor *nor) ++{ ++ nor->read_proto = SNOR_PROTO_1_1_2; ++ nor->read_opcode = SPINOR_OP_READ_1_1_2; ++ return 0; ++} ++ ++static int spansion_set_single_mode(struct spi_nor *nor) ++{ ++ nor->read_proto = SNOR_PROTO_1_1_1; ++ return 0; ++} ++ ++static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) ++{ + switch (JEDEC_MFR(info)) { + case SNOR_MFR_MACRONIX: + return macronix_set_quad_mode(nor); +@@ -1473,20 +1498,13 @@ static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) + return micron_set_quad_mode(nor); + + case SNOR_MFR_SPANSION: +- status = spansion_quad_enable(nor); +- if (status) { +- dev_err(nor->dev, "Spansion quad-read not enabled\n"); +- return -EINVAL; +- } +- nor->read_proto = SNOR_PROTO_1_1_4; +- break; ++ return spansion_set_quad_mode(nor); + + default: +- return -EINVAL; ++ break; + } + +- nor->read_opcode = SPINOR_OP_READ_1_1_4; +- return 0; ++ return -EINVAL; + } + + static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info) +@@ -1501,13 +1519,14 @@ static int set_dual_mode(struct spi_nor *nor, const struct flash_info *info) + case SNOR_MFR_MICRON: + return micron_set_dual_mode(nor); + ++ case SNOR_MFR_SPANSION: ++ return spansion_set_dual_mode(nor); ++ + default: +- nor->read_proto = SNOR_PROTO_1_1_2; + break; + } + +- nor->read_opcode = SPINOR_OP_READ_1_1_2; +- return 0; ++ return -EINVAL; + } + + static int set_single_mode(struct spi_nor *nor, const struct flash_info *info) +@@ -1522,12 +1541,14 @@ static int set_single_mode(struct spi_nor *nor, const struct flash_info *info) + case SNOR_MFR_MICRON: + return micron_set_single_mode(nor); + ++ case SNOR_MFR_SPANSION: ++ return spansion_set_single_mode(nor); ++ + default: +- nor->read_proto = SNOR_PROTO_1_1_1; + break; + } + +- return 0; ++ return -EINVAL; + } + + static int spi_nor_check(struct spi_nor *nor) +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0023-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch b/target/linux/socfpga/patches-4.4/0023-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch new file mode 100644 index 0000000000..7153a9f171 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0023-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch @@ -0,0 +1,264 @@ +From 16410a33d6655d6c85c8c522bc6f2cfebf7c06a4 Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:02:20 +0100 +Subject: [PATCH 23/33] mtd: spi-nor: configure the number of dummy clock + cycles by manufacturer + +This is a transitional patch which let us set the number of dummy clock +cycles by manufacturer. + +More patches will follow by manufacturer to actually configure the +relevant number of dummy clock cycles following the dedicated procedure. + +For instance, some manufacturers like Spansion configure the number of +dummy clock cycles to be used by Fast Read command through some +non-volatile register. In such a case, we should avoid updating its value +but instead read it then set the nor->read_dummy accordingly. + +On the other hand, some manufacturers like Micron use some volatile +register. In this case, we'd rather update this register to use a number +of dummy clock cycles, which is a multiple of 8. +Indeed some drivers, like m25p80, only support writing bytes, hence +multiples of 8 bits. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 99 ++++++++++++++++++++++++++++++++----------- + 1 file changed, 74 insertions(+), 25 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 8a042ab..ae3e9d8 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -139,24 +139,6 @@ static int read_cr(struct spi_nor *nor) + } + + /* +- * Dummy Cycle calculation for different type of read. +- * It can be used to support more commands with +- * different dummy cycle requirements. +- */ +-static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor) +-{ +- switch (nor->flash_read) { +- case SPI_NOR_FAST: +- case SPI_NOR_DUAL: +- case SPI_NOR_QUAD: +- return 8; +- case SPI_NOR_NORMAL: +- return 0; +- } +- return 0; +-} +- +-/* + * Write status register 1 byte + * Returns negative if error occurred. + */ +@@ -1217,6 +1199,7 @@ static int macronix_set_quad_mode(struct spi_nor *nor) + * read (performance enhance) mode by mistake! + */ + nor->read_opcode = SPINOR_OP_READ_1_4_4; ++ nor->read_dummy = 8; + return 0; + } + +@@ -1238,6 +1221,7 @@ static int macronix_set_quad_mode(struct spi_nor *nor) + } + nor->read_proto = SNOR_PROTO_1_1_4; + nor->read_opcode = SPINOR_OP_READ_1_1_4; ++ nor->read_dummy = 8; + return 0; + } + +@@ -1251,12 +1235,27 @@ static int macronix_set_dual_mode(struct spi_nor *nor) + { + nor->read_proto = SNOR_PROTO_1_1_2; + nor->read_opcode = SPINOR_OP_READ_1_1_2; ++ nor->read_dummy = 8; + return 0; + } + + static int macronix_set_single_mode(struct spi_nor *nor) + { ++ u8 read_dummy; ++ ++ switch (nor->read_opcode) { ++ case SPINOR_OP_READ: ++ case SPINOR_OP_READ4: ++ read_dummy = 0; ++ break; ++ ++ default: ++ read_dummy = 8; ++ break; ++ } ++ + nor->read_proto = SNOR_PROTO_1_1_1; ++ nor->read_dummy = read_dummy; + return 0; + } + +@@ -1277,6 +1276,7 @@ static int winbond_set_quad_mode(struct spi_nor *nor) + * Hence the Fast Read 1-1-1 (0x0b) op code is chosen. + */ + nor->read_opcode = SPINOR_OP_READ_FAST; ++ nor->read_dummy = 8; + return 0; + } + +@@ -1295,6 +1295,7 @@ static int winbond_set_quad_mode(struct spi_nor *nor) + } + nor->read_proto = SNOR_PROTO_1_1_4; + nor->read_opcode = SPINOR_OP_READ_1_1_4; ++ nor->read_dummy = 8; + return 0; + } + +@@ -1308,12 +1309,27 @@ static int winbond_set_dual_mode(struct spi_nor *nor) + { + nor->read_proto = SNOR_PROTO_1_1_2; + nor->read_opcode = SPINOR_OP_READ_1_1_2; ++ nor->read_dummy = 8; + return 0; + } + + static int winbond_set_single_mode(struct spi_nor *nor) + { ++ u8 read_dummy; ++ ++ switch (nor->read_opcode) { ++ case SPINOR_OP_READ: ++ case SPINOR_OP_READ4: ++ read_dummy = 0; ++ break; ++ ++ default: ++ read_dummy = 8; ++ break; ++ } ++ + nor->read_proto = SNOR_PROTO_1_1_1; ++ nor->read_dummy = read_dummy; + return 0; + } + +@@ -1405,6 +1421,7 @@ static int micron_set_quad_mode(struct spi_nor *nor) + if (nor->read_proto != SNOR_PROTO_4_4_4) + nor->read_proto = SNOR_PROTO_1_1_4; + nor->read_opcode = SPINOR_OP_READ_1_1_4; ++ nor->read_dummy = 8; + return 0; + } + +@@ -1434,11 +1451,14 @@ static int micron_set_dual_mode(struct spi_nor *nor) + if (nor->read_proto != SNOR_PROTO_2_2_2) + nor->read_proto = SNOR_PROTO_1_1_2; + nor->read_opcode = SPINOR_OP_READ_1_1_2; ++ nor->read_dummy = 8; + return 0; + } + + static int micron_set_single_mode(struct spi_nor *nor) + { ++ u8 read_dummy; ++ + /* Check whether either the Dual or Quad mode is enabled. */ + if (unlikely(nor->read_proto != SNOR_PROTO_1_1_1)) { + int ret; +@@ -1455,6 +1475,18 @@ static int micron_set_single_mode(struct spi_nor *nor) + nor->read_proto = SNOR_PROTO_1_1_1; + } + ++ /* Force the number of dummy cycles to 8 for Fast Read, 0 for Read. */ ++ switch (nor->read_opcode) { ++ case SPINOR_OP_READ: ++ case SPINOR_OP_READ4: ++ read_dummy = 0; ++ break; ++ ++ default: ++ read_dummy = 8; ++ break; ++ } ++ nor->read_dummy = read_dummy; + return 0; + } + +@@ -1469,6 +1501,7 @@ static int spansion_set_quad_mode(struct spi_nor *nor) + } + nor->read_proto = SNOR_PROTO_1_1_4; + nor->read_opcode = SPINOR_OP_READ_1_1_4; ++ nor->read_dummy = 8; + return 0; + } + +@@ -1476,12 +1509,27 @@ static int spansion_set_dual_mode(struct spi_nor *nor) + { + nor->read_proto = SNOR_PROTO_1_1_2; + nor->read_opcode = SPINOR_OP_READ_1_1_2; ++ nor->read_dummy = 8; + return 0; + } + + static int spansion_set_single_mode(struct spi_nor *nor) + { ++ u8 read_dummy; ++ ++ switch (nor->read_opcode) { ++ case SPINOR_OP_READ: ++ case SPINOR_OP_READ4: ++ read_dummy = 0; ++ break; ++ ++ default: ++ read_dummy = 8; ++ break; ++ } ++ + nor->read_proto = SNOR_PROTO_1_1_1; ++ nor->read_dummy = read_dummy; + return 0; + } + +@@ -1696,11 +1744,14 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + if (info->flags & SPI_NOR_NO_FR) + nor->flash_read = SPI_NOR_NORMAL; + +- /* Default commands */ +- if (nor->flash_read == SPI_NOR_NORMAL) ++ /* Default commands and number of dummy cycles */ ++ if (nor->flash_read == SPI_NOR_NORMAL) { + nor->read_opcode = SPINOR_OP_READ; +- else ++ nor->read_dummy = 0; ++ } else { + nor->read_opcode = SPINOR_OP_READ_FAST; ++ nor->read_dummy = 8; ++ } + + nor->program_opcode = SPINOR_OP_PP; + +@@ -1715,8 +1766,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + * - SNOR_PROTO_2_2_2 is either: + * + Micron Dual mode enabled + * +- * The opcodes and the protocols are updated depending on the +- * manufacturer. ++ * The opcodes, the protocols and the number of dummy cycles are updated ++ * depending on the manufacturer. + * The read opcode and protocol should be updated by the relevant + * function when entering Quad or Dual mode. + */ +@@ -1780,8 +1831,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode) + return -EINVAL; + } + +- nor->read_dummy = spi_nor_read_dummy_cycles(nor); +- + dev_info(dev, "%s (%lld Kbytes)\n", info->name, + (long long)mtd->size >> 10); + +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0024-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch b/target/linux/socfpga/patches-4.4/0024-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch new file mode 100644 index 0000000000..c6bf87099a --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0024-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch @@ -0,0 +1,159 @@ +From 77fee227b15835d03517dc34675f72e8963ae882 Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:02:21 +0100 +Subject: [PATCH 24/33] mtd: spi-nor: configure the number of dummy clock + cycles on Micron memories + +The spi-nor framework currently expects all Fast Read operations to use 8 +dummy clock cycles. Especially some drivers like m25p80 can only support +multiple of 8 dummy clock cycles. + +On Micron memories, the number of dummy clock cycles to be used by Fast +Read commands can be safely set to 8 by updating the Volatile +Configuration Register (VCR). + +Also the XIP bit is set at the same time when updating the VCR so the +Continuous Read mode is disabled: this prevents us from entering it by +mistake. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 72 ++++++++++++++++++++++++++++++++++++++----- + include/linux/mtd/spi-nor.h | 2 ++ + 2 files changed, 67 insertions(+), 7 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index ae3e9d8..5232984 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1333,6 +1333,53 @@ static int winbond_set_single_mode(struct spi_nor *nor) + return 0; + } + ++static int micron_set_dummy_cycles(struct spi_nor *nor, u8 read_dummy) ++{ ++ u8 vcr, val, mask; ++ int ret; ++ ++ /* Set bit3 (XIP) to disable the Continuous Read mode */ ++ mask = GENMASK(7, 4) | BIT(3); ++ val = ((read_dummy << 4) | BIT(3)) & mask; ++ ++ /* Read the Volatile Configuration Register (VCR). */ ++ ret = nor->read_reg(nor, SPINOR_OP_RD_VCR, &vcr, 1); ++ if (ret < 0) { ++ dev_err(nor->dev, "error while reading VCR register\n"); ++ return ret; ++ } ++ ++ /* Check whether we need to update the number of dummy cycles. */ ++ if ((vcr & mask) == val) { ++ nor->read_dummy = read_dummy; ++ return 0; ++ } ++ ++ /* Update the number of dummy into the VCR. */ ++ write_enable(nor); ++ vcr = (vcr & ~mask) | val; ++ ret = nor->write_reg(nor, SPINOR_OP_WR_VCR, &vcr, 1); ++ if (ret < 0) { ++ dev_err(nor->dev, "error while writing VCR register\n"); ++ return ret; ++ } ++ ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ return ret; ++ ++ /* Read VCR and check it. */ ++ ret = nor->read_reg(nor, SPINOR_OP_RD_VCR, &vcr, 1); ++ if (ret < 0 || (vcr & mask) != val) { ++ dev_err(nor->dev, "Micron VCR dummy cycles not updated\n"); ++ return -EINVAL; ++ } ++ ++ /* Save the number of dummy cycles to use with Fast Read commands */ ++ nor->read_dummy = read_dummy; ++ return 0; ++} ++ + static int micron_set_protocol(struct spi_nor *nor, u8 mask, u8 val, + enum spi_nor_protocol proto) + { +@@ -1417,12 +1464,15 @@ static int micron_set_quad_mode(struct spi_nor *nor) + /* + * Whatever the Quad mode is enabled or not, the + * Fast Read Quad Output 1-1-4 (0x6b) op code is supported. ++ * Force the number of dummy cycles to 8 and disable the Continuous Read ++ * mode to prevent some drivers from using it by mistake (m25p80). ++ * We can change these settings safely as we write into a volatile ++ * register. + */ + if (nor->read_proto != SNOR_PROTO_4_4_4) + nor->read_proto = SNOR_PROTO_1_1_4; + nor->read_opcode = SPINOR_OP_READ_1_1_4; +- nor->read_dummy = 8; +- return 0; ++ return micron_set_dummy_cycles(nor, 8); + } + + static int micron_set_dual_mode(struct spi_nor *nor) +@@ -1447,12 +1497,15 @@ static int micron_set_dual_mode(struct spi_nor *nor) + /* + * Whatever the Dual mode is enabled or not, the + * Fast Read Dual Output 1-1-2 (0x3b) op code is supported. ++ * Force the number of dummy cycles to 8 and disable the Continuous Read ++ * mode to prevent some drivers from using it by mistake (m25p80). ++ * We can change these settings safely as we write into a volatile ++ * register. + */ + if (nor->read_proto != SNOR_PROTO_2_2_2) + nor->read_proto = SNOR_PROTO_1_1_2; + nor->read_opcode = SPINOR_OP_READ_1_1_2; +- nor->read_dummy = 8; +- return 0; ++ return micron_set_dummy_cycles(nor, 8); + } + + static int micron_set_single_mode(struct spi_nor *nor) +@@ -1475,7 +1528,13 @@ static int micron_set_single_mode(struct spi_nor *nor) + nor->read_proto = SNOR_PROTO_1_1_1; + } + +- /* Force the number of dummy cycles to 8 for Fast Read, 0 for Read. */ ++ /* ++ * Force the number of dummy cycles to 8 for Fast Read, 0 for Read ++ * and disable the Continuous Read mode to prevent some drivers from ++ * using it by mistake (m25p80). ++ * We can change these settings safely as we write into a volatile ++ * register. ++ */ + switch (nor->read_opcode) { + case SPINOR_OP_READ: + case SPINOR_OP_READ4: +@@ -1486,8 +1545,7 @@ static int micron_set_single_mode(struct spi_nor *nor) + read_dummy = 8; + break; + } +- nor->read_dummy = read_dummy; +- return 0; ++ return micron_set_dummy_cycles(nor, read_dummy); + } + + static int spansion_set_quad_mode(struct spi_nor *nor) +diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h +index d0a6f34..2dc0f8b 100644 +--- a/include/linux/mtd/spi-nor.h ++++ b/include/linux/mtd/spi-nor.h +@@ -86,6 +86,8 @@ + + /* Used for Micron flashes only. */ + #define SPINOR_OP_MIO_RDID 0xaf /* Multiple I/O Read JEDEC ID */ ++#define SPINOR_OP_RD_VCR 0x85 /* Read VCR register */ ++#define SPINOR_OP_WR_VCR 0x81 /* Write VCR register */ + #define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */ + #define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */ + +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0025-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch b/target/linux/socfpga/patches-4.4/0025-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch new file mode 100644 index 0000000000..a9c3afd98d --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0025-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch @@ -0,0 +1,237 @@ +From a714a2af12d0de527be168b821373f29f4343cb7 Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:02:22 +0100 +Subject: [PATCH 25/33] mtd: spi-nor: configure the number of dummy clock + cycles on Macronix memories + +The spi-nor framework currently expects all Fast Read operations to use 8 +dummy clock cycles. Especially some drivers like m25p80 can only support +multiple of 8 dummy clock cycles. + +On Macronix memories, the number of dummy clock cycles to be used by Fast +Read commands can be safely set to 8 by updating the DC0 and DC1 volatile +bits inside the Configuration Register. + +According to the mx66l1g45g datasheet from Macronix, using 8 dummy clock +cycles should be enough to set the SPI bus clock frequency up to: +- 133 MHz for Fast Read 1-1-1, 1-1-2, 1-1-4 and 1-2-2 commands in Single + Transfer Rate (STR) +- 104 MHz for Fast Read 1-4-4 (or 4-4-4 in QPI mode) commands (STR) + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 155 +++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 147 insertions(+), 8 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 5232984..55a1d74 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1182,6 +1182,136 @@ static int winbond_quad_enable(struct spi_nor *nor) + return 0; + } + ++static int macronix_dummy2code(u8 read_opcode, u8 read_dummy, u8 *dc) ++{ ++ switch (read_opcode) { ++ case SPINOR_OP_READ: ++ case SPINOR_OP_READ4: ++ *dc = 0; ++ break; ++ ++ case SPINOR_OP_READ_FAST: ++ case SPINOR_OP_READ_1_1_2: ++ case SPINOR_OP_READ_1_1_4: ++ case SPINOR_OP_READ4_FAST: ++ case SPINOR_OP_READ4_1_1_2: ++ case SPINOR_OP_READ4_1_1_4: ++ switch (read_dummy) { ++ case 6: ++ *dc = 1; ++ break; ++ case 8: ++ *dc = 0; ++ break; ++ case 10: ++ *dc = 3; ++ break; ++ default: ++ return -EINVAL; ++ } ++ break; ++ ++ case SPINOR_OP_READ_1_2_2: ++ case SPINOR_OP_READ4_1_2_2: ++ switch (read_dummy) { ++ case 4: ++ *dc = 0; ++ break; ++ case 6: ++ *dc = 1; ++ break; ++ case 8: ++ *dc = 2; ++ break; ++ case 10: ++ *dc = 3; ++ default: ++ return -EINVAL; ++ } ++ break; ++ ++ case SPINOR_OP_READ_1_4_4: ++ case SPINOR_OP_READ4_1_4_4: ++ switch (read_dummy) { ++ case 4: ++ *dc = 1; ++ break; ++ case 6: ++ *dc = 0; ++ break; ++ case 8: ++ *dc = 2; ++ break; ++ case 10: ++ *dc = 3; ++ default: ++ return -EINVAL; ++ } ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int macronix_set_dummy_cycles(struct spi_nor *nor, u8 read_dummy) ++{ ++ int ret, sr, cr, mask, val; ++ u16 sr_cr; ++ u8 dc; ++ ++ /* Convert the number of dummy cycles into Macronix DC volatile bits */ ++ ret = macronix_dummy2code(nor->read_opcode, read_dummy, &dc); ++ if (ret) ++ return ret; ++ ++ mask = GENMASK(7, 6); ++ val = (dc << 6) & mask; ++ ++ cr = read_cr(nor); ++ if (cr < 0) { ++ dev_err(nor->dev, "error while reading the config register\n"); ++ return cr; ++ } ++ ++ if ((cr & mask) == val) { ++ nor->read_dummy = read_dummy; ++ return 0; ++ } ++ ++ sr = read_sr(nor); ++ if (sr < 0) { ++ dev_err(nor->dev, "error while reading the status register\n"); ++ return sr; ++ } ++ ++ cr = (cr & ~mask) | val; ++ sr_cr = (sr & 0xff) | ((cr & 0xff) << 8); ++ write_enable(nor); ++ ret = write_sr_cr(nor, sr_cr); ++ if (ret) { ++ dev_err(nor->dev, ++ "error while writing the SR and CR registers\n"); ++ return ret; ++ } ++ ++ ret = spi_nor_wait_till_ready(nor); ++ if (ret) ++ return ret; ++ ++ cr = read_cr(nor); ++ if (cr < 0 || (cr & mask) != val) { ++ dev_err(nor->dev, "Macronix Dummy Cycle bits not updated\n"); ++ return -EINVAL; ++ } ++ ++ /* Save the number of dummy cycles to use with Fast Read commands */ ++ nor->read_dummy = read_dummy; ++ return 0; ++} ++ + static int macronix_set_quad_mode(struct spi_nor *nor) + { + int status; +@@ -1199,8 +1329,7 @@ static int macronix_set_quad_mode(struct spi_nor *nor) + * read (performance enhance) mode by mistake! + */ + nor->read_opcode = SPINOR_OP_READ_1_4_4; +- nor->read_dummy = 8; +- return 0; ++ return macronix_set_dummy_cycles(nor, 8); + } + + /* +@@ -1213,6 +1342,9 @@ static int macronix_set_quad_mode(struct spi_nor *nor) + * entering the continuous read mode by mistake if some + * performance enhance toggling bits P0-P7 were written during + * dummy/mode cycles. ++ * ++ * Use the Fast Read Quad Output 1-1-4 (0x6b) command with 8 dummy ++ * cycles (up to 133MHz for STR and 66MHz for DTR). + */ + status = macronix_quad_enable(nor); + if (status) { +@@ -1221,8 +1353,7 @@ static int macronix_set_quad_mode(struct spi_nor *nor) + } + nor->read_proto = SNOR_PROTO_1_1_4; + nor->read_opcode = SPINOR_OP_READ_1_1_4; +- nor->read_dummy = 8; +- return 0; ++ return macronix_set_dummy_cycles(nor, 8); + } + + /* +@@ -1233,16 +1364,25 @@ static int macronix_set_quad_mode(struct spi_nor *nor) + + static int macronix_set_dual_mode(struct spi_nor *nor) + { ++ /* ++ * Use the Fast Read Dual Output 1-1-2 (0x3b) command with 8 dummy ++ * cycles (up to 133MHz for STR and 66MHz for DTR). ++ */ + nor->read_proto = SNOR_PROTO_1_1_2; + nor->read_opcode = SPINOR_OP_READ_1_1_2; +- nor->read_dummy = 8; +- return 0; ++ return macronix_set_dummy_cycles(nor, 8); + } + + static int macronix_set_single_mode(struct spi_nor *nor) + { + u8 read_dummy; + ++ /* ++ * Configure 8 dummy cycles for Fast Read 1-1-1 (0x0b) command (up to ++ * 133MHz for STR and 66MHz for DTR). The Read 1-1-1 (0x03) command ++ * expects no dummy cycle. ++ * read_opcode should not be overridden here! ++ */ + switch (nor->read_opcode) { + case SPINOR_OP_READ: + case SPINOR_OP_READ4: +@@ -1255,8 +1395,7 @@ static int macronix_set_single_mode(struct spi_nor *nor) + } + + nor->read_proto = SNOR_PROTO_1_1_1; +- nor->read_dummy = read_dummy; +- return 0; ++ return macronix_set_dummy_cycles(nor, read_dummy); + } + + static int winbond_set_quad_mode(struct spi_nor *nor) +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0026-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch b/target/linux/socfpga/patches-4.4/0026-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch new file mode 100644 index 0000000000..df79cdcce6 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0026-mtd-spi-nor-configure-the-number-of-dummy-clock-cycl.patch @@ -0,0 +1,215 @@ +From 7b411f38f7882fdf9f5607bc75deb940a7aaa480 Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:10:53 +0100 +Subject: [PATCH 26/33] mtd: spi-nor: configure the number of dummy clock + cycles on Spansion memories + +On Spansion memories, the number of dummy clock cycles to be used during +Fast Read commands is configured through the 2bit latency code (LC). These +bits are non-volatile inside the Configuration Register. + +To avoid breaking the configuration expected at reset by some bootloaders, +we'd rather read the latency code and set the nor->read_dummy value +accordingly than update those non-volatile bits. + +Since the Quad Enable non-volatile bit can be read at the same time from +the Control Register, we now check its value to avoid some calls of the +spansion_quad_enable() function when they are not needed. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/spi-nor/spi-nor.c | 159 ++++++++++++++++++++++++++++++++++++------ + 1 file changed, 137 insertions(+), 22 deletions(-) + +diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c +index 55a1d74..654209a 100644 +--- a/drivers/mtd/spi-nor/spi-nor.c ++++ b/drivers/mtd/spi-nor/spi-nor.c +@@ -1687,47 +1687,162 @@ static int micron_set_single_mode(struct spi_nor *nor) + return micron_set_dummy_cycles(nor, read_dummy); + } + +-static int spansion_set_quad_mode(struct spi_nor *nor) ++static inline int spansion_get_config(struct spi_nor *nor, ++ bool *quad_enabled, ++ u8 *latency_code) + { +- int status; ++ int cr; + +- status = spansion_quad_enable(nor); +- if (status) { +- dev_err(nor->dev, "Spansion quad-read not enabled\n"); ++ cr = read_cr(nor); ++ if (cr < 0) { ++ dev_err(nor->dev, ++ "error while reading the configuration register\n"); ++ return cr; ++ } ++ ++ if (quad_enabled) ++ *quad_enabled = !!(cr & CR_QUAD_EN_SPAN); ++ ++ if (latency_code) ++ *latency_code = (u8)((cr & GENMASK(7, 6)) >> 6); ++ ++ return 0; ++} ++ ++static int spansion_set_dummy_cycles(struct spi_nor *nor, u8 latency_code) ++{ ++ /* SDR dummy cycles */ ++ switch (nor->read_opcode) { ++ case SPINOR_OP_READ: ++ case SPINOR_OP_READ4: ++ nor->read_dummy = 0; ++ break; ++ ++ case SPINOR_OP_READ_FAST: ++ case SPINOR_OP_READ_1_1_2: ++ case SPINOR_OP_READ_1_1_4: ++ case SPINOR_OP_READ4_FAST: ++ case SPINOR_OP_READ4_1_1_2: ++ case SPINOR_OP_READ4_1_1_4: ++ nor->read_dummy = (latency_code == 3) ? 0 : 8; ++ break; ++ ++ case SPINOR_OP_READ_1_2_2: ++ case SPINOR_OP_READ4_1_2_2: ++ switch (latency_code) { ++ default: ++ case 0: ++ case 3: ++ nor->read_dummy = 4; ++ break; ++ case 1: ++ nor->read_dummy = 5; ++ break; ++ case 2: ++ nor->read_dummy = 6; ++ break; ++ } ++ break; ++ ++ ++ case SPINOR_OP_READ_1_4_4: ++ case SPINOR_OP_READ4_1_4_4: ++ switch (latency_code) { ++ default: ++ case 0: ++ case 1: ++ nor->read_dummy = 4; ++ break; ++ case 2: ++ nor->read_dummy = 5; ++ break; ++ case 3: ++ nor->read_dummy = 1; ++ break; ++ } ++ ++ default: + return -EINVAL; + } ++ ++ return 0; ++} ++ ++static int spansion_set_quad_mode(struct spi_nor *nor) ++{ ++ bool quad_enabled; ++ u8 latency_code; ++ int ret; ++ ++ /* ++ * The QUAD bit of Configuration Register must be set (CR Bit1=1) for ++ * using any Quad SPI command. ++ */ ++ ret = spansion_get_config(nor, &quad_enabled, &latency_code); ++ if (ret) ++ return ret; ++ ++ /* The Quad mode should be enabled ... */ ++ if (!quad_enabled) { ++ /* ... if not try to enable it. */ ++ dev_warn(nor->dev, "Spansion Quad mode disabled, enable it\n"); ++ ret = spansion_quad_enable(nor); ++ if (ret) ++ return ret; ++ } ++ ++ /* ++ * Don't use the Fast Read Quad I/O (0xeb / 0xec) commands as their ++ * number of dummy cycles can not be set to a multiple of 8: some SPI ++ * controllers, especially those relying on the m25p80 driver, expect ++ * the number of dummy cycles to be a multiple of 8. ++ * Also when using a Fast Read Quad I/O command, the memory checks the ++ * value of the first mode/dummy cycles to decice whether it enters or ++ * leaves the Countinuous Read mode. We should never enter the ++ * Countinuous Read mode as the spi-nor framework doesn't support it. ++ * For all these reason, we'd rather use the Fast Read Quad Output ++ * 1-1-4 (0x6b / 0x6c) commands instead. ++ */ + nor->read_proto = SNOR_PROTO_1_1_4; + nor->read_opcode = SPINOR_OP_READ_1_1_4; +- nor->read_dummy = 8; +- return 0; ++ return spansion_set_dummy_cycles(nor, latency_code); + } + + static int spansion_set_dual_mode(struct spi_nor *nor) + { ++ u8 latency_code; ++ int ret; ++ ++ /* We don't care about the quad mode status */ ++ ret = spansion_get_config(nor, NULL, &latency_code); ++ if (ret) ++ return ret; ++ ++ /* ++ * Don't use the Fast Read Dual I/O (0xbb / 0xbc) commands as their ++ * number of dummy cycles can not bet set to a multiple of 8: some SPI ++ * controllers, especially those relying on the m25p80 driver, expect ++ * the number of dummy cycles to be a multiple of 8. ++ * For this reason, w'd rather use the Fast Read Dual Output 1-1-2 ++ * (0x3b / 0x3c) commands instead. ++ */ + nor->read_proto = SNOR_PROTO_1_1_2; + nor->read_opcode = SPINOR_OP_READ_1_1_2; +- nor->read_dummy = 8; +- return 0; ++ return spansion_set_dummy_cycles(nor, latency_code); + } + + static int spansion_set_single_mode(struct spi_nor *nor) + { +- u8 read_dummy; +- +- switch (nor->read_opcode) { +- case SPINOR_OP_READ: +- case SPINOR_OP_READ4: +- read_dummy = 0; +- break; ++ u8 latency_code; ++ int ret; + +- default: +- read_dummy = 8; +- break; +- } ++ /* We don't care about the quad mode status */ ++ ret = spansion_get_config(nor, NULL, &latency_code); ++ if (ret) ++ return ret; + + nor->read_proto = SNOR_PROTO_1_1_1; +- nor->read_dummy = read_dummy; +- return 0; ++ return spansion_set_dummy_cycles(nor, latency_code); + } + + static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info) +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0027-mtd-m25p80-add-support-of-dual-and-quad-spi-protocol.patch b/target/linux/socfpga/patches-4.4/0027-mtd-m25p80-add-support-of-dual-and-quad-spi-protocol.patch new file mode 100644 index 0000000000..d2f5ab6e84 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0027-mtd-m25p80-add-support-of-dual-and-quad-spi-protocol.patch @@ -0,0 +1,293 @@ +From 8b4f14b2f8ed819a6b9e371128259271e8d88841 Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:10:54 +0100 +Subject: [PATCH 27/33] mtd: m25p80: add support of dual and quad spi protocols + to all commands + +Before this patch, m25p80_read() supported few SPI protocols: +- regular SPI 1-1-1 +- SPI Dual Output 1-1-2 +- SPI Quad Output 1-1-4 +On the other hand, all other m25p80_*() hooks only supported SPI 1-1-1. + +However once their Quad mode enabled, Micron and Macronix spi-nor memories +expect all commands to use the SPI 4-4-4 protocol. + +Also, once their Dual mode enabled, Micron spi-nor memories expect all +commands to use the SPI-2-2-2 protocol. + +So this patch adds support to all currently existing SPI protocols to +cover as many protocols as possible. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +--- + drivers/mtd/devices/m25p80.c | 192 ++++++++++++++++++++++++++++++++++--------- + 1 file changed, 151 insertions(+), 41 deletions(-) + +diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c +index bc7a802..e3e2708 100644 +--- a/drivers/mtd/devices/m25p80.c ++++ b/drivers/mtd/devices/m25p80.c +@@ -27,22 +27,64 @@ + #include <linux/spi/flash.h> + #include <linux/mtd/spi-nor.h> + +-#define MAX_CMD_SIZE 6 ++#define MAX_CMD_SIZE 16 + struct m25p { + struct spi_device *spi; + struct spi_nor spi_nor; + u8 command[MAX_CMD_SIZE]; + }; + ++static inline int m25p80_proto2nbits(enum spi_nor_protocol proto, ++ unsigned *code_nbits, ++ unsigned *addr_nbits, ++ unsigned *data_nbits) ++{ ++ if (code_nbits) ++ *code_nbits = SNOR_PROTO_CMD_FROM_PROTO(proto); ++ if (addr_nbits) ++ *addr_nbits = SNOR_PROTO_ADDR_FROM_PROTO(proto); ++ if (data_nbits) ++ *data_nbits = SNOR_PROTO_DATA_FROM_PROTO(proto); ++ ++ return 0; ++} ++ + static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) + { + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; ++ unsigned code_nbits, data_nbits; ++ struct spi_transfer xfers[2]; + int ret; + +- ret = spi_write_then_read(spi, &code, 1, val, len); ++ /* Check the total length of command op code and data. */ ++ if (len + 1 > MAX_CMD_SIZE) ++ return -EINVAL; ++ ++ /* Get transfer protocols (addr_nbits is not relevant here). */ ++ ret = m25p80_proto2nbits(nor->reg_proto, ++ &code_nbits, NULL, &data_nbits); ++ if (ret < 0) ++ return ret; ++ ++ /* Set up transfers. */ ++ memset(xfers, 0, sizeof(xfers)); ++ ++ flash->command[0] = code; ++ xfers[0].len = 1; ++ xfers[0].tx_buf = flash->command; ++ xfers[0].tx_nbits = code_nbits; ++ ++ xfers[1].len = len; ++ xfers[1].rx_buf = &flash->command[1]; ++ xfers[1].rx_nbits = data_nbits; ++ ++ /* Process command. */ ++ ret = spi_sync_transfer(spi, xfers, 2); + if (ret < 0) + dev_err(&spi->dev, "error %d reading %x\n", ret, code); ++ else ++ memcpy(val, &flash->command[1], len); + + return ret; + } +@@ -65,12 +107,42 @@ static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) + { + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; ++ unsigned code_nbits, data_nbits, num_xfers = 1; ++ struct spi_transfer xfers[2]; ++ int ret; ++ ++ /* Check the total length of command op code and data. */ ++ if (buf && (len + 1 > MAX_CMD_SIZE)) ++ return -EINVAL; ++ ++ /* Get transfer protocols (addr_nbits is not relevant here). */ ++ ret = m25p80_proto2nbits(nor->reg_proto, ++ &code_nbits, NULL, &data_nbits); ++ if (ret < 0) ++ return ret; ++ ++ /* Set up transfer(s). */ ++ memset(xfers, 0, sizeof(xfers)); + + flash->command[0] = opcode; +- if (buf) ++ xfers[0].len = 1; ++ xfers[0].tx_buf = flash->command; ++ xfers[0].tx_nbits = code_nbits; ++ ++ if (buf) { + memcpy(&flash->command[1], buf, len); ++ if (data_nbits == code_nbits) { ++ xfers[0].len += len; ++ } else { ++ xfers[1].len = len; ++ xfers[1].tx_buf = &flash->command[1]; ++ xfers[1].tx_nbits = data_nbits; ++ num_xfers++; ++ } ++ } + +- return spi_write(spi, flash->command, len + 1); ++ /* Process command. */ ++ return spi_sync_transfer(spi, xfers, num_xfers); + } + + static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, +@@ -78,43 +150,54 @@ static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len, + { + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; +- struct spi_transfer t[2] = {}; ++ unsigned code_nbits, addr_nbits, data_nbits, num_xfers = 1; ++ struct spi_transfer xfers[3]; + struct spi_message m; +- int cmd_sz = m25p_cmdsz(nor); +- +- spi_message_init(&m); ++ int ret, cmd_sz = m25p_cmdsz(nor); + + if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) + cmd_sz = 1; + +- flash->command[0] = nor->program_opcode; +- m25p_addr2cmd(nor, to, flash->command); ++ /* Get transfer protocols. */ ++ ret = m25p80_proto2nbits(nor->write_proto, ++ &code_nbits, &addr_nbits, &data_nbits); ++ if (ret < 0) { ++ *retlen = 0; ++ return; ++ } + +- t[0].tx_buf = flash->command; +- t[0].len = cmd_sz; +- spi_message_add_tail(&t[0], &m); ++ /* Set up transfers. */ ++ memset(xfers, 0, sizeof(xfers)); ++ ++ flash->command[0] = nor->program_opcode; ++ xfers[0].len = 1; ++ xfers[0].tx_buf = flash->command; ++ xfers[0].tx_nbits = code_nbits; ++ ++ if (cmd_sz > 1) { ++ m25p_addr2cmd(nor, to, flash->command); ++ if (addr_nbits == code_nbits) { ++ xfers[0].len += nor->addr_width; ++ } else { ++ xfers[1].len = nor->addr_width; ++ xfers[1].tx_buf = &flash->command[1]; ++ xfers[1].tx_nbits = addr_nbits; ++ num_xfers++; ++ } ++ } + +- t[1].tx_buf = buf; +- t[1].len = len; +- spi_message_add_tail(&t[1], &m); ++ xfers[num_xfers].len = len; ++ xfers[num_xfers].tx_buf = buf; ++ xfers[num_xfers].tx_nbits = data_nbits; ++ num_xfers++; + ++ /* Process command. */ ++ spi_message_init_with_transfers(&m, xfers, num_xfers); + spi_sync(spi, &m); + + *retlen += m.actual_length - cmd_sz; + } + +-static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor) +-{ +- switch (nor->flash_read) { +- case SPI_NOR_DUAL: +- return 2; +- case SPI_NOR_QUAD: +- return 4; +- default: +- return 0; +- } +-} +- + /* + * Read an address range from the nor chip. The address range + * may be any size provided it is within the physical boundaries. +@@ -124,28 +207,55 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, + { + struct m25p *flash = nor->priv; + struct spi_device *spi = flash->spi; +- struct spi_transfer t[2]; +- struct spi_message m; ++ unsigned code_nbits, addr_nbits, data_nbits, num_xfers = 1; + unsigned int dummy = nor->read_dummy; ++ struct spi_transfer xfers[3]; ++ struct spi_message m; ++ int ret; ++ ++ /* Get transfer protocols. */ ++ ret = m25p80_proto2nbits(nor->read_proto, ++ &code_nbits, &addr_nbits, &data_nbits); ++ if (ret < 0) { ++ *retlen = 0; ++ return ret; ++ } + + /* convert the dummy cycles to the number of bytes */ +- dummy /= 8; ++ dummy = (dummy * addr_nbits) / 8; + +- spi_message_init(&m); +- memset(t, 0, (sizeof t)); ++ /* Set up transfers. */ ++ memset(xfers, 0, sizeof(xfers)); + + flash->command[0] = nor->read_opcode; +- m25p_addr2cmd(nor, from, flash->command); ++ xfers[0].len = 1; ++ xfers[0].tx_buf = flash->command; ++ xfers[0].tx_nbits = code_nbits; + +- t[0].tx_buf = flash->command; +- t[0].len = m25p_cmdsz(nor) + dummy; +- spi_message_add_tail(&t[0], &m); ++ m25p_addr2cmd(nor, from, flash->command); ++ /* ++ * Clear all dummy/mode cycle bits to avoid sending some manufacturer ++ * specific pattern, which might make the memory enter its Continuous ++ * Read mode by mistake. ++ */ ++ memset(flash->command + 1 + nor->addr_width, 0, dummy); ++ ++ if (addr_nbits == code_nbits) { ++ xfers[0].len += nor->addr_width + dummy; ++ } else { ++ xfers[1].len = nor->addr_width + dummy; ++ xfers[1].tx_buf = &flash->command[1]; ++ xfers[1].tx_nbits = addr_nbits; ++ num_xfers++; ++ } + +- t[1].rx_buf = buf; +- t[1].rx_nbits = m25p80_rx_nbits(nor); +- t[1].len = len; +- spi_message_add_tail(&t[1], &m); ++ xfers[num_xfers].len = len; ++ xfers[num_xfers].rx_buf = buf; ++ xfers[num_xfers].rx_nbits = data_nbits; ++ num_xfers++; + ++ /* Process command. */ ++ spi_message_init_with_transfers(&m, xfers, num_xfers); + spi_sync(spi, &m); + + *retlen = m.actual_length - m25p_cmdsz(nor) - dummy; +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0028-mtd-ofpart-grab-device-tree-node-directly-from-maste.patch b/target/linux/socfpga/patches-4.4/0028-mtd-ofpart-grab-device-tree-node-directly-from-maste.patch new file mode 100644 index 0000000000..3787607236 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0028-mtd-ofpart-grab-device-tree-node-directly-from-maste.patch @@ -0,0 +1,81 @@ +From 62b613003aa4cef3f8bf9a2ec4c7709daf4dde8a Mon Sep 17 00:00:00 2001 +From: Brian Norris <computersforpeace@gmail.com> +Date: Fri, 30 Oct 2015 20:33:21 -0700 +Subject: [PATCH 28/33] mtd: ofpart: grab device tree node directly from master + device node + +It seems more logical to use a device node directly associated with the +MTD master device (i.e., mtd->dev.of_node field) rather than requiring +auxiliary partition parser information to be passed in by the driver in +a separate struct. + +This patch supports the mtd->dev.of_node field and deprecates the parser +data 'of_node' field + +Driver conversions may now follow. + +Additional side benefit to assigning mtd->dev.of_node rather than using +parser data: the driver core will automatically create a device -> node +symlink for us. + +Signed-off-by: Brian Norris <computersforpeace@gmail.com> +Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com> +--- + drivers/mtd/ofpart.c | 18 ++++++++++-------- + include/linux/mtd/partitions.h | 4 +++- + 2 files changed, 13 insertions(+), 9 deletions(-) + +diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c +index 9ed6038..cf4780c 100644 +--- a/drivers/mtd/ofpart.c ++++ b/drivers/mtd/ofpart.c +@@ -37,10 +37,11 @@ static int parse_ofpart_partitions(struct mtd_info *master, + bool dedicated = true; + + +- if (!data) +- return 0; +- +- mtd_node = data->of_node; ++ /* ++ * of_node can be provided through auxiliary parser data or (preferred) ++ * by assigning the master device node ++ */ ++ mtd_node = data && data->of_node ? data->of_node : mtd_get_of_node(master); + if (!mtd_node) + return 0; + +@@ -157,10 +158,11 @@ static int parse_ofoldpart_partitions(struct mtd_info *master, + } *part; + const char *names; + +- if (!data) +- return 0; +- +- dp = data->of_node; ++ /* ++ * of_node can be provided through auxiliary parser data or (preferred) ++ * by assigning the master device node ++ */ ++ dp = data && data->of_node ? data->of_node : mtd_get_of_node(master); + if (!dp) + return 0; + +diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h +index 6a35e6d..e742f34 100644 +--- a/include/linux/mtd/partitions.h ++++ b/include/linux/mtd/partitions.h +@@ -56,7 +56,9 @@ struct device_node; + /** + * struct mtd_part_parser_data - used to pass data to MTD partition parsers. + * @origin: for RedBoot, start address of MTD device +- * @of_node: for OF parsers, device node containing partitioning information ++ * @of_node: for OF parsers, device node containing partitioning information. ++ * This field is deprecated, as the device node should simply be ++ * assigned to the master struct device. + */ + struct mtd_part_parser_data { + unsigned long origin; +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0029-Documentation-atmel-quadspi-add-binding-file-for-Atm.patch b/target/linux/socfpga/patches-4.4/0029-Documentation-atmel-quadspi-add-binding-file-for-Atm.patch new file mode 100644 index 0000000000..ca8831d15a --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0029-Documentation-atmel-quadspi-add-binding-file-for-Atm.patch @@ -0,0 +1,58 @@ +From 771ee7cd27c39617ece8727c70f904c31f7415fb Mon Sep 17 00:00:00 2001 +From: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Date: Fri, 8 Jan 2016 17:10:55 +0100 +Subject: [PATCH 29/33] Documentation: atmel-quadspi: add binding file for + Atmel QSPI driver + +This patch documents the DT bindings for the driver of the Atmel QSPI +controller embedded inside sama5d2x SoCs. + +Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com> +Acked-by: Rob Herring <robh@kernel.org> +Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com> +--- + .../devicetree/bindings/mtd/atmel-quadspi.txt | 32 ++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/atmel-quadspi.txt + +diff --git a/Documentation/devicetree/bindings/mtd/atmel-quadspi.txt b/Documentation/devicetree/bindings/mtd/atmel-quadspi.txt +new file mode 100644 +index 0000000..4898070 +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/atmel-quadspi.txt +@@ -0,0 +1,32 @@ ++* Atmel Quad Serial Peripheral Interface (QSPI) ++ ++Required properties: ++- compatible: Should be "atmel,sama5d2-qspi". ++- reg: Should contain the locations and lengths of the base registers ++ and the mapped memory. ++- reg-names: Should contain the resource reg names: ++ - qspi_base: configuration register address space ++ - qspi_mmap: memory mapped address space ++- interrupts: Should contain the interrupt for the device. ++- clocks: The phandle of the clock needed by the QSPI controller. ++- #address-cells: Should be <1>. ++- #size-cells: Should be <0>. ++ ++Example: ++ ++spi@f0020000 { ++ compatible = "atmel,sama5d2-qspi"; ++ reg = <0xf0020000 0x100>, <0xd0000000 0x8000000>; ++ reg-names = "qspi_base", "qspi_mmap"; ++ interrupts = <52 IRQ_TYPE_LEVEL_HIGH 7>; ++ clocks = <&spi0_clk>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_spi0_default>; ++ status = "okay"; ++ ++ m25p80@0 { ++ ... ++ }; ++}; +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0030-mtd-spi-nor-Bindings-for-Cadence-Quad-SPI-Flash-Cont.patch b/target/linux/socfpga/patches-4.4/0030-mtd-spi-nor-Bindings-for-Cadence-Quad-SPI-Flash-Cont.patch new file mode 100644 index 0000000000..7b454a84a1 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0030-mtd-spi-nor-Bindings-for-Cadence-Quad-SPI-Flash-Cont.patch @@ -0,0 +1,99 @@ +From 8e8a7168e89cc978ca14ab74ab20193ca09e3f3a Mon Sep 17 00:00:00 2001 +From: Graham Moore <grmoore@opensource.altera.com> +Date: Tue, 28 Jul 2015 12:38:02 -0500 +Subject: [PATCH 30/33] mtd: spi-nor: Bindings for Cadence Quad SPI Flash + Controller driver. + +Add binding document for the Cadence QSPI controller. + +Signed-off-by: Graham Moore <grmoore@opensource.altera.com> +Signed-off-by: Marek Vasut <marex@denx.de> +Cc: Alan Tull <atull@opensource.altera.com> +Cc: Brian Norris <computersforpeace@gmail.com> +Cc: David Woodhouse <dwmw2@infradead.org> +Cc: Dinh Nguyen <dinguyen@opensource.altera.com> +Cc: Graham Moore <grmoore@opensource.altera.com> +Cc: Vignesh R <vigneshr@ti.com> +Cc: Yves Vandervennet <yvanderv@opensource.altera.com> +Cc: devicetree@vger.kernel.org + +V2: Add cdns prefix to driver-specific bindings. +V3: Use existing property "is-decoded-cs" instead of creating a + duplicate, "ext-decoder". Timing parameters are in nanoseconds, + not master reference clocks. Remove bus-num completely. +V4: Add new properties fifo-width and trigger-address +V7: - Prefix all of the Cadence-specific properties with cdns prefix, + those are in particular "cdns,is-decoded-cs", "cdns,fifo-depth", + "cdns,fifo-width", "cdns,trigger-address". + - Drop bogus properties which were not used and were incorrect. +V8: Align lines to 80 chars. +--- + .../devicetree/bindings/mtd/cadence-quadspi.txt | 56 ++++++++++++++++++++++ + 1 file changed, 56 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mtd/cadence-quadspi.txt + +diff --git a/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt b/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt +new file mode 100644 +index 0000000..f248056 +--- /dev/null ++++ b/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt +@@ -0,0 +1,56 @@ ++* Cadence Quad SPI controller ++ ++Required properties: ++- compatible : Should be "cdns,qspi-nor". ++- reg : Contains two entries, each of which is a tuple consisting of a ++ physical address and length. The first entry is the address and ++ length of the controller register set. The second entry is the ++ address and length of the QSPI Controller data area. ++- interrupts : Unit interrupt specifier for the controller interrupt. ++- clocks : phandle to the Quad SPI clock. ++- cdns,fifo-depth : Size of the data FIFO in words. ++- cdns,fifo-width : Bus width of the data FIFO in bytes. ++- cdns,trigger-address : 32-bit indirect AHB trigger address. ++ ++Optional properties: ++- cdns,is-decoded-cs : Flag to indicate whether decoder is used or not. ++ ++Optional subnodes: ++Subnodes of the Cadence Quad SPI controller are spi slave nodes with additional ++custom properties: ++- cdns,read-delay : Delay for read capture logic, in clock cycles ++- cdns,tshsl-ns : Delay in nanoseconds for the length that the master ++ mode chip select outputs are de-asserted between ++ transactions. ++- cdns,tsd2d-ns : Delay in nanoseconds between one chip select being ++ de-activated and the activation of another. ++- cdns,tchsh-ns : Delay in nanoseconds between last bit of current ++ transaction and deasserting the device chip select ++ (qspi_n_ss_out). ++- cdns,tslch-ns : Delay in nanoseconds between setting qspi_n_ss_out low ++ and first bit transfer. ++ ++Example: ++ ++ qspi: spi@ff705000 { ++ compatible = "cdns,qspi-nor"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0xff705000 0x1000>, ++ <0xffa00000 0x1000>; ++ interrupts = <0 151 4>; ++ clocks = <&qspi_clk>; ++ cdns,is-decoded-cs; ++ cdns,fifo-depth = <128>; ++ cdns,fifo-width = <4>; ++ cdns,trigger-address = <0x00000000>; ++ ++ flash0: n25q00@0 { ++ ... ++ cdns,read-delay = <4>; ++ cdns,tshsl-ns = <50>; ++ cdns,tsd2d-ns = <50>; ++ cdns,tchsh-ns = <4>; ++ cdns,tslch-ns = <4>; ++ }; ++ }; +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0031-mtd-spi-nor-Add-driver-for-Cadence-Quad-SPI-Flash-Co.patch b/target/linux/socfpga/patches-4.4/0031-mtd-spi-nor-Add-driver-for-Cadence-Quad-SPI-Flash-Co.patch new file mode 100644 index 0000000000..1a55ee2dd6 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0031-mtd-spi-nor-Add-driver-for-Cadence-Quad-SPI-Flash-Co.patch @@ -0,0 +1,1431 @@ +From 30e33517815b3c518fc2483a23bfe1445c0ae92d Mon Sep 17 00:00:00 2001 +From: Graham Moore <grmoore@opensource.altera.com> +Date: Tue, 28 Jul 2015 12:38:03 -0500 +Subject: [PATCH 31/33] mtd: spi-nor: Add driver for Cadence Quad SPI Flash + Controller. + +Add support for the Cadence QSPI controller. This controller is +present in the Altera SoCFPGA SoCs and this driver has been tested +on the Cyclone V SoC. + +Signed-off-by: Graham Moore <grmoore@opensource.altera.com> +Signed-off-by: Marek Vasut <marex@denx.de> +Cc: Alan Tull <atull@opensource.altera.com> +Cc: Brian Norris <computersforpeace@gmail.com> +Cc: David Woodhouse <dwmw2@infradead.org> +Cc: Dinh Nguyen <dinguyen@opensource.altera.com> +Cc: Graham Moore <grmoore@opensource.altera.com> +Cc: Vignesh R <vigneshr@ti.com> +Cc: Yves Vandervennet <yvanderv@opensource.altera.com> +Cc: devicetree@vger.kernel.org + +V2: use NULL instead of modalias in spi_nor_scan call +V3: Use existing property is-decoded-cs instead of creating duplicate. +V4: Support Micron quad mode by snooping command stream for EVCR command + and subsequently configuring Cadence controller for quad mode. +V5: Clean up sparse and smatch complaints. Remove snooping of Micron + quad mode. Add comment on XIP mode bit and dummy clock cycles. Set + up SRAM partition at 1:1 during init. +V6: Remove dts patch that was included by mistake. Incorporate Vikas's + comments regarding fifo width, SRAM partition setting, and trigger + address. Trigger address was added as an unsigned int, as it is not + an IO resource per se, and does not need to be mapped. Also add + Marek Vasut's workaround for picking up OF properties on subnodes. +V7: - Perform coding-style cleanup and type fixes. Remove ugly QSPI_*() + macros and replace them with functions. Get rid of unused variables. + - Implement support for nor->set_protocol() to handle Quad-command, + this patch now depends on the following patch: + mtd: spi-nor: notify (Q)SPI controller about protocol change + - Replace that cqspi_fifo_read() disaster with plain old readsl() + and cqspi_fifo_write() tentacle horror with pretty writesl(). + - Remove CQSPI_SUPPORT_XIP_CHIPS, which is broken. + - Get rid of cqspi_find_chipselect() mess, instead just place the + struct cqspi_st and chipselect number into struct cqspi_flash_pdata + and set nor->priv to the struct cqspi_flash_pdata of that particular + chip. + - Replace the odd math in calculate_ticks_for_ns() with DIV_ROUND_UP(). + - Make variables const where applicable. +V8: - Implement a function to wait for bit being set/unset for a given + period of time and use it to replace the ad-hoc bits of code. + - Configure the write underflow watermark to be 1/8 if FIFO size. + - Extract out the SPI NOR flash probing code into separate function + to clearly mark what will soon be considered a boilerplate code. + - Repair the handling of mode bits, which caused instability in V7. + - Clean up the interrupt handling + - Fix Kconfig help text and make the patch depend on OF and COMPILE_TEST. +V9: - Rename CQSPI_REG_IRQ_IND_RD_OVERFLOW to CQSPI_REG_IRQ_IND_SRAM_FULL + - Merge cqspi_controller_disable() into cqspi_controller_enable() and + make the mode selectable via parameter. +V10: - Update against Cyrille's new patchset and changes to linux-mtd. + - Repair problem with multiple QSPI NOR devices having the same mtd->name, + they are now named devname.cs , where cs is the chipselect ID. +V11: - Replace dependency on ARCH_SOCFPGA with dependency on ARM +--- + drivers/mtd/spi-nor/Kconfig | 11 + + drivers/mtd/spi-nor/Makefile | 1 + + drivers/mtd/spi-nor/cadence-quadspi.c | 1324 +++++++++++++++++++++++++++++++++ + 3 files changed, 1336 insertions(+) + create mode 100644 drivers/mtd/spi-nor/cadence-quadspi.c + +diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig +index 2fe2a7e..02082ae 100644 +--- a/drivers/mtd/spi-nor/Kconfig ++++ b/drivers/mtd/spi-nor/Kconfig +@@ -41,4 +41,15 @@ config SPI_NXP_SPIFI + Flash. Enable this option if you have a device with a SPIFI + controller and want to access the Flash as a mtd device. + ++config SPI_CADENCE_QUADSPI ++ tristate "Cadence Quad SPI controller" ++ depends on OF && (ARM || COMPILE_TEST) ++ help ++ Enable support for the Cadence Quad SPI Flash controller. ++ ++ Cadence QSPI is a specialized controller for connecting an SPI ++ Flash over 1/2/4-bit wide bus. Enable this option if you have a ++ device with a Cadence QSPI controller and want to access the ++ Flash as an MTD device. ++ + endif # MTD_SPI_NOR +diff --git a/drivers/mtd/spi-nor/Makefile b/drivers/mtd/spi-nor/Makefile +index e53333e..446c6b9 100644 +--- a/drivers/mtd/spi-nor/Makefile ++++ b/drivers/mtd/spi-nor/Makefile +@@ -1,3 +1,4 @@ + obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o ++obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o + obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o + obj-$(CONFIG_SPI_NXP_SPIFI) += nxp-spifi.o +diff --git a/drivers/mtd/spi-nor/cadence-quadspi.c b/drivers/mtd/spi-nor/cadence-quadspi.c +new file mode 100644 +index 0000000..7e61fba +--- /dev/null ++++ b/drivers/mtd/spi-nor/cadence-quadspi.c +@@ -0,0 +1,1324 @@ ++/* ++ * Driver for Cadence QSPI Controller ++ * ++ * Copyright Altera Corporation (C) 2012-2014. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms and conditions of the GNU General Public License, ++ * version 2, as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ++ * more details. ++ * ++ * You should have received a copy of the GNU General Public License along with ++ * this program. If not, see <http://www.gnu.org/licenses/>. ++ */ ++#include <linux/clk.h> ++#include <linux/completion.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/io.h> ++#include <linux/jiffies.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/partitions.h> ++#include <linux/mtd/spi-nor.h> ++#include <linux/of_device.h> ++#include <linux/of.h> ++#include <linux/platform_device.h> ++#include <linux/sched.h> ++#include <linux/spi/spi.h> ++#include <linux/timer.h> ++ ++#define CQSPI_NAME "cadence-qspi" ++#define CQSPI_MAX_CHIPSELECT 16 ++ ++struct cqspi_st; ++ ++struct cqspi_flash_pdata { ++ struct spi_nor nor; ++ struct cqspi_st *cqspi; ++ u32 clk_rate; ++ u32 read_delay; ++ u32 tshsl_ns; ++ u32 tsd2d_ns; ++ u32 tchsh_ns; ++ u32 tslch_ns; ++ u8 inst_width; ++ u8 addr_width; ++ u8 cs; ++}; ++ ++struct cqspi_st { ++ struct platform_device *pdev; ++ ++ struct clk *clk; ++ unsigned int sclk; ++ ++ void __iomem *iobase; ++ void __iomem *ahb_base; ++ struct completion transfer_complete; ++ struct mutex bus_mutex; ++ ++ int current_cs; ++ unsigned long master_ref_clk_hz; ++ bool is_decoded_cs; ++ u32 fifo_depth; ++ u32 fifo_width; ++ u32 trigger_address; ++ struct cqspi_flash_pdata f_pdata[CQSPI_MAX_CHIPSELECT]; ++}; ++ ++/* Operation timeout value */ ++#define CQSPI_TIMEOUT_MS 500 ++#define CQSPI_READ_TIMEOUT_MS 10 ++ ++/* Instruction type */ ++#define CQSPI_INST_TYPE_SINGLE 0 ++#define CQSPI_INST_TYPE_DUAL 1 ++#define CQSPI_INST_TYPE_QUAD 2 ++ ++#define CQSPI_DUMMY_CLKS_PER_BYTE 8 ++#define CQSPI_DUMMY_BYTES_MAX 4 ++#define CQSPI_DUMMY_CLKS_MAX 31 ++ ++#define CQSPI_STIG_DATA_LEN_MAX 8 ++ ++/* Register map */ ++#define CQSPI_REG_CONFIG 0x00 ++#define CQSPI_REG_CONFIG_ENABLE_MASK BIT(0) ++#define CQSPI_REG_CONFIG_DECODE_MASK BIT(9) ++#define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10 ++#define CQSPI_REG_CONFIG_DMA_MASK BIT(15) ++#define CQSPI_REG_CONFIG_BAUD_LSB 19 ++#define CQSPI_REG_CONFIG_IDLE_LSB 31 ++#define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF ++#define CQSPI_REG_CONFIG_BAUD_MASK 0xF ++ ++#define CQSPI_REG_RD_INSTR 0x04 ++#define CQSPI_REG_RD_INSTR_OPCODE_LSB 0 ++#define CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB 8 ++#define CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB 12 ++#define CQSPI_REG_RD_INSTR_TYPE_DATA_LSB 16 ++#define CQSPI_REG_RD_INSTR_MODE_EN_LSB 20 ++#define CQSPI_REG_RD_INSTR_DUMMY_LSB 24 ++#define CQSPI_REG_RD_INSTR_TYPE_INSTR_MASK 0x3 ++#define CQSPI_REG_RD_INSTR_TYPE_ADDR_MASK 0x3 ++#define CQSPI_REG_RD_INSTR_TYPE_DATA_MASK 0x3 ++#define CQSPI_REG_RD_INSTR_DUMMY_MASK 0x1F ++ ++#define CQSPI_REG_WR_INSTR 0x08 ++#define CQSPI_REG_WR_INSTR_OPCODE_LSB 0 ++#define CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB 12 ++#define CQSPI_REG_WR_INSTR_TYPE_DATA_LSB 16 ++ ++#define CQSPI_REG_DELAY 0x0C ++#define CQSPI_REG_DELAY_TSLCH_LSB 0 ++#define CQSPI_REG_DELAY_TCHSH_LSB 8 ++#define CQSPI_REG_DELAY_TSD2D_LSB 16 ++#define CQSPI_REG_DELAY_TSHSL_LSB 24 ++#define CQSPI_REG_DELAY_TSLCH_MASK 0xFF ++#define CQSPI_REG_DELAY_TCHSH_MASK 0xFF ++#define CQSPI_REG_DELAY_TSD2D_MASK 0xFF ++#define CQSPI_REG_DELAY_TSHSL_MASK 0xFF ++ ++#define CQSPI_REG_READCAPTURE 0x10 ++#define CQSPI_REG_READCAPTURE_BYPASS_LSB 0 ++#define CQSPI_REG_READCAPTURE_DELAY_LSB 1 ++#define CQSPI_REG_READCAPTURE_DELAY_MASK 0xF ++ ++#define CQSPI_REG_SIZE 0x14 ++#define CQSPI_REG_SIZE_ADDRESS_LSB 0 ++#define CQSPI_REG_SIZE_PAGE_LSB 4 ++#define CQSPI_REG_SIZE_BLOCK_LSB 16 ++#define CQSPI_REG_SIZE_ADDRESS_MASK 0xF ++#define CQSPI_REG_SIZE_PAGE_MASK 0xFFF ++#define CQSPI_REG_SIZE_BLOCK_MASK 0x3F ++ ++#define CQSPI_REG_SRAMPARTITION 0x18 ++#define CQSPI_REG_INDIRECTTRIGGER 0x1C ++ ++#define CQSPI_REG_DMA 0x20 ++#define CQSPI_REG_DMA_SINGLE_LSB 0 ++#define CQSPI_REG_DMA_BURST_LSB 8 ++#define CQSPI_REG_DMA_SINGLE_MASK 0xFF ++#define CQSPI_REG_DMA_BURST_MASK 0xFF ++ ++#define CQSPI_REG_REMAP 0x24 ++#define CQSPI_REG_MODE_BIT 0x28 ++ ++#define CQSPI_REG_SDRAMLEVEL 0x2C ++#define CQSPI_REG_SDRAMLEVEL_RD_LSB 0 ++#define CQSPI_REG_SDRAMLEVEL_WR_LSB 16 ++#define CQSPI_REG_SDRAMLEVEL_RD_MASK 0xFFFF ++#define CQSPI_REG_SDRAMLEVEL_WR_MASK 0xFFFF ++ ++#define CQSPI_REG_IRQSTATUS 0x40 ++#define CQSPI_REG_IRQMASK 0x44 ++ ++#define CQSPI_REG_INDIRECTRD 0x60 ++#define CQSPI_REG_INDIRECTRD_START_MASK BIT(0) ++#define CQSPI_REG_INDIRECTRD_CANCEL_MASK BIT(1) ++#define CQSPI_REG_INDIRECTRD_DONE_MASK BIT(5) ++ ++#define CQSPI_REG_INDIRECTRDWATERMARK 0x64 ++#define CQSPI_REG_INDIRECTRDSTARTADDR 0x68 ++#define CQSPI_REG_INDIRECTRDBYTES 0x6C ++ ++#define CQSPI_REG_CMDCTRL 0x90 ++#define CQSPI_REG_CMDCTRL_EXECUTE_MASK BIT(0) ++#define CQSPI_REG_CMDCTRL_INPROGRESS_MASK BIT(1) ++#define CQSPI_REG_CMDCTRL_WR_BYTES_LSB 12 ++#define CQSPI_REG_CMDCTRL_WR_EN_LSB 15 ++#define CQSPI_REG_CMDCTRL_ADD_BYTES_LSB 16 ++#define CQSPI_REG_CMDCTRL_ADDR_EN_LSB 19 ++#define CQSPI_REG_CMDCTRL_RD_BYTES_LSB 20 ++#define CQSPI_REG_CMDCTRL_RD_EN_LSB 23 ++#define CQSPI_REG_CMDCTRL_OPCODE_LSB 24 ++#define CQSPI_REG_CMDCTRL_WR_BYTES_MASK 0x7 ++#define CQSPI_REG_CMDCTRL_ADD_BYTES_MASK 0x3 ++#define CQSPI_REG_CMDCTRL_RD_BYTES_MASK 0x7 ++ ++#define CQSPI_REG_INDIRECTWR 0x70 ++#define CQSPI_REG_INDIRECTWR_START_MASK BIT(0) ++#define CQSPI_REG_INDIRECTWR_CANCEL_MASK BIT(1) ++#define CQSPI_REG_INDIRECTWR_DONE_MASK BIT(5) ++ ++#define CQSPI_REG_INDIRECTWRWATERMARK 0x74 ++#define CQSPI_REG_INDIRECTWRSTARTADDR 0x78 ++#define CQSPI_REG_INDIRECTWRBYTES 0x7C ++ ++#define CQSPI_REG_CMDADDRESS 0x94 ++#define CQSPI_REG_CMDREADDATALOWER 0xA0 ++#define CQSPI_REG_CMDREADDATAUPPER 0xA4 ++#define CQSPI_REG_CMDWRITEDATALOWER 0xA8 ++#define CQSPI_REG_CMDWRITEDATAUPPER 0xAC ++ ++/* Interrupt status bits */ ++#define CQSPI_REG_IRQ_MODE_ERR BIT(0) ++#define CQSPI_REG_IRQ_UNDERFLOW BIT(1) ++#define CQSPI_REG_IRQ_IND_COMP BIT(2) ++#define CQSPI_REG_IRQ_IND_RD_REJECT BIT(3) ++#define CQSPI_REG_IRQ_WR_PROTECTED_ERR BIT(4) ++#define CQSPI_REG_IRQ_ILLEGAL_AHB_ERR BIT(5) ++#define CQSPI_REG_IRQ_WATERMARK BIT(6) ++#define CQSPI_REG_IRQ_IND_SRAM_FULL BIT(12) ++ ++#define CQSPI_IRQ_MASK_RD (CQSPI_REG_IRQ_WATERMARK | \ ++ CQSPI_REG_IRQ_IND_SRAM_FULL | \ ++ CQSPI_REG_IRQ_IND_COMP) ++ ++#define CQSPI_IRQ_MASK_WR (CQSPI_REG_IRQ_IND_COMP | \ ++ CQSPI_REG_IRQ_WATERMARK | \ ++ CQSPI_REG_IRQ_UNDERFLOW) ++ ++#define CQSPI_IRQ_STATUS_MASK 0x1FFFF ++ ++static int cqspi_wait_for_bit(void __iomem *reg, const u32 mask, bool clear) ++{ ++ unsigned long end = jiffies + msecs_to_jiffies(CQSPI_TIMEOUT_MS); ++ u32 val; ++ ++ while (1) { ++ val = readl(reg); ++ if (clear) ++ val = ~val; ++ val &= mask; ++ ++ if (val == mask) ++ return 0; ++ ++ if (time_after(jiffies, end)) ++ return -ETIMEDOUT; ++ } ++} ++ ++static bool cqspi_is_idle(struct cqspi_st *cqspi) ++{ ++ u32 reg = readl(cqspi->iobase + CQSPI_REG_CONFIG); ++ ++ return reg & (1 << CQSPI_REG_CONFIG_IDLE_LSB); ++} ++ ++static u32 cqspi_get_rd_sram_level(struct cqspi_st *cqspi) ++{ ++ u32 reg = readl(cqspi->iobase + CQSPI_REG_SDRAMLEVEL); ++ ++ reg >>= CQSPI_REG_SDRAMLEVEL_RD_LSB; ++ return reg & CQSPI_REG_SDRAMLEVEL_RD_MASK; ++} ++ ++static irqreturn_t cqspi_irq_handler(int this_irq, void *dev) ++{ ++ struct cqspi_st *cqspi = dev; ++ unsigned int irq_status; ++ ++ /* Read interrupt status */ ++ irq_status = readl(cqspi->iobase + CQSPI_REG_IRQSTATUS); ++ ++ /* Clear interrupt */ ++ writel(irq_status, cqspi->iobase + CQSPI_REG_IRQSTATUS); ++ ++ irq_status &= CQSPI_IRQ_MASK_RD | CQSPI_IRQ_MASK_WR; ++ ++ if (irq_status) ++ complete(&cqspi->transfer_complete); ++ ++ return IRQ_HANDLED; ++} ++ ++static unsigned int cqspi_calc_rdreg(struct spi_nor *nor, const u8 opcode) ++{ ++ unsigned int rdreg = 0; ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ ++ rdreg |= f_pdata->inst_width << CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB; ++ rdreg |= f_pdata->addr_width << CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB; ++ ++ if (nor->flash_read == SPI_NOR_QUAD) ++ rdreg |= CQSPI_INST_TYPE_QUAD ++ << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB; ++ return rdreg; ++} ++ ++static int cqspi_wait_idle(struct cqspi_st *cqspi) ++{ ++ const unsigned int poll_idle_retry = 3; ++ unsigned int count = 0; ++ unsigned long timeout; ++ ++ timeout = jiffies + msecs_to_jiffies(CQSPI_TIMEOUT_MS); ++ while (1) { ++ /* ++ * Read few times in succession to ensure the controller ++ * is indeed idle, that is, the bit does not transition ++ * low again. ++ */ ++ if (cqspi_is_idle(cqspi)) ++ count++; ++ else ++ count = 0; ++ ++ if (count >= poll_idle_retry) ++ return 0; ++ ++ if (time_after(jiffies, timeout)) { ++ /* Timeout, in busy mode. */ ++ dev_err(&cqspi->pdev->dev, ++ "QSPI is still busy after %dms timeout.\n", ++ CQSPI_TIMEOUT_MS); ++ return -ETIMEDOUT; ++ } ++ ++ cpu_relax(); ++ } ++} ++ ++static int cqspi_exec_flash_cmd(struct cqspi_st *cqspi, unsigned int reg) ++{ ++ void __iomem *reg_base = cqspi->iobase; ++ int ret; ++ ++ /* Write the CMDCTRL without start execution. */ ++ writel(reg, reg_base + CQSPI_REG_CMDCTRL); ++ /* Start execute */ ++ reg |= CQSPI_REG_CMDCTRL_EXECUTE_MASK; ++ writel(reg, reg_base + CQSPI_REG_CMDCTRL); ++ ++ /* Polling for completion. */ ++ ret = cqspi_wait_for_bit(reg_base + CQSPI_REG_CMDCTRL, ++ CQSPI_REG_CMDCTRL_INPROGRESS_MASK, 1); ++ if (ret) { ++ dev_err(&cqspi->pdev->dev, ++ "Flash command execution timed out.\n"); ++ return ret; ++ } ++ ++ /* Polling QSPI idle status. */ ++ return cqspi_wait_idle(cqspi); ++} ++ ++static int cqspi_command_read(struct spi_nor *nor, ++ const u8 *txbuf, const unsigned n_tx, ++ u8 *rxbuf, const unsigned n_rx) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ void __iomem *reg_base = cqspi->iobase; ++ unsigned int rdreg; ++ unsigned int reg; ++ unsigned int read_len; ++ int status; ++ ++ if (!n_rx || n_rx > CQSPI_STIG_DATA_LEN_MAX || !rxbuf) { ++ dev_err(nor->dev, "Invalid input argument, len %d rxbuf 0x%p\n", ++ n_rx, rxbuf); ++ return -EINVAL; ++ } ++ ++ reg = txbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB; ++ ++ rdreg = cqspi_calc_rdreg(nor, txbuf[0]); ++ writel(rdreg, reg_base + CQSPI_REG_RD_INSTR); ++ ++ reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB); ++ ++ /* 0 means 1 byte. */ ++ reg |= (((n_rx - 1) & CQSPI_REG_CMDCTRL_RD_BYTES_MASK) ++ << CQSPI_REG_CMDCTRL_RD_BYTES_LSB); ++ status = cqspi_exec_flash_cmd(cqspi, reg); ++ if (status) ++ return status; ++ ++ reg = readl(reg_base + CQSPI_REG_CMDREADDATALOWER); ++ ++ /* Put the read value into rx_buf */ ++ read_len = (n_rx > 4) ? 4 : n_rx; ++ memcpy(rxbuf, ®, read_len); ++ rxbuf += read_len; ++ ++ if (n_rx > 4) { ++ reg = readl(reg_base + CQSPI_REG_CMDREADDATAUPPER); ++ ++ read_len = n_rx - read_len; ++ memcpy(rxbuf, ®, read_len); ++ } ++ ++ return 0; ++} ++ ++static int cqspi_command_write(struct spi_nor *nor, const u8 opcode, ++ const u8 *txbuf, const unsigned n_tx) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ void __iomem *reg_base = cqspi->iobase; ++ unsigned int reg; ++ unsigned int data; ++ int ret; ++ ++ if (n_tx > 4 || (n_tx && !txbuf)) { ++ dev_err(nor->dev, ++ "Invalid input argument, cmdlen %d txbuf 0x%p\n", ++ n_tx, txbuf); ++ return -EINVAL; ++ } ++ ++ reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; ++ if (n_tx) { ++ reg |= (0x1 << CQSPI_REG_CMDCTRL_WR_EN_LSB); ++ reg |= ((n_tx - 1) & CQSPI_REG_CMDCTRL_WR_BYTES_MASK) ++ << CQSPI_REG_CMDCTRL_WR_BYTES_LSB; ++ data = 0; ++ memcpy(&data, txbuf, n_tx); ++ writel(data, reg_base + CQSPI_REG_CMDWRITEDATALOWER); ++ } ++ ++ ret = cqspi_exec_flash_cmd(cqspi, reg); ++ return ret; ++} ++ ++static int cqspi_command_write_addr(struct spi_nor *nor, ++ const u8 opcode, const unsigned int addr) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ void __iomem *reg_base = cqspi->iobase; ++ unsigned int reg; ++ ++ reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB; ++ reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB); ++ reg |= ((nor->addr_width - 1) & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK) ++ << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB; ++ ++ writel(addr, reg_base + CQSPI_REG_CMDADDRESS); ++ ++ return cqspi_exec_flash_cmd(cqspi, reg); ++} ++ ++static int cqspi_indirect_read_setup(struct spi_nor *nor, ++ const unsigned int from_addr) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ void __iomem *reg_base = cqspi->iobase; ++ unsigned int dummy_clk = 0; ++ unsigned int reg; ++ ++ writel(from_addr, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR); ++ ++ reg = nor->read_opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB; ++ reg |= cqspi_calc_rdreg(nor, nor->read_opcode); ++ ++ /* Setup dummy clock cycles */ ++ dummy_clk = nor->read_dummy; ++ if (dummy_clk > CQSPI_DUMMY_CLKS_MAX) ++ dummy_clk = CQSPI_DUMMY_CLKS_MAX; ++ ++ if (dummy_clk / 8) { ++ reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB); ++ /* Set mode bits high to ensure chip doesn't enter XIP */ ++ writel(0xFF, reg_base + CQSPI_REG_MODE_BIT); ++ ++ /* Need to subtract the mode byte (8 clocks). */ ++ if (f_pdata->inst_width != CQSPI_INST_TYPE_QUAD) ++ dummy_clk -= 8; ++ ++ if (dummy_clk) ++ reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK) ++ << CQSPI_REG_RD_INSTR_DUMMY_LSB; ++ } ++ ++ writel(reg, reg_base + CQSPI_REG_RD_INSTR); ++ ++ /* Set address width */ ++ reg = readl(reg_base + CQSPI_REG_SIZE); ++ reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; ++ reg |= (nor->addr_width - 1); ++ writel(reg, reg_base + CQSPI_REG_SIZE); ++ return 0; ++} ++ ++static int cqspi_indirect_read_execute(struct spi_nor *nor, ++ u8 *rxbuf, const unsigned n_rx) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ void __iomem *reg_base = cqspi->iobase; ++ void __iomem *ahb_base = cqspi->ahb_base; ++ unsigned int remaining = n_rx; ++ unsigned int bytes_to_read = 0; ++ int ret = 0; ++ ++ writel(remaining, reg_base + CQSPI_REG_INDIRECTRDBYTES); ++ ++ /* Clear all interrupts. */ ++ writel(CQSPI_IRQ_STATUS_MASK, reg_base + CQSPI_REG_IRQSTATUS); ++ ++ writel(CQSPI_IRQ_MASK_RD, reg_base + CQSPI_REG_IRQMASK); ++ ++ reinit_completion(&cqspi->transfer_complete); ++ writel(CQSPI_REG_INDIRECTRD_START_MASK, ++ reg_base + CQSPI_REG_INDIRECTRD); ++ ++ while (remaining > 0) { ++ ret = wait_for_completion_timeout(&cqspi->transfer_complete, ++ msecs_to_jiffies ++ (CQSPI_READ_TIMEOUT_MS)); ++ ++ bytes_to_read = cqspi_get_rd_sram_level(cqspi); ++ ++ if (!ret && bytes_to_read == 0) { ++ dev_err(nor->dev, "Indirect read timeout, no bytes\n"); ++ ret = -ETIMEDOUT; ++ goto failrd; ++ } ++ ++ while (bytes_to_read != 0) { ++ bytes_to_read *= cqspi->fifo_width; ++ bytes_to_read = bytes_to_read > remaining ? ++ remaining : bytes_to_read; ++ readsl(ahb_base, rxbuf, DIV_ROUND_UP(bytes_to_read, 4)); ++ rxbuf += bytes_to_read; ++ remaining -= bytes_to_read; ++ bytes_to_read = cqspi_get_rd_sram_level(cqspi); ++ } ++ ++ if (remaining > 0) ++ reinit_completion(&cqspi->transfer_complete); ++ } ++ ++ /* Check indirect done status */ ++ ret = cqspi_wait_for_bit(reg_base + CQSPI_REG_INDIRECTRD, ++ CQSPI_REG_INDIRECTRD_DONE_MASK, 0); ++ if (ret) { ++ dev_err(nor->dev, ++ "Indirect read completion error (%i)\n", ret); ++ goto failrd; ++ } ++ ++ /* Disable interrupt */ ++ writel(0, reg_base + CQSPI_REG_IRQMASK); ++ ++ /* Clear indirect completion status */ ++ writel(CQSPI_REG_INDIRECTRD_DONE_MASK, reg_base + CQSPI_REG_INDIRECTRD); ++ ++ return 0; ++ ++failrd: ++ /* Disable interrupt */ ++ writel(0, reg_base + CQSPI_REG_IRQMASK); ++ ++ /* Cancel the indirect read */ ++ writel(CQSPI_REG_INDIRECTWR_CANCEL_MASK, ++ reg_base + CQSPI_REG_INDIRECTRD); ++ return ret; ++} ++ ++static int cqspi_indirect_write_setup(struct spi_nor *nor, ++ const unsigned int to_addr) ++{ ++ unsigned int reg; ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ void __iomem *reg_base = cqspi->iobase; ++ ++ /* Set opcode. */ ++ reg = nor->program_opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB; ++ writel(reg, reg_base + CQSPI_REG_WR_INSTR); ++ reg = cqspi_calc_rdreg(nor, nor->program_opcode); ++ writel(reg, reg_base + CQSPI_REG_RD_INSTR); ++ ++ writel(to_addr, reg_base + CQSPI_REG_INDIRECTWRSTARTADDR); ++ ++ reg = readl(reg_base + CQSPI_REG_SIZE); ++ reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; ++ reg |= (nor->addr_width - 1); ++ writel(reg, reg_base + CQSPI_REG_SIZE); ++ return 0; ++} ++ ++static int cqspi_indirect_write_execute(struct spi_nor *nor, ++ const u8 *txbuf, const unsigned n_tx) ++{ ++ const unsigned int page_size = nor->page_size; ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ void __iomem *reg_base = cqspi->iobase; ++ unsigned int remaining = n_tx; ++ unsigned int write_bytes; ++ int ret; ++ ++ writel(remaining, reg_base + CQSPI_REG_INDIRECTWRBYTES); ++ ++ /* Clear all interrupts. */ ++ writel(CQSPI_IRQ_STATUS_MASK, reg_base + CQSPI_REG_IRQSTATUS); ++ ++ writel(CQSPI_IRQ_MASK_WR, reg_base + CQSPI_REG_IRQMASK); ++ ++ reinit_completion(&cqspi->transfer_complete); ++ writel(CQSPI_REG_INDIRECTWR_START_MASK, ++ reg_base + CQSPI_REG_INDIRECTWR); ++ ++ while (remaining > 0) { ++ write_bytes = remaining > page_size ? page_size : remaining; ++ writesl(cqspi->ahb_base, txbuf, DIV_ROUND_UP(write_bytes, 4)); ++ ++ ret = wait_for_completion_timeout(&cqspi->transfer_complete, ++ msecs_to_jiffies ++ (CQSPI_TIMEOUT_MS)); ++ if (!ret) { ++ dev_err(nor->dev, "Indirect write timeout\n"); ++ ret = -ETIMEDOUT; ++ goto failwr; ++ } ++ ++ txbuf += write_bytes; ++ remaining -= write_bytes; ++ ++ if (remaining > 0) ++ reinit_completion(&cqspi->transfer_complete); ++ } ++ ++ /* Check indirect done status */ ++ ret = cqspi_wait_for_bit(reg_base + CQSPI_REG_INDIRECTWR, ++ CQSPI_REG_INDIRECTWR_DONE_MASK, 0); ++ if (ret) { ++ dev_err(nor->dev, ++ "Indirect write completion error (%i)\n", ret); ++ goto failwr; ++ } ++ ++ /* Disable interrupt. */ ++ writel(0, reg_base + CQSPI_REG_IRQMASK); ++ ++ /* Clear indirect completion status */ ++ writel(CQSPI_REG_INDIRECTWR_DONE_MASK, reg_base + CQSPI_REG_INDIRECTWR); ++ ++ cqspi_wait_idle(cqspi); ++ ++ return 0; ++ ++failwr: ++ /* Disable interrupt. */ ++ writel(0, reg_base + CQSPI_REG_IRQMASK); ++ ++ /* Cancel the indirect write */ ++ writel(CQSPI_REG_INDIRECTWR_CANCEL_MASK, ++ reg_base + CQSPI_REG_INDIRECTWR); ++ return ret; ++} ++ ++static int cqspi_set_protocol(struct spi_nor *nor, enum spi_nor_protocol proto) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ ++ switch (proto) { ++ case SNOR_PROTO_1_1_1: ++ case SNOR_PROTO_1_1_2: ++ case SNOR_PROTO_1_1_4: ++ case SNOR_PROTO_1_2_2: ++ case SNOR_PROTO_1_4_4: ++ f_pdata->inst_width = CQSPI_INST_TYPE_SINGLE; ++ break; ++ case SNOR_PROTO_2_2_2: ++ f_pdata->inst_width = CQSPI_INST_TYPE_DUAL; ++ break; ++ case SNOR_PROTO_4_4_4: ++ f_pdata->inst_width = CQSPI_INST_TYPE_QUAD; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ switch (proto) { ++ case SNOR_PROTO_1_1_1: ++ case SNOR_PROTO_1_1_2: ++ case SNOR_PROTO_1_1_4: ++ f_pdata->addr_width = CQSPI_INST_TYPE_SINGLE; ++ break; ++ case SNOR_PROTO_1_2_2: ++ case SNOR_PROTO_2_2_2: ++ f_pdata->addr_width = CQSPI_INST_TYPE_DUAL; ++ break; ++ case SNOR_PROTO_1_4_4: ++ case SNOR_PROTO_4_4_4: ++ f_pdata->addr_width = CQSPI_INST_TYPE_QUAD; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static void cqspi_write(struct spi_nor *nor, loff_t to, ++ size_t len, size_t *retlen, const u_char *buf) ++{ ++ int ret; ++ ++ ret = cqspi_set_protocol(nor, nor->write_proto); ++ if (ret) ++ return; ++ ++ ret = cqspi_indirect_write_setup(nor, to); ++ if (ret) ++ return; ++ ++ ret = cqspi_indirect_write_execute(nor, buf, len); ++ if (ret) ++ return; ++ ++ *retlen += len; ++} ++ ++static int cqspi_read(struct spi_nor *nor, loff_t from, ++ size_t len, size_t *retlen, u_char *buf) ++{ ++ int ret; ++ ++ ret = cqspi_set_protocol(nor, nor->read_proto); ++ if (ret) ++ return ret; ++ ++ ret = cqspi_indirect_read_setup(nor, from); ++ if (ret) ++ return ret; ++ ++ ret = cqspi_indirect_read_execute(nor, buf, len); ++ if (ret) ++ return ret; ++ ++ *retlen += len; ++ return ret; ++} ++ ++static int cqspi_erase(struct spi_nor *nor, loff_t offs) ++{ ++ int ret; ++ ++ ret = cqspi_set_protocol(nor, nor->erase_proto); ++ if (ret) ++ return ret; ++ ++ /* Send write enable, then erase commands. */ ++ ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0); ++ if (ret) ++ return ret; ++ ++ /* Set up command buffer. */ ++ ret = cqspi_command_write_addr(nor, nor->erase_opcode, offs); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static unsigned int calculate_ticks_for_ns(const unsigned int ref_clk_hz, ++ const unsigned int ns_val) ++{ ++ unsigned int ticks; ++ ++ ticks = ref_clk_hz / 1000; /* kHz */ ++ ticks = DIV_ROUND_UP(ticks * ns_val, 1000000); ++ ++ return ticks; ++} ++ ++static void cqspi_delay(struct spi_nor *nor, const unsigned int sclk_hz) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ void __iomem *iobase = cqspi->iobase; ++ const unsigned int ref_clk_hz = cqspi->master_ref_clk_hz; ++ unsigned int tshsl, tchsh, tslch, tsd2d; ++ unsigned int reg; ++ unsigned int tsclk; ++ ++ /* calculate the number of ref ticks for one sclk tick */ ++ tsclk = (ref_clk_hz + sclk_hz - 1) / sclk_hz; ++ ++ tshsl = calculate_ticks_for_ns(ref_clk_hz, f_pdata->tshsl_ns); ++ /* this particular value must be at least one sclk */ ++ if (tshsl < tsclk) ++ tshsl = tsclk; ++ ++ tchsh = calculate_ticks_for_ns(ref_clk_hz, f_pdata->tchsh_ns); ++ tslch = calculate_ticks_for_ns(ref_clk_hz, f_pdata->tslch_ns); ++ tsd2d = calculate_ticks_for_ns(ref_clk_hz, f_pdata->tsd2d_ns); ++ ++ reg = (tshsl & CQSPI_REG_DELAY_TSHSL_MASK) ++ << CQSPI_REG_DELAY_TSHSL_LSB; ++ reg |= (tchsh & CQSPI_REG_DELAY_TCHSH_MASK) ++ << CQSPI_REG_DELAY_TCHSH_LSB; ++ reg |= (tslch & CQSPI_REG_DELAY_TSLCH_MASK) ++ << CQSPI_REG_DELAY_TSLCH_LSB; ++ reg |= (tsd2d & CQSPI_REG_DELAY_TSD2D_MASK) ++ << CQSPI_REG_DELAY_TSD2D_LSB; ++ writel(reg, iobase + CQSPI_REG_DELAY); ++} ++ ++static void cqspi_config_baudrate_div(struct cqspi_st *cqspi, ++ const unsigned int sclk_hz) ++{ ++ const unsigned int ref_clk_hz = cqspi->master_ref_clk_hz; ++ void __iomem *reg_base = cqspi->iobase; ++ unsigned int reg; ++ unsigned int div; ++ ++ reg = readl(reg_base + CQSPI_REG_CONFIG); ++ reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB); ++ ++ div = ref_clk_hz / sclk_hz; ++ ++ /* Recalculate the baudrate divisor based on QSPI specification. */ ++ if (div > 32) ++ div = 32; ++ ++ /* Check if even number. */ ++ if (div & 1) ++ div = (div / 2); ++ else ++ div = (div / 2) - 1; ++ ++ div = (div & CQSPI_REG_CONFIG_BAUD_MASK) << CQSPI_REG_CONFIG_BAUD_LSB; ++ reg |= div; ++ writel(reg, reg_base + CQSPI_REG_CONFIG); ++} ++ ++static void cqspi_readdata_capture(struct cqspi_st *cqspi, ++ const unsigned int bypass, ++ const unsigned int delay) ++{ ++ void __iomem *reg_base = cqspi->iobase; ++ unsigned int reg; ++ ++ reg = readl(reg_base + CQSPI_REG_READCAPTURE); ++ ++ if (bypass) ++ reg |= (1 << CQSPI_REG_READCAPTURE_BYPASS_LSB); ++ else ++ reg &= ~(1 << CQSPI_REG_READCAPTURE_BYPASS_LSB); ++ ++ reg &= ~(CQSPI_REG_READCAPTURE_DELAY_MASK ++ << CQSPI_REG_READCAPTURE_DELAY_LSB); ++ ++ reg |= (delay & CQSPI_REG_READCAPTURE_DELAY_MASK) ++ << CQSPI_REG_READCAPTURE_DELAY_LSB; ++ ++ writel(reg, reg_base + CQSPI_REG_READCAPTURE); ++} ++ ++static void cqspi_chipselect(struct spi_nor *nor) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ void __iomem *reg_base = cqspi->iobase; ++ unsigned int chip_select = f_pdata->cs; ++ unsigned int reg; ++ ++ reg = readl(reg_base + CQSPI_REG_CONFIG); ++ if (cqspi->is_decoded_cs) { ++ reg |= CQSPI_REG_CONFIG_DECODE_MASK; ++ } else { ++ reg &= ~CQSPI_REG_CONFIG_DECODE_MASK; ++ ++ /* Convert CS if without decoder. ++ * CS0 to 4b'1110 ++ * CS1 to 4b'1101 ++ * CS2 to 4b'1011 ++ * CS3 to 4b'0111 ++ */ ++ chip_select = 0xF & ~(1 << chip_select); ++ } ++ ++ reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK ++ << CQSPI_REG_CONFIG_CHIPSELECT_LSB); ++ reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK) ++ << CQSPI_REG_CONFIG_CHIPSELECT_LSB; ++ writel(reg, reg_base + CQSPI_REG_CONFIG); ++} ++ ++static void cqspi_controller_enable(struct cqspi_st *cqspi, bool enable) ++{ ++ void __iomem *reg_base = cqspi->iobase; ++ unsigned int reg; ++ ++ reg = readl(reg_base + CQSPI_REG_CONFIG); ++ ++ if (enable) ++ reg |= CQSPI_REG_CONFIG_ENABLE_MASK; ++ else ++ reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK; ++ ++ writel(reg, reg_base + CQSPI_REG_CONFIG); ++} ++ ++static void cqspi_switch_cs(struct spi_nor *nor) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ void __iomem *iobase = cqspi->iobase; ++ unsigned int reg; ++ ++ /* configure page size and block size. */ ++ reg = readl(iobase + CQSPI_REG_SIZE); ++ reg &= ~(CQSPI_REG_SIZE_PAGE_MASK << CQSPI_REG_SIZE_PAGE_LSB); ++ reg &= ~(CQSPI_REG_SIZE_BLOCK_MASK << CQSPI_REG_SIZE_BLOCK_LSB); ++ reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK; ++ reg |= (nor->page_size << CQSPI_REG_SIZE_PAGE_LSB); ++ reg |= (ilog2(nor->mtd.erasesize) << CQSPI_REG_SIZE_BLOCK_LSB); ++ reg |= (nor->addr_width - 1); ++ writel(reg, iobase + CQSPI_REG_SIZE); ++ ++ /* configure the chip select */ ++ cqspi_chipselect(nor); ++} ++ ++static int cqspi_prep_unlocked(struct spi_nor *nor, enum spi_nor_ops ops) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ const unsigned int sclk = f_pdata->clk_rate; ++ const int switch_cs = (cqspi->current_cs != f_pdata->cs); ++ const int switch_ck = (cqspi->sclk != sclk); ++ ++ if (switch_cs || switch_ck) ++ cqspi_controller_enable(cqspi, 0); ++ ++ /* Switch chip select. */ ++ if (switch_cs) { ++ cqspi->current_cs = f_pdata->cs; ++ cqspi_switch_cs(nor); ++ } ++ ++ /* Setup baudrate divisor and delays */ ++ if (switch_ck) { ++ cqspi->sclk = sclk; ++ cqspi_config_baudrate_div(cqspi, sclk); ++ cqspi_delay(nor, sclk); ++ cqspi_readdata_capture(cqspi, 1, f_pdata->read_delay); ++ } ++ ++ if (switch_cs || switch_ck) ++ cqspi_controller_enable(cqspi, 1); ++ ++ return 0; ++} ++ ++static int cqspi_prep(struct spi_nor *nor, enum spi_nor_ops ops) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ ++ mutex_lock(&cqspi->bus_mutex); ++ ++ return cqspi_prep_unlocked(nor, ops); ++} ++ ++static void cqspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops) ++{ ++ struct cqspi_flash_pdata *f_pdata = nor->priv; ++ struct cqspi_st *cqspi = f_pdata->cqspi; ++ ++ mutex_unlock(&cqspi->bus_mutex); ++} ++ ++static int cqspi_read_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) ++{ ++ int ret; ++ ++ ret = cqspi_set_protocol(nor, nor->reg_proto); ++ if (ret) ++ goto exit; ++ ++ cqspi_prep_unlocked(nor, SPI_NOR_OPS_READ); ++ ++ ret = cqspi_command_read(nor, &opcode, 1, buf, len); ++exit: ++ return ret; ++} ++ ++static int cqspi_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len) ++{ ++ int ret; ++ ++ ret = cqspi_set_protocol(nor, nor->reg_proto); ++ if (ret) ++ goto exit; ++ ++ cqspi_prep_unlocked(nor, SPI_NOR_OPS_WRITE); ++ ++ ret = cqspi_command_write(nor, opcode, buf, len); ++exit: ++ return ret; ++} ++ ++static int cqspi_of_get_flash_pdata(struct platform_device *pdev, ++ struct cqspi_flash_pdata *f_pdata, ++ struct device_node *np) ++{ ++ if (of_property_read_u32(np, "cdns,read-delay", &f_pdata->read_delay)) { ++ dev_err(&pdev->dev, "couldn't determine read-delay\n"); ++ return -ENXIO; ++ } ++ ++ if (of_property_read_u32(np, "cdns,tshsl-ns", &f_pdata->tshsl_ns)) { ++ dev_err(&pdev->dev, "couldn't determine tshsl-ns\n"); ++ return -ENXIO; ++ } ++ ++ if (of_property_read_u32(np, "cdns,tsd2d-ns", &f_pdata->tsd2d_ns)) { ++ dev_err(&pdev->dev, "couldn't determine tsd2d-ns\n"); ++ return -ENXIO; ++ } ++ ++ if (of_property_read_u32(np, "cdns,tchsh-ns", &f_pdata->tchsh_ns)) { ++ dev_err(&pdev->dev, "couldn't determine tchsh-ns\n"); ++ return -ENXIO; ++ } ++ ++ if (of_property_read_u32(np, "cdns,tslch-ns", &f_pdata->tslch_ns)) { ++ dev_err(&pdev->dev, "couldn't determine tslch-ns\n"); ++ return -ENXIO; ++ } ++ ++ if (of_property_read_u32(np, "spi-max-frequency", &f_pdata->clk_rate)) { ++ dev_err(&pdev->dev, "couldn't determine spi-max-frequency\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static int cqspi_of_get_pdata(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct cqspi_st *cqspi = platform_get_drvdata(pdev); ++ ++ cqspi->is_decoded_cs = of_property_read_bool(np, "cdns,is-decoded-cs"); ++ ++ if (of_property_read_u32(np, "cdns,fifo-depth", &cqspi->fifo_depth)) { ++ dev_err(&pdev->dev, "couldn't determine fifo-depth\n"); ++ return -ENXIO; ++ } ++ ++ if (of_property_read_u32(np, "cdns,fifo-width", &cqspi->fifo_width)) { ++ dev_err(&pdev->dev, "couldn't determine fifo-width\n"); ++ return -ENXIO; ++ } ++ ++ if (of_property_read_u32(np, "cdns,trigger-address", ++ &cqspi->trigger_address)) { ++ dev_err(&pdev->dev, "couldn't determine trigger-address\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static void cqspi_controller_init(struct cqspi_st *cqspi) ++{ ++ cqspi_controller_enable(cqspi, 0); ++ ++ /* Configure the remap address register, no remap */ ++ writel(0, cqspi->iobase + CQSPI_REG_REMAP); ++ ++ /* Disable all interrupts. */ ++ writel(0, cqspi->iobase + CQSPI_REG_IRQMASK); ++ ++ /* Configure the SRAM split to 1:1 . */ ++ writel(cqspi->fifo_depth / 2, cqspi->iobase + CQSPI_REG_SRAMPARTITION); ++ ++ /* Load indirect trigger address. */ ++ writel(cqspi->trigger_address, ++ cqspi->iobase + CQSPI_REG_INDIRECTTRIGGER); ++ ++ /* Program read watermark -- 1/2 of the FIFO. */ ++ writel(cqspi->fifo_depth * cqspi->fifo_width / 2, ++ cqspi->iobase + CQSPI_REG_INDIRECTRDWATERMARK); ++ /* Program write watermark -- 1/8 of the FIFO. */ ++ writel(cqspi->fifo_depth * cqspi->fifo_width / 8, ++ cqspi->iobase + CQSPI_REG_INDIRECTWRWATERMARK); ++ ++ cqspi_controller_enable(cqspi, 1); ++} ++ ++static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np) ++{ ++ struct platform_device *pdev = cqspi->pdev; ++ struct device *dev = &pdev->dev; ++ struct cqspi_flash_pdata *f_pdata; ++ struct spi_nor *nor; ++ struct mtd_info *mtd; ++ unsigned int cs; ++ int i, ret; ++ ++ /* Get flash device data */ ++ for_each_available_child_of_node(dev->of_node, np) { ++ if (of_property_read_u32(np, "reg", &cs)) { ++ dev_err(dev, "Couldn't determine chip select.\n"); ++ goto err; ++ } ++ ++ if (cs > CQSPI_MAX_CHIPSELECT) { ++ dev_err(dev, "Chip select %d out of range.\n", cs); ++ goto err; ++ } ++ ++ f_pdata = &cqspi->f_pdata[cs]; ++ f_pdata->cqspi = cqspi; ++ f_pdata->cs = cs; ++ ++ ret = cqspi_of_get_flash_pdata(pdev, f_pdata, np); ++ if (ret) ++ goto err; ++ ++ nor = &f_pdata->nor; ++ mtd = &nor->mtd; ++ ++ mtd->priv = nor; ++ ++ nor->dev = dev; ++ spi_nor_set_flash_node(nor, np); ++ nor->priv = f_pdata; ++ ++ nor->read_reg = cqspi_read_reg; ++ nor->write_reg = cqspi_write_reg; ++ nor->read = cqspi_read; ++ nor->write = cqspi_write; ++ nor->erase = cqspi_erase; ++ nor->prepare = cqspi_prep; ++ nor->unprepare = cqspi_unprep; ++ ++ mtd->name = kasprintf(GFP_KERNEL, "%s.%d", dev_name(dev), cs); ++ if (!mtd->name) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD); ++ if (ret) ++ goto err; ++ ++ ret = mtd_device_register(mtd, NULL, 0); ++ if (ret) ++ goto err; ++ } ++ ++ return 0; ++ ++err: ++ for (i = 0; i < CQSPI_MAX_CHIPSELECT; i++) ++ if (cqspi->f_pdata[i].nor.mtd.name) { ++ mtd_device_unregister(&cqspi->f_pdata[i].nor.mtd); ++ kfree(cqspi->f_pdata[i].nor.mtd.name); ++ } ++ return ret; ++} ++ ++static int cqspi_probe(struct platform_device *pdev) ++{ ++ struct device_node *np = pdev->dev.of_node; ++ struct device *dev = &pdev->dev; ++ struct cqspi_st *cqspi; ++ struct resource *res; ++ struct resource *res_ahb; ++ int ret; ++ int irq; ++ ++ cqspi = devm_kzalloc(dev, sizeof(*cqspi), GFP_KERNEL); ++ if (!cqspi) ++ return -ENOMEM; ++ ++ mutex_init(&cqspi->bus_mutex); ++ cqspi->pdev = pdev; ++ platform_set_drvdata(pdev, cqspi); ++ ++ /* Obtain configuration from OF. */ ++ ret = cqspi_of_get_pdata(pdev); ++ if (ret) { ++ dev_err(dev, "Cannot get mandatory OF data.\n"); ++ return -ENODEV; ++ } ++ ++ /* Obtain QSPI clock. */ ++ cqspi->clk = devm_clk_get(dev, NULL); ++ if (IS_ERR(cqspi->clk)) { ++ dev_err(dev, "Cannot claim QSPI clock.\n"); ++ return PTR_ERR(cqspi->clk); ++ } ++ ++ /* Obtain and remap controller address. */ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ cqspi->iobase = devm_ioremap_resource(dev, res); ++ if (IS_ERR(cqspi->iobase)) { ++ dev_err(dev, "Cannot remap controller address.\n"); ++ return PTR_ERR(cqspi->iobase); ++ } ++ ++ /* Obtain and remap AHB address. */ ++ res_ahb = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ cqspi->ahb_base = devm_ioremap_resource(dev, res_ahb); ++ if (IS_ERR(cqspi->ahb_base)) { ++ dev_err(dev, "Cannot remap AHB address.\n"); ++ return PTR_ERR(cqspi->ahb_base); ++ } ++ ++ init_completion(&cqspi->transfer_complete); ++ ++ /* Obtain IRQ line. */ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(dev, "Cannot obtain IRQ.\n"); ++ return -ENXIO; ++ } ++ ++ ret = clk_prepare_enable(cqspi->clk); ++ if (ret) { ++ dev_err(dev, "Cannot enable QSPI clock.\n"); ++ return ret; ++ } ++ ++ cqspi->master_ref_clk_hz = clk_get_rate(cqspi->clk); ++ ++ ret = devm_request_irq(dev, irq, cqspi_irq_handler, 0, ++ pdev->name, cqspi); ++ if (ret) { ++ dev_err(dev, "Cannot request IRQ.\n"); ++ goto probe_irq_failed; ++ } ++ ++ cqspi_wait_idle(cqspi); ++ cqspi_controller_init(cqspi); ++ cqspi->current_cs = -1; ++ cqspi->sclk = 0; ++ ++ ret = cqspi_setup_flash(cqspi, np); ++ if (ret) { ++ dev_err(dev, "Cadence QSPI NOR probe failed %d\n", ret); ++ goto probe_setup_failed; ++ } ++ ++ return ret; ++probe_irq_failed: ++ cqspi_controller_enable(cqspi, 0); ++probe_setup_failed: ++ clk_disable_unprepare(cqspi->clk); ++ return ret; ++} ++ ++static int cqspi_remove(struct platform_device *pdev) ++{ ++ struct cqspi_st *cqspi = platform_get_drvdata(pdev); ++ int i; ++ ++ cqspi_controller_enable(cqspi, 0); ++ ++ for (i = 0; i < CQSPI_MAX_CHIPSELECT; i++) ++ if (cqspi->f_pdata[i].nor.mtd.name) { ++ mtd_device_unregister(&cqspi->f_pdata[i].nor.mtd); ++ kfree(cqspi->f_pdata[i].nor.mtd.name); ++ } ++ ++ clk_disable_unprepare(cqspi->clk); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM_SLEEP ++static int cqspi_suspend(struct device *dev) ++{ ++ struct cqspi_st *cqspi = dev_get_drvdata(dev); ++ ++ cqspi_controller_enable(cqspi, 0); ++ return 0; ++} ++ ++static int cqspi_resume(struct device *dev) ++{ ++ struct cqspi_st *cqspi = dev_get_drvdata(dev); ++ ++ cqspi_controller_enable(cqspi, 1); ++ return 0; ++} ++ ++static const struct dev_pm_ops cqspi__dev_pm_ops = { ++ .suspend = cqspi_suspend, ++ .resume = cqspi_resume, ++}; ++ ++#define CQSPI_DEV_PM_OPS (&cqspi__dev_pm_ops) ++#else ++#define CQSPI_DEV_PM_OPS NULL ++#endif ++ ++static struct of_device_id const cqspi_dt_ids[] = { ++ {.compatible = "cdns,qspi-nor",}, ++ { /* end of table */ } ++}; ++ ++MODULE_DEVICE_TABLE(of, cqspi_dt_ids); ++ ++static struct platform_driver cqspi_platform_driver = { ++ .probe = cqspi_probe, ++ .remove = cqspi_remove, ++ .driver = { ++ .name = CQSPI_NAME, ++ .pm = CQSPI_DEV_PM_OPS, ++ .of_match_table = cqspi_dt_ids, ++ }, ++}; ++ ++module_platform_driver(cqspi_platform_driver); ++ ++MODULE_DESCRIPTION("Cadence QSPI Controller Driver"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:" CQSPI_NAME); ++MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>"); ++MODULE_AUTHOR("Graham Moore <grmoore@opensource.altera.com>"); +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0032-ARM-socfpga-Add-Candence-QSPI-controller-DT-node.patch b/target/linux/socfpga/patches-4.4/0032-ARM-socfpga-Add-Candence-QSPI-controller-DT-node.patch new file mode 100644 index 0000000000..e6b4ec7a44 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0032-ARM-socfpga-Add-Candence-QSPI-controller-DT-node.patch @@ -0,0 +1,42 @@ +From 8abee0bfd7bc652e028e51e2b95cbb3bf42fc152 Mon Sep 17 00:00:00 2001 +From: Stefan Roese <sr@denx.de> +Date: Wed, 20 May 2015 10:32:03 +0200 +Subject: [PATCH 32/33] ARM: socfpga: Add Candence QSPI controller DT node + +Now that the device driver is available, lets add the controller node +to the socfpga dtsi. So that the SPI NOR flash can be used. + +Signed-off-by: Stefan Roese <sr@denx.de> +Signed-off-by: Marek Vasut <marex@denx.de> +--- + arch/arm/boot/dts/socfpga.dtsi | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi +index 3ed4abd..ebcd081 100644 +--- a/arch/arm/boot/dts/socfpga.dtsi ++++ b/arch/arm/boot/dts/socfpga.dtsi +@@ -685,6 +685,20 @@ + reg = <0xffff0000 0x10000>; + }; + ++ qspi: spi@ff705000 { ++ compatible = "cdns,qspi-nor"; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ reg = <0xff705000 0x1000>, ++ <0xffa00000 0x1000>; ++ interrupts = <0 151 4>; ++ clocks = <&qspi_clk>; ++ cdns,fifo-depth = <128>; ++ cdns,fifo-width = <4>; ++ cdns,trigger-address = <0x00000000>; ++ status = "disabled"; ++ }; ++ + rst: rstmgr@ffd05000 { + #reset-cells = <1>; + compatible = "altr,rst-mgr"; +-- +2.8.1 + diff --git a/target/linux/socfpga/patches-4.4/0033-ARM-socfpga-Enable-QSPI-flash-on-SoCKit.patch b/target/linux/socfpga/patches-4.4/0033-ARM-socfpga-Enable-QSPI-flash-on-SoCKit.patch new file mode 100644 index 0000000000..f242d8cb72 --- /dev/null +++ b/target/linux/socfpga/patches-4.4/0033-ARM-socfpga-Enable-QSPI-flash-on-SoCKit.patch @@ -0,0 +1,47 @@ +From 8c1cd66d13406533f9948dbcd25d4b53d389c109 Mon Sep 17 00:00:00 2001 +From: Marek Vasut <marex@denx.de> +Date: Sun, 21 Jun 2015 23:00:06 +0200 +Subject: [PATCH 33/33] ARM: socfpga: Enable QSPI flash on SoCKit + +Add the QSPI NOR node on SoCkit. + +Signed-off-by: Marek Vasut <marex@denx.de> +--- + arch/arm/boot/dts/socfpga_cyclone5_sockit.dts | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts b/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts +index 02e22f5..d706348 100644 +--- a/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts ++++ b/arch/arm/boot/dts/socfpga_cyclone5_sockit.dts +@@ -175,6 +175,27 @@ + status = "okay"; + }; + ++&qspi { ++ status = "okay"; ++ ++ flash0: n25q00@0 { ++ #address-cells = <1>; ++ #size-cells = <1>; ++ compatible = "n25q00"; ++ reg = <0>; /* chip select */ ++ spi-max-frequency = <100000000>; ++ m25p,fast-read; ++ ++ cdns,page-size = <256>; ++ cdns,block-size = <16>; ++ cdns,read-delay = <4>; ++ cdns,tshsl-ns = <50>; ++ cdns,tsd2d-ns = <50>; ++ cdns,tchsh-ns = <4>; ++ cdns,tslch-ns = <4>; ++ }; ++}; ++ + &usb1 { + status = "okay"; + }; +-- +2.8.1 + |