mtd: rawnand: brcmnand: Add support for flash-edu for dma transfers
Subsystems (7)
v4 cover mtd: rawnand: brcmnand: Add support for flash-edu for dma transfers
V2 Changes:
Refactored drivers/mtd/nand/raw/brcmnand/brcmnand.c based on initial review comments
V3 Changes :
No change ignore this patch set
V4 changes :
- Included cover letter to resend the patch set .../devicetree/bindings/mtd/brcm,brcmnand.txt - Added Reviewed-by tag
arch/mips/boot/dts/brcm/bcm7425.dtsi - Added Reviwed-by, Acked-by tag
drivers/mtd/nand/raw/brcmnand/brcmnand.c - refactored code based on V2 review changes
Kamal Dasu (3):
dt: bindings: brcmnand: Add support for flash-edu
arch: mips: brcm: Add 7425 flash-edu support
mtd: rawnand: brcmnand: Add support for flash-edu for dma transfers
.../devicetree/bindings/mtd/brcm,brcmnand.txt | 10 +- arch/mips/boot/dts/brcm/bcm7425.dtsi | 4 +- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 293 +++++++++++++++++- 3 files changed, 294 insertions(+), 13 deletions(-)
Signature · 1 line
2.17.1
PATCH v4 1/3 dt: bindings: brcmnand: Add support for flash-edu
Adding support for EBI DMA unit (EDU).
--- .../devicetree/bindings/mtd/brcm,brcmnand.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
Documentation/devicetree/bindings/mtd/brcm,brcmnand.txt +5 -5
@@ -35,11 +35,11 @@ Required properties: (optional) NAND flash cache range (if at non-standard offset) - reg-names : a list of the names corresponding to the previous register ranges. Should contain "nand" and (optionally) - "flash-dma" and/or "nand-cache". -- interrupts : The NAND CTLRDY interrupt and (if Flash DMA is available) - FLASH_DMA_DONE -- interrupt-names : May be "nand_ctlrdy" or "flash_dma_done", if broken out as - individual interrupts. + "flash-dma" or "flash-edu" and/or "nand-cache". +- interrupts : The NAND CTLRDY interrupt, (if Flash DMA is available) + FLASH_DMA_DONE and if EDU is avaialble and used FLASH_EDU_DONE +- interrupt-names : May be "nand_ctlrdy" or "flash_dma_done" or "flash_edu_done", + if broken out as individual interrupts. May be "nand", if the SoC has the individual NAND interrupts multiplexed behind another custom piece of hardware
Signature · 1 line
2.17.1
Re: [PATCH V4 1/3] dt: bindings: brcmnand: Add support for flash-edu
Quoted 4 lines · Kamal Dasu
Adding support for EBI DMA unit (EDU).
Signed-off-by Kamal Dasu <[email protected]>Reviewed-by Rob Herring <[email protected]>
Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next, thanks.
Miquel
PATCH v4 2/3 arch: mips: brcm: Add 7425 flash-edu support
In reply to Kamal DasuNand controller v5.0 and v6.0 have nand edu blocks that enable dma nand flash transfers. This allows for faster read and write access.
--- arch/mips/boot/dts/brcm/bcm7425.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/mips/boot/dts/brcm/bcm7425.dtsi +2 -2
@@ -403,8 +403,8 @@ compatible = "brcm,brcmnand-v5.0", "brcm,brcmnand"; #address-cells = <1>; #size-cells = <0>; - reg-names = "nand"; - reg = <0x41b800 0x400>; + reg-names = "nand", "flash-edu"; + reg = <0x41b800 0x400>, <0x41bc00 0x24>; interrupt-parent = <&hif_l2_intc>; interrupts = <24>; status = "disabled";
Signature · 1 line
2.17.1
Re: [PATCH V4 2/3] arch: mips: brcm: Add 7425 flash-edu support
Quoted 7 lines · Kamal Dasu
Nand controller v5.0 and v6.0 have nand edu blocks that enable dma nand flash transfers. This allows for faster read and write access.
Signed-off-by Kamal Dasu <[email protected]>Acked-by Paul Burton <[email protected]>Reviewed-by Florian Fainelli <[email protected]>
Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next, thanks.
Miquel
PATCH v4 3/3 mtd: rawnand: brcmnand: Add support for flash-edu for dma transfers
In reply to Kamal DasuLegacy mips soc platforms that have controller v5.0 and 6.0 use flash-edu block for dma transfers. This change adds support for nand dma transfers using the EDU block.
--- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 293 ++++++++++++++++++++++- 1 file changed, 287 insertions(+), 6 deletions(-)
drivers/mtd/nand/raw/brcmnand/brcmnand.c +287 -6
@@ -102,6 +102,45 @@ struct brcm_nand_dma_desc {#define NAND_CTRL_RDY (INTFC_CTLR_READY | INTFC_FLASH_READY)#define NAND_POLL_STATUS_TIMEOUT_MS 100 +#define EDU_CMD_WRITE 0x00 +#define EDU_CMD_READ 0x01 +#define EDU_STATUS_ACTIVE BIT(0) +#define EDU_ERR_STATUS_ERRACK BIT(0) +#define EDU_DONE_MASK GENMASK(1, 0) + +#define EDU_CONFIG_MODE_NAND BIT(0) +#define EDU_CONFIG_SWAP_BYTE BIT(1) +#ifdef CONFIG_CPU_BIG_ENDIAN +#define EDU_CONFIG_SWAP_CFG EDU_CONFIG_SWAP_BYTE +#else +#define EDU_CONFIG_SWAP_CFG 0 +#endif + +/* edu registers */ +enum edu_reg { +EDU_CONFIG =0 , +EDU_DRAM_ADDR , +EDU_EXT_ADDR , +EDU_LENGTH , +EDU_CMD , +EDU_STOP , +EDU_STATUS , +EDU_DONE , +EDU_ERR_STATUS , +}; + +static const u16 edu_regs[] = { + [EDU_CONFIG ] =0x00 , + [EDU_DRAM_ADDR ] =0x04 , + [EDU_EXT_ADDR ] =0x08 , + [EDU_LENGTH ] =0x0c , + [EDU_CMD ] =0x10 , + [EDU_STOP ] =0x14 , + [EDU_STATUS ] =0x18 , + [EDU_DONE ] =0x1c , + [EDU_ERR_STATUS ] =0x20 , +}; +/* flash_dma registers */ enum flash_dma_reg {FLASH_DMA_REVISION =0 ,
@@ -167,6 +206,8 @@ enum { BRCMNAND_HAS_WP = BIT(3), }; +struct brcmnand_host ; +struct brcmnand_controller {struct device *dev;struct nand_controller controller;
@@ -185,17 +226,32 @@ struct brcmnand_controller {int cmd_pending;bool dma_pending; +bool edu_pending;struct completion done;struct completion dma_done; +struct completion edu_done;/* List of NAND hosts (one for each chip-select) */ struct list_head host_list; +/* EDU info, per-transaction */ +const u16 *edu_offsets; +void __iomem *edu_base; +int edu_irq; +int edu_count; +u64 edu_dram_addr; +u32 edu_ext_addr; +u32 edu_cmd; +u32 edu_config; +/* flash_dma reg */ const u16 *flash_dma_offsets;struct brcm_nand_dma_desc *dma_desc;dma_addr_t dma_pa; +int (*dma_trans)(struct brcmnand_host *host,u64 addr,u32 *buf, +u32 len,u8 dma_cmd); +/* in-memory cache of the FLASH_CACHE, used only for some commands */ u8 flash_cache[FC_BYTES ];
@@ -216,6 +272,7 @@ struct brcmnand_controller {u32 nand_cs_nand_xor;u32 corr_stat_threshold;u32 flash_dma_mode; +u32 flash_edu_mode;bool pio_poll_mode; };
@@ -657,6 +714,22 @@ static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,__raw_writel (val, ctrl->nand_fc + word *4 ); } +static inline void edu_writel (struct brcmnand_controller *ctrl, +enum edu_reg reg,u32 val) +{ +u16 offs = ctrl->edu_offsets[reg]; + +brcmnand_writel (val, ctrl->edu_base + offs); +} + +static inline u32 edu_readl (struct brcmnand_controller *ctrl, +enum edu_reg reg) +{ +u16 offs = ctrl->edu_offsets[reg]; + +return brcmnand_readl (ctrl->edu_base + offs); +} +static void brcmnand_clear_ecc_addr (struct brcmnand_controller *ctrl) {
@@ -926,6 +999,16 @@ static inline bool has_flash_dma(struct brcmnand_controller *ctrl)return ctrl->flash_dma_base; } +static inline bool has_edu (struct brcmnand_controller *ctrl) +{ +return ctrl->edu_base; +} + +static inline bool use_dma (struct brcmnand_controller *ctrl) +{ +return has_flash_dma (ctrl) ||has_edu (ctrl); +} +static inline void disable_ctrl_irqs (struct brcmnand_controller *ctrl) {if (ctrl->pio_poll_mode)
@@ -1299,6 +1382,52 @@ static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i,return tbytes; } +static void brcmnand_edu_init (struct brcmnand_controller *ctrl) +{ +/* initialize edu */ +edu_writel (ctrl,EDU_ERR_STATUS ,0 ); +edu_readl (ctrl,EDU_ERR_STATUS ); +edu_writel (ctrl,EDU_DONE ,0 ); +edu_writel (ctrl,EDU_DONE ,0 ); +edu_writel (ctrl,EDU_DONE ,0 ); +edu_writel (ctrl,EDU_DONE ,0 ); +edu_readl (ctrl,EDU_DONE ); +} + +/* edu irq */ +static irqreturn_t brcmnand_edu_irq (int irq,void *data) +{ +struct brcmnand_controller *ctrl = data; + +if (ctrl->edu_count) { + ctrl->edu_count--; +while (!(edu_readl (ctrl,EDU_DONE ) &EDU_DONE_MASK )) +udelay (1 ); +edu_writel (ctrl,EDU_DONE ,0 ); +edu_readl (ctrl,EDU_DONE ); + } + +if (ctrl->edu_count) { + ctrl->edu_dram_addr +=FC_BYTES ; + ctrl->edu_ext_addr +=FC_BYTES ; + +edu_writel (ctrl,EDU_DRAM_ADDR , (u32 )ctrl->edu_dram_addr); +edu_readl (ctrl,EDU_DRAM_ADDR ); +edu_writel (ctrl,EDU_EXT_ADDR , ctrl->edu_ext_addr); +edu_readl (ctrl,EDU_EXT_ADDR ); + +mb ();/* flush previous writes */ +edu_writel (ctrl,EDU_CMD , ctrl->edu_cmd); +edu_readl (ctrl,EDU_CMD ); + +return IRQ_HANDLED ; + } + +complete (&ctrl->edu_done); + +return IRQ_HANDLED ; +} +static irqreturn_t brcmnand_ctlrdy_irq (int irq,void *data) {struct brcmnand_controller *ctrl = data;
@@ -1307,6 +1436,16 @@ static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data)if (ctrl->dma_pending)return IRQ_HANDLED ; +/* check if you need to piggy back on the ctrlrdy irq */ +if (ctrl->edu_pending) { +if (irq == ctrl->irq && ((int )ctrl->edu_irq >=0 )) +/* Discard interrupts while using dedicated edu irq */ +return IRQ_HANDLED ; + +/* no registered edu irq, call handler */ +return brcmnand_edu_irq (irq, data); + } +complete (&ctrl->done);return IRQ_HANDLED ; }
@@ -1644,6 +1783,81 @@ static void brcmnand_write_buf(struct nand_chip *chip, const uint8_t *buf, } } +/** +* Kick EDU engine +*/ +static int brcmnand_edu_trans (struct brcmnand_host *host,u64 addr,u32 *buf, +u32 len,u8 cmd) +{ +struct brcmnand_controller *ctrl = host->ctrl; +unsigned long timeo =msecs_to_jiffies (200 ); +int ret =0 ; +int dir = (cmd ==CMD_PAGE_READ ?DMA_FROM_DEVICE :DMA_TO_DEVICE ); +u8 edu_cmd = (cmd ==CMD_PAGE_READ ?EDU_CMD_READ :EDU_CMD_WRITE ); +unsigned int trans = len >>FC_SHIFT ; +dma_addr_t pa; + + pa =dma_map_single (ctrl->dev, buf, len, dir); +if (dma_mapping_error (ctrl->dev, pa)) { +dev_err (ctrl->dev,"unable to map buffer for EDU DMA\n" ); +return -ENOMEM ; + } + + ctrl->edu_pending = true; + ctrl->edu_dram_addr = pa; + ctrl->edu_ext_addr = addr; + ctrl->edu_cmd = edu_cmd; + ctrl->edu_count = trans; + +edu_writel (ctrl,EDU_DRAM_ADDR , (u32 )ctrl->edu_dram_addr); +edu_readl (ctrl,EDU_DRAM_ADDR ); +edu_writel (ctrl,EDU_EXT_ADDR , ctrl->edu_ext_addr); +edu_readl (ctrl,EDU_EXT_ADDR ); +edu_writel (ctrl,EDU_LENGTH ,FC_BYTES ); +edu_readl (ctrl,EDU_LENGTH ); + +/* Start edu engine */ +mb ();/* flush previous writes */ +edu_writel (ctrl,EDU_CMD , ctrl->edu_cmd); +edu_readl (ctrl,EDU_CMD ); + +if (wait_for_completion_timeout (&ctrl->edu_done, timeo) <=0 ) { +dev_err (ctrl->dev, +"timeout waiting for EDU; status %#x, error status %#x\n" , +edu_readl (ctrl,EDU_STATUS ), +edu_readl (ctrl,EDU_ERR_STATUS )); + } + +dma_unmap_single (ctrl->dev, pa, len, dir); + +/* for program page check NAND status */ +if (((brcmnand_read_reg (ctrl,BRCMNAND_INTFC_STATUS ) & +INTFC_FLASH_STATUS ) &NAND_STATUS_FAIL ) && + edu_cmd ==EDU_CMD_WRITE ) { +dev_info (ctrl->dev,"program failed at %llx\n" , + (unsigned long long )addr); + ret = -EIO ; + } + +/* Make sure the EDU status is clean */ +if (edu_readl (ctrl,EDU_STATUS ) &EDU_STATUS_ACTIVE ) +dev_warn (ctrl->dev,"EDU still active: %#x\n" , +edu_readl (ctrl,EDU_STATUS )); + +if (unlikely (edu_readl (ctrl,EDU_ERR_STATUS ) &EDU_ERR_STATUS_ERRACK )) { +dev_warn (ctrl->dev,"EDU RBUS error at addr %llx\n" , + (unsigned long long )addr); + ret = -EIO ; + } + + ctrl->edu_pending = false; +brcmnand_edu_init (ctrl); +edu_writel (ctrl,EDU_STOP ,0 );/* force stop */ +edu_readl (ctrl,EDU_STOP ); + +return ret; +} +/** * Construct a FLASH_DMA descriptor as part of a linked list. You must know the * following ahead of time:
@@ -1850,9 +2064,11 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, try_dmaread:brcmnand_clear_ecc_addr (ctrl); -if (has_flash_dma (ctrl) && !oob &&flash_dma_buf_ok (buf)) { - err =brcmnand_dma_trans (host, addr, buf, trans *FC_BYTES , -CMD_PAGE_READ ); +if (ctrl->dma_trans && !oob &&flash_dma_buf_ok (buf)) { + err = ctrl->dma_trans (host, addr, buf, + trans *FC_BYTES , +CMD_PAGE_READ ); +if (err) {if (mtd_is_bitflip_or_eccerr (err)) err_addr = addr;
@@ -1988,10 +2204,12 @@ static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,for (i =0 ; i < ctrl->max_oob; i +=4 )oob_reg_write (ctrl, i,0xffffffff ); -if (has_flash_dma (ctrl) && !oob &&flash_dma_buf_ok (buf)) { -if (brcmnand_dma_trans (host, addr, (u32 *)buf, - mtd->writesize,CMD_PROGRAM_PAGE )) +if (use_dma (ctrl) && !oob &&flash_dma_buf_ok (buf)) { +if (ctrl->dma_trans (host, addr, (u32 *)buf, mtd->writesize, +CMD_PROGRAM_PAGE )) + ret = -EIO ; + goto out; }
@@ -2494,6 +2712,8 @@ static int brcmnand_suspend(struct device *dev)if (has_flash_dma (ctrl)) ctrl->flash_dma_mode =flash_dma_readl (ctrl,FLASH_DMA_MODE ); +else if (has_edu (ctrl)) + ctrl->edu_config =edu_readl (ctrl,EDU_CONFIG );return 0 ; }
@@ -2508,6 +2728,14 @@ static int brcmnand_resume(struct device *dev)flash_dma_writel (ctrl,FLASH_DMA_ERROR_STATUS ,0 ); } +if (has_edu (ctrl)) + ctrl->edu_config =edu_readl (ctrl,EDU_CONFIG ); +else { +edu_writel (ctrl,EDU_CONFIG , ctrl->edu_config); +edu_readl (ctrl,EDU_CONFIG ); +brcmnand_edu_init (ctrl); + } +brcmnand_write_reg (ctrl,BRCMNAND_CS_SELECT , ctrl->nand_cs_nand_select);brcmnand_write_reg (ctrl,BRCMNAND_CS_XOR , ctrl->nand_cs_nand_xor); brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD,
@@ -2553,6 +2781,49 @@ MODULE_DEVICE_TABLE(of, brcmnand_of_match);/*********************************************************************** * Platform driver setup (per controller) ***********************************************************************/ +static int brcmnand_edu_setup (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct brcmnand_controller *ctrl =dev_get_drvdata (&pdev->dev); +struct resource *res; +int ret; + + res =platform_get_resource_byname (pdev,IORESOURCE_MEM ,"flash-edu" ); +if (res) { + ctrl->edu_base =devm_ioremap_resource (dev, res); +if (IS_ERR (ctrl->edu_base)) +return PTR_ERR (ctrl->edu_base); + + ctrl->edu_offsets = edu_regs; + +edu_writel (ctrl,EDU_CONFIG ,EDU_CONFIG_MODE_NAND | +EDU_CONFIG_SWAP_CFG ); +edu_readl (ctrl,EDU_CONFIG ); + +/* initialize edu */ +brcmnand_edu_init (ctrl); + + ctrl->edu_irq =platform_get_irq_optional (pdev,1 ); +if (ctrl->edu_irq <0 ) { +dev_warn (dev, +"FLASH EDU enabled, using ctlrdy irq\n" ); + }else { + ret =devm_request_irq (dev, ctrl->edu_irq, + brcmnand_edu_irq,0 , +"brcmnand-edu" , ctrl); +if (ret <0 ) { +dev_err (ctrl->dev,"can't allocate IRQ %d: error %d\n" , + ctrl->edu_irq, ret); +return ret; + } + +dev_info (dev,"FLASH EDU enabled using irq %u\n" , + ctrl->edu_irq); + } + } + +return 0 ; +}int brcmnand_probe (struct platform_device *pdev,struct brcmnand_soc *soc) {
@@ -2578,6 +2849,7 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)init_completion (&ctrl->done);init_completion (&ctrl->dma_done); +init_completion (&ctrl->edu_done);nand_controller_init (&ctrl->controller); ctrl->controller.ops = &brcmnand_controller_ops;INIT_LIST_HEAD (&ctrl->host_list);
@@ -2665,6 +2937,15 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc) }dev_info (dev,"enabling FLASH_DMA\n" ); +/* set flash dma transfer function to call */ + ctrl->dma_trans = brcmnand_dma_trans; + }else { + ret =brcmnand_edu_setup (pdev); +if (ret <0 ) + goto err; + +/* set edu transfer function to call */ + ctrl->dma_trans = brcmnand_edu_trans; }/* Disable automatic device ID config, direct addressing */
Signature · 1 line
2.17.1
Quoted 5 lines · Kamal Dasu
Legacy mips soc platforms that have controller v5.0 and 6.0 use flash-edu block for dma transfers. This change adds support for nand dma transfers using the EDU block.
Signed-off-by Kamal Dasu <[email protected]>
Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next, thanks.
Miquel