diff options
Diffstat (limited to 'drivers/spi/spi-fsl-qspi.c')
| -rw-r--r-- | drivers/spi/spi-fsl-qspi.c | 88 |
1 files changed, 63 insertions, 25 deletions
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); |
