linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* PSC-SPI and MMC-SPI driver on lite5200b EVB. Device registration problems.
@ 2008-09-23 12:43 gianfranco.casanova
  2008-09-23 14:38 ` Grant Likely
  0 siblings, 1 reply; 3+ messages in thread
From: gianfranco.casanova @ 2008-09-23 12:43 UTC (permalink / raw)
  To: linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 1396 bytes --]

Hi guys

I'm using a lite5200b EVB (u-boot 1.2, kernel 2.6.26.3), using the psc-spi driver and the mmc-spi driver.

I've modified the lite5200b.dts file, I'd have SPI on PSC6 and mmc_spi connected to SPI on PSC6

first I've commented 

//		spi@f00 {
//			compatible = "fsl,mpc5200b-spi","fsl,mpc5200-spi";
//			reg = <0xf00 0x20>;
//			interrupts = <2 13 0 2 14 0>;
//			interrupt-parent = <&mpc5200_pic>;
//		};

and then uncommented

		// PSC6 in spi mode example
		spi@2c00 {		// PSC6
			compatible = "fsl,mpc5200b-psc-spi","fsl,mpc5200-psc-spi";
			cell-index = <5>;
			reg = <0x2c00 0x100>;
			interrupts = <2 4 0>;
			interrupt-parent = <&mpc5200_pic>;
			mmc_spi@0 {
				linux,modalias = "mmc_spi";
			};
		};

I've also added a node to spi@2c00 (I'm not sure about last change).

My question are:
a) Is this the right way to insert a node in device tree (in this example for mmc-spi) or there are an other way to pass the information to mmc-spi that SPI is on PSC6?

b) Why in /proc/devices I do not see any spi device (mpc5200b-psc-spi) or something like that?
Adding some printk I have:
Fun = mpc52xx_psc_spi_init Ret = 0
Is my of_register_platform_driver was ok.

c) BTW a Block device mmc in /proc/devices is added (not mmc-spi as I'm waiting), if I try to create a node and then write on device nothing happens.

Any suggestion?

Thanks J

[-- Attachment #2: Type: text/html, Size: 4056 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: PSC-SPI and MMC-SPI driver on lite5200b EVB. Device registration problems.
  2008-09-23 12:43 PSC-SPI and MMC-SPI driver on lite5200b EVB. Device registration problems gianfranco.casanova
@ 2008-09-23 14:38 ` Grant Likely
  2008-09-23 15:03   ` Jon Smirl
  0 siblings, 1 reply; 3+ messages in thread
From: Grant Likely @ 2008-09-23 14:38 UTC (permalink / raw)
  To: gianfranco.casanova; +Cc: linuxppc-dev

On Tue, Sep 23, 2008 at 02:43:18PM +0200, gianfranco.casanova@alice.it wrote:
> Hi guys
> 
> I'm using a lite5200b EVB (u-boot 1.2, kernel 2.6.26.3), using the psc-spi driver and the mmc-spi driver.
> 
> I've modified the lite5200b.dts file, I'd have SPI on PSC6 and mmc_spi connected to SPI on PSC6
> 
> and then uncommented
> 
> 		// PSC6 in spi mode example
> 		spi@2c00 {		// PSC6
> 			compatible = "fsl,mpc5200b-psc-spi","fsl,mpc5200-psc-spi";
> 			cell-index = <5>;
> 			reg = <0x2c00 0x100>;
> 			interrupts = <2 4 0>;
> 			interrupt-parent = <&mpc5200_pic>;
> 			mmc_spi@0 {
> 				linux,modalias = "mmc_spi";
> 			};
> 		};
> 
> I've also added a node to spi@2c00 (I'm not sure about last change).
> 
> My question are:
> a) Is this the right way to insert a node in device tree (in this example for mmc-spi) or there are an other way to pass the information to mmc-spi that SPI is on PSC6?

Yes, this is mostly right.  However, you need to add 2 properties to
your PSC6 spi@2c00 node; '#address-cells = <1>;' and '#size-cells = <0>;'
These are needed so that the device tree code knows how to interpret the
address of the child node.

For the mmc_spi@0 node, you need to remove the linux,modalias property.
That method of describing the device is strongly discouraged.  Instead,
add a property 'compatible = "mmc-spi";' and 'reg = <0>;'.  Compatible
is the property used to bind to the device and reg gives the device
address (which is 0 since you only have one device wired to the SPI
bus).

Unfortunately, you'll need to hack around a bit with binding code.
Support for MMC devices described in the device tree has not yet been
merged, so some of this stuff is up in the air.  However, as long as you
get the device tree description correct now you should be insulated from
changes in this area.

Take a look at this thread for more details:
http://www.mail-archive.com/linuxppc-dev@ozlabs.org/msg18836.html

> 
> b) Why in /proc/devices I do not see any spi device (mpc5200b-psc-spi) or something like that?
> Adding some printk I have:
> Fun = mpc52xx_psc_spi_init Ret = 0
> Is my of_register_platform_driver was ok.

Poke around in /sys/bus/of_platform/devices/.  See if anything shows up
there.

> 
> c) BTW a Block device mmc in /proc/devices is added (not mmc-spi as I'm waiting), if I try to create a node and then write on device nothing happens.
> 
> Any suggestion?
> 
> Thanks J

> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-dev

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: PSC-SPI and MMC-SPI driver on lite5200b EVB. Device registration problems.
  2008-09-23 14:38 ` Grant Likely
@ 2008-09-23 15:03   ` Jon Smirl
  0 siblings, 0 replies; 3+ messages in thread
From: Jon Smirl @ 2008-09-23 15:03 UTC (permalink / raw)
  To: Grant Likely; +Cc: linuxppc-dev, gianfranco.casanova

[-- Attachment #1: Type: text/plain, Size: 276 bytes --]

I have mmc working over Grant's driver for dedicated SPI.

I still have
compatible = "linux,mmc-spi";

I've tried a couple of times to remove the linux prefix but when I
remove it the driver won't load anymore and I haven't tracked down
why.

-- 
Jon Smirl
jonsmirl@gmail.com

[-- Attachment #2: m_1 --]
[-- Type: application/octet-stream, Size: 8281 bytes --]

Here is v3. I'm out of ideas if you won't like it. :-)

From: Jon Smirl <jonsmirl@gmail.com>

v3:
- Now these bindings are using bus notifiers chain, thus we adhere to the
  spi bus.

  By the way, this scheme (IMO) looks good for I2C devices which needs
  platform_data extracted from the device tree too (Cc'ing Jochen).

- Plus changed the OF bindings themselves, implemented voltage-range
  property. (Pierre, please take a look at vddrange_to_ocrmask(). I
  wonder if you would like this in the MMC core instead, with a kernel
  doc, of course.)

v2:
- Bindings were adhered to the MMC_SPI driver. Withdrawn by Pierre Ossman.

v1:
- Bindings were adhered to the OF SPI core. Withdrawn by OF people.

Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
---
 Documentation/powerpc/booting-without-of.txt |   21 +++
 drivers/of/Kconfig                           |    9 +
 drivers/of/Makefile                          |    2 
 drivers/of/of_mmc_spi.c                      |  210 ++++++++++++++++++++++++++
 4 files changed, 242 insertions(+), 0 deletions(-)
 create mode 100644 drivers/of/of_mmc_spi.c

diff --git a/Documentation/powerpc/booting-without-of.txt b/Documentation/powerpc/booting-without-of.txt
index de4063c..602cdd9 100644
--- a/Documentation/powerpc/booting-without-of.txt
+++ b/Documentation/powerpc/booting-without-of.txt
@@ -2093,7 +2093,28 @@ prefixed with the string "marvell,", for Marvell Technology Group Ltd.
 	     local-mac-address = [ 00 00 00 00 00 00 ];
      };
 
+    ...) MMC-over-SPI
 
+      Required properties:
+      - compatible : should be "mmc-spi".
+      - reg : should specify SPI address (chip-select number).
+      - max-speed : (optional) maximum frequency for this device (Hz).
+      - voltage-range : two cells are required, first cell specifies minimum
+        slot voltage (mV), second cell specifies maximum slot voltage (mV).
+      - gpios : (optional) may specify GPIOs in this order: Write-Protect GPIO,
+        Card-Detect GPIO.
+
+      Example:
+
+	mmc-slot@0 {
+		compatible = "mmc-spi";
+		reg = <0>;
+		max-speed = <50000000>;
+		/* Unregulated slot. */
+		voltage-range = <3300 3300>;
+		gpios = <&sdcsr_pio 1 0   /*  WP */
+			 &sdcsr_pio 0 1>; /* nCD */
+	};
 
    c) Marvell Discovery PHY nodes
 
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index f821dbc..f0d9fba 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -19,3 +19,12 @@ config OF_SPI
 	depends on OF && PPC_OF && SPI
 	help
 	  OpenFirmware SPI accessors
+
+config OF_MMC_SPI
+	bool "OpenFirmware bindings for MMC/SD over SPI"
+	depends on PPC_OF && SPI
+	default y if MMC_SPI
+	help
+	  Say Y here to enable OpenFirmware bindings for the MMC/SD over SPI
+	  driver.
+
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index 4c3c6f8..0757022 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -3,3 +3,5 @@ obj-$(CONFIG_OF_DEVICE) += device.o platform.o
 obj-$(CONFIG_OF_GPIO)   += gpio.o
 obj-$(CONFIG_OF_I2C)	+= of_i2c.o
 obj-$(CONFIG_OF_SPI)	+= of_spi.o
+obj-$(CONFIG_OF_MMC_SPI)	+= of_mmc_spi.o
+
diff --git a/drivers/of/of_mmc_spi.c b/drivers/of/of_mmc_spi.c
new file mode 100644
index 0000000..aa4e017
--- /dev/null
+++ b/drivers/of/of_mmc_spi.c
@@ -0,0 +1,210 @@
+/*
+ * OpenFirmware bindings for the MMC-over-SPI driver
+ *
+ * Copyright (c) MontaVista Software, Inc. 2008.
+ *
+ * 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/init.h>
+#include <linux/device.h>
+#include <linux/notifier.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
+#include <linux/mmc/host.h>
+
+/*
+ * XXX: Place it somewhere in the generic MMC code?
+ */
+static int vdd_to_bitnum(int vdd)
+{
+	int bit;
+	const int max_bit = ilog2(MMC_VDD_35_36);
+
+	if (vdd < 1650 || vdd > 3600)
+		return -EINVAL;
+
+	if (vdd >= 1650 && vdd <= 1950)
+		return ilog2(MMC_VDD_165_195);
+
+	/* base 2000 mV, step 100 mV, bit's base 8 */
+	bit = (vdd - 2000) / 100 + 8;
+	if (bit > max_bit)
+		return max_bit;
+	return bit;
+}
+
+static int vddrange_to_ocrmask(int vdd_min, int vdd_max, unsigned int *mask)
+{
+	if (vdd_max < vdd_min)
+		return -EINVAL;
+
+	vdd_max = vdd_to_bitnum(vdd_max);
+	if (vdd_max < 0)
+		return -EINVAL;
+
+	vdd_min = vdd_to_bitnum(vdd_min);
+	if (vdd_min < 0)
+		return -EINVAL;
+
+	/* fill the mask, from max bit to min bit */
+	while (vdd_max >= vdd_min)
+		*mask |= 1 << vdd_max--;
+	return 0;
+}
+
+struct of_mmc_spi {
+	int wp_gpio;
+	int cd_gpio;
+	struct mmc_spi_platform_data mmc_pdata;
+};
+
+static struct of_mmc_spi *to_of_mmc_spi(struct device *dev)
+{
+	return container_of(dev->platform_data, struct of_mmc_spi, mmc_pdata);
+}
+
+static int mmc_get_ro(struct device *dev)
+{
+	struct of_mmc_spi *oms = to_of_mmc_spi(dev);
+
+	return gpio_get_value(oms->wp_gpio);
+}
+
+static int mmc_get_cd(struct device *dev)
+{
+	struct of_mmc_spi *oms = to_of_mmc_spi(dev);
+
+	return gpio_get_value(oms->cd_gpio);
+}
+
+static int of_mmc_spi_add(struct device *dev)
+{
+	int ret = -EINVAL;
+	struct device_node *np = dev->archdata.of_node;
+	struct of_mmc_spi *oms;
+	const u32 *voltage_range;
+	int size;
+
+	if (!np || !of_device_is_compatible(np, "mmc-spi"))
+		return NOTIFY_DONE;
+
+	oms = kzalloc(sizeof(*oms), GFP_KERNEL);
+	if (!oms)
+		return notifier_to_errno(-ENOMEM);
+
+	/* We don't support interrupts yet, let's poll. */
+	oms->mmc_pdata.caps |= MMC_CAP_NEEDS_POLL;
+
+	voltage_range = of_get_property(np, "voltage-range", &size);
+	if (!voltage_range || size < sizeof(*voltage_range) * 2) {
+		dev_err(dev, "OF: voltage-range unspecified\n");
+		goto err_ocr;
+	}
+
+	ret = vddrange_to_ocrmask(voltage_range[0], voltage_range[1],
+				  &oms->mmc_pdata.ocr_mask);
+	if (ret) {
+		dev_err(dev, "OF: specified voltage-range is invalid\n");
+		goto err_ocr;
+	}
+
+	oms->wp_gpio = of_get_gpio(np, 0);
+	if (gpio_is_valid(oms->wp_gpio)) {
+		ret = gpio_request(oms->wp_gpio, dev->bus_id);
+		if (ret < 0)
+			goto err_wp_gpio;
+		oms->mmc_pdata.get_ro = &mmc_get_ro;
+	}
+
+	oms->cd_gpio = of_get_gpio(np, 1);
+	if (gpio_is_valid(oms->cd_gpio)) {
+		ret = gpio_request(oms->cd_gpio, dev->bus_id);
+		if (ret < 0)
+			goto err_cd_gpio;
+		oms->mmc_pdata.get_cd = &mmc_get_cd;
+	}
+
+	dev->platform_data = &oms->mmc_pdata;
+
+	return NOTIFY_STOP;
+
+err_cd_gpio:
+	if (gpio_is_valid(oms->wp_gpio))
+		gpio_free(oms->wp_gpio);
+err_wp_gpio:
+err_ocr:
+	kfree(oms);
+	return notifier_to_errno(ret);
+}
+
+static int of_mmc_spi_del(struct device *dev)
+{
+	struct device_node *np = dev->archdata.of_node;
+	struct of_mmc_spi *oms;
+
+	if (!np || !of_device_is_compatible(np, "mmc-spi") ||
+			!dev->platform_data)
+		return NOTIFY_DONE;
+
+	oms = to_of_mmc_spi(dev);
+
+	if (gpio_is_valid(oms->cd_gpio))
+		gpio_free(oms->cd_gpio);
+	if (gpio_is_valid(oms->wp_gpio))
+		gpio_free(oms->wp_gpio);
+
+	kfree(oms);
+	return NOTIFY_STOP;
+}
+
+static int of_mmc_spi_notify(struct notifier_block *nb, unsigned long action,
+			     void *_dev)
+{
+	struct device *dev = _dev;
+
+	switch (action) {
+	case BUS_NOTIFY_ADD_DEVICE:
+		return of_mmc_spi_add(dev);
+	case BUS_NOTIFY_DEL_DEVICE:
+		return of_mmc_spi_del(dev);
+	};
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block of_mmc_spi_notifier = {
+	.notifier_call = of_mmc_spi_notify,
+};
+
+/*
+ * Should be called early enough, but after SPI core initializes. So, we
+ * use subsys_initcall (as in the SPI core), and link order guaranties
+ * that we'll be called at the right time.
+ */
+static int __init of_mmc_spi_init(void)
+{
+	int ret;
+
+	ret = bus_register_notifier(&spi_bus_type, &of_mmc_spi_notifier);
+	if (ret) {
+		pr_err("%s: unable to register notifier on the spi bus\n",
+			__func__);
+		return ret;
+	}
+
+	return 0;
+}
+subsys_initcall(of_mmc_spi_init);
+
+MODULE_DESCRIPTION("OpenFirmware bindings for the MMC-over-SPI driver");
+MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>");
+MODULE_LICENSE("GPL");

[-- Attachment #3: g_spi_4 --]
[-- Type: application/octet-stream, Size: 19543 bytes --]

powerpc/mpc5200: Add mpc5200-spi (non-PSC) device driver

From: Grant Likely <grant.likely@secretlab.ca>

Adds support for the dedicated SPI device on the Freescale MPC5200(b)
SoC.

Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
 drivers/spi/Kconfig             |    8 +
 drivers/spi/Makefile            |    1 
 drivers/spi/mpc52xx_spi.c       |  595 +++++++++++++++++++++++++++++++++++++++
 include/linux/spi/mpc52xx_spi.h |   10 +
 4 files changed, 614 insertions(+), 0 deletions(-)
 create mode 100644 drivers/spi/mpc52xx_spi.c
 create mode 100644 include/linux/spi/mpc52xx_spi.h

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b9d0efb..223ed9f 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -116,6 +116,14 @@ config SPI_LM70_LLP
 	  which interfaces to an LM70 temperature sensor using
 	  a parallel port.
 
+config SPI_MPC52xx
+	tristate "Freescale MPC52xx SPI (non-PSC) controller support"
+	depends on PPC_MPC52xx && SPI
+	select SPI_MASTER_OF
+	help
+	  This drivers supports the MPC52xx SPI controller in master SPI
+	  mode.
+
 config SPI_MPC52xx_PSC
 	tristate "Freescale MPC52xx PSC SPI controller"
 	depends on PPC_MPC52xx && EXPERIMENTAL
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index ccf18de..113f95f 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_SPI_OMAP_UWIRE)		+= omap_uwire.o
 obj-$(CONFIG_SPI_OMAP24XX)		+= omap2_mcspi.o
 obj-$(CONFIG_SPI_ORION)			+= orion_spi.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= mpc52xx_psc_spi.o
+obj-$(CONFIG_SPI_MPC52xx)		+= mpc52xx_spi.o
 obj-$(CONFIG_SPI_MPC83xx)		+= spi_mpc83xx.o
 obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
 obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx.o
diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c
new file mode 100644
index 0000000..453690f
--- /dev/null
+++ b/drivers/spi/mpc52xx_spi.c
@@ -0,0 +1,595 @@
+/*
+ * MPC52xx SPI master driver.
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ *
+ * This is the driver for the MPC5200's dedicated SPI device (not for a
+ * PSC in SPI mode)
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/of_platform.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/mpc52xx_spi.h>
+#include <linux/of_spi.h>
+#include <linux/io.h>
+#include <asm/time.h>
+#include <asm/mpc52xx.h>
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("MPC52xx SPI (non-PSC) Driver");
+MODULE_LICENSE("GPL");
+
+/* Register offsets */
+#define SPI_CTRL1	0x00
+#define SPI_CTRL1_SPIE		(1 << 7)
+#define SPI_CTRL1_SPE		(1 << 6)
+#define SPI_CTRL1_MSTR		(1 << 4)
+#define SPI_CTRL1_CPOL		(1 << 3)
+#define SPI_CTRL1_CPHA		(1 << 2)
+#define SPI_CTRL1_SSOE		(1 << 1)
+#define SPI_CTRL1_LSBFE		(1 << 0)
+
+#define SPI_CTRL2	0x01
+#define SPI_BRR		0x04
+
+#define SPI_STATUS	0x05
+#define SPI_STATUS_SPIF		(1 << 7)
+#define SPI_STATUS_WCOL		(1 << 6)
+#define SPI_STATUS_MODF		(1 << 4)
+
+#define SPI_DATA	0x09
+#define SPI_PORTDATA	0x0d
+#define SPI_DATADIR	0x10
+
+/* FSM state return values */
+#define FSM_STOP	0
+#define FSM_POLL	1
+#define FSM_CONTINUE	2
+
+/* Driver internal data */
+struct mpc52xx_spi {
+	struct spi_master *master;
+	u32 sysclk;
+	void __iomem *regs;
+	int irq0;	/* MODF irq */
+	int irq1;	/* SPIF irq */
+	int ipb_freq;
+
+	/* Statistics */
+	int msg_count;
+	int wcol_count;
+	int wcol_ticks;
+	u32 wcol_tx_timestamp;
+	int modf_count;
+	int byte_count;
+
+	/* Hooks for platform modification of behaviour */
+	void (*premessage)(struct spi_message *m, void *context);
+	void *premessage_context;
+
+	struct list_head queue;		/* queue of pending messages */
+	spinlock_t lock;
+	struct work_struct work;
+
+
+	/* Details of current transfer (length, and buffer pointers) */
+	struct spi_message *message;	/* current message */
+	struct spi_transfer *transfer;	/* current transfer */
+	int (*state)(int irq, struct mpc52xx_spi *ms, u8 status, u8 data);
+	int len;
+	int timestamp;
+	u8 *rx_buf;
+	const u8 *tx_buf;
+	int cs_change;
+};
+
+/*
+ * CS control function
+ */
+static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value)
+{
+	if (value)
+		writeb(0, ms->regs + SPI_PORTDATA); /* Assert SS pin */
+	else
+		writeb(0x08, ms->regs + SPI_PORTDATA); /* Deassert SS pin */
+}
+
+/*
+ * Start a new transfer.  This is called both by the idle state
+ * for the first transfer in a message, and by the wait state when the
+ * previous transfer in a message is complete.
+ */
+static void mpc52xx_spi_start_transfer(struct mpc52xx_spi *ms)
+{
+	ms->rx_buf = ms->transfer->rx_buf;
+	ms->tx_buf = ms->transfer->tx_buf;
+	ms->len = ms->transfer->len;
+
+	/* Activate the chip select */
+	if (ms->cs_change)
+		mpc52xx_spi_chipsel(ms, 1);
+	ms->cs_change = ms->transfer->cs_change;
+
+	/* Write out the first byte */
+	ms->wcol_tx_timestamp = get_tbl();
+	if (ms->tx_buf)
+		writeb(*ms->tx_buf++, ms->regs + SPI_DATA);
+	else
+		writeb(0, ms->regs + SPI_DATA);
+}
+
+/* Forward declaration of state handlers */
+static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms,
+					 u8 status, u8 data);
+static int mpc52xx_spi_fsmstate_wait(int irq, struct mpc52xx_spi *ms,
+				     u8 status, u8 data);
+
+/*
+ * IDLE state
+ *
+ * No transfers are in progress; if another transfer is pending then retrieve
+ * it and kick it off.  Otherwise, stop processing the state machine
+ */
+static int
+mpc52xx_spi_fsmstate_idle(int irq, struct mpc52xx_spi *ms, u8 status, u8 data)
+{
+	struct spi_message *m;
+	struct spi_device *spi;
+	int spr, sppr;
+	u8 ctrl1;
+
+	if (status && (irq != NO_IRQ))
+		dev_err(&ms->master->dev, "spurious irq, status=0x%.2x\n",
+			status);
+
+	/* Check if there is another transfer waiting */
+	if (list_empty(&ms->queue))
+		return FSM_STOP;
+
+	/* Get the next message */
+	spin_lock(&ms->lock);
+
+	/* Call the pre-message hook with a pointer to the next
+	 * message.  The pre-message hook may enqueue a new message for
+	 * changing the chip select value to the head of the queue */
+	m = list_first_entry(&ms->queue, struct spi_message, queue);
+	if (ms->premessage)
+		ms->premessage(m, ms->premessage_context);
+
+	/* reget the head of the queue (the premessage hook may have enqueued
+	 * something before it.) and drop the spinlock */
+	ms->message = list_first_entry(&ms->queue, struct spi_message, queue);
+	list_del_init(&ms->message->queue);
+	spin_unlock(&ms->lock);
+
+	/* Setup the controller parameters */
+	ctrl1 = SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR;
+	spi = ms->message->spi;
+	if (spi->mode & SPI_CPHA)
+		ctrl1 |= SPI_CTRL1_CPHA;
+	if (spi->mode & SPI_CPOL)
+		ctrl1 |= SPI_CTRL1_CPOL;
+	if (spi->mode & SPI_LSB_FIRST)
+		ctrl1 |= SPI_CTRL1_LSBFE;
+	writeb(ctrl1, ms->regs + SPI_CTRL1);
+
+	/* Setup the controller speed */
+	/* minimum divider is '2'.  Also, add '1' to force rounding up. */
+	sppr = ((ms->ipb_freq / ms->message->spi->max_speed_hz) + 1) >> 1;
+	spr = 0;
+	if (sppr < 1)
+		sppr = 1;
+	while (((sppr - 1) & ~0x7) != 0) {
+		sppr = (sppr + 1) >> 1; /* add '1' to force rounding up */
+		spr++;
+	}
+	sppr--;		/* sppr quantity in register is offset by 1 */
+	if (spr > 7) {
+		/* Don't overrun limits of SPI baudrate register */
+		spr = 7;
+		sppr = 7;
+	}
+	writeb(sppr << 4 | spr, ms->regs + SPI_BRR); /* Set speed */
+
+	ms->cs_change = 1;
+	ms->transfer = container_of(ms->message->transfers.next,
+				    struct spi_transfer, transfer_list);
+
+	mpc52xx_spi_start_transfer(ms);
+	ms->state = mpc52xx_spi_fsmstate_transfer;
+
+#if defined(VERBOSE_DEBUG)
+	dev_info(&ms->master->dev, "msg:%p, max_speed:%i, brr:%.2x\n",
+		 ms->message, ms->message->spi->max_speed_hz,
+		 readb(ms->regs + SPI_BRR));
+#endif
+
+	return FSM_CONTINUE;
+}
+
+/*
+ * TRANSFER state
+ *
+ * In the middle of a transfer.  If the SPI core has completed processing
+ * a byte, then read out the received data and write out the next byte
+ * (unless this transfer is finished; in which case go on to the wait
+ * state)
+ */
+static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms,
+					 u8 status, u8 data)
+{
+	if (!status)
+		return ms->irq0 == NO_IRQ ? FSM_POLL : FSM_STOP;
+
+	if (status & SPI_STATUS_WCOL) {
+		/* The SPI device is stoopid.  At slower speeds, it may raise
+		 * the SPIF flag before the state machine is actually finished.
+		 * which causes a collision (internal to the state machine
+		 * only).  The manual recommends inserting a delay between
+		 * receving the interrupt and sending the next byte, but
+		 * it can also be worked around simply by retrying the
+		 * transfer which is what we do here. */
+		ms->wcol_count++;
+		ms->wcol_ticks += get_tbl() - ms->wcol_tx_timestamp;
+		ms->wcol_tx_timestamp = get_tbl();
+		data = 0;
+		if (ms->tx_buf)
+			data = *(ms->tx_buf-1);
+		writeb(data, ms->regs + SPI_DATA); /* try again */
+		return FSM_CONTINUE;
+	} else if (status & SPI_STATUS_MODF) {
+		ms->modf_count++;
+		dev_err(&ms->master->dev, "mod fault\n");
+		mpc52xx_spi_chipsel(ms, 0);
+		ms->message->status = -EIO;
+		if (ms->message->complete)
+			ms->message->complete(ms->message->context);
+		ms->state = mpc52xx_spi_fsmstate_idle;
+		return FSM_CONTINUE;
+	}
+
+	/* Read data out of the spi device */
+	ms->byte_count++;
+	if (ms->rx_buf)
+		*ms->rx_buf++ = data;
+
+	/* Is the transfer complete? */
+	ms->len--;
+	if (ms->len == 0) {
+		ms->timestamp = get_tbl();
+		ms->timestamp += ms->transfer->delay_usecs * tb_ticks_per_usec;
+		ms->state = mpc52xx_spi_fsmstate_wait;
+		return FSM_CONTINUE;
+	}
+
+	/* Write out the next byte */
+	ms->wcol_tx_timestamp = get_tbl();
+	if (ms->tx_buf)
+		writeb(*ms->tx_buf++, ms->regs + SPI_DATA);
+	else
+		writeb(0, ms->regs + SPI_DATA);
+
+	return FSM_CONTINUE;
+}
+
+/*
+ * WAIT state
+ *
+ * A transfer has completed; need to wait for the delay period to complete
+ * before starting the next transfer
+ */
+static int
+mpc52xx_spi_fsmstate_wait(int irq, struct mpc52xx_spi *ms, u8 status, u8 data)
+{
+	if (status && irq != NO_IRQ)
+		dev_err(&ms->master->dev, "spurious irq, status=0x%.2x\n",
+			status);
+
+	if (((int)get_tbl()) - ms->timestamp < 0)
+		return FSM_POLL;
+
+	ms->message->actual_length += ms->transfer->len;
+
+	/* Check if there is another transfer in this message.  If there
+	 * aren't then deactivate CS, notify sender, and drop back to idle
+	 * to start the next message. */
+	if (ms->transfer->transfer_list.next == &ms->message->transfers) {
+		ms->msg_count++;
+		mpc52xx_spi_chipsel(ms, 0);
+		ms->message->status = 0;
+		if (ms->message->complete)
+			ms->message->complete(ms->message->context);
+		ms->state = mpc52xx_spi_fsmstate_idle;
+		return FSM_CONTINUE;
+	}
+
+	/* There is another transfer; kick it off */
+
+	if (ms->cs_change)
+		mpc52xx_spi_chipsel(ms, 0);
+
+	ms->transfer = container_of(ms->transfer->transfer_list.next,
+				    struct spi_transfer, transfer_list);
+	mpc52xx_spi_start_transfer(ms);
+	ms->state = mpc52xx_spi_fsmstate_transfer;
+	return FSM_CONTINUE;
+}
+
+/*
+ * IRQ handler
+ */
+static irqreturn_t mpc52xx_spi_irq(int irq, void *_ms)
+{
+	struct mpc52xx_spi *ms = _ms;
+	int rc = FSM_CONTINUE;
+	u8 status, data;
+
+	while (rc == FSM_CONTINUE) {
+		/* Interrupt cleared by read of STATUS followed by
+		 * read of DATA registers*/
+		status = readb(ms->regs + SPI_STATUS);
+		data = readb(ms->regs + SPI_DATA); /* clear status */
+		rc = ms->state(irq, ms, status, data);
+	}
+
+	if (rc == FSM_POLL)
+		schedule_work(&ms->work);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Workqueue method of running the state machine
+ */
+static void mpc52xx_spi_wq(struct work_struct *work)
+{
+	struct mpc52xx_spi *ms = container_of(work, struct mpc52xx_spi, work);
+	mpc52xx_spi_irq(NO_IRQ, ms);
+}
+
+/*
+ * spi_master callbacks
+ */
+
+static int mpc52xx_spi_setup(struct spi_device *spi)
+{
+	return 0;
+}
+
+static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m)
+{
+	struct mpc52xx_spi *ms = spi_master_get_devdata(spi->master);
+	unsigned long flags;
+
+	m->actual_length = 0;
+	m->status = -EINPROGRESS;
+
+	spin_lock_irqsave(&ms->lock, flags);
+	list_add_tail(&m->queue, &ms->queue);
+	spin_unlock_irqrestore(&ms->lock, flags);
+	schedule_work(&ms->work);
+
+	return 0;
+}
+
+/*
+ * Hook to modify premessage hook
+ */
+void mpc52xx_spi_set_premessage_hook(struct spi_master *master,
+				     void (*hook)(struct spi_message *m,
+						  void *context),
+				     void *hook_context)
+{
+	struct mpc52xx_spi *ms = spi_master_get_devdata(master);
+	ms->premessage = hook;
+	ms->premessage_context = hook_context;
+}
+EXPORT_SYMBOL(mpc52xx_spi_set_premessage_hook);
+
+/*
+ * SysFS files
+ */
+static int
+*mpc52xx_spi_sysfs_get_counter(struct mpc52xx_spi *ms, const char *name)
+{
+	if (strcmp(name, "msg_count") == 0)
+		return &ms->msg_count;
+	if (strcmp(name, "byte_count") == 0)
+		return &ms->byte_count;
+	if (strcmp(name, "wcol_count") == 0)
+		return &ms->wcol_count;
+	if (strcmp(name, "wcol_ticks") == 0)
+		return &ms->wcol_ticks;
+	if (strcmp(name, "modf_count") == 0)
+		return &ms->modf_count;
+	return NULL;
+}
+
+static ssize_t mpc52xx_spi_show_count(struct device *dev,
+				      struct device_attribute *attr,
+				      char *buf)
+{
+	struct spi_master *master = container_of(dev, struct spi_master, dev);
+	struct mpc52xx_spi *ms = spi_master_get_devdata(master);
+	int *counter;
+
+	counter = mpc52xx_spi_sysfs_get_counter(ms, attr->attr.name);
+	if (!counter)
+		return sprintf(buf, "error\n");
+	return sprintf(buf, "%d\n", *counter);
+}
+
+static ssize_t mpc52xx_spi_set_count(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t count)
+{
+	struct spi_master *master = container_of(dev, struct spi_master, dev);
+	struct mpc52xx_spi *ms = spi_master_get_devdata(master);
+	int *counter;
+	int value = simple_strtoul(buf, NULL, 0);
+
+	counter = mpc52xx_spi_sysfs_get_counter(ms, attr->attr.name);
+	if (counter)
+		*counter = value;
+	return count;
+}
+
+DEVICE_ATTR(msg_count, 0644, mpc52xx_spi_show_count, mpc52xx_spi_set_count);
+DEVICE_ATTR(byte_count, 0644, mpc52xx_spi_show_count, mpc52xx_spi_set_count);
+DEVICE_ATTR(wcol_count, 0644, mpc52xx_spi_show_count, mpc52xx_spi_set_count);
+DEVICE_ATTR(wcol_ticks, 0644, mpc52xx_spi_show_count, mpc52xx_spi_set_count);
+DEVICE_ATTR(modf_count, 0644, mpc52xx_spi_show_count, mpc52xx_spi_set_count);
+
+/*
+ * OF Platform Bus Binding
+ */
+static int __devinit mpc52xx_spi_of_probe(struct of_device *op,
+					  const struct of_device_id *match)
+{
+	struct spi_master *master;
+	struct mpc52xx_spi *ms;
+	void __iomem *regs;
+	const u32 *prop;
+	int rc, len;
+
+	/* MMIO registers */
+	dev_dbg(&op->dev, "probing mpc5200 SPI device\n");
+	regs = of_iomap(op->node, 0);
+	if (!regs)
+		return -ENODEV;
+
+	/* initialize the device */
+	writeb(SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR, regs+SPI_CTRL1);
+	writeb(0x0, regs + SPI_CTRL2);
+	writeb(0xe, regs + SPI_DATADIR);	/* Set output pins */
+	writeb(0x8, regs + SPI_PORTDATA);	/* Deassert /SS signal */
+
+	/* Clear the status register and re-read it to check for a MODF
+	 * failure.  This driver cannot currently handle multiple masters
+	 * on the SPI bus.  This fault will also occur if the SPI signals
+	 * are not connected to any pins (port_config setting) */
+	readb(regs + SPI_STATUS);
+	readb(regs + SPI_DATA);
+	if (readb(regs + SPI_STATUS) & SPI_STATUS_MODF) {
+		dev_err(&op->dev, "mode fault; is port_config correct?\n");
+		return -EIO;
+	}
+
+	dev_dbg(&op->dev, "allocating spi_master struct\n");
+	master = spi_alloc_master(&op->dev, sizeof *ms);
+	if (!master)
+		return -ENOMEM;
+	master->bus_num = -1;
+	master->num_chipselect = 1;
+	prop = of_get_property(op->node, "num-slaves", &len);
+	if (prop && len >= sizeof(*prop))
+		master->num_chipselect = *prop;
+
+	master->setup = mpc52xx_spi_setup;
+	master->transfer = mpc52xx_spi_transfer;
+	dev_set_drvdata(&op->dev, master);
+
+	ms = spi_master_get_devdata(master);
+	ms->master = master;
+	ms->regs = regs;
+	ms->irq0 = irq_of_parse_and_map(op->node, 0);
+	ms->irq1 = irq_of_parse_and_map(op->node, 1);
+	ms->state = mpc52xx_spi_fsmstate_idle;
+	ms->ipb_freq = mpc52xx_find_ipb_freq(op->node);
+	spin_lock_init(&ms->lock);
+	INIT_LIST_HEAD(&ms->queue);
+	INIT_WORK(&ms->work, mpc52xx_spi_wq);
+
+	dev_dbg(&op->dev, "registering spi_master struct\n");
+	rc = spi_register_master(master);
+	if (rc < 0)
+		goto err_register;
+
+	/* Decide if interrupts can be used */
+	if ((ms->irq0 != NO_IRQ) && (ms->irq1 != NO_IRQ)) {
+		rc = request_irq(ms->irq0, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM,
+				  "mpc5200-spi-modf", ms);
+		rc |= request_irq(ms->irq1, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM,
+				  "mpc5200-spi-spiF", ms);
+		if (rc) {
+			free_irq(ms->irq0, ms);
+			free_irq(ms->irq1, ms);
+			ms->irq0 = ms->irq1 = NO_IRQ;
+			dev_info(&op->dev, "using polled mode\n");
+		}
+	} else {
+		/* operate in polled mode */
+		ms->irq0 = ms->irq1 = NO_IRQ;
+		dev_info(&op->dev, "using polled mode\n");
+	}
+
+	/* Create SysFS files */
+	rc = device_create_file(&ms->master->dev, &dev_attr_msg_count);
+	rc |= device_create_file(&ms->master->dev, &dev_attr_byte_count);
+	rc |= device_create_file(&ms->master->dev, &dev_attr_wcol_count);
+	rc |= device_create_file(&ms->master->dev, &dev_attr_wcol_ticks);
+	rc |= device_create_file(&ms->master->dev, &dev_attr_modf_count);
+	if (rc)
+		dev_info(&ms->master->dev, "error creating sysfs files\n");
+
+	dev_info(&ms->master->dev, "registered MPC5200 SPI bus\n");
+
+	of_register_spi_devices(master, op->node);
+
+	return rc;
+
+ err_register:
+	dev_err(&ms->master->dev, "initialization failed\n");
+	spi_master_put(master);
+	return rc;
+}
+
+static void __devexit mpc52xx_spi_of_remove(struct of_device *op)
+{
+	struct spi_master *master = dev_get_drvdata(&op->dev);
+	struct mpc52xx_spi *ms = spi_master_get_devdata(master);
+
+	device_remove_file(&ms->master->dev, &dev_attr_msg_count);
+	device_remove_file(&ms->master->dev, &dev_attr_byte_count);
+	device_remove_file(&ms->master->dev, &dev_attr_wcol_count);
+	device_remove_file(&ms->master->dev, &dev_attr_wcol_ticks);
+	device_remove_file(&ms->master->dev, &dev_attr_modf_count);
+
+	free_irq(ms->irq0, ms);
+	free_irq(ms->irq1, ms);
+
+	spi_unregister_master(master);
+	spi_master_put(master);
+	iounmap(ms->regs);
+}
+
+static struct of_device_id mpc52xx_spi_of_match[] __devinitdata = {
+	{ .compatible = "fsl,mpc5200-spi", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, mpc52xx_psc_spi_of_match);
+
+static struct of_platform_driver mpc52xx_spi_of_driver = {
+	.owner = THIS_MODULE,
+	.name = "mpc52xx-spi",
+	.match_table = mpc52xx_spi_of_match,
+	.probe = mpc52xx_spi_of_probe,
+	.remove = __exit_p(mpc52xx_spi_of_remove),
+};
+
+static int __init mpc52xx_spi_init(void)
+{
+	return of_register_platform_driver(&mpc52xx_spi_of_driver);
+}
+module_init(mpc52xx_spi_init);
+
+static void __exit mpc52xx_spi_exit(void)
+{
+	of_unregister_platform_driver(&mpc52xx_spi_of_driver);
+}
+module_exit(mpc52xx_spi_exit);
+
diff --git a/include/linux/spi/mpc52xx_spi.h b/include/linux/spi/mpc52xx_spi.h
new file mode 100644
index 0000000..d1004cf
--- /dev/null
+++ b/include/linux/spi/mpc52xx_spi.h
@@ -0,0 +1,10 @@
+
+#ifndef INCLUDE_MPC5200_SPI_H
+#define INCLUDE_MPC5200_SPI_H
+
+extern void mpc52xx_spi_set_premessage_hook(struct spi_master *master,
+					    void (*hook)(struct spi_message *m,
+							 void *context),
+					    void *hook_context);
+
+#endif

[-- Attachment #4: spi-mmc --]
[-- Type: application/octet-stream, Size: 1961 bytes --]

Fix ups to make mmc over spi work

From: Jon Smirl <jonsmirl@gmail.com>


---
 arch/powerpc/boot/dts/dspeak01.dts |    4 ++--
 drivers/mmc/host/mmc_spi.c         |    2 +-
 drivers/of/of_mmc_spi.c            |    4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/boot/dts/dspeak01.dts b/arch/powerpc/boot/dts/dspeak01.dts
index edb11bd..1d10f0a 100644
--- a/arch/powerpc/boot/dts/dspeak01.dts
+++ b/arch/powerpc/boot/dts/dspeak01.dts
@@ -176,9 +176,9 @@
 			interrupt-parent = <&mpc5200_pic>;
 
 			mmc-slot@0 {
-				compatible = "mmc-spi";
+				compatible = "linux,mmc-spi";
 				reg = <0>;
-				max-speed = <50000000>;
+				spi-max-frequency = <20000000>;
 				/* Unregulated slot. */
 				voltage-range = <3300 3300>;
 				/*gpios = <&sdcsr_pio 1 0   /*  WP */
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 7503b81..eb056b9 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -1400,7 +1400,7 @@ static int __devexit mmc_spi_remove(struct spi_device *spi)
 
 static struct spi_driver mmc_spi_driver = {
 	.driver = {
-		.name =		"mmc_spi",
+		.name =		"mmc-spi",
 		.bus =		&spi_bus_type,
 		.owner =	THIS_MODULE,
 	},
diff --git a/drivers/of/of_mmc_spi.c b/drivers/of/of_mmc_spi.c
index aa4e017..f189cdb 100644
--- a/drivers/of/of_mmc_spi.c
+++ b/drivers/of/of_mmc_spi.c
@@ -95,7 +95,7 @@ static int of_mmc_spi_add(struct device *dev)
 	const u32 *voltage_range;
 	int size;
 
-	if (!np || !of_device_is_compatible(np, "mmc-spi"))
+	if (!np || !of_device_is_compatible(np, "linux,mmc-spi"))
 		return NOTIFY_DONE;
 
 	oms = kzalloc(sizeof(*oms), GFP_KERNEL);
@@ -152,7 +152,7 @@ static int of_mmc_spi_del(struct device *dev)
 	struct device_node *np = dev->archdata.of_node;
 	struct of_mmc_spi *oms;
 
-	if (!np || !of_device_is_compatible(np, "mmc-spi") ||
+	if (!np || !of_device_is_compatible(np, "linux,mmc-spi") ||
 			!dev->platform_data)
 		return NOTIFY_DONE;
 

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2008-09-23 15:03 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-09-23 12:43 PSC-SPI and MMC-SPI driver on lite5200b EVB. Device registration problems gianfranco.casanova
2008-09-23 14:38 ` Grant Likely
2008-09-23 15:03   ` Jon Smirl

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).