spi: enable the SpacemiT K1 SoC QSPI

Merge series from Alex Elder <elder@riscstar.com>:

This series adds support for the SpacemiT K1 SoC QSPI.  This IP is
generally compatible with the Freescale QSPI driver, requiring three
minor changes to enable it to be supported.  The changes are:
  - Adding support for optional resets
  - Having the clock *not* be disabled when changing its rate
  - Allowing the size of storage blocks written to flash chips
    to be set to something different from the AHB buffer size
This commit is contained in:
Mark Brown 2025-11-07 09:33:17 +00:00
commit 4a58f60df5
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
3 changed files with 83 additions and 29 deletions

View File

@ -9,9 +9,6 @@ title: Freescale Quad Serial Peripheral Interface (QuadSPI)
maintainers:
- Han Xu <han.xu@nxp.com>
allOf:
- $ref: spi-controller.yaml#
properties:
compatible:
oneOf:
@ -22,6 +19,7 @@ properties:
- fsl,imx6ul-qspi
- fsl,ls1021a-qspi
- fsl,ls2080a-qspi
- spacemit,k1-qspi
- items:
- enum:
- fsl,ls1043a-qspi
@ -54,6 +52,11 @@ properties:
- const: qspi_en
- const: qspi
resets:
items:
- description: SoC QSPI reset
- description: SoC QSPI bus reset
required:
- compatible
- reg
@ -62,6 +65,18 @@ required:
- clocks
- clock-names
allOf:
- $ref: spi-controller.yaml#
- if:
properties:
compatible:
not:
contains:
const: spacemit,k1-qspi
then:
properties:
resets: false
unevaluatedProperties: false
examples:

View File

@ -435,7 +435,8 @@ config SPI_FSL_LPSPI
config SPI_FSL_QUADSPI
tristate "Freescale QSPI controller"
depends on ARCH_MXC || SOC_LS1021A || ARCH_LAYERSCAPE || COMPILE_TEST
depends on ARCH_MXC || SOC_LS1021A || ARCH_LAYERSCAPE || \
ARCH_SPACEMIT || COMPILE_TEST
depends on HAS_IOMEM
help
This enables support for the Quad SPI controller in master mode.

View File

@ -36,6 +36,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
#include <linux/reset.h>
#include <linux/sizes.h>
#include <linux/spi/spi.h>
@ -196,11 +197,17 @@
*/
#define QUADSPI_QUIRK_USE_TDH_SETTING BIT(5)
/*
* Do not disable the "qspi" clock when changing its rate.
*/
#define QUADSPI_QUIRK_SKIP_CLK_DISABLE BIT(6)
struct fsl_qspi_devtype_data {
unsigned int rxfifo;
unsigned int txfifo;
int invalid_mstrid;
unsigned int ahb_buf_size;
unsigned int sfa_size;
unsigned int quirks;
bool little_endian;
};
@ -261,12 +268,23 @@ static const struct fsl_qspi_devtype_data ls2080a_data = {
.little_endian = true,
};
static const struct fsl_qspi_devtype_data spacemit_k1_data = {
.rxfifo = SZ_128,
.txfifo = SZ_256,
.ahb_buf_size = SZ_512,
.sfa_size = SZ_1K,
.invalid_mstrid = QUADSPI_BUFXCR_INVALID_MSTRID,
.quirks = QUADSPI_QUIRK_TKT253890 | QUADSPI_QUIRK_SKIP_CLK_DISABLE,
.little_endian = true,
};
struct fsl_qspi {
void __iomem *iobase;
void __iomem *ahb_addr;
const struct fsl_qspi_devtype_data *devtype_data;
struct mutex lock;
struct completion c;
struct reset_control *resets;
struct clk *clk, *clk_en;
struct pm_qos_request pm_qos_req;
struct device *dev;
@ -274,34 +292,39 @@ struct fsl_qspi {
u32 memmap_phy;
};
static inline int needs_swap_endian(struct fsl_qspi *q)
static bool needs_swap_endian(struct fsl_qspi *q)
{
return q->devtype_data->quirks & QUADSPI_QUIRK_SWAP_ENDIAN;
return !!(q->devtype_data->quirks & QUADSPI_QUIRK_SWAP_ENDIAN);
}
static inline int needs_4x_clock(struct fsl_qspi *q)
static bool needs_4x_clock(struct fsl_qspi *q)
{
return q->devtype_data->quirks & QUADSPI_QUIRK_4X_INT_CLK;
return !!(q->devtype_data->quirks & QUADSPI_QUIRK_4X_INT_CLK);
}
static inline int needs_fill_txfifo(struct fsl_qspi *q)
static bool needs_fill_txfifo(struct fsl_qspi *q)
{
return q->devtype_data->quirks & QUADSPI_QUIRK_TKT253890;
return !!(q->devtype_data->quirks & QUADSPI_QUIRK_TKT253890);
}
static inline int needs_wakeup_wait_mode(struct fsl_qspi *q)
static bool needs_wakeup_wait_mode(struct fsl_qspi *q)
{
return q->devtype_data->quirks & QUADSPI_QUIRK_TKT245618;
return !!(q->devtype_data->quirks & QUADSPI_QUIRK_TKT245618);
}
static inline int needs_amba_base_offset(struct fsl_qspi *q)
static bool needs_amba_base_offset(struct fsl_qspi *q)
{
return !(q->devtype_data->quirks & QUADSPI_QUIRK_BASE_INTERNAL);
}
static inline int needs_tdh_setting(struct fsl_qspi *q)
static bool needs_tdh_setting(struct fsl_qspi *q)
{
return q->devtype_data->quirks & QUADSPI_QUIRK_USE_TDH_SETTING;
return !!(q->devtype_data->quirks & QUADSPI_QUIRK_USE_TDH_SETTING);
}
static bool needs_clk_disable(struct fsl_qspi *q)
{
return !(q->devtype_data->quirks & QUADSPI_QUIRK_SKIP_CLK_DISABLE);
}
/*
@ -534,15 +557,18 @@ static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi,
if (needs_4x_clock(q))
rate *= 4;
fsl_qspi_clk_disable_unprep(q);
if (needs_clk_disable(q))
fsl_qspi_clk_disable_unprep(q);
ret = clk_set_rate(q->clk, rate);
if (ret)
return;
ret = fsl_qspi_clk_prep_enable(q);
if (ret)
return;
if (needs_clk_disable(q)) {
ret = fsl_qspi_clk_prep_enable(q);
if (ret)
return;
}
q->selected = spi_get_chipselect(spi, 0);
@ -722,6 +748,7 @@ static int fsl_qspi_default_setup(struct fsl_qspi *q)
{
void __iomem *base = q->iobase;
u32 reg, addr_offset = 0;
u32 sfa_size;
int ret;
/* disable and unprepare clock to avoid glitch pass to controller */
@ -780,17 +807,17 @@ static int fsl_qspi_default_setup(struct fsl_qspi *q)
* In HW there can be a maximum of four chips on two buses with
* two chip selects on each bus. We use four chip selects in SW
* to differentiate between the four chips.
* We use ahb_buf_size for each chip and set SFA1AD, SFA2AD, SFB1AD,
* SFB2AD accordingly.
*
* By default we write the AHB buffer size to each chip, but
* a different size can be specified with devtype_data->sfa_size.
* The SFA1AD, SFA2AD, SFB1AD, and SFB2AD registers define the
* top (end) of these four regions.
*/
qspi_writel(q, q->devtype_data->ahb_buf_size + addr_offset,
base + QUADSPI_SFA1AD);
qspi_writel(q, q->devtype_data->ahb_buf_size * 2 + addr_offset,
base + QUADSPI_SFA2AD);
qspi_writel(q, q->devtype_data->ahb_buf_size * 3 + addr_offset,
base + QUADSPI_SFB1AD);
qspi_writel(q, q->devtype_data->ahb_buf_size * 4 + addr_offset,
base + QUADSPI_SFB2AD);
sfa_size = q->devtype_data->sfa_size ? : q->devtype_data->ahb_buf_size;
qspi_writel(q, addr_offset + 1 * sfa_size, base + QUADSPI_SFA1AD);
qspi_writel(q, addr_offset + 2 * sfa_size, base + QUADSPI_SFA2AD);
qspi_writel(q, addr_offset + 3 * sfa_size, base + QUADSPI_SFB1AD);
qspi_writel(q, addr_offset + 4 * sfa_size, base + QUADSPI_SFB2AD);
q->selected = -1;
@ -857,6 +884,8 @@ static void fsl_qspi_cleanup(void *data)
{
struct fsl_qspi *q = data;
reset_control_assert(q->resets);
fsl_qspi_clk_disable_unprep(q);
mutex_destroy(&q->lock);
@ -902,6 +931,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (!q->ahb_addr)
return -ENOMEM;
q->resets = devm_reset_control_array_get_optional_exclusive(dev);
if (IS_ERR(q->resets))
return PTR_ERR(q->resets);
/* find the clocks */
q->clk_en = devm_clk_get(dev, "qspi_en");
if (IS_ERR(q->clk_en))
@ -923,6 +956,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
if (ret)
return ret;
ret = reset_control_deassert(q->resets);
if (ret)
return ret;
/* find the irq */
ret = platform_get_irq(pdev, 0);
if (ret < 0)
@ -976,6 +1013,7 @@ static const struct of_device_id fsl_qspi_dt_ids[] = {
{ .compatible = "fsl,imx6ul-qspi", .data = &imx6ul_data, },
{ .compatible = "fsl,ls1021a-qspi", .data = &ls1021a_data, },
{ .compatible = "fsl,ls2080a-qspi", .data = &ls2080a_data, },
{ .compatible = "spacemit,k1-qspi", .data = &spacemit_k1_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);