diff options
Diffstat (limited to 'drivers/spi/spi-nxp-fspi.c')
| -rw-r--r-- | drivers/spi/spi-nxp-fspi.c | 115 | 
1 files changed, 104 insertions, 11 deletions
| diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index ab9035662717..6e6c2403944d 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -49,8 +49,11 @@  #include <linux/of_device.h>  #include <linux/platform_device.h>  #include <linux/pm_qos.h> +#include <linux/regmap.h>  #include <linux/sizes.h> +#include <linux/sys_soc.h> +#include <linux/mfd/syscon.h>  #include <linux/spi/spi.h>  #include <linux/spi/spi-mem.h> @@ -311,6 +314,11 @@  #define NXP_FSPI_MAX_CHIPSELECT		4  #define NXP_FSPI_MIN_IOMAP	SZ_4M +#define DCFG_RCWSR1		0x100 + +/* Access flash memory using IP bus only */ +#define FSPI_QUIRK_USE_IP_ONLY	BIT(0) +  struct nxp_fspi_devtype_data {  	unsigned int rxfifo;  	unsigned int txfifo; @@ -319,7 +327,7 @@ struct nxp_fspi_devtype_data {  	bool little_endian;  }; -static const struct nxp_fspi_devtype_data lx2160a_data = { +static struct nxp_fspi_devtype_data lx2160a_data = {  	.rxfifo = SZ_512,       /* (64  * 64 bits)  */  	.txfifo = SZ_1K,        /* (128 * 64 bits)  */  	.ahb_buf_size = SZ_2K,  /* (256 * 64 bits)  */ @@ -327,7 +335,7 @@ static const struct nxp_fspi_devtype_data lx2160a_data = {  	.little_endian = true,  /* little-endian    */  }; -static const struct nxp_fspi_devtype_data imx8mm_data = { +static struct nxp_fspi_devtype_data imx8mm_data = {  	.rxfifo = SZ_512,       /* (64  * 64 bits)  */  	.txfifo = SZ_1K,        /* (128 * 64 bits)  */  	.ahb_buf_size = SZ_2K,  /* (256 * 64 bits)  */ @@ -335,7 +343,7 @@ static const struct nxp_fspi_devtype_data imx8mm_data = {  	.little_endian = true,  /* little-endian    */  }; -static const struct nxp_fspi_devtype_data imx8qxp_data = { +static struct nxp_fspi_devtype_data imx8qxp_data = {  	.rxfifo = SZ_512,       /* (64  * 64 bits)  */  	.txfifo = SZ_1K,        /* (128 * 64 bits)  */  	.ahb_buf_size = SZ_2K,  /* (256 * 64 bits)  */ @@ -343,6 +351,14 @@ static const struct nxp_fspi_devtype_data imx8qxp_data = {  	.little_endian = true,  /* little-endian    */  }; +static struct nxp_fspi_devtype_data imx8dxl_data = { +	.rxfifo = SZ_512,       /* (64  * 64 bits)  */ +	.txfifo = SZ_1K,        /* (128 * 64 bits)  */ +	.ahb_buf_size = SZ_2K,  /* (256 * 64 bits)  */ +	.quirks = FSPI_QUIRK_USE_IP_ONLY, +	.little_endian = true,  /* little-endian    */ +}; +  struct nxp_fspi {  	void __iomem *iobase;  	void __iomem *ahb_addr; @@ -353,12 +369,17 @@ struct nxp_fspi {  	struct clk *clk, *clk_en;  	struct device *dev;  	struct completion c; -	const struct nxp_fspi_devtype_data *devtype_data; +	struct nxp_fspi_devtype_data *devtype_data;  	struct mutex lock;  	struct pm_qos_request pm_qos_req;  	int selected;  }; +static inline int needs_ip_only(struct nxp_fspi *f) +{ +	return f->devtype_data->quirks & FSPI_QUIRK_USE_IP_ONLY; +} +  /*   * R/W functions for big- or little-endian registers:   * The FSPI controller's endianness is independent of @@ -553,8 +574,8 @@ static void nxp_fspi_prepare_lut(struct nxp_fspi *f,  	for (i = 0; i < ARRAY_SIZE(lutval); i++)  		fspi_writel(f, lutval[i], base + FSPI_LUT_REG(i)); -	dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x]\n", -		op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3]); +	dev_dbg(f->dev, "CMD[%x] lutval[0:%x \t 1:%x \t 2:%x \t 3:%x], size: 0x%08x\n", +		op->cmd.opcode, lutval[0], lutval[1], lutval[2], lutval[3], op->data.nbytes);  	/* lock LUT */  	fspi_writel(f, FSPI_LUTKEY_VALUE, f->iobase + FSPI_LUTKEY); @@ -852,12 +873,14 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)  	nxp_fspi_prepare_lut(f, op);  	/* -	 * If we have large chunks of data, we read them through the AHB bus -	 * by accessing the mapped memory. In all other cases we use -	 * IP commands to access the flash. +	 * If we have large chunks of data, we read them through the AHB bus by +	 * accessing the mapped memory. In all other cases we use IP commands +	 * to access the flash. Read via AHB bus may be corrupted due to +	 * existence of an errata and therefore discard AHB read in such cases.  	 */  	if (op->data.nbytes > (f->devtype_data->rxfifo - 4) && -	    op->data.dir == SPI_MEM_DATA_IN) { +	    op->data.dir == SPI_MEM_DATA_IN && +	    !needs_ip_only(f)) {  		err = nxp_fspi_read_ahb(f, op);  	} else {  		if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) @@ -888,9 +911,68 @@ static int nxp_fspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)  			op->data.nbytes = ALIGN_DOWN(op->data.nbytes, 8);  	} +	/* Limit data bytes to RX FIFO in case of IP read only */ +	if (op->data.dir == SPI_MEM_DATA_IN && +	    needs_ip_only(f) && +	    op->data.nbytes > f->devtype_data->rxfifo) +		op->data.nbytes = f->devtype_data->rxfifo; +  	return 0;  } +static void erratum_err050568(struct nxp_fspi *f) +{ +	const struct soc_device_attribute ls1028a_soc_attr[] = { +		{ .family = "QorIQ LS1028A" }, +		{ /* sentinel */ } +	}; +	struct device_node *np; +	struct regmap *map; +	u32 val = 0, sysclk = 0; +	int ret; + +	/* Check for LS1028A family */ +	if (!soc_device_match(ls1028a_soc_attr)) { +		dev_dbg(f->dev, "Errata applicable only for LS1028A\n"); +		return; +	} + +	/* Compute system clock frequency multiplier ratio */ +	map = syscon_regmap_lookup_by_compatible("fsl,ls1028a-dcfg"); +	if (IS_ERR(map)) { +		dev_err(f->dev, "No syscon regmap\n"); +		goto err; +	} + +	ret = regmap_read(map, DCFG_RCWSR1, &val); +	if (ret < 0) +		goto err; + +	/* Strap bits 6:2 define SYS_PLL_RAT i.e frequency multiplier ratio */ +	val = (val >> 2) & 0x1F; +	WARN(val == 0, "Strapping is zero: Cannot determine ratio"); + +	/* Compute system clock frequency */ +	np = of_find_node_by_name(NULL, "clock-sysclk"); +	if (!np) +		goto err; + +	if (of_property_read_u32(np, "clock-frequency", &sysclk)) +		goto err; + +	sysclk = (sysclk * val) / 1000000; /* Convert sysclk to Mhz */ +	dev_dbg(f->dev, "val: 0x%08x, sysclk: %dMhz\n", val, sysclk); + +	/* Use IP bus only if PLL is 300MHz */ +	if (sysclk == 300) +		f->devtype_data->quirks |= FSPI_QUIRK_USE_IP_ONLY; + +	return; + +err: +	dev_err(f->dev, "Errata cannot be executed. Read via IP bus may not work\n"); +} +  static int nxp_fspi_default_setup(struct nxp_fspi *f)  {  	void __iomem *base = f->iobase; @@ -909,6 +991,15 @@ static int nxp_fspi_default_setup(struct nxp_fspi *f)  	if (ret)  		return ret; +	/* +	 * ERR050568: Flash access by FlexSPI AHB command may not work with +	 * platform frequency equal to 300 MHz on LS1028A. +	 * LS1028A reuses LX2160A compatible entry. Make errata applicable for +	 * Layerscape LS1028A platform. +	 */ +	if (of_device_is_compatible(f->dev->of_node, "nxp,lx2160a-fspi")) +		erratum_err050568(f); +  	/* Reset the module */  	/* w1c register, wait unit clear */  	ret = fspi_readl_poll_tout(f, f->iobase + FSPI_MCR0, @@ -1012,7 +1103,7 @@ static int nxp_fspi_probe(struct platform_device *pdev)  	f = spi_controller_get_devdata(ctlr);  	f->dev = dev; -	f->devtype_data = device_get_match_data(dev); +	f->devtype_data = (struct nxp_fspi_devtype_data *)device_get_match_data(dev);  	if (!f->devtype_data) {  		ret = -ENODEV;  		goto err_put_ctrl; @@ -1151,7 +1242,9 @@ static int nxp_fspi_resume(struct device *dev)  static const struct of_device_id nxp_fspi_dt_ids[] = {  	{ .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, },  	{ .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, }, +	{ .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, },  	{ .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, }, +	{ .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, },  	{ /* sentinel */ }  };  MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids); | 
