[uClinux-dev] Accessing MMC/SD over SPI
Jeffrey.Johnston at idealindustries.com
Jeffrey.Johnston at idealindustries.com
Thu May 17 19:17:27 EDT 2007
Hi,
I used the set of patches from
From: Steve Bennett <steveb at workware.net.au>
Subject: [uClinux-dev] Patches for Intec coldfire 5282-based WildFire and
WildFireMod
to implement MMC/SD access over SPI on a M5329EVB board. It is working
fairly well, but I had a couple of questions.
1) That maximum write speed I have been able to achieve is about 240 K
bytes/sec. (With multi-block writes re-enabled).
Is that about the maximum I will be able to achieve with a SPI interface?
(Any speed information I was able to find on the web was using a
MMC controller.)
2) I noticed that the Chip Select is getting removed between command and
response. It seems to work OK, but I have noticed that all of the MMC
specs have the following wording.
"The CS signal must be continuously active for the duration of the SPI
transaction (command, response and data). The
only exception occurs during card programming, when the host can de-assert
the CS signal without affecting the
programming process."
Is this a known violation of the spec that does not cause any problems?
Jeff Johnston
------------------------------
Message: 2
Date: Mon, 14 May 2007 09:47:51 +1000
From: Steve Bennett <steveb at workware.net.au>
Subject: [uClinux-dev] Patches for Intec coldfire 5282-based WildFire
and WildFireMod
To: uclinux-dev at uclinux.org
Message-ID: <4647A3A7.4000405 at workware.net.au>
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Hi Greg,
Following are a series of patches which add support
for the Intec Automation ColdFire 5282-based boards,
the WildFire and WildFireMod.
These patches are all against uClinux-dist-20070130
Let me know if you want these updated against your
latest 2.6.21-uc0 or if you think some of these
should be submitted elsewhere.
Cheers,
Steve
------------------------------
Message: 3
Date: Mon, 14 May 2007 09:51:49 +1000
From: Steve Bennett <steveb at workware.net.au>
Subject: [uClinux-dev] [PATCH]create /dev/rtc with correct dev node
To: uclinux-dev at uclinux.org
Message-ID: <4647A495.5030507 at workware.net.au>
Content-Type: text/plain; charset="iso-8859-1"
-------------- next part --------------
This patch uses 254,0 as the rtc device node if the CONFIG_RTC_INTF_DEV
feature is enabled.
Signed-Off-By: Steve Bennett <steveb at workware.net.au>
diff -urN uClinux-dist.orig/vendors/config/config.dev
uClinux-dist/vendors/config/config.dev
--- uClinux-dist.orig/vendors/config/config.dev 2006-12-08
08:51:58.000000000 +1000
+++ uClinux-dist/vendors/config/config.dev 2007-05-11
16:12:16.000000000 +1000
@@ -134,7 +134,7 @@
DEVICES += $(DEVICE_CRYPTO) crypto,c,10,70
endif
-ifneq ($(CONFIG_RTC_DRV_SH)$(CONFIG_RTC_DRV_DS1302),)
+ifneq
($(CONFIG_RTC_INTF_DEV),$(CONFIG_RTC_DRV_SH)$(CONFIG_RTC_DRV_DS1302),)
DEVICES += $(DEVICE_PRIVATE) rtc,c,254,0
else
ifneq
($(CONFIG_RTC)$(CONFIG_M41T11M6)$(CONFIG_SENSORS_M41T11)$(CONFIG_NVRAM)$(CONFIG_SH_RTC),)
------------------------------
Message: 4
Date: Mon, 14 May 2007 09:53:05 +1000
From: Steve Bennett <steveb at workware.net.au>
Subject: [uClinux-dev] [PATCH]Add various 5282 register definitions
To: uclinux-dev at uclinux.org
Message-ID: <4647A4E1.1080900 at workware.net.au>
Content-Type: text/plain; charset="iso-8859-1"
-------------- next part --------------
This patch adds definitions for various MCF 5282 registers
Signed-Off-By: Steve Bennett <steveb at workware.net.au>
diff -urN uClinux-dist.orig/linux-2.6.x/include/asm-m68knommu/m528xsim.h
uClinux-dist/linux-2.6.x/include/asm-m68knommu/m528xsim.h
--- uClinux-dist.orig/linux-2.6.x/include/asm-m68knommu/m528xsim.h
2007-01-25 10:44:14.000000000 +1000
+++ uClinux-dist/linux-2.6.x/include/asm-m68knommu/m528xsim.h 2007-05-11
16:12:16.000000000 +1000
@@ -31,6 +31,8 @@
#define MCFINT_UART0 13 /*
Interrupt number for UART0 */
#define MCFINT_PIT1 55 /*
Interrupt number for PIT1 */
+#define MCF5282_INTC0 (MCF_IPSBAR + MCFICM_INTC0)
+
/*
* SDRAM configuration registers.
*/
@@ -50,6 +52,17 @@
/* Port UA Pin Assignment Register (8 Bit) */
#define MCF5282_GPIO_PUAPAR 0x10005C
+#define MCF5282_GPIO_PORTQS (*(volatile u8 *) (MCF_IPSBAR +
0x0010000D))
+#define MCF5282_GPIO_DDRQS (*(volatile u8 *) (MCF_IPSBAR +
0x00100021))
+#define MCF5282_GPIO_PORTQSP (*(volatile u8 *) (MCF_IPSBAR +
0x00100035))
+#define MCF5282_GPIO_PQSPAR (*(volatile u8 *) (MCF_IPSBAR +
0x00100059))
+
+#define MCF5282_GPIO_PEPAR (*(volatile u16 *) (MCF_IPSBAR +
0x00100052))
+
+#define MCF5282_GPIO_PORTE (*(volatile u8 *) (MCF_IPSBAR +
0x00100004))
+#define MCF5282_GPIO_DDRE (*(volatile u8 *) (MCF_IPSBAR +
0x00100018))
+#define MCF5282_GPIO_PORTEP (*(volatile u8 *) (MCF_IPSBAR +
0x0010002C))
+
/* Interrupt Mask Register Register Low */
#define MCF5282_INTC0_IMRL (volatile u32 *) (MCF_IPSBAR + 0x0C0C)
/* Interrupt Control Register 7 */
@@ -107,6 +120,11 @@
#define MCF5282_QSPI_QDR MCF_IPSBAR + 0x0354
#define MCF5282_QSPI_QCR MCF_IPSBAR + 0x0354
+#define MCF5282_QSPI_PAR (MCF_IPSBAR + 0x00100059)
+
+#define MCF5282_QSPI_IRQ_SOURCE 18
+#define MCF5282_QSPI_IRQ_VECTOR (64 + MCF5282_QSPI_IRQ_SOURCE)
+
/* Bit level definitions and macros */
#define MCF5282_QSPI_QMR_MSTR (0x8000)
#define MCF5282_QSPI_QMR_DOHIE (0x4000)
------------------------------
Message: 5
Date: Mon, 14 May 2007 09:54:22 +1000
From: Steve Bennett <steveb at workware.net.au>
Subject: [uClinux-dev] [PATCH]Common names for MCF I2C
To: uclinux-dev at uclinux.org
Message-ID: <4647A52E.7050908 at workware.net.au>
Content-Type: text/plain; charset="iso-8859-1"
-------------- next part --------------
The MCF I2C-related register definitions have been renamed separately
for the MCF 528x and 532x series. This breaks the i2c-mcf driver.
I see no reason not to use common names, thus making the i2c-mcf driver
work again.
Signed-Off-By: Steve Bennett <steveb at workware.net.au>
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/i2c/busses/i2c-mcf.c
uClinux-dist/linux-2.6.x/drivers/i2c/busses/i2c-mcf.c
--- uClinux-dist.orig/linux-2.6.x/drivers/i2c/busses/i2c-mcf.c 2006-12-12
23:21:50.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/i2c/busses/i2c-mcf.c 2007-05-11
16:12:16.000000000 +1000
@@ -501,7 +501,7 @@
/* Port AS Pin Assignment Register (PASPAR) */
/* PASPA1 = 11 = AS1 pin is
I2C SDA */
/* PASPA0 = 11 = AS0 pin is
I2C SCL */
- *MCF_GPIO_PASPAR |= 0x000F; /* u16 declaration */
+ *MCF5282_GPIO_PASPAR |= 0x000F; /* u16 declaration */
#endif
diff -urN uClinux-dist.orig/linux-2.6.x/include/asm-m68knommu/m528xsim.h
uClinux-dist/linux-2.6.x/include/asm-m68knommu/m528xsim.h
--- uClinux-dist.orig/linux-2.6.x/include/asm-m68knommu/m528xsim.h
2007-01-25 10:44:14.000000000 +1000
+++ uClinux-dist/linux-2.6.x/include/asm-m68knommu/m528xsim.h 2007-05-11
16:12:16.000000000 +1000
@@ -63,31 +76,31 @@
*
*********************************************************************/
/* Read/Write access macros for general use */
-#define MCF5282_I2C_I2ADR (volatile u8 *) (MCF_IPSBAR + 0x0300) //
Address
-#define MCF5282_I2C_I2FDR (volatile u8 *) (MCF_IPSBAR + 0x0304) //
Freq Divider
-#define MCF5282_I2C_I2CR (volatile u8 *) (MCF_IPSBAR + 0x0308) //
Control
-#define MCF5282_I2C_I2SR (volatile u8 *) (MCF_IPSBAR + 0x030C) //
Status
-#define MCF5282_I2C_I2DR (volatile u8 *) (MCF_IPSBAR + 0x0310) //
Data I/O
+#define MCF_I2C_I2ADR (volatile u8 *) (MCF_IPSBAR + 0x0300) //
Address
+#define MCF_I2C_I2FDR (volatile u8 *) (MCF_IPSBAR + 0x0304) // Freq
Divider
+#define MCF_I2C_I2CR (volatile u8 *) (MCF_IPSBAR + 0x0308) //
Control
+#define MCF_I2C_I2SR (volatile u8 *) (MCF_IPSBAR + 0x030C) //
Status
+#define MCF_I2C_I2DR (volatile u8 *) (MCF_IPSBAR + 0x0310) // Data
I/O
/* Bit level definitions and macros */
-#define MCF5282_I2C_I2ADR_ADDR(x) (((x)&0x7F)<<0x01)
+#define MCF_I2C_I2ADR_ADDR(x) (((x)&0x7F)<<0x01)
-#define MCF5282_I2C_I2FDR_IC(x) (((x)&0x3F))
+#define MCF_I2C_I2FDR_IC(x) (((x)&0x3F))
-#define MCF5282_I2C_I2CR_IEN (0x80) // I2C enable
-#define MCF5282_I2C_I2CR_IIEN (0x40) // interrupt enable
-#define MCF5282_I2C_I2CR_MSTA (0x20) // master/slave mode
-#define MCF5282_I2C_I2CR_MTX (0x10) // transmit/receive mode
-#define MCF5282_I2C_I2CR_TXAK (0x08) // transmit acknowledge enable
-#define MCF5282_I2C_I2CR_RSTA (0x04) // repeat start
-
-#define MCF5282_I2C_I2SR_ICF (0x80) // data transfer bit
-#define MCF5282_I2C_I2SR_IAAS (0x40) // I2C addressed as a slave
-#define MCF5282_I2C_I2SR_IBB (0x20) // I2C bus busy
-#define MCF5282_I2C_I2SR_IAL (0x10) // aribitration lost
-#define MCF5282_I2C_I2SR_SRW (0x04) // slave read/write
-#define MCF5282_I2C_I2SR_IIF (0x02) // I2C interrupt
-#define MCF5282_I2C_I2SR_RXAK (0x01) // received acknowledge
+#define MCF_I2C_I2CR_IEN (0x80) // I2C enable
+#define MCF_I2C_I2CR_IIEN (0x40) // interrupt enable
+#define MCF_I2C_I2CR_MSTA (0x20) // master/slave mode
+#define MCF_I2C_I2CR_MTX (0x10) // transmit/receive mode
+#define MCF_I2C_I2CR_TXAK (0x08) // transmit acknowledge enable
+#define MCF_I2C_I2CR_RSTA (0x04) // repeat start
+
+#define MCF_I2C_I2SR_ICF (0x80) // data transfer bit
+#define MCF_I2C_I2SR_IAAS (0x40) // I2C addressed as a slave
+#define MCF_I2C_I2SR_IBB (0x20) // I2C bus busy
+#define MCF_I2C_I2SR_IAL (0x10) // aribitration lost
+#define MCF_I2C_I2SR_SRW (0x04) // slave read/write
+#define MCF_I2C_I2SR_IIF (0x02) // I2C interrupt
+#define MCF_I2C_I2SR_RXAK (0x01) // received acknowledge
diff -urN uClinux-dist.orig/linux-2.6.x/include/asm-m68knommu/m532xsim.h
uClinux-dist/linux-2.6.x/include/asm-m68knommu/m532xsim.h
--- uClinux-dist.orig/linux-2.6.x/include/asm-m68knommu/m532xsim.h
2006-05-26 16:18:02.000000000 +1000
+++ uClinux-dist/linux-2.6.x/include/asm-m68knommu/m532xsim.h 2007-05-11
16:12:16.000000000 +1000
@@ -131,33 +131,33 @@
*********************************************************************/
/* Read/Write access macros for general use */
-#define MCF532x_I2C_I2ADR (volatile u8 *) (0xFC058000) // Address
-#define MCF532x_I2C_I2FDR (volatile u8 *) (0xFC058004) // Freq
Divider
-#define MCF532x_I2C_I2CR (volatile u8 *) (0xFC058008) // Control
-#define MCF532x_I2C_I2SR (volatile u8 *) (0xFC05800C) // Status
-#define MCF532x_I2C_I2DR (volatile u8 *) (0xFC058010) // Data I/O
+#define MCF_I2C_I2ADR (volatile u8 *) (0xFC058000) // Address
+#define MCF_I2C_I2FDR (volatile u8 *) (0xFC058004) // Freq Divider
+#define MCF_I2C_I2CR (volatile u8 *) (0xFC058008) // Control
+#define MCF_I2C_I2SR (volatile u8 *) (0xFC05800C) // Status
+#define MCF_I2C_I2DR (volatile u8 *) (0xFC058010) // Data I/O
/* Bit level definitions and macros */
-#define MCF532x_I2C_I2ADR_ADDR(x) (((x)&0x7F)<<0x01)
+#define MCF_I2C_I2ADR_ADDR(x) (((x)&0x7F)<<0x01)
-#define MCF532x_I2C_I2FDR_IC(x) (((x)&0x3F))
+#define MCF_I2C_I2FDR_IC(x) (((x)&0x3F))
-#define MCF532x_I2C_I2CR_IEN (0x80) // I2C enable
-#define MCF532x_I2C_I2CR_IIEN (0x40) // interrupt enable
-#define MCF532x_I2C_I2CR_MSTA (0x20) // master/slave mode
-#define MCF532x_I2C_I2CR_MTX (0x10) // transmit/receive mode
-#define MCF532x_I2C_I2CR_TXAK (0x08) // transmit acknowledge enable
-#define MCF532x_I2C_I2CR_RSTA (0x04) // repeat start
-
-#define MCF532x_I2C_I2SR_ICF (0x80) // data transfer bit
-#define MCF532x_I2C_I2SR_IAAS (0x40) // I2C addressed as a slave
-#define MCF532x_I2C_I2SR_IBB (0x20) // I2C bus busy
-#define MCF532x_I2C_I2SR_IAL (0x10) // aribitration lost
-#define MCF532x_I2C_I2SR_SRW (0x04) // slave read/write
-#define MCF532x_I2C_I2SR_IIF (0x02) // I2C interrupt
-#define MCF532x_I2C_I2SR_RXAK (0x01) // received acknowledge
+#define MCF_I2C_I2CR_IEN (0x80) // I2C enable
+#define MCF_I2C_I2CR_IIEN (0x40) // interrupt enable
+#define MCF_I2C_I2CR_MSTA (0x20) // master/slave mode
+#define MCF_I2C_I2CR_MTX (0x10) // transmit/receive mode
+#define MCF_I2C_I2CR_TXAK (0x08) // transmit acknowledge enable
+#define MCF_I2C_I2CR_RSTA (0x04) // repeat start
+
+#define MCF_I2C_I2SR_ICF (0x80) // data transfer bit
+#define MCF_I2C_I2SR_IAAS (0x40) // I2C addressed as a slave
+#define MCF_I2C_I2SR_IBB (0x20) // I2C bus busy
+#define MCF_I2C_I2SR_IAL (0x10) // aribitration lost
+#define MCF_I2C_I2SR_SRW (0x04) // slave read/write
+#define MCF_I2C_I2SR_IIF (0x02) // I2C interrupt
+#define MCF_I2C_I2SR_RXAK (0x01) // received acknowledge
-#define MCF532x_PAR_FECI2C (volatile u8 *) (0xFC0A4053)
+#define MCF_PAR_FECI2C (volatile u8 *) (0xFC0A4053)
/*
------------------------------
Message: 6
Date: Mon, 14 May 2007 09:55:00 +1000
From: Steve Bennett <steveb at workware.net.au>
Subject: [uClinux-dev] [PATCH]Alarm support for PCF8563 RTC
To: uclinux-dev at uclinux.org
Message-ID: <4647A554.7040304 at workware.net.au>
Content-Type: text/plain; charset="iso-8859-1"
-------------- next part --------------
This patch adds support for reading and writing the alarm setting
on the PCF8563 RTC.
Signed-Off-By: Steve Bennett <steveb at workware.net.au>
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/rtc/rtc-pcf8563.c
uClinux-dist/linux-2.6.x/drivers/rtc/rtc-pcf8563.c
--- uClinux-dist.orig/linux-2.6.x/drivers/rtc/rtc-pcf8563.c 2006-11-30
09:27:49.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/rtc/rtc-pcf8563.c 2007-05-11
16:12:16.000000000 +1000
@@ -53,14 +53,32 @@
#define PCF8563_SC_LV 0x80 /* low voltage */
#define PCF8563_MO_C 0x80 /* century */
+/* Define various control and status bits in the registers. */
+#define PCF8563_ST1_TEST1 0x80 // 1 = test mode.
+#define PCF8563_ST1_STOP 0x20 // 1 = stop RTC.
+#define PCF8563_ST1_TESTC 0x8 // 1 = override
power-on reset.
+
+#define PCF8563_ST2_TI 0x10 // 1 = output
clock on /int.
+#define PCF8563_ST2_AF 0x8 // Alarm flag.
+#define PCF8563_ST2_TF 0x4 // Timer flag.
+#define PCF8563_ST2_AIE 0x2 // Alarm
interrupt enable.
+#define PCF8563_ST2_TIE 0x1 // Timer
interrupt enable.
+
+#define PCF8563_ALARM_AE 0x80 // 1 = enable
alarm output.
+
static int pcf8563_probe(struct i2c_adapter *adapter, int address, int
kind);
static int pcf8563_detach(struct i2c_client *client);
+#define ALARM_BCD2BIN(B) (((B) & PCF8563_ALARM_AE) ? 0xFF : BCD2BIN(B))
+#define ALARM_BIN(B) (((B) & PCF8563_ALARM_AE) ? 0xFF : (B))
+
/*
* In the routines that deal directly with the pcf8563 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ *
+ * If 'alrm' is not NULL, the alarm state is retrieved into *tm,
alrm->pending and alrm->enabled.
*/
-static int pcf8563_get_datetime(struct i2c_client *client, struct
rtc_time *tm)
+static int pcf8563_get_datetime(struct i2c_client *client, struct
rtc_time *tm, struct rtc_wkalrm *alrm)
{
unsigned char buf[13] = { PCF8563_REG_ST1 };
@@ -87,39 +105,63 @@
buf[4], buf[5], buf[6], buf[7],
buf[8]);
+ if (alrm) {
+ dev_dbg(&client->dev,
+ "%s: alarm raw data is
min=%02x, hr=%02x, "
+ "mday=%02x, wday=%02x\n",
+ __FUNCTION__,
+ buf[9], buf[10], buf[11],
buf[12]);
+ }
- tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC] & 0x7F);
- tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN] & 0x7F);
- tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR] & 0x3F); /* rtc
hr 0-23 */
- tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM] & 0x3F);
- tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
+ /* Alarm has no month and year, so these are the same for
both */
tm->tm_mon = BCD2BIN(buf[PCF8563_REG_MO] & 0x1F) - 1; /*
rtc mn 1-12 */
tm->tm_year = BCD2BIN(buf[PCF8563_REG_YR])
+ (buf[PCF8563_REG_MO] & PCF8563_MO_C ? 0
: 100);
- dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d,
hours=%d, "
+ if (alrm) {
+ tm->tm_sec = 0;
+ tm->tm_min =
ALARM_BCD2BIN(buf[PCF8563_REG_AMN]);
+ tm->tm_hour =
ALARM_BCD2BIN(buf[PCF8563_REG_AHR] & 0xBF);
+ tm->tm_mday =
ALARM_BCD2BIN(buf[PCF8563_REG_ADM] & 0xBF);
+ tm->tm_wday =
ALARM_BIN(buf[PCF8563_REG_ADW] & 0x87);
+ /* tm_mon and tm_year are set below */
+ alrm->enabled = buf[PCF8563_REG_ST2] &
PCF8563_ST2_AIE;
+ alrm->pending = buf[PCF8563_REG_ST2] &
PCF8563_ST2_AF;
+ }
+ else {
+ tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC]
& 0x7F);
+ tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN]
& 0x7F);
+ tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR]
& 0x3F); /* rtc hr 0-23 */
+ tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM]
& 0x3F);
+ tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
+
+ /* the clock can give out invalid
datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse
to set the time on bootup.
+ */
+ if (rtc_valid_tm(tm) < 0) {
+ dev_err(&client->dev,
"retrieved date/time is not valid.\n");
+ }
+ }
+
+ dev_dbg(&client->dev, "%s: %s tm is secs=%d, mins=%d,
hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
+ alrm ? "alarm" : "time",
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year,
tm->tm_wday);
- /* the clock can give out invalid datetime, but we cannot
return
- * -EINVAL otherwise hwclock will refuse to set the time
on bootup.
- */
- if (rtc_valid_tm(tm) < 0)
- dev_err(&client->dev, "retrieved
date/time is not valid.\n");
-
return 0;
}
-static int pcf8563_set_datetime(struct i2c_client *client, struct
rtc_time *tm)
+static int pcf8563_set_datetime(struct i2c_client *client, struct
rtc_time *tm, struct rtc_wkalrm *alrm)
{
int i, err;
- unsigned char buf[9];
+ unsigned char buf[13];
- dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ dev_dbg(&client->dev, "%s: %s secs=%d, mins=%d, hours=%d,
"
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
+ alrm ? "alarm" : "time",
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year,
tm->tm_wday);
@@ -157,6 +199,48 @@
return 0;
}
+static int pcf8563_set_alarm(struct i2c_client *client, struct rtc_wkalrm
*alrm)
+{
+ int err;
+ unsigned char buf[5];
+
+ dev_dbg(&client->dev, "%s: alarm mins=%d, hours=%d, "
+ "mday=%d, wday=%d, enabled=%d\n",
+ __FUNCTION__,
+ alrm->time.tm_min, alrm->time.tm_hour,
+ alrm->time.tm_mday, alrm->time.tm_wday,
alrm->enabled);
+
+ /* First set the alarm time/date */
+ /* Wild card (0xff) value in field means that alarm is
not set for that field. */
+ buf[0] = PCF8563_REG_AMN; /* Address to
write data */
+ buf[1] = (alrm->time.tm_min == 0xff) ? 0x80 :
BIN_TO_BCD(alrm->time.tm_min);
+ buf[2] = (alrm->time.tm_hour == 0xff) ? 0x80 :
BIN_TO_BCD(alrm->time.tm_hour);
+ buf[3] = (alrm->time.tm_mday == 0xff) ? 0x81 :
BIN_TO_BCD(alrm->time.tm_mday);
+ buf[4] = (alrm->time.tm_wday == 0xff) ? 0x80 :
alrm->time.tm_wday;
+
+ /* write alarm registers */
+ err = i2c_master_send(client, buf, sizeof(buf));
+ if (err != sizeof(buf)) {
+ dev_err(&client->dev,
+ "%s: err=%d addr=%02x\n",
+ __FUNCTION__, err,
buf[0]);
+ return -EIO;
+ }
+
+ /* Now write the status/control register 2 */
+ buf[0] = PCF8563_REG_ST2; /* Address to
write data */
+ buf[1] = alrm->enabled ? PCF8563_ST2_AIE : 0;
+ err = i2c_master_send(client, buf, 2);
+ if (err != 2) {
+ dev_err(&client->dev,
+ "%s: err=%d addr=%02x\n",
+ __FUNCTION__, err,
buf[0]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
struct pcf8563_limit
{
unsigned char reg;
@@ -217,19 +301,31 @@
return 0;
}
+static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm
*alrm)
+{
+ return pcf8563_get_datetime(to_i2c_client(dev),
&alrm->time, alrm);
+}
+
+static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm
*alrm)
+{
+ return pcf8563_set_alarm(to_i2c_client(dev), alrm);
+}
+
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- return pcf8563_get_datetime(to_i2c_client(dev), tm);
+ return pcf8563_get_datetime(to_i2c_client(dev), tm, 0);
}
static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- return pcf8563_set_datetime(to_i2c_client(dev), tm);
+ return pcf8563_set_datetime(to_i2c_client(dev), tm, 0);
}
static const struct rtc_class_ops pcf8563_rtc_ops = {
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
+ .read_alarm = pcf8563_rtc_read_alarm,
+ .set_alarm = pcf8563_rtc_set_alarm,
};
static int pcf8563_attach(struct i2c_adapter *adapter)
------------------------------
Message: 7
Date: Mon, 14 May 2007 09:55:36 +1000
From: Steve Bennett <steveb at workware.net.au>
Subject: [uClinux-dev] [PATCH]Driver for access to MCF IPS
To: uclinux-dev at uclinux.org
Message-ID: <4647A578.4050100 at workware.net.au>
Content-Type: text/plain; charset="iso-8859-1"
-------------- next part --------------
This patch adds a driver for simple access to the
Coldfire Internal Peripheral System (IPS) address space
via the /dev/ips char device.
Signed-Off-By: Steve Bennett <steveb at workware.net.au>
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/char/Makefile
uClinux-dist/linux-2.6.x/drivers/char/Makefile
--- uClinux-dist.orig/linux-2.6.x/drivers/char/Makefile 2006-11-30
12:03:15.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/char/Makefile 2007-05-11
16:12:16.000000000 +1000
@@ -54,6 +54,7 @@
obj-$(CONFIG_HVCS) += hvcs.o
obj-$(CONFIG_SGI_MBCS) += mbcs.o
obj-$(CONFIG_MCF_QSPI) += mcf_qspi.o
+obj-$(CONFIG_MCF_IPS) += mcf_ips.o
obj-$(CONFIG_BRIQ_PANEL) += briq_panel.o
obj-$(CONFIG_PRINTER) += lp.o
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/char/mcf_ips.c
uClinux-dist/linux-2.6.x/drivers/char/mcf_ips.c
--- uClinux-dist.orig/linux-2.6.x/drivers/char/mcf_ips.c 1970-01-01
10:00:00.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/char/mcf_ips.c 2007-05-11
16:12:16.000000000 +1000
@@ -0,0 +1,184 @@
+/*
+ * linux-2.4.x/drivers/char/mcf_ips.c
+ *
+ * Internal Peripheral System Access Driver
+ *
+ * Copyright (C) 2005 Intec Automation (mike at steroidmicros.com)
+ *
+ *
+ * Provides simple access to the Coldfire Internal Peripheral System
+ * address space via the /dev/ips char device.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+
+#define DEVICE_NAME "ips"
+
+/* Use a dynamically assigned misc device minor */
+/*#define IPS_MAJOR 0*/
+/* Or a statically assigned device major */
+#define IPS_MAJOR 122
+
+MODULE_AUTHOR("Mike Lavender <mike at steroidmicros.com>");
+MODULE_DESCRIPTION("Driver for Reading and Writing Peripherals");
+MODULE_SUPPORTED_DEVICE("Motorola MCF5282");
+MODULE_LICENSE("GPL");
+
+/* ioctl to set the read/write address */
+#define IPS_REG_SET 1
+
+static ssize_t
+ ips_read(
+ struct file *file,
+ char *buffer,
+ size_t length,
+ loff_t *offset )
+{
+ int ret = 0;
+
+ switch (length)
+ {
+ case 1:
+ *(char *)buffer = *(char *)file->private_data;
+ ret = 1;
+ break;
+ case 2:
+ *(short *)buffer = *(short *)file->private_data;
+ ret = 2;
+ break;
+ case 4:
+ *(int *)buffer = *(int *)file->private_data;
+ ret = 4;
+ break;
+ default:
+ memcpy( buffer, (char *)file->private_data, length );
+ ret = length;
+ }
+
+ return (ret);
+}
+
+static ssize_t
+ ips_write(
+ struct file *file,
+ const char *buffer,
+ size_t length,
+ loff_t *offset )
+{
+ int ret = 0;
+
+ switch (length)
+ {
+ case 1:
+ *(char *)file->private_data = *(char *)buffer;
+ ret = 1;
+ break;
+ case 2:
+ *(short *)file->private_data = *(short *)buffer;
+ ret = 2;
+ break;
+ case 4:
+ *(int *)file->private_data = *(int *)buffer;
+ ret = 4;
+ break;
+ default:
+ memcpy( (char *)file->private_data, buffer, length );
+ ret = length;
+ }
+
+ return (ret);
+}
+
+int
+ ips_ioctl(
+ struct inode *inode,
+ struct file *file,
+ unsigned int ioctl_num,
+ unsigned long ioctl_param )
+{
+ int ret = 0;
+
+ switch (ioctl_num) {
+ case IPS_REG_SET:
+ file->private_data = (void *)ioctl_param;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return (ret);
+}
+
+static int
+ ips_open(
+ struct inode *inode,
+ struct file *file )
+{
+ return 0;
+}
+
+static int
+ ips_release(
+ struct inode *inode,
+ struct file *file )
+{
+ return 0;
+}
+
+/* fixed for 2.4 kernel, owner was ifdef'ed out for 2.0 kernel */
+static struct file_operations fops = {
+ owner: THIS_MODULE,
+ read: ips_read,
+ write: ips_write,
+ ioctl: ips_ioctl,
+ open: ips_open,
+ release: ips_release /* a.k.a. close */
+};
+
+#if IPS_MAJOR == 0
+static struct miscdevice misc_fops = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = DEVICE_NAME,
+ .fops = &fops
+};
+#endif
+
+int __init
+ ips_init(void)
+{
+ int ret;
+
+#if IPS_MAJOR == 0
+ ret = misc_register(&misc_fops);
+#else
+ ret = register_chrdev(IPS_MAJOR, DEVICE_NAME, &fops);
+#endif
+
+ if (ret < 0) {
+ return -EIO;
+ }
+
+ printk("MCF IPS device driver installed\n");
+
+ return 0;
+}
+
+void __exit
+ ips_exit(void)
+{
+#if IPS_MAJOR == 0
+ misc_deregister(&misc_fops);
+#else
+ unregister_chrdev(IPS_MAJOR, DEVICE_NAME);
+#endif
+}
+
+module_init(ips_init);
+module_exit(ips_exit);
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/char/Kconfig
uClinux-dist/linux-2.6.x/drivers/char/Kconfig
--- uClinux-dist.orig/linux-2.6.x/drivers/char/Kconfig 2006-12-07
17:19:47.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/char/Kconfig 2007-05-11
16:12:16.000000000 +1000
@@ -1088,6 +1088,14 @@
help
Driver for Coldfire processors QSPI
+config MCF_IPS
+ tristate "Coldfire IPS access"
+ default n
+ depends on COLDFIRE
+ help
+ Simple access to the Coldfire Internal Peripheral
System address space
+ via the /dev/ips char device.
+
config M41T11M6
tristate "M41T11M6 Real Time Clock (RTC) support"
help
------------------------------
Message: 8
Date: Mon, 14 May 2007 09:56:40 +1000
From: Steve Bennett <steveb at workware.net.au>
Subject: [uClinux-dev] [PATCH]Access FEC MII via bit bang interface
To: uclinux-dev at uclinux.org
Message-ID: <4647A5B8.5010001 at workware.net.au>
Content-Type: text/plain; charset="iso-8859-1"
-------------- next part --------------
This patch add support for FEC MII access via bit banging.
Signed-Off-By: Steve Bennett <steveb at workware.net.au>
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/net/Kconfig
uClinux-dist/linux-2.6.x/drivers/net/Kconfig
--- uClinux-dist.orig/linux-2.6.x/drivers/net/Kconfig 2006-11-30
12:03:18.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/net/Kconfig 2007-05-11
16:12:16.000000000 +1000
@@ -1909,6 +1909,14 @@
Say Y here if you want to use the built-in 10/100 Fast
ethernet
controller on some Motorola ColdFire processors.
+config FEC_MII_BIT_BANG
+ bool "FEC MII bit bang interface"
+ depends on FEC && (WILDFIRE || WILDFIREMOD)
+ help
+ Say Y here if you want to use the MII bit banging
interfaces.
+ The bit banging interfaces will use two digital IO
lines
+ instead of the built in MII module.
+
config FEC2
bool "Second FEC ethernet controller (on some ColdFire
CPUs)"
depends on FEC
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/net/Makefile
uClinux-dist/linux-2.6.x/drivers/net/Makefile
--- uClinux-dist.orig/linux-2.6.x/drivers/net/Makefile 2006-11-30
12:03:18.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/net/Makefile 2007-05-11
16:12:16.000000000 +1000
@@ -91,6 +91,7 @@
obj-$(CONFIG_OPEN_ETH) += open_eth.o
obj-$(CONFIG_MTIP1000_ETH) += mtip1000.o
obj-$(CONFIG_FEC) += fec.o
+obj-$(CONFIG_FEC_MII_BIT_BANG) += fec_mii_bit_bang.o
obj-$(CONFIG_ARM_ETHERH) += 8390.o
obj-$(CONFIG_WD80x3) += wd.o 8390.o
obj-$(CONFIG_EL2) += 3c503.o 8390.o
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/net/fec.c
uClinux-dist/linux-2.6.x/drivers/net/fec.c
--- uClinux-dist.orig/linux-2.6.x/drivers/net/fec.c 2007-01-17
15:58:59.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/net/fec.c 2007-05-11
16:12:16.000000000 +1000
@@ -60,6 +60,10 @@
#include "commproc.h"
#endif
+#if defined(CONFIG_FEC_MII_BIT_BANG)
+#include "fec_mii_bit_bang.h"
+#endif
+
#if defined(CONFIG_FEC2)
#define FEC_MAX_PORTS 2
#else
@@ -745,6 +749,19 @@
spin_lock_irqsave(&fep->lock,flags);
+#if defined(CONFIG_FEC_MII_BIT_BANG)
+ /* If bit banging the MII just do it and don't bother
+ * adding it to the queue. once we have the result
+ * call the function to parse it.
+ */
+ {
+ uint mii_result;
+ mii_result = mii_bit_bang_do(regval);
+ if (func != NULL) {
+ func(mii_result, dev);
+ }
+ }
+#else
if ((mip = mii_free) != NULL) {
mii_free = mip->mii_next;
mip->mii_regval = regval;
@@ -762,6 +779,7 @@
else {
retval = 1;
}
+#endif
spin_unlock_irqrestore(&fep->lock,flags);
@@ -1159,6 +1177,28 @@
};
/*
-------------------------------------------------------------------------
*/
+/* ST Microelectronics STE100P phy */
+
+/* register definitions for the STE100P */
+
+static phy_cmd_t const phy_cmd_ste100p_config[] = {
+ { mk_mii_read(MII_REG_CR), mii_parse_cr
},
+ { mk_mii_read(MII_REG_ANAR),
mii_parse_anar },
+ { mk_mii_end, }
+ };
+static phy_cmd_t const phy_cmd_ste100p_startup[] = {
+ { mk_mii_read(MII_REG_SR), mii_parse_sr
},
+ { mk_mii_end, }
+ };
+
+static phy_info_t phy_info_ste100p = {
+ .id = 0x1c04001,
+ .name = "STE100P",
+ .config = phy_cmd_ste100p_config,
+ .startup = phy_cmd_ste100p_startup,
+};
+
+/*
-------------------------------------------------------------------------
*/
/* register definitions for the DP83848 */
#define MII_DP8384X_PHYSTST 16 /* PHY Status Register */
@@ -1226,6 +1266,7 @@
&phy_info_am79c874,
&phy_info_ks8721bl,
&phy_info_dp83848,
+ &phy_info_ste100p,
NULL
};
@@ -1423,7 +1464,9 @@
gpio_paspar = (volatile u16 *)
(MCF_IPSBAR + 0x100056);
gpio_pehlpar = (volatile u16 *)
(MCF_IPSBAR + 0x100058);
+#if !defined(CONFIG_FEC_MII_BIT_BANG)
*gpio_paspar |= 0x0f00;
+#endif
*gpio_pehlpar = 0xc0;
}
#endif
@@ -2469,6 +2512,10 @@
/* setup MII interface */
fec_set_mii(dev, fep);
+#if defined(CONFIG_FEC_MII_BIT_BANG)
+ mii_bit_bang_setup();
+#endif
+
/* Clear and enable interrupts */
fecp->fec_ievent = 0xffc00000;
fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/net/fec_mii_bit_bang.c
uClinux-dist/linux-2.6.x/drivers/net/fec_mii_bit_bang.c
--- uClinux-dist.orig/linux-2.6.x/drivers/net/fec_mii_bit_bang.c
1970-01-01 10:00:00.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/net/fec_mii_bit_bang.c 2007-05-11
16:12:16.000000000 +1000
@@ -0,0 +1,335 @@
+/****************************************************************************/
+
+/*
+ * fec_mii_bit_bang.c -- Interface for bit banging the MII
used in
+ * FEC module
+ *
+ * (C) Copyright 2005, Intec Automation,
+ * Mike Lavender
(mike at steroidmicros)
+ *
+
+ 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
-------------------------------------------------------------------------
*/
+
+/****************************************************************************/
+
+/****************************************************************************/
+
+/*
+ * Includes
+ */
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <asm/coldfire.h>
+#include <asm/mcfsim.h>
+
+#include "fec_mii_bit_bang.h"
+
+/****************************************************************************/
+
+/*
+ * Defines and Macros
+ */
+
+#define READING_MII(x) (((x) & 0xf0000000) ==
0x60000000)
+
+#if defined(CONFIG_WILDFIREMOD)
+
+#define MDIO_INDEX 4 // PIN 4
+#define MDC_INDEX 7 // PIN 7
+
+static inline void mii_bb_init(void)
+{
+ /* Set lines to Digital IO */
+ MCF5282_GPIO_PEPAR &= ~((1 << (MDIO_INDEX * 2)) | (1 <<
(MDC_INDEX * 2)));
+
+ /* Set clock as output */
+ MCF5282_GPIO_DDRE |= (1 << MDC_INDEX);
+}
+
+static inline int mii_bb_data(void)
+{
+ return !!(MCF5282_GPIO_PORTEP & (1 << MDIO_INDEX));
+}
+
+static inline void mii_bb_set_data(int value)
+{
+ if (value) {
+ MCF5282_GPIO_PORTE |= (1 << MDIO_INDEX);
+ }
+ else {
+ MCF5282_GPIO_PORTE &= ~(1 << MDIO_INDEX);
+ }
+}
+
+static inline void mii_bb_set_clock(int value)
+{
+ if (value) {
+ MCF5282_GPIO_PORTE |= (1 << MDC_INDEX);
+ }
+ else {
+ MCF5282_GPIO_PORTE &= ~(1 << MDC_INDEX);
+ }
+}
+
+static inline void mii_bb_data_as_output(void)
+{
+ MCF5282_GPIO_DDRE |= (1 << MDIO_INDEX);
+}
+
+static inline void mii_bb_data_as_input(void)
+{
+ MCF5282_GPIO_DDRE &= ~(1 << MDIO_INDEX);
+}
+
+#elif defined(CONFIG_WILDFIRE)
+
+#define MDIO_INDEX 6 // PIN 6
+#define MDC_INDEX 5 // PIN 5
+
+static inline void mii_bb_init(void)
+{
+ /* Set lines to Digital IO */
+ MCF5282_GPIO_PQSPAR &= ~((1 << MDIO_INDEX) | (1 <<
MDC_INDEX));
+
+ /* Set clock as output */
+ MCF5282_GPIO_DDRQS |= (1 << MDC_INDEX);
+}
+
+static inline int mii_bb_data(void)
+{
+ return !!(MCF5282_GPIO_PORTQSP & (1 << MDIO_INDEX));
+}
+
+static inline void mii_bb_set_data(int value)
+{
+ if (value) {
+ MCF5282_GPIO_PORTQS |= (1 << MDIO_INDEX);
+ }
+ else {
+ MCF5282_GPIO_PORTQS &= ~(1 <<
MDIO_INDEX);
+ }
+}
+
+static inline void mii_bb_set_clock(int value)
+{
+ if (value) {
+ MCF5282_GPIO_PORTQS |= (1 << MDC_INDEX);
+ }
+ else {
+ MCF5282_GPIO_PORTQS &= ~(1 << MDC_INDEX);
+ }
+}
+
+static inline void mii_bb_data_as_output(void)
+{
+ MCF5282_GPIO_DDRQS |= (1 << MDIO_INDEX);
+}
+
+static inline void mii_bb_data_as_input(void)
+{
+ MCF5282_GPIO_DDRQS &= ~(1 << MDIO_INDEX);
+}
+
+#else
+
+#error don't know how to bitbang mii on this platform
+
+#endif
+
+/****************************************************************************/
+
+/*
+ * Global variables
+ */
+
+
+/****************************************************************************/
+
+/*
+ * Implementation
+ */
+
+/*
+ * Read a single bit from the MII bus and then set the clock.
+ */
+static int
+write_clock_and_read_data(int clock)
+{
+ unsigned char data;
+
+ mii_bb_data_as_input();
+ data = mii_bb_data();
+ mii_bb_set_clock(clock);
+
+ udelay(1);
+
+ return data;
+}
+
+
+/*
+ * Write a single bit on the MII bus by first setting the clock
+ * and then writing the data.
+ */
+static void
+write_clock_and_data(int clock, int data)
+{
+ mii_bb_data_as_output();
+ mii_bb_set_clock(clock);
+ mii_bb_set_data(data);
+
+ udelay(1);
+}
+
+/**
+ * Write a single bit while toggling the clock
+ * from low to high
+ */
+static inline void
+write_data(int data)
+{
+ write_clock_and_data(0, data);
+ write_clock_and_data(1, data);
+}
+
+
+static void
+mii_preamble(void)
+{
+ int i;
+
+ for (i = 0; i < 36; i++) {
+ write_data(1);
+ }
+}
+
+
+static uint
+mii_bit_bang_readreg(int regval)
+{
+ int bit;
+ uint result;
+ uint id = (regval >> 23) & 0x1f;
+ uint addr = (regval >> 18) & 0x1f;
+
+ // Preamble.
+ mii_preamble();
+
+ // Setup: Write 0, 1 to MDIO.
+ write_data(0);
+ write_data(1);
+
+ // Opcode: Write 1, 0 to MDIO.
+ write_data(1);
+ write_data(0);
+
+ // 5 which-port bits
+ for (bit = 4; bit >= 0; bit--) {
+ write_data((id >> bit) & 1);
+ }
+
+ // 5 register bits
+ for (bit = 4; bit >= 0; bit--) {
+ write_data((addr >> bit) & 1);
+ }
+
+ // Turn around
+ (void)write_clock_and_read_data(0);
+ (void)write_clock_and_read_data(1);
+ (void)write_clock_and_read_data(0);
+
+ // 16 data bits
+ result = 0;
+ for (bit = 15; bit >= 0; bit--) {
+ (void)write_clock_and_read_data(1);
+ result |= write_clock_and_read_data(0) <<
bit;
+ }
+
+ return result;
+}
+
+static void
+mii_bit_bang_writereg(int regval)
+{
+ uint id = (regval >> 23) & 0x1f;
+ uint addr = (regval >> 18) & 0x1f;
+ uint data = regval & 0xffff;
+ int bit;
+
+ // Preamble.
+ mii_preamble();
+
+ // ST (whatever that means): Write 0, 1 to MDIO.
+ write_data(0);
+ write_data(1);
+
+ // Opcode: Write 0, 1 to MDIO.
+ write_data(0);
+ write_data(1);
+
+ // 5 which-port bits
+ for (bit = 4; bit >= 0; bit--) {
+ write_data((id >> bit) & 1);
+ }
+
+ // 5 register bits
+ for (bit = 4; bit >= 0; bit--) {
+ write_data((addr >> bit) & 1);
+ }
+
+ // Turn around
+ write_data(1);
+ write_data(0);
+
+ // 16 data bits
+ for (bit = 15; bit >= 0; bit--) {
+ write_data((data >> bit) & 1);
+ }
+}
+
+/*
+ * Called by the fec module (fec_enet_init) if bit banging the
+ * MII is configured. Will setup the pins required to bit bang
+ * and any internal data.
+ */
+void
+mii_bit_bang_setup(void)
+{
+ mii_bb_init();
+
+ /* drive the clock and data low for now */
+ mii_bb_set_data(0);
+ mii_bb_set_clock(0);
+
+ /* Make the data an output to begin with */
+ mii_bb_data_as_output();
+}
+
+
+/*
+ * Execute an MII register read or write. If the request is
+ * a read then return the register value otherwise return 0.
+ */
+uint
+mii_bit_bang_do(int regval)
+{
+ if (READING_MII(regval)) {
+ return mii_bit_bang_readreg(regval);
+ } else {
+ mii_bit_bang_writereg(regval);
+ return 0;
+ }
+}
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/net/fec_mii_bit_bang.h
uClinux-dist/linux-2.6.x/drivers/net/fec_mii_bit_bang.h
--- uClinux-dist.orig/linux-2.6.x/drivers/net/fec_mii_bit_bang.h
1970-01-01 10:00:00.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/net/fec_mii_bit_bang.h 2007-05-11
16:12:16.000000000 +1000
@@ -0,0 +1,37 @@
+/****************************************************************************/
+
+/*
+ * fec_mii_bit_bang.h -- Interface for bit banging the MII
used in
+ * FEC module
+ *
+ * (C) Copyright 2005, Intec Automation,
+ * Mike Lavender
(mike at steroidmicros)
+ */
+
+/****************************************************************************/
+#ifndef FEC_MII_BIT_BANG_H
+#define FEC_MII_BIT_BANG_H
+/****************************************************************************/
+
+/* Bit banging the MII interface: On some boards the MII lines are
+ * multiplexed with other signals like UART3 on the MCF5282. This
+ * configuration will use two digital IO pins to talk to the PHY
+ * instead of the integrated MII module
+ */
+
+/*
+ * Called by the fec module (fec_enet_init) if bit banging the
+ * MII is configured. Will setup the pins required to bit bang
+ * and any internal data.
+ */
+void
+mii_bit_bang_setup(void);
+
+/*
+ * Execute an MII register read or write. If the request is
+ * a read then return the register value otherwise return 0.
+ */
+uint
+mii_bit_bang_do(int regval);
+
+#endif /* FEC_MII_BIT_BANG_H */
------------------------------
Message: 9
Date: Mon, 14 May 2007 09:57:47 +1000
From: Steve Bennett <steveb at workware.net.au>
Subject: [uClinux-dev] [PATCH]spi bitbang with explicit "tx idle"
output
To: uclinux-dev at uclinux.org
Message-ID: <4647A5FB.7020503 at workware.net.au>
Content-Type: text/plain; charset="iso-8859-1"
-------------- next part --------------
This patch adds support to the spi-bitbang driver for explicitly setting
what data is transmitted when the tx buffer is empty.
Either zeroes (the default), or ones (if SPI_TX_1 is set)
Signed-Off-By: Steve Bennett <steveb at workware.net.au>
diff -urN uClinux-dist.orig/linux-2.6.x/include/linux/spi/spi.h
uClinux-dist/linux-2.6.x/include/linux/spi/spi.h
--- uClinux-dist.orig/linux-2.6.x/include/linux/spi/spi.h 2006-10-09
10:01:37.000000000 +1000
+++ uClinux-dist/linux-2.6.x/include/linux/spi/spi.h 2007-05-11
16:12:05.000000000 +1000
@@ -71,6 +71,7 @@
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /*
chipselect active high? */
#define SPI_LSB_FIRST 0x08 /*
per-word bits-on-wire */
+#define SPI_TX_1 0x10 /* shift
out ones on rx-only */
u8 bits_per_word;
int irq;
void *controller_state;
@@ -289,8 +290,9 @@
* the data being transferred; that may reduce overhead, when the
* underlying driver uses dma.
*
- * If the transmit buffer is null, undefined data will be shifted out
- * while filling rx_buf. If the receive buffer is null, the data
+ * If the transmit buffer is null, zeroes will be shifted out while
+ * filling rx_buf, unless SPI_TX_1 is set in spi->mode (in which case
+ * ones will be shifted out). If the receive buffer is null, the data
* shifted in will be discarded. Only "len" bytes shift out (or in).
* It's an error to try to shift out a partial word. (For example, by
* shifting out three bytes with word size of sixteen or twenty bits;
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/spi/spi_bitbang.c
uClinux-dist/linux-2.6.x/drivers/spi/spi_bitbang.c
--- uClinux-dist.orig/linux-2.6.x/drivers/spi/spi_bitbang.c 2006-10-09
10:01:44.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/spi/spi_bitbang.c 2007-05-11
16:12:05.000000000 +1000
@@ -73,10 +73,14 @@
u8 *rx =
t->rx_buf;
while (likely(count > 0)) {
- u8 word = 0;
+ u8 word;
if (tx)
word = *tx++;
+ else if (spi->mode & SPI_TX_1)
+ word = ~0;
+ else
+ word = 0;
word = txrx_word(spi, ns, word, bits);
if (rx)
*rx++ = word;
@@ -99,10 +103,14 @@
u16 *rx =
t->rx_buf;
while (likely(count > 1)) {
- u16 word = 0;
+ u16 word;
if (tx)
word = *tx++;
+ else if (spi->mode & SPI_TX_1)
+ word = ~0;
+ else
+ word = 0;
word = txrx_word(spi, ns, word, bits);
if (rx)
*rx++ = word;
@@ -125,10 +133,14 @@
u32 *rx =
t->rx_buf;
while (likely(count > 3)) {
- u32 word = 0;
+ u32 word;
if (tx)
word = *tx++;
+ else if (spi->mode & SPI_TX_1)
+ word = ~0;
+ else
+ word = 0;
word = txrx_word(spi, ns, word, bits);
if (rx)
*rx++ = word;
@@ -176,6 +188,8 @@
}
EXPORT_SYMBOL_GPL(spi_bitbang_setup_transfer);
+#define MODEBITS (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH |
SPI_TX_1)
+
/**
* spi_bitbang_setup - default setup for per-word I/O loops
*/
@@ -192,8 +206,11 @@
* just bitbang_txrx_le_cphaX() routines shifting the
other way, and
* some hardware controllers also have this support.
*/
- if ((spi->mode & SPI_LSB_FIRST) != 0)
+ if (spi->mode & ~MODEBITS) {
+ dev_dbg(&spi->dev, "unsupported SPI mode
bits %04x\n",
+ spi->mode
& ~MODEBITS);
return -EINVAL;
+ }
if (!cs) {
cs = kzalloc(sizeof *cs, SLAB_KERNEL);
------------------------------
Message: 10
Date: Mon, 14 May 2007 09:58:48 +1000
From: Steve Bennett <steveb at workware.net.au>
Subject: [uClinux-dev] [PATCH]coldfire qspi driver
To: uclinux-dev at uclinux.org
Message-ID: <4647A638.3030802 at workware.net.au>
Content-Type: text/plain; charset="iso-8859-1"
-------------- next part --------------
This patch adds support for the SPI driver for Freescale Coldfire QSPI
module
in master mode. Tested with the 5282 processor, but should also work with
other
Coldfire variants.
Signed-Off-By: Steve Bennett <steveb at workware.net.au>
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/mtd/devices/m25p80.c
uClinux-dist/linux-2.6.x/drivers/mtd/devices/m25p80.c
--- uClinux-dist.orig/linux-2.6.x/drivers/mtd/devices/m25p80.c 2006-10-09
10:01:47.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/mtd/devices/m25p80.c 2007-05-11
16:12:09.000000000 +1000
@@ -201,6 +201,7 @@
addr = instr->addr;
len = instr->len;
+ qspi_mutex_down("m25p80");
down(&flash->lock);
/* now erase those sectors */
@@ -216,6 +217,7 @@
}
up(&flash->lock);
+ qspi_mutex_up("m25p80");
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
@@ -260,6 +262,7 @@
if (retlen)
*retlen = 0;
+ qspi_mutex_down("m25p80");
down(&flash->lock);
/* Wait till previous write/erase is done. */
@@ -282,6 +285,7 @@
*retlen = m.actual_length - sizeof(flash->command);
up(&flash->lock);
+ qspi_mutex_up("m25p80");
return 0;
}
@@ -323,6 +327,7 @@
t[1].tx_buf = buf;
spi_message_add_tail(&t[1], &m);
+ qspi_mutex_down("m25p80");
down(&flash->lock);
/* Wait until finished previous write command. */
@@ -385,6 +390,7 @@
}
up(&flash->lock);
+ qspi_mutex_up("m25p80");
return 0;
}
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/spi/Kconfig
uClinux-dist/linux-2.6.x/drivers/spi/Kconfig
--- uClinux-dist.orig/linux-2.6.x/drivers/spi/Kconfig 2006-06-19
11:02:15.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/spi/Kconfig 2007-05-11
16:12:05.000000000 +1000
@@ -103,6 +103,15 @@
GPIO lines to provide the SPI bus. This can be used
where
the inbuilt hardware cannot provide the transfer mode,
or
where the board is using non hardware connected pins.
+
+config SPI_COLDFIRE
+ tristate "Coldfire QSPI SPI Master"
+ depends on SPI_MASTER && COLDFIRE && EXPERIMENTAL
+ help
+ SPI driver for Freescale Coldfire QSPI module in master
mode.
+ Tested with the 5282 processor, but should also work
with other
+ Coldfire variants.
+
#
# Add new SPI master controllers in alphabetical order above this line
#
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/spi/Makefile
uClinux-dist/linux-2.6.x/drivers/spi/Makefile
--- uClinux-dist.orig/linux-2.6.x/drivers/spi/Makefile 2006-06-19
11:02:15.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/spi/Makefile 2007-05-11
16:12:05.000000000 +1000
@@ -17,6 +17,7 @@
obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o
obj-$(CONFIG_SPI_S3C24XX_GPIO) +=
spi_s3c24xx_gpio.o
obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
+obj-$(CONFIG_SPI_COLDFIRE) += spi_coldfire.o
obj-$(CONFIG_MCFQSPI) +=
mcf_qspi.o
obj-$(CONFIG_DS1305) +=
DS1305RTC.o
# ... add above this line ...
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/spi/spi.c
uClinux-dist/linux-2.6.x/drivers/spi/spi.c
--- uClinux-dist.orig/linux-2.6.x/drivers/spi/spi.c 2006-11-30
09:27:55.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/spi/spi.c 2007-05-11
16:12:09.000000000 +1000
@@ -25,6 +25,40 @@
#include <linux/cache.h>
#include <linux/spi/spi.h>
+#if 1
+
+EXPORT_SYMBOL(qspi_mutex_down);
+EXPORT_SYMBOL(qspi_mutex_up);
+
+static DECLARE_MUTEX(sem);
+
+ /**
+ * qspi_mutex_down.
+ * get in line for the qspi mutex
+ * the internal kernel calls do not hold the mutex themselves and so
down/up
+ * must be called manually. This introduces a new level of complexity,
+ * but is required, as it may be necessary for some drivers to
+ * hold the mutex through more than one transaction.
+ */
+ void qspi_mutex_down(char *s){
+ //printk( "d:%s", s);
+ down_interruptible(&sem);
+ //printk( "-");
+ }
+
+ /**
+ * qspi_mutex up
+ * signal the qspi mutex.
+ * see qspi_mutex_down
+ */
+ void qspi_mutex_up(char *s){
+ //printk( "%s:", s);
+ up(&sem);
+ //printk( "u\n");
+ }
+
+ #endif
+
/* SPI bustype and spi_master class are registered after board init code
* provides the SPI device tables, ensuring that both are present by the
@@ -360,7 +394,7 @@
if (!dev)
return NULL;
- master = kzalloc(size + sizeof *master, SLAB_KERNEL);
+ master = kzalloc(size + sizeof *master, GFP_KERNEL);
if (!master)
return NULL;
@@ -447,7 +481,9 @@
*/
void spi_unregister_master(struct spi_master *master)
{
- (void) device_for_each_child(master->cdev.dev, NULL,
__unregister);
+ int dummy;
+
+ dummy = device_for_each_child(master->cdev.dev, NULL,
__unregister);
class_device_unregister(&master->cdev);
}
EXPORT_SYMBOL_GPL(spi_unregister_master);
@@ -463,16 +499,20 @@
*/
struct spi_master *spi_busnum_to_master(u16 bus_num)
{
- if (bus_num) {
- char name[8];
- struct kobject *bus;
-
- snprintf(name, sizeof name, "spi%u",
bus_num);
- bus =
kset_find_obj(&spi_master_class.subsys.kset, name);
- if (bus)
- return container_of(bus,
struct spi_master, cdev.kobj);
+ struct class_device *cdev;
+ struct spi_master *master = NULL;
+ struct spi_master *m;
+
+ down(&spi_master_class.sem);
+ list_for_each_entry(cdev, &spi_master_class.children,
node) {
+ m = container_of(cdev, struct spi_master,
cdev);
+ if (m->bus_num == bus_num) {
+ master =
spi_master_get(m);
+ break;
+ }
}
- return NULL;
+ up(&spi_master_class.sem);
+ return master;
}
EXPORT_SYMBOL_GPL(spi_busnum_to_master);
@@ -607,7 +647,7 @@
{
int status;
- buf = kmalloc(SPI_BUFSIZ, SLAB_KERNEL);
+ buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!buf) {
status = -ENOMEM;
goto err0;
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/spi/spi_coldfire.c
uClinux-dist/linux-2.6.x/drivers/spi/spi_coldfire.c
--- uClinux-dist.orig/linux-2.6.x/drivers/spi/spi_coldfire.c 1970-01-01
10:00:00.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/spi/spi_coldfire.c 2007-05-11
16:12:05.000000000 +1000
@@ -0,0 +1,1000 @@
+/****************************************************************************/
+
+/*
+ * coldfire.c - Master QSPI controller for the ColdFire
processors
+ *
+ * (C) Copyright 2005, Intec Automation,
+ * Mike Lavender
(mike at steroidmicros)
+ *
+
+ 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
-------------------------------------------------------------------------
*/
+
+
+/****************************************************************************/
+
+/*
+ * Includes
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+
+#include <asm/delay.h>
+#include <asm/mcfsim.h>
+#include <asm/mcfqspi.h>
+#include <asm/coldfire.h>
+
+MODULE_AUTHOR("Mike Lavender");
+MODULE_DESCRIPTION("ColdFire QSPI Contoller");
+MODULE_LICENSE("GPL");
+
+/****************************************************************************/
+
+/*
+ * Local constants and macros
+ */
+
+#define QSPI_RAM_SIZE 0x10 /* 16 word table
*/
+
+#define QSPI_TRANSMIT_RAM 0x00
+#define QSPI_RECEIVE_RAM 0x10
+#define QSPI_COMMAND_RAM 0x20
+
+#define QSPI_COMMAND 0x7000 /* 15: X =
Continuous CS
+ * 14: 1 = Get BITSE from QMR[BITS]
+ * 13: 1 = Get DT from QDLYR[DTL]
+ * 12: 1 = Get DSK from QDLYR[QCD]
+ * 8-11: XXXX = next 4 bytes for CS
+ * 0-7: 0000 0000 Reserved
+ */
+
+#define QIR_WCEF 0x0008 /* write collison */
+#define QIR_ABRT 0x0004 /* abort */
+#define QIR_SPIF 0x0001 /* finished */
+
+#define QIR_WCEFE 0x0800
+#define QIR_ABRTE 0x0400
+#define QIR_SPIFE 0x0100
+
+#define QIR_WCEFB 0x8000
+#define QIR_ABRTB 0x4000
+#define QIR_ABRTL 0x1000
+
+#define QMR_BITS 0x3C00
+#define QMR_BITS_8 0x2000
+
+#define QCR_CONT 0x8000
+
+#define QDLYR_SPE 0x8000
+
+#define QWR_ENDQP_MASK 0x0F00
+#define QWR_CSIV 0x1000 /* 1 = active low
chip selects */
+
+
+#define START_STATE ((void*)0)
+#define RUNNING_STATE ((void*)1)
+#define DONE_STATE ((void*)2)
+#define ERROR_STATE ((void*)-1)
+
+#define QUEUE_RUNNING 0
+#define QUEUE_STOPPED 1
+
+/****************************************************************************/
+
+/*
+ * Local Data Structures
+ */
+
+struct transfer_state {
+ u32 index;
+ u32 len;
+ void *tx;
+ void *tx_end;
+ void *rx;
+ void *rx_end;
+ char flags;
+#define TRAN_STATE_RX_VOID 0x01
+#define TRAN_STATE_TX_VOID 0x02
+#define TRAN_STATE_WORD_ODD_NUM 0x04
+ u8 cs;
+ u16 void_write_data;
+ unsigned cs_change:1;
+};
+
+typedef struct {
+ unsigned master:1;
+ unsigned dohie:1;
+ unsigned bits:4;
+ unsigned cpol:1;
+ unsigned cpha:1;
+ unsigned baud:8;
+} QMR;
+
+typedef struct {
+ unsigned spe:1;
+ unsigned qcd:7;
+ unsigned dtl:8;
+} QDLYR;
+
+typedef struct {
+ unsigned halt:1;
+ unsigned wren:1;
+ unsigned wrto:1;
+ unsigned csiv:1;
+ unsigned endqp:4;
+ unsigned cptqp:4;
+ unsigned newqp:4;
+} QWR;
+
+
+struct chip_data {
+ union {
+ u16 qmr_val;
+ QMR qmr;
+ };
+ union {
+ u16 qdlyr_val;
+ QDLYR qdlyr;
+ };
+ union {
+ u16 qwr_val;
+ QWR qwr;
+ };
+ u16 void_write_data;
+};
+
+
+struct driver_data {
+ /* Driver model hookup */
+ struct platform_device *pdev;
+
+ /* SPI framework hookup */
+ struct spi_master *master;
+
+ /* Driver message queue */
+ struct workqueue_struct *workqueue;
+ struct work_struct pump_messages;
+ spinlock_t lock;
+ struct list_head queue;
+ int busy;
+ int run;
+
+ /* Message Transfer pump */
+ struct tasklet_struct pump_transfers;
+
+ /* Current message transfer state info */
+ struct spi_message* cur_msg;
+ struct spi_transfer* cur_transfer;
+ struct chip_data *cur_chip;
+ size_t len;
+ void *tx;
+ void *tx_end;
+ void *rx;
+ void *rx_end;
+ char flags;
+#define TRAN_STATE_RX_VOID 0x01
+#define TRAN_STATE_TX_VOID 0x02
+#define TRAN_STATE_WORD_ODD_NUM 0x04
+ u8 cs;
+ u16 void_write_data;
+ unsigned cs_change:1;
+
+ u32 trans_cnt;
+ u32 wce_cnt;
+ u32 abrt_cnt;
+ u16 *qmr; /* QSPI mode register */
+ u16 *qdlyr; /* QSPI delay register */
+ u16 *qwr; /* QSPI wrap register */
+ u16 *qir; /* QSPI interrupt register */
+ u16 *qar; /* QSPI address register */
+ u16 *qdr; /* QSPI data register */
+ u16 *qcr; /* QSPI command register */
+ u8 *par; /* Pin assignment register */
+ u8 *int_icr; /* Interrupt level and
priority register */
+ u32 *int_mr; /* Interrupt mask register */
+ void (*cs_control)(u8 cs, u8 command);
+};
+
+
+
+/****************************************************************************/
+
+/*
+ * SPI local functions
+ */
+
+//#define SPI_COLDFIRE_DEBUG
+
+static void *next_transfer(struct driver_data *drv_data)
+{
+ struct spi_message *msg = drv_data->cur_msg;
+ struct spi_transfer *trans = drv_data->cur_transfer;
+
+ /* Move to next transfer */
+ if (trans->transfer_list.next != &msg->transfers) {
+ drv_data->cur_transfer =
+ list_entry(trans->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ return RUNNING_STATE;
+ } else
+ return DONE_STATE;
+}
+
+static int write(struct driver_data *drv_data)
+{
+ int tx_count = 0;
+ int cmd_count = 0;
+ int tx_word = ((*drv_data->qmr & QMR_BITS) == QMR_BITS_8)
? 0 : 1;
+
+ // If we are in word mode, but only have a single byte to
transfer
+ // then switch to byte mode temporarily. Will switch
back at the
+ // end of the transfer.
+ if (tx_word && ((drv_data->tx_end - drv_data->tx) == 1))
{
+ drv_data->flags |=
TRAN_STATE_WORD_ODD_NUM;
+ *drv_data->qmr |= (*drv_data->qmr &
~QMR_BITS) | QMR_BITS_8;
+ tx_word = 0;
+ }
+
+ *drv_data->qar = QSPI_TRANSMIT_RAM;
+ while ((drv_data->tx < drv_data->tx_end) && (tx_count <
QSPI_RAM_SIZE)) {
+ if (tx_word) {
+ if ((drv_data->tx_end -
drv_data->tx) == 1)
+ break;
+
+ if (!(drv_data->flags &
TRAN_STATE_TX_VOID))
+ *drv_data->qdr = *(u16 *)drv_data->tx;
+ else
+ *drv_data->qdr = drv_data->void_write_data;
+ drv_data->tx += 2;
+ } else {
+ if (!(drv_data->flags &
TRAN_STATE_TX_VOID))
+ *drv_data->qdr = *(u8 *)drv_data->tx;
+ else
+ *drv_data->qdr = *(u8 *)&drv_data->void_write_data;
+ drv_data->tx++;
+ }
+ tx_count++;
+ }
+
+
+ *drv_data->qar = QSPI_COMMAND_RAM;
+ while (cmd_count < tx_count) {
+ u16 qcr = QSPI_COMMAND
+ | QCR_CONT
+ | (~((0x01 <<
drv_data->cs) << 8) & 0x0F00);
+
+ if ( (cmd_count == tx_count
- 1)
+ && (drv_data->tx ==
drv_data->tx_end)
+ && (drv_data->cs_change)
) {
+ qcr &= ~QCR_CONT;
+ }
+ *drv_data->qcr = qcr;
+ cmd_count++;
+ }
+
+ *drv_data->qwr = (*drv_data->qwr & ~QWR_ENDQP_MASK) |
((cmd_count - 1) << 8);
+
+ /* Fire it up! */
+ *drv_data->qdlyr |= QDLYR_SPE;
+
+ return tx_count;
+}
+
+
+static int read(struct driver_data *drv_data)
+{
+ int rx_count = 0;
+ int rx_word = ((*drv_data->qmr & QMR_BITS) == QMR_BITS_8)
? 0 : 1;
+
+ *drv_data->qar = QSPI_RECEIVE_RAM;
+ while ((drv_data->rx < drv_data->rx_end) && (rx_count <
QSPI_RAM_SIZE)) {
+ if (rx_word) {
+ if ((drv_data->rx_end -
drv_data->rx) == 1)
+ break;
+
+ if (!(drv_data->flags &
TRAN_STATE_RX_VOID))
+ *(u16
*)drv_data->rx = *drv_data->qdr;
+ drv_data->rx += 2;
+ } else {
+ if (!(drv_data->flags &
TRAN_STATE_RX_VOID))
+ *(u8
*)drv_data->rx = *drv_data->qdr;
+ drv_data->rx++;
+ }
+ rx_count++;
+ }
+
+ return rx_count;
+}
+
+
+static inline void qspi_setup_chip(struct driver_data *drv_data)
+{
+ struct chip_data *chip = drv_data->cur_chip;
+
+ *drv_data->qmr = chip->qmr_val;
+ *drv_data->qdlyr = chip->qdlyr_val;
+ *drv_data->qwr = chip->qwr_val;
+
+ /*
+ * Enable all the interrupts and clear all the flags
+ */
+ *drv_data->qir = (QIR_SPIFE | QIR_ABRTE | QIR_WCEFE)
+ | (QIR_WCEFB | QIR_ABRTB
| QIR_ABRTL)
+ | (QIR_SPIF | QIR_ABRT |
QIR_WCEF);
+}
+
+
+static irqreturn_t qspi_interrupt(int irq, void *dev_id)
+{
+ struct driver_data *drv_data = (struct driver_data
*)dev_id;
+ struct spi_message *msg = drv_data->cur_msg;
+ u16 irq_status = *drv_data->qir;
+
+ /* Clear all flags immediately */
+ *drv_data->qir |= (QIR_SPIF | QIR_ABRT | QIR_WCEF);
+
+ if (!drv_data->cur_msg || !drv_data->cur_msg->state) {
+ printk(KERN_ERR "coldfire-qspi: bad
message or transfer "
+ "state in interrupt handler\n");
+ return IRQ_NONE;
+ }
+
+ if (irq_status & QIR_SPIF) {
+ /*
+ * Read the data into the buffer and
reload and start
+ * queue with new data if not finished.
If finished
+ * then setup the next transfer
+ */
+ read(drv_data);
+
+ if (drv_data->rx == drv_data->rx_end) {
+ /*
+ * Finished now - fall
through and schedule next
+ * transfer tasklet
+ */
+ if (drv_data->flags &
TRAN_STATE_WORD_ODD_NUM)
+ *drv_data->qmr &= ~QMR_BITS;
+
+ msg->state =
next_transfer(drv_data);
+ msg->actual_length +=
drv_data->len;
+ } else {
+ /* not finished yet -
keep going */
+ write(drv_data);
+ return IRQ_HANDLED;
+ }
+ } else {
+ if (irq_status & QIR_WCEF)
+ drv_data->wce_cnt++;
+
+ if (irq_status & QIR_ABRT)
+ drv_data->abrt_cnt++;
+
+ msg->state = ERROR_STATE;
+ }
+
+ tasklet_schedule(&drv_data->pump_transfers);
+
+ return IRQ_HANDLED;
+}
+
+/* caller already set message->status; dma and pio irqs are blocked */
+static void giveback(struct driver_data *drv_data)
+{
+ struct spi_transfer* last_transfer;
+ unsigned long flags;
+ struct spi_message *msg;
+
+ spin_lock_irqsave(&drv_data->lock, flags);
+ msg = drv_data->cur_msg;
+ drv_data->cur_msg = NULL;
+ drv_data->cur_transfer = NULL;
+ drv_data->cur_chip = NULL;
+ queue_work(drv_data->workqueue,
&drv_data->pump_messages);
+ spin_unlock_irqrestore(&drv_data->lock, flags);
+
+ last_transfer = list_entry(msg->transfers.prev,
+ struct spi_transfer,
+ transfer_list);
+
+ if (!last_transfer->cs_change)
+ drv_data->cs_control(drv_data->cs,
QSPI_CS_DROP);
+
+ msg->state = NULL;
+ if (msg->complete)
+ msg->complete(msg->context);
+}
+
+
+static void pump_transfers(unsigned long data)
+{
+ struct driver_data *drv_data = (struct driver_data
*)data;
+ struct spi_message *message = NULL;
+ struct spi_transfer *transfer = NULL;
+ struct spi_transfer *previous = NULL;
+ struct chip_data *chip = NULL;
+ unsigned long flags;
+
+ /* Get current state information */
+ message = drv_data->cur_msg;
+ transfer = drv_data->cur_transfer;
+ chip = drv_data->cur_chip;
+
+ /* Handle for abort */
+ if (message->state == ERROR_STATE) {
+ message->status = -EIO;
+ giveback(drv_data);
+ return;
+ }
+
+ /* Handle end of message */
+ if (message->state == DONE_STATE) {
+ message->status = 0;
+ giveback(drv_data);
+ return;
+ }
+
+ if (message->state == START_STATE) {
+ qspi_setup_chip(drv_data);
+
+ if (drv_data->cs_control) {
+ //printk( "m s\n" );
+ drv_data->cs_control(message->spi->chip_select, QSPI_CS_ASSERT);
+ }
+ }
+
+ /* Delay if requested at end of transfer*/
+ if (message->state == RUNNING_STATE) {
+ previous =
list_entry(transfer->transfer_list.prev,
+ struct spi_transfer,
+ transfer_list);
+
+ if (drv_data->cs_control &&
transfer->cs_change)
+ drv_data->cs_control(message->spi->chip_select, QSPI_CS_DROP);
+
+ if (previous->delay_usecs)
+ udelay(previous->delay_usecs);
+
+ if (drv_data->cs_control &&
transfer->cs_change)
+ drv_data->cs_control(message->spi->chip_select, QSPI_CS_ASSERT);
+ }
+
+ drv_data->flags = 0;
+ drv_data->tx = (void *)transfer->tx_buf;
+ drv_data->tx_end = drv_data->tx + transfer->len;
+ drv_data->rx = transfer->rx_buf;
+ drv_data->rx_end = drv_data->rx + transfer->len;
+ drv_data->len = transfer->len;
+ if (!drv_data->rx)
+ drv_data->flags |= TRAN_STATE_RX_VOID;
+ if (!drv_data->tx)
+ drv_data->flags |= TRAN_STATE_TX_VOID;
+ drv_data->cs = message->spi->chip_select;
+ drv_data->cs_change = transfer->cs_change;
+ drv_data->void_write_data = chip->void_write_data;
+
+ message->state = RUNNING_STATE;
+
+ /* Go baby, go */
+ local_irq_save(flags);
+ write(drv_data);
+ local_irq_restore(flags);
+}
+
+
+static void pump_messages(void * data)
+{
+ struct driver_data *drv_data = data;
+ unsigned long flags;
+
+ /* Lock queue and check for queue work */
+ spin_lock_irqsave(&drv_data->lock, flags);
+ if (list_empty(&drv_data->queue) || drv_data->run ==
QUEUE_STOPPED) {
+ drv_data->busy = 0;
+ spin_unlock_irqrestore(&drv_data->lock,
flags);
+ return;
+ }
+
+ /* Make sure we are not already running a message */
+ if (drv_data->cur_msg) {
+ spin_unlock_irqrestore(&drv_data->lock,
flags);
+ return;
+ }
+
+ /* Extract head of queue */
+ drv_data->cur_msg = list_entry(drv_data->queue.next,
+ struct spi_message, queue);
+ list_del_init(&drv_data->cur_msg->queue);
+
+ /* Initial message state*/
+ drv_data->cur_msg->state = START_STATE;
+ drv_data->cur_transfer =
list_entry(drv_data->cur_msg->transfers.next,
+ struct spi_transfer,
+ transfer_list);
+
+ /* Setup the SPI Registers using the per chip
configuration */
+ drv_data->cur_chip =
spi_get_ctldata(drv_data->cur_msg->spi);
+
+ /* Mark as busy and launch transfers */
+ tasklet_schedule(&drv_data->pump_transfers);
+
+ drv_data->busy = 1;
+ spin_unlock_irqrestore(&drv_data->lock, flags);
+}
+
+/****************************************************************************/
+
+/*
+ * SPI master implementation
+ */
+
+static int transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct driver_data *drv_data =
spi_master_get_devdata(spi->master);
+ unsigned long flags;
+
+ spin_lock_irqsave(&drv_data->lock, flags);
+
+ if (drv_data->run == QUEUE_STOPPED) {
+ spin_unlock_irqrestore(&drv_data->lock,
flags);
+ return -ESHUTDOWN;
+ }
+
+ msg->actual_length = 0;
+ msg->status = -EINPROGRESS;
+ msg->state = START_STATE;
+
+ list_add_tail(&msg->queue, &drv_data->queue);
+
+ if (drv_data->run == QUEUE_RUNNING && !drv_data->busy)
+ queue_work(drv_data->workqueue,
&drv_data->pump_messages);
+
+ spin_unlock_irqrestore(&drv_data->lock, flags);
+
+ return 0;
+}
+
+
+static int setup(struct spi_device *spi)
+{
+ struct coldfire_spi_chip *chip_info;
+ struct chip_data *chip;
+ u32 baud_divisor = 255;
+
+ chip_info = (struct coldfire_spi_chip
*)spi->controller_data;
+
+ /* Only alloc on first setup */
+ chip = spi_get_ctldata(spi);
+ if (chip == NULL) {
+ chip = kcalloc(1, sizeof(struct
chip_data), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+ spi->mode = chip_info->mode;
+ spi->bits_per_word =
chip_info->bits_per_word;
+ }
+
+ chip->qwr.csiv = 1; // Chip selects are active low
+ chip->qmr.master = 1; // Must set to master mode
+ chip->qmr.dohie = 1; // Data output high impediance
enabled
+ chip->void_write_data = chip_info->void_write_data;
+
+ chip->qdlyr.qcd = chip_info->del_cs_to_clk;
+ chip->qdlyr.dtl = chip_info->del_after_trans;
+
+ if (spi->max_speed_hz != 0)
+ baud_divisor =
(MCF_CLK/(2*spi->max_speed_hz));
+
+ if (baud_divisor < 2)
+ baud_divisor = 2;
+
+ if (baud_divisor > 255)
+ baud_divisor = 255;
+
+ chip->qmr.baud = baud_divisor;
+
+ //printk( "QSPI: spi->max_speed_hz %d\n",
spi->max_speed_hz );
+ //printk( "QSPI: Baud set to %d\n", chip->qmr.baud );
+
+ if (spi->mode & SPI_CPHA)
+ chip->qmr.cpha = 1;
+
+ if (spi->mode & SPI_CPOL)
+ chip->qmr.cpol = 1;
+
+ if (spi->bits_per_word == 16) {
+ chip->qmr.bits = 0;
+ } else if ((spi->bits_per_word >= 8) &&
(spi->bits_per_word <= 15)) {
+ chip->qmr.bits = spi->bits_per_word;
+ } else {
+ printk(KERN_ERR "coldfire-qspi: invalid
wordsize\n");
+ kfree(chip);
+ return -ENODEV;
+ }
+
+ spi_set_ctldata(spi, chip);
+
+ return 0;
+}
+
+static int init_queue(struct driver_data *drv_data)
+{
+ INIT_LIST_HEAD(&drv_data->queue);
+ spin_lock_init(&drv_data->lock);
+
+ drv_data->run = QUEUE_STOPPED;
+ drv_data->busy = 0;
+
+ tasklet_init(&drv_data->pump_transfers,
+ pump_transfers,
(unsigned long)drv_data);
+
+ INIT_WORK(&drv_data->pump_messages, pump_messages,
drv_data);
+ drv_data->workqueue = create_singlethread_workqueue(
+ drv_data->master->cdev.dev->bus_id);
+ if (drv_data->workqueue == NULL)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int start_queue(struct driver_data *drv_data)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&drv_data->lock, flags);
+
+ if (drv_data->run == QUEUE_RUNNING || drv_data->busy) {
+ spin_unlock_irqrestore(&drv_data->lock,
flags);
+ return -EBUSY;
+ }
+
+ drv_data->run = QUEUE_RUNNING;
+ drv_data->cur_msg = NULL;
+ drv_data->cur_transfer = NULL;
+ drv_data->cur_chip = NULL;
+ spin_unlock_irqrestore(&drv_data->lock, flags);
+
+ queue_work(drv_data->workqueue,
&drv_data->pump_messages);
+
+ return 0;
+}
+
+static int stop_queue(struct driver_data *drv_data)
+{
+ unsigned long flags;
+ unsigned limit = 500;
+ int status = 0;
+
+ spin_lock_irqsave(&drv_data->lock, flags);
+
+ /* This is a bit lame, but is optimized for the common
execution path.
+ * A wait_queue on the drv_data->busy could be used, but
then the common
+ * execution path (pump_messages) would be required to
call wake_up or
+ * friends on every SPI message. Do this instead */
+ drv_data->run = QUEUE_STOPPED;
+ while (!list_empty(&drv_data->queue) && drv_data->busy &&
limit--) {
+ spin_unlock_irqrestore(&drv_data->lock,
flags);
+ msleep(10);
+ spin_lock_irqsave(&drv_data->lock,
flags);
+ }
+
+ if (!list_empty(&drv_data->queue) || drv_data->busy)
+ status = -EBUSY;
+
+ spin_unlock_irqrestore(&drv_data->lock, flags);
+
+ return status;
+}
+
+static int destroy_queue(struct driver_data *drv_data)
+{
+ int status;
+
+ status = stop_queue(drv_data);
+ if (status != 0)
+ return status;
+
+ destroy_workqueue(drv_data->workqueue);
+
+ return 0;
+}
+
+
+static void cleanup(const struct spi_device *spi)
+{
+ struct chip_data *chip = spi_get_ctldata((struct
spi_device *)spi);
+
+ dev_dbg(&spi->dev, "spi_device %u.%u cleanup\n",
+ spi->master->bus_num, spi->chip_select);
+
+ kfree(chip);
+}
+
+
+/****************************************************************************/
+
+/*
+ * Generic Device driver routines and interface implementation
+ */
+
+static int coldfire_spi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct coldfire_spi_master *platform_info;
+ struct spi_master *master;
+ struct driver_data *drv_data = 0;
+ struct resource *memory_resource;
+ int irq;
+ int status = 0;
+ int i;
+
+ platform_info = (struct coldfire_spi_master
*)pdev->dev.platform_data;
+
+ master = spi_alloc_master(dev, sizeof(struct
driver_data));
+ if (!master)
+ return -ENOMEM;
+
+ drv_data = class_get_devdata(&master->cdev);
+ drv_data->master = master;
+
+ INIT_LIST_HEAD(&drv_data->queue);
+ spin_lock_init(&drv_data->lock);
+
+ master->bus_num = platform_info->bus_num;
+ master->num_chipselect = platform_info->num_chipselect;
+ master->cleanup = cleanup;
+ master->setup = setup;
+ master->transfer = transfer;
+
+
+ drv_data->cs_control = platform_info->cs_control;
+ if (drv_data->cs_control)
+ for(i = 0; i < master->num_chipselect;
i++)
+ drv_data->cs_control(i,
QSPI_CS_INIT | QSPI_CS_DROP);
+
+ /* Setup register addresses */
+ memory_resource = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "qspi-module");
+ if (!memory_resource) {
+ dev_dbg(dev, "can not find platform
module memory\n");
+ goto out_error_master_alloc;
+ }
+
+ drv_data->qmr = (void *)(memory_resource->start +
0x00000000);
+ drv_data->qdlyr = (void *)(memory_resource->start +
0x00000004);
+ drv_data->qwr = (void *)(memory_resource->start +
0x00000008);
+ drv_data->qir = (void *)(memory_resource->start +
0x0000000c);
+ drv_data->qar = (void *)(memory_resource->start +
0x00000010);
+ drv_data->qdr = (void *)(memory_resource->start +
0x00000014);
+ drv_data->qcr = (void *)(memory_resource->start +
0x00000014);
+
+ /* Setup register addresses */
+ memory_resource = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "qspi-par");
+ if (!memory_resource) {
+ dev_dbg(dev, "can not find platform par
memory\n");
+ goto out_error_master_alloc;
+ }
+
+ drv_data->par = (void *)memory_resource->start;
+
+ /* Setup register addresses */
+ memory_resource = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "qspi-int-level");
+ if (!memory_resource) {
+ dev_dbg(dev, "can not find platform par
memory\n");
+ goto out_error_master_alloc;
+ }
+
+ drv_data->int_icr = (void *)memory_resource->start;
+
+ /* Setup register addresses */
+ memory_resource = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "qspi-int-mask");
+ if (!memory_resource) {
+ dev_dbg(dev, "can not find platform par
memory\n");
+ goto out_error_master_alloc;
+ }
+
+ drv_data->int_mr = (void *)memory_resource->start;
+
+ status = request_irq(platform_info->irq_vector,
qspi_interrupt, SA_INTERRUPT, dev->bus_id, drv_data);
+ if (status < 0) {
+ dev_err(&pdev->dev, "unable to attach
ColdFire QSPI interrupt\n");
+ goto out_error_master_alloc;
+ }
+
+ /* Now that we have all the addresses etc. Let's set it up */
+ *drv_data->par = platform_info->par_val;
+ *drv_data->int_icr = platform_info->irq_lp;
+ *drv_data->int_mr &= ~platform_info->irq_mask;
+
+ /* Initial and start queue */
+ status = init_queue(drv_data);
+ if (status != 0) {
+ dev_err(&pdev->dev, "problem initializing
queue\n");
+ goto out_error_irq_alloc;
+ }
+ status = start_queue(drv_data);
+ if (status != 0) {
+ dev_err(&pdev->dev, "problem starting
queue\n");
+ goto out_error_irq_alloc;
+ }
+
+ /* Register with the SPI framework */
+ platform_set_drvdata(pdev, drv_data);
+ status = spi_register_master(master);
+ if (status != 0) {
+ dev_err(&pdev->dev, "problem registering
spi master\n");
+ status = -EINVAL;
+ goto out_error_queue_alloc;
+ }
+
+ printk( "SPI: Coldfire master initialized\n" );
+ //dev_info(&pdev->dev, "driver initialized\n");
+ return status;
+
+out_error_queue_alloc:
+ destroy_queue(drv_data);
+
+out_error_irq_alloc:
+ free_irq(irq, drv_data);
+
+out_error_master_alloc:
+ spi_master_put(master);
+ return status;
+
+}
+
+static int coldfire_spi_remove(struct platform_device *pdev)
+{
+ struct driver_data *drv_data =
platform_get_drvdata(pdev);
+ int irq;
+ int status = 0;
+
+ if (!drv_data)
+ return 0;
+
+ /* Remove the queue */
+ status = destroy_queue(drv_data);
+ if (status != 0)
+ return status;
+
+ /* Disable the SSP at the peripheral and SOC level */
+ /*write_SSCR0(0, drv_data->ioaddr);
+ pxa_set_cken(drv_data->master_info->clock_enable, 0);*/
+
+ /* Release DMA */
+ /*if (drv_data->master_info->enable_dma) {
+ if (drv_data->ioaddr == SSP1_VIRT) {
+ DRCMRRXSSDR = 0;
+ DRCMRTXSSDR = 0;
+ } else if (drv_data->ioaddr == SSP2_VIRT)
{
+ DRCMRRXSS2DR = 0;
+ DRCMRTXSS2DR = 0;
+ } else if (drv_data->ioaddr == SSP3_VIRT)
{
+ DRCMRRXSS3DR = 0;
+ DRCMRTXSS3DR = 0;
+ }
+ pxa_free_dma(drv_data->tx_channel);
+ pxa_free_dma(drv_data->rx_channel);
+ }*/
+
+ /* Release IRQ */
+ irq = platform_get_irq(pdev, 0);
+ if (irq >= 0)
+ free_irq(irq, drv_data);
+
+ /* Disconnect from the SPI framework */
+ spi_unregister_master(drv_data->master);
+
+ /* Prevent double remove */
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static void coldfire_spi_shutdown(struct platform_device *pdev)
+{
+ int status = 0;
+
+ if ((status = coldfire_spi_remove(pdev)) != 0)
+ dev_err(&pdev->dev, "shutdown failed with
%d\n", status);
+}
+
+
+#ifdef CONFIG_PM
+static int suspend_devices(struct device *dev, void *pm_message)
+{
+ pm_message_t *state = pm_message;
+
+ if (dev->power.power_state.event != state->event) {
+ dev_warn(dev, "pm state does not match
request\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int coldfire_spi_suspend(struct platform_device *pdev,
pm_message_t state)
+{
+ struct driver_data *drv_data =
platform_get_drvdata(pdev);
+ int status = 0;
+
+ /* Check all childern for current power state */
+ if (device_for_each_child(&pdev->dev, &state,
suspend_devices) != 0) {
+ dev_warn(&pdev->dev, "suspend
aborted\n");
+ return -1;
+ }
+
+ status = stop_queue(drv_data);
+ if (status != 0)
+ return status;
+ /*write_SSCR0(0, drv_data->ioaddr);
+ pxa_set_cken(drv_data->master_info->clock_enable, 0);*/
+
+ return 0;
+}
+
+static int coldfire_spi_resume(struct platform_device *pdev)
+{
+ struct driver_data *drv_data =
platform_get_drvdata(pdev);
+ int status = 0;
+
+ /* Enable the SSP clock */
+ /*pxa_set_cken(drv_data->master_info->clock_enable, 1);*/
+
+ /* Start the queue running */
+ status = start_queue(drv_data);
+ if (status != 0) {
+ dev_err(&pdev->dev, "problem starting
queue (%d)\n", status);
+ return status;
+ }
+
+ return 0;
+}
+#else
+#define coldfire_spi_suspend NULL
+#define coldfire_spi_resume NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver driver = {
+ .driver = {
+ .name = "spi_coldfire",
+ .bus = &platform_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = coldfire_spi_probe,
+ .remove = __devexit_p(coldfire_spi_remove),
+ .shutdown = coldfire_spi_shutdown,
+ .suspend = coldfire_spi_suspend,
+ .resume = coldfire_spi_resume,
+};
+
+static int __init coldfire_spi_init(void)
+{
+ platform_driver_register(&driver);
+
+ return 0;
+}
+module_init(coldfire_spi_init);
+
+static void __exit coldfire_spi_exit(void)
+{
+ platform_driver_unregister(&driver);
+}
+module_exit(coldfire_spi_exit);
diff -urN uClinux-dist.orig/linux-2.6.x/include/asm-m68knommu/mcfqspi.h
uClinux-dist/linux-2.6.x/include/asm-m68knommu/mcfqspi.h
--- uClinux-dist.orig/linux-2.6.x/include/asm-m68knommu/mcfqspi.h
2006-12-12 23:16:12.000000000 +1000
+++ uClinux-dist/linux-2.6.x/include/asm-m68knommu/mcfqspi.h 2007-05-11
16:12:05.000000000 +1000
@@ -1,30 +1,52 @@
-#if !defined(MCFQSPI_H)
-#define MCFQSPI_H
+/****************************************************************************/
-#include <linux/types.h>
+/*
+ * mcfqspi.c - Master QSPI controller for the ColdFire
processors
+ *
+ * (C) Copyright 2005, Intec Automation,
+ * Mike Lavender
(mike at steroidmicros)
+ *
+
+ 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.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+/*
-------------------------------------------------------------------------
*/
+
+#ifndef MCFQSPI_H_
+#define MCFQSPI_H_
+
+#define QSPI_CS_INIT 0x01
+#define QSPI_CS_ASSERT 0x02
+#define QSPI_CS_DROP 0x04
+
+struct coldfire_spi_master {
+ u16 bus_num;
+ u16 num_chipselect;
+ u8 irq_source;
+ u32 irq_vector;
+ u32 irq_mask;
+ u8 irq_lp;
+ u8 par_val;
+ void (*cs_control)(u8 cs, u8 command);
+};
+
+
+struct coldfire_spi_chip {
+ u8 mode;
+ u8 bits_per_word;
+ u8 del_cs_to_clk;
+ u8 del_after_trans;
+ u16 void_write_data;
+};
-
-#define QSPIIOCS_DOUT_HIZ 1 /* QMR[DOHIE] set hi-z dout
between transfers */
-#define QSPIIOCS_BITS 2 /* QMR[BITS] set transfer size */
-#define QSPIIOCG_BITS 3 /* QMR[BITS] get transfer size */
-#define QSPIIOCS_CPOL 4 /* QMR[CPOL] set SCK inactive
state */
-#define QSPIIOCS_CPHA 5 /* QMR[CPHA] set SCK phase,
1=rising edge */
-#define QSPIIOCS_BAUD 6 /* QMR[BAUD] set SCK baud rate
divider */
-#define QSPIIOCS_QCD 7 /* QDLYR[QCD] set start delay */
-#define QSPIIOCS_DTL 8 /* QDLYR[DTL] set after delay */
-#define QSPIIOCS_CONT 9 /* continuous CS asserted during
transfer */
-#define QSPIIOCS_READDATA 10 /* set data send during read */
-#define QSPIIOCS_ODD_MOD 11 /* if length of buffer is a odd
number, 16-bit transfers */
- /* are finalized with a 8-bit
transfer */
-#define QSPIIOCS_DSP_MOD 12 /* transfers are bounded to 15/30
bytes (a multiple of 3 bytes = 1 DSP word) */
-#define QSPIIOCS_POLL_MOD 13 /* driver uses polling instead of
interrupts */
-
-
-typedef struct qspi_read_data {
- __u32 length;
- __u8 *buf; /* data to send during read */
- unsigned int loop : 1;
-} qspi_read_data;
-
-
-#endif /* MCFQSPI_H */
+#endif /*MCFQSPI_H_*/
------------------------------
Message: 11
Date: Mon, 14 May 2007 09:59:43 +1000
From: Steve Bennett <steveb at workware.net.au>
Subject: [uClinux-dev] [PATCH]Support MMC/SD cards over SPI
To: uclinux-dev at uclinux.org
Message-ID: <4647A66F.9060803 at workware.net.au>
Content-Type: text/plain; charset="iso-8859-1"
-------------- next part --------------
This patch adds support for MMC/SD access over SPI
Signed-Off-By: Steve Bennett <steveb at workware.net.au>
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/mmc/Kconfig
uClinux-dist/linux-2.6.x/drivers/mmc/Kconfig
--- uClinux-dist.orig/linux-2.6.x/drivers/mmc/Kconfig 2006-11-30
09:27:54.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/mmc/Kconfig 2007-05-11
16:12:05.000000000 +1000
@@ -123,6 +123,17 @@
(TIFM_7XX1)'.
To compile this driver as a module, choose M here: the
- module will be called tifm_sd.
+ module will be called tifm_sd
+
+config MMC_SPI
+ tristate "MMC/SD over SPI"
+ depends on MMC && SPI && EXPERIMENTAL
+ help
+ Some systems accss MMC/SD cards using the SPI protocol
instead of
+ using an MMC/SD controller. The disadvantage of using
SPI is that
+ it's often not as fast; its compensating advantage is
that SPI is
+ available on many systems without MMC/SD controllers.
+
+ If unsure, or if your system has no SPI controller
driver, say N.
endmenu
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/mmc/Makefile
uClinux-dist/linux-2.6.x/drivers/mmc/Makefile
--- uClinux-dist.orig/linux-2.6.x/drivers/mmc/Makefile 2006-11-30
09:27:54.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/mmc/Makefile 2007-05-11
16:12:05.000000000 +1000
@@ -24,6 +24,7 @@
obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91RM9200) += at91_mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
+obj-$(CONFIG_MMC_SPI) += mmc_spi.o
mmc_core-y := mmc.o mmc_sysfs.o
mmc_core-$(CONFIG_BLOCK) += mmc_queue.o
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/mmc/mmc_block.c
uClinux-dist/linux-2.6.x/drivers/mmc/mmc_block.c
--- uClinux-dist.orig/linux-2.6.x/drivers/mmc/mmc_block.c 2006-11-30
09:27:54.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/mmc/mmc_block.c 2007-05-11
16:12:05.000000000 +1000
@@ -254,9 +254,9 @@
* this rule as they support querying the
number of
* successfully written sectors.
*/
- if (rq_data_dir(req) != READ &&
- !(card->host->caps &
MMC_CAP_MULTIWRITE) &&
- !mmc_card_sd(card))
+ //if (rq_data_dir(req) != READ &&
+ // !(card->host->caps &
MMC_CAP_MULTIWRITE) &&
+ // !mmc_card_sd(card))
brq.data.blocks = 1;
if (brq.data.blocks > 1) {
diff -urN uClinux-dist.orig/linux-2.6.x/drivers/mmc/mmc_spi.c
uClinux-dist/linux-2.6.x/drivers/mmc/mmc_spi.c
--- uClinux-dist.orig/linux-2.6.x/drivers/mmc/mmc_spi.c 1970-01-01
10:00:00.000000000 +1000
+++ uClinux-dist/linux-2.6.x/drivers/mmc/mmc_spi.c 2007-05-11
16:12:09.000000000 +1000
@@ -0,0 +1,1526 @@
+/*
+ * mmc_spi.c - Access an SD/MMC card using the SPI bus
+ *
+ * (C) Copyright 2005, Intec Automation,
+ * Mike Lavender (mike at steroidmicros)
+ * (C) Copyright 2006, David Brownell
+ * (C) Copyright 2007, Axis Communications,
+ * Hans-Peter Nilsson (hp at axis.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/autoconf.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/mmc_spi.h>
+
+
+/* NOTES:
+ *
+ * - For now, we won't try to interoperate with a real mmc/sd/sdio
+ * controller. The main reason for such configs would be mmc-format
+ * cards which (like dataflash) don't support that "other" protocol.
+ * SPI mode is a bit slower than non-parallel versions of MMC.
+ *
+ * - Likewise we don't try to detect dataflash cards, which would
+ * imply switching to a different driver. Not many folk folk use
+ * both dataflash cards and MMC/SD cards, and Linux doesn't have
+ * an "MMC/SD interface" abstraction for coupling to drivers.
+ *
+ * - This version gets part way through enumeration of MMC cards.
+ *
+ * - Protocol details, including timings, need to be audited
+ *
+ * - A "use CRCs" option would probably be useful.
+ */
+
+
+/*
+ * Local defines
+ */
+
+// MOVE TO <linux/mmc/protocol.h> ?
+#define SPI_MMC_COMMAND 0x40 /* mask into
mmc command */
+
+/* class 0 */
+#define SPI_MMC_READ_OCR 58 /* R3, SPI-only
*/
+#define SPI_MMC_CRC_ON_OFF 59 /* SPI-only */
+
+/* R1 response status to almost all commands */
+#define SPI_MMC_R1_IDLE 0x01
+#define SPI_MMC_R1_ERASE_RESET 0x02
+#define SPI_MMC_R1_ILLEGAL_COMMAND 0x04
+#define SPI_MMC_R1_COM_CRC 0x08
+#define SPI_MMC_R1_ERASE_SEQ 0x10
+#define SPI_MMC_R1_ADDRESS 0x20
+#define SPI_MMC_R1_PARAMETER 0x40
+
+/* R2 response to CMD13 (SEND_STATUS) is an R1 plus a high byte */
+#define SPI_MMC_R2_CARD_LOCKED 0x01
+#define SPI_MMC_R2_WP_ERASE_SKIP 0x02
+#define SPI_MMC_R2_ERROR 0x04
+#define SPI_MMC_R2_CC_ERROR 0x08
+#define SPI_MMC_R2_CARD_ECC_ERROR 0x10
+#define SPI_MMC_R2_WP_VIOLATION 0x20
+#define SPI_MMC_R2_ERASE_PARAM 0x40
+#define SPI_MMC_R2_OUT_OF_RANGE 0x80
+
+/* response tokens used to ack each block written: */
+#define SPI_MMC_RESPONSE_CODE(x) ((x) & (7 << 1))
+#define SPI_RESPONSE_ACCEPTED (2 << 1)
+#define SPI_RESPONSE_CRC_ERR (5 << 1)
+#define SPI_RESPONSE_WRITE_ERR (6 << 1)
+
+/* read and write blocks start with these tokens and end with crc;
+ * on error, read tokens act like SPI_MMC_R2 values.
+ */
+#define SPI_TOKEN_SINGLE 0xfe /* single block
r/w, multiblock read */
+#define SPI_TOKEN_MULTI_WRITE 0xfc /* multiblock
write */
+#define SPI_TOKEN_STOP_TRAN 0xfd /* terminate
multiblock write */
+// END MOVE
+
+
+#define NO_ARG 0x00000000 // No
argument all 0's
+
+#define CRC_GO_IDLE_STATE 0x95
+#define CRC_NO_CRC 0x01
+
+#define MMC_POWERCYCLE_MSECS 20 /*
board-specific? */
+
+
+/* The unit for these timeouts is milliseconds. See mmc_spi_scanbyte. */
+#define MINI_TIMEOUT 1
+#define READ_TIMEOUT 100
+#define WRITE_TIMEOUT 250
+
+
+/****************************************************************************/
+
+/*
+ * Local Data Structures
+ */
+
+union mmc_spi_command {
+ u8 buf[7];
+ struct {
+ u8 dummy;
+ u8 code;
+ u8 addr1;
+ u8 addr2;
+ u8 addr3;
+ u8 addr4;
+ u8 crc;
+ } command;
+};
+
+
+struct mmc_spi_host {
+ struct mmc_host *mmc;
+ struct spi_device *spi;
+ u8 *rx_buf;
+ u8 *tx_buf;
+ u32 tx_idx;
+ u32 rx_idx;
+ u8 cid_sequence;
+ u8 rsp_type;
+ u8 app_cmd;
+
+ struct mmc_spi_platform_data *pdata;
+
+ /* for bulk data transfers */
+ struct spi_transfer token, t, crc;
+ struct spi_message m;
+ struct spi_transfer early_status;
+
+ /* for status readback */
+ struct spi_transfer status;
+ struct spi_message readback;
+
+ /* underlying controller might support dma, but we can't
+ * rely on it being used for any particular request
+ */
+ struct device *dma_dev;
+ dma_addr_t dma; /* of
mmc_spi_host */
+
+ /* pre-allocated dma-safe buffers */
+ union mmc_spi_command command;
+ u8 data_token;
+ u8 status_byte;
+ u16 crc_val;
+ u8 response[2];
+ u8 bundled_status[2];
+
+ /* specs describe always writing ones even if we
+ * don't think the card should care what it sees.
+ * (Unused if the spi controller can specify default tx
data.)
+ */
+ u8 ones[];
+#define ONES_BUFFER_SIZE 512
+};
+
+#ifdef DEBUG
+static unsigned debug = 1;
+module_param(debug, uint, 0644);
+#else
+#define debug 0
+#endif
+
+/**** Mike Lavender Add - NO DAM support ****/
+#ifndef CONFIG_PCI
+#define dma_unmap_single(w,x,y,z)
+#define dma_map_single(w,x,y,z) 0
+#define dma_unmap_page(w,x,y,z)
+#define dma_map_page(v,w,x,y,z) 0
+#define dma_sync_single_for_device(w,x,y,z)
+#define dma_sync_single_for_cpu(w,x,y,z)
+#endif
+
+/****************************************************************************/
+
+static inline int mmc_spi_readbyte(struct mmc_spi_host *host)
+{
+ int status = spi_sync(host->spi, &host->readback);
+ if (status < 0)
+ return status;
+ return host->status_byte;
+}
+
+static inline int
+mmc_spi_readbytes(struct mmc_spi_host *host, void *bytes, unsigned len)
+{
+ int status;
+
+ host->status.rx_buf = bytes;
+ host->status.len = len;
+
+ host->readback.is_dma_mapped = 0;
+ status = spi_sync(host->spi, &host->readback);
+ host->readback.is_dma_mapped = 1;
+
+ host->status.rx_buf = &host->status_byte;
+ host->status.len = 1;
+ return status;
+}
+
+
+/* REVISIT: is this fast enough? these kinds of sync points could
easily
+ * be offloaded to irq-ish code called by controller drivers, eliminating
+ * context switch costs.
+ *
+ * REVISIT: after writes and erases, mmc_spi_busy() == true might be a
+ * fair hint to yield exclusive access to the card (so another driver can
+ * use the bus) and msleep if busy-waiting doesn't succeed quickly.
+ * Measurements on half a dozen cards show however that a simple
+ * implementation doing msleep(1) every 100 busy-iterations (when
+ * busy, increment and test a static variable, reset it after the
+ * msleep) doesn't provide any consistent speedup or increased
+ * user-level system performance (less load).
+ */
+static int mmc_spi_busy(u8 byte)
+{
+ return byte == 0;
+}
+
+static int mmc_spi_delayed(u8 byte)
+{
+ return byte == 0xff;
+}
+
+static int
+mmc_spi_scanbyte(struct mmc_spi_host *host, int (*fail)(u8), unsigned
delay)
+{
+ int value;
+ unsigned wait;
+
+ /*
+ * Because we might (we will, for bitbanged SPI) be
scheduled
+ * out for extensive periods in this call, we'd get an
+ * abundance of timeouts if we counted in jiffies on a
system
+ * with load, so instead we calculate it in the max
number of
+ * bytes we could theoretically scan before the timeout,
if
+ * everything else took zero time.
+ */
+ unsigned long end_wait = delay *
host->spi->max_speed_hz / 1000 / 8;
+
+ for (wait = 0; wait < end_wait; wait++) {
+ value = mmc_spi_readbyte(host);
+ if (value < 0)
+ return value;
+ if (!fail(value)) {
+ if (debug > 1)
+ dev_dbg(&host->spi->dev,
+ " mmc_spi: token %02x, wait %d\n",
+ value, wait);
+ return value;
+ }
+ }
+
+ return -ETIMEDOUT;
+}
+
+static inline void mmc_spi_map_r1(struct mmc_command *cmd, u8 r1)
+{
+ u32 mapped = 0;
+
+ /* spi mode doesn't expose the mmc/sd state machine, but
+ * we can at least avoid lying about the IDLE state
+ */
+ if (!(r1 & SPI_MMC_R1_IDLE))
+ mapped |= (3 /*standby*/ << 9);
+
+ if (r1 & (SPI_MMC_R1_ERASE_RESET
+ | SPI_MMC_R1_ERASE_SEQ
+ | SPI_MMC_R1_ADDRESS
+ | SPI_MMC_R1_PARAMETER))
{
+ cmd->error = MMC_ERR_FAILED;
+ if (r1 & SPI_MMC_R1_ERASE_RESET)
+ mapped |= R1_ERASE_RESET;
+ if (r1 & SPI_MMC_R1_ERASE_SEQ)
+ mapped |=
R1_ERASE_SEQ_ERROR;
+ if (r1 & SPI_MMC_R1_ADDRESS)
+ mapped |=
R1_ADDRESS_ERROR;
+ /* this one's a loose match... */
+ if (r1 & SPI_MMC_R1_PARAMETER)
+ mapped |=
R1_BLOCK_LEN_ERROR;
+ }
+ if (r1 & SPI_MMC_R1_ILLEGAL_COMMAND) {
+ cmd->error = MMC_ERR_INVALID;
+ mapped |= R1_ILLEGAL_COMMAND;
+ }
+ if (r1 & SPI_MMC_R1_COM_CRC) {
+ cmd->error = MMC_ERR_BADCRC;
+ mapped |= R1_COM_CRC_ERROR;
+ }
+
+ cmd->resp[0] = mapped;
+}
+
+static void mmc_spi_map_r2(struct mmc_command *cmd, u8 r2)
+{
+ u32 mapped = 0;
+
+ if (!r2)
+ return;
+
+ if (r2 & SPI_MMC_R2_CARD_LOCKED)
+ mapped |= R1_CARD_IS_LOCKED;
+ if (r2 & SPI_MMC_R2_WP_ERASE_SKIP)
+ mapped |= R1_WP_ERASE_SKIP;
+ if (r2 & SPI_MMC_R2_ERROR)
+ mapped |= R1_ERROR;
+ if (r2 & SPI_MMC_R2_CC_ERROR)
+ mapped |= R1_CC_ERROR;
+ if (r2 & SPI_MMC_R2_CARD_ECC_ERROR)
+ mapped |= R1_CARD_ECC_FAILED;
+ if (r2 & SPI_MMC_R2_WP_VIOLATION)
+ mapped |= R1_WP_VIOLATION;
+ if (r2 & SPI_MMC_R2_ERASE_PARAM)
+ mapped |= R1_ERASE_PARAM;
+ if (r2 & SPI_MMC_R2_OUT_OF_RANGE)
+ mapped |= R1_OUT_OF_RANGE |
R1_CID_CSD_OVERWRITE;
+
+ if (mapped) {
+ cmd->resp[0] |= mapped;
+ if (cmd->error == MMC_ERR_NONE)
+ cmd->error =
MMC_ERR_FAILED;
+ }
+}
+
+#ifdef DEBUG
+static char *maptype(u8 type)
+{
+ switch (type) {
+ case MMC_RSP_R1: return "R1";
+ case MMC_RSP_R1B: return "R1B";
+ case MMC_RSP_R2: return "R2";
+ case MMC_RSP_R3: return "R3";
+ case MMC_RSP_NONE: return "NONE";
+ default: return "?";
+ }
+}
+#endif
+
+static void
+mmc_spi_response_get(struct mmc_spi_host *host, struct mmc_command *cmd)
+{
+ int value;
+
+ dev_dbg(&host->spi->dev,
+ "%sCMD%d response SPI_%s: ",
+ host->app_cmd ? "A" : "",
+ cmd->opcode, maptype(host->rsp_type));
+
+ if (cmd->opcode == MMC_STOP_TRANSMISSION) {
+ /*
+ * We can't tell whether we read block
data or the
+ * command reply, so to cope with trash
data during
+