|
@@ -26,6 +26,20 @@
|
|
|
#include <linux/mtd/mtd.h>
|
|
|
#include <linux/mtd/partitions.h>
|
|
|
#include <linux/mtd/spi-nor.h>
|
|
|
+#include <linux/mutex.h>
|
|
|
+#include <linux/pm_qos.h>
|
|
|
+
|
|
|
+/* Controller needs driver to swap endian */
|
|
|
+#define QUADSPI_QUIRK_SWAP_ENDIAN (1 << 0)
|
|
|
+/* Controller needs 4x internal clock */
|
|
|
+#define QUADSPI_QUIRK_4X_INT_CLK (1 << 1)
|
|
|
+/*
|
|
|
+ * TKT253890, Controller needs driver to fill txfifo till 16 byte to
|
|
|
+ * trigger data transfer even though extern data will not transferred.
|
|
|
+ */
|
|
|
+#define QUADSPI_QUIRK_TKT253890 (1 << 2)
|
|
|
+/* Controller cannot wake up from wait mode, TKT245618 */
|
|
|
+#define QUADSPI_QUIRK_TKT245618 (1 << 3)
|
|
|
|
|
|
/* The registers */
|
|
|
#define QUADSPI_MCR 0x00
|
|
@@ -191,9 +205,13 @@
|
|
|
#define SEQID_EN4B 10
|
|
|
#define SEQID_BRWR 11
|
|
|
|
|
|
+#define QUADSPI_MIN_IOMAP SZ_4M
|
|
|
+
|
|
|
enum fsl_qspi_devtype {
|
|
|
FSL_QUADSPI_VYBRID,
|
|
|
FSL_QUADSPI_IMX6SX,
|
|
|
+ FSL_QUADSPI_IMX7D,
|
|
|
+ FSL_QUADSPI_IMX6UL,
|
|
|
};
|
|
|
|
|
|
struct fsl_qspi_devtype_data {
|
|
@@ -201,20 +219,42 @@ struct fsl_qspi_devtype_data {
|
|
|
int rxfifo;
|
|
|
int txfifo;
|
|
|
int ahb_buf_size;
|
|
|
+ int driver_data;
|
|
|
};
|
|
|
|
|
|
static struct fsl_qspi_devtype_data vybrid_data = {
|
|
|
.devtype = FSL_QUADSPI_VYBRID,
|
|
|
.rxfifo = 128,
|
|
|
.txfifo = 64,
|
|
|
- .ahb_buf_size = 1024
|
|
|
+ .ahb_buf_size = 1024,
|
|
|
+ .driver_data = QUADSPI_QUIRK_SWAP_ENDIAN,
|
|
|
};
|
|
|
|
|
|
static struct fsl_qspi_devtype_data imx6sx_data = {
|
|
|
.devtype = FSL_QUADSPI_IMX6SX,
|
|
|
.rxfifo = 128,
|
|
|
.txfifo = 512,
|
|
|
- .ahb_buf_size = 1024
|
|
|
+ .ahb_buf_size = 1024,
|
|
|
+ .driver_data = QUADSPI_QUIRK_4X_INT_CLK
|
|
|
+ | QUADSPI_QUIRK_TKT245618,
|
|
|
+};
|
|
|
+
|
|
|
+static struct fsl_qspi_devtype_data imx7d_data = {
|
|
|
+ .devtype = FSL_QUADSPI_IMX7D,
|
|
|
+ .rxfifo = 512,
|
|
|
+ .txfifo = 512,
|
|
|
+ .ahb_buf_size = 1024,
|
|
|
+ .driver_data = QUADSPI_QUIRK_TKT253890
|
|
|
+ | QUADSPI_QUIRK_4X_INT_CLK,
|
|
|
+};
|
|
|
+
|
|
|
+static struct fsl_qspi_devtype_data imx6ul_data = {
|
|
|
+ .devtype = FSL_QUADSPI_IMX6UL,
|
|
|
+ .rxfifo = 128,
|
|
|
+ .txfifo = 512,
|
|
|
+ .ahb_buf_size = 1024,
|
|
|
+ .driver_data = QUADSPI_QUIRK_TKT253890
|
|
|
+ | QUADSPI_QUIRK_4X_INT_CLK,
|
|
|
};
|
|
|
|
|
|
#define FSL_QSPI_MAX_CHIP 4
|
|
@@ -222,8 +262,10 @@ struct fsl_qspi {
|
|
|
struct mtd_info mtd[FSL_QSPI_MAX_CHIP];
|
|
|
struct spi_nor nor[FSL_QSPI_MAX_CHIP];
|
|
|
void __iomem *iobase;
|
|
|
- void __iomem *ahb_base; /* Used when read from AHB bus */
|
|
|
+ void __iomem *ahb_addr;
|
|
|
u32 memmap_phy;
|
|
|
+ u32 memmap_offs;
|
|
|
+ u32 memmap_len;
|
|
|
struct clk *clk, *clk_en;
|
|
|
struct device *dev;
|
|
|
struct completion c;
|
|
@@ -233,16 +275,28 @@ struct fsl_qspi {
|
|
|
u32 clk_rate;
|
|
|
unsigned int chip_base_addr; /* We may support two chips. */
|
|
|
bool has_second_chip;
|
|
|
+ struct mutex lock;
|
|
|
+ struct pm_qos_request pm_qos_req;
|
|
|
};
|
|
|
|
|
|
-static inline int is_vybrid_qspi(struct fsl_qspi *q)
|
|
|
+static inline int needs_swap_endian(struct fsl_qspi *q)
|
|
|
+{
|
|
|
+ return q->devtype_data->driver_data & QUADSPI_QUIRK_SWAP_ENDIAN;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int needs_4x_clock(struct fsl_qspi *q)
|
|
|
+{
|
|
|
+ return q->devtype_data->driver_data & QUADSPI_QUIRK_4X_INT_CLK;
|
|
|
+}
|
|
|
+
|
|
|
+static inline int needs_fill_txfifo(struct fsl_qspi *q)
|
|
|
{
|
|
|
- return q->devtype_data->devtype == FSL_QUADSPI_VYBRID;
|
|
|
+ return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT253890;
|
|
|
}
|
|
|
|
|
|
-static inline int is_imx6sx_qspi(struct fsl_qspi *q)
|
|
|
+static inline int needs_wakeup_wait_mode(struct fsl_qspi *q)
|
|
|
{
|
|
|
- return q->devtype_data->devtype == FSL_QUADSPI_IMX6SX;
|
|
|
+ return q->devtype_data->driver_data & QUADSPI_QUIRK_TKT245618;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -251,7 +305,7 @@ static inline int is_imx6sx_qspi(struct fsl_qspi *q)
|
|
|
*/
|
|
|
static inline u32 fsl_qspi_endian_xchg(struct fsl_qspi *q, u32 a)
|
|
|
{
|
|
|
- return is_vybrid_qspi(q) ? __swab32(a) : a;
|
|
|
+ return needs_swap_endian(q) ? __swab32(a) : a;
|
|
|
}
|
|
|
|
|
|
static inline void fsl_qspi_unlock_lut(struct fsl_qspi *q)
|
|
@@ -343,14 +397,8 @@ static void fsl_qspi_init_lut(struct fsl_qspi *q)
|
|
|
/* Erase a sector */
|
|
|
lut_base = SEQID_SE * 4;
|
|
|
|
|
|
- if (q->nor_size <= SZ_16M) {
|
|
|
- cmd = SPINOR_OP_SE;
|
|
|
- addrlen = ADDR24BIT;
|
|
|
- } else {
|
|
|
- /* use the 4-byte address */
|
|
|
- cmd = SPINOR_OP_SE;
|
|
|
- addrlen = ADDR32BIT;
|
|
|
- }
|
|
|
+ cmd = q->nor[0].erase_opcode;
|
|
|
+ addrlen = q->nor_size <= SZ_16M ? ADDR24BIT : ADDR32BIT;
|
|
|
|
|
|
writel(LUT0(CMD, PAD1, cmd) | LUT1(ADDR, PAD1, addrlen),
|
|
|
base + QUADSPI_LUT(lut_base));
|
|
@@ -419,6 +467,8 @@ static int fsl_qspi_get_seqid(struct fsl_qspi *q, u8 cmd)
|
|
|
case SPINOR_OP_BRWR:
|
|
|
return SEQID_BRWR;
|
|
|
default:
|
|
|
+ if (cmd == q->nor[0].erase_opcode)
|
|
|
+ return SEQID_SE;
|
|
|
dev_err(q->dev, "Unsupported cmd 0x%.2x\n", cmd);
|
|
|
break;
|
|
|
}
|
|
@@ -537,7 +587,7 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
|
|
|
|
|
|
/* clear the TX FIFO. */
|
|
|
tmp = readl(q->iobase + QUADSPI_MCR);
|
|
|
- writel(tmp | QUADSPI_MCR_CLR_RXF_MASK, q->iobase + QUADSPI_MCR);
|
|
|
+ writel(tmp | QUADSPI_MCR_CLR_TXF_MASK, q->iobase + QUADSPI_MCR);
|
|
|
|
|
|
/* fill the TX data to the FIFO */
|
|
|
for (j = 0, i = ((count + 3) / 4); j < i; j++) {
|
|
@@ -546,6 +596,11 @@ static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
|
|
|
txbuf++;
|
|
|
}
|
|
|
|
|
|
+ /* fill the TXFIFO upto 16 bytes for i.MX7d */
|
|
|
+ if (needs_fill_txfifo(q))
|
|
|
+ for (; i < 4; i++)
|
|
|
+ writel(tmp, q->iobase + QUADSPI_TBDR);
|
|
|
+
|
|
|
/* Trigger it */
|
|
|
ret = fsl_qspi_runcmd(q, opcode, to, count);
|
|
|
|
|
@@ -606,6 +661,38 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
|
|
|
q->iobase + QUADSPI_BFGENCR);
|
|
|
}
|
|
|
|
|
|
+/* This function was used to prepare and enable QSPI clock */
|
|
|
+static int fsl_qspi_clk_prep_enable(struct fsl_qspi *q)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = clk_prepare_enable(q->clk_en);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ ret = clk_prepare_enable(q->clk);
|
|
|
+ if (ret) {
|
|
|
+ clk_disable_unprepare(q->clk_en);
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (needs_wakeup_wait_mode(q))
|
|
|
+ pm_qos_add_request(&q->pm_qos_req, PM_QOS_CPU_DMA_LATENCY, 0);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* This function was used to disable and unprepare QSPI clock */
|
|
|
+static void fsl_qspi_clk_disable_unprep(struct fsl_qspi *q)
|
|
|
+{
|
|
|
+ if (needs_wakeup_wait_mode(q))
|
|
|
+ pm_qos_remove_request(&q->pm_qos_req);
|
|
|
+
|
|
|
+ clk_disable_unprepare(q->clk);
|
|
|
+ clk_disable_unprepare(q->clk_en);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
/* We use this function to do some basic init for spi_nor_scan(). */
|
|
|
static int fsl_qspi_nor_setup(struct fsl_qspi *q)
|
|
|
{
|
|
@@ -613,11 +700,23 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
|
|
|
u32 reg;
|
|
|
int ret;
|
|
|
|
|
|
- /* the default frequency, we will change it in the future.*/
|
|
|
+ /* disable and unprepare clock to avoid glitch pass to controller */
|
|
|
+ fsl_qspi_clk_disable_unprep(q);
|
|
|
+
|
|
|
+ /* the default frequency, we will change it in the future. */
|
|
|
ret = clk_set_rate(q->clk, 66000000);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
+ ret = fsl_qspi_clk_prep_enable(q);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* Reset the module */
|
|
|
+ writel(QUADSPI_MCR_SWRSTSD_MASK | QUADSPI_MCR_SWRSTHD_MASK,
|
|
|
+ base + QUADSPI_MCR);
|
|
|
+ udelay(1);
|
|
|
+
|
|
|
/* Init the LUT table. */
|
|
|
fsl_qspi_init_lut(q);
|
|
|
|
|
@@ -635,6 +734,9 @@ static int fsl_qspi_nor_setup(struct fsl_qspi *q)
|
|
|
writel(QUADSPI_MCR_RESERVED_MASK | QUADSPI_MCR_END_CFG_MASK,
|
|
|
base + QUADSPI_MCR);
|
|
|
|
|
|
+ /* clear all interrupt status */
|
|
|
+ writel(0xffffffff, q->iobase + QUADSPI_FR);
|
|
|
+
|
|
|
/* enable the interrupt */
|
|
|
writel(QUADSPI_RSER_TFIE, q->iobase + QUADSPI_RSER);
|
|
|
|
|
@@ -646,13 +748,20 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
|
|
|
unsigned long rate = q->clk_rate;
|
|
|
int ret;
|
|
|
|
|
|
- if (is_imx6sx_qspi(q))
|
|
|
+ if (needs_4x_clock(q))
|
|
|
rate *= 4;
|
|
|
|
|
|
+ /* disable and unprepare clock to avoid glitch pass to controller */
|
|
|
+ fsl_qspi_clk_disable_unprep(q);
|
|
|
+
|
|
|
ret = clk_set_rate(q->clk, rate);
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
+ ret = fsl_qspi_clk_prep_enable(q);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
/* Init the LUT table again. */
|
|
|
fsl_qspi_init_lut(q);
|
|
|
|
|
@@ -665,6 +774,8 @@ static int fsl_qspi_nor_setup_last(struct fsl_qspi *q)
|
|
|
static const struct of_device_id fsl_qspi_dt_ids[] = {
|
|
|
{ .compatible = "fsl,vf610-qspi", .data = (void *)&vybrid_data, },
|
|
|
{ .compatible = "fsl,imx6sx-qspi", .data = (void *)&imx6sx_data, },
|
|
|
+ { .compatible = "fsl,imx7d-qspi", .data = (void *)&imx7d_data, },
|
|
|
+ { .compatible = "fsl,imx6ul-qspi", .data = (void *)&imx6ul_data, },
|
|
|
{ /* sentinel */ }
|
|
|
};
|
|
|
MODULE_DEVICE_TABLE(of, fsl_qspi_dt_ids);
|
|
@@ -730,11 +841,42 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from,
|
|
|
struct fsl_qspi *q = nor->priv;
|
|
|
u8 cmd = nor->read_opcode;
|
|
|
|
|
|
- dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n",
|
|
|
- cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len);
|
|
|
+ /* if necessary,ioremap buffer before AHB read, */
|
|
|
+ if (!q->ahb_addr) {
|
|
|
+ q->memmap_offs = q->chip_base_addr + from;
|
|
|
+ q->memmap_len = len > QUADSPI_MIN_IOMAP ? len : QUADSPI_MIN_IOMAP;
|
|
|
+
|
|
|
+ q->ahb_addr = ioremap_nocache(
|
|
|
+ q->memmap_phy + q->memmap_offs,
|
|
|
+ q->memmap_len);
|
|
|
+ if (!q->ahb_addr) {
|
|
|
+ dev_err(q->dev, "ioremap failed\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ /* ioremap if the data requested is out of range */
|
|
|
+ } else if (q->chip_base_addr + from < q->memmap_offs
|
|
|
+ || q->chip_base_addr + from + len >
|
|
|
+ q->memmap_offs + q->memmap_len) {
|
|
|
+ iounmap(q->ahb_addr);
|
|
|
+
|
|
|
+ q->memmap_offs = q->chip_base_addr + from;
|
|
|
+ q->memmap_len = len > QUADSPI_MIN_IOMAP ? len : QUADSPI_MIN_IOMAP;
|
|
|
+ q->ahb_addr = ioremap_nocache(
|
|
|
+ q->memmap_phy + q->memmap_offs,
|
|
|
+ q->memmap_len);
|
|
|
+ if (!q->ahb_addr) {
|
|
|
+ dev_err(q->dev, "ioremap failed\n");
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ dev_dbg(q->dev, "cmd [%x],read from 0x%p, len:%d\n",
|
|
|
+ cmd, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
|
|
|
+ len);
|
|
|
|
|
|
/* Read out the data directly from the AHB buffer.*/
|
|
|
- memcpy(buf, q->ahb_base + q->chip_base_addr + from, len);
|
|
|
+ memcpy(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
|
|
|
+ len);
|
|
|
|
|
|
*retlen += len;
|
|
|
return 0;
|
|
@@ -761,26 +903,26 @@ static int fsl_qspi_prep(struct spi_nor *nor, enum spi_nor_ops ops)
|
|
|
struct fsl_qspi *q = nor->priv;
|
|
|
int ret;
|
|
|
|
|
|
- ret = clk_enable(q->clk_en);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ mutex_lock(&q->lock);
|
|
|
|
|
|
- ret = clk_enable(q->clk);
|
|
|
- if (ret) {
|
|
|
- clk_disable(q->clk_en);
|
|
|
- return ret;
|
|
|
- }
|
|
|
+ ret = fsl_qspi_clk_prep_enable(q);
|
|
|
+ if (ret)
|
|
|
+ goto err_mutex;
|
|
|
|
|
|
fsl_qspi_set_base_addr(q, nor);
|
|
|
return 0;
|
|
|
+
|
|
|
+err_mutex:
|
|
|
+ mutex_unlock(&q->lock);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
|
|
|
{
|
|
|
struct fsl_qspi *q = nor->priv;
|
|
|
|
|
|
- clk_disable(q->clk);
|
|
|
- clk_disable(q->clk_en);
|
|
|
+ fsl_qspi_clk_disable_unprep(q);
|
|
|
+ mutex_unlock(&q->lock);
|
|
|
}
|
|
|
|
|
|
static int fsl_qspi_probe(struct platform_device *pdev)
|
|
@@ -804,6 +946,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
|
|
if (!q->nor_num || q->nor_num > FSL_QSPI_MAX_CHIP)
|
|
|
return -ENODEV;
|
|
|
|
|
|
+ q->dev = dev;
|
|
|
+ q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
|
|
|
+ platform_set_drvdata(pdev, q);
|
|
|
+
|
|
|
/* find the resources */
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI");
|
|
|
q->iobase = devm_ioremap_resource(dev, res);
|
|
@@ -812,9 +958,11 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
|
|
|
|
|
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
|
|
|
"QuadSPI-memory");
|
|
|
- q->ahb_base = devm_ioremap_resource(dev, res);
|
|
|
- if (IS_ERR(q->ahb_base))
|
|
|
- return PTR_ERR(q->ahb_base);
|
|
|
+ if (!devm_request_mem_region(dev, res->start, resource_size(res),
|
|
|
+ res->name)) {
|
|
|
+ dev_err(dev, "can't request region for resource %pR\n", res);
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
|
|
|
q->memmap_phy = res->start;
|
|
|
|
|
@@ -827,15 +975,9 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
|
|
if (IS_ERR(q->clk))
|
|
|
return PTR_ERR(q->clk);
|
|
|
|
|
|
- ret = clk_prepare_enable(q->clk_en);
|
|
|
- if (ret) {
|
|
|
- dev_err(dev, "cannot enable the qspi_en clock: %d\n", ret);
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- ret = clk_prepare_enable(q->clk);
|
|
|
+ ret = fsl_qspi_clk_prep_enable(q);
|
|
|
if (ret) {
|
|
|
- dev_err(dev, "cannot enable the qspi clock: %d\n", ret);
|
|
|
+ dev_err(dev, "can not enable the clock\n");
|
|
|
goto clk_failed;
|
|
|
}
|
|
|
|
|
@@ -853,10 +995,6 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
|
|
goto irq_failed;
|
|
|
}
|
|
|
|
|
|
- q->dev = dev;
|
|
|
- q->devtype_data = (struct fsl_qspi_devtype_data *)of_id->data;
|
|
|
- platform_set_drvdata(pdev, q);
|
|
|
-
|
|
|
ret = fsl_qspi_nor_setup(q);
|
|
|
if (ret)
|
|
|
goto irq_failed;
|
|
@@ -864,6 +1002,8 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
|
|
if (of_get_property(np, "fsl,qspi-has-second-chip", NULL))
|
|
|
q->has_second_chip = true;
|
|
|
|
|
|
+ mutex_init(&q->lock);
|
|
|
+
|
|
|
/* iterate the subnodes. */
|
|
|
for_each_available_child_of_node(dev->of_node, np) {
|
|
|
char modalias[40];
|
|
@@ -892,24 +1032,24 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
|
|
|
|
|
ret = of_modalias_node(np, modalias, sizeof(modalias));
|
|
|
if (ret < 0)
|
|
|
- goto irq_failed;
|
|
|
+ goto mutex_failed;
|
|
|
|
|
|
ret = of_property_read_u32(np, "spi-max-frequency",
|
|
|
&q->clk_rate);
|
|
|
if (ret < 0)
|
|
|
- goto irq_failed;
|
|
|
+ goto mutex_failed;
|
|
|
|
|
|
/* set the chip address for READID */
|
|
|
fsl_qspi_set_base_addr(q, nor);
|
|
|
|
|
|
ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD);
|
|
|
if (ret)
|
|
|
- goto irq_failed;
|
|
|
+ goto mutex_failed;
|
|
|
|
|
|
ppdata.of_node = np;
|
|
|
ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
|
|
|
if (ret)
|
|
|
- goto irq_failed;
|
|
|
+ goto mutex_failed;
|
|
|
|
|
|
/* Set the correct NOR size now. */
|
|
|
if (q->nor_size == 0) {
|
|
@@ -939,8 +1079,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
|
|
if (ret)
|
|
|
goto last_init_failed;
|
|
|
|
|
|
- clk_disable(q->clk);
|
|
|
- clk_disable(q->clk_en);
|
|
|
+ fsl_qspi_clk_disable_unprep(q);
|
|
|
return 0;
|
|
|
|
|
|
last_init_failed:
|
|
@@ -950,10 +1089,12 @@ last_init_failed:
|
|
|
i *= 2;
|
|
|
mtd_device_unregister(&q->mtd[i]);
|
|
|
}
|
|
|
+mutex_failed:
|
|
|
+ mutex_destroy(&q->lock);
|
|
|
irq_failed:
|
|
|
- clk_disable_unprepare(q->clk);
|
|
|
+ fsl_qspi_clk_disable_unprep(q);
|
|
|
clk_failed:
|
|
|
- clk_disable_unprepare(q->clk_en);
|
|
|
+ dev_err(dev, "Freescale QuadSPI probe failed\n");
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -973,8 +1114,11 @@ static int fsl_qspi_remove(struct platform_device *pdev)
|
|
|
writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
|
|
|
writel(0x0, q->iobase + QUADSPI_RSER);
|
|
|
|
|
|
- clk_unprepare(q->clk);
|
|
|
- clk_unprepare(q->clk_en);
|
|
|
+ mutex_destroy(&q->lock);
|
|
|
+
|
|
|
+ if (q->ahb_addr)
|
|
|
+ iounmap(q->ahb_addr);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -985,12 +1129,19 @@ static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state)
|
|
|
|
|
|
static int fsl_qspi_resume(struct platform_device *pdev)
|
|
|
{
|
|
|
+ int ret;
|
|
|
struct fsl_qspi *q = platform_get_drvdata(pdev);
|
|
|
|
|
|
+ ret = fsl_qspi_clk_prep_enable(q);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
fsl_qspi_nor_setup(q);
|
|
|
fsl_qspi_set_map_addr(q);
|
|
|
fsl_qspi_nor_setup_last(q);
|
|
|
|
|
|
+ fsl_qspi_clk_disable_unprep(q);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|