2009-10-12 16:49:22

by Anton Vorontsov

[permalink] [raw]
Subject: [RESEND][PATCH 0/8] spi_mpc8xxx: Add support for DMA transfers

Hello Kumar,

There are Acks from Greg KH and David Brownell to pass the whole
patchset via PowerPC tree. Can you please apply it?

Thanks,

--
Anton Vorontsov
email: [email protected]
irc://irc.freenode.net/bd2


2009-10-12 16:51:09

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 1/8] powerpc/cpm: Remove SPI defines and spi structs

When cpm2.h included into spi_mpc8xxx driver, the SPI defines
in the header conflict with defines in the driver.

We don't need them in the header file, so remove them. Plus
remove "struct spi", we'll use a better version in the driver.

Signed-off-by: Anton Vorontsov <[email protected]>
---
arch/powerpc/include/asm/cpm1.h | 45 ---------------------------------------
arch/powerpc/include/asm/cpm2.h | 39 ---------------------------------
2 files changed, 0 insertions(+), 84 deletions(-)

diff --git a/arch/powerpc/include/asm/cpm1.h b/arch/powerpc/include/asm/cpm1.h
index 7685ffd..81b0119 100644
--- a/arch/powerpc/include/asm/cpm1.h
+++ b/arch/powerpc/include/asm/cpm1.h
@@ -478,51 +478,6 @@ typedef struct iic {
char res2[2]; /* Reserved */
} iic_t;

-/* SPI parameter RAM.
-*/
-typedef struct spi {
- ushort spi_rbase; /* Rx Buffer descriptor base address */
- ushort spi_tbase; /* Tx Buffer descriptor base address */
- u_char spi_rfcr; /* Rx function code */
- u_char spi_tfcr; /* Tx function code */
- ushort spi_mrblr; /* Max receive buffer length */
- uint spi_rstate; /* Internal */
- uint spi_rdp; /* Internal */
- ushort spi_rbptr; /* Internal */
- ushort spi_rbc; /* Internal */
- uint spi_rxtmp; /* Internal */
- uint spi_tstate; /* Internal */
- uint spi_tdp; /* Internal */
- ushort spi_tbptr; /* Internal */
- ushort spi_tbc; /* Internal */
- uint spi_txtmp; /* Internal */
- uint spi_res;
- ushort spi_rpbase; /* Relocation pointer */
- ushort spi_res2;
-} spi_t;
-
-/* SPI Mode register.
-*/
-#define SPMODE_LOOP ((ushort)0x4000) /* Loopback */
-#define SPMODE_CI ((ushort)0x2000) /* Clock Invert */
-#define SPMODE_CP ((ushort)0x1000) /* Clock Phase */
-#define SPMODE_DIV16 ((ushort)0x0800) /* BRG/16 mode */
-#define SPMODE_REV ((ushort)0x0400) /* Reversed Data */
-#define SPMODE_MSTR ((ushort)0x0200) /* SPI Master */
-#define SPMODE_EN ((ushort)0x0100) /* Enable */
-#define SPMODE_LENMSK ((ushort)0x00f0) /* character length */
-#define SPMODE_LEN4 ((ushort)0x0030) /* 4 bits per char */
-#define SPMODE_LEN8 ((ushort)0x0070) /* 8 bits per char */
-#define SPMODE_LEN16 ((ushort)0x00f0) /* 16 bits per char */
-#define SPMODE_PMMSK ((ushort)0x000f) /* prescale modulus */
-
-/* SPIE fields */
-#define SPIE_MME 0x20
-#define SPIE_TXE 0x10
-#define SPIE_BSY 0x04
-#define SPIE_TXB 0x02
-#define SPIE_RXB 0x01
-
/*
* RISC Controller Configuration Register definitons
*/
diff --git a/arch/powerpc/include/asm/cpm2.h b/arch/powerpc/include/asm/cpm2.h
index 990ff19..236cfa3 100644
--- a/arch/powerpc/include/asm/cpm2.h
+++ b/arch/powerpc/include/asm/cpm2.h
@@ -654,45 +654,6 @@ typedef struct iic {
uint iic_txtmp; /* Internal */
} iic_t;

-/* SPI parameter RAM.
-*/
-typedef struct spi {
- ushort spi_rbase; /* Rx Buffer descriptor base address */
- ushort spi_tbase; /* Tx Buffer descriptor base address */
- u_char spi_rfcr; /* Rx function code */
- u_char spi_tfcr; /* Tx function code */
- ushort spi_mrblr; /* Max receive buffer length */
- uint spi_rstate; /* Internal */
- uint spi_rdp; /* Internal */
- ushort spi_rbptr; /* Internal */
- ushort spi_rbc; /* Internal */
- uint spi_rxtmp; /* Internal */
- uint spi_tstate; /* Internal */
- uint spi_tdp; /* Internal */
- ushort spi_tbptr; /* Internal */
- ushort spi_tbc; /* Internal */
- uint spi_txtmp; /* Internal */
- uint spi_res; /* Tx temp. */
- uint spi_res1[4]; /* SDMA temp. */
-} spi_t;
-
-/* SPI Mode register.
-*/
-#define SPMODE_LOOP ((ushort)0x4000) /* Loopback */
-#define SPMODE_CI ((ushort)0x2000) /* Clock Invert */
-#define SPMODE_CP ((ushort)0x1000) /* Clock Phase */
-#define SPMODE_DIV16 ((ushort)0x0800) /* BRG/16 mode */
-#define SPMODE_REV ((ushort)0x0400) /* Reversed Data */
-#define SPMODE_MSTR ((ushort)0x0200) /* SPI Master */
-#define SPMODE_EN ((ushort)0x0100) /* Enable */
-#define SPMODE_LENMSK ((ushort)0x00f0) /* character length */
-#define SPMODE_PMMSK ((ushort)0x000f) /* prescale modulus */
-
-#define SPMODE_LEN(x) ((((x)-1)&0xF)<<4)
-#define SPMODE_PM(x) ((x) &0xF)
-
-#define SPI_EB ((u_char)0x10) /* big endian byte order */
-
/* IDMA parameter RAM
*/
typedef struct idma {
--
1.6.3.3

2009-10-12 16:50:08

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 2/8] powerpc/qe&cpm2: Avoid redefinitions in CPM2 and QE headers

struct mcc defined in both immap_qe.h and immap_cpm2.h, so they will
conflic when included in a single file. The mcc struct is easy to deal
with, since it isn't used in any driver (yet), so let's just rename QE
version to qe_mcc.

The ucb_ctlr is a bit trickier, since it is used by fsl_qe_udc driver,
and the driver supports both CPM and QE UDCs, plus the QE version is
used to form a bigger immap struct.

I don't want to touch too much of USB code in this series, so for now
let's just copy most generic version into the common cpm.h header,
later we'll create cpm_usb.h where we'll place common USB structs that
are used by QE/CPM UDC and QE Host drivers (FHCI).

And as for the structs in qe.h and cpm2.h, just prefix them with qe_
and cpm_.

Signed-off-by: Anton Vorontsov <[email protected]>
---
arch/powerpc/include/asm/cpm.h | 22 ++++++++++++++++++++++
arch/powerpc/include/asm/immap_cpm2.h | 2 +-
arch/powerpc/include/asm/immap_qe.h | 8 ++++----
3 files changed, 27 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/include/asm/cpm.h b/arch/powerpc/include/asm/cpm.h
index 24d79e3..b5f1534 100644
--- a/arch/powerpc/include/asm/cpm.h
+++ b/arch/powerpc/include/asm/cpm.h
@@ -5,6 +5,28 @@
#include <linux/types.h>
#include <linux/of.h>

+/*
+ * USB Controller pram common to QE and CPM.
+ */
+struct usb_ctlr {
+ u8 usb_usmod;
+ u8 usb_usadr;
+ u8 usb_uscom;
+ u8 res1[1];
+ __be16 usb_usep[4];
+ u8 res2[4];
+ __be16 usb_usber;
+ u8 res3[2];
+ __be16 usb_usbmr;
+ u8 res4[1];
+ u8 usb_usbs;
+ /* Fields down below are QE-only */
+ __be16 usb_ussft;
+ u8 res5[2];
+ __be16 usb_usfrn;
+ u8 res6[0x22];
+} __attribute__ ((packed));
+
/* Opcodes common to CPM1 and CPM2
*/
#define CPM_CR_INIT_TRX ((ushort)0x0000)
diff --git a/arch/powerpc/include/asm/immap_cpm2.h b/arch/powerpc/include/asm/immap_cpm2.h
index d4f069b..7c64fda 100644
--- a/arch/powerpc/include/asm/immap_cpm2.h
+++ b/arch/powerpc/include/asm/immap_cpm2.h
@@ -549,7 +549,7 @@ typedef struct comm_proc {

/* USB Controller.
*/
-typedef struct usb_ctlr {
+typedef struct cpm_usb_ctlr {
u8 usb_usmod;
u8 usb_usadr;
u8 usb_uscom;
diff --git a/arch/powerpc/include/asm/immap_qe.h b/arch/powerpc/include/asm/immap_qe.h
index c346d0b..4e10f50 100644
--- a/arch/powerpc/include/asm/immap_qe.h
+++ b/arch/powerpc/include/asm/immap_qe.h
@@ -210,7 +210,7 @@ struct sir {
} __attribute__ ((packed));

/* USB Controller */
-struct usb_ctlr {
+struct qe_usb_ctlr {
u8 usb_usmod;
u8 usb_usadr;
u8 usb_uscom;
@@ -229,7 +229,7 @@ struct usb_ctlr {
} __attribute__ ((packed));

/* MCC */
-struct mcc {
+struct qe_mcc {
__be32 mcce; /* MCC event register */
__be32 mccm; /* MCC mask register */
__be32 mccf; /* MCC configuration register */
@@ -431,9 +431,9 @@ struct qe_immap {
struct qe_mux qmx; /* QE Multiplexer */
struct qe_timers qet; /* QE Timers */
struct spi spi[0x2]; /* spi */
- struct mcc mcc; /* mcc */
+ struct qe_mcc mcc; /* mcc */
struct qe_brg brg; /* brg */
- struct usb_ctlr usb; /* USB */
+ struct qe_usb_ctlr usb; /* USB */
struct si1 si1; /* SI */
u8 res11[0x800];
struct sir sir; /* SI Routing Tables */
--
1.6.3.3

2009-10-12 16:49:56

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 3/8] powerpc/cpm: Move CPMFCR_* defines into cpm.h

The bits are generic to CPM devices, so let's move them to the
common header file, so drivers won't need to privately reintroduce
another bunch of the same bits (as we can't include cpm2.h header
together with cpm1.h).

Signed-off-by: Anton Vorontsov <[email protected]>
---
arch/powerpc/include/asm/cpm.h | 16 ++++++++++++++++
arch/powerpc/include/asm/cpm2.h | 8 --------
2 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/arch/powerpc/include/asm/cpm.h b/arch/powerpc/include/asm/cpm.h
index b5f1534..ea3fdb9 100644
--- a/arch/powerpc/include/asm/cpm.h
+++ b/arch/powerpc/include/asm/cpm.h
@@ -27,6 +27,22 @@ struct usb_ctlr {
u8 res6[0x22];
} __attribute__ ((packed));

+/*
+ * Function code bits, usually generic to devices.
+ */
+#ifdef CONFIG_CPM1
+#define CPMFCR_GBL ((u_char)0x00) /* Flag doesn't exist in CPM1 */
+#define CPMFCR_TC2 ((u_char)0x00) /* Flag doesn't exist in CPM1 */
+#define CPMFCR_DTB ((u_char)0x00) /* Flag doesn't exist in CPM1 */
+#define CPMFCR_BDB ((u_char)0x00) /* Flag doesn't exist in CPM1 */
+#else
+#define CPMFCR_GBL ((u_char)0x20) /* Set memory snooping */
+#define CPMFCR_TC2 ((u_char)0x04) /* Transfer code 2 value */
+#define CPMFCR_DTB ((u_char)0x02) /* Use local bus for data when set */
+#define CPMFCR_BDB ((u_char)0x01) /* Use local bus for BD when set */
+#endif
+#define CPMFCR_EB ((u_char)0x10) /* Set big endian byte order */
+
/* Opcodes common to CPM1 and CPM2
*/
#define CPM_CR_INIT_TRX ((ushort)0x0000)
diff --git a/arch/powerpc/include/asm/cpm2.h b/arch/powerpc/include/asm/cpm2.h
index 236cfa3..f42e9ba 100644
--- a/arch/powerpc/include/asm/cpm2.h
+++ b/arch/powerpc/include/asm/cpm2.h
@@ -124,14 +124,6 @@ static inline void cpm2_fastbrg(uint brg, uint rate, int div16)
__cpm2_setbrg(brg, rate, CPM2_BRG_INT_CLK, div16, CPM_BRG_EXTC_INT);
}

-/* Function code bits, usually generic to devices.
-*/
-#define CPMFCR_GBL ((u_char)0x20) /* Set memory snooping */
-#define CPMFCR_EB ((u_char)0x10) /* Set big endian byte order */
-#define CPMFCR_TC2 ((u_char)0x04) /* Transfer code 2 value */
-#define CPMFCR_DTB ((u_char)0x02) /* Use local bus for data when set */
-#define CPMFCR_BDB ((u_char)0x01) /* Use local bus for BD when set */
-
/* Parameter RAM offsets from the base.
*/
#define PROFF_SCC1 ((uint)0x8000)
--
1.6.3.3

2009-10-12 16:50:11

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 4/8] powerpc/qe&cpm: Implement static inline stubs for non-QE/CPM builds

This is needed to avoid ugly #ifdefs in drivers. Also update fsl_qe_udc
driver so that now it doesn't define its own versions that cause build
breakage when the generic stubs are used.

Signed-off-by: Anton Vorontsov <[email protected]>
Acked-by: Greg Kroah-Hartman <[email protected]>
---
arch/powerpc/include/asm/cpm.h | 44 +++++++++++++++++++++++++++++++++++++++
arch/powerpc/include/asm/qe.h | 11 ++++++++-
drivers/usb/gadget/fsl_qe_udc.h | 15 -------------
3 files changed, 54 insertions(+), 16 deletions(-)

diff --git a/arch/powerpc/include/asm/cpm.h b/arch/powerpc/include/asm/cpm.h
index ea3fdb9..0835eb9 100644
--- a/arch/powerpc/include/asm/cpm.h
+++ b/arch/powerpc/include/asm/cpm.h
@@ -3,6 +3,7 @@

#include <linux/compiler.h>
#include <linux/types.h>
+#include <linux/errno.h>
#include <linux/of.h>

/*
@@ -131,13 +132,56 @@ typedef struct cpm_buf_desc {
#define BD_I2C_START (0x0400)

int cpm_muram_init(void);
+
+#if defined(CONFIG_CPM) || defined(CONFIG_QUICC_ENGINE)
unsigned long cpm_muram_alloc(unsigned long size, unsigned long align);
int cpm_muram_free(unsigned long offset);
unsigned long cpm_muram_alloc_fixed(unsigned long offset, unsigned long size);
void __iomem *cpm_muram_addr(unsigned long offset);
unsigned long cpm_muram_offset(void __iomem *addr);
dma_addr_t cpm_muram_dma(void __iomem *addr);
+#else
+static inline unsigned long cpm_muram_alloc(unsigned long size,
+ unsigned long align)
+{
+ return -ENOSYS;
+}
+
+static inline int cpm_muram_free(unsigned long offset)
+{
+ return -ENOSYS;
+}
+
+static inline unsigned long cpm_muram_alloc_fixed(unsigned long offset,
+ unsigned long size)
+{
+ return -ENOSYS;
+}
+
+static inline void __iomem *cpm_muram_addr(unsigned long offset)
+{
+ return NULL;
+}
+
+static inline unsigned long cpm_muram_offset(void __iomem *addr)
+{
+ return -ENOSYS;
+}
+
+static inline dma_addr_t cpm_muram_dma(void __iomem *addr)
+{
+ return 0;
+}
+#endif /* defined(CONFIG_CPM) || defined(CONFIG_QUICC_ENGINE) */
+
+#ifdef CONFIG_CPM
int cpm_command(u32 command, u8 opcode);
+#else
+static inline int cpm_command(u32 command, u8 opcode)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_CPM */

int cpm2_gpiochip_add32(struct device_node *np);

diff --git a/arch/powerpc/include/asm/qe.h b/arch/powerpc/include/asm/qe.h
index f388f0a..d013d7e 100644
--- a/arch/powerpc/include/asm/qe.h
+++ b/arch/powerpc/include/asm/qe.h
@@ -145,8 +145,17 @@ static inline void qe_pin_set_gpio(struct qe_pin *qe_pin) {}
static inline void qe_pin_set_dedicated(struct qe_pin *pin) {}
#endif /* CONFIG_QE_GPIO */

-/* QE internal API */
+#ifdef CONFIG_QUICC_ENGINE
int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol, u32 cmd_input);
+#else
+static inline int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol,
+ u32 cmd_input)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_QUICC_ENGINE */
+
+/* QE internal API */
enum qe_clock qe_clock_source(const char *source);
unsigned int qe_get_brg_clk(void);
int qe_setbrg(enum qe_clock brg, unsigned int rate, unsigned int multiplier);
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
index 31b2710..bea5b82 100644
--- a/drivers/usb/gadget/fsl_qe_udc.h
+++ b/drivers/usb/gadget/fsl_qe_udc.h
@@ -419,19 +419,4 @@ struct qe_udc {
#define CPM_USB_RESTART_TX_OPCODE 0x0b
#define CPM_USB_EP_SHIFT 5

-#ifndef CONFIG_CPM
-inline int cpm_command(u32 command, u8 opcode)
-{
- return -EOPNOTSUPP;
-}
-#endif
-
-#ifndef CONFIG_QUICC_ENGINE
-inline int qe_issue_cmd(u32 cmd, u32 device, u8 mcn_protocol,
- u32 cmd_input)
-{
- return -EOPNOTSUPP;
-}
-#endif
-
#endif /* __FSL_QE_UDC_H */
--
1.6.3.3

2009-10-12 16:50:05

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 5/8] spi_mpc8xxx: Fix uninitialized variable

This patch fixes the following warning:

CC drivers/spi/spi_mpc8xxx.o
spi_mpc8xxx.c: In function 'of_mpc8xxx_spi_probe':
spi_mpc8xxx.c:681: warning: 'ret' may be used uninitialized in this function

Signed-off-by: Anton Vorontsov <[email protected]>
Acked-by: David Brownell <[email protected]>
---
drivers/spi/spi_mpc8xxx.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 0fd0ec4..518671b 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -709,6 +709,7 @@ static int of_mpc8xxx_spi_get_chipselects(struct device *dev)
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;
}

--
1.6.3.3

2009-10-12 16:50:17

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 6/8] spi_mpc8xxx: Factor out SPI mode change steps into a call

We'll add more steps soon, so get rid of the duplication.

Signed-off-by: Anton Vorontsov <[email protected]>
Acked-by: David Brownell <[email protected]>
---
drivers/spi/spi_mpc8xxx.c | 56 +++++++++++++++++++-------------------------
1 files changed, 24 insertions(+), 32 deletions(-)

diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 518671b..4b119ea 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -155,6 +155,26 @@ 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);
+ 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);
@@ -168,27 +188,13 @@ static void mpc8xxx_spi_chipselect(struct spi_device *spi, int value)
}

if (value == BITBANG_CS_ACTIVE) {
- u32 regval = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
-
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;

- if (cs->hw_mode != regval) {
- unsigned long flags;
- __be32 __iomem *mode = &mpc8xxx_spi->base->mode;
-
- regval = cs->hw_mode;
- /* 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, regval & ~SPMODE_ENABLE);
- mpc8xxx_spi_write_reg(mode, regval);
- local_irq_restore(flags);
- }
+ mpc8xxx_spi_change_mode(spi);
+
if (pdata->cs_control)
pdata->cs_control(spi, pol);
}
@@ -198,7 +204,6 @@ static
int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct mpc8xxx_spi *mpc8xxx_spi;
- u32 regval;
u8 bits_per_word, pm;
u32 hz;
struct spi_mpc8xxx_cs *cs = spi->controller_state;
@@ -286,21 +291,8 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
pm--;

cs->hw_mode |= SPMODE_PM(pm);
- regval = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->mode);
- if (cs->hw_mode != regval) {
- unsigned long flags;
- __be32 __iomem *mode = &mpc8xxx_spi->base->mode;
-
- regval = cs->hw_mode;
- /* 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, regval & ~SPMODE_ENABLE);
- mpc8xxx_spi_write_reg(mode, regval);
- local_irq_restore(flags);
- }
+
+ mpc8xxx_spi_change_mode(spi);
return 0;
}

--
1.6.3.3

2009-10-12 16:50:43

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 7/8] spi_mpc8xxx: Turn qe_mode into flags

Soon there will be more flags introduced in subsequent patches, so
let's turn qe_mode into flags.

Also introduce mpc8xxx_spi_strmode() and print current SPI mode.

Signed-off-by: Anton Vorontsov <[email protected]>
Acked-by: David Brownell <[email protected]>
---
drivers/spi/spi_mpc8xxx.c | 30 +++++++++++++++++++-----------
include/linux/fsl_devices.h | 2 +-
2 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 4b119ea..80374df 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -96,7 +96,8 @@ 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 */

- bool qe_mode;
+ unsigned int flags;
+#define SPI_QE_CPU_MODE (1 << 0) /* QE CPU ("PIO") mode */

struct workqueue_struct *workqueue;
struct work_struct work;
@@ -235,14 +236,14 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
if (bits_per_word <= 8) {
cs->get_rx = mpc8xxx_spi_rx_buf_u8;
cs->get_tx = mpc8xxx_spi_tx_buf_u8;
- if (mpc8xxx_spi->qe_mode) {
+ 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->qe_mode) {
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
cs->rx_shift = 16;
cs->tx_shift = 16;
}
@@ -252,7 +253,8 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
} else
return -EINVAL;

- if (mpc8xxx_spi->qe_mode && spi->mode & SPI_LSB_FIRST) {
+ 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;
@@ -518,6 +520,13 @@ static void mpc8xxx_spi_cleanup(struct spi_device *spi)
kfree(spi->controller_state);
}

+static const char *mpc8xxx_spi_strmode(unsigned int flags)
+{
+ if (flags & SPI_QE_CPU_MODE)
+ return "QE CPU";
+ return "CPU";
+}
+
static struct spi_master * __devinit
mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
{
@@ -544,14 +553,14 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
master->cleanup = mpc8xxx_spi_cleanup;

mpc8xxx_spi = spi_master_get_devdata(master);
- mpc8xxx_spi->qe_mode = pdata->qe_mode;
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->rx_shift = 0;
mpc8xxx_spi->tx_shift = 0;
- if (mpc8xxx_spi->qe_mode) {
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE) {
mpc8xxx_spi->rx_shift = 16;
mpc8xxx_spi->tx_shift = 24;
}
@@ -584,7 +593,7 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)

/* Enable SPI interface */
regval = pdata->initial_spmode | SPMODE_INIT_VAL | SPMODE_ENABLE;
- if (pdata->qe_mode)
+ if (mpc8xxx_spi->flags & SPI_QE_CPU_MODE)
regval |= SPMODE_OP;

mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mode, regval);
@@ -604,9 +613,8 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
if (ret < 0)
goto unreg_master;

- printk(KERN_INFO
- "%s: MPC8xxx SPI Controller driver at 0x%p (irq = %d)\n",
- dev_name(dev), mpc8xxx_spi->base, mpc8xxx_spi->irq);
+ 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;

@@ -797,7 +805,7 @@ static int __devinit of_mpc8xxx_spi_probe(struct of_device *ofdev,

prop = of_get_property(np, "mode", NULL);
if (prop && !strcmp(prop, "cpu-qe"))
- pdata->qe_mode = 1;
+ pdata->flags = SPI_QE_CPU_MODE;

ret = of_mpc8xxx_spi_get_chipselects(dev);
if (ret)
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index 43fc95d..39fd946 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -74,7 +74,7 @@ struct spi_device;
struct fsl_spi_platform_data {
u32 initial_spmode; /* initial SPMODE value */
s16 bus_num;
- bool qe_mode;
+ unsigned int flags;
/* board specific information */
u16 max_chipselect;
void (*cs_control)(struct spi_device *spi, bool on);
--
1.6.3.3

2009-10-12 16:50:44

by Anton Vorontsov

[permalink] [raw]
Subject: [PATCH 8/8] spi_mpc8xxx: Add support for QE DMA mode and CPM1/CPM2 chips

This patch adds QE buffer descriptors mode support for the
spi_mpc8xxx driver, and as a side effect we now support CPM1
and CPM2 SPI controllers.

That means that today we support almost all MPC SPI controllers:

- MPC834x-style controllers (support PIO mode only);
- CPM1 and CPM2 controllers (support DMA mode only);
- QE SPI controllers in CPU mode (PIO mode with shift quirks);
- QE SPI controllers in buffer descriptors (DMA) mode;

The only controller we don't currently support is a newer eSPI
(with a dedicated chip selects and a bit different registers map).

Signed-off-by: Anton Vorontsov <[email protected]>
Acked-by: David Brownell <[email protected]>
---
drivers/spi/Kconfig | 3 -
drivers/spi/spi_mpc8xxx.c | 540 +++++++++++++++++++++++++++++++++++++++++----
2 files changed, 500 insertions(+), 43 deletions(-)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4b6f7cb..94058c6 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -147,9 +147,6 @@ config SPI_MPC8xxx
This enables using the Freescale MPC8xxx SPI controllers in master
mode.

- This driver uses a simple set of shift registers for data (opposed
- to the CPM based descriptor model).
-
config SPI_OMAP_UWIRE
tristate "OMAP1 MicroWire"
depends on ARCH_OMAP1
diff --git a/drivers/spi/spi_mpc8xxx.c b/drivers/spi/spi_mpc8xxx.c
index 80374df..394b658 100644
--- a/drivers/spi/spi_mpc8xxx.c
+++ b/drivers/spi/spi_mpc8xxx.c
@@ -5,6 +5,10 @@
*
* Copyright (C) 2006 Polycom, Inc.
*
+ * CPM SPI and QE buffer descriptors mode support:
+ * Copyright (c) 2009 MontaVista Software, Inc.
+ * Author: Anton Vorontsov <[email protected]>
+ *
* 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
@@ -27,6 +31,9 @@
#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>
@@ -34,8 +41,19 @@
#include <linux/of_spi.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];
@@ -47,6 +65,28 @@ struct mpc8xxx_spi_reg {
__be32 receive;
};

+/* SPI Parameter RAM */
+struct spi_pram {
+ __be16 rbase; /* Rx Buffer descriptor base address */
+ __be16 tbase; /* Tx Buffer descriptor base address */
+ u8 rfcr; /* Rx function code */
+ u8 tfcr; /* Tx function code */
+ __be16 mrblr; /* Max receive buffer length */
+ __be32 rstate; /* Internal */
+ __be32 rdp; /* Internal */
+ __be16 rbptr; /* Internal */
+ __be16 rbc; /* Internal */
+ __be32 rxtmp; /* Internal */
+ __be32 tstate; /* Internal */
+ __be32 tdp; /* Internal */
+ __be16 tbptr; /* Internal */
+ __be16 tbc; /* Internal */
+ __be32 txtmp; /* Internal */
+ __be32 res; /* Tx temp. */
+ __be16 rpbase; /* Relocation pointer (CPM1 only) */
+ __be16 res1; /* Reserved */
+};
+
/* SPI Controller mode register definitions */
#define SPMODE_LOOP (1 << 30)
#define SPMODE_CI_INACTIVEHIGH (1 << 29)
@@ -75,14 +115,40 @@ struct mpc8xxx_spi_reg {
#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 *);
@@ -98,6 +164,10 @@ struct mpc8xxx_spi {

unsigned int flags;
#define SPI_QE_CPU_MODE (1 << 0) /* QE CPU ("PIO") mode */
+#define SPI_CPM_MODE (1 << 1) /* CPM/QE ("DMA") mode */
+#define SPI_CPM1 (1 << 2) /* SPI unit is in CPM1 block */
+#define SPI_CPM2 (1 << 3) /* SPI unit is in CPM2 block */
+#define SPI_QE (1 << 4) /* SPI unit is in QE block */

struct workqueue_struct *workqueue;
struct work_struct work;
@@ -108,6 +178,10 @@ struct mpc8xxx_spi {
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 *);
@@ -173,6 +247,22 @@ static void mpc8xxx_spi_change_mode(struct spi_device *spi)
mpc8xxx_spi_write_reg(mode, cs->hw_mode & ~SPMODE_ENABLE);
mpc8xxx_spi_write_reg(mode, cs->hw_mode);

+ /* 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));
+ }
+ }
+ }
+
local_irq_restore(flags);
}

@@ -298,19 +388,133 @@ int mpc8xxx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
return 0;
}

-static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
+static void mpc8xxx_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi)
{
- struct mpc8xxx_spi *mpc8xxx_spi;
- u32 word, len, bits_per_word;
+ 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;

- mpc8xxx_spi = spi_master_get_devdata(spi->master);
+ 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 {
+ 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 {
+ 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_tx_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;

- mpc8xxx_spi->tx = t->tx_buf;
- mpc8xxx_spi->rx = t->rx_buf;
bits_per_word = spi->bits_per_word;
if (t->bits_per_word)
bits_per_word = t->bits_per_word;
- len = t->len;
+
if (bits_per_word > 8) {
/* invalid length? */
if (len & 1)
@@ -323,22 +527,27 @@ static int mpc8xxx_spi_bufs(struct spi_device *spi, struct spi_transfer *t)
return -EINVAL;
len /= 2;
}
- mpc8xxx_spi->count = len;

- INIT_COMPLETION(mpc8xxx_spi->done);
+ mpc8xxx_spi->tx = t->tx_buf;
+ mpc8xxx_spi->rx = t->rx_buf;

- /* enable rx ints */
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->mask, SPIM_NE);
+ INIT_COMPLETION(mpc8xxx_spi->done);

- /* transmit word */
- word = mpc8xxx_spi->get_tx(mpc8xxx_spi);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->transmit, word);
+ 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;
}

@@ -369,7 +578,7 @@ static void mpc8xxx_spi_do_one_msg(struct spi_message *m)
}
cs_change = t->cs_change;
if (t->len)
- status = mpc8xxx_spi_bufs(spi, t);
+ status = mpc8xxx_spi_bufs(spi, t, m->is_dma_mapped);
if (status) {
status = -EMSGSIZE;
break;
@@ -458,45 +667,80 @@ static int mpc8xxx_spi_setup(struct spi_device *spi)
return 0;
}

-static irqreturn_t mpc8xxx_spi_irq(s32 irq, void *context_data)
+static void mpc8xxx_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events)
{
- struct mpc8xxx_spi *mpc8xxx_spi = context_data;
- u32 event;
- irqreturn_t ret = IRQ_NONE;
+ u16 len;

- /* Get interrupt events(tx/rx) */
- event = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->event);
+ dev_dbg(mspi->dev, "%s: bd datlen %d, count %d\n", __func__,
+ in_be16(&mspi->rx_bd->cbd_datlen), mspi->count);

- /* We need handle RX first */
- if (event & SPIE_NE) {
- u32 rx_data = mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->receive);
+ len = in_be16(&mspi->rx_bd->cbd_datlen);
+ if (len > mspi->count) {
+ WARN_ON(1);
+ len = mspi->count;
+ }

- if (mpc8xxx_spi->rx)
- mpc8xxx_spi->get_rx(rx_data, mpc8xxx_spi);
+ /* Clear the events */
+ mpc8xxx_spi_write_reg(&mspi->base->event, events);

- ret = IRQ_HANDLED;
+ 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 ((event & SPIE_NF) == 0)
+ if ((events & SPIE_NF) == 0)
/* spin until TX is done */
- while (((event =
- mpc8xxx_spi_read_reg(&mpc8xxx_spi->base->event)) &
+ while (((events =
+ mpc8xxx_spi_read_reg(&mspi->base->event)) &
SPIE_NF) == 0)
cpu_relax();

- mpc8xxx_spi->count -= 1;
- if (mpc8xxx_spi->count) {
- u32 word = mpc8xxx_spi->get_tx(mpc8xxx_spi);
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->transmit, word);
+ /* 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(&mpc8xxx_spi->done);
+ complete(&mspi->done);
}
+}

- /* Clear the events */
- mpc8xxx_spi_write_reg(&mpc8xxx_spi->base->event, event);
+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)
{
@@ -520,10 +764,212 @@ 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_archdata_get_node(&dev->archdata);
+ 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_archdata_get_node(&dev->archdata);
+ 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)
+ 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";
}

@@ -553,11 +999,16 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
master->cleanup = mpc8xxx_spi_cleanup;

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) {
@@ -570,7 +1021,7 @@ mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq)
mpc8xxx_spi->base = ioremap(mem->start, mem->end - mem->start + 1);
if (mpc8xxx_spi->base == NULL) {
ret = -ENOMEM;
- goto put_master;
+ goto err_ioremap;
}

mpc8xxx_spi->irq = irq;
@@ -624,7 +1075,9 @@ free_irq:
free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
unmap_io:
iounmap(mpc8xxx_spi->base);
-put_master:
+err_ioremap:
+ mpc8xxx_spi_cpm_free(mpc8xxx_spi);
+err_cpm_init:
spi_master_put(master);
err:
return ERR_PTR(ret);
@@ -644,6 +1097,7 @@ static int __devexit mpc8xxx_spi_remove(struct device *dev)

free_irq(mpc8xxx_spi->irq, mpc8xxx_spi);
iounmap(mpc8xxx_spi->base);
+ mpc8xxx_spi_cpm_free(mpc8xxx_spi);

return 0;
}
@@ -806,6 +1260,12 @@ static int __devinit of_mpc8xxx_spi_probe(struct of_device *ofdev,
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)
--
1.6.3.3

2009-11-05 13:46:59

by Kumar Gala

[permalink] [raw]
Subject: Re: [PATCH 1/8] powerpc/cpm: Remove SPI defines and spi structs


On Oct 12, 2009, at 11:49 AM, Anton Vorontsov wrote:

> When cpm2.h included into spi_mpc8xxx driver, the SPI defines
> in the header conflict with defines in the driver.
>
> We don't need them in the header file, so remove them. Plus
> remove "struct spi", we'll use a better version in the driver.
>
> Signed-off-by: Anton Vorontsov <[email protected]>
> ---
> arch/powerpc/include/asm/cpm1.h | 45
> ---------------------------------------
> arch/powerpc/include/asm/cpm2.h | 39
> ---------------------------------
> 2 files changed, 0 insertions(+), 84 deletions(-)

applied to next

- k

2009-11-05 13:47:15

by Kumar Gala

[permalink] [raw]
Subject: Re: [PATCH 2/8] powerpc/qe&cpm2: Avoid redefinitions in CPM2 and QE headers


On Oct 12, 2009, at 11:49 AM, Anton Vorontsov wrote:

> struct mcc defined in both immap_qe.h and immap_cpm2.h, so they will
> conflic when included in a single file. The mcc struct is easy to deal
> with, since it isn't used in any driver (yet), so let's just rename QE
> version to qe_mcc.
>
> The ucb_ctlr is a bit trickier, since it is used by fsl_qe_udc driver,
> and the driver supports both CPM and QE UDCs, plus the QE version is
> used to form a bigger immap struct.
>
> I don't want to touch too much of USB code in this series, so for now
> let's just copy most generic version into the common cpm.h header,
> later we'll create cpm_usb.h where we'll place common USB structs that
> are used by QE/CPM UDC and QE Host drivers (FHCI).
>
> And as for the structs in qe.h and cpm2.h, just prefix them with qe_
> and cpm_.
>
> Signed-off-by: Anton Vorontsov <[email protected]>
> ---
> arch/powerpc/include/asm/cpm.h | 22 ++++++++++++++++++++++
> arch/powerpc/include/asm/immap_cpm2.h | 2 +-
> arch/powerpc/include/asm/immap_qe.h | 8 ++++----
> 3 files changed, 27 insertions(+), 5 deletions(-)

applied to next

- k

2009-11-05 14:40:46

by Kumar Gala

[permalink] [raw]
Subject: Re: [PATCH 3/8] powerpc/cpm: Move CPMFCR_* defines into cpm.h


On Oct 12, 2009, at 11:49 AM, Anton Vorontsov wrote:

> The bits are generic to CPM devices, so let's move them to the
> common header file, so drivers won't need to privately reintroduce
> another bunch of the same bits (as we can't include cpm2.h header
> together with cpm1.h).
>
> Signed-off-by: Anton Vorontsov <[email protected]>
> ---
> arch/powerpc/include/asm/cpm.h | 16 ++++++++++++++++
> arch/powerpc/include/asm/cpm2.h | 8 --------
> 2 files changed, 16 insertions(+), 8 deletions(-)

applied to next

- k

2009-11-05 13:47:25

by Kumar Gala

[permalink] [raw]
Subject: Re: [PATCH 4/8] powerpc/qe&cpm: Implement static inline stubs for non-QE/CPM builds


On Oct 12, 2009, at 11:49 AM, Anton Vorontsov wrote:

> This is needed to avoid ugly #ifdefs in drivers. Also update
> fsl_qe_udc
> driver so that now it doesn't define its own versions that cause build
> breakage when the generic stubs are used.
>
> Signed-off-by: Anton Vorontsov <[email protected]>
> Acked-by: Greg Kroah-Hartman <[email protected]>
> ---
> arch/powerpc/include/asm/cpm.h | 44 ++++++++++++++++++++++++++++++
> +++++++++
> arch/powerpc/include/asm/qe.h | 11 ++++++++-
> drivers/usb/gadget/fsl_qe_udc.h | 15 -------------
> 3 files changed, 54 insertions(+), 16 deletions(-)

applied to next

- k

2009-11-05 14:06:22

by Kumar Gala

[permalink] [raw]
Subject: Re: [PATCH 5/8] spi_mpc8xxx: Fix uninitialized variable


On Oct 12, 2009, at 11:49 AM, Anton Vorontsov wrote:

> This patch fixes the following warning:
>
> CC drivers/spi/spi_mpc8xxx.o
> spi_mpc8xxx.c: In function 'of_mpc8xxx_spi_probe':
> spi_mpc8xxx.c:681: warning: 'ret' may be used uninitialized in this
> function
>
> Signed-off-by: Anton Vorontsov <[email protected]>
> Acked-by: David Brownell <[email protected]>
> ---
> drivers/spi/spi_mpc8xxx.c | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)

applied to next

- k

2009-11-05 14:06:36

by Kumar Gala

[permalink] [raw]
Subject: Re: [PATCH 6/8] spi_mpc8xxx: Factor out SPI mode change steps into a call


On Oct 12, 2009, at 11:49 AM, Anton Vorontsov wrote:

> We'll add more steps soon, so get rid of the duplication.
>
> Signed-off-by: Anton Vorontsov <[email protected]>
> Acked-by: David Brownell <[email protected]>
> ---
> drivers/spi/spi_mpc8xxx.c | 56 ++++++++++++++++++
> +-------------------------
> 1 files changed, 24 insertions(+), 32 deletions(-)

applied to next

- k

2009-11-05 14:09:53

by Kumar Gala

[permalink] [raw]
Subject: Re: [PATCH 7/8] spi_mpc8xxx: Turn qe_mode into flags


On Oct 12, 2009, at 11:49 AM, Anton Vorontsov wrote:

> Soon there will be more flags introduced in subsequent patches, so
> let's turn qe_mode into flags.
>
> Also introduce mpc8xxx_spi_strmode() and print current SPI mode.
>
> Signed-off-by: Anton Vorontsov <[email protected]>
> Acked-by: David Brownell <[email protected]>
> ---
> drivers/spi/spi_mpc8xxx.c | 30 +++++++++++++++++++-----------
> include/linux/fsl_devices.h | 2 +-
> 2 files changed, 20 insertions(+), 12 deletions(-)

applied to next

- k

2009-11-05 13:53:25

by Kumar Gala

[permalink] [raw]
Subject: Re: [PATCH 8/8] spi_mpc8xxx: Add support for QE DMA mode and CPM1/CPM2 chips


On Oct 12, 2009, at 11:49 AM, Anton Vorontsov wrote:

> This patch adds QE buffer descriptors mode support for the
> spi_mpc8xxx driver, and as a side effect we now support CPM1
> and CPM2 SPI controllers.
>
> That means that today we support almost all MPC SPI controllers:
>
> - MPC834x-style controllers (support PIO mode only);
> - CPM1 and CPM2 controllers (support DMA mode only);
> - QE SPI controllers in CPU mode (PIO mode with shift quirks);
> - QE SPI controllers in buffer descriptors (DMA) mode;
>
> The only controller we don't currently support is a newer eSPI
> (with a dedicated chip selects and a bit different registers map).
>
> Signed-off-by: Anton Vorontsov <[email protected]>
> Acked-by: David Brownell <[email protected]>
> ---
> drivers/spi/Kconfig | 3 -
> drivers/spi/spi_mpc8xxx.c | 540 ++++++++++++++++++++++++++++++++++++
> +++++----
> 2 files changed, 500 insertions(+), 43 deletions(-)

applied to next

- k

2009-11-24 05:36:42

by Benjamin Herrenschmidt

[permalink] [raw]
Subject: Re: [PATCH 7/8] spi_mpc8xxx: Turn qe_mode into flags

On Thu, 2009-11-05 at 07:47 -0600, Kumar Gala wrote:
> On Oct 12, 2009, at 11:49 AM, Anton Vorontsov wrote:
>
> > Soon there will be more flags introduced in subsequent patches, so
> > let's turn qe_mode into flags.
> >
> > Also introduce mpc8xxx_spi_strmode() and print current SPI mode.
> >
> > Signed-off-by: Anton Vorontsov <[email protected]>
> > Acked-by: David Brownell <[email protected]>
> > ---
> > drivers/spi/spi_mpc8xxx.c | 30 +++++++++++++++++++-----------
> > include/linux/fsl_devices.h | 2 +-
> > 2 files changed, 20 insertions(+), 12 deletions(-)
>
> applied to next

This patch breaks my 6xx config:

/home/benh/linux-powerpc-test/arch/powerpc/platforms/83xx/mpc832x_rdb.c: In function ‘of_fsl_spi_probe’:
/home/benh/linux-powerpc-test/arch/powerpc/platforms/83xx/mpc832x_rdb.c:77: error: ‘struct fsl_spi_platform_data’ has no member named ‘qe_

The reason is that the mpc832x_rdb.c code still uses the legacy probing
method. The fix is not totally trivial as the new flags are defined inside
spi_mpc8xxx.c so either the flags need to be moved to fsl_devices.h or
mpc832x_rdb.c needs to be converted to new style stuff.

I'll commit (will be up later today in my next branch) a fix moving the
flags over for now so the tree doesn't break building.

If you are going to keep the flags in the .c file you probably also want
to remove the platform device definition from fsl_devices.h anyways as
there's no point exposing to the world a structure with a "flags" member
if the definition of those flags isn't also exposed.

Cheers,
Ben.

2009-11-24 16:37:13

by Anton Vorontsov

[permalink] [raw]
Subject: Re: [PATCH 7/8] spi_mpc8xxx: Turn qe_mode into flags

On Tue, Nov 24, 2009 at 04:03:36PM +1100, Benjamin Herrenschmidt wrote:
[...]
> This patch breaks my 6xx config:
>
> /home/benh/linux-powerpc-test/arch/powerpc/platforms/83xx/mpc832x_rdb.c: In function ‘of_fsl_spi_probe’:
> /home/benh/linux-powerpc-test/arch/powerpc/platforms/83xx/mpc832x_rdb.c:77: error: ‘struct fsl_spi_platform_data’ has no member named ‘qe_
>
> The reason is that the mpc832x_rdb.c code still uses the legacy probing
> method. The fix is not totally trivial as the new flags are defined inside
> spi_mpc8xxx.c

Thanks for noticing. I indeed forgot about the legacy stuff. :-/

> If you are going to keep the flags in the .c file you probably also want
> to remove the platform device definition from fsl_devices.h anyways as
> there's no point exposing to the world a structure with a "flags" member
> if the definition of those flags isn't also exposed.

Yep. Though, IIRC Joakim asked to keep the legacy bindings for some
time (until we implement SPI chip-select framework?)... so for now
we'll have to leave we the exposed flags.

Thanks again,

--
Anton Vorontsov
email: [email protected]
irc://irc.freenode.net/bd2