Commit 25f97138 authored by Florian Fainelli's avatar Florian Fainelli Committed by Miquel Raynal
Browse files

mtd: rawnand: brcmnand: Allow SoC to provide I/O operations



Allow a brcmnand_soc instance to provide a custom set of I/O operations
which we will require when using this driver on a BCMA bus which is not
directly memory mapped I/O. Update the nand_{read,write}_reg accordingly
to use the SoC operations if provided.

To minimize the penalty on other SoCs which do support standard MMIO
accesses, we use a static key which is disabled by default and gets
enabled if a soc implementation does provide I/O operations.

Signed-off-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarMiquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20220107184614.2670254-3-f.fainelli@gmail.com
parent 9e37532b
Loading
Loading
Loading
Loading
+26 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/static_key.h>
#include <linux/list.h>
#include <linux/log2.h>

@@ -207,6 +208,8 @@ enum {

struct brcmnand_host;

static DEFINE_STATIC_KEY_FALSE(brcmnand_soc_has_ops_key);

struct brcmnand_controller {
	struct device		*dev;
	struct nand_controller	controller;
@@ -592,14 +595,24 @@ enum {
	INTFC_CTLR_READY		= BIT(31),
};

static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl)
{
	return static_branch_unlikely(&brcmnand_soc_has_ops_key);
}

static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
{
	if (brcmnand_non_mmio_ops(ctrl))
		return brcmnand_soc_read(ctrl->soc, offs);
	return brcmnand_readl(ctrl->nand_base + offs);
}

static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
				 u32 val)
{
	if (brcmnand_non_mmio_ops(ctrl))
		brcmnand_soc_write(ctrl->soc, val, offs);
	else
		brcmnand_writel(val, ctrl->nand_base + offs);
}

@@ -766,12 +779,17 @@ static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,

static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
{
	if (brcmnand_non_mmio_ops(ctrl))
		return brcmnand_soc_read(ctrl->soc, BRCMNAND_NON_MMIO_FC_ADDR);
	return __raw_readl(ctrl->nand_fc + word * 4);
}

static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
				     int word, u32 val)
{
	if (brcmnand_non_mmio_ops(ctrl))
		brcmnand_soc_write(ctrl->soc, val, BRCMNAND_NON_MMIO_FC_ADDR);
	else
		__raw_writel(val, ctrl->nand_fc + word * 4);
}

@@ -3000,6 +3018,12 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
	ctrl->dev = dev;
	ctrl->soc = soc;

	/* Enable the static key if the soc provides I/O operations indicating
	 * that a non-memory mapped IO access path must be used
	 */
	if (brcmnand_soc_has_ops(ctrl->soc))
		static_branch_enable(&brcmnand_soc_has_ops_key);

	init_completion(&ctrl->done);
	init_completion(&ctrl->dma_done);
	init_completion(&ctrl->edu_done);
+29 −0
Original line number Diff line number Diff line
@@ -11,12 +11,25 @@

struct platform_device;
struct dev_pm_ops;
struct brcmnand_io_ops;

/* Special register offset constant to intercept a non-MMIO access
 * to the flash cache register space. This is intentionally large
 * not to overlap with an existing offset.
 */
#define BRCMNAND_NON_MMIO_FC_ADDR	0xffffffff

struct brcmnand_soc {
	bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
	void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
	void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare,
				 bool is_param);
	const struct brcmnand_io_ops *ops;
};

struct brcmnand_io_ops {
	u32 (*read_reg)(struct brcmnand_soc *soc, u32 offset);
	void (*write_reg)(struct brcmnand_soc *soc, u32 val, u32 offset);
};

static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc,
@@ -58,6 +71,22 @@ static inline void brcmnand_writel(u32 val, void __iomem *addr)
		writel_relaxed(val, addr);
}

static inline bool brcmnand_soc_has_ops(struct brcmnand_soc *soc)
{
	return soc && soc->ops && soc->ops->read_reg && soc->ops->write_reg;
}

static inline u32 brcmnand_soc_read(struct brcmnand_soc *soc, u32 offset)
{
	return soc->ops->read_reg(soc, offset);
}

static inline void brcmnand_soc_write(struct brcmnand_soc *soc, u32 val,
				      u32 offset)
{
	soc->ops->write_reg(soc, val, offset);
}

int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc);
int brcmnand_remove(struct platform_device *pdev);