* [PATCH v3 2/7] spi/mpc8xxx: refactor the common code for SPI/eSPI controller
From: Mingkai Hu @ 2010-09-30 8:00 UTC (permalink / raw)
To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-2-git-send-email-Mingkai.hu@freescale.com>
Refactor the common code in file spi_fsl_spi.c to spi_fsl_lib.c used
by SPI/eSPI controller driver as a library, and leave the QE/CPM SPI
controller code in the SPI controller driver spi_fsl_spi.c.
Because the register map of the SPI controller and eSPI controller
is so different, also leave the code operated the register to the
driver code, not the common code.
Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
- Update to the latest kernel base.
- Add a void *reg_base to compatible for SPI and eSPI register base.
drivers/spi/Kconfig | 5 +
drivers/spi/Makefile | 1 +
drivers/spi/spi_fsl_lib.c | 237 +++++++++++++++++++
drivers/spi/spi_fsl_lib.h | 119 ++++++++++
drivers/spi/spi_fsl_spi.c | 552 +++++++++++++--------------------------------
5 files changed, 522 insertions(+), 392 deletions(-)
create mode 100644 drivers/spi/spi_fsl_lib.c
create mode 100644 drivers/spi/spi_fsl_lib.h
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 6af34c6..79ad06f 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -182,9 +182,14 @@ config SPI_MPC512x_PSC
This enables using the Freescale MPC5121 Programmable Serial
Controller in SPI master mode.
+config SPI_FSL_LIB
+ tristate
+ depends on FSL_SOC
+
config SPI_FSL_SPI
tristate "Freescale SPI controller"
depends on FSL_SOC
+ select SPI_FSL_LIB
help
This enables using the Freescale SPI controllers in master mode.
MPC83xx platform uses the controller in cpu mode or CPM/QE mode.
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 770817c..7974c21 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_SPI_PL022) += amba-pl022.o
obj-$(CONFIG_SPI_MPC512x_PSC) += mpc512x_psc_spi.o
obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o
obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o
+obj-$(CONFIG_SPI_FSL_LIB) += spi_fsl_lib.o
obj-$(CONFIG_SPI_FSL_SPI) += spi_fsl_spi.o
obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi_fsl_lib.c
new file mode 100644
index 0000000..5cd741f
--- /dev/null
+++ b/drivers/spi/spi_fsl_lib.c
@@ -0,0 +1,237 @@
+/*
+ * Freescale SPI/eSPI controller driver library.
+ *
+ * Maintainer: Kumar Gala
+ *
+ * Copyright (C) 2006 Polycom, Inc.
+ *
+ * CPM SPI and QE buffer descriptors mode support:
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/fsl_devices.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/of_platform.h>
+#include <linux/of_spi.h>
+#include <sysdev/fsl_soc.h>
+
+#include "spi_fsl_lib.h"
+
+#define MPC8XXX_SPI_RX_BUF(type) \
+void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
+{ \
+ type *rx = mpc8xxx_spi->rx; \
+ *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \
+ mpc8xxx_spi->rx = rx; \
+}
+
+#define MPC8XXX_SPI_TX_BUF(type) \
+u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \
+{ \
+ u32 data; \
+ const type *tx = mpc8xxx_spi->tx; \
+ if (!tx) \
+ return 0; \
+ data = *tx++ << mpc8xxx_spi->tx_shift; \
+ mpc8xxx_spi->tx = tx; \
+ return data; \
+}
+
+MPC8XXX_SPI_RX_BUF(u8)
+MPC8XXX_SPI_RX_BUF(u16)
+MPC8XXX_SPI_RX_BUF(u32)
+MPC8XXX_SPI_TX_BUF(u8)
+MPC8XXX_SPI_TX_BUF(u16)
+MPC8XXX_SPI_TX_BUF(u32)
+
+struct mpc8xxx_spi_probe_info *to_of_pinfo(struct fsl_spi_platform_data *pdata)
+{
+ return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata);
+}
+
+void mpc8xxx_spi_work(struct work_struct *work)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi,
+ work);
+
+ spin_lock_irq(&mpc8xxx_spi->lock);
+ while (!list_empty(&mpc8xxx_spi->queue)) {
+ struct spi_message *m = container_of(mpc8xxx_spi->queue.next,
+ struct spi_message, queue);
+
+ list_del_init(&m->queue);
+ spin_unlock_irq(&mpc8xxx_spi->lock);
+
+ if (mpc8xxx_spi->spi_do_one_msg)
+ mpc8xxx_spi->spi_do_one_msg(m);
+
+ spin_lock_irq(&mpc8xxx_spi->lock);
+ }
+ spin_unlock_irq(&mpc8xxx_spi->lock);
+}
+
+int mpc8xxx_spi_transfer(struct spi_device *spi,
+ struct spi_message *m)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ unsigned long flags;
+
+ m->actual_length = 0;
+ m->status = -EINPROGRESS;
+
+ spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
+ list_add_tail(&m->queue, &mpc8xxx_spi->queue);
+ queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
+ spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags);
+
+ return 0;
+}
+
+void mpc8xxx_spi_cleanup(struct spi_device *spi)
+{
+ kfree(spi->controller_state);
+}
+
+const char *mpc8xxx_spi_strmode(unsigned int flags)
+{
+ if (flags & SPI_QE_CPU_MODE) {
+ return "QE CPU";
+ } else if (flags & SPI_CPM_MODE) {
+ if (flags & SPI_QE)
+ return "QE";
+ else if (flags & SPI_CPM2)
+ return "CPM2";
+ else
+ return "CPM1";
+ }
+ return "CPU";
+}
+
+int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
+ unsigned int irq)
+{
+ struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct spi_master *master;
+ struct mpc8xxx_spi *mpc8xxx_spi;
+ int ret = 0;
+
+ master = dev_get_drvdata(dev);
+
+ /* the spi->mode bits understood by this driver: */
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
+ | SPI_LSB_FIRST | SPI_LOOP;
+
+ master->transfer = mpc8xxx_spi_transfer;
+ master->cleanup = mpc8xxx_spi_cleanup;
+ master->dev.of_node = dev->of_node;
+
+ mpc8xxx_spi = spi_master_get_devdata(master);
+ mpc8xxx_spi->dev = dev;
+ mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
+ mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
+ mpc8xxx_spi->flags = pdata->flags;
+ mpc8xxx_spi->spibrg = pdata->sysclk;
+ mpc8xxx_spi->irq = irq;
+
+ mpc8xxx_spi->rx_shift = 0;
+ mpc8xxx_spi->tx_shift = 0;
+
+ init_completion(&mpc8xxx_spi->done);
+
+ master->bus_num = pdata->bus_num;
+ master->num_chipselect = pdata->max_chipselect;
+
+ spin_lock_init(&mpc8xxx_spi->lock);
+ init_completion(&mpc8xxx_spi->done);
+ INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work);
+ INIT_LIST_HEAD(&mpc8xxx_spi->queue);
+
+ mpc8xxx_spi->workqueue = create_singlethread_workqueue(
+ dev_name(master->dev.parent));
+ if (mpc8xxx_spi->workqueue == NULL) {
+ ret = -EBUSY;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ return ret;
+}
+
+int __devexit mpc8xxx_spi_remove(struct device *dev)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi;
+ struct spi_master *master;
+
+ master = dev_get_drvdata(dev);
+ mpc8xxx_spi = spi_master_get_devdata(master);
+
+ flush_workqueue(mpc8xxx_spi->workqueue);
+ destroy_workqueue(mpc8xxx_spi->workqueue);
+ spi_unregister_master(master);
+
+ free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
+
+ if (mpc8xxx_spi->spi_remove)
+ mpc8xxx_spi->spi_remove(mpc8xxx_spi);
+
+ return 0;
+}
+
+int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
+ const struct of_device_id *ofid)
+{
+ struct device *dev = &ofdev->dev;
+ struct device_node *np = ofdev->dev.of_node;
+ struct mpc8xxx_spi_probe_info *pinfo;
+ struct fsl_spi_platform_data *pdata;
+ const void *prop;
+ int ret = -ENOMEM;
+
+ pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
+ if (!pinfo)
+ return -ENOMEM;
+
+ pdata = &pinfo->pdata;
+ dev->platform_data = pdata;
+
+ /* Allocate bus num dynamically. */
+ pdata->bus_num = -1;
+
+ /* SPI controller is either clocked from QE or SoC clock. */
+ pdata->sysclk = get_brgfreq();
+ if (pdata->sysclk == -1) {
+ pdata->sysclk = fsl_get_sys_freq();
+ if (pdata->sysclk == -1) {
+ ret = -ENODEV;
+ goto err;
+ }
+ }
+
+ prop = of_get_property(np, "mode", NULL);
+ if (prop && !strcmp(prop, "cpu-qe"))
+ pdata->flags = SPI_QE_CPU_MODE;
+ else if (prop && !strcmp(prop, "qe"))
+ pdata->flags = SPI_CPM_MODE | SPI_QE;
+ else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
+ pdata->flags = SPI_CPM_MODE | SPI_CPM2;
+ else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
+ pdata->flags = SPI_CPM_MODE | SPI_CPM1;
+
+ return 0;
+
+err:
+ kfree(pinfo);
+ return ret;
+}
diff --git a/drivers/spi/spi_fsl_lib.h b/drivers/spi/spi_fsl_lib.h
new file mode 100644
index 0000000..6ae8949
--- /dev/null
+++ b/drivers/spi/spi_fsl_lib.h
@@ -0,0 +1,119 @@
+/*
+ * Freescale SPI/eSPI controller driver library.
+ *
+ * Maintainer: Kumar Gala
+ *
+ * Copyright 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2006 Polycom, Inc.
+ *
+ * CPM SPI and QE buffer descriptors mode support:
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#ifndef __SPI_FSL_LIB_H__
+#define __SPI_FSL_LIB_H__
+
+/* SPI/eSPI Controller driver's private data. */
+struct mpc8xxx_spi {
+ struct device *dev;
+ void *reg_base;
+
+ /* rx & tx bufs from the spi_transfer */
+ const void *tx;
+ void *rx;
+
+ int subblock;
+ struct spi_pram __iomem *pram;
+ struct cpm_buf_desc __iomem *tx_bd;
+ struct cpm_buf_desc __iomem *rx_bd;
+
+ struct spi_transfer *xfer_in_progress;
+
+ /* dma addresses for CPM transfers */
+ dma_addr_t tx_dma;
+ dma_addr_t rx_dma;
+ bool map_tx_dma;
+ bool map_rx_dma;
+
+ dma_addr_t dma_dummy_tx;
+ dma_addr_t dma_dummy_rx;
+
+ /* functions to deal with different sized buffers */
+ void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
+ u32(*get_tx) (struct mpc8xxx_spi *);
+
+ /* hooks for different controller driver */
+ void (*spi_do_one_msg) (struct spi_message *m);
+ void (*spi_remove) (struct mpc8xxx_spi *mspi);
+
+ unsigned int count;
+ unsigned int irq;
+
+ unsigned nsecs; /* (clock cycle time)/2 */
+
+ u32 spibrg; /* SPIBRG input clock */
+ u32 rx_shift; /* RX data reg shift when in qe mode */
+ u32 tx_shift; /* TX data reg shift when in qe mode */
+
+ unsigned int flags;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ struct list_head queue;
+ spinlock_t lock;
+
+ struct completion done;
+};
+
+struct spi_mpc8xxx_cs {
+ /* functions to deal with different sized buffers */
+ void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
+ u32 (*get_tx) (struct mpc8xxx_spi *);
+ u32 rx_shift; /* RX data reg shift when in qe mode */
+ u32 tx_shift; /* TX data reg shift when in qe mode */
+ u32 hw_mode; /* Holds HW mode register settings */
+};
+
+static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
+{
+ out_be32(reg, val);
+}
+
+static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
+{
+ return in_be32(reg);
+}
+
+struct mpc8xxx_spi_probe_info {
+ struct fsl_spi_platform_data pdata;
+ int *gpios;
+ bool *alow_flags;
+};
+
+extern u32 mpc8xxx_spi_tx_buf_u8(struct mpc8xxx_spi *mpc8xxx_spi);
+extern u32 mpc8xxx_spi_tx_buf_u16(struct mpc8xxx_spi *mpc8xxx_spi);
+extern u32 mpc8xxx_spi_tx_buf_u32(struct mpc8xxx_spi *mpc8xxx_spi);
+extern void mpc8xxx_spi_rx_buf_u8(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
+extern void mpc8xxx_spi_rx_buf_u16(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
+extern void mpc8xxx_spi_rx_buf_u32(u32 data, struct mpc8xxx_spi *mpc8xxx_spi);
+
+extern struct mpc8xxx_spi_probe_info *to_of_pinfo(
+ struct fsl_spi_platform_data *pdata);
+extern int mpc8xxx_spi_bufs(struct mpc8xxx_spi *mspi,
+ struct spi_transfer *t, unsigned int len);
+extern int mpc8xxx_spi_transfer(struct spi_device *spi, struct spi_message *m);
+extern void mpc8xxx_spi_cleanup(struct spi_device *spi);
+extern const char *mpc8xxx_spi_strmode(unsigned int flags);
+extern int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
+ unsigned int irq);
+extern int mpc8xxx_spi_remove(struct device *dev);
+extern int of_mpc8xxx_spi_probe(struct platform_device *ofdev,
+ const struct of_device_id *ofid);
+
+#endif /* __SPI_FSL_LIB_H__ */
diff --git a/drivers/spi/spi_fsl_spi.c b/drivers/spi/spi_fsl_spi.c
index d31b57f..c662e7e 100644
--- a/drivers/spi/spi_fsl_spi.c
+++ b/drivers/spi/spi_fsl_spi.c
@@ -1,9 +1,10 @@
/*
- * MPC8xxx SPI controller driver.
+ * Freescale SPI controller driver.
*
* Maintainer: Kumar Gala
*
* Copyright (C) 2006 Polycom, Inc.
+ * Copyright 2010 Freescale Semiconductor, Inc.
*
* CPM SPI and QE buffer descriptors mode support:
* Copyright (c) 2009 MontaVista Software, Inc.
@@ -15,18 +16,11 @@
* option) any later version.
*/
#include <linux/module.h>
-#include <linux/init.h>
#include <linux/types.h>
#include <linux/kernel.h>
-#include <linux/bug.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/irq.h>
-#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/platform_device.h>
@@ -38,12 +32,12 @@
#include <linux/of_platform.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
-#include <linux/slab.h>
#include <sysdev/fsl_soc.h>
#include <asm/cpm.h>
#include <asm/qe.h>
-#include <asm/irq.h>
+
+#include "spi_fsl_lib.h"
/* CPM1 and CPM2 are mutually exclusive. */
#ifdef CONFIG_CPM1
@@ -55,7 +49,7 @@
#endif
/* SPI Controller registers */
-struct mpc8xxx_spi_reg {
+struct fsl_spi_reg {
u8 res1[0x20];
__be32 mode;
__be32 event;
@@ -80,7 +74,7 @@ struct mpc8xxx_spi_reg {
/*
* Default for SPI Mode:
- * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
+ * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
*/
#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \
SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf))
@@ -102,112 +96,16 @@ struct mpc8xxx_spi_reg {
#define SPI_PRAM_SIZE 0x100
#define SPI_MRBLR ((unsigned int)PAGE_SIZE)
-/* SPI Controller driver's private data. */
-struct mpc8xxx_spi {
- struct device *dev;
- struct mpc8xxx_spi_reg __iomem *base;
-
- /* rx & tx bufs from the spi_transfer */
- const void *tx;
- void *rx;
-
- int subblock;
- struct spi_pram __iomem *pram;
- struct cpm_buf_desc __iomem *tx_bd;
- struct cpm_buf_desc __iomem *rx_bd;
-
- struct spi_transfer *xfer_in_progress;
-
- /* dma addresses for CPM transfers */
- dma_addr_t tx_dma;
- dma_addr_t rx_dma;
- bool map_tx_dma;
- bool map_rx_dma;
-
- dma_addr_t dma_dummy_tx;
- dma_addr_t dma_dummy_rx;
-
- /* functions to deal with different sized buffers */
- void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
- u32(*get_tx) (struct mpc8xxx_spi *);
-
- unsigned int count;
- unsigned int irq;
-
- unsigned nsecs; /* (clock cycle time)/2 */
-
- u32 spibrg; /* SPIBRG input clock */
- u32 rx_shift; /* RX data reg shift when in qe mode */
- u32 tx_shift; /* TX data reg shift when in qe mode */
-
- unsigned int flags;
-
- struct workqueue_struct *workqueue;
- struct work_struct work;
-
- struct list_head queue;
- spinlock_t lock;
-
- struct completion done;
-};
-
-static void *mpc8xxx_dummy_rx;
-static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock);
-static int mpc8xxx_dummy_rx_refcnt;
-
-struct spi_mpc8xxx_cs {
- /* functions to deal with different sized buffers */
- void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
- u32 (*get_tx) (struct mpc8xxx_spi *);
- u32 rx_shift; /* RX data reg shift when in qe mode */
- u32 tx_shift; /* TX data reg shift when in qe mode */
- u32 hw_mode; /* Holds HW mode register settings */
-};
-
-static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
-{
- out_be32(reg, val);
-}
-
-static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
-{
- return in_be32(reg);
-}
-
-#define MPC83XX_SPI_RX_BUF(type) \
-static \
-void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
-{ \
- type *rx = mpc8xxx_spi->rx; \
- *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \
- mpc8xxx_spi->rx = rx; \
-}
-
-#define MPC83XX_SPI_TX_BUF(type) \
-static \
-u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \
-{ \
- u32 data; \
- const type *tx = mpc8xxx_spi->tx; \
- if (!tx) \
- return 0; \
- data = *tx++ << mpc8xxx_spi->tx_shift; \
- mpc8xxx_spi->tx = tx; \
- return data; \
-}
+static void *fsl_dummy_rx;
+static DEFINE_MUTEX(fsl_dummy_rx_lock);
+static int fsl_dummy_rx_refcnt;
-MPC83XX_SPI_RX_BUF(u8)
-MPC83XX_SPI_RX_BUF(u16)
-MPC83XX_SPI_RX_BUF(u32)
-MPC83XX_SPI_TX_BUF(u8)
-MPC83XX_SPI_TX_BUF(u16)
-MPC83XX_SPI_TX_BUF(u32)
-
-static void mpc8xxx_spi_change_mode(struct spi_device *spi)
+static void fsl_spi_change_mode(struct spi_device *spi)
{
struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
struct spi_mpc8xxx_cs *cs = spi->controller_state;
- __be32 __iomem *mode = &mspi->base->mode;
+ struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
+ __be32 __iomem *mode = ®_base->mode;
unsigned long flags;
if (cs->hw_mode == mpc8xxx_spi_read_reg(mode))
@@ -238,7 +136,7 @@ static void mpc8xxx_spi_change_mode(struct spi_device *spi)
local_irq_restore(flags);
}
-static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
+static void fsl_spi_chipselect(struct spi_device *spi, int value)
{
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data;
@@ -256,18 +154,17 @@ static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
mpc8xxx_spi->get_rx = cs->get_rx;
mpc8xxx_spi->get_tx = cs->get_tx;
- mpc8xxx_spi_change_mode(spi);
+ fsl_spi_change_mode(spi);
if (pdata->cs_control)
pdata->cs_control(spi, pol);
}
}
-static int
-mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
- struct spi_device *spi,
- struct mpc8xxx_spi *mpc8xxx_spi,
- int bits_per_word)
+static int mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
+ struct spi_device *spi,
+ struct mpc8xxx_spi *mpc8xxx_spi,
+ int bits_per_word)
{
cs->rx_shift = 0;
cs->tx_shift = 0;
@@ -307,10 +204,9 @@ mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
return bits_per_word;
}
-static int
-mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
- struct spi_device *spi,
- int bits_per_word)
+static int mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
+ struct spi_device *spi,
+ int bits_per_word)
{
/* QE uses Little Endian for words > 8
* so transform all words > 8 into 8 bits
@@ -326,13 +222,13 @@ mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
return bits_per_word;
}
-static
-int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
+static int fsl_spi_setup_transfer(struct spi_device *spi,
+ struct spi_transfer *t)
{
struct mpc8xxx_spi *mpc8xxx_spi;
- int bits_per_word;
+ int bits_per_word = 0;
u8 pm;
- u32 hz;
+ u32 hz = 0;
struct spi_mpc8xxx_cs *cs = spi->controller_state;
mpc8xxx_spi = spi_master_get_devdata(spi->master);
@@ -340,9 +236,6 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
if (t) {
bits_per_word = t->bits_per_word;
hz = t->speed_hz;
- } else {
- bits_per_word = 0;
- hz = 0;
}
/* spi_transfer level calls that work per-word */
@@ -388,23 +281,25 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
hz, mpc8xxx_spi->spibrg / 1024);
if (pm > 16)
pm = 16;
- } else
+ } else {
pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
+ }
if (pm)
pm--;
cs->hw_mode |= SPMODE_PM(pm);
- mpc8xxx_spi_change_mode(spi);
+ fsl_spi_change_mode(spi);
return 0;
}
-static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
+static void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
{
struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
unsigned int xfer_ofs;
+ struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
@@ -418,13 +313,14 @@ static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
BD_SC_LAST);
/* start transfer */
- mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR);
+ mpc8xxx_spi_write_reg(®_base->command, SPCOM_STR);
}
-static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
+static int fsl_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
struct spi_transfer *t, bool is_dma_mapped)
{
struct device *dev = mspi->dev;
+ struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
if (is_dma_mapped) {
mspi->map_tx_dma = 0;
@@ -469,13 +365,13 @@ static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
}
/* enable rx ints */
- mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB);
+ mpc8xxx_spi_write_reg(®_base->mask, SPIE_RXB);
mspi->xfer_in_progress = t;
mspi->count = t->len;
/* start CPM transfers */
- mpc8xxx_spi_cpm_bufs_start(mspi);
+ fsl_spi_cpm_bufs_start(mspi);
return 0;
@@ -485,7 +381,7 @@ err_rx_dma:
return -ENOMEM;
}
-static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
+static void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
{
struct device *dev = mspi->dev;
struct spi_transfer *t = mspi->xfer_in_progress;
@@ -497,31 +393,34 @@ static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
mspi->xfer_in_progress = NULL;
}
-static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
+static int fsl_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
struct spi_transfer *t, unsigned int len)
{
u32 word;
+ struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
mspi->count = len;
/* enable rx ints */
- mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE);
+ mpc8xxx_spi_write_reg(®_base->mask, SPIM_NE);
/* transmit word */
word = mspi->get_tx(mspi);
- mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+ mpc8xxx_spi_write_reg(®_base->transmit, word);
return 0;
}
-static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
+static int fsl_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
bool is_dma_mapped)
{
struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ struct fsl_spi_reg *reg_base;
unsigned int len = t->len;
u8 bits_per_word;
int ret;
+ reg_base = (struct fsl_spi_reg *)&mpc8xxx_spi->reg_base;
bits_per_word = spi->bits_per_word;
if (t->bits_per_word)
bits_per_word = t->bits_per_word;
@@ -545,24 +444,24 @@ static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
INIT_COMPLETION(mpc8xxx_spi->done);
if (mpc8xxx_spi->flags & SPI_CPM_MODE)
- ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
+ ret = fsl_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
else
- ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len);
+ ret = fsl_spi_cpu_bufs(mpc8xxx_spi, t, len);
if (ret)
return ret;
wait_for_completion(&mpc8xxx_spi->done);
/* disable rx ints */
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
+ mpc8xxx_spi_write_reg(®_base->mask, 0);
if (mpc8xxx_spi->flags & SPI_CPM_MODE)
- mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi);
+ fsl_spi_cpm_bufs_complete(mpc8xxx_spi);
return mpc8xxx_spi->count;
}
-static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
+static void fsl_spi_do_one_msg(struct spi_message *m)
{
struct spi_device *spi = m->spi;
struct spi_transfer *t;
@@ -578,18 +477,18 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
status = -EINVAL;
if (cs_change)
- status = mpc8xxx_spi_setup_transfer(spi, t);
+ status = fsl_spi_setup_transfer(spi, t);
if (status < 0)
break;
}
if (cs_change) {
- mpc8xxx_spi_chipselect(spi, BITBANG_CS_ACTIVE);
+ fsl_spi_chipselect(spi, BITBANG_CS_ACTIVE);
ndelay(nsecs);
}
cs_change = t->cs_change;
if (t->len)
- status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped);
+ status = fsl_spi_bufs(spi, t, m->is_dma_mapped);
if (status) {
status = -EMSGSIZE;
break;
@@ -601,7 +500,7 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
if (cs_change) {
ndelay(nsecs);
- mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+ fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(nsecs);
}
}
@@ -611,35 +510,16 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
if (status || !cs_change) {
ndelay(nsecs);
- mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+ fsl_spi_chipselect(spi, BITBANG_CS_INACTIVE);
}
- mpc8xxx_spi_setup_transfer(spi, NULL);
-}
-
-static void mpc8xxx_spi_work(struct work_struct *work)
-{
- struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi,
- work);
-
- spin_lock_irq(&mpc8xxx_spi->lock);
- while (!list_empty(&mpc8xxx_spi->queue)) {
- struct spi_message *m = container_of(mpc8xxx_spi->queue.next,
- struct spi_message, queue);
-
- list_del_init(&m->queue);
- spin_unlock_irq(&mpc8xxx_spi->lock);
-
- mpc8xxx_spi_do_one_msg(m);
-
- spin_lock_irq(&mpc8xxx_spi->lock);
- }
- spin_unlock_irq(&mpc8xxx_spi->lock);
+ fsl_spi_setup_transfer(spi, NULL);
}
-static int mpc8xxx_spi_setup(struct spi_device *spi)
+static int fsl_spi_setup(struct spi_device *spi)
{
struct mpc8xxx_spi *mpc8xxx_spi;
+ struct fsl_spi_reg *reg_base;
int retval;
u32 hw_mode;
struct spi_mpc8xxx_cs *cs = spi->controller_state;
@@ -655,8 +535,10 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
}
mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ reg_base = (struct fsl_spi_reg *)&mpc8xxx_spi->reg_base;
+
hw_mode = cs->hw_mode; /* Save original settings */
- cs->hw_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
+ cs->hw_mode = mpc8xxx_spi_read_reg(®_base->mode);
/* mask out bits we are going to set */
cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH
| SPMODE_REV | SPMODE_LOOP);
@@ -670,7 +552,7 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
if (spi->mode & SPI_LOOP)
cs->hw_mode |= SPMODE_LOOP;
- retval = mpc8xxx_spi_setup_transfer(spi, NULL);
+ retval = fsl_spi_setup_transfer(spi, NULL);
if (retval < 0) {
cs->hw_mode = hw_mode; /* Restore settings */
return retval;
@@ -678,9 +560,10 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
return 0;
}
-static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
+static void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
{
u16 len;
+ struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
@@ -692,20 +575,22 @@ static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
}
/* Clear the events */
- mpc8xxx_spi_write_reg(&mspi->base->event, events);
+ mpc8xxx_spi_write_reg(®_base->event, events);
mspi->count -= len;
if (mspi->count)
- mpc8xxx_spi_cpm_bufs_start(mspi);
+ fsl_spi_cpm_bufs_start(mspi);
else
complete(&mspi->done);
}
-static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
+static void fsl_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
{
+ struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
+
/* We need handle RX first */
if (events & SPIE_NE) {
- u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive);
+ u32 rx_data = mpc8xxx_spi_read_reg(®_base->receive);
if (mspi->rx)
mspi->get_rx(rx_data, mspi);
@@ -714,102 +599,80 @@ static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
if ((events & SPIE_NF) == 0)
/* spin until TX is done */
while (((events =
- mpc8xxx_spi_read_reg(&mspi->base->event)) &
+ mpc8xxx_spi_read_reg(®_base->event)) &
SPIE_NF) == 0)
cpu_relax();
/* Clear the events */
- mpc8xxx_spi_write_reg(&mspi->base->event, events);
+ mpc8xxx_spi_write_reg(®_base->event, events);
mspi->count -= 1;
if (mspi->count) {
u32 word = mspi->get_tx(mspi);
- mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+ mpc8xxx_spi_write_reg(®_base->transmit, word);
} else {
complete(&mspi->done);
}
}
-static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
+static irqreturn_t fsl_spi_irq(s32 irq, void *context_data)
{
struct mpc8xxx_spi *mspi = context_data;
irqreturn_t ret = IRQ_NONE;
u32 events;
+ struct fsl_spi_reg *reg_base = (struct fsl_spi_reg *)mspi->reg_base;
/* Get interrupt events(tx/rx) */
- events = mpc8xxx_spi_read_reg(&mspi->base->event);
+ events = mpc8xxx_spi_read_reg(®_base->event);
if (events)
ret = IRQ_HANDLED;
dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);
if (mspi->flags & SPI_CPM_MODE)
- mpc8xxx_spi_cpm_irq(mspi, events);
+ fsl_spi_cpm_irq(mspi, events);
else
- mpc8xxx_spi_cpu_irq(mspi, events);
+ fsl_spi_cpu_irq(mspi, events);
return ret;
}
-static int mpc8xxx_spi_transfer(struct spi_device *spi,
- struct spi_message *m)
+static void *fsl_spi_alloc_dummy_rx(void)
{
- struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
- unsigned long flags;
+ mutex_lock(&fsl_dummy_rx_lock);
- m->actual_length = 0;
- m->status = -EINPROGRESS;
+ if (!fsl_dummy_rx)
+ fsl_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
+ if (fsl_dummy_rx)
+ fsl_dummy_rx_refcnt++;
- spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
- list_add_tail(&m->queue, &mpc8xxx_spi->queue);
- queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
- spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags);
+ mutex_unlock(&fsl_dummy_rx_lock);
- return 0;
+ return fsl_dummy_rx;
}
-
-static void mpc8xxx_spi_cleanup(struct spi_device *spi)
+static void fsl_spi_free_dummy_rx(void)
{
- kfree(spi->controller_state);
-}
+ mutex_lock(&fsl_dummy_rx_lock);
-static void *mpc8xxx_spi_alloc_dummy_rx(void)
-{
- mutex_lock(&mpc8xxx_dummy_rx_lock);
-
- if (!mpc8xxx_dummy_rx)
- mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
- if (mpc8xxx_dummy_rx)
- mpc8xxx_dummy_rx_refcnt++;
-
- mutex_unlock(&mpc8xxx_dummy_rx_lock);
-
- return mpc8xxx_dummy_rx;
-}
-
-static void mpc8xxx_spi_free_dummy_rx(void)
-{
- mutex_lock(&mpc8xxx_dummy_rx_lock);
-
- switch (mpc8xxx_dummy_rx_refcnt) {
+ switch (fsl_dummy_rx_refcnt) {
case 0:
WARN_ON(1);
break;
case 1:
- kfree(mpc8xxx_dummy_rx);
- mpc8xxx_dummy_rx = NULL;
+ kfree(fsl_dummy_rx);
+ fsl_dummy_rx = NULL;
/* fall through */
default:
- mpc8xxx_dummy_rx_refcnt--;
+ fsl_dummy_rx_refcnt--;
break;
}
- mutex_unlock(&mpc8xxx_dummy_rx_lock);
+ mutex_unlock(&fsl_dummy_rx_lock);
}
-static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
+static unsigned long fsl_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
{
struct device *dev = mspi->dev;
struct device_node *np = dev->of_node;
@@ -863,7 +726,7 @@ static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
return pram_ofs;
}
-static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
+static int fsl_spi_cpm_init(struct mpc8xxx_spi *mspi)
{
struct device *dev = mspi->dev;
struct device_node *np = dev->of_node;
@@ -875,7 +738,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
if (!(mspi->flags & SPI_CPM_MODE))
return 0;
- if (!mpc8xxx_spi_alloc_dummy_rx())
+ if (!fsl_spi_alloc_dummy_rx())
return -ENOMEM;
if (mspi->flags & SPI_QE) {
@@ -896,7 +759,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
}
}
- pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi);
+ pram_ofs = fsl_spi_cpm_get_pram(mspi);
if (IS_ERR_VALUE(pram_ofs)) {
dev_err(dev, "can't allocate spi parameter ram\n");
goto err_pram;
@@ -916,7 +779,7 @@ static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
goto err_dummy_tx;
}
- mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR,
+ mspi->dma_dummy_rx = dma_map_single(dev, fsl_dummy_rx, SPI_MRBLR,
DMA_FROM_DEVICE);
if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
dev_err(dev, "unable to map dummy rx buffer\n");
@@ -954,11 +817,11 @@ err_dummy_tx:
err_bds:
cpm_muram_free(pram_ofs);
err_pram:
- mpc8xxx_spi_free_dummy_rx();
+ fsl_spi_free_dummy_rx();
return -ENOMEM;
}
-static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
+static void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi)
{
struct device *dev = mspi->dev;
@@ -966,30 +829,22 @@ static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
cpm_muram_free(cpm_muram_offset(mspi->pram));
- mpc8xxx_spi_free_dummy_rx();
+ fsl_spi_free_dummy_rx();
}
-static const char *mpc8xxx_spi_strmode(unsigned int flags)
+static void fsl_spi_remove(struct mpc8xxx_spi *mspi)
{
- if (flags & SPI_QE_CPU_MODE) {
- return "QE CPU";
- } else if (flags & SPI_CPM_MODE) {
- if (flags & SPI_QE)
- return "QE";
- else if (flags & SPI_CPM2)
- return "CPM2";
- else
- return "CPM1";
- }
- return "CPU";
+ iounmap(mspi->reg_base);
+ fsl_spi_cpm_free(mspi);
}
-static struct spi_master * __devinit
-mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
+static struct spi_master * __devinit fsl_spi_probe(struct device *dev,
+ struct resource *mem, unsigned int irq)
{
struct fsl_spi_platform_data *pdata = dev->platform_data;
struct spi_master *master;
struct mpc8xxx_spi *mpc8xxx_spi;
+ struct fsl_spi_reg *reg_base;
u32 regval;
int ret = 0;
@@ -1001,132 +856,77 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
dev_set_drvdata(dev, master);
- /* the spi->mode bits understood by this driver: */
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
- | SPI_LSB_FIRST | SPI_LOOP;
+ ret = mpc8xxx_spi_probe(dev, mem, irq);
+ if (ret)
+ goto err_probe;
- master->setup = mpc8xxx_spi_setup;
- master->transfer = mpc8xxx_spi_transfer;
- master->cleanup = mpc8xxx_spi_cleanup;
- master->dev.of_node = dev->of_node;
+ master->setup = fsl_spi_setup;
mpc8xxx_spi = spi_master_get_devdata(master);
- mpc8xxx_spi->dev = dev;
- mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
- mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
- mpc8xxx_spi->flags = pdata->flags;
- mpc8xxx_spi->spibrg = pdata->sysclk;
+ mpc8xxx_spi->spi_do_one_msg = fsl_spi_do_one_msg;
+ mpc8xxx_spi->spi_remove = fsl_spi_remove;
+
- ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi);
+ ret = fsl_spi_cpm_init(mpc8xxx_spi);
if (ret)
goto err_cpm_init;
- mpc8xxx_spi->rx_shift = 0;
- mpc8xxx_spi->tx_shift = 0;
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
mpc8xxx_spi->rx_shift = 16;
mpc8xxx_spi->tx_shift = 24;
}
- init_completion(&mpc8xxx_spi->done);
-
- mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem));
- if (mpc8xxx_spi->base == NULL) {
+ mpc8xxx_spi->reg_base = ioremap(mem->start, resource_size(mem));
+ if (mpc8xxx_spi->reg_base == NULL) {
ret = -ENOMEM;
goto err_ioremap;
}
- mpc8xxx_spi->irq = irq;
-
/* Register for SPI Interrupt */
- ret = request_irq(mpc8xxx_spi->irq, mpc8xxx_spi_irq,
- 0, "mpc8xxx_spi", mpc8xxx_spi);
+ ret = request_irq(mpc8xxx_spi->irq, fsl_spi_irq,
+ 0, "fsl_spi", mpc8xxx_spi);
if (ret != 0)
- goto unmap_io;
+ goto free_irq;
- master->bus_num = pdata->bus_num;
- master->num_chipselect = pdata->max_chipselect;
+ reg_base = (struct fsl_spi_reg *)mpc8xxx_spi->reg_base;
/* SPI controller initializations */
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, 0);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->command, 0);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, 0xffffffff);
+ mpc8xxx_spi_write_reg(®_base->mode, 0);
+ mpc8xxx_spi_write_reg(®_base->mask, 0);
+ mpc8xxx_spi_write_reg(®_base->command, 0);
+ mpc8xxx_spi_write_reg(®_base->event, 0xffffffff);
/* Enable SPI interface */
regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
regval |= SPMODE_OP;
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval);
- spin_lock_init(&mpc8xxx_spi->lock);
- init_completion(&mpc8xxx_spi->done);
- INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work);
- INIT_LIST_HEAD(&mpc8xxx_spi->queue);
-
- mpc8xxx_spi->workqueue = create_singlethread_workqueue(
- dev_name(master->dev.parent));
- if (mpc8xxx_spi->workqueue == NULL) {
- ret = -EBUSY;
- goto free_irq;
- }
+ mpc8xxx_spi_write_reg(®_base->mode, regval);
ret = spi_register_master(master);
if (ret < 0)
goto unreg_master;
- dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base,
+ dev_info(dev, "at 0x%p (irq = %d), %s mode\n", reg_base,
mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags));
return master;
unreg_master:
- destroy_workqueue(mpc8xxx_spi->workqueue);
-free_irq:
free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
-unmap_io:
- iounmap(mpc8xxx_spi->base);
+free_irq:
+ iounmap(mpc8xxx_spi->reg_base);
err_ioremap:
- mpc8xxx_spi_cpm_free(mpc8xxx_spi);
+ fsl_spi_cpm_free(mpc8xxx_spi);
err_cpm_init:
+err_probe:
spi_master_put(master);
err:
return ERR_PTR(ret);
}
-static int __devexit mpc8xxx_spi_remove(struct device *dev)
-{
- struct mpc8xxx_spi *mpc8xxx_spi;
- struct spi_master *master;
-
- master = dev_get_drvdata(dev);
- mpc8xxx_spi = spi_master_get_devdata(master);
-
- flush_workqueue(mpc8xxx_spi->workqueue);
- destroy_workqueue(mpc8xxx_spi->workqueue);
- spi_unregister_master(master);
-
- free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
- iounmap(mpc8xxx_spi->base);
- mpc8xxx_spi_cpm_free(mpc8xxx_spi);
-
- return 0;
-}
-
-struct mpc8xxx_spi_probe_info {
- struct fsl_spi_platform_data pdata;
- int *gpios;
- bool *alow_flags;
-};
-
-static struct mpc8xxx_spi_probe_info *
-to_of_pinfo(struct fsl_spi_platform_data *pdata)
-{
- return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata);
-}
-
-static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on)
+static void fsl_spi_cs_control(struct spi_device *spi, bool on)
{
struct device *dev = spi->dev.parent;
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data);
@@ -1137,7 +937,7 @@ static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on)
gpio_set_value(gpio, on ^ alow);
}
-static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
+static int of_fsl_spi_get_chipselects(struct device *dev)
{
struct device_node *np = dev->of_node;
struct fsl_spi_platform_data *pdata = dev->platform_data;
@@ -1198,7 +998,7 @@ static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
}
pdata->max_chipselect = ngpios;
- pdata->cs_control = mpc8xxx_spi_cs_control;
+ pdata->cs_control = fsl_spi_cs_control;
return 0;
@@ -1217,7 +1017,7 @@ err_alloc_flags:
return ret;
}
-static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
+static int of_fsl_spi_free_chipselects(struct device *dev)
{
struct fsl_spi_platform_data *pdata = dev->platform_data;
struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
@@ -1236,50 +1036,21 @@ static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
return 0;
}
-static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
- const struct of_device_id *ofid)
+static int __devinit of_fsl_spi_probe(struct platform_device *ofdev,
+ const struct of_device_id *ofid)
{
struct device *dev = &ofdev->dev;
struct device_node *np = ofdev->dev.of_node;
- struct mpc8xxx_spi_probe_info *pinfo;
- struct fsl_spi_platform_data *pdata;
struct spi_master *master;
struct resource mem;
struct resource irq;
- const void *prop;
int ret = -ENOMEM;
- pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
- if (!pinfo)
- return -ENOMEM;
-
- pdata = &pinfo->pdata;
- dev->platform_data = pdata;
-
- /* Allocate bus num dynamically. */
- pdata->bus_num = -1;
-
- /* SPI controller is either clocked from QE or SoC clock. */
- pdata->sysclk = get_brgfreq();
- if (pdata->sysclk == -1) {
- pdata->sysclk = fsl_get_sys_freq();
- if (pdata->sysclk == -1) {
- ret = -ENODEV;
- goto err_clk;
- }
- }
+ ret = of_mpc8xxx_spi_probe(ofdev, ofid);
+ if (ret)
+ return ret;
- prop = of_get_property(np, "mode", NULL);
- if (prop && !strcmp(prop, "cpu-qe"))
- pdata->flags = SPI_QE_CPU_MODE;
- else if (prop && !strcmp(prop, "qe"))
- pdata->flags = SPI_CPM_MODE | SPI_QE;
- else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
- pdata->flags = SPI_CPM_MODE | SPI_CPM2;
- else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
- pdata->flags = SPI_CPM_MODE | SPI_CPM1;
-
- ret = of_mpc8xxx_spi_get_chipselects(dev);
+ ret = of_fsl_spi_get_chipselects(dev);
if (ret)
goto err;
@@ -1293,7 +1064,7 @@ static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
goto err;
}
- master = mpc8xxx_spi_probe(dev, &mem, irq.start);
+ master = fsl_spi_probe(dev, &mem, irq.start);
if (IS_ERR(master)) {
ret = PTR_ERR(master);
goto err;
@@ -1302,42 +1073,40 @@ static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
return 0;
err:
- of_mpc8xxx_spi_free_chipselects(dev);
-err_clk:
- kfree(pinfo);
+ of_fsl_spi_free_chipselects(dev);
return ret;
}
-static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev)
+static int __devexit of_fsl_spi_remove(struct platform_device *ofdev)
{
int ret;
ret = mpc8xxx_spi_remove(&ofdev->dev);
if (ret)
return ret;
- of_mpc8xxx_spi_free_chipselects(&ofdev->dev);
+ of_fsl_spi_free_chipselects(&ofdev->dev);
return 0;
}
-static const struct of_device_id of_mpc8xxx_spi_match[] = {
+static const struct of_device_id of_fsl_spi_match[] = {
{ .compatible = "fsl,spi" },
- {},
+ {}
};
-MODULE_DEVICE_TABLE(of, of_mpc8xxx_spi_match);
+MODULE_DEVICE_TABLE(of, of_fsl_spi_match);
-static struct of_platform_driver of_mpc8xxx_spi_driver = {
+static struct of_platform_driver of_fsl_spi_driver = {
.driver = {
- .name = "mpc8xxx_spi",
+ .name = "fsl_spi",
.owner = THIS_MODULE,
- .of_match_table = of_mpc8xxx_spi_match,
+ .of_match_table = of_fsl_spi_match,
},
- .probe = of_mpc8xxx_spi_probe,
- .remove = __devexit_p(of_mpc8xxx_spi_remove),
+ .probe = of_fsl_spi_probe,
+ .remove = __devexit_p(of_fsl_spi_remove),
};
#ifdef CONFIG_MPC832x_RDB
/*
- * XXX XXX XXX
+ * XXX XXX XXX
* This is "legacy" platform driver, was used by the MPC8323E-RDB boards
* only. The driver should go away soon, since newer MPC8323E-RDB's device
* tree can work with OpenFirmware driver. But for now we support old trees
@@ -1360,7 +1129,7 @@ static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev)
if (irq <= 0)
return -EINVAL;
- master = mpc8xxx_spi_probe(&pdev->dev, mem, irq);
+ master = fsl_spi_probe(&pdev->dev, mem, irq);
if (IS_ERR(master))
return PTR_ERR(master);
return 0;
@@ -1399,21 +1168,20 @@ static void __init legacy_driver_register(void) {}
static void __exit legacy_driver_unregister(void) {}
#endif /* CONFIG_MPC832x_RDB */
-static int __init mpc8xxx_spi_init(void)
+static int __init fsl_spi_init(void)
{
legacy_driver_register();
- return of_register_platform_driver(&of_mpc8xxx_spi_driver);
+ return of_register_platform_driver(&of_fsl_spi_driver);
}
+module_init(fsl_spi_init);
-static void __exit mpc8xxx_spi_exit(void)
+static void __exit fsl_spi_exit(void)
{
- of_unregister_platform_driver(&of_mpc8xxx_spi_driver);
+ of_unregister_platform_driver(&of_fsl_spi_driver);
legacy_driver_unregister();
}
-
-module_init(mpc8xxx_spi_init);
-module_exit(mpc8xxx_spi_exit);
+module_exit(fsl_spi_exit);
MODULE_AUTHOR("Kumar Gala");
-MODULE_DESCRIPTION("Simple MPC8xxx SPI Driver");
+MODULE_DESCRIPTION("Simple Freescale SPI Driver");
MODULE_LICENSE("GPL");
--
1.6.4
^ permalink raw reply related
* [PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
From: Mingkai Hu @ 2010-09-30 8:00 UTC (permalink / raw)
To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-6-git-send-email-Mingkai.hu@freescale.com>
For Freescale's eSPI controller, the max transaction length one time
is limitted by the SPCOM[TRANSLEN] field which is 0xFFFF. When used
mkfs.ext2 command to create ext2 filesystem on the flash, the read
length will exceed the max value of the SPCOM[TRANSLEN] field, so
change the read function to read page by page.
For other SPI flash driver, also needed to supply the read function
if used the eSPI controller.
Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
- Add a quirks member for the SPI master to handle the contrains of the
SPI controller. I can't think of other method. :-(
drivers/mtd/devices/m25p80.c | 78 ++++++++++++++++++++++++++++++++++++++++++
drivers/spi/spi_fsl_lib.c | 4 ++
include/linux/spi/spi.h | 5 +++
3 files changed, 87 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 47d53c7..f65cca8 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -377,6 +377,81 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
}
/*
+ * Read an address range from the flash chip page by page.
+ * Some controller has transaction length limitation such as the
+ * Freescale's eSPI controller can only trasmit 0xFFFF bytes one
+ * time, so we have to read page by page if the len is more than
+ * the limitation.
+ */
+static int m25p80_page_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct m25p *flash = mtd_to_m25p(mtd);
+ struct spi_transfer t[2];
+ struct spi_message m;
+ u32 i, page_size = 0;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
+ dev_name(&flash->spi->dev), __func__, "from",
+ (u32)from, len);
+
+ /* sanity checks */
+ if (!len)
+ return 0;
+
+ if (from + len > flash->mtd.size)
+ return -EINVAL;
+
+ spi_message_init(&m);
+ memset(t, 0, (sizeof t));
+
+ /* NOTE:
+ * OPCODE_FAST_READ (if available) is faster.
+ * Should add 1 byte DUMMY_BYTE.
+ */
+ t[0].tx_buf = flash->command;
+ t[0].len = m25p_cmdsz(flash) + FAST_READ_DUMMY_BYTE;
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].rx_buf = buf;
+ spi_message_add_tail(&t[1], &m);
+
+ /* Byte count starts at zero. */
+ if (retlen)
+ *retlen = 0;
+
+ mutex_lock(&flash->lock);
+
+ /* Wait till previous write/erase is done. */
+ if (wait_till_ready(flash)) {
+ /* REVISIT status return?? */
+ mutex_unlock(&flash->lock);
+ return 1;
+ }
+
+ /* Set up the write data buffer. */
+ flash->command[0] = OPCODE_READ;
+
+ for (i = page_size; i < len; i += page_size) {
+ page_size = len - i;
+ if (page_size > flash->page_size)
+ page_size = flash->page_size;
+ m25p_addr2cmd(flash, from + i, flash->command);
+ t[1].len = page_size;
+ t[1].rx_buf = buf + i;
+
+ spi_sync(flash->spi, &m);
+
+ *retlen += m.actual_length - m25p_cmdsz(flash)
+ - FAST_READ_DUMMY_BYTE;
+ }
+
+ mutex_unlock(&flash->lock);
+
+ return 0;
+}
+
+/*
* Write an address range to the flash chip. Data must be written in
* FLASH_PAGESIZE chunks. The address range may be any size provided
* it is within the physical boundaries.
@@ -874,6 +949,9 @@ static int __devinit m25p_probe(struct spi_device *spi)
flash->mtd.erase = m25p80_erase;
flash->mtd.read = m25p80_read;
+ if (spi->master->quirks & SPI_QUIRK_TRANS_LEN_LIMIT)
+ flash->mtd.read = m25p80_page_read;
+
/* sst flash chips use AAI word program */
if (info->jedec_id >> 16 == 0xbf)
flash->mtd.write = sst_write;
diff --git a/drivers/spi/spi_fsl_lib.c b/drivers/spi/spi_fsl_lib.c
index 5cd741f..c8d8c2d 100644
--- a/drivers/spi/spi_fsl_lib.c
+++ b/drivers/spi/spi_fsl_lib.c
@@ -135,6 +135,10 @@ int mpc8xxx_spi_probe(struct device *dev, struct resource *mem,
master->cleanup = mpc8xxx_spi_cleanup;
master->dev.of_node = dev->of_node;
+ if (of_get_property(dev->of_node,
+ "fsl,spi-quirk-trans-len-limit", NULL))
+ master->quirks |= SPI_QUIRK_TRANS_LEN_LIMIT;
+
mpc8xxx_spi = spi_master_get_devdata(master);
mpc8xxx_spi->dev = dev;
mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 92e52a1..4234dfd 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -304,6 +304,11 @@ struct spi_master {
/* called on release() to free memory provided by spi_master */
void (*cleanup)(struct spi_device *spi);
+
+ /* some constraints of the controller */
+ u16 quirks;
+#define SPI_QUIRK_TRANS_LEN_LIMIT BIT(0) /* have trans length limit */
+
};
static inline void *spi_master_get_devdata(struct spi_master *master)
--
1.6.4
^ permalink raw reply related
* [PATCH v3 5/7] mtd: m25p80: add support to parse the SPI flash's partitions
From: Mingkai Hu @ 2010-09-30 8:00 UTC (permalink / raw)
To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-5-git-send-email-Mingkai.hu@freescale.com>
Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
- Move the SPI flash partition code to the probe function.
drivers/mtd/devices/m25p80.c | 39 +++++++++++++++++++++++++++------------
1 files changed, 27 insertions(+), 12 deletions(-)
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index 6f512b5..47d53c7 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -772,7 +772,7 @@ static const struct spi_device_id *__devinit jedec_probe(struct spi_device *spi)
static int __devinit m25p_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
- struct flash_platform_data *data;
+ struct flash_platform_data data, *pdata;
struct m25p *flash;
struct flash_info *info;
unsigned i;
@@ -782,13 +782,27 @@ static int __devinit m25p_probe(struct spi_device *spi)
* a chip ID, try the JEDEC id commands; they'll work for most
* newer chips, even if we don't recognize the particular chip.
*/
- data = spi->dev.platform_data;
- if (data && data->type) {
+ pdata = spi->dev.platform_data;
+ if (!pdata && spi->dev.of_node) {
+ int nr_parts;
+ struct mtd_partition *parts;
+ struct device_node *np = spi->dev.of_node;
+
+ nr_parts = of_mtd_parse_partitions(&spi->dev, np, &parts);
+ if (nr_parts) {
+ pdata = &data;
+ memset(pdata, 0, sizeof(*pdata));
+ pdata->parts = parts;
+ pdata->nr_parts = nr_parts;
+ }
+ }
+
+ if (pdata && pdata->type) {
const struct spi_device_id *plat_id;
for (i = 0; i < ARRAY_SIZE(m25p_ids) - 1; i++) {
plat_id = &m25p_ids[i];
- if (strcmp(data->type, plat_id->name))
+ if (strcmp(pdata->type, plat_id->name))
continue;
break;
}
@@ -796,7 +810,8 @@ static int __devinit m25p_probe(struct spi_device *spi)
if (i < ARRAY_SIZE(m25p_ids) - 1)
id = plat_id;
else
- dev_warn(&spi->dev, "unrecognized id %s\n", data->type);
+ dev_warn(&spi->dev, "unrecognized id %s\n",
+ pdata->type);
}
info = (void *)id->driver_data;
@@ -847,8 +862,8 @@ static int __devinit m25p_probe(struct spi_device *spi)
write_sr(flash, 0);
}
- if (data && data->name)
- flash->mtd.name = data->name;
+ if (pdata && pdata->name)
+ flash->mtd.name = pdata->name;
else
flash->mtd.name = dev_name(&spi->dev);
@@ -919,9 +934,9 @@ static int __devinit m25p_probe(struct spi_device *spi)
part_probes, &parts, 0);
}
- if (nr_parts <= 0 && data && data->parts) {
- parts = data->parts;
- nr_parts = data->nr_parts;
+ if (nr_parts <= 0 && pdata && pdata->parts) {
+ parts = pdata->parts;
+ nr_parts = pdata->nr_parts;
}
if (nr_parts > 0) {
@@ -937,9 +952,9 @@ static int __devinit m25p_probe(struct spi_device *spi)
flash->partitioned = 1;
return add_mtd_partitions(&flash->mtd, parts, nr_parts);
}
- } else if (data && data->nr_parts)
+ } else if (pdata && pdata->nr_parts)
dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
- data->nr_parts, data->name);
+ pdata->nr_parts, pdata->name);
return add_mtd_device(&flash->mtd) == 1 ? -ENODEV : 0;
}
--
1.6.4
^ permalink raw reply related
* [PATCH v3 4/7] powerpc/of: add eSPI controller dts bindings and DTS modification
From: Mingkai Hu @ 2010-09-30 8:00 UTC (permalink / raw)
To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-4-git-send-email-Mingkai.hu@freescale.com>
Also modifiy the document of cell-index in SPI controller. Add the
SPI flash(s25fl128p01) support on p4080ds and mpc8536ds board.
Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
- Add "fsl,p4080-espi" compatible property.
Documentation/powerpc/dts-bindings/fsl/spi.txt | 24 ++++++++++-
arch/powerpc/boot/dts/mpc8536ds.dts | 52 ++++++++++++++++++++++++
arch/powerpc/boot/dts/p4080ds.dts | 11 ++---
3 files changed, 79 insertions(+), 8 deletions(-)
diff --git a/Documentation/powerpc/dts-bindings/fsl/spi.txt b/Documentation/powerpc/dts-bindings/fsl/spi.txt
index 80510c0..777abd7 100644
--- a/Documentation/powerpc/dts-bindings/fsl/spi.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/spi.txt
@@ -1,7 +1,9 @@
* SPI (Serial Peripheral Interface)
Required properties:
-- cell-index : SPI controller index.
+- cell-index : QE SPI subblock index.
+ 0: QE subblock SPI1
+ 1: QE subblock SPI2
- compatible : should be "fsl,spi".
- mode : the SPI operation mode, it can be "cpu" or "cpu-qe".
- reg : Offset and length of the register set for the device
@@ -29,3 +31,23 @@ Example:
gpios = <&gpio 18 1 // device reg=<0>
&gpio 19 1>; // device reg=<1>
};
+
+
+* eSPI (Enhanced Serial Peripheral Interface)
+
+Required properties:
+- compatible : should be "fsl,mpc8536-espi".
+- reg : Offset and length of the register set for the device.
+- interrupts : should contain eSPI interrupt, the device has one interrupt.
+- fsl,espi-num-chipselects : the number of the chipselect signals.
+
+Example:
+ spi@110000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,mpc8536-espi";
+ reg = <0x110000 0x1000>;
+ interrupts = <53 0x2>;
+ interrupt-parent = <&mpic>;
+ fsl,espi-num-chipselects = <4>;
+ };
diff --git a/arch/powerpc/boot/dts/mpc8536ds.dts b/arch/powerpc/boot/dts/mpc8536ds.dts
index 815cebb..a75c10e 100644
--- a/arch/powerpc/boot/dts/mpc8536ds.dts
+++ b/arch/powerpc/boot/dts/mpc8536ds.dts
@@ -108,6 +108,58 @@
};
};
+ spi@7000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "fsl,mpc8536-espi";
+ reg = <0x7000 0x1000>;
+ interrupts = <59 0x2>;
+ interrupt-parent = <&mpic>;
+ fsl,espi-num-chipselects = <4>;
+
+ flash@0 {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "spansion,s25sl12801";
+ reg = <0>;
+ spi-max-frequency = <40000000>;
+ partition@u-boot {
+ label = "u-boot";
+ reg = <0x00000000 0x00100000>;
+ read-only;
+ };
+ partition@kernel {
+ label = "kernel";
+ reg = <0x00100000 0x00500000>;
+ read-only;
+ };
+ partition@dtb {
+ label = "dtb";
+ reg = <0x00600000 0x00100000>;
+ read-only;
+ };
+ partition@fs {
+ label = "file system";
+ reg = <0x00700000 0x00900000>;
+ };
+ };
+ flash@1 {
+ compatible = "spansion,s25sl12801";
+ reg = <1>;
+ spi-max-frequency = <40000000>;
+ };
+ flash@2 {
+ compatible = "spansion,s25sl12801";
+ reg = <2>;
+ spi-max-frequency = <40000000>;
+ };
+ flash@3 {
+ compatible = "spansion,s25sl12801";
+ reg = <3>;
+ spi-max-frequency = <40000000>;
+ };
+ };
+
dma@21300 {
#address-cells = <1>;
#size-cells = <1>;
diff --git a/arch/powerpc/boot/dts/p4080ds.dts b/arch/powerpc/boot/dts/p4080ds.dts
index 2f0de24..5b7fc29 100644
--- a/arch/powerpc/boot/dts/p4080ds.dts
+++ b/arch/powerpc/boot/dts/p4080ds.dts
@@ -236,22 +236,19 @@
};
spi@110000 {
- cell-index = <0>;
#address-cells = <1>;
#size-cells = <0>;
- compatible = "fsl,espi";
+ compatible = "fsl,p4080-espi", "fsl,mpc8536-espi";
reg = <0x110000 0x1000>;
interrupts = <53 0x2>;
interrupt-parent = <&mpic>;
- espi,num-ss-bits = <4>;
- mode = "cpu";
+ fsl,espi-num-chipselects = <4>;
- fsl_m25p80@0 {
+ flash@0 {
#address-cells = <1>;
#size-cells = <1>;
- compatible = "fsl,espi-flash";
+ compatible = "spansion,s25sl12801";
reg = <0>;
- linux,modalias = "fsl_m25p80";
spi-max-frequency = <40000000>; /* input clock */
partition@u-boot {
label = "u-boot";
--
1.6.4
^ permalink raw reply related
* [PATCH v3 1/7] spi/mpc8xxx: rename spi_mpc8xxx.c to spi_fsl_spi.c
From: Mingkai Hu @ 2010-09-30 8:00 UTC (permalink / raw)
To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala, Mingkai Hu
In-Reply-To: <1285833646-12006-1-git-send-email-Mingkai.hu@freescale.com>
This will pave the way to refactor out the common code which can be used
by the eSPI controller driver, and rename the SPI controller dirver to the
file spi_fsl_spi.c.
Signed-off-by: Mingkai Hu <Mingkai.hu@freescale.com>
---
v3:
- Update to the latest Kernel base.
- Rename from spi_mpc8xxx.c to spi_fsl_fsl.c verbatim.
drivers/spi/Kconfig | 9 +-
drivers/spi/Makefile | 2 +-
drivers/spi/spi_fsl_spi.c | 1419 +++++++++++++++++++++++++++++++++++++++++++++
drivers/spi/spi_mpc8xxx.c | 1419 ---------------------------------------------
4 files changed, 1425 insertions(+), 1424 deletions(-)
create mode 100644 drivers/spi/spi_fsl_spi.c
delete mode 100644 drivers/spi/spi_mpc8xxx.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 91c2f4f..6af34c6 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -182,12 +182,13 @@ config SPI_MPC512x_PSC
This enables using the Freescale MPC5121 Programmable Serial
Controller in SPI master mode.
-config SPI_MPC8xxx
- tristate "Freescale MPC8xxx SPI controller"
+config SPI_FSL_SPI
+ tristate "Freescale SPI controller"
depends on FSL_SOC
help
- This enables using the Freescale MPC8xxx SPI controllers in master
- mode.
+ This enables using the Freescale SPI controllers in master mode.
+ MPC83xx platform uses the controller in cpu mode or CPM/QE mode.
+ MPC8569 uses the controller in QE mode, MPC8610 in cpu mode.
config SPI_OMAP_UWIRE
tristate "OMAP1 MicroWire"
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e9cbd18..770817c 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -34,7 +34,7 @@ obj-$(CONFIG_SPI_PL022) += amba-pl022.o
obj-$(CONFIG_SPI_MPC512x_PSC) += mpc512x_psc_spi.o
obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o
obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o
-obj-$(CONFIG_SPI_MPC8xxx) += spi_mpc8xxx.o
+obj-$(CONFIG_SPI_FSL_SPI) += spi_fsl_spi.o
obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o
obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o
diff --git a/drivers/spi/spi_fsl_spi.c b/drivers/spi/spi_fsl_spi.c
new file mode 100644
index 0000000..d31b57f
--- /dev/null
+++ b/drivers/spi/spi_fsl_spi.c
@@ -0,0 +1,1419 @@
+/*
+ * MPC8xxx SPI controller driver.
+ *
+ * Maintainer: Kumar Gala
+ *
+ * Copyright (C) 2006 Polycom, Inc.
+ *
+ * CPM SPI and QE buffer descriptors mode support:
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/bug.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/platform_device.h>
+#include <linux/fsl_devices.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+
+#include <sysdev/fsl_soc.h>
+#include <asm/cpm.h>
+#include <asm/qe.h>
+#include <asm/irq.h>
+
+/* CPM1 and CPM2 are mutually exclusive. */
+#ifdef CONFIG_CPM1
+#include <asm/cpm1.h>
+#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0)
+#else
+#include <asm/cpm2.h>
+#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0)
+#endif
+
+/* SPI Controller registers */
+struct mpc8xxx_spi_reg {
+ u8 res1[0x20];
+ __be32 mode;
+ __be32 event;
+ __be32 mask;
+ __be32 command;
+ __be32 transmit;
+ __be32 receive;
+};
+
+/* SPI Controller mode register definitions */
+#define SPMODE_LOOP (1 << 30)
+#define SPMODE_CI_INACTIVEHIGH (1 << 29)
+#define SPMODE_CP_BEGIN_EDGECLK (1 << 28)
+#define SPMODE_DIV16 (1 << 27)
+#define SPMODE_REV (1 << 26)
+#define SPMODE_MS (1 << 25)
+#define SPMODE_ENABLE (1 << 24)
+#define SPMODE_LEN(x) ((x) << 20)
+#define SPMODE_PM(x) ((x) << 16)
+#define SPMODE_OP (1 << 14)
+#define SPMODE_CG(x) ((x) << 7)
+
+/*
+ * Default for SPI Mode:
+ * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
+ */
+#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \
+ SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf))
+
+/* SPIE register values */
+#define SPIE_NE 0x00000200 /* Not empty */
+#define SPIE_NF 0x00000100 /* Not full */
+
+/* SPIM register values */
+#define SPIM_NE 0x00000200 /* Not empty */
+#define SPIM_NF 0x00000100 /* Not full */
+
+#define SPIE_TXB 0x00000200 /* Last char is written to tx fifo */
+#define SPIE_RXB 0x00000100 /* Last char is written to rx buf */
+
+/* SPCOM register values */
+#define SPCOM_STR (1 << 23) /* Start transmit */
+
+#define SPI_PRAM_SIZE 0x100
+#define SPI_MRBLR ((unsigned int)PAGE_SIZE)
+
+/* SPI Controller driver's private data. */
+struct mpc8xxx_spi {
+ struct device *dev;
+ struct mpc8xxx_spi_reg __iomem *base;
+
+ /* rx & tx bufs from the spi_transfer */
+ const void *tx;
+ void *rx;
+
+ int subblock;
+ struct spi_pram __iomem *pram;
+ struct cpm_buf_desc __iomem *tx_bd;
+ struct cpm_buf_desc __iomem *rx_bd;
+
+ struct spi_transfer *xfer_in_progress;
+
+ /* dma addresses for CPM transfers */
+ dma_addr_t tx_dma;
+ dma_addr_t rx_dma;
+ bool map_tx_dma;
+ bool map_rx_dma;
+
+ dma_addr_t dma_dummy_tx;
+ dma_addr_t dma_dummy_rx;
+
+ /* functions to deal with different sized buffers */
+ void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
+ u32(*get_tx) (struct mpc8xxx_spi *);
+
+ unsigned int count;
+ unsigned int irq;
+
+ unsigned nsecs; /* (clock cycle time)/2 */
+
+ u32 spibrg; /* SPIBRG input clock */
+ u32 rx_shift; /* RX data reg shift when in qe mode */
+ u32 tx_shift; /* TX data reg shift when in qe mode */
+
+ unsigned int flags;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ struct list_head queue;
+ spinlock_t lock;
+
+ struct completion done;
+};
+
+static void *mpc8xxx_dummy_rx;
+static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock);
+static int mpc8xxx_dummy_rx_refcnt;
+
+struct spi_mpc8xxx_cs {
+ /* functions to deal with different sized buffers */
+ void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
+ u32 (*get_tx) (struct mpc8xxx_spi *);
+ u32 rx_shift; /* RX data reg shift when in qe mode */
+ u32 tx_shift; /* TX data reg shift when in qe mode */
+ u32 hw_mode; /* Holds HW mode register settings */
+};
+
+static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
+{
+ out_be32(reg, val);
+}
+
+static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
+{
+ return in_be32(reg);
+}
+
+#define MPC83XX_SPI_RX_BUF(type) \
+static \
+void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
+{ \
+ type *rx = mpc8xxx_spi->rx; \
+ *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \
+ mpc8xxx_spi->rx = rx; \
+}
+
+#define MPC83XX_SPI_TX_BUF(type) \
+static \
+u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \
+{ \
+ u32 data; \
+ const type *tx = mpc8xxx_spi->tx; \
+ if (!tx) \
+ return 0; \
+ data = *tx++ << mpc8xxx_spi->tx_shift; \
+ mpc8xxx_spi->tx = tx; \
+ return data; \
+}
+
+MPC83XX_SPI_RX_BUF(u8)
+MPC83XX_SPI_RX_BUF(u16)
+MPC83XX_SPI_RX_BUF(u32)
+MPC83XX_SPI_TX_BUF(u8)
+MPC83XX_SPI_TX_BUF(u16)
+MPC83XX_SPI_TX_BUF(u32)
+
+static void mpc8xxx_spi_change_mode(struct spi_device *spi)
+{
+ struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
+ struct spi_mpc8xxx_cs *cs = spi->controller_state;
+ __be32 __iomem *mode = &mspi->base->mode;
+ unsigned long flags;
+
+ if (cs->hw_mode == mpc8xxx_spi_read_reg(mode))
+ return;
+
+ /* Turn off IRQs locally to minimize time that SPI is disabled. */
+ local_irq_save(flags);
+
+ /* Turn off SPI unit prior changing mode */
+ mpc8xxx_spi_write_reg(mode, cs->hw_mode & ~SPMODE_ENABLE);
+
+ /* When in CPM mode, we need to reinit tx and rx. */
+ if (mspi->flags & SPI_CPM_MODE) {
+ if (mspi->flags & SPI_QE) {
+ qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock,
+ QE_CR_PROTOCOL_UNSPECIFIED, 0);
+ } else {
+ cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX);
+ if (mspi->flags & SPI_CPM1) {
+ out_be16(&mspi->pram->rbptr,
+ in_be16(&mspi->pram->rbase));
+ out_be16(&mspi->pram->tbptr,
+ in_be16(&mspi->pram->tbase));
+ }
+ }
+ }
+ mpc8xxx_spi_write_reg(mode, cs->hw_mode);
+ local_irq_restore(flags);
+}
+
+static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data;
+ bool pol = spi->mode & SPI_CS_HIGH;
+ struct spi_mpc8xxx_cs *cs = spi->controller_state;
+
+ if (value == BITBANG_CS_INACTIVE) {
+ if (pdata->cs_control)
+ pdata->cs_control(spi, !pol);
+ }
+
+ if (value == BITBANG_CS_ACTIVE) {
+ mpc8xxx_spi->rx_shift = cs->rx_shift;
+ mpc8xxx_spi->tx_shift = cs->tx_shift;
+ mpc8xxx_spi->get_rx = cs->get_rx;
+ mpc8xxx_spi->get_tx = cs->get_tx;
+
+ mpc8xxx_spi_change_mode(spi);
+
+ if (pdata->cs_control)
+ pdata->cs_control(spi, pol);
+ }
+}
+
+static int
+mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
+ struct spi_device *spi,
+ struct mpc8xxx_spi *mpc8xxx_spi,
+ int bits_per_word)
+{
+ cs->rx_shift = 0;
+ cs->tx_shift = 0;
+ if (bits_per_word <= 8) {
+ cs->get_rx = mpc8xxx_spi_rx_buf_u8;
+ cs->get_tx = mpc8xxx_spi_tx_buf_u8;
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
+ cs->rx_shift = 16;
+ cs->tx_shift = 24;
+ }
+ } else if (bits_per_word <= 16) {
+ cs->get_rx = mpc8xxx_spi_rx_buf_u16;
+ cs->get_tx = mpc8xxx_spi_tx_buf_u16;
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
+ cs->rx_shift = 16;
+ cs->tx_shift = 16;
+ }
+ } else if (bits_per_word <= 32) {
+ cs->get_rx = mpc8xxx_spi_rx_buf_u32;
+ cs->get_tx = mpc8xxx_spi_tx_buf_u32;
+ } else
+ return -EINVAL;
+
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE &&
+ spi->mode & SPI_LSB_FIRST) {
+ cs->tx_shift = 0;
+ if (bits_per_word <= 8)
+ cs->rx_shift = 8;
+ else
+ cs->rx_shift = 0;
+ }
+ mpc8xxx_spi->rx_shift = cs->rx_shift;
+ mpc8xxx_spi->tx_shift = cs->tx_shift;
+ mpc8xxx_spi->get_rx = cs->get_rx;
+ mpc8xxx_spi->get_tx = cs->get_tx;
+
+ return bits_per_word;
+}
+
+static int
+mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
+ struct spi_device *spi,
+ int bits_per_word)
+{
+ /* QE uses Little Endian for words > 8
+ * so transform all words > 8 into 8 bits
+ * Unfortnatly that doesn't work for LSB so
+ * reject these for now */
+ /* Note: 32 bits word, LSB works iff
+ * tfcr/rfcr is set to CPMFCR_GBL */
+ if (spi->mode & SPI_LSB_FIRST &&
+ bits_per_word > 8)
+ return -EINVAL;
+ if (bits_per_word > 8)
+ return 8; /* pretend its 8 bits */
+ return bits_per_word;
+}
+
+static
+int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi;
+ int bits_per_word;
+ u8 pm;
+ u32 hz;
+ struct spi_mpc8xxx_cs *cs = spi->controller_state;
+
+ mpc8xxx_spi = spi_master_get_devdata(spi->master);
+
+ if (t) {
+ bits_per_word = t->bits_per_word;
+ hz = t->speed_hz;
+ } else {
+ bits_per_word = 0;
+ hz = 0;
+ }
+
+ /* spi_transfer level calls that work per-word */
+ if (!bits_per_word)
+ bits_per_word = spi->bits_per_word;
+
+ /* Make sure its a bit width we support [4..16, 32] */
+ if ((bits_per_word < 4)
+ || ((bits_per_word > 16) && (bits_per_word != 32)))
+ return -EINVAL;
+
+ if (!hz)
+ hz = spi->max_speed_hz;
+
+ if (!(mpc8xxx_spi->flags & SPI_CPM_MODE))
+ bits_per_word = mspi_apply_cpu_mode_quirks(cs, spi,
+ mpc8xxx_spi,
+ bits_per_word);
+ else if (mpc8xxx_spi->flags & SPI_QE)
+ bits_per_word = mspi_apply_qe_mode_quirks(cs, spi,
+ bits_per_word);
+
+ if (bits_per_word < 0)
+ return bits_per_word;
+
+ if (bits_per_word == 32)
+ bits_per_word = 0;
+ else
+ bits_per_word = bits_per_word - 1;
+
+ /* mask out bits we are going to set */
+ cs->hw_mode &= ~(SPMODE_LEN(0xF) | SPMODE_DIV16
+ | SPMODE_PM(0xF));
+
+ cs->hw_mode |= SPMODE_LEN(bits_per_word);
+
+ if ((mpc8xxx_spi->spibrg / hz) > 64) {
+ cs->hw_mode |= SPMODE_DIV16;
+ pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1;
+
+ WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. "
+ "Will use %d Hz instead.\n", dev_name(&spi->dev),
+ hz, mpc8xxx_spi->spibrg / 1024);
+ if (pm > 16)
+ pm = 16;
+ } else
+ pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
+ if (pm)
+ pm--;
+
+ cs->hw_mode |= SPMODE_PM(pm);
+
+ mpc8xxx_spi_change_mode(spi);
+ return 0;
+}
+
+static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
+{
+ struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
+ struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
+ unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
+ unsigned int xfer_ofs;
+
+ xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
+
+ out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs);
+ out_be16(&rx_bd->cbd_datlen, 0);
+ out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP);
+
+ out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs);
+ out_be16(&tx_bd->cbd_datlen, xfer_len);
+ out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP |
+ BD_SC_LAST);
+
+ /* start transfer */
+ mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR);
+}
+
+static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
+ struct spi_transfer *t, bool is_dma_mapped)
+{
+ struct device *dev = mspi->dev;
+
+ if (is_dma_mapped) {
+ mspi->map_tx_dma = 0;
+ mspi->map_rx_dma = 0;
+ } else {
+ mspi->map_tx_dma = 1;
+ mspi->map_rx_dma = 1;
+ }
+
+ if (!t->tx_buf) {
+ mspi->tx_dma = mspi->dma_dummy_tx;
+ mspi->map_tx_dma = 0;
+ }
+
+ if (!t->rx_buf) {
+ mspi->rx_dma = mspi->dma_dummy_rx;
+ mspi->map_rx_dma = 0;
+ }
+
+ if (mspi->map_tx_dma) {
+ void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */
+
+ mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, mspi->tx_dma)) {
+ dev_err(dev, "unable to map tx dma\n");
+ return -ENOMEM;
+ }
+ } else if (t->tx_buf) {
+ mspi->tx_dma = t->tx_dma;
+ }
+
+ if (mspi->map_rx_dma) {
+ mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, mspi->rx_dma)) {
+ dev_err(dev, "unable to map rx dma\n");
+ goto err_rx_dma;
+ }
+ } else if (t->rx_buf) {
+ mspi->rx_dma = t->rx_dma;
+ }
+
+ /* enable rx ints */
+ mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB);
+
+ mspi->xfer_in_progress = t;
+ mspi->count = t->len;
+
+ /* start CPM transfers */
+ mpc8xxx_spi_cpm_bufs_start(mspi);
+
+ return 0;
+
+err_rx_dma:
+ if (mspi->map_tx_dma)
+ dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
+ return -ENOMEM;
+}
+
+static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
+{
+ struct device *dev = mspi->dev;
+ struct spi_transfer *t = mspi->xfer_in_progress;
+
+ if (mspi->map_tx_dma)
+ dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
+ if (mspi->map_rx_dma)
+ dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE);
+ mspi->xfer_in_progress = NULL;
+}
+
+static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
+ struct spi_transfer *t, unsigned int len)
+{
+ u32 word;
+
+ mspi->count = len;
+
+ /* enable rx ints */
+ mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE);
+
+ /* transmit word */
+ word = mspi->get_tx(mspi);
+ mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+
+ return 0;
+}
+
+static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
+ bool is_dma_mapped)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ unsigned int len = t->len;
+ u8 bits_per_word;
+ int ret;
+
+ bits_per_word = spi->bits_per_word;
+ if (t->bits_per_word)
+ bits_per_word = t->bits_per_word;
+
+ if (bits_per_word > 8) {
+ /* invalid length? */
+ if (len & 1)
+ return -EINVAL;
+ len /= 2;
+ }
+ if (bits_per_word > 16) {
+ /* invalid length? */
+ if (len & 1)
+ return -EINVAL;
+ len /= 2;
+ }
+
+ mpc8xxx_spi->tx = t->tx_buf;
+ mpc8xxx_spi->rx = t->rx_buf;
+
+ INIT_COMPLETION(mpc8xxx_spi->done);
+
+ if (mpc8xxx_spi->flags & SPI_CPM_MODE)
+ ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
+ else
+ ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len);
+ if (ret)
+ return ret;
+
+ wait_for_completion(&mpc8xxx_spi->done);
+
+ /* disable rx ints */
+ mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
+
+ if (mpc8xxx_spi->flags & SPI_CPM_MODE)
+ mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi);
+
+ return mpc8xxx_spi->count;
+}
+
+static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
+{
+ struct spi_device *spi = m->spi;
+ struct spi_transfer *t;
+ unsigned int cs_change;
+ const int nsecs = 50;
+ int status;
+
+ cs_change = 1;
+ status = 0;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if (t->bits_per_word || t->speed_hz) {
+ /* Don't allow changes if CS is active */
+ status = -EINVAL;
+
+ if (cs_change)
+ status = mpc8xxx_spi_setup_transfer(spi, t);
+ if (status < 0)
+ break;
+ }
+
+ if (cs_change) {
+ mpc8xxx_spi_chipselect(spi, BITBANG_CS_ACTIVE);
+ ndelay(nsecs);
+ }
+ cs_change = t->cs_change;
+ if (t->len)
+ status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped);
+ if (status) {
+ status = -EMSGSIZE;
+ break;
+ }
+ m->actual_length += t->len;
+
+ if (t->delay_usecs)
+ udelay(t->delay_usecs);
+
+ if (cs_change) {
+ ndelay(nsecs);
+ mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+ ndelay(nsecs);
+ }
+ }
+
+ m->status = status;
+ m->complete(m->context);
+
+ if (status || !cs_change) {
+ ndelay(nsecs);
+ mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
+ }
+
+ mpc8xxx_spi_setup_transfer(spi, NULL);
+}
+
+static void mpc8xxx_spi_work(struct work_struct *work)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi,
+ work);
+
+ spin_lock_irq(&mpc8xxx_spi->lock);
+ while (!list_empty(&mpc8xxx_spi->queue)) {
+ struct spi_message *m = container_of(mpc8xxx_spi->queue.next,
+ struct spi_message, queue);
+
+ list_del_init(&m->queue);
+ spin_unlock_irq(&mpc8xxx_spi->lock);
+
+ mpc8xxx_spi_do_one_msg(m);
+
+ spin_lock_irq(&mpc8xxx_spi->lock);
+ }
+ spin_unlock_irq(&mpc8xxx_spi->lock);
+}
+
+static int mpc8xxx_spi_setup(struct spi_device *spi)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi;
+ int retval;
+ u32 hw_mode;
+ struct spi_mpc8xxx_cs *cs = spi->controller_state;
+
+ if (!spi->max_speed_hz)
+ return -EINVAL;
+
+ if (!cs) {
+ cs = kzalloc(sizeof *cs, GFP_KERNEL);
+ if (!cs)
+ return -ENOMEM;
+ spi->controller_state = cs;
+ }
+ mpc8xxx_spi = spi_master_get_devdata(spi->master);
+
+ hw_mode = cs->hw_mode; /* Save original settings */
+ cs->hw_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
+ /* mask out bits we are going to set */
+ cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH
+ | SPMODE_REV | SPMODE_LOOP);
+
+ if (spi->mode & SPI_CPHA)
+ cs->hw_mode |= SPMODE_CP_BEGIN_EDGECLK;
+ if (spi->mode & SPI_CPOL)
+ cs->hw_mode |= SPMODE_CI_INACTIVEHIGH;
+ if (!(spi->mode & SPI_LSB_FIRST))
+ cs->hw_mode |= SPMODE_REV;
+ if (spi->mode & SPI_LOOP)
+ cs->hw_mode |= SPMODE_LOOP;
+
+ retval = mpc8xxx_spi_setup_transfer(spi, NULL);
+ if (retval < 0) {
+ cs->hw_mode = hw_mode; /* Restore settings */
+ return retval;
+ }
+ return 0;
+}
+
+static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
+{
+ u16 len;
+
+ dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
+ in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
+
+ len = in_be16(&mspi->rx_bd->cbd_datlen);
+ if (len > mspi->count) {
+ WARN_ON(1);
+ len = mspi->count;
+ }
+
+ /* Clear the events */
+ mpc8xxx_spi_write_reg(&mspi->base->event, events);
+
+ mspi->count -= len;
+ if (mspi->count)
+ mpc8xxx_spi_cpm_bufs_start(mspi);
+ else
+ complete(&mspi->done);
+}
+
+static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
+{
+ /* We need handle RX first */
+ if (events & SPIE_NE) {
+ u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive);
+
+ if (mspi->rx)
+ mspi->get_rx(rx_data, mspi);
+ }
+
+ if ((events & SPIE_NF) == 0)
+ /* spin until TX is done */
+ while (((events =
+ mpc8xxx_spi_read_reg(&mspi->base->event)) &
+ SPIE_NF) == 0)
+ cpu_relax();
+
+ /* Clear the events */
+ mpc8xxx_spi_write_reg(&mspi->base->event, events);
+
+ mspi->count -= 1;
+ if (mspi->count) {
+ u32 word = mspi->get_tx(mspi);
+
+ mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
+ } else {
+ complete(&mspi->done);
+ }
+}
+
+static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
+{
+ struct mpc8xxx_spi *mspi = context_data;
+ irqreturn_t ret = IRQ_NONE;
+ u32 events;
+
+ /* Get interrupt events(tx/rx) */
+ events = mpc8xxx_spi_read_reg(&mspi->base->event);
+ if (events)
+ ret = IRQ_HANDLED;
+
+ dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);
+
+ if (mspi->flags & SPI_CPM_MODE)
+ mpc8xxx_spi_cpm_irq(mspi, events);
+ else
+ mpc8xxx_spi_cpu_irq(mspi, events);
+
+ return ret;
+}
+
+static int mpc8xxx_spi_transfer(struct spi_device *spi,
+ struct spi_message *m)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ unsigned long flags;
+
+ m->actual_length = 0;
+ m->status = -EINPROGRESS;
+
+ spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
+ list_add_tail(&m->queue, &mpc8xxx_spi->queue);
+ queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
+ spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags);
+
+ return 0;
+}
+
+
+static void mpc8xxx_spi_cleanup(struct spi_device *spi)
+{
+ kfree(spi->controller_state);
+}
+
+static void *mpc8xxx_spi_alloc_dummy_rx(void)
+{
+ mutex_lock(&mpc8xxx_dummy_rx_lock);
+
+ if (!mpc8xxx_dummy_rx)
+ mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
+ if (mpc8xxx_dummy_rx)
+ mpc8xxx_dummy_rx_refcnt++;
+
+ mutex_unlock(&mpc8xxx_dummy_rx_lock);
+
+ return mpc8xxx_dummy_rx;
+}
+
+static void mpc8xxx_spi_free_dummy_rx(void)
+{
+ mutex_lock(&mpc8xxx_dummy_rx_lock);
+
+ switch (mpc8xxx_dummy_rx_refcnt) {
+ case 0:
+ WARN_ON(1);
+ break;
+ case 1:
+ kfree(mpc8xxx_dummy_rx);
+ mpc8xxx_dummy_rx = NULL;
+ /* fall through */
+ default:
+ mpc8xxx_dummy_rx_refcnt--;
+ break;
+ }
+
+ mutex_unlock(&mpc8xxx_dummy_rx_lock);
+}
+
+static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
+{
+ struct device *dev = mspi->dev;
+ struct device_node *np = dev->of_node;
+ const u32 *iprop;
+ int size;
+ unsigned long spi_base_ofs;
+ unsigned long pram_ofs = -ENOMEM;
+
+ /* Can't use of_address_to_resource(), QE muram isn't at 0. */
+ iprop = of_get_property(np, "reg", &size);
+
+ /* QE with a fixed pram location? */
+ if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4)
+ return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE);
+
+ /* QE but with a dynamic pram location? */
+ if (mspi->flags & SPI_QE) {
+ pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
+ qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock,
+ QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs);
+ return pram_ofs;
+ }
+
+ /* CPM1 and CPM2 pram must be at a fixed addr. */
+ if (!iprop || size != sizeof(*iprop) * 4)
+ return -ENOMEM;
+
+ spi_base_ofs = cpm_muram_alloc_fixed(iprop[2], 2);
+ if (IS_ERR_VALUE(spi_base_ofs))
+ return -ENOMEM;
+
+ if (mspi->flags & SPI_CPM2) {
+ pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
+ if (!IS_ERR_VALUE(pram_ofs)) {
+ u16 __iomem *spi_base = cpm_muram_addr(spi_base_ofs);
+
+ out_be16(spi_base, pram_ofs);
+ }
+ } else {
+ struct spi_pram __iomem *pram = cpm_muram_addr(spi_base_ofs);
+ u16 rpbase = in_be16(&pram->rpbase);
+
+ /* Microcode relocation patch applied? */
+ if (rpbase)
+ pram_ofs = rpbase;
+ else
+ return spi_base_ofs;
+ }
+
+ cpm_muram_free(spi_base_ofs);
+ return pram_ofs;
+}
+
+static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
+{
+ struct device *dev = mspi->dev;
+ struct device_node *np = dev->of_node;
+ const u32 *iprop;
+ int size;
+ unsigned long pram_ofs;
+ unsigned long bds_ofs;
+
+ if (!(mspi->flags & SPI_CPM_MODE))
+ return 0;
+
+ if (!mpc8xxx_spi_alloc_dummy_rx())
+ return -ENOMEM;
+
+ if (mspi->flags & SPI_QE) {
+ iprop = of_get_property(np, "cell-index", &size);
+ if (iprop && size == sizeof(*iprop))
+ mspi->subblock = *iprop;
+
+ switch (mspi->subblock) {
+ default:
+ dev_warn(dev, "cell-index unspecified, assuming SPI1");
+ /* fall through */
+ case 0:
+ mspi->subblock = QE_CR_SUBBLOCK_SPI1;
+ break;
+ case 1:
+ mspi->subblock = QE_CR_SUBBLOCK_SPI2;
+ break;
+ }
+ }
+
+ pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi);
+ if (IS_ERR_VALUE(pram_ofs)) {
+ dev_err(dev, "can't allocate spi parameter ram\n");
+ goto err_pram;
+ }
+
+ bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) +
+ sizeof(*mspi->rx_bd), 8);
+ if (IS_ERR_VALUE(bds_ofs)) {
+ dev_err(dev, "can't allocate bds\n");
+ goto err_bds;
+ }
+
+ mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, mspi->dma_dummy_tx)) {
+ dev_err(dev, "unable to map dummy tx buffer\n");
+ goto err_dummy_tx;
+ }
+
+ mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
+ dev_err(dev, "unable to map dummy rx buffer\n");
+ goto err_dummy_rx;
+ }
+
+ mspi->pram = cpm_muram_addr(pram_ofs);
+
+ mspi->tx_bd = cpm_muram_addr(bds_ofs);
+ mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd));
+
+ /* Initialize parameter ram. */
+ out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd));
+ out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd));
+ out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL);
+ out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL);
+ out_be16(&mspi->pram->mrblr, SPI_MRBLR);
+ out_be32(&mspi->pram->rstate, 0);
+ out_be32(&mspi->pram->rdp, 0);
+ out_be16(&mspi->pram->rbptr, 0);
+ out_be16(&mspi->pram->rbc, 0);
+ out_be32(&mspi->pram->rxtmp, 0);
+ out_be32(&mspi->pram->tstate, 0);
+ out_be32(&mspi->pram->tdp, 0);
+ out_be16(&mspi->pram->tbptr, 0);
+ out_be16(&mspi->pram->tbc, 0);
+ out_be32(&mspi->pram->txtmp, 0);
+
+ return 0;
+
+err_dummy_rx:
+ dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
+err_dummy_tx:
+ cpm_muram_free(bds_ofs);
+err_bds:
+ cpm_muram_free(pram_ofs);
+err_pram:
+ mpc8xxx_spi_free_dummy_rx();
+ return -ENOMEM;
+}
+
+static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
+{
+ struct device *dev = mspi->dev;
+
+ dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE);
+ dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
+ cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
+ cpm_muram_free(cpm_muram_offset(mspi->pram));
+ mpc8xxx_spi_free_dummy_rx();
+}
+
+static const char *mpc8xxx_spi_strmode(unsigned int flags)
+{
+ if (flags & SPI_QE_CPU_MODE) {
+ return "QE CPU";
+ } else if (flags & SPI_CPM_MODE) {
+ if (flags & SPI_QE)
+ return "QE";
+ else if (flags & SPI_CPM2)
+ return "CPM2";
+ else
+ return "CPM1";
+ }
+ return "CPU";
+}
+
+static struct spi_master * __devinit
+mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
+{
+ struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct spi_master *master;
+ struct mpc8xxx_spi *mpc8xxx_spi;
+ u32 regval;
+ int ret = 0;
+
+ master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
+ if (master == NULL) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev_set_drvdata(dev, master);
+
+ /* the spi->mode bits understood by this driver: */
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
+ | SPI_LSB_FIRST | SPI_LOOP;
+
+ master->setup = mpc8xxx_spi_setup;
+ master->transfer = mpc8xxx_spi_transfer;
+ master->cleanup = mpc8xxx_spi_cleanup;
+ master->dev.of_node = dev->of_node;
+
+ mpc8xxx_spi = spi_master_get_devdata(master);
+ mpc8xxx_spi->dev = dev;
+ mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
+ mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
+ mpc8xxx_spi->flags = pdata->flags;
+ mpc8xxx_spi->spibrg = pdata->sysclk;
+
+ ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi);
+ if (ret)
+ goto err_cpm_init;
+
+ mpc8xxx_spi->rx_shift = 0;
+ mpc8xxx_spi->tx_shift = 0;
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
+ mpc8xxx_spi->rx_shift = 16;
+ mpc8xxx_spi->tx_shift = 24;
+ }
+
+ init_completion(&mpc8xxx_spi->done);
+
+ mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem));
+ if (mpc8xxx_spi->base == NULL) {
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ mpc8xxx_spi->irq = irq;
+
+ /* Register for SPI Interrupt */
+ ret = request_irq(mpc8xxx_spi->irq, mpc8xxx_spi_irq,
+ 0, "mpc8xxx_spi", mpc8xxx_spi);
+
+ if (ret != 0)
+ goto unmap_io;
+
+ master->bus_num = pdata->bus_num;
+ master->num_chipselect = pdata->max_chipselect;
+
+ /* SPI controller initializations */
+ mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, 0);
+ mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
+ mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->command, 0);
+ mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, 0xffffffff);
+
+ /* Enable SPI interface */
+ regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
+ regval |= SPMODE_OP;
+
+ mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval);
+ spin_lock_init(&mpc8xxx_spi->lock);
+ init_completion(&mpc8xxx_spi->done);
+ INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work);
+ INIT_LIST_HEAD(&mpc8xxx_spi->queue);
+
+ mpc8xxx_spi->workqueue = create_singlethread_workqueue(
+ dev_name(master->dev.parent));
+ if (mpc8xxx_spi->workqueue == NULL) {
+ ret = -EBUSY;
+ goto free_irq;
+ }
+
+ ret = spi_register_master(master);
+ if (ret < 0)
+ goto unreg_master;
+
+ dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base,
+ mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags));
+
+ return master;
+
+unreg_master:
+ destroy_workqueue(mpc8xxx_spi->workqueue);
+free_irq:
+ free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
+unmap_io:
+ iounmap(mpc8xxx_spi->base);
+err_ioremap:
+ mpc8xxx_spi_cpm_free(mpc8xxx_spi);
+err_cpm_init:
+ spi_master_put(master);
+err:
+ return ERR_PTR(ret);
+}
+
+static int __devexit mpc8xxx_spi_remove(struct device *dev)
+{
+ struct mpc8xxx_spi *mpc8xxx_spi;
+ struct spi_master *master;
+
+ master = dev_get_drvdata(dev);
+ mpc8xxx_spi = spi_master_get_devdata(master);
+
+ flush_workqueue(mpc8xxx_spi->workqueue);
+ destroy_workqueue(mpc8xxx_spi->workqueue);
+ spi_unregister_master(master);
+
+ free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
+ iounmap(mpc8xxx_spi->base);
+ mpc8xxx_spi_cpm_free(mpc8xxx_spi);
+
+ return 0;
+}
+
+struct mpc8xxx_spi_probe_info {
+ struct fsl_spi_platform_data pdata;
+ int *gpios;
+ bool *alow_flags;
+};
+
+static struct mpc8xxx_spi_probe_info *
+to_of_pinfo(struct fsl_spi_platform_data *pdata)
+{
+ return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata);
+}
+
+static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on)
+{
+ struct device *dev = spi->dev.parent;
+ struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data);
+ u16 cs = spi->chip_select;
+ int gpio = pinfo->gpios[cs];
+ bool alow = pinfo->alow_flags[cs];
+
+ gpio_set_value(gpio, on ^ alow);
+}
+
+static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
+ unsigned int ngpios;
+ int i = 0;
+ int ret;
+
+ ngpios = of_gpio_count(np);
+ if (!ngpios) {
+ /*
+ * SPI w/o chip-select line. One SPI device is still permitted
+ * though.
+ */
+ pdata->max_chipselect = 1;
+ return 0;
+ }
+
+ pinfo->gpios = kmalloc(ngpios * sizeof(*pinfo->gpios), GFP_KERNEL);
+ if (!pinfo->gpios)
+ return -ENOMEM;
+ memset(pinfo->gpios, -1, ngpios * sizeof(*pinfo->gpios));
+
+ pinfo->alow_flags = kzalloc(ngpios * sizeof(*pinfo->alow_flags),
+ GFP_KERNEL);
+ if (!pinfo->alow_flags) {
+ ret = -ENOMEM;
+ goto err_alloc_flags;
+ }
+
+ for (; i < ngpios; i++) {
+ int gpio;
+ enum of_gpio_flags flags;
+
+ gpio = of_get_gpio_flags(np, i, &flags);
+ if (!gpio_is_valid(gpio)) {
+ dev_err(dev, "invalid gpio #%d: %d\n", i, gpio);
+ ret = gpio;
+ goto err_loop;
+ }
+
+ ret = gpio_request(gpio, dev_name(dev));
+ if (ret) {
+ dev_err(dev, "can't request gpio #%d: %d\n", i, ret);
+ goto err_loop;
+ }
+
+ pinfo->gpios[i] = gpio;
+ pinfo->alow_flags[i] = flags & OF_GPIO_ACTIVE_LOW;
+
+ ret = gpio_direction_output(pinfo->gpios[i],
+ pinfo->alow_flags[i]);
+ if (ret) {
+ dev_err(dev, "can't set output direction for gpio "
+ "#%d: %d\n", i, ret);
+ goto err_loop;
+ }
+ }
+
+ pdata->max_chipselect = ngpios;
+ pdata->cs_control = mpc8xxx_spi_cs_control;
+
+ return 0;
+
+err_loop:
+ while (i >= 0) {
+ if (gpio_is_valid(pinfo->gpios[i]))
+ gpio_free(pinfo->gpios[i]);
+ i--;
+ }
+
+ kfree(pinfo->alow_flags);
+ pinfo->alow_flags = NULL;
+err_alloc_flags:
+ kfree(pinfo->gpios);
+ pinfo->gpios = NULL;
+ return ret;
+}
+
+static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
+{
+ struct fsl_spi_platform_data *pdata = dev->platform_data;
+ struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
+ int i;
+
+ if (!pinfo->gpios)
+ return 0;
+
+ for (i = 0; i < pdata->max_chipselect; i++) {
+ if (gpio_is_valid(pinfo->gpios[i]))
+ gpio_free(pinfo->gpios[i]);
+ }
+
+ kfree(pinfo->gpios);
+ kfree(pinfo->alow_flags);
+ return 0;
+}
+
+static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
+ const struct of_device_id *ofid)
+{
+ struct device *dev = &ofdev->dev;
+ struct device_node *np = ofdev->dev.of_node;
+ struct mpc8xxx_spi_probe_info *pinfo;
+ struct fsl_spi_platform_data *pdata;
+ struct spi_master *master;
+ struct resource mem;
+ struct resource irq;
+ const void *prop;
+ int ret = -ENOMEM;
+
+ pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
+ if (!pinfo)
+ return -ENOMEM;
+
+ pdata = &pinfo->pdata;
+ dev->platform_data = pdata;
+
+ /* Allocate bus num dynamically. */
+ pdata->bus_num = -1;
+
+ /* SPI controller is either clocked from QE or SoC clock. */
+ pdata->sysclk = get_brgfreq();
+ if (pdata->sysclk == -1) {
+ pdata->sysclk = fsl_get_sys_freq();
+ if (pdata->sysclk == -1) {
+ ret = -ENODEV;
+ goto err_clk;
+ }
+ }
+
+ prop = of_get_property(np, "mode", NULL);
+ if (prop && !strcmp(prop, "cpu-qe"))
+ pdata->flags = SPI_QE_CPU_MODE;
+ else if (prop && !strcmp(prop, "qe"))
+ pdata->flags = SPI_CPM_MODE | SPI_QE;
+ else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
+ pdata->flags = SPI_CPM_MODE | SPI_CPM2;
+ else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
+ pdata->flags = SPI_CPM_MODE | SPI_CPM1;
+
+ ret = of_mpc8xxx_spi_get_chipselects(dev);
+ if (ret)
+ goto err;
+
+ ret = of_address_to_resource(np, 0, &mem);
+ if (ret)
+ goto err;
+
+ ret = of_irq_to_resource(np, 0, &irq);
+ if (!ret) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ master = mpc8xxx_spi_probe(dev, &mem, irq.start);
+ if (IS_ERR(master)) {
+ ret = PTR_ERR(master);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ of_mpc8xxx_spi_free_chipselects(dev);
+err_clk:
+ kfree(pinfo);
+ return ret;
+}
+
+static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev)
+{
+ int ret;
+
+ ret = mpc8xxx_spi_remove(&ofdev->dev);
+ if (ret)
+ return ret;
+ of_mpc8xxx_spi_free_chipselects(&ofdev->dev);
+ return 0;
+}
+
+static const struct of_device_id of_mpc8xxx_spi_match[] = {
+ { .compatible = "fsl,spi" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_mpc8xxx_spi_match);
+
+static struct of_platform_driver of_mpc8xxx_spi_driver = {
+ .driver = {
+ .name = "mpc8xxx_spi",
+ .owner = THIS_MODULE,
+ .of_match_table = of_mpc8xxx_spi_match,
+ },
+ .probe = of_mpc8xxx_spi_probe,
+ .remove = __devexit_p(of_mpc8xxx_spi_remove),
+};
+
+#ifdef CONFIG_MPC832x_RDB
+/*
+ * XXX XXX XXX
+ * This is "legacy" platform driver, was used by the MPC8323E-RDB boards
+ * only. The driver should go away soon, since newer MPC8323E-RDB's device
+ * tree can work with OpenFirmware driver. But for now we support old trees
+ * as well.
+ */
+static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev)
+{
+ struct resource *mem;
+ int irq;
+ struct spi_master *master;
+
+ if (!pdev->dev.platform_data)
+ return -EINVAL;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -EINVAL;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return -EINVAL;
+
+ master = mpc8xxx_spi_probe(&pdev->dev, mem, irq);
+ if (IS_ERR(master))
+ return PTR_ERR(master);
+ return 0;
+}
+
+static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev)
+{
+ return mpc8xxx_spi_remove(&pdev->dev);
+}
+
+MODULE_ALIAS("platform:mpc8xxx_spi");
+static struct platform_driver mpc8xxx_spi_driver = {
+ .probe = plat_mpc8xxx_spi_probe,
+ .remove = __devexit_p(plat_mpc8xxx_spi_remove),
+ .driver = {
+ .name = "mpc8xxx_spi",
+ .owner = THIS_MODULE,
+ },
+};
+
+static bool legacy_driver_failed;
+
+static void __init legacy_driver_register(void)
+{
+ legacy_driver_failed = platform_driver_register(&mpc8xxx_spi_driver);
+}
+
+static void __exit legacy_driver_unregister(void)
+{
+ if (legacy_driver_failed)
+ return;
+ platform_driver_unregister(&mpc8xxx_spi_driver);
+}
+#else
+static void __init legacy_driver_register(void) {}
+static void __exit legacy_driver_unregister(void) {}
+#endif /* CONFIG_MPC832x_RDB */
+
+static int __init mpc8xxx_spi_init(void)
+{
+ legacy_driver_register();
+ return of_register_platform_driver(&of_mpc8xxx_spi_driver);
+}
+
+static void __exit mpc8xxx_spi_exit(void)
+{
+ of_unregister_platform_driver(&of_mpc8xxx_spi_driver);
+ legacy_driver_unregister();
+}
+
+module_init(mpc8xxx_spi_init);
+module_exit(mpc8xxx_spi_exit);
+
+MODULE_AUTHOR("Kumar Gala");
+MODULE_DESCRIPTION("Simple MPC8xxx SPI Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
deleted file mode 100644
index d31b57f..0000000
--- a/drivers/spi/spi_mpc8xxx.c
+++ /dev/null
@@ -1,1419 +0,0 @@
-/*
- * MPC8xxx SPI controller driver.
- *
- * Maintainer: Kumar Gala
- *
- * Copyright (C) 2006 Polycom, Inc.
- *
- * CPM SPI and QE buffer descriptors mode support:
- * Copyright (c) 2009 MontaVista Software, Inc.
- * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/bug.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/completion.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/irq.h>
-#include <linux/device.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/spi_bitbang.h>
-#include <linux/platform_device.h>
-#include <linux/fsl_devices.h>
-#include <linux/dma-mapping.h>
-#include <linux/mm.h>
-#include <linux/mutex.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <linux/slab.h>
-
-#include <sysdev/fsl_soc.h>
-#include <asm/cpm.h>
-#include <asm/qe.h>
-#include <asm/irq.h>
-
-/* CPM1 and CPM2 are mutually exclusive. */
-#ifdef CONFIG_CPM1
-#include <asm/cpm1.h>
-#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_CH_SPI, 0)
-#else
-#include <asm/cpm2.h>
-#define CPM_SPI_CMD mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 0, 0)
-#endif
-
-/* SPI Controller registers */
-struct mpc8xxx_spi_reg {
- u8 res1[0x20];
- __be32 mode;
- __be32 event;
- __be32 mask;
- __be32 command;
- __be32 transmit;
- __be32 receive;
-};
-
-/* SPI Controller mode register definitions */
-#define SPMODE_LOOP (1 << 30)
-#define SPMODE_CI_INACTIVEHIGH (1 << 29)
-#define SPMODE_CP_BEGIN_EDGECLK (1 << 28)
-#define SPMODE_DIV16 (1 << 27)
-#define SPMODE_REV (1 << 26)
-#define SPMODE_MS (1 << 25)
-#define SPMODE_ENABLE (1 << 24)
-#define SPMODE_LEN(x) ((x) << 20)
-#define SPMODE_PM(x) ((x) << 16)
-#define SPMODE_OP (1 << 14)
-#define SPMODE_CG(x) ((x) << 7)
-
-/*
- * Default for SPI Mode:
- * SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
- */
-#define SPMODE_INIT_VAL (SPMODE_CI_INACTIVEHIGH | SPMODE_DIV16 | SPMODE_REV | \
- SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf))
-
-/* SPIE register values */
-#define SPIE_NE 0x00000200 /* Not empty */
-#define SPIE_NF 0x00000100 /* Not full */
-
-/* SPIM register values */
-#define SPIM_NE 0x00000200 /* Not empty */
-#define SPIM_NF 0x00000100 /* Not full */
-
-#define SPIE_TXB 0x00000200 /* Last char is written to tx fifo */
-#define SPIE_RXB 0x00000100 /* Last char is written to rx buf */
-
-/* SPCOM register values */
-#define SPCOM_STR (1 << 23) /* Start transmit */
-
-#define SPI_PRAM_SIZE 0x100
-#define SPI_MRBLR ((unsigned int)PAGE_SIZE)
-
-/* SPI Controller driver's private data. */
-struct mpc8xxx_spi {
- struct device *dev;
- struct mpc8xxx_spi_reg __iomem *base;
-
- /* rx & tx bufs from the spi_transfer */
- const void *tx;
- void *rx;
-
- int subblock;
- struct spi_pram __iomem *pram;
- struct cpm_buf_desc __iomem *tx_bd;
- struct cpm_buf_desc __iomem *rx_bd;
-
- struct spi_transfer *xfer_in_progress;
-
- /* dma addresses for CPM transfers */
- dma_addr_t tx_dma;
- dma_addr_t rx_dma;
- bool map_tx_dma;
- bool map_rx_dma;
-
- dma_addr_t dma_dummy_tx;
- dma_addr_t dma_dummy_rx;
-
- /* functions to deal with different sized buffers */
- void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
- u32(*get_tx) (struct mpc8xxx_spi *);
-
- unsigned int count;
- unsigned int irq;
-
- unsigned nsecs; /* (clock cycle time)/2 */
-
- u32 spibrg; /* SPIBRG input clock */
- u32 rx_shift; /* RX data reg shift when in qe mode */
- u32 tx_shift; /* TX data reg shift when in qe mode */
-
- unsigned int flags;
-
- struct workqueue_struct *workqueue;
- struct work_struct work;
-
- struct list_head queue;
- spinlock_t lock;
-
- struct completion done;
-};
-
-static void *mpc8xxx_dummy_rx;
-static DEFINE_MUTEX(mpc8xxx_dummy_rx_lock);
-static int mpc8xxx_dummy_rx_refcnt;
-
-struct spi_mpc8xxx_cs {
- /* functions to deal with different sized buffers */
- void (*get_rx) (u32 rx_data, struct mpc8xxx_spi *);
- u32 (*get_tx) (struct mpc8xxx_spi *);
- u32 rx_shift; /* RX data reg shift when in qe mode */
- u32 tx_shift; /* TX data reg shift when in qe mode */
- u32 hw_mode; /* Holds HW mode register settings */
-};
-
-static inline void mpc8xxx_spi_write_reg(__be32 __iomem *reg, u32 val)
-{
- out_be32(reg, val);
-}
-
-static inline u32 mpc8xxx_spi_read_reg(__be32 __iomem *reg)
-{
- return in_be32(reg);
-}
-
-#define MPC83XX_SPI_RX_BUF(type) \
-static \
-void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \
-{ \
- type *rx = mpc8xxx_spi->rx; \
- *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \
- mpc8xxx_spi->rx = rx; \
-}
-
-#define MPC83XX_SPI_TX_BUF(type) \
-static \
-u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \
-{ \
- u32 data; \
- const type *tx = mpc8xxx_spi->tx; \
- if (!tx) \
- return 0; \
- data = *tx++ << mpc8xxx_spi->tx_shift; \
- mpc8xxx_spi->tx = tx; \
- return data; \
-}
-
-MPC83XX_SPI_RX_BUF(u8)
-MPC83XX_SPI_RX_BUF(u16)
-MPC83XX_SPI_RX_BUF(u32)
-MPC83XX_SPI_TX_BUF(u8)
-MPC83XX_SPI_TX_BUF(u16)
-MPC83XX_SPI_TX_BUF(u32)
-
-static void mpc8xxx_spi_change_mode(struct spi_device *spi)
-{
- struct mpc8xxx_spi *mspi = spi_master_get_devdata(spi->master);
- struct spi_mpc8xxx_cs *cs = spi->controller_state;
- __be32 __iomem *mode = &mspi->base->mode;
- unsigned long flags;
-
- if (cs->hw_mode == mpc8xxx_spi_read_reg(mode))
- return;
-
- /* Turn off IRQs locally to minimize time that SPI is disabled. */
- local_irq_save(flags);
-
- /* Turn off SPI unit prior changing mode */
- mpc8xxx_spi_write_reg(mode, cs->hw_mode & ~SPMODE_ENABLE);
-
- /* When in CPM mode, we need to reinit tx and rx. */
- if (mspi->flags & SPI_CPM_MODE) {
- if (mspi->flags & SPI_QE) {
- qe_issue_cmd(QE_INIT_TX_RX, mspi->subblock,
- QE_CR_PROTOCOL_UNSPECIFIED, 0);
- } else {
- cpm_command(CPM_SPI_CMD, CPM_CR_INIT_TRX);
- if (mspi->flags & SPI_CPM1) {
- out_be16(&mspi->pram->rbptr,
- in_be16(&mspi->pram->rbase));
- out_be16(&mspi->pram->tbptr,
- in_be16(&mspi->pram->tbase));
- }
- }
- }
- mpc8xxx_spi_write_reg(mode, cs->hw_mode);
- local_irq_restore(flags);
-}
-
-static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
-{
- struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
- struct fsl_spi_platform_data *pdata = spi->dev.parent->platform_data;
- bool pol = spi->mode & SPI_CS_HIGH;
- struct spi_mpc8xxx_cs *cs = spi->controller_state;
-
- if (value == BITBANG_CS_INACTIVE) {
- if (pdata->cs_control)
- pdata->cs_control(spi, !pol);
- }
-
- if (value == BITBANG_CS_ACTIVE) {
- mpc8xxx_spi->rx_shift = cs->rx_shift;
- mpc8xxx_spi->tx_shift = cs->tx_shift;
- mpc8xxx_spi->get_rx = cs->get_rx;
- mpc8xxx_spi->get_tx = cs->get_tx;
-
- mpc8xxx_spi_change_mode(spi);
-
- if (pdata->cs_control)
- pdata->cs_control(spi, pol);
- }
-}
-
-static int
-mspi_apply_cpu_mode_quirks(struct spi_mpc8xxx_cs *cs,
- struct spi_device *spi,
- struct mpc8xxx_spi *mpc8xxx_spi,
- int bits_per_word)
-{
- cs->rx_shift = 0;
- cs->tx_shift = 0;
- if (bits_per_word <= 8) {
- cs->get_rx = mpc8xxx_spi_rx_buf_u8;
- cs->get_tx = mpc8xxx_spi_tx_buf_u8;
- if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
- cs->rx_shift = 16;
- cs->tx_shift = 24;
- }
- } else if (bits_per_word <= 16) {
- cs->get_rx = mpc8xxx_spi_rx_buf_u16;
- cs->get_tx = mpc8xxx_spi_tx_buf_u16;
- if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
- cs->rx_shift = 16;
- cs->tx_shift = 16;
- }
- } else if (bits_per_word <= 32) {
- cs->get_rx = mpc8xxx_spi_rx_buf_u32;
- cs->get_tx = mpc8xxx_spi_tx_buf_u32;
- } else
- return -EINVAL;
-
- if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE &&
- spi->mode & SPI_LSB_FIRST) {
- cs->tx_shift = 0;
- if (bits_per_word <= 8)
- cs->rx_shift = 8;
- else
- cs->rx_shift = 0;
- }
- mpc8xxx_spi->rx_shift = cs->rx_shift;
- mpc8xxx_spi->tx_shift = cs->tx_shift;
- mpc8xxx_spi->get_rx = cs->get_rx;
- mpc8xxx_spi->get_tx = cs->get_tx;
-
- return bits_per_word;
-}
-
-static int
-mspi_apply_qe_mode_quirks(struct spi_mpc8xxx_cs *cs,
- struct spi_device *spi,
- int bits_per_word)
-{
- /* QE uses Little Endian for words > 8
- * so transform all words > 8 into 8 bits
- * Unfortnatly that doesn't work for LSB so
- * reject these for now */
- /* Note: 32 bits word, LSB works iff
- * tfcr/rfcr is set to CPMFCR_GBL */
- if (spi->mode & SPI_LSB_FIRST &&
- bits_per_word > 8)
- return -EINVAL;
- if (bits_per_word > 8)
- return 8; /* pretend its 8 bits */
- return bits_per_word;
-}
-
-static
-int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
-{
- struct mpc8xxx_spi *mpc8xxx_spi;
- int bits_per_word;
- u8 pm;
- u32 hz;
- struct spi_mpc8xxx_cs *cs = spi->controller_state;
-
- mpc8xxx_spi = spi_master_get_devdata(spi->master);
-
- if (t) {
- bits_per_word = t->bits_per_word;
- hz = t->speed_hz;
- } else {
- bits_per_word = 0;
- hz = 0;
- }
-
- /* spi_transfer level calls that work per-word */
- if (!bits_per_word)
- bits_per_word = spi->bits_per_word;
-
- /* Make sure its a bit width we support [4..16, 32] */
- if ((bits_per_word < 4)
- || ((bits_per_word > 16) && (bits_per_word != 32)))
- return -EINVAL;
-
- if (!hz)
- hz = spi->max_speed_hz;
-
- if (!(mpc8xxx_spi->flags & SPI_CPM_MODE))
- bits_per_word = mspi_apply_cpu_mode_quirks(cs, spi,
- mpc8xxx_spi,
- bits_per_word);
- else if (mpc8xxx_spi->flags & SPI_QE)
- bits_per_word = mspi_apply_qe_mode_quirks(cs, spi,
- bits_per_word);
-
- if (bits_per_word < 0)
- return bits_per_word;
-
- if (bits_per_word == 32)
- bits_per_word = 0;
- else
- bits_per_word = bits_per_word - 1;
-
- /* mask out bits we are going to set */
- cs->hw_mode &= ~(SPMODE_LEN(0xF) | SPMODE_DIV16
- | SPMODE_PM(0xF));
-
- cs->hw_mode |= SPMODE_LEN(bits_per_word);
-
- if ((mpc8xxx_spi->spibrg / hz) > 64) {
- cs->hw_mode |= SPMODE_DIV16;
- pm = (mpc8xxx_spi->spibrg - 1) / (hz * 64) + 1;
-
- WARN_ONCE(pm > 16, "%s: Requested speed is too low: %d Hz. "
- "Will use %d Hz instead.\n", dev_name(&spi->dev),
- hz, mpc8xxx_spi->spibrg / 1024);
- if (pm > 16)
- pm = 16;
- } else
- pm = (mpc8xxx_spi->spibrg - 1) / (hz * 4) + 1;
- if (pm)
- pm--;
-
- cs->hw_mode |= SPMODE_PM(pm);
-
- mpc8xxx_spi_change_mode(spi);
- return 0;
-}
-
-static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
-{
- struct cpm_buf_desc __iomem *tx_bd = mspi->tx_bd;
- struct cpm_buf_desc __iomem *rx_bd = mspi->rx_bd;
- unsigned int xfer_len = min(mspi->count, SPI_MRBLR);
- unsigned int xfer_ofs;
-
- xfer_ofs = mspi->xfer_in_progress->len - mspi->count;
-
- out_be32(&rx_bd->cbd_bufaddr, mspi->rx_dma + xfer_ofs);
- out_be16(&rx_bd->cbd_datlen, 0);
- out_be16(&rx_bd->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_WRAP);
-
- out_be32(&tx_bd->cbd_bufaddr, mspi->tx_dma + xfer_ofs);
- out_be16(&tx_bd->cbd_datlen, xfer_len);
- out_be16(&tx_bd->cbd_sc, BD_SC_READY | BD_SC_INTRPT | BD_SC_WRAP |
- BD_SC_LAST);
-
- /* start transfer */
- mpc8xxx_spi_write_reg(&mspi->base->command, SPCOM_STR);
-}
-
-static int mpc8xxx_spi_cpm_bufs(struct mpc8xxx_spi *mspi,
- struct spi_transfer *t, bool is_dma_mapped)
-{
- struct device *dev = mspi->dev;
-
- if (is_dma_mapped) {
- mspi->map_tx_dma = 0;
- mspi->map_rx_dma = 0;
- } else {
- mspi->map_tx_dma = 1;
- mspi->map_rx_dma = 1;
- }
-
- if (!t->tx_buf) {
- mspi->tx_dma = mspi->dma_dummy_tx;
- mspi->map_tx_dma = 0;
- }
-
- if (!t->rx_buf) {
- mspi->rx_dma = mspi->dma_dummy_rx;
- mspi->map_rx_dma = 0;
- }
-
- if (mspi->map_tx_dma) {
- void *nonconst_tx = (void *)mspi->tx; /* shut up gcc */
-
- mspi->tx_dma = dma_map_single(dev, nonconst_tx, t->len,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, mspi->tx_dma)) {
- dev_err(dev, "unable to map tx dma\n");
- return -ENOMEM;
- }
- } else if (t->tx_buf) {
- mspi->tx_dma = t->tx_dma;
- }
-
- if (mspi->map_rx_dma) {
- mspi->rx_dma = dma_map_single(dev, mspi->rx, t->len,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(dev, mspi->rx_dma)) {
- dev_err(dev, "unable to map rx dma\n");
- goto err_rx_dma;
- }
- } else if (t->rx_buf) {
- mspi->rx_dma = t->rx_dma;
- }
-
- /* enable rx ints */
- mpc8xxx_spi_write_reg(&mspi->base->mask, SPIE_RXB);
-
- mspi->xfer_in_progress = t;
- mspi->count = t->len;
-
- /* start CPM transfers */
- mpc8xxx_spi_cpm_bufs_start(mspi);
-
- return 0;
-
-err_rx_dma:
- if (mspi->map_tx_dma)
- dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
- return -ENOMEM;
-}
-
-static void mpc8xxx_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi)
-{
- struct device *dev = mspi->dev;
- struct spi_transfer *t = mspi->xfer_in_progress;
-
- if (mspi->map_tx_dma)
- dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE);
- if (mspi->map_rx_dma)
- dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE);
- mspi->xfer_in_progress = NULL;
-}
-
-static int mpc8xxx_spi_cpu_bufs(struct mpc8xxx_spi *mspi,
- struct spi_transfer *t, unsigned int len)
-{
- u32 word;
-
- mspi->count = len;
-
- /* enable rx ints */
- mpc8xxx_spi_write_reg(&mspi->base->mask, SPIM_NE);
-
- /* transmit word */
- word = mspi->get_tx(mspi);
- mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
-
- return 0;
-}
-
-static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t,
- bool is_dma_mapped)
-{
- struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
- unsigned int len = t->len;
- u8 bits_per_word;
- int ret;
-
- bits_per_word = spi->bits_per_word;
- if (t->bits_per_word)
- bits_per_word = t->bits_per_word;
-
- if (bits_per_word > 8) {
- /* invalid length? */
- if (len & 1)
- return -EINVAL;
- len /= 2;
- }
- if (bits_per_word > 16) {
- /* invalid length? */
- if (len & 1)
- return -EINVAL;
- len /= 2;
- }
-
- mpc8xxx_spi->tx = t->tx_buf;
- mpc8xxx_spi->rx = t->rx_buf;
-
- INIT_COMPLETION(mpc8xxx_spi->done);
-
- if (mpc8xxx_spi->flags & SPI_CPM_MODE)
- ret = mpc8xxx_spi_cpm_bufs(mpc8xxx_spi, t, is_dma_mapped);
- else
- ret = mpc8xxx_spi_cpu_bufs(mpc8xxx_spi, t, len);
- if (ret)
- return ret;
-
- wait_for_completion(&mpc8xxx_spi->done);
-
- /* disable rx ints */
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
-
- if (mpc8xxx_spi->flags & SPI_CPM_MODE)
- mpc8xxx_spi_cpm_bufs_complete(mpc8xxx_spi);
-
- return mpc8xxx_spi->count;
-}
-
-static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
-{
- struct spi_device *spi = m->spi;
- struct spi_transfer *t;
- unsigned int cs_change;
- const int nsecs = 50;
- int status;
-
- cs_change = 1;
- status = 0;
- list_for_each_entry(t, &m->transfers, transfer_list) {
- if (t->bits_per_word || t->speed_hz) {
- /* Don't allow changes if CS is active */
- status = -EINVAL;
-
- if (cs_change)
- status = mpc8xxx_spi_setup_transfer(spi, t);
- if (status < 0)
- break;
- }
-
- if (cs_change) {
- mpc8xxx_spi_chipselect(spi, BITBANG_CS_ACTIVE);
- ndelay(nsecs);
- }
- cs_change = t->cs_change;
- if (t->len)
- status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped);
- if (status) {
- status = -EMSGSIZE;
- break;
- }
- m->actual_length += t->len;
-
- if (t->delay_usecs)
- udelay(t->delay_usecs);
-
- if (cs_change) {
- ndelay(nsecs);
- mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
- ndelay(nsecs);
- }
- }
-
- m->status = status;
- m->complete(m->context);
-
- if (status || !cs_change) {
- ndelay(nsecs);
- mpc8xxx_spi_chipselect(spi, BITBANG_CS_INACTIVE);
- }
-
- mpc8xxx_spi_setup_transfer(spi, NULL);
-}
-
-static void mpc8xxx_spi_work(struct work_struct *work)
-{
- struct mpc8xxx_spi *mpc8xxx_spi = container_of(work, struct mpc8xxx_spi,
- work);
-
- spin_lock_irq(&mpc8xxx_spi->lock);
- while (!list_empty(&mpc8xxx_spi->queue)) {
- struct spi_message *m = container_of(mpc8xxx_spi->queue.next,
- struct spi_message, queue);
-
- list_del_init(&m->queue);
- spin_unlock_irq(&mpc8xxx_spi->lock);
-
- mpc8xxx_spi_do_one_msg(m);
-
- spin_lock_irq(&mpc8xxx_spi->lock);
- }
- spin_unlock_irq(&mpc8xxx_spi->lock);
-}
-
-static int mpc8xxx_spi_setup(struct spi_device *spi)
-{
- struct mpc8xxx_spi *mpc8xxx_spi;
- int retval;
- u32 hw_mode;
- struct spi_mpc8xxx_cs *cs = spi->controller_state;
-
- if (!spi->max_speed_hz)
- return -EINVAL;
-
- if (!cs) {
- cs = kzalloc(sizeof *cs, GFP_KERNEL);
- if (!cs)
- return -ENOMEM;
- spi->controller_state = cs;
- }
- mpc8xxx_spi = spi_master_get_devdata(spi->master);
-
- hw_mode = cs->hw_mode; /* Save original settings */
- cs->hw_mode = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
- /* mask out bits we are going to set */
- cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH
- | SPMODE_REV | SPMODE_LOOP);
-
- if (spi->mode & SPI_CPHA)
- cs->hw_mode |= SPMODE_CP_BEGIN_EDGECLK;
- if (spi->mode & SPI_CPOL)
- cs->hw_mode |= SPMODE_CI_INACTIVEHIGH;
- if (!(spi->mode & SPI_LSB_FIRST))
- cs->hw_mode |= SPMODE_REV;
- if (spi->mode & SPI_LOOP)
- cs->hw_mode |= SPMODE_LOOP;
-
- retval = mpc8xxx_spi_setup_transfer(spi, NULL);
- if (retval < 0) {
- cs->hw_mode = hw_mode; /* Restore settings */
- return retval;
- }
- return 0;
-}
-
-static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
-{
- u16 len;
-
- dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
- in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);
-
- len = in_be16(&mspi->rx_bd->cbd_datlen);
- if (len > mspi->count) {
- WARN_ON(1);
- len = mspi->count;
- }
-
- /* Clear the events */
- mpc8xxx_spi_write_reg(&mspi->base->event, events);
-
- mspi->count -= len;
- if (mspi->count)
- mpc8xxx_spi_cpm_bufs_start(mspi);
- else
- complete(&mspi->done);
-}
-
-static void mpc8xxx_spi_cpu_irq(struct mpc8xxx_spi *mspi, u32 events)
-{
- /* We need handle RX first */
- if (events & SPIE_NE) {
- u32 rx_data = mpc8xxx_spi_read_reg(&mspi->base->receive);
-
- if (mspi->rx)
- mspi->get_rx(rx_data, mspi);
- }
-
- if ((events & SPIE_NF) == 0)
- /* spin until TX is done */
- while (((events =
- mpc8xxx_spi_read_reg(&mspi->base->event)) &
- SPIE_NF) == 0)
- cpu_relax();
-
- /* Clear the events */
- mpc8xxx_spi_write_reg(&mspi->base->event, events);
-
- mspi->count -= 1;
- if (mspi->count) {
- u32 word = mspi->get_tx(mspi);
-
- mpc8xxx_spi_write_reg(&mspi->base->transmit, word);
- } else {
- complete(&mspi->done);
- }
-}
-
-static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
-{
- struct mpc8xxx_spi *mspi = context_data;
- irqreturn_t ret = IRQ_NONE;
- u32 events;
-
- /* Get interrupt events(tx/rx) */
- events = mpc8xxx_spi_read_reg(&mspi->base->event);
- if (events)
- ret = IRQ_HANDLED;
-
- dev_dbg(mspi->dev, "%s: events %x\n", __func__, events);
-
- if (mspi->flags & SPI_CPM_MODE)
- mpc8xxx_spi_cpm_irq(mspi, events);
- else
- mpc8xxx_spi_cpu_irq(mspi, events);
-
- return ret;
-}
-
-static int mpc8xxx_spi_transfer(struct spi_device *spi,
- struct spi_message *m)
-{
- struct mpc8xxx_spi *mpc8xxx_spi = spi_master_get_devdata(spi->master);
- unsigned long flags;
-
- m->actual_length = 0;
- m->status = -EINPROGRESS;
-
- spin_lock_irqsave(&mpc8xxx_spi->lock, flags);
- list_add_tail(&m->queue, &mpc8xxx_spi->queue);
- queue_work(mpc8xxx_spi->workqueue, &mpc8xxx_spi->work);
- spin_unlock_irqrestore(&mpc8xxx_spi->lock, flags);
-
- return 0;
-}
-
-
-static void mpc8xxx_spi_cleanup(struct spi_device *spi)
-{
- kfree(spi->controller_state);
-}
-
-static void *mpc8xxx_spi_alloc_dummy_rx(void)
-{
- mutex_lock(&mpc8xxx_dummy_rx_lock);
-
- if (!mpc8xxx_dummy_rx)
- mpc8xxx_dummy_rx = kmalloc(SPI_MRBLR, GFP_KERNEL);
- if (mpc8xxx_dummy_rx)
- mpc8xxx_dummy_rx_refcnt++;
-
- mutex_unlock(&mpc8xxx_dummy_rx_lock);
-
- return mpc8xxx_dummy_rx;
-}
-
-static void mpc8xxx_spi_free_dummy_rx(void)
-{
- mutex_lock(&mpc8xxx_dummy_rx_lock);
-
- switch (mpc8xxx_dummy_rx_refcnt) {
- case 0:
- WARN_ON(1);
- break;
- case 1:
- kfree(mpc8xxx_dummy_rx);
- mpc8xxx_dummy_rx = NULL;
- /* fall through */
- default:
- mpc8xxx_dummy_rx_refcnt--;
- break;
- }
-
- mutex_unlock(&mpc8xxx_dummy_rx_lock);
-}
-
-static unsigned long mpc8xxx_spi_cpm_get_pram(struct mpc8xxx_spi *mspi)
-{
- struct device *dev = mspi->dev;
- struct device_node *np = dev->of_node;
- const u32 *iprop;
- int size;
- unsigned long spi_base_ofs;
- unsigned long pram_ofs = -ENOMEM;
-
- /* Can't use of_address_to_resource(), QE muram isn't at 0. */
- iprop = of_get_property(np, "reg", &size);
-
- /* QE with a fixed pram location? */
- if (mspi->flags & SPI_QE && iprop && size == sizeof(*iprop) * 4)
- return cpm_muram_alloc_fixed(iprop[2], SPI_PRAM_SIZE);
-
- /* QE but with a dynamic pram location? */
- if (mspi->flags & SPI_QE) {
- pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
- qe_issue_cmd(QE_ASSIGN_PAGE_TO_DEVICE, mspi->subblock,
- QE_CR_PROTOCOL_UNSPECIFIED, pram_ofs);
- return pram_ofs;
- }
-
- /* CPM1 and CPM2 pram must be at a fixed addr. */
- if (!iprop || size != sizeof(*iprop) * 4)
- return -ENOMEM;
-
- spi_base_ofs = cpm_muram_alloc_fixed(iprop[2], 2);
- if (IS_ERR_VALUE(spi_base_ofs))
- return -ENOMEM;
-
- if (mspi->flags & SPI_CPM2) {
- pram_ofs = cpm_muram_alloc(SPI_PRAM_SIZE, 64);
- if (!IS_ERR_VALUE(pram_ofs)) {
- u16 __iomem *spi_base = cpm_muram_addr(spi_base_ofs);
-
- out_be16(spi_base, pram_ofs);
- }
- } else {
- struct spi_pram __iomem *pram = cpm_muram_addr(spi_base_ofs);
- u16 rpbase = in_be16(&pram->rpbase);
-
- /* Microcode relocation patch applied? */
- if (rpbase)
- pram_ofs = rpbase;
- else
- return spi_base_ofs;
- }
-
- cpm_muram_free(spi_base_ofs);
- return pram_ofs;
-}
-
-static int mpc8xxx_spi_cpm_init(struct mpc8xxx_spi *mspi)
-{
- struct device *dev = mspi->dev;
- struct device_node *np = dev->of_node;
- const u32 *iprop;
- int size;
- unsigned long pram_ofs;
- unsigned long bds_ofs;
-
- if (!(mspi->flags & SPI_CPM_MODE))
- return 0;
-
- if (!mpc8xxx_spi_alloc_dummy_rx())
- return -ENOMEM;
-
- if (mspi->flags & SPI_QE) {
- iprop = of_get_property(np, "cell-index", &size);
- if (iprop && size == sizeof(*iprop))
- mspi->subblock = *iprop;
-
- switch (mspi->subblock) {
- default:
- dev_warn(dev, "cell-index unspecified, assuming SPI1");
- /* fall through */
- case 0:
- mspi->subblock = QE_CR_SUBBLOCK_SPI1;
- break;
- case 1:
- mspi->subblock = QE_CR_SUBBLOCK_SPI2;
- break;
- }
- }
-
- pram_ofs = mpc8xxx_spi_cpm_get_pram(mspi);
- if (IS_ERR_VALUE(pram_ofs)) {
- dev_err(dev, "can't allocate spi parameter ram\n");
- goto err_pram;
- }
-
- bds_ofs = cpm_muram_alloc(sizeof(*mspi->tx_bd) +
- sizeof(*mspi->rx_bd), 8);
- if (IS_ERR_VALUE(bds_ofs)) {
- dev_err(dev, "can't allocate bds\n");
- goto err_bds;
- }
-
- mspi->dma_dummy_tx = dma_map_single(dev, empty_zero_page, PAGE_SIZE,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, mspi->dma_dummy_tx)) {
- dev_err(dev, "unable to map dummy tx buffer\n");
- goto err_dummy_tx;
- }
-
- mspi->dma_dummy_rx = dma_map_single(dev, mpc8xxx_dummy_rx, SPI_MRBLR,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(dev, mspi->dma_dummy_rx)) {
- dev_err(dev, "unable to map dummy rx buffer\n");
- goto err_dummy_rx;
- }
-
- mspi->pram = cpm_muram_addr(pram_ofs);
-
- mspi->tx_bd = cpm_muram_addr(bds_ofs);
- mspi->rx_bd = cpm_muram_addr(bds_ofs + sizeof(*mspi->tx_bd));
-
- /* Initialize parameter ram. */
- out_be16(&mspi->pram->tbase, cpm_muram_offset(mspi->tx_bd));
- out_be16(&mspi->pram->rbase, cpm_muram_offset(mspi->rx_bd));
- out_8(&mspi->pram->tfcr, CPMFCR_EB | CPMFCR_GBL);
- out_8(&mspi->pram->rfcr, CPMFCR_EB | CPMFCR_GBL);
- out_be16(&mspi->pram->mrblr, SPI_MRBLR);
- out_be32(&mspi->pram->rstate, 0);
- out_be32(&mspi->pram->rdp, 0);
- out_be16(&mspi->pram->rbptr, 0);
- out_be16(&mspi->pram->rbc, 0);
- out_be32(&mspi->pram->rxtmp, 0);
- out_be32(&mspi->pram->tstate, 0);
- out_be32(&mspi->pram->tdp, 0);
- out_be16(&mspi->pram->tbptr, 0);
- out_be16(&mspi->pram->tbc, 0);
- out_be32(&mspi->pram->txtmp, 0);
-
- return 0;
-
-err_dummy_rx:
- dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
-err_dummy_tx:
- cpm_muram_free(bds_ofs);
-err_bds:
- cpm_muram_free(pram_ofs);
-err_pram:
- mpc8xxx_spi_free_dummy_rx();
- return -ENOMEM;
-}
-
-static void mpc8xxx_spi_cpm_free(struct mpc8xxx_spi *mspi)
-{
- struct device *dev = mspi->dev;
-
- dma_unmap_single(dev, mspi->dma_dummy_rx, SPI_MRBLR, DMA_FROM_DEVICE);
- dma_unmap_single(dev, mspi->dma_dummy_tx, PAGE_SIZE, DMA_TO_DEVICE);
- cpm_muram_free(cpm_muram_offset(mspi->tx_bd));
- cpm_muram_free(cpm_muram_offset(mspi->pram));
- mpc8xxx_spi_free_dummy_rx();
-}
-
-static const char *mpc8xxx_spi_strmode(unsigned int flags)
-{
- if (flags & SPI_QE_CPU_MODE) {
- return "QE CPU";
- } else if (flags & SPI_CPM_MODE) {
- if (flags & SPI_QE)
- return "QE";
- else if (flags & SPI_CPM2)
- return "CPM2";
- else
- return "CPM1";
- }
- return "CPU";
-}
-
-static struct spi_master * __devinit
-mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
-{
- struct fsl_spi_platform_data *pdata = dev->platform_data;
- struct spi_master *master;
- struct mpc8xxx_spi *mpc8xxx_spi;
- u32 regval;
- int ret = 0;
-
- master = spi_alloc_master(dev, sizeof(struct mpc8xxx_spi));
- if (master == NULL) {
- ret = -ENOMEM;
- goto err;
- }
-
- dev_set_drvdata(dev, master);
-
- /* the spi->mode bits understood by this driver: */
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH
- | SPI_LSB_FIRST | SPI_LOOP;
-
- master->setup = mpc8xxx_spi_setup;
- master->transfer = mpc8xxx_spi_transfer;
- master->cleanup = mpc8xxx_spi_cleanup;
- master->dev.of_node = dev->of_node;
-
- mpc8xxx_spi = spi_master_get_devdata(master);
- mpc8xxx_spi->dev = dev;
- mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8;
- mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8;
- mpc8xxx_spi->flags = pdata->flags;
- mpc8xxx_spi->spibrg = pdata->sysclk;
-
- ret = mpc8xxx_spi_cpm_init(mpc8xxx_spi);
- if (ret)
- goto err_cpm_init;
-
- mpc8xxx_spi->rx_shift = 0;
- mpc8xxx_spi->tx_shift = 0;
- if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
- mpc8xxx_spi->rx_shift = 16;
- mpc8xxx_spi->tx_shift = 24;
- }
-
- init_completion(&mpc8xxx_spi->done);
-
- mpc8xxx_spi->base = ioremap(mem->start, resource_size(mem));
- if (mpc8xxx_spi->base == NULL) {
- ret = -ENOMEM;
- goto err_ioremap;
- }
-
- mpc8xxx_spi->irq = irq;
-
- /* Register for SPI Interrupt */
- ret = request_irq(mpc8xxx_spi->irq, mpc8xxx_spi_irq,
- 0, "mpc8xxx_spi", mpc8xxx_spi);
-
- if (ret != 0)
- goto unmap_io;
-
- master->bus_num = pdata->bus_num;
- master->num_chipselect = pdata->max_chipselect;
-
- /* SPI controller initializations */
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, 0);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, 0);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->command, 0);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, 0xffffffff);
-
- /* Enable SPI interface */
- regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
- if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
- regval |= SPMODE_OP;
-
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval);
- spin_lock_init(&mpc8xxx_spi->lock);
- init_completion(&mpc8xxx_spi->done);
- INIT_WORK(&mpc8xxx_spi->work, mpc8xxx_spi_work);
- INIT_LIST_HEAD(&mpc8xxx_spi->queue);
-
- mpc8xxx_spi->workqueue = create_singlethread_workqueue(
- dev_name(master->dev.parent));
- if (mpc8xxx_spi->workqueue == NULL) {
- ret = -EBUSY;
- goto free_irq;
- }
-
- ret = spi_register_master(master);
- if (ret < 0)
- goto unreg_master;
-
- dev_info(dev, "at 0x%p (irq = %d), %s mode\n", mpc8xxx_spi->base,
- mpc8xxx_spi->irq, mpc8xxx_spi_strmode(mpc8xxx_spi->flags));
-
- return master;
-
-unreg_master:
- destroy_workqueue(mpc8xxx_spi->workqueue);
-free_irq:
- free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
-unmap_io:
- iounmap(mpc8xxx_spi->base);
-err_ioremap:
- mpc8xxx_spi_cpm_free(mpc8xxx_spi);
-err_cpm_init:
- spi_master_put(master);
-err:
- return ERR_PTR(ret);
-}
-
-static int __devexit mpc8xxx_spi_remove(struct device *dev)
-{
- struct mpc8xxx_spi *mpc8xxx_spi;
- struct spi_master *master;
-
- master = dev_get_drvdata(dev);
- mpc8xxx_spi = spi_master_get_devdata(master);
-
- flush_workqueue(mpc8xxx_spi->workqueue);
- destroy_workqueue(mpc8xxx_spi->workqueue);
- spi_unregister_master(master);
-
- free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
- iounmap(mpc8xxx_spi->base);
- mpc8xxx_spi_cpm_free(mpc8xxx_spi);
-
- return 0;
-}
-
-struct mpc8xxx_spi_probe_info {
- struct fsl_spi_platform_data pdata;
- int *gpios;
- bool *alow_flags;
-};
-
-static struct mpc8xxx_spi_probe_info *
-to_of_pinfo(struct fsl_spi_platform_data *pdata)
-{
- return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata);
-}
-
-static void mpc8xxx_spi_cs_control(struct spi_device *spi, bool on)
-{
- struct device *dev = spi->dev.parent;
- struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(dev->platform_data);
- u16 cs = spi->chip_select;
- int gpio = pinfo->gpios[cs];
- bool alow = pinfo->alow_flags[cs];
-
- gpio_set_value(gpio, on ^ alow);
-}
-
-static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
-{
- struct device_node *np = dev->of_node;
- struct fsl_spi_platform_data *pdata = dev->platform_data;
- struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
- unsigned int ngpios;
- int i = 0;
- int ret;
-
- ngpios = of_gpio_count(np);
- if (!ngpios) {
- /*
- * SPI w/o chip-select line. One SPI device is still permitted
- * though.
- */
- pdata->max_chipselect = 1;
- return 0;
- }
-
- pinfo->gpios = kmalloc(ngpios * sizeof(*pinfo->gpios), GFP_KERNEL);
- if (!pinfo->gpios)
- return -ENOMEM;
- memset(pinfo->gpios, -1, ngpios * sizeof(*pinfo->gpios));
-
- pinfo->alow_flags = kzalloc(ngpios * sizeof(*pinfo->alow_flags),
- GFP_KERNEL);
- if (!pinfo->alow_flags) {
- ret = -ENOMEM;
- goto err_alloc_flags;
- }
-
- for (; i < ngpios; i++) {
- int gpio;
- enum of_gpio_flags flags;
-
- gpio = of_get_gpio_flags(np, i, &flags);
- if (!gpio_is_valid(gpio)) {
- dev_err(dev, "invalid gpio #%d: %d\n", i, gpio);
- ret = gpio;
- goto err_loop;
- }
-
- ret = gpio_request(gpio, dev_name(dev));
- if (ret) {
- dev_err(dev, "can't request gpio #%d: %d\n", i, ret);
- goto err_loop;
- }
-
- pinfo->gpios[i] = gpio;
- pinfo->alow_flags[i] = flags & OF_GPIO_ACTIVE_LOW;
-
- ret = gpio_direction_output(pinfo->gpios[i],
- pinfo->alow_flags[i]);
- if (ret) {
- dev_err(dev, "can't set output direction for gpio "
- "#%d: %d\n", i, ret);
- goto err_loop;
- }
- }
-
- pdata->max_chipselect = ngpios;
- pdata->cs_control = mpc8xxx_spi_cs_control;
-
- return 0;
-
-err_loop:
- while (i >= 0) {
- if (gpio_is_valid(pinfo->gpios[i]))
- gpio_free(pinfo->gpios[i]);
- i--;
- }
-
- kfree(pinfo->alow_flags);
- pinfo->alow_flags = NULL;
-err_alloc_flags:
- kfree(pinfo->gpios);
- pinfo->gpios = NULL;
- return ret;
-}
-
-static int of_mpc8xxx_spi_free_chipselects(struct device *dev)
-{
- struct fsl_spi_platform_data *pdata = dev->platform_data;
- struct mpc8xxx_spi_probe_info *pinfo = to_of_pinfo(pdata);
- int i;
-
- if (!pinfo->gpios)
- return 0;
-
- for (i = 0; i < pdata->max_chipselect; i++) {
- if (gpio_is_valid(pinfo->gpios[i]))
- gpio_free(pinfo->gpios[i]);
- }
-
- kfree(pinfo->gpios);
- kfree(pinfo->alow_flags);
- return 0;
-}
-
-static int __devinit of_mpc8xxx_spi_probe(struct platform_device *ofdev,
- const struct of_device_id *ofid)
-{
- struct device *dev = &ofdev->dev;
- struct device_node *np = ofdev->dev.of_node;
- struct mpc8xxx_spi_probe_info *pinfo;
- struct fsl_spi_platform_data *pdata;
- struct spi_master *master;
- struct resource mem;
- struct resource irq;
- const void *prop;
- int ret = -ENOMEM;
-
- pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
- if (!pinfo)
- return -ENOMEM;
-
- pdata = &pinfo->pdata;
- dev->platform_data = pdata;
-
- /* Allocate bus num dynamically. */
- pdata->bus_num = -1;
-
- /* SPI controller is either clocked from QE or SoC clock. */
- pdata->sysclk = get_brgfreq();
- if (pdata->sysclk == -1) {
- pdata->sysclk = fsl_get_sys_freq();
- if (pdata->sysclk == -1) {
- ret = -ENODEV;
- goto err_clk;
- }
- }
-
- prop = of_get_property(np, "mode", NULL);
- if (prop && !strcmp(prop, "cpu-qe"))
- pdata->flags = SPI_QE_CPU_MODE;
- else if (prop && !strcmp(prop, "qe"))
- pdata->flags = SPI_CPM_MODE | SPI_QE;
- else if (of_device_is_compatible(np, "fsl,cpm2-spi"))
- pdata->flags = SPI_CPM_MODE | SPI_CPM2;
- else if (of_device_is_compatible(np, "fsl,cpm1-spi"))
- pdata->flags = SPI_CPM_MODE | SPI_CPM1;
-
- ret = of_mpc8xxx_spi_get_chipselects(dev);
- if (ret)
- goto err;
-
- ret = of_address_to_resource(np, 0, &mem);
- if (ret)
- goto err;
-
- ret = of_irq_to_resource(np, 0, &irq);
- if (!ret) {
- ret = -EINVAL;
- goto err;
- }
-
- master = mpc8xxx_spi_probe(dev, &mem, irq.start);
- if (IS_ERR(master)) {
- ret = PTR_ERR(master);
- goto err;
- }
-
- return 0;
-
-err:
- of_mpc8xxx_spi_free_chipselects(dev);
-err_clk:
- kfree(pinfo);
- return ret;
-}
-
-static int __devexit of_mpc8xxx_spi_remove(struct platform_device *ofdev)
-{
- int ret;
-
- ret = mpc8xxx_spi_remove(&ofdev->dev);
- if (ret)
- return ret;
- of_mpc8xxx_spi_free_chipselects(&ofdev->dev);
- return 0;
-}
-
-static const struct of_device_id of_mpc8xxx_spi_match[] = {
- { .compatible = "fsl,spi" },
- {},
-};
-MODULE_DEVICE_TABLE(of, of_mpc8xxx_spi_match);
-
-static struct of_platform_driver of_mpc8xxx_spi_driver = {
- .driver = {
- .name = "mpc8xxx_spi",
- .owner = THIS_MODULE,
- .of_match_table = of_mpc8xxx_spi_match,
- },
- .probe = of_mpc8xxx_spi_probe,
- .remove = __devexit_p(of_mpc8xxx_spi_remove),
-};
-
-#ifdef CONFIG_MPC832x_RDB
-/*
- * XXX XXX XXX
- * This is "legacy" platform driver, was used by the MPC8323E-RDB boards
- * only. The driver should go away soon, since newer MPC8323E-RDB's device
- * tree can work with OpenFirmware driver. But for now we support old trees
- * as well.
- */
-static int __devinit plat_mpc8xxx_spi_probe(struct platform_device *pdev)
-{
- struct resource *mem;
- int irq;
- struct spi_master *master;
-
- if (!pdev->dev.platform_data)
- return -EINVAL;
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem)
- return -EINVAL;
-
- irq = platform_get_irq(pdev, 0);
- if (irq <= 0)
- return -EINVAL;
-
- master = mpc8xxx_spi_probe(&pdev->dev, mem, irq);
- if (IS_ERR(master))
- return PTR_ERR(master);
- return 0;
-}
-
-static int __devexit plat_mpc8xxx_spi_remove(struct platform_device *pdev)
-{
- return mpc8xxx_spi_remove(&pdev->dev);
-}
-
-MODULE_ALIAS("platform:mpc8xxx_spi");
-static struct platform_driver mpc8xxx_spi_driver = {
- .probe = plat_mpc8xxx_spi_probe,
- .remove = __devexit_p(plat_mpc8xxx_spi_remove),
- .driver = {
- .name = "mpc8xxx_spi",
- .owner = THIS_MODULE,
- },
-};
-
-static bool legacy_driver_failed;
-
-static void __init legacy_driver_register(void)
-{
- legacy_driver_failed = platform_driver_register(&mpc8xxx_spi_driver);
-}
-
-static void __exit legacy_driver_unregister(void)
-{
- if (legacy_driver_failed)
- return;
- platform_driver_unregister(&mpc8xxx_spi_driver);
-}
-#else
-static void __init legacy_driver_register(void) {}
-static void __exit legacy_driver_unregister(void) {}
-#endif /* CONFIG_MPC832x_RDB */
-
-static int __init mpc8xxx_spi_init(void)
-{
- legacy_driver_register();
- return of_register_platform_driver(&of_mpc8xxx_spi_driver);
-}
-
-static void __exit mpc8xxx_spi_exit(void)
-{
- of_unregister_platform_driver(&of_mpc8xxx_spi_driver);
- legacy_driver_unregister();
-}
-
-module_init(mpc8xxx_spi_init);
-module_exit(mpc8xxx_spi_exit);
-
-MODULE_AUTHOR("Kumar Gala");
-MODULE_DESCRIPTION("Simple MPC8xxx SPI Driver");
-MODULE_LICENSE("GPL");
--
1.6.4
^ permalink raw reply related
* [PATCH v3 0/7] refactor spi_mpc8xxx.c and add eSPI controller support
From: Mingkai Hu @ 2010-09-30 8:00 UTC (permalink / raw)
To: linuxppc-dev, spi-devel-general, linux-mtd; +Cc: kumar.gala
This patchset refactor the file spi_mpc8xxx.c to abstract some common
code as a lib used by the SPI/eSPI controller driver, move the SPI
controller driver code to spi_fsl_spi.c, and add the eSPI controller
support with spi_fsl_espi.c.
Tested on P4080DS and MPC8536DS board based on latest Linux tree.
v3 main changes:
- Update to the latest kernel base.
- Change the file spi_mpc8xxx.c to spi_fsl_fsl.c verbatim.
- Add the SPI flash partition code to the m25p80 probe function.
- Add a quirks for SPI master to handle the contrains of the controller.
[PATCH v3 1/7] spi/mpc8xxx: rename spi_mpc8xxx.c to spi_fsl_spi.c
[PATCH v3 2/7] spi/mpc8xxx: refactor the common code for SPI/eSPI controller
[PATCH v3 3/7] eSPI: add eSPI controller support
[PATCH v3 4/7] powerpc/of: add eSPI controller dts bindings and DTS modification
[PATCH v3 5/7] mtd: m25p80: add support to parse the SPI flash's partitions
[PATCH v3 6/7] mtd: m25p80: add a read function to read page by page
[PATCH v3 7/7] DTS: add fsl,spi-quirk-trans-len-limit property
Thanks,
Mingkai
^ permalink raw reply
* Re: [PATCH 4/4] ste_dma40: implement support for scatterlist to scatterlist copy
From: Per Friden @ 2010-09-30 7:13 UTC (permalink / raw)
To: Dan Williams
Cc: Per FORLIN, Linus Walleij, linuxppc-dev@lists.ozlabs.org,
linux-kernel@vger.kernel.org, Ira W. Snyder
In-Reply-To: <AANLkTinYG4LqtEXxM5pdA0vavahtqbt6=yV341pnFmZw@mail.gmail.com>
On 09/29/2010 11:19 PM, Dan Williams wrote:
> On Mon, Sep 27, 2010 at 3:57 PM, Ira W. Snyder <iws@ovro.caltech.edu> wrote:
>> Now that the DMAEngine API has support for scatterlist to scatterlist
>> copy, implement support for the STE DMA40 DMA controller.
>>
>> Cc: Linus Walleij <linus.ml.walleij@gmail.com>
>> Cc: Per Fridén <per.friden@stericsson.com>
>> Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
>> ---
>> drivers/dma/ste_dma40.c | 17 +++++++++++++++++
>> 1 files changed, 17 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
>> index 17e2600..cd48859 100644
>> --- a/drivers/dma/ste_dma40.c
>> +++ b/drivers/dma/ste_dma40.c
>> @@ -1857,6 +1857,18 @@ err:
>> return NULL;
>> }
>>
>> +static struct dma_async_tx_descriptor *
>> +d40_prep_sg(struct dma_chan *chan,
>> + struct scatterlist *dst_sg, unsigned int dst_nents,
>> + struct scatterlist *src_sg, unsigned int src_nents,
>> + unsigned long dma_flags)
>> +{
>> + if (dst_nents != src_nents)
>> + return -EINVAL;
>
> I suspect you wanted "return NULL;" here. I can fix that up.
>
> Linus, Per ack?
>
> --
> Dan
Thanks Dan. Acked by Per.
/Per
^ permalink raw reply
* Re: [PATCH v6 0/8] ptp: IEEE 1588 hardware clock support
From: Christian Riesch @ 2010-09-30 3:50 UTC (permalink / raw)
To: Christoph Lameter
Cc: Rodolfo Giometti, Arnd Bergmann, Peter Zijlstra, john stultz,
devicetree-discuss, linux-kernel, David Miller, netdev,
linux-arm-kernel, linux-api, Thomas Gleixner, linuxppc-dev,
Richard Cochran, Alan Cox, Krzysztof Halasa
In-Reply-To: <alpine.DEB.2.00.1009271035110.9258@router.home>
Quoting Christoph Lameter <cl@linux.com>:
> On Thu, 23 Sep 2010, Christian Riesch wrote:
>
>> > > It implies clock tuning in userspace for a potential sub microsecond
>> > > accurate clock. The clock accuracy will be limited by user space
>> > > latencies and noise. You wont be able to discipline the system clock
>> > > accurately.
>> >
>> > Noise matters, latency doesn't.
>>
>> Well put! That's why we need hardware support for PTP timestamping to reduce
>> the noise, but get along well with the clock servo that is steering
>> the PHC in
>> user space.
>
> Even if I buy into the catch phrase above: User space is subject to noise
> that the in kernel code is not. If you do the tuning over long intervals
> then it hopefully averages out but it still causes jitter effects that
> affects the degree of accuracy (or sync) that you can reach. And the noise
> varies with the load on the system.
Yes and no. If you regard it as a control system: The latencies of the
operating system are a dead time in the control system. The sampling
time is quite large, one second, maybe around 100ms or 10ms in
telecommunication applications, but that is still large compared to
the latencies you expect to have in the operating system. Hence, this
latencies (=dead time) can be neglected and the important thing that
remains is the noise that you introduce in the measurements of the
time stamps, which is therefore done in hardware.
I admit that my short statement above is not completely correct, I
should have mentioned the rather large sampling time we are dealing
with here.
Christian
^ permalink raw reply
* Re: [PATCH 3/4] fsldma: remove DMA_SLAVE support
From: Ira W. Snyder @ 2010-09-29 22:19 UTC (permalink / raw)
To: Dan Williams; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <AANLkTik4_se70GkKb2FLwsiUtQGyaHgxsLn3MQhmv5jh@mail.gmail.com>
On Wed, Sep 29, 2010 at 02:52:16PM -0700, Dan Williams wrote:
> On Mon, Sep 27, 2010 at 3:57 PM, Ira W. Snyder <iws@ovro.caltech.edu> wrote:
> > Now that the generic DMAEngine API has support for scatterlist to
> > scatterlist copying, this implementation of the DMA_SLAVE API is no
> > longer necessary.
> >
> > In order to let device_control() continue to function, a stub
> > device_prep_slave_sg() function is provided. This allows custom device
> > configuration, such as enabling external control.
> >
>
> > + case DMA_SLAVE_CONFIG:
> > +
> > + cfg = (struct fsldma_slave_config *)arg;
>
> Now that I actually see someone trying to use the recommended
> extension model it comes across as unsafe, what guarantees that arg is
> pointing to a fsldma_slave_config. At at minimum you could ensure
> that this channel has been claimed for private usage which loosely
> implies that the client knows that it is talking to an fsldma channel.
> Even safer is to just assign you a one-off dma_ctrl_cmd
> (FSLDMA_EXTERNAL_START) for this purpose. Otherwise this and the
> other patches look good.
>
I agree, it is a very unsafe model.
I'll take your suggestion, and do that instead. A new patch will be
forthcoming shortly.
Thanks,
Ira
^ permalink raw reply
* Re: [PATCH 3/4] fsldma: remove DMA_SLAVE support
From: Dan Williams @ 2010-09-29 21:52 UTC (permalink / raw)
To: Ira W. Snyder; +Cc: linuxppc-dev, linux-kernel
In-Reply-To: <1285628277-26894-4-git-send-email-iws@ovro.caltech.edu>
On Mon, Sep 27, 2010 at 3:57 PM, Ira W. Snyder <iws@ovro.caltech.edu> wrote=
:
> Now that the generic DMAEngine API has support for scatterlist to
> scatterlist copying, this implementation of the DMA_SLAVE API is no
> longer necessary.
>
> In order to let device_control() continue to function, a stub
> device_prep_slave_sg() function is provided. This allows custom device
> configuration, such as enabling external control.
>
> + =A0 =A0 =A0 case DMA_SLAVE_CONFIG:
> +
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 cfg =3D (struct fsldma_slave_config *)arg;
Now that I actually see someone trying to use the recommended
extension model it comes across as unsafe, what guarantees that arg is
pointing to a fsldma_slave_config. At at minimum you could ensure
that this channel has been claimed for private usage which loosely
implies that the client knows that it is talking to an fsldma channel.
Even safer is to just assign you a one-off dma_ctrl_cmd
(FSLDMA_EXTERNAL_START) for this purpose. Otherwise this and the
other patches look good.
--
Dan
^ permalink raw reply
* Re: Parsing a bus fault message?
From: david.hagood @ 2010-09-29 21:24 UTC (permalink / raw)
To: tiejun.chen; +Cc: Scott Wood, david.hagood, linuxppc-dev, Ira W. Snyder
In-Reply-To: <4CA2FDA3.20704@windriver.com>
> Scott Wood wrote:
> I also meet machine check exception if configure LAW improperly for PCI.
> (i.e.
> unmatched PCIe controller id.)
>
> From you log looks 0xexxxxxxx should be your PCI space. So you can check
> if that
> fall into appropriate LAW configuration. Maybe you can post your boot log
> and
> error log here.
Using EP8641A machine description
Total memory = 1024MB; using 2048kB for hash table (at cfa00000)
Linux version 2.6.26.2-ep1.10 (SRWhite@WIC-102333) (gcc version 4.0.0
(DENX ELDK 4.1 4.0.0)) #269 SMP PREEMPT Tue Sep 28 15:48:43 CDT 2010
Found initrd at 0xcfdfa000:0xcffa9663
Found legacy serial port 0 for /soc8641@e0000000/serial@4500
mem=e0004500, taddr=e0004500, irq=0, clk=500000000, speed=0
Found legacy serial port 1 for /soc8641@e0000000/serial@4600
mem=e0004600, taddr=e0004600, irq=0, clk=500000000, speed=0
CPU maps initialized for 1 thread per core
(thread shift is 0)
console [udbg0] enabled
Entering add_active_range(0, 0, 262144) 0 entries of 256 used
EP8641A board from Embedded Planet
Top of RAM: 0x40000000, Total RAM: 0x40000000
Memory hole size: 0MB
Zone PFN ranges:
DMA 0 -> 196608
Normal 196608 -> 196608
HighMem 196608 -> 262144
Movable zone start PFN for each node
early_node_map[1] active PFN ranges
0: 0 -> 262144
On node 0 totalpages: 262144
DMA zone: 1536 pages used for memmap
DMA zone: 0 pages reserved
DMA zone: 195072 pages, LIFO batch:31
Normal zone: 0 pages used for memmap
HighMem zone: 512 pages used for memmap
HighMem zone: 65024 pages, LIFO batch:15
Movable zone: 0 pages used for memmap
Built 1 zonelists in Zone order, mobility grouping on. Total pages: 260096
Kernel command line: root=/dev/ram rw console=ttyS0,115200
ip=10.200.120.158:::255.255.0.0::eth0
mtdparts=physmap-flash.0:0x1300000(linux)ro,0x6bc0000(jffs),-(rsvd)ro
mpic: Setting up MPIC " MPIC " version 1.2 at e0040000, max 2 CPUs
mpic: ISU size: 16, shift: 4, mask: f
mpic: Initializing for 80 sources
PID hash table entries: 4096 (order: 12, 16384 bytes)
time_init: decrementer frequency = 125.000000 MHz
time_init: processor frequency = 1500.000000 MHz
clocksource: timebase mult[2000000] shift[22] registered
clockevent: decrementer mult[2000] shift[16] cpu[0]
Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)
High memory: 262144k
Memory: 1031248k/1048576k available (3064k kernel code, 16476k reserved,
84k data, 149k bss, 164k init)
Calibrating delay loop... 249.85 BogoMIPS (lpj=499712)
Mount-cache hash table entries: 512
mpic: requesting IPIs ...
Processor 1 found.
Synchronizing timebase
Got ack
score 299, offset 1000
score 299, offset 500
score 299, offset 250
score 299, offset 125
score 299, offset 62
score 297, offset 31
score -299, offset 15
score 297, offset 23
score 253, offset 19
score -299, offset 17
score -269, offset 18
Min 18 (score -269), Max 19 (score 253)
Final offset: 19 (269/300)
clockevent: decrementer mult[2000] shift[16] cpu[1]
Brought up 2 CPUs
net_namespace: 208 bytes
NET: Registered protocol family 16
NET: Registered protocol family 2
IP route cache hash table entries: 32768 (order: 5, 131072 bytes)
TCP established hash table entries: 131072 (order: 8, 1048576 bytes)
TCP bind hash table entries: 65536 (order: 7, 786432 bytes)
TCP: Hash tables configured (established 131072 bind 65536)
TCP reno registered
NET: Registered protocol family 1
checking if image is initramfs...it isn't (no cpio magic); looks like an
initrd
Freeing initrd memory: 1725k freed
setup_kcore: restrict size=3fffffff
of-fsl-dma e0021300.dma: Probe the Freescale DMA driver for
fsl,eloplus-dma controller at e0021300...
of-fsl-dma-channel e0021100.dma-channe: #0 (fsl,eloplus-dma-channel), irq 20
of-fsl-dma-channel e0021180.dma-channe: #1 (fsl,eloplus-dma-channel), irq 21
of-fsl-dma-channel e0021200.dma-channe: #2 (fsl,eloplus-dma-channel), irq 22
of-fsl-dma-channel e0021280.dma-channe: #3 (fsl,eloplus-dma-channel), irq 23
Setting up RapidIO peer-to-peer network /rapidio@e00c0000
fsl-of-rio e00c0000.rapidio: Of-device full name /rapidio@e00c0000
fsl-of-rio e00c0000.rapidio: Regs start 0xe00c0000 size 0x20000
fsl-of-rio e00c0000.rapidio: LAW start 0x00000000c0000000, size
0x0000000020000000.
fsl-of-rio e00c0000.rapidio: bellirq: 50, txirq: 53, rxirq 54
fsl-of-rio e00c0000.rapidio: RapidIO PHY type: serial
fsl-of-rio e00c0000.rapidio: Hardware port width: 4
fsl-of-rio e00c0000.rapidio: Training connection status: Four-lane
fsl-of-rio e00c0000.rapidio: RapidIO Common Transport System size: 256
highmem bounce pool size: 64 pages
Installing knfsd (copyright (C) 1996 okir@monad.swb.de).
JFFS2 version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
msgmni has been set to 1507
io scheduler noop registered (default)
Generic RTC Driver v1.07
Serial: 8250/16550 driver $Revision: 1.90 $ 2 ports, IRQ sharing disabled
serial8250.0: ttyS0 at MMIO 0xe0004500 (irq = 42) is a 16550A
console handover: boot [udbg0] -> real [ttyS0]
serial8250.0: ttyS1 at MMIO 0xe0004600 (irq = 28) is a 16550A
brd: module loaded
loop: module loaded
Gianfar MII Bus: probed
eth0: Gianfar Ethernet Controller Version 1.2, 00:10:ec:01:1a:d3
eth0: Running with NAPI enabled
eth0: 256/256 RX/TX BD ring size
eth1: Gianfar Ethernet Controller Version 1.2, 00:10:ec:81:1a:d3
eth1: Running with NAPI enabled
eth1: 256/256 RX/TX BD ring size
eth2: Gianfar Ethernet Controller Version 1.2, 00:10:ec:41:1a:d3
eth2: Running with NAPI enabled
eth2: 256/256 RX/TX BD ring size
eth3: Gianfar Ethernet Controller Version 1.2, 00:10:ec:c1:1a:d3
eth3: Running with NAPI enabled
eth3: 256/256 RX/TX BD ring size
physmap platform flash device: 08000000 at f8000000
physmap-flash.0: Found 2 x16 devices at 0x0 in 32-bit bank
Amd/Fujitsu Extended Query Table at 0x0040
physmap-flash.0: CFI does not contain boot bank location. Assuming top.
number of CFI chips: 1
cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.
3 cmdlinepart partitions found on MTD device physmap-flash.0
Creating 3 MTD partitions on "physmap-flash.0":
0x00000000-0x01300000 : "linux"
0x01300000-0x07ec0000 : "jffs"
0x07ec0000-0x08000000 : "rsvd"
TCP cubic registered
NET: Registered protocol family 17
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
IP-Config: Complete:
device=eth0, addr=10.200.120.158, mask=255.255.0.0, gw=255.255.255.255,
host=10.200.120.158, domain=, nis-domain=(none),
bootserver=255.255.255.255, rootserver=255.255.255.255, rootpath=
RAMDISK: Compressed image found at block 0
VFS: Mounted root (ext2 filesystem).
Freeing unused kernel memory: 164k init
PHY: e0024520:10 - Link is Up - 100/Full
RIO: discover master port 0, RIO0 mport
rionetlink_init: receive handler registration suceeded!!!!
rionetlink_init: rio_register_driver suceeded!!!!
Besides, in my setup, there are 2 LAWS programmed to point at the PEX: one
mapping A0000000 to BFFFFFFF to the PEX, and one mapping E2000000 to
E2FFFFFF. My code directly scans the LAWS and picks the first one it sees
mapped to the PEX, so it is picking A00000000, and using that. Is there an
issue with having 2 LAWs mapping the same device to different locations?
Being unfortunately a man of many hats, I haven't had a lot of time today
to work on this particular fire. Hopefully in the next couple of days I
can get some more time to look into it more.
I do thank all of you for the pointers, and I'll look into them.
(BTW: Anybody near Wichita, and want to earn some extra $$$ helping me out?)
^ permalink raw reply
* Re: [PATCH 4/4] ste_dma40: implement support for scatterlist to scatterlist copy
From: Dan Williams @ 2010-09-29 21:19 UTC (permalink / raw)
To: Ira W. Snyder; +Cc: Per Fridén, Linus Walleij, linuxppc-dev, linux-kernel
In-Reply-To: <1285628277-26894-5-git-send-email-iws@ovro.caltech.edu>
On Mon, Sep 27, 2010 at 3:57 PM, Ira W. Snyder <iws@ovro.caltech.edu> wrote=
:
> Now that the DMAEngine API has support for scatterlist to scatterlist
> copy, implement support for the STE DMA40 DMA controller.
>
> Cc: Linus Walleij <linus.ml.walleij@gmail.com>
> Cc: Per Frid=E9n <per.friden@stericsson.com>
> Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
> ---
> =A0drivers/dma/ste_dma40.c | =A0 17 +++++++++++++++++
> =A01 files changed, 17 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c
> index 17e2600..cd48859 100644
> --- a/drivers/dma/ste_dma40.c
> +++ b/drivers/dma/ste_dma40.c
> @@ -1857,6 +1857,18 @@ err:
> =A0 =A0 =A0 =A0return NULL;
> =A0}
>
> +static struct dma_async_tx_descriptor *
> +d40_prep_sg(struct dma_chan *chan,
> + =A0 =A0 =A0 =A0 =A0 struct scatterlist *dst_sg, unsigned int dst_nents,
> + =A0 =A0 =A0 =A0 =A0 struct scatterlist *src_sg, unsigned int src_nents,
> + =A0 =A0 =A0 =A0 =A0 unsigned long dma_flags)
> +{
> + =A0 =A0 =A0 if (dst_nents !=3D src_nents)
> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 return -EINVAL;
I suspect you wanted "return NULL;" here. I can fix that up.
Linus, Per ack?
--
Dan
^ permalink raw reply
* Re: [PATCH 0/8] v2 De-Couple sysfs memory directories from memory sections
From: Robin Holt @ 2010-09-29 19:28 UTC (permalink / raw)
To: Nathan Fontenot
Cc: linux-mm, Greg KH, linux-kernel, Dave Hansen, linuxppc-dev,
Robin Holt, KAMEZAWA Hiroyuki
In-Reply-To: <4CA2313D.2030508@austin.ibm.com>
On Tue, Sep 28, 2010 at 01:17:33PM -0500, Nathan Fontenot wrote:
> On 09/28/2010 07:38 AM, Robin Holt wrote:
> > I was tasked with looking at a slowdown in similar sized SGI machines
> > booting x86_64. Jack Steiner had already looked into the memory_dev_init.
> > I was looking at link_mem_sections().
> >
> > I made a dramatic improvement on a 16TB machine in that function by
> > merely caching the most recent memory section and checking to see if
> > the next memory section happens to be the subsequent in the linked list
> > of kobjects.
> >
> > That simple cache reduced the time for link_mem_sections from 1 hour 27
> > minutes down to 46 seconds.
>
> Nice!
>
> >
> > I would like to propose we implement something along those lines also,
> > but I am currently swamped. I can probably get you a patch tomorrow
> > afternoon that applies at the end of this set.
>
> Should this be done as a separate patch? This patch set concentrates on
> updates to the memory code with the node updates only being done due to the
> memory changes.
>
> I think its a good idea to do the caching and have no problem adding on to
> this patchset if no one else has any objections.
I am sorry. I had meant to include you on the Cc: list. I just posted a
set of patches (3 small patches) which implement the cache most recent bit
I aluded to above. Search for a subject of "Speed up link_mem_sections
during boot" and you will find them. I did add you to the Cc: list for
the next time I end up sending the set.
My next task is to implement a x86_64 SGI UV specific chunk of code
to memory_block_size_bytes(). Would you consider adding that to your
patch set? I expect to have that either later today or early tomorrow.
Robin
^ permalink raw reply
* Re: [PATCH (Option 2)] of/i2c: fix module load order issue caused by of_i2c.c
From: Jean Delvare @ 2010-09-29 15:43 UTC (permalink / raw)
To: Grant Likely; +Cc: rdunlap, mikpe, linuxppc-dev, linux-kernel, linux-i2c
In-Reply-To: <20100924221458.28357.58921.stgit@angua>
On Fri, 24 Sep 2010 16:15:18 -0600, Grant Likely wrote:
> Commit 959e85f7, "i2c: add OF-style registration and binding" caused a
> module dependency loop where of_i2c.c calls functions in i2c-core, and
> i2c-core calls of_i2c_register_devices() in of_i2c. This means that
> when i2c support is built as a module when CONFIG_OF is set, then
> neither i2c_core nor of_i2c are able to be loaded.
>
> This patch fixes the problem by moving the of_i2c_register_devices()
> calls back into the device drivers. Device drivers already
> specifically request the core code to parse the device tree for
> devices anyway by setting the of_node pointer, so it isn't a big
> deal to also call the registration function. The drivers just become
> slightly more verbose.
>
> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
> ---
> drivers/i2c/busses/i2c-cpm.c | 5 +++++
> drivers/i2c/busses/i2c-ibm_iic.c | 3 +++
> drivers/i2c/busses/i2c-mpc.c | 1 +
> drivers/i2c/i2c-core.c | 4 ----
> 4 files changed, 9 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c
> index f7bd261..f2de3be 100644
> --- a/drivers/i2c/busses/i2c-cpm.c
> +++ b/drivers/i2c/busses/i2c-cpm.c
> @@ -677,6 +677,11 @@ static int __devinit cpm_i2c_probe(struct platform_device *ofdev,
> dev_dbg(&ofdev->dev, "hw routines for %s registered.\n",
> cpm->adap.name);
>
> + /*
> + * register OF I2C devices
> + */
> + of_i2c_register_devices(&cpm->adap);
> +
> return 0;
> out_shut:
> cpm_i2c_shutdown(cpm);
> diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c
> index 43ca32f..89eedf4 100644
> --- a/drivers/i2c/busses/i2c-ibm_iic.c
> +++ b/drivers/i2c/busses/i2c-ibm_iic.c
> @@ -761,6 +761,9 @@ static int __devinit iic_probe(struct platform_device *ofdev,
> dev_info(&ofdev->dev, "using %s mode\n",
> dev->fast_mode ? "fast (400 kHz)" : "standard (100 kHz)");
>
> + /* Now register all the child nodes */
> + of_i2c_register_devices(adap);
> +
> return 0;
>
> error_cleanup:
> diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
> index a1c419a..b74e6dc 100644
> --- a/drivers/i2c/busses/i2c-mpc.c
> +++ b/drivers/i2c/busses/i2c-mpc.c
> @@ -632,6 +632,7 @@ static int __devinit fsl_i2c_probe(struct platform_device *op,
> dev_err(i2c->dev, "failed to add adapter\n");
> goto fail_add;
> }
> + of_i2c_register_devices(&i2c->adap);
>
> return result;
>
> diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
> index 6649176..a9589f5 100644
> --- a/drivers/i2c/i2c-core.c
> +++ b/drivers/i2c/i2c-core.c
> @@ -32,7 +32,6 @@
> #include <linux/init.h>
> #include <linux/idr.h>
> #include <linux/mutex.h>
> -#include <linux/of_i2c.h>
> #include <linux/of_device.h>
> #include <linux/completion.h>
> #include <linux/hardirq.h>
> @@ -876,9 +875,6 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
> if (adap->nr < __i2c_first_dynamic_bus_num)
> i2c_scan_static_board_info(adap);
>
> - /* Register devices from the device tree */
> - of_i2c_register_devices(adap);
> -
> /* Notify drivers */
> mutex_lock(&core_lock);
> bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
>
I've applied this variant, thanks.
--
Jean Delvare
^ permalink raw reply
* Re: [PATCH (Option 1)] of/i2c: fix module load order issue caused by of_i2c.c
From: Jean Delvare @ 2010-09-29 14:42 UTC (permalink / raw)
To: Ben Dooks; +Cc: mikpe, linux-kernel, rdunlap, linux-i2c, linuxppc-dev
In-Reply-To: <20100928232054.GQ21564@trinity.fluff.org>
On Wed, 29 Sep 2010 00:20:54 +0100, Ben Dooks wrote:
> On Fri, Sep 24, 2010 at 04:14:53PM -0600, Grant Likely wrote:
> > Commit 959e85f7, "i2c: add OF-style registration and binding" caused a
> > module dependency loop where of_i2c.c calls functions in i2c-core, and
> > i2c-core calls of_i2c_register_devices() in of_i2c. This means that
> > when i2c support is built as a module when CONFIG_OF is set, then
> > neither i2c_core nor of_i2c are able to be loaded.
> >
> > This patch fixes the problem by moving the of_i2c_register_devices()
> > function into the body of i2c_core and renaming it to
> > i2c_scan_of_devices (of_i2c_register_devices is analogous to the
> > existing i2c_scan_static_board_info function and so should be named
> > similarly). This function isn't called by any code outside of
> > i2c_core, and it must always be present when CONFIG_OF is selected, so
> > it makes sense to locate it there. When CONFIG_OF is not selected,
> > of_i2c_register_devices() becomes a no-op.
>
> I sort of go with this one.
Actually I would prefer option #2, even though I understand it won't
make Grant too happy. Having a large chunk of OF-specific code in
i2c-core, leaving of_i2c.c almost empty, doesn't seem right.
I took a look at what other relevant subsystems do. SPI is boolean so it
doesn't have the issue. MDIO is tristate, the registration function is
in of_mdio.c and individual drivers call it. And there are a lot more
of these (9) than i2c drivers (3).
So I would let individual drivers call of_i2c_register_devices(), as it
used to be until 2.6.35. 2 extra functions calls doesn't seem a high
price to pay to keep the code logically separated. This also make
things consistent, with all OF registration functions living under
drivers/of.
Thanks,
--
Jean Delvare
^ permalink raw reply
* Re: [PATCH 0/8] v2 De-Couple sysfs memory directories from memory sections
From: Kay Sievers @ 2010-09-29 13:39 UTC (permalink / raw)
To: Greg KH
Cc: linuxppc-dev, linux-kernel, Dave Hansen, linux-mm, Avi Kivity,
KAMEZAWA Hiroyuki, Robin Holt
In-Reply-To: <20100929123752.GA18865@kroah.com>
On Wed, Sep 29, 2010 at 14:37, Greg KH <greg@kroah.com> wrote:
> On Wed, Sep 29, 2010 at 10:32:34AM +0200, Avi Kivity wrote:
>> =C2=A0On 09/29/2010 04:50 AM, Greg KH wrote:
>>> >
>>> > =C2=A0Because the old ABI creates 129,000+ entries inside
>>> > =C2=A0/sys/devices/system/memory with their associated links from
>>> > =C2=A0/sys/devices/system/node/node*/ back to those directory entries=
.
>>> >
>>> > =C2=A0Thankfully things like rpm, hald, and other miscellaneous comma=
nds scan
>>> > =C2=A0that information.
>>>
>>> Really? =C2=A0Why? =C2=A0Why would rpm care about this? =C2=A0hald is d=
ead now so we
>>> don't need to worry about that anymore,
>>
>> That's not what compatiblity means. =C2=A0We can't just support
>> latest-and-greatest userspace on latest-and-greatest kernels.
>
> Oh, I know that, that's not what I was getting at at all here, sorry if
> it came across that way.
>
> I wanted to know so we could go fix programs that are mucking around in
> these files, as odds are, the shouldn't be doing that in the first
> place.
>
> Like rpm, why would it matter what the memory in the system looks like?
HAL does many inefficient things, but I don't think it's using
/sys/system/, besides that it may check the cpufreq govenors state
there.
Kay
^ permalink raw reply
* Re: [PATCH 0/8] v2 De-Couple sysfs memory directories from memory sections
From: Greg KH @ 2010-09-29 12:37 UTC (permalink / raw)
To: Avi Kivity
Cc: linuxppc-dev, linux-kernel, Dave Hansen, linux-mm, Robin Holt,
KAMEZAWA Hiroyuki
In-Reply-To: <4CA2F9A2.3090202@redhat.com>
On Wed, Sep 29, 2010 at 10:32:34AM +0200, Avi Kivity wrote:
> On 09/29/2010 04:50 AM, Greg KH wrote:
>> >
>> > Because the old ABI creates 129,000+ entries inside
>> > /sys/devices/system/memory with their associated links from
>> > /sys/devices/system/node/node*/ back to those directory entries.
>> >
>> > Thankfully things like rpm, hald, and other miscellaneous commands scan
>> > that information.
>>
>> Really? Why? Why would rpm care about this? hald is dead now so we
>> don't need to worry about that anymore,
>
> That's not what compatiblity means. We can't just support
> latest-and-greatest userspace on latest-and-greatest kernels.
Oh, I know that, that's not what I was getting at at all here, sorry if
it came across that way.
I wanted to know so we could go fix programs that are mucking around in
these files, as odds are, the shouldn't be doing that in the first
place.
Like rpm, why would it matter what the memory in the system looks like?
thanks,
greg k-h
^ permalink raw reply
* Re: Parsing a bus fault message?
From: tiejun.chen @ 2010-09-29 8:49 UTC (permalink / raw)
To: Scott Wood; +Cc: david.hagood, linuxppc-dev, Ira W. Snyder
In-Reply-To: <20100928134554.323667e5@udp111988uds.am.freescale.net>
Scott Wood wrote:
> On Tue, 28 Sep 2010 08:31:54 -0700
> "Ira W. Snyder" <iws@ovro.caltech.edu> wrote:
>
>> On Tue, Sep 28, 2010 at 09:26:51AM -0500, david.hagood@gmail.com wrote:
>>> Alternatively, can somebody see a hint in the message that I don't know
>>> enough to pick out? At this point, my code is trying to memcpy() from the
>>> PCIe bus (mapped via the outbound ATMU) to local memory, so the fault is
>>> either a) the ATMU is not accessible b) the ATMU is accessible but not
>>> mapped (which I would have thought the ioremap call I made would have
>>> handled) or c) the chip is not able to bus master on the PCI bus.
>
> Check the LAWs, the outbound ATMU, and the PCI device's BAR. Make sure
I also meet machine check exception if configure LAW improperly for PCI. (i.e.
unmatched PCIe controller id.)
>From you log looks 0xexxxxxxx should be your PCI space. So you can check if that
fall into appropriate LAW configuration. Maybe you can post your boot log and
error log here.
> the address goes where you're expecting at each level.
>
>>> Machine check in kernel mode.
>>> Caused by (from SRR1=149030): Transfer error ack signal
>> ^^^ this is the line that contains some critical info
>>
>> In the 86xx CPU manual, you should be able to find information about the
>> SRR1 register. Decoding the hex SRR1=0x149030 may help.
Actually 'Transfer error ack signal' is the result just after kernel decode
SRR1/MSSSR0.
>>
>> The kernel is telling you this is a TEA (transfer error acknowledge)
>> error. I've only seen this when I get an unhandled timeout on the local
>> bus. For example, a FPGA that has died in the middle of a request.
>
I met this only one time when kernel access USB host controller REGs on one
mpc837x. But the same kernel is fine on another same version target. So I think
sometimes you have to check the hardware.
> I've seen it when you access a physical address that has no device
> backing it up.
>
Yes. This should be most common reason for machine check exception when we
access one address with cache inhibited.
>> On the PCI bus, I haven't seen this error. The 83xx PCI controller is
>> smart enough to return 0xffffffff when reading a non-existent device.
>
> I believe that behavior is configurable.
I know 0xfffffffff will be returned by some PCI controller when PCI controller
access non-existent device. Because PCI controller can't get any response from
that non-existed device. So PCI controller think this 'read' should be aborted
by asserting bus to one known state, 0xffffffff. But I have to admit I really am
not sure if this is configured. I prefer to this behavior should be associated
to the given PCI controller fixed feature.
Tiejun
>
> -Scott
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* Re: [PATCH 0/8] v2 De-Couple sysfs memory directories from memory sections
From: Avi Kivity @ 2010-09-29 8:32 UTC (permalink / raw)
To: Greg KH
Cc: linuxppc-dev, linux-kernel, Dave Hansen, linux-mm, Robin Holt,
KAMEZAWA Hiroyuki
In-Reply-To: <20100929025035.GA13096@kroah.com>
On 09/29/2010 04:50 AM, Greg KH wrote:
> >
> > Because the old ABI creates 129,000+ entries inside
> > /sys/devices/system/memory with their associated links from
> > /sys/devices/system/node/node*/ back to those directory entries.
> >
> > Thankfully things like rpm, hald, and other miscellaneous commands scan
> > that information.
>
> Really? Why? Why would rpm care about this? hald is dead now so we
> don't need to worry about that anymore,
That's not what compatiblity means. We can't just support
latest-and-greatest userspace on latest-and-greatest kernels.
--
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
^ permalink raw reply
* Re: [PATCH 0/8] v2 De-Couple sysfs memory directories from memory sections
From: Greg KH @ 2010-09-29 2:50 UTC (permalink / raw)
To: Robin Holt
Cc: linuxppc-dev, linux-kernel, Dave Hansen, linux-mm, Avi Kivity,
KAMEZAWA Hiroyuki
In-Reply-To: <20100928151218.GJ14068@sgi.com>
On Tue, Sep 28, 2010 at 10:12:18AM -0500, Robin Holt wrote:
> On Tue, Sep 28, 2010 at 02:44:40PM +0200, Avi Kivity wrote:
> > On 09/27/2010 09:09 PM, Nathan Fontenot wrote:
> > >This set of patches decouples the concept that a single memory
> > >section corresponds to a single directory in
> > >/sys/devices/system/memory/. On systems
> > >with large amounts of memory (1+ TB) there are perfomance issues
> > >related to creating the large number of sysfs directories. For
> > >a powerpc machine with 1 TB of memory we are creating 63,000+
> > >directories. This is resulting in boot times of around 45-50
> > >minutes for systems with 1 TB of memory and 8 hours for systems
> > >with 2 TB of memory. With this patch set applied I am now seeing
> > >boot times of 5 minutes or less.
> > >
> > >The root of this issue is in sysfs directory creation. Every time
> > >a directory is created a string compare is done against all sibling
> > >directories to ensure we do not create duplicates. The list of
> > >directory nodes in sysfs is kept as an unsorted list which results
> > >in this being an exponentially longer operation as the number of
> > >directories are created.
> > >
> > >The solution solved by this patch set is to allow a single
> > >directory in sysfs to span multiple memory sections. This is
> > >controlled by an optional architecturally defined function
> > >memory_block_size_bytes(). The default definition of this
> > >routine returns a memory block size equal to the memory section
> > >size. This maintains the current layout of sysfs memory
> > >directories as it appears to userspace to remain the same as it
> > >is today.
> > >
> >
> > Why not update sysfs directory creation to be fast, for example by
> > using an rbtree instead of a linked list. This fixes an
> > implementation problem in the kernel instead of working around it
> > and creating a new ABI.
>
> Because the old ABI creates 129,000+ entries inside
> /sys/devices/system/memory with their associated links from
> /sys/devices/system/node/node*/ back to those directory entries.
>
> Thankfully things like rpm, hald, and other miscellaneous commands scan
> that information.
Really? Why? Why would rpm care about this? hald is dead now so we
don't need to worry about that anymore, but what other commands/programs
read this information?
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH 1/1 v3] Add kernel parameter to disable batched hcalls
From: Will Schmidt @ 2010-09-29 1:33 UTC (permalink / raw)
To: Olof Johansson; +Cc: linuxppc-dev, Anton Blanchard
In-Reply-To: <20100928175206.GA15783@lixom.net>
This introduces a pair of kernel parameters that can be used to disable
the MULTITCE and BULK_REMOVE h-calls.
By default, those hcalls are enabled, active, and good for throughput
and performance. The ability to disable them will be useful for some of
the PREEMPT_RT related investigation and work occurring on Power.
Signed-off-by: Will Schmidt <will_schmidt@vnet.ibm.com>
cc: Olof Johansson <olof@lixom.net>
cc: Anton Blanchard <anton@samba.org>
cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---
v2 - Per feedback from Olof, the code is reworked to utilize kernel
parameter runtime checks, rather than CONFIG options.
- Added relevant change to kernel-parameters.txt
v3 - ran through checkpatch and fixed whitespace issues.
- consolidated the if() staircases to save indentation.
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index e2c7487..5c40801 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -426,6 +426,10 @@ and is between 256 and 4096 characters. It is defined in the file
bttv.pll= See Documentation/video4linux/bttv/Insmod-options
bttv.tuner= and Documentation/video4linux/bttv/CARDLIST
+ bulk_remove=off [PPC] This parameter disables the use of the pSeries
+ firmware feature for flushing multiple hpte entries
+ at a time.
+
BusLogic= [HW,SCSI]
See drivers/scsi/BusLogic.c, comment before function
BusLogic_ParseDriverOptions().
@@ -1499,6 +1503,10 @@ and is between 256 and 4096 characters. It is defined in the file
mtdparts= [MTD]
See drivers/mtd/cmdlinepart.c.
+ multitce=off [PPC] This parameter disables the use of the pSeries
+ firmware feature for updating multiple TCE entries
+ at a time.
+
onenand.bdry= [HW,MTD] Flex-OneNAND Boundary Configuration
Format: [die0_boundary][,die0_lock][,die1_boundary][,die1_lock]
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 902987d..e174a2f 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -625,3 +625,17 @@ void iommu_init_early_pSeries(void)
set_pci_dma_ops(&dma_iommu_ops);
}
+static int __init disable_multitce(char *str)
+{
+ if (strcmp(str, "off") == 0 &&
+ firmware_has_feature(FW_FEATURE_LPAR) &&
+ firmware_has_feature(FW_FEATURE_MULTITCE)) {
+ printk(KERN_INFO "Disabling MULTITCE firmware feature\n");
+ ppc_md.tce_build = tce_build_pSeriesLP;
+ ppc_md.tce_free = tce_free_pSeriesLP;
+ powerpc_firmware_features &= ~FW_FEATURE_MULTITCE;
+ }
+ return 1;
+}
+
+__setup("multitce=", disable_multitce);
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 0707653..82d15e7 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -599,6 +599,18 @@ static void pSeries_lpar_flush_hash_range(unsigned long number, int local)
spin_unlock_irqrestore(&pSeries_lpar_tlbie_lock, flags);
}
+static int __init disable_bulk_remove(char *str)
+{
+ if (strcmp(str, "off") == 0 &&
+ firmware_has_feature(FW_FEATURE_BULK_REMOVE)) {
+ printk(KERN_INFO "Disabling BULK_REMOVE firmware feature");
+ powerpc_firmware_features &= ~FW_FEATURE_BULK_REMOVE;
+ }
+ return 1;
+}
+
+__setup("bulk_remove=", disable_bulk_remove);
+
void __init hpte_init_lpar(void)
{
ppc_md.hpte_invalidate = pSeries_lpar_hpte_invalidate;
^ permalink raw reply related
* Re: [PATCH (Option 1)] of/i2c: fix module load order issue caused by of_i2c.c
From: Ben Dooks @ 2010-09-28 23:20 UTC (permalink / raw)
To: Grant Likely; +Cc: mikpe, linux-kernel, rdunlap, linux-i2c, khali, linuxppc-dev
In-Reply-To: <20100924221313.28357.61419.stgit@angua>
On Fri, Sep 24, 2010 at 04:14:53PM -0600, Grant Likely wrote:
> Commit 959e85f7, "i2c: add OF-style registration and binding" caused a
> module dependency loop where of_i2c.c calls functions in i2c-core, and
> i2c-core calls of_i2c_register_devices() in of_i2c. This means that
> when i2c support is built as a module when CONFIG_OF is set, then
> neither i2c_core nor of_i2c are able to be loaded.
>
> This patch fixes the problem by moving the of_i2c_register_devices()
> function into the body of i2c_core and renaming it to
> i2c_scan_of_devices (of_i2c_register_devices is analogous to the
> existing i2c_scan_static_board_info function and so should be named
> similarly). This function isn't called by any code outside of
> i2c_core, and it must always be present when CONFIG_OF is selected, so
> it makes sense to locate it there. When CONFIG_OF is not selected,
> of_i2c_register_devices() becomes a no-op.
I sort of go with this one.
--
Ben
Q: What's a light-year?
A: One-third less calories than a regular year.
^ permalink raw reply
* Re: [PATCH (Option 1)] of/i2c: fix module load order issue caused by of_i2c.c
From: Ben Dooks @ 2010-09-28 23:21 UTC (permalink / raw)
To: Grant Likely; +Cc: mikpe, linux-kernel, rdunlap, linux-i2c, khali, linuxppc-dev
In-Reply-To: <20100924221313.28357.61419.stgit@angua>
On Fri, Sep 24, 2010 at 04:14:53PM -0600, Grant Likely wrote:
> Commit 959e85f7, "i2c: add OF-style registration and binding" caused a
> module dependency loop where of_i2c.c calls functions in i2c-core, and
> i2c-core calls of_i2c_register_devices() in of_i2c. This means that
> when i2c support is built as a module when CONFIG_OF is set, then
> neither i2c_core nor of_i2c are able to be loaded.
>
> This patch fixes the problem by moving the of_i2c_register_devices()
> function into the body of i2c_core and renaming it to
> i2c_scan_of_devices (of_i2c_register_devices is analogous to the
> existing i2c_scan_static_board_info function and so should be named
> similarly). This function isn't called by any code outside of
> i2c_core, and it must always be present when CONFIG_OF is selected, so
> it makes sense to locate it there. When CONFIG_OF is not selected,
> of_i2c_register_devices() becomes a no-op.
>
> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
> ---
> drivers/i2c/i2c-core.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++--
> drivers/of/of_i2c.c | 57 ---------------------------------------------
> include/linux/of_i2c.h | 7 ------
> 3 files changed, 59 insertions(+), 66 deletions(-)
>
> diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
> index 6649176..64a261b 100644
> --- a/drivers/i2c/i2c-core.c
> +++ b/drivers/i2c/i2c-core.c
> @@ -32,8 +32,8 @@
> #include <linux/init.h>
> #include <linux/idr.h>
> #include <linux/mutex.h>
> -#include <linux/of_i2c.h>
> #include <linux/of_device.h>
> +#include <linux/of_irq.h>
> #include <linux/completion.h>
> #include <linux/hardirq.h>
> #include <linux/irqflags.h>
> @@ -818,6 +818,63 @@ static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
> up_read(&__i2c_board_lock);
> }
>
> +#ifdef CONFIG_OF
> +void i2c_scan_of_devices(struct i2c_adapter *adap)
> +{
> + void *result;
> + struct device_node *node;
> +
> + /* Only register child devices if the adapter has a node pointer set */
> + if (!adap->dev.of_node)
> + return;
> +
> + for_each_child_of_node(adap->dev.of_node, node) {
> + struct i2c_board_info info = {};
> + struct dev_archdata dev_ad = {};
> + const __be32 *addr;
> + int len;
> +
> + dev_dbg(&adap->dev, "of_i2c: register %s\n", node->full_name);
> + if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
> + dev_err(&adap->dev, "of_i2c: modalias failure on %s\n",
> + node->full_name);
> + continue;
> + }
> +
> + addr = of_get_property(node, "reg", &len);
> + if (!addr || (len < sizeof(int))) {
> + dev_err(&adap->dev, "of_i2c: invalid reg on %s\n",
> + node->full_name);
> + continue;
> + }
> +
> + info.addr = be32_to_cpup(addr);
> + if (info.addr > (1 << 10) - 1) {
> + dev_err(&adap->dev, "of_i2c: invalid addr=%x on %s\n",
> + info.addr, node->full_name);
> + continue;
> + }
> +
> + info.irq = irq_of_parse_and_map(node, 0);
> + info.of_node = of_node_get(node);
> + info.archdata = &dev_ad;
> +
> + request_module("%s", info.type);
> +
> + result = i2c_new_device(adap, &info);
> + if (result == NULL) {
> + dev_err(&adap->dev, "of_i2c: Failure registering %s\n",
> + node->full_name);
> + of_node_put(node);
> + irq_dispose_mapping(info.irq);
> + continue;
> + }
> + }
> +}
> +#else
> +static inline void i2c_scan_of_devices(struct i2c_adapter *adap) { }
> +#endif
Is there any advantage to just making the definition in the header
file a static inline and removing the #else part of this?
--
Ben
Q: What's a light-year?
A: One-third less calories than a regular year.
^ permalink raw reply
* Re: [PATCH (Option 1)] of/i2c: fix module load order issue caused by of_i2c.c
From: Grant Likely @ 2010-09-28 23:26 UTC (permalink / raw)
To: Ben Dooks; +Cc: mikpe, linux-kernel, rdunlap, linux-i2c, khali, linuxppc-dev
In-Reply-To: <20100928232147.GR21564@trinity.fluff.org>
On Wed, Sep 29, 2010 at 8:21 AM, Ben Dooks <ben-i2c@fluff.org> wrote:
> On Fri, Sep 24, 2010 at 04:14:53PM -0600, Grant Likely wrote:
>> Commit 959e85f7, "i2c: add OF-style registration and binding" caused a
>> module dependency loop where of_i2c.c calls functions in i2c-core, and
>> i2c-core calls of_i2c_register_devices() in of_i2c. =A0This means that
>> when i2c support is built as a module when CONFIG_OF is set, then
>> neither i2c_core nor of_i2c are able to be loaded.
>>
>> This patch fixes the problem by moving the of_i2c_register_devices()
>> function into the body of i2c_core and renaming it to
>> i2c_scan_of_devices (of_i2c_register_devices is analogous to the
>> existing i2c_scan_static_board_info function and so should be named
>> similarly). =A0This function isn't called by any code outside of
>> i2c_core, and it must always be present when CONFIG_OF is selected, so
>> it makes sense to locate it there. =A0When CONFIG_OF is not selected,
>> of_i2c_register_devices() becomes a no-op.
>>
>> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
>> ---
>> =A0drivers/i2c/i2c-core.c | =A0 61 +++++++++++++++++++++++++++++++++++++=
+++++++++--
>> =A0drivers/of/of_i2c.c =A0 =A0| =A0 57 ---------------------------------=
------------
>> =A0include/linux/of_i2c.h | =A0 =A07 ------
>> =A03 files changed, 59 insertions(+), 66 deletions(-)
>>
>> diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
>> index 6649176..64a261b 100644
>> --- a/drivers/i2c/i2c-core.c
>> +++ b/drivers/i2c/i2c-core.c
>> @@ -32,8 +32,8 @@
>> =A0#include <linux/init.h>
>> =A0#include <linux/idr.h>
>> =A0#include <linux/mutex.h>
>> -#include <linux/of_i2c.h>
>> =A0#include <linux/of_device.h>
>> +#include <linux/of_irq.h>
>> =A0#include <linux/completion.h>
>> =A0#include <linux/hardirq.h>
>> =A0#include <linux/irqflags.h>
>> @@ -818,6 +818,63 @@ static void i2c_scan_static_board_info(struct i2c_a=
dapter *adapter)
>> =A0 =A0 =A0 up_read(&__i2c_board_lock);
>> =A0}
>>
>> +#ifdef CONFIG_OF
>> +void i2c_scan_of_devices(struct i2c_adapter *adap)
>> +{
>> + =A0 =A0 void *result;
>> + =A0 =A0 struct device_node *node;
>> +
>> + =A0 =A0 /* Only register child devices if the adapter has a node point=
er set */
>> + =A0 =A0 if (!adap->dev.of_node)
>> + =A0 =A0 =A0 =A0 =A0 =A0 return;
>> +
>> + =A0 =A0 for_each_child_of_node(adap->dev.of_node, node) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 struct i2c_board_info info =3D {};
>> + =A0 =A0 =A0 =A0 =A0 =A0 struct dev_archdata dev_ad =3D {};
>> + =A0 =A0 =A0 =A0 =A0 =A0 const __be32 *addr;
>> + =A0 =A0 =A0 =A0 =A0 =A0 int len;
>> +
>> + =A0 =A0 =A0 =A0 =A0 =A0 dev_dbg(&adap->dev, "of_i2c: register %s\n", n=
ode->full_name);
>> + =A0 =A0 =A0 =A0 =A0 =A0 if (of_modalias_node(node, info.type, sizeof(i=
nfo.type)) < 0) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(&adap->dev, "of_i2c: m=
odalias failure on %s\n",
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 node->full_nam=
e);
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue;
>> + =A0 =A0 =A0 =A0 =A0 =A0 }
>> +
>> + =A0 =A0 =A0 =A0 =A0 =A0 addr =3D of_get_property(node, "reg", &len);
>> + =A0 =A0 =A0 =A0 =A0 =A0 if (!addr || (len < sizeof(int))) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(&adap->dev, "of_i2c: i=
nvalid reg on %s\n",
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 node->full_nam=
e);
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue;
>> + =A0 =A0 =A0 =A0 =A0 =A0 }
>> +
>> + =A0 =A0 =A0 =A0 =A0 =A0 info.addr =3D be32_to_cpup(addr);
>> + =A0 =A0 =A0 =A0 =A0 =A0 if (info.addr > (1 << 10) - 1) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(&adap->dev, "of_i2c: i=
nvalid addr=3D%x on %s\n",
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 info.addr, nod=
e->full_name);
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue;
>> + =A0 =A0 =A0 =A0 =A0 =A0 }
>> +
>> + =A0 =A0 =A0 =A0 =A0 =A0 info.irq =3D irq_of_parse_and_map(node, 0);
>> + =A0 =A0 =A0 =A0 =A0 =A0 info.of_node =3D of_node_get(node);
>> + =A0 =A0 =A0 =A0 =A0 =A0 info.archdata =3D &dev_ad;
>> +
>> + =A0 =A0 =A0 =A0 =A0 =A0 request_module("%s", info.type);
>> +
>> + =A0 =A0 =A0 =A0 =A0 =A0 result =3D i2c_new_device(adap, &info);
>> + =A0 =A0 =A0 =A0 =A0 =A0 if (result =3D=3D NULL) {
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 dev_err(&adap->dev, "of_i2c: F=
ailure registering %s\n",
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 node->full_nam=
e);
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 of_node_put(node);
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 irq_dispose_mapping(info.irq);
>> + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 continue;
>> + =A0 =A0 =A0 =A0 =A0 =A0 }
>> + =A0 =A0 }
>> +}
>> +#else
>> +static inline void i2c_scan_of_devices(struct i2c_adapter *adap) { }
>> +#endif
>
> Is there any advantage to just making the definition in the header
> file a static inline and removing the #else part of this?
I'm not sure what you mean. This particular patch makes
i2c_scan_of_devices() completely internal to i2c-core.c so that there
is no need to expose it in the header file at all.
g.
>
> --
> Ben
>
> Q: =A0 =A0 =A0What's a light-year?
> A: =A0 =A0 =A0One-third less calories than a regular year.
>
>
--=20
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
^ permalink raw reply
* [PATCH v3 2/2] USB: add USB EHCI support for MPC5121 SoC
From: Anatolij Gustschin @ 2010-09-28 18:55 UTC (permalink / raw)
To: linux-usb
Cc: David Brownell, Wolfgang Denk, Detlev Zundel, Greg Kroah-Hartman,
linuxppc-dev, Anatolij Gustschin
In-Reply-To: <1285666594-21150-1-git-send-email-agust@denx.de>
Extends FSL EHCI platform driver glue layer to support
MPC5121 USB controllers. MPC5121 Rev 2.0 silicon EHCI
registers are in big endian format. The appropriate flags
are set using the information in the platform data structure.
MPC83xx system interface registers are not available on
MPC512x, so the access to these registers is isolated in
MPC512x case. Furthermore the USB controller clocks
must be enabled before 512x register accesses which is
done by providing platform specific init callback.
The MPC512x internal USB PHY doesn't provide supply voltage.
For boards using different power switches allow specifying
DRVVBUS and PWR_FAULT signal polarity of the MPC5121 internal
PHY using "fsl,invert-drvvbus" and "fsl,invert-pwr-fault"
properties in the device tree USB nodes. Adds documentation
for this new device tree bindings.
Signed-off-by: Anatolij Gustschin <agust@denx.de>
Cc: Grant Likely <grant.likely@secretlab.ca>
---
v3:
- rebased after changing of_platform_driver to platform_driver
Documentation/powerpc/dts-bindings/fsl/usb.txt | 22 +++++
drivers/usb/Kconfig | 1 +
drivers/usb/host/Kconfig | 6 +-
drivers/usb/host/ehci-fsl.c | 99 +++++++++++++++++-------
drivers/usb/host/ehci-fsl.h | 13 +++-
drivers/usb/host/ehci-mem.c | 2 +-
drivers/usb/host/fsl-mph-dr-of.c | 89 +++++++++++++++++++++
include/linux/fsl_devices.h | 15 ++++
8 files changed, 215 insertions(+), 32 deletions(-)
diff --git a/Documentation/powerpc/dts-bindings/fsl/usb.txt b/Documentation/powerpc/dts-bindings/fsl/usb.txt
index b001524..bd5723f 100644
--- a/Documentation/powerpc/dts-bindings/fsl/usb.txt
+++ b/Documentation/powerpc/dts-bindings/fsl/usb.txt
@@ -8,6 +8,7 @@ and additions :
Required properties :
- compatible : Should be "fsl-usb2-mph" for multi port host USB
controllers, or "fsl-usb2-dr" for dual role USB controllers
+ or "fsl,mpc5121-usb2-dr" for dual role USB controllers of MPC5121
- phy_type : For multi port host USB controllers, should be one of
"ulpi", or "serial". For dual role USB controllers, should be
one of "ulpi", "utmi", "utmi_wide", or "serial".
@@ -33,6 +34,12 @@ Recommended properties :
- interrupt-parent : the phandle for the interrupt controller that
services interrupts for this device.
+Optional properties :
+ - fsl,invert-drvvbus : boolean; for MPC5121 USB0 only. Indicates the
+ port power polarity of internal PHY signal DRVVBUS is inverted.
+ - fsl,invert-pwr-fault : boolean; for MPC5121 USB0 only. Indicates
+ the PWR_FAULT signal polarity is inverted.
+
Example multi port host USB controller device node :
usb@22000 {
compatible = "fsl-usb2-mph";
@@ -57,3 +64,18 @@ Example dual role USB controller device node :
dr_mode = "otg";
phy = "ulpi";
};
+
+Example dual role USB controller device node for MPC5121ADS:
+
+ usb@4000 {
+ compatible = "fsl,mpc5121-usb2-dr";
+ reg = <0x4000 0x1000>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-parent = < &ipic >;
+ interrupts = <44 0x8>;
+ dr_mode = "otg";
+ phy_type = "utmi_wide";
+ fsl,invert-drvvbus;
+ fsl,invert-pwr-fault;
+ };
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 4aa00e6..67eb377 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -59,6 +59,7 @@ config USB_ARCH_HAS_OHCI
config USB_ARCH_HAS_EHCI
boolean
default y if PPC_83xx
+ default y if PPC_MPC512x
default y if SOC_AU1200
default y if ARCH_IXP4XX
default y if ARCH_W90X900
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index f3a90b0..bf2e7d2 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -93,12 +93,14 @@ config USB_EHCI_TT_NEWSCHED
config USB_EHCI_BIG_ENDIAN_MMIO
bool
- depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX)
+ depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || ARCH_IXP4XX || \
+ XPS_USB_HCD_XILINX || PPC_MPC512x)
default y
config USB_EHCI_BIG_ENDIAN_DESC
bool
- depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX)
+ depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \
+ PPC_MPC512x)
default y
config XPS_USB_HCD_XILINX
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 8600317..86e4289 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -116,13 +116,33 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
goto err3;
}
- /* Enable USB controller */
- temp = in_be32(hcd->regs + 0x500);
- out_be32(hcd->regs + 0x500, temp | 0x4);
+ pdata->regs = hcd->regs;
- /* Set to Host mode */
- temp = in_le32(hcd->regs + 0x1a8);
- out_le32(hcd->regs + 0x1a8, temp | 0x3);
+ /*
+ * do platform specific init: check the clock, grab/config pins, etc.
+ */
+ if (pdata->init && pdata->init(pdev)) {
+ retval = -ENODEV;
+ goto err3;
+ }
+
+ /*
+ * Check if it is MPC5121 SoC, otherwise set pdata->have_sysif_regs
+ * flag for 83xx or 8536 system interface registers.
+ */
+ if (pdata->big_endian_mmio)
+ temp = in_be32(hcd->regs + FSL_SOC_USB_ID);
+ else
+ temp = in_le32(hcd->regs + FSL_SOC_USB_ID);
+
+ if ((temp & ID_MSK) != (~((temp & NID_MSK) >> 8) & ID_MSK))
+ pdata->have_sysif_regs = 1;
+
+ /* Enable USB controller, 83xx or 8536 */
+ if (pdata->have_sysif_regs)
+ setbits32(hcd->regs + FSL_SOC_USB_CTRL, 0x4);
+
+ /* Don't need to set host mode here. It will be done by tdi_reset() */
retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
if (retval != 0)
@@ -137,6 +157,8 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
usb_put_hcd(hcd);
err1:
dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
+ if (pdata->exit)
+ pdata->exit(pdev);
return retval;
}
@@ -154,17 +176,30 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
struct platform_device *pdev)
{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
usb_remove_hcd(hcd);
+
+ /*
+ * do platform specific un-initialization:
+ * release iomux pins, disable clock, etc.
+ */
+ if (pdata->exit)
+ pdata->exit(pdev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
-static void mpc83xx_setup_phy(struct ehci_hcd *ehci,
- enum fsl_usb2_phy_modes phy_mode,
- unsigned int port_offset)
+static void ehci_fsl_setup_phy(struct ehci_hcd *ehci,
+ enum fsl_usb2_phy_modes phy_mode,
+ unsigned int port_offset)
{
- u32 portsc = 0;
+ u32 portsc;
+
+ portsc = ehci_readl(ehci, &ehci->regs->port_status[port_offset]);
+ portsc &= ~(PORT_PTS_MSK | PORT_PTS_PTW);
+
switch (phy_mode) {
case FSL_USB2_PHY_ULPI:
portsc |= PORT_PTS_ULPI;
@@ -184,20 +219,21 @@ static void mpc83xx_setup_phy(struct ehci_hcd *ehci,
ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]);
}
-static void mpc83xx_usb_setup(struct usb_hcd *hcd)
+static void ehci_fsl_usb_setup(struct ehci_hcd *ehci)
{
- struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ struct usb_hcd *hcd = ehci_to_hcd(ehci);
struct fsl_usb2_platform_data *pdata;
void __iomem *non_ehci = hcd->regs;
u32 temp;
- pdata =
- (struct fsl_usb2_platform_data *)hcd->self.controller->
- platform_data;
+ pdata = hcd->self.controller->platform_data;
+
/* Enable PHY interface in the control reg. */
- temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
- out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
- out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
+ if (pdata->have_sysif_regs) {
+ temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
+ out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
+ out_be32(non_ehci + FSL_SOC_USB_SNOOP1, 0x0000001b);
+ }
#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
/*
@@ -214,7 +250,7 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd)
if ((pdata->operating_mode == FSL_USB2_DR_HOST) ||
(pdata->operating_mode == FSL_USB2_DR_OTG))
- mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
+ ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0);
if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
unsigned int chip, rev, svr;
@@ -228,25 +264,27 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd)
ehci->has_fsl_port_bug = 1;
if (pdata->port_enables & FSL_USB2_PORT0_ENABLED)
- mpc83xx_setup_phy(ehci, pdata->phy_mode, 0);
+ ehci_fsl_setup_phy(ehci, pdata->phy_mode, 0);
if (pdata->port_enables & FSL_USB2_PORT1_ENABLED)
- mpc83xx_setup_phy(ehci, pdata->phy_mode, 1);
+ ehci_fsl_setup_phy(ehci, pdata->phy_mode, 1);
}
+ if (pdata->have_sysif_regs) {
#ifdef CONFIG_PPC_85xx
- out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
- out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
+ out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
+ out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
#else
- out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
- out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
+ out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
+ out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
#endif
- out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
+ out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
+ }
}
/* called after powerup, by probe or system-pm "wakeup" */
static int ehci_fsl_reinit(struct ehci_hcd *ehci)
{
- mpc83xx_usb_setup(ehci_to_hcd(ehci));
+ ehci_fsl_usb_setup(ehci);
ehci_port_power(ehci, 0);
return 0;
@@ -257,6 +295,11 @@ static int ehci_fsl_setup(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int retval;
+ struct fsl_usb2_platform_data *pdata;
+
+ pdata = hcd->self.controller->platform_data;
+ ehci->big_endian_desc = pdata->big_endian_desc;
+ ehci->big_endian_mmio = pdata->big_endian_mmio;
/* EHCI registers start at offset 0x100 */
ehci->caps = hcd->regs + 0x100;
@@ -370,7 +413,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
* generic hardware linkage
*/
.irq = ehci_irq,
- .flags = HCD_USB2,
+ .flags = HCD_USB2 | HCD_MEMORY,
/*
* basic lifecycle operations
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
index eb537aa..2c83537 100644
--- a/drivers/usb/host/ehci-fsl.h
+++ b/drivers/usb/host/ehci-fsl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2005 freescale semiconductor
+/* Copyright (C) 2005-2010 Freescale Semiconductor, Inc.
* Copyright (c) 2005 MontaVista Software
*
* This program is free software; you can redistribute it and/or modify it
@@ -19,6 +19,9 @@
#define _EHCI_FSL_H
/* offsets for the non-ehci registers in the FSL SOC USB controller */
+#define FSL_SOC_USB_ID 0x0
+#define ID_MSK 0x3f
+#define NID_MSK 0x3f00
#define FSL_SOC_USB_ULPIVP 0x170
#define FSL_SOC_USB_PORTSC1 0x184
#define PORT_PTS_MSK (3<<30)
@@ -27,6 +30,14 @@
#define PORT_PTS_SERIAL (3<<30)
#define PORT_PTS_PTW (1<<28)
#define FSL_SOC_USB_PORTSC2 0x188
+
+#define FSL_SOC_USB_USBGENCTRL 0x200
+#define USBGENCTRL_PPP (1 << 3)
+#define USBGENCTRL_PFP (1 << 2)
+#define FSL_SOC_USB_ISIPHYCTRL 0x204
+#define ISIPHYCTRL_PXE (1)
+#define ISIPHYCTRL_PHYE (1 << 4)
+
#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */
#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */
#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index 1f3f01e..d36e4e7 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -40,7 +40,7 @@ static inline void ehci_qtd_init(struct ehci_hcd *ehci, struct ehci_qtd *qtd,
{
memset (qtd, 0, sizeof *qtd);
qtd->qtd_dma = dma;
- qtd->hw_token = cpu_to_le32 (QTD_STS_HALT);
+ qtd->hw_token = cpu_to_hc32(ehci, QTD_STS_HALT);
qtd->hw_next = EHCI_LIST_END(ehci);
qtd->hw_alt_next = EHCI_LIST_END(ehci);
INIT_LIST_HEAD (&qtd->qtd_list);
diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c
index 12db5d5..574b99e 100644
--- a/drivers/usb/host/fsl-mph-dr-of.c
+++ b/drivers/usb/host/fsl-mph-dr-of.c
@@ -15,6 +15,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of_platform.h>
+#include <linux/clk.h>
struct fsl_usb2_dev_data {
char *dr_mode; /* controller mode */
@@ -153,6 +154,12 @@ static int __devinit fsl_usb2_mph_dr_of_probe(struct platform_device *ofdev)
pdata->operating_mode = FSL_USB2_MPH_HOST;
} else {
+ if (of_get_property(np, "fsl,invert-drvvbus", NULL))
+ pdata->invert_drvvbus = 1;
+
+ if (of_get_property(np, "fsl,invert-pwr-fault", NULL))
+ pdata->invert_pwr_fault = 1;
+
/* setup mode selected in the device tree */
pdata->operating_mode = dev_data->op_mode;
}
@@ -186,9 +193,91 @@ static int __devexit fsl_usb2_mph_dr_of_remove(struct platform_device *ofdev)
return 0;
}
+#ifdef CONFIG_PPC_MPC512x
+
+#define USBGENCTRL 0x200 /* NOTE: big endian */
+#define GC_WU_INT_CLR (1 << 5) /* Wakeup int clear */
+#define GC_ULPI_SEL (1 << 4) /* ULPI i/f select (usb0 only)*/
+#define GC_PPP (1 << 3) /* Inv. Port Power Polarity */
+#define GC_PFP (1 << 2) /* Inv. Power Fault Polarity */
+#define GC_WU_ULPI_EN (1 << 1) /* Wakeup on ULPI event */
+#define GC_WU_IE (1 << 1) /* Wakeup interrupt enable */
+
+#define ISIPHYCTRL 0x204 /* NOTE: big endian */
+#define PHYCTRL_PHYE (1 << 4) /* On-chip UTMI PHY enable */
+#define PHYCTRL_BSENH (1 << 3) /* Bit Stuff Enable High */
+#define PHYCTRL_BSEN (1 << 2) /* Bit Stuff Enable */
+#define PHYCTRL_LSFE (1 << 1) /* Line State Filter Enable */
+#define PHYCTRL_PXE (1 << 0) /* PHY oscillator enable */
+
+int fsl_usb2_mpc5121_init(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+ struct clk *clk;
+ char clk_name[10];
+ int base, clk_num;
+
+ base = pdev->resource->start & 0xf000;
+ if (base == 0x3000)
+ clk_num = 1;
+ else if (base == 0x4000)
+ clk_num = 2;
+ else
+ return -ENODEV;
+
+ snprintf(clk_name, sizeof(clk_name), "usb%d_clk", clk_num);
+ clk = clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get clk\n");
+ return PTR_ERR(clk);
+ }
+
+ clk_enable(clk);
+ pdata->clk = clk;
+
+ if (pdata->phy_mode == FSL_USB2_PHY_UTMI_WIDE) {
+ u32 reg = 0;
+
+ if (pdata->invert_drvvbus)
+ reg |= GC_PPP;
+
+ if (pdata->invert_pwr_fault)
+ reg |= GC_PFP;
+
+ out_be32(pdata->regs + ISIPHYCTRL, PHYCTRL_PHYE | PHYCTRL_PXE);
+ out_be32(pdata->regs + USBGENCTRL, reg);
+ }
+ return 0;
+}
+
+static void fsl_usb2_mpc5121_exit(struct platform_device *pdev)
+{
+ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
+
+ pdata->regs = NULL;
+
+ if (pdata->clk) {
+ clk_disable(pdata->clk);
+ clk_put(pdata->clk);
+ }
+}
+
+struct fsl_usb2_platform_data fsl_usb2_mpc5121_pd = {
+ .big_endian_desc = 1,
+ .big_endian_mmio = 1,
+ .es = 1,
+ .le_setup_buf = 1,
+ .init = fsl_usb2_mpc5121_init,
+ .exit = fsl_usb2_mpc5121_exit,
+};
+#endif /* CONFIG_PPC_MPC512x */
+
static const struct of_device_id fsl_usb2_mph_dr_of_match[] = {
{ .compatible = "fsl-usb2-mph", },
{ .compatible = "fsl-usb2-dr", },
+#ifdef CONFIG_PPC_MPC512x
+ { .compatible = "fsl,mpc5121-usb2-dr", .data = &fsl_usb2_mpc5121_pd, },
+#endif
{},
};
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index 28e33fe..d5f9a74 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -58,11 +58,26 @@ enum fsl_usb2_phy_modes {
FSL_USB2_PHY_SERIAL,
};
+struct clk;
+struct platform_device;
+
struct fsl_usb2_platform_data {
/* board specific information */
enum fsl_usb2_operating_modes operating_mode;
enum fsl_usb2_phy_modes phy_mode;
unsigned int port_enables;
+
+ int (*init)(struct platform_device *);
+ void (*exit)(struct platform_device *);
+ void __iomem *regs; /* ioremap'd register base */
+ struct clk *clk;
+ unsigned big_endian_mmio:1;
+ unsigned big_endian_desc:1;
+ unsigned es:1; /* need USBMODE:ES */
+ unsigned le_setup_buf:1;
+ unsigned have_sysif_regs:1;
+ unsigned invert_drvvbus:1;
+ unsigned invert_pwr_fault:1;
};
/* Flags in fsl_usb2_mph_platform_data */
--
1.7.0.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox