summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2025-11-07 09:33:17 +0000
committerMark Brown <broonie@kernel.org>2025-11-07 09:33:17 +0000
commit4a58f60df578bb55a1d0adca0c356e03b2818de1 (patch)
tree05493f808339670fdfd2646bb360ca413dd70ec1
parent4e92abd0a11b91af3742197a9ca962c3c00d0948 (diff)
parentabc9a349b87ac0fd3ba8787ca00971b59c2e1257 (diff)
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
-rw-r--r--Documentation/devicetree/bindings/spi/fsl,spi-fsl-qspi.yaml21
-rw-r--r--drivers/spi/Kconfig3
-rw-r--r--drivers/spi/spi-fsl-qspi.c88
3 files changed, 83 insertions, 29 deletions
diff --git a/Documentation/devicetree/bindings/spi/fsl,spi-fsl-qspi.yaml b/Documentation/devicetree/bindings/spi/fsl,spi-fsl-qspi.yaml
index f2dd20370dbb..1d10cfbad86c 100644
--- a/Documentation/devicetree/bindings/spi/fsl,spi-fsl-qspi.yaml
+++ b/Documentation/devicetree/bindings/spi/fsl,spi-fsl-qspi.yaml
@@ -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:
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4d8f00c850c1..592d46c9998b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -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.
diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c
index c887abb028d7..a223b4bc6e63 100644
--- a/drivers/spi/spi-fsl-qspi.c
+++ b/drivers/spi/spi-fsl-qspi.c
@@ -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);
+}
+
+static bool needs_clk_disable(struct fsl_qspi *q)
{
- return q->devtype_data->quirks & QUADSPI_QUIRK_USE_TDH_SETTING;
+ 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);