summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-fsl-qspi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-fsl-qspi.c')
-rw-r--r--drivers/spi/spi-fsl-qspi.c227
1 files changed, 128 insertions, 99 deletions
diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c
index 85cc71ba624a..a223b4bc6e63 100644
--- a/drivers/spi/spi-fsl-qspi.c
+++ b/drivers/spi/spi-fsl-qspi.c
@@ -34,9 +34,9 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_qos.h>
+#include <linux/reset.h>
#include <linux/sizes.h>
#include <linux/spi/spi.h>
@@ -197,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;
};
@@ -262,47 +268,63 @@ 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;
- u32 memmap_phy;
- struct clk *clk, *clk_en;
- struct device *dev;
- struct completion c;
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;
int selected;
+ 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);
}
/*
@@ -368,7 +390,7 @@ static int fsl_qspi_check_buswidth(struct fsl_qspi *q, u8 width)
static bool fsl_qspi_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
- struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->master);
+ struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
int ret;
ret = fsl_qspi_check_buswidth(q, op->cmd.buswidth);
@@ -523,28 +545,32 @@ static void fsl_qspi_invalidate(struct fsl_qspi *q)
qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
}
-static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi)
+static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi,
+ const struct spi_mem_op *op)
{
- unsigned long rate = spi->max_speed_hz;
+ unsigned long rate = op->max_freq;
int ret;
- if (q->selected == spi->chip_select)
+ if (q->selected == spi_get_chipselect(spi, 0))
return;
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->chip_select;
+ q->selected = spi_get_chipselect(spi, 0);
fsl_qspi_invalidate(q);
}
@@ -641,7 +667,7 @@ static int fsl_qspi_readl_poll_tout(struct fsl_qspi *q, void __iomem *base,
static int fsl_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
- struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->master);
+ struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
void __iomem *base = q->iobase;
u32 addr_offset = 0;
int err = 0;
@@ -653,7 +679,7 @@ static int fsl_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
fsl_qspi_readl_poll_tout(q, base + QUADSPI_SR, (QUADSPI_SR_IP_ACC_MASK |
QUADSPI_SR_AHB_ACC_MASK), 10, 1000);
- fsl_qspi_select_mem(q, mem->spi);
+ fsl_qspi_select_mem(q, mem->spi, op);
if (needs_amba_base_offset(q))
addr_offset = q->memmap_phy;
@@ -703,7 +729,7 @@ static int fsl_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
static int fsl_qspi_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
- struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->master);
+ struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
if (op->data.dir == SPI_MEM_DATA_OUT) {
if (op->data.nbytes > q->devtype_data->txfifo)
@@ -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;
@@ -809,7 +836,7 @@ static int fsl_qspi_default_setup(struct fsl_qspi *q)
static const char *fsl_qspi_get_name(struct spi_mem *mem)
{
- struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->master);
+ struct fsl_qspi *q = spi_controller_get_devdata(mem->spi->controller);
struct device *dev = &mem->spi->dev;
const char *name;
@@ -823,7 +850,7 @@ static const char *fsl_qspi_get_name(struct spi_mem *mem)
name = devm_kasprintf(dev, GFP_KERNEL,
"%s-%d", dev_name(q->dev),
- mem->spi->chip_select);
+ spi_get_chipselect(mem->spi, 0));
if (!name) {
dev_err(dev, "failed to get memory for custom flash name\n");
@@ -840,6 +867,30 @@ static const struct spi_controller_mem_ops fsl_qspi_mem_ops = {
.get_name = fsl_qspi_get_name,
};
+static const struct spi_controller_mem_caps fsl_qspi_mem_caps = {
+ .per_op_freq = true,
+};
+
+static void fsl_qspi_disable(void *data)
+{
+ struct fsl_qspi *q = data;
+
+ /* disable the hardware */
+ qspi_writel(q, QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
+ qspi_writel(q, 0x0, q->iobase + QUADSPI_RSER);
+}
+
+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);
+}
+
static int fsl_qspi_probe(struct platform_device *pdev)
{
struct spi_controller *ctlr;
@@ -849,7 +900,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
struct fsl_qspi *q;
int ret;
- ctlr = spi_alloc_master(&pdev->dev, sizeof(*q));
+ ctlr = devm_spi_alloc_host(&pdev->dev, sizeof(*q));
if (!ctlr)
return -ENOMEM;
@@ -859,106 +910,84 @@ static int fsl_qspi_probe(struct platform_device *pdev)
q = spi_controller_get_devdata(ctlr);
q->dev = dev;
q->devtype_data = of_device_get_match_data(dev);
- if (!q->devtype_data) {
- ret = -ENODEV;
- goto err_put_ctrl;
- }
+ if (!q->devtype_data)
+ return -ENODEV;
platform_set_drvdata(pdev, q);
/* find the resources */
q->iobase = devm_platform_ioremap_resource_byname(pdev, "QuadSPI");
- if (IS_ERR(q->iobase)) {
- ret = PTR_ERR(q->iobase);
- goto err_put_ctrl;
- }
+ if (IS_ERR(q->iobase))
+ return PTR_ERR(q->iobase);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"QuadSPI-memory");
- if (!res) {
- ret = -EINVAL;
- goto err_put_ctrl;
- }
+ if (!res)
+ return -EINVAL;
q->memmap_phy = res->start;
/* Since there are 4 cs, map size required is 4 times ahb_buf_size */
q->ahb_addr = devm_ioremap(dev, q->memmap_phy,
(q->devtype_data->ahb_buf_size * 4));
- if (!q->ahb_addr) {
- ret = -ENOMEM;
- goto err_put_ctrl;
- }
+ 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)) {
- ret = PTR_ERR(q->clk_en);
- goto err_put_ctrl;
- }
+ if (IS_ERR(q->clk_en))
+ return PTR_ERR(q->clk_en);
q->clk = devm_clk_get(dev, "qspi");
- if (IS_ERR(q->clk)) {
- ret = PTR_ERR(q->clk);
- goto err_put_ctrl;
- }
+ if (IS_ERR(q->clk))
+ return PTR_ERR(q->clk);
+
+ mutex_init(&q->lock);
ret = fsl_qspi_clk_prep_enable(q);
if (ret) {
dev_err(dev, "can not enable the clock\n");
- goto err_put_ctrl;
+ return ret;
}
+ ret = devm_add_action_or_reset(dev, fsl_qspi_cleanup, q);
+ 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)
- goto err_disable_clk;
+ return ret;
ret = devm_request_irq(dev, ret,
fsl_qspi_irq_handler, 0, pdev->name, q);
if (ret) {
dev_err(dev, "failed to request irq: %d\n", ret);
- goto err_disable_clk;
+ return ret;
}
- mutex_init(&q->lock);
-
ctlr->bus_num = -1;
ctlr->num_chipselect = 4;
ctlr->mem_ops = &fsl_qspi_mem_ops;
+ ctlr->mem_caps = &fsl_qspi_mem_caps;
fsl_qspi_default_setup(q);
ctlr->dev.of_node = np;
- ret = devm_spi_register_controller(dev, ctlr);
+ ret = devm_add_action_or_reset(dev, fsl_qspi_disable, q);
if (ret)
- goto err_destroy_mutex;
-
- return 0;
-
-err_destroy_mutex:
- mutex_destroy(&q->lock);
-
-err_disable_clk:
- fsl_qspi_clk_disable_unprep(q);
-
-err_put_ctrl:
- spi_controller_put(ctlr);
-
- dev_err(dev, "Freescale QuadSPI probe failed\n");
- return ret;
-}
-
-static int fsl_qspi_remove(struct platform_device *pdev)
-{
- struct fsl_qspi *q = platform_get_drvdata(pdev);
-
- /* disable the hardware */
- qspi_writel(q, QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
- qspi_writel(q, 0x0, q->iobase + QUADSPI_RSER);
-
- fsl_qspi_clk_disable_unprep(q);
+ return ret;
- mutex_destroy(&q->lock);
+ ret = devm_spi_register_controller(dev, ctlr);
+ if (ret)
+ return ret;
return 0;
}
@@ -984,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);
@@ -1000,7 +1030,6 @@ static struct platform_driver fsl_qspi_driver = {
.pm = &fsl_qspi_pm_ops,
},
.probe = fsl_qspi_probe,
- .remove = fsl_qspi_remove,
};
module_platform_driver(fsl_qspi_driver);