diff options
-rw-r--r-- | board/amcc/taihu/taihu.c | 16 | ||||
-rw-r--r-- | board/freescale/mpc8349emds/mpc8349emds.c | 25 | ||||
-rw-r--r-- | board/sacsng/sacsng.c | 35 | ||||
-rw-r--r-- | board/ssv/adnpesc1/adnpesc1.c | 27 | ||||
-rw-r--r-- | common/cmd_df.c | 37 | ||||
-rw-r--r-- | common/cmd_spi.c | 42 | ||||
-rw-r--r-- | common/soft_spi.c | 124 | ||||
-rw-r--r-- | cpu/nios/spi.c | 79 | ||||
-rw-r--r-- | drivers/rtc/ds1306.c | 67 | ||||
-rw-r--r-- | drivers/rtc/mc13783-rtc.c | 43 | ||||
-rw-r--r-- | drivers/spi/mpc8xxx_spi.c | 54 | ||||
-rw-r--r-- | drivers/spi/mxc_spi.c | 88 | ||||
-rw-r--r-- | include/configs/imx31_litekit.h | 3 | ||||
-rw-r--r-- | include/configs/mx31ads.h | 3 | ||||
-rw-r--r-- | include/spi.h | 150 |
15 files changed, 584 insertions, 209 deletions
diff --git a/board/amcc/taihu/taihu.c b/board/amcc/taihu/taihu.c index eedde597b81..891b4d92498 100644 --- a/board/amcc/taihu/taihu.c +++ b/board/amcc/taihu/taihu.c @@ -165,16 +165,20 @@ unsigned char spi_read(void) return (unsigned char)gpio_read_in_bit(SPI_DIN_GPIO15); } -void taihu_spi_chipsel(int cs) +int spi_cs_is_valid(unsigned int bus, unsigned int cs) { - gpio_write_bit(SPI_CS_GPIO0, cs); + return bus == 0 && cs == 0; } -spi_chipsel_type spi_chipsel[]= { - taihu_spi_chipsel -}; +void spi_cs_activate(struct spi_slave *slave) +{ + gpio_write_bit(SPI_CS_GPIO0, 1); +} -int spi_chipsel_cnt = sizeof(spi_chipsel) / sizeof(spi_chipsel[0]); +void spi_cs_deactivate(struct spi_slave *slave) +{ + gpio_write_bit(SPI_CS_GPIO0, 0); +} #ifdef CONFIG_PCI static unsigned char int_lines[32] = { diff --git a/board/freescale/mpc8349emds/mpc8349emds.c b/board/freescale/mpc8349emds/mpc8349emds.c index 6c825969d38..e18e68e8cec 100644 --- a/board/freescale/mpc8349emds/mpc8349emds.c +++ b/board/freescale/mpc8349emds/mpc8349emds.c @@ -257,25 +257,24 @@ void sdram_init(void) #define SPI_CS_MASK 0x80000000 -void spi_eeprom_chipsel(int cs) +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus == 0 && cs == 0; +} + +void spi_cs_activate(struct spi_slave *slave) { volatile gpio83xx_t *iopd = &((immap_t *)CFG_IMMR)->gpio[0]; - if (cs) - iopd->dat &= ~SPI_CS_MASK; - else - iopd->dat |= SPI_CS_MASK; + iopd->dat &= ~SPI_CS_MASK; } -/* - * The SPI command uses this table of functions for controlling the SPI - * chip selects. - */ -spi_chipsel_type spi_chipsel[] = { - spi_eeprom_chipsel, -}; -int spi_chipsel_cnt = sizeof(spi_chipsel) / sizeof(spi_chipsel[0]); +void spi_cs_deactivate(struct spi_slave *slave) +{ + volatile gpio83xx_t *iopd = &((immap_t *)CFG_IMMR)->gpio[0]; + iopd->dat |= SPI_CS_MASK; +} #endif /* CONFIG_HARD_SPI */ #if defined(CONFIG_OF_BOARD_SETUP) diff --git a/board/sacsng/sacsng.c b/board/sacsng/sacsng.c index 25209e05464..e85a0fc4dbe 100644 --- a/board/sacsng/sacsng.c +++ b/board/sacsng/sacsng.c @@ -842,37 +842,30 @@ void show_boot_progress (int status) #define SPI_ADC_CS_MASK 0x00000800 #define SPI_DAC_CS_MASK 0x00001000 -void spi_adc_chipsel(int cs) +static const u32 cs_mask[] = { + SPI_ADC_CS_MASK, + SPI_DAC_CS_MASK, +}; + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus == 0 && cs < sizeof(cs_mask) / sizeof(cs_mask[0]); +} + +void spi_cs_activate(struct spi_slave *slave) { volatile ioport_t *iopd = ioport_addr((immap_t *)CFG_IMMR, 3 /* port D */); - if(cs) - iopd->pdat &= ~SPI_ADC_CS_MASK; /* activate the chip select */ - else - iopd->pdat |= SPI_ADC_CS_MASK; /* deactivate the chip select */ + iopd->pdat &= ~cs_mask[slave->cs]; } -void spi_dac_chipsel(int cs) +void spi_cs_deactivate(struct spi_slave *slave) { volatile ioport_t *iopd = ioport_addr((immap_t *)CFG_IMMR, 3 /* port D */); - if(cs) - iopd->pdat &= ~SPI_DAC_CS_MASK; /* activate the chip select */ - else - iopd->pdat |= SPI_DAC_CS_MASK; /* deactivate the chip select */ + iopd->pdat |= cs_mask[slave->cs]; } -/* - * The SPI command uses this table of functions for controlling the SPI - * chip selects: it calls the appropriate function to control the SPI - * chip selects. - */ -spi_chipsel_type spi_chipsel[] = { - spi_adc_chipsel, - spi_dac_chipsel -}; -int spi_chipsel_cnt = sizeof(spi_chipsel) / sizeof(spi_chipsel[0]); - #endif #endif /* CONFIG_MISC_INIT_R */ diff --git a/board/ssv/adnpesc1/adnpesc1.c b/board/ssv/adnpesc1/adnpesc1.c index 2ec3a728d74..3ee8ba588dc 100644 --- a/board/ssv/adnpesc1/adnpesc1.c +++ b/board/ssv/adnpesc1/adnpesc1.c @@ -69,25 +69,24 @@ long int initdram (int board_type) #define SPI_RTC_CS_MASK 0x00000001 -void spi_rtc_chipsel(int cs) +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus == 0 && cs == 0; +} + +void spi_cs_activate(struct spi_slave *slave) { nios_spi_t *spi = (nios_spi_t *)CFG_NIOS_SPIBASE; - if (cs) - spi->slaveselect = SPI_RTC_CS_MASK; /* activate (1) */ - else - spi->slaveselect = 0; /* deactivate (0) */ + spi->slaveselect = SPI_RTC_CS_MASK; /* activate (1) */ } -/* - * The SPI command uses this table of functions for controlling the SPI - * chip selects: it calls the appropriate function to control the SPI - * chip selects. - */ -spi_chipsel_type spi_chipsel[] = { - spi_rtc_chipsel -}; -int spi_chipsel_cnt = sizeof(spi_chipsel) / sizeof(spi_chipsel[0]); +void spi_cs_deactivate(struct spi_slave *slave) +{ + nios_spi_t *spi = (nios_spi_t *)CFG_NIOS_SPIBASE; + + spi->slaveselect = 0; /* deactivate (0) */ +} #endif diff --git a/common/cmd_df.c b/common/cmd_df.c new file mode 100644 index 00000000000..5f650442c01 --- /dev/null +++ b/common/cmd_df.c @@ -0,0 +1,37 @@ +/* + * Command for accessing DataFlash. + * + * Copyright (C) 2008 Atmel Corporation + */ +#include <common.h> +#include <df.h> + +static int do_df(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + const char *cmd; + + /* need at least two arguments */ + if (argc < 2) + goto usage; + + cmd = argv[1]; + + if (strcmp(cmd, "init") == 0) { + df_init(0, 0, 1000000); + return 0; + } + + if (strcmp(cmd, "info") == 0) { + df_show_info(); + return 0; + } + +usage: + printf("Usage:\n%s\n", cmdtp->usage); + return 1; +} + +U_BOOT_CMD( + sf, 2, 1, do_serial_flash, + "sf - Serial flash sub-system\n", + "probe [bus:]cs - init flash device on given SPI bus and CS\n") diff --git a/common/cmd_spi.c b/common/cmd_spi.c index 76044221416..40ee7e7dd3c 100644 --- a/common/cmd_spi.c +++ b/common/cmd_spi.c @@ -37,20 +37,20 @@ # define MAX_SPI_BYTES 32 /* Maximum number of bytes we can handle */ #endif -/* - * External table of chip select functions (see the appropriate board - * support for the actual definition of the table). - */ -extern spi_chipsel_type spi_chipsel[]; -extern int spi_chipsel_cnt; +#ifndef CONFIG_DEFAULT_SPI_BUS +# define CONFIG_DEFAULT_SPI_BUS 0 +#endif +#ifndef CONFIG_DEFAULT_SPI_MODE +# define CONFIG_DEFAULT_SPI_MODE SPI_MODE_0 +#endif /* * Values from last command. */ -static int device; -static int bitlen; -static uchar dout[MAX_SPI_BYTES]; -static uchar din[MAX_SPI_BYTES]; +static unsigned int device; +static int bitlen; +static uchar dout[MAX_SPI_BYTES]; +static uchar din[MAX_SPI_BYTES]; /* * SPI read/write @@ -65,6 +65,7 @@ static uchar din[MAX_SPI_BYTES]; int do_spi (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { + struct spi_slave *slave; char *cp = 0; uchar tmp; int j; @@ -101,19 +102,24 @@ int do_spi (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) } } - if ((device < 0) || (device >= spi_chipsel_cnt)) { - printf("Invalid device %d, giving up.\n", device); - return 1; - } if ((bitlen < 0) || (bitlen > (MAX_SPI_BYTES * 8))) { printf("Invalid bitlen %d, giving up.\n", bitlen); return 1; } - debug ("spi_chipsel[%d] = %08X\n", - device, (uint)spi_chipsel[device]); + /* FIXME: Make these parameters run-time configurable */ + slave = spi_setup_slave(CONFIG_DEFAULT_SPI_BUS, device, 1000000, + CONFIG_DEFAULT_SPI_MODE); + if (!slave) { + printf("Invalid device %d, giving up.\n", device); + return 1; + } + + debug ("spi chipsel = %08X\n", device); - if(spi_xfer(spi_chipsel[device], bitlen, dout, din) != 0) { + spi_claim_bus(slave); + if(spi_xfer(slave, bitlen, dout, din, + SPI_XFER_BEGIN | SPI_XFER_END) != 0) { printf("Error with the SPI transaction.\n"); rcode = 1; } else { @@ -123,6 +129,8 @@ int do_spi (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) } printf("\n"); } + spi_release_bus(slave); + spi_free_slave(slave); return rcode; } diff --git a/common/soft_spi.c b/common/soft_spi.c index e4250616c28..c13165030db 100644 --- a/common/soft_spi.c +++ b/common/soft_spi.c @@ -29,6 +29,8 @@ #if defined(CONFIG_SOFT_SPI) +#include <malloc.h> + /*----------------------------------------------------------------------- * Definitions */ @@ -39,6 +41,15 @@ #define PRINTD(fmt,args...) #endif +struct soft_spi_slave { + struct spi_slave slave; + unsigned int mode; +}; + +static inline struct soft_spi_slave *to_soft_spi(struct spi_slave *slave) +{ + return container_of(slave, struct soft_spi_slave, slave); +} /*=====================================================================*/ /* Public Functions */ @@ -56,6 +67,57 @@ void spi_init (void) #endif } +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct soft_spi_slave *ss; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + ss = malloc(sizeof(struct soft_spi_slave)); + if (!ss) + return NULL; + + ss->slave.bus = bus; + ss->slave.cs = cs; + ss->mode = mode; + + /* TODO: Use max_hz to limit the SCK rate */ + + return &ss->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct soft_spi_slave *ss = to_soft_spi(slave); + + free(ss); +} + +int spi_claim_bus(struct spi_slave *slave) +{ +#ifdef CFG_IMMR + volatile immap_t *immr = (immap_t *)CFG_IMMR; +#endif + struct soft_spi_slave *ss = to_soft_spi(slave); + + /* + * Make sure the SPI clock is in idle state as defined for + * this slave. + */ + if (ss->mode & SPI_CPOL) + SPI_SCL(1); + else + SPI_SCL(0); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + /* Nothing to do */ +} /*----------------------------------------------------------------------- * SPI transfer @@ -68,50 +130,54 @@ void spi_init (void) * and "din" can point to the same memory location, in which case the * input data overwrites the output data (since both are buffered by * temporary variables, this is OK). - * - * If the chipsel() function is not NULL, it is called with a parameter - * of '1' (chip select active) at the start of the transfer and again with - * a parameter of '0' at the end of the transfer. - * - * If the chipsel() function _is_ NULL, it the responsibility of the - * caller to make the appropriate chip select active before calling - * spi_xfer() and making it inactive after spi_xfer() returns. */ -int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) { #ifdef CFG_IMMR volatile immap_t *immr = (immap_t *)CFG_IMMR; #endif - uchar tmpdin = 0; - uchar tmpdout = 0; - int j; + struct soft_spi_slave *ss = to_soft_spi(slave); + uchar tmpdin = 0; + uchar tmpdout = 0; + const u8 *txd = dout; + u8 *rxd = din; + int cpol = ss->mode & SPI_CPOL; + int cpha = ss->mode & SPI_CPHA; + unsigned int j; - PRINTD("spi_xfer: chipsel %08X dout %08X din %08X bitlen %d\n", - (int)chipsel, *(uint *)dout, *(uint *)din, bitlen); + PRINTD("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", + slave->bus, slave->cs, *(uint *)txd, *(uint *)rxd, bitlen); - if(chipsel != NULL) { - (*chipsel)(1); /* select the target chip */ - } + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); for(j = 0; j < bitlen; j++) { /* * Check if it is time to work on a new byte. */ if((j % 8) == 0) { - tmpdout = *dout++; + tmpdout = *txd++; if(j != 0) { - *din++ = tmpdin; + *rxd++ = tmpdin; } tmpdin = 0; } - SPI_SCL(0); + + if (!cpha) + SPI_SCL(!cpol); SPI_SDA(tmpdout & 0x80); SPI_DELAY; - SPI_SCL(1); + if (cpha) + SPI_SCL(!cpol); + else + SPI_SCL(cpol); + tmpdin <<= 1; + tmpdin |= SPI_READ; + tmpdout <<= 1; SPI_DELAY; - tmpdin <<= 1; - tmpdin |= SPI_READ; - tmpdout <<= 1; + if (cpha) + SPI_SCL(cpol); } /* * If the number of bits isn't a multiple of 8, shift the last @@ -120,14 +186,10 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) */ if((bitlen % 8) != 0) tmpdin <<= 8 - (bitlen % 8); - *din++ = tmpdin; - - SPI_SCL(0); /* SPI wants the clock left low for idle */ + *rxd++ = tmpdin; - if(chipsel != NULL) { - (*chipsel)(0); /* deselect the target chip */ - - } + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); return(0); } diff --git a/cpu/nios/spi.c b/cpu/nios/spi.c index f37146b7939..6408180147a 100644 --- a/cpu/nios/spi.c +++ b/cpu/nios/spi.c @@ -63,10 +63,10 @@ static char quickhex (int i) return hex_digit[i]; } -static void memdump (void *pv, int num) +static void memdump (const void *pv, int num) { int i; - unsigned char *pc = (unsigned char *) pv; + const unsigned char *pc = (const unsigned char *) pv; for (i = 0; i < num; i++) printf ("%c%c ", quickhex (pc[i] >> 4), quickhex (pc[i] & 0x0f)); @@ -83,26 +83,64 @@ static void memdump (void *pv, int num) #endif /* DEBUG */ +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct spi_slave *slave; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + slave = malloc(sizeof(struct spi_slave)); + if (!slave) + return NULL; + + slave->bus = bus; + slave->cs = cs; + + /* TODO: Add support for different modes and speeds */ + + return slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + free(slave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + +} + /* * SPI transfer: * * See include/spi.h and http://www.altera.com/literature/ds/ds_nios_spi.pdf * for more informations. */ -int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) +int spi_xfer(struct spi_slave *slave, int bitlen, const void *dout, + void *din, unsigned long flags) { + const u8 *txd = dout; + u8 *rxd = din; int j; - DPRINT(("spi_xfer: chipsel %08X dout %08X din %08X bitlen %d\n", - (int)chipsel, *(uint *)dout, *(uint *)din, bitlen)); + DPRINT(("spi_xfer: slave %u:%u dout %08X din %08X bitlen %d\n", + slave->bus, slave->cs, *(uint *)dout, *(uint *)din, bitlen)); - memdump((void*)dout, (bitlen + 7) / 8); + memdump(dout, (bitlen + 7) / 8); - if(chipsel != NULL) { - chipsel(1); /* select the target chip */ - } + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); - if (bitlen > CFG_NIOS_SPIBITS) { /* leave chip select active */ + if (!(flags & SPI_XFER_END) || bitlen > CFG_NIOS_SPIBITS) { + /* leave chip select active */ spi->control |= NIOS_SPI_SSO; } @@ -114,11 +152,11 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) while ((spi->status & NIOS_SPI_TRDY) == 0) ; - spi->txdata = (unsigned)(dout[j]); + spi->txdata = (unsigned)(txd[j]); while ((spi->status & NIOS_SPI_RRDY) == 0) ; - din[j] = (unsigned char)(spi->rxdata & 0xff); + rxd[j] = (unsigned char)(spi->rxdata & 0xff); #elif (CFG_NIOS_SPIBITS == 16) j++, j++) { @@ -126,15 +164,15 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) while ((spi->status & NIOS_SPI_TRDY) == 0) ; if ((j+1) < ((bitlen + 7) / 8)) - spi->txdata = (unsigned)((dout[j] << 8) | dout[j+1]); + spi->txdata = (unsigned)((txd[j] << 8) | txd[j+1]); else - spi->txdata = (unsigned)(dout[j] << 8); + spi->txdata = (unsigned)(txd[j] << 8); while ((spi->status & NIOS_SPI_RRDY) == 0) ; - din[j] = (unsigned char)((spi->rxdata >> 8) & 0xff); + rxd[j] = (unsigned char)((spi->rxdata >> 8) & 0xff); if ((j+1) < ((bitlen + 7) / 8)) - din[j+1] = (unsigned char)(spi->rxdata & 0xff); + rxd[j+1] = (unsigned char)(spi->rxdata & 0xff); #else #error "*** unsupported value of CFG_NIOS_SPIBITS ***" @@ -142,15 +180,14 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) } - if (bitlen > CFG_NIOS_SPIBITS) { + if (bitlen > CFG_NIOS_SPIBITS && (flags & SPI_XFER_END)) { spi->control &= ~NIOS_SPI_SSO; } - if(chipsel != NULL) { - chipsel(0); /* deselect the target chip */ - } + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); - memdump((void*)din, (bitlen + 7) / 8); + memdump(din, (bitlen + 7) / 8); return 0; } diff --git a/drivers/rtc/ds1306.c b/drivers/rtc/ds1306.c index 1c8ac7f2927..29854fc7c4c 100644 --- a/drivers/rtc/ds1306.c +++ b/drivers/rtc/ds1306.c @@ -62,13 +62,6 @@ #define RTC_USER_RAM_BASE 0x20 -/* - * External table of chip select functions (see the appropriate board - * support for the actual definition of the table). - */ -extern spi_chipsel_type spi_chipsel[]; -extern int spi_chipsel_cnt; - static unsigned int bin2bcd (unsigned int n); static unsigned char bcd2bin (unsigned char c); @@ -305,11 +298,29 @@ void rtc_reset (void) static unsigned char rtc_read (unsigned char reg); static void rtc_write (unsigned char reg, unsigned char val); +static struct spi_slave *slave; + /* read clock time from DS1306 and return it in *tmp */ int rtc_get (struct rtc_time *tmp) { unsigned char sec, min, hour, mday, wday, mon, year; + /* + * Assuming Vcc = 2.0V (lowest speed) + * + * REVISIT: If we add an rtc_init() function we can do this + * step just once. + */ + if (!slave) { + slave = spi_setup_slave(0, CFG_SPI_RTC_DEVID, 600000, + SPI_MODE_3 | SPI_CS_HIGH); + if (!slave) + return; + } + + if (spi_claim_bus(slave)) + return; + sec = rtc_read (RTC_SECONDS); min = rtc_read (RTC_MINUTES); hour = rtc_read (RTC_HOURS); @@ -318,6 +329,8 @@ int rtc_get (struct rtc_time *tmp) mon = rtc_read (RTC_MONTH); year = rtc_read (RTC_YEAR); + spi_release_bus(slave); + debug ("Get RTC year: %02x mon: %02x mday: %02x wday: %02x " "hr: %02x min: %02x sec: %02x\n", year, mon, mday, wday, hour, min, sec); @@ -360,6 +373,17 @@ int rtc_get (struct rtc_time *tmp) /* set clock time from *tmp in DS1306 RTC */ void rtc_set (struct rtc_time *tmp) { + /* Assuming Vcc = 2.0V (lowest speed) */ + if (!slave) { + slave = spi_setup_slave(0, CFG_SPI_RTC_DEVID, 600000, + SPI_MODE_3 | SPI_CS_HIGH); + if (!slave) + return; + } + + if (spi_claim_bus(slave)) + return; + debug ("Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n", tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday, tmp->tm_hour, tmp->tm_min, tmp->tm_sec); @@ -371,6 +395,8 @@ void rtc_set (struct rtc_time *tmp) rtc_write (RTC_DATE_OF_MONTH, bin2bcd (tmp->tm_mday)); rtc_write (RTC_MONTH, bin2bcd (tmp->tm_mon)); rtc_write (RTC_YEAR, bin2bcd (tmp->tm_year - 2000)); + + spi_release_bus(slave); } /* ------------------------------------------------------------------------- */ @@ -378,6 +404,17 @@ void rtc_set (struct rtc_time *tmp) /* reset the DS1306 */ void rtc_reset (void) { + /* Assuming Vcc = 2.0V (lowest speed) */ + if (!slave) { + slave = spi_setup_slave(0, CFG_SPI_RTC_DEVID, 600000, + SPI_MODE_3 | SPI_CS_HIGH); + if (!slave) + return; + } + + if (spi_claim_bus(slave)) + return; + /* clear the control register */ rtc_write (RTC_CONTROL, 0x00); /* 1st step: reset WP */ rtc_write (RTC_CONTROL, 0x00); /* 2nd step: reset 1Hz, AIE1, AIE0 */ @@ -391,22 +428,18 @@ void rtc_reset (void) rtc_write (RTC_HOURS_ALARM1, 0x00); rtc_write (RTC_DAY_OF_WEEK_ALARM0, 0x00); rtc_write (RTC_DAY_OF_WEEK_ALARM1, 0x00); + + spi_release_bus(slave); } /* ------------------------------------------------------------------------- */ static unsigned char rtc_read (unsigned char reg) { - unsigned char dout[2]; /* SPI Output Data Bytes */ - unsigned char din[2]; /* SPI Input Data Bytes */ - - dout[0] = reg; + int ret; - if (spi_xfer (spi_chipsel[CFG_SPI_RTC_DEVID], 16, dout, din) != 0) { - return 0; - } else { - return din[1]; - } + ret = spi_w8r8(slave, reg); + return ret < 0 ? 0 : ret; } /* ------------------------------------------------------------------------- */ @@ -419,7 +452,7 @@ static void rtc_write (unsigned char reg, unsigned char val) dout[0] = 0x80 | reg; dout[1] = val; - spi_xfer (spi_chipsel[CFG_SPI_RTC_DEVID], 16, dout, din); + spi_xfer (slave, 16, dout, din, SPI_XFER_BEGIN | SPI_XFER_END); } #endif /* end of code exclusion (see #ifdef CONFIG_SXNI855T above) */ diff --git a/drivers/rtc/mc13783-rtc.c b/drivers/rtc/mc13783-rtc.c index 35b1b8b254d..b6e15014bb6 100644 --- a/drivers/rtc/mc13783-rtc.c +++ b/drivers/rtc/mc13783-rtc.c @@ -24,34 +24,50 @@ #include <rtc.h> #include <spi.h> +static struct spi_slave *slave; + int rtc_get(struct rtc_time *rtc) { u32 day1, day2, time; u32 reg; int err, tim, i = 0; - spi_select(1, 0, SPI_MODE_2 | SPI_CS_HIGH); + if (!slave) { + /* FIXME: Verify the max SCK rate */ + slave = spi_setup_slave(1, 0, 1000000, + SPI_MODE_2 | SPI_CS_HIGH); + if (!slave) + return -1; + } + + if (spi_claim_bus(slave)) + return -1; do { reg = 0x2c000000; - err = spi_xfer(0, 32, (uchar *)®, (uchar *)&day1); + err = spi_xfer(slave, 32, (uchar *)®, (uchar *)&day1, + SPI_XFER_BEGIN | SPI_XFER_END); if (err) return err; reg = 0x28000000; - err = spi_xfer(0, 32, (uchar *)®, (uchar *)&time); + err = spi_xfer(slave, 32, (uchar *)®, (uchar *)&time, + SPI_XFER_BEGIN | SPI_XFER_END); if (err) return err; reg = 0x2c000000; - err = spi_xfer(0, 32, (uchar *)®, (uchar *)&day2); + err = spi_xfer(slave, 32, (uchar *)®, (uchar *)&day2, + SPI_XFER_BEGIN | SPI_XFER_END); if (err) return err; } while (day1 != day2 && i++ < 3); + spi_release_bus(slave); + tim = day1 * 86400 + time; to_tm(tim, rtc); @@ -65,16 +81,31 @@ void rtc_set(struct rtc_time *rtc) { u32 time, day, reg; + if (!slave) { + /* FIXME: Verify the max SCK rate */ + slave = spi_setup_slave(1, 0, 1000000, + SPI_MODE_2 | SPI_CS_HIGH); + if (!slave) + return; + } + time = mktime(rtc->tm_year, rtc->tm_mon, rtc->tm_mday, rtc->tm_hour, rtc->tm_min, rtc->tm_sec); day = time / 86400; time %= 86400; + if (spi_claim_bus(slave)) + return; + reg = 0x2c000000 | day | 0x80000000; - spi_xfer(0, 32, (uchar *)®, (uchar *)&day); + spi_xfer(slave, 32, (uchar *)®, (uchar *)&day, + SPI_XFER_BEGIN | SPI_XFER_END); reg = 0x28000000 | time | 0x80000000; - spi_xfer(0, 32, (uchar *)®, (uchar *)&time); + spi_xfer(slave, 32, (uchar *)®, (uchar *)&time, + SPI_XFER_BEGIN | SPI_XFER_END); + + spi_release_bus(slave); } void rtc_reset(void) diff --git a/drivers/spi/mpc8xxx_spi.c b/drivers/spi/mpc8xxx_spi.c index 2fe838c45d5..136fb50052f 100644 --- a/drivers/spi/mpc8xxx_spi.c +++ b/drivers/spi/mpc8xxx_spi.c @@ -24,6 +24,7 @@ #include <common.h> #if defined(CONFIG_MPC8XXX_SPI) && defined(CONFIG_HARD_SPI) +#include <malloc.h> #include <spi.h> #include <asm/mpc8xxx_spi.h> @@ -37,6 +38,34 @@ #define SPI_TIMEOUT 1000 +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct spi_slave *slave; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + slave = malloc(sizeof(struct spi_slave)); + if (!slave) + return NULL; + + slave->bus = bus; + slave->cs = cs; + + /* + * TODO: Some of the code in spi_init() should probably move + * here, or into spi_claim_bus() below. + */ + + return slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + free(slave); +} + void spi_init(void) { volatile spi8xxx_t *spi = &((immap_t *) (CFG_IMMR))->spi; @@ -53,7 +82,18 @@ void spi_init(void) spi->com = 0; /* LST bit doesn't do anything, so disregard */ } -int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) { volatile spi8xxx_t *spi = &((immap_t *) (CFG_IMMR))->spi; unsigned int tmpdout, tmpdin, event; @@ -61,11 +101,11 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) int tm, isRead = 0; unsigned char charSize = 32; - debug("spi_xfer: chipsel %08X dout %08X din %08X bitlen %d\n", - (int)chipsel, *(uint *) dout, *(uint *) din, bitlen); + debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", + slave->bus, slave->cs, *(uint *) dout, *(uint *) din, bitlen); - if (chipsel != NULL) - (*chipsel) (1); /* select the target chip */ + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); spi->event = 0xffffffff; /* Clear all SPI events */ @@ -135,8 +175,8 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) debug("*** spi_xfer: transfer ended. Value=%08x\n", tmpdin); } - if (chipsel != NULL) - (*chipsel) (0); /* deselect the target chip */ + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); return 0; } diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c index c166ec50239..5957ada3a4a 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -19,6 +19,7 @@ */ #include <common.h> +#include <malloc.h> #include <spi.h> #include <asm/io.h> @@ -61,17 +62,18 @@ static unsigned long spi_bases[] = { 0x53f84000, }; -static unsigned long spi_base; - #endif -spi_chipsel_type spi_chipsel[] = { - (spi_chipsel_type)0, - (spi_chipsel_type)1, - (spi_chipsel_type)2, - (spi_chipsel_type)3, +struct mxc_spi_slave { + struct spi_slave slave; + unsigned long base; + u32 ctrl_reg; }; -int spi_chipsel_cnt = sizeof(spi_chipsel) / sizeof(spi_chipsel[0]); + +static inline struct mxc_spi_slave *to_mxc_spi_slave(struct spi_slave *slave) +{ + return container_of(slave, struct mxc_spi_slave, slave); +} static inline u32 reg_read(unsigned long addr) { @@ -83,30 +85,31 @@ static inline void reg_write(unsigned long addr, u32 val) *(volatile unsigned long*)addr = val; } -static u32 spi_xchg_single(u32 data, int bitlen) +static u32 spi_xchg_single(struct spi_slave *slave, u32 data, int bitlen) { - - unsigned int cfg_reg = reg_read(spi_base + MXC_CSPICTRL); + struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave); + unsigned int cfg_reg = reg_read(mxcs->base + MXC_CSPICTRL); if (MXC_CSPICTRL_BITCOUNT(bitlen - 1) != (cfg_reg & MXC_CSPICTRL_BITCOUNT(31))) { cfg_reg = (cfg_reg & ~MXC_CSPICTRL_BITCOUNT(31)) | MXC_CSPICTRL_BITCOUNT(bitlen - 1); - reg_write(spi_base + MXC_CSPICTRL, cfg_reg); + reg_write(mxcs->base + MXC_CSPICTRL, cfg_reg); } - reg_write(spi_base + MXC_CSPITXDATA, data); + reg_write(mxcs->base + MXC_CSPITXDATA, data); cfg_reg |= MXC_CSPICTRL_XCH; - reg_write(spi_base + MXC_CSPICTRL, cfg_reg); + reg_write(mxcs->base + MXC_CSPICTRL, cfg_reg); - while (reg_read(spi_base + MXC_CSPICTRL) & MXC_CSPICTRL_XCH) + while (reg_read(mxcs->base + MXC_CSPICTRL) & MXC_CSPICTRL_XCH) ; - return reg_read(spi_base + MXC_CSPIRXDATA); + return reg_read(mxcs->base + MXC_CSPIRXDATA); } -int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) { int n_blks = (bitlen + 31) / 32; u32 *out_l, *in_l; @@ -117,13 +120,10 @@ int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din) return 1; } - if (!spi_base) - spi_select(CONFIG_MXC_SPI_IFACE, (int)chipsel, SPI_MODE_2 | SPI_CS_HIGH); - for (i = 0, in_l = (u32 *)din, out_l = (u32 *)dout; i < n_blks; i++, in_l++, out_l++, bitlen -= 32) - *in_l = spi_xchg_single(*out_l, bitlen); + *in_l = spi_xchg_single(slave, *out_l, bitlen); return 0; } @@ -132,17 +132,17 @@ void spi_init(void) { } -int spi_select(unsigned int bus, unsigned int dev, unsigned long mode) +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) { unsigned int ctrl_reg; + struct mxc_spi_slave *mxcs; if (bus >= sizeof(spi_bases) / sizeof(spi_bases[0]) || - dev > 3) - return 1; - - spi_base = spi_bases[bus]; + cs > 3) + return NULL; - ctrl_reg = MXC_CSPICTRL_CHIPSELECT(dev) | + ctrl_reg = MXC_CSPICTRL_CHIPSELECT(cs) | MXC_CSPICTRL_BITCOUNT(31) | MXC_CSPICTRL_DATARATE(7) | /* FIXME: calculate data rate */ MXC_CSPICTRL_EN | @@ -155,12 +155,38 @@ int spi_select(unsigned int bus, unsigned int dev, unsigned long mode) if (mode & SPI_CS_HIGH) ctrl_reg |= MXC_CSPICTRL_SSPOL; - reg_write(spi_base + MXC_CSPIRESET, 1); + mxcs = malloc(sizeof(struct mxc_spi_slave)); + if (!mxcs) + return NULL; + + mxcs->slave.bus = bus; + mxcs->slave.cs = cs; + mxcs->base = spi_bases[bus]; + mxcs->ctrl_reg = ctrl_reg; + + return &mxcs->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + free(slave); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave); + + reg_write(mxcs->base + MXC_CSPIRESET, 1); udelay(1); - reg_write(spi_base + MXC_CSPICTRL, ctrl_reg); - reg_write(spi_base + MXC_CSPIPERIOD, + reg_write(mxcs->base + MXC_CSPICTRL, mxcs->ctrl_reg); + reg_write(mxcs->base + MXC_CSPIPERIOD, MXC_CSPIPERIOD_32KHZ); - reg_write(spi_base + MXC_CSPIINT, 0); + reg_write(mxcs->base + MXC_CSPIINT, 0); return 0; } + +void spi_release_bus(struct spi_slave *slave) +{ + /* TODO: Shut the controller down */ +} diff --git a/include/configs/imx31_litekit.h b/include/configs/imx31_litekit.h index 4281d73c90b..ec4ed1eeb67 100644 --- a/include/configs/imx31_litekit.h +++ b/include/configs/imx31_litekit.h @@ -65,7 +65,8 @@ #define CONFIG_HARD_SPI 1 #define CONFIG_MXC_SPI 1 -#define CONFIG_MXC_SPI_IFACE 1 +#define CONFIG_DEFAULT_SPI_BUS 1 +#define CONFIG_DEFAULT_SPI_MODE (SPI_MODE_2 | SPI_CS_HIGH) #define CONFIG_RTC_MC13783 1 diff --git a/include/configs/mx31ads.h b/include/configs/mx31ads.h index 2ea48a6da9a..37ba872a439 100644 --- a/include/configs/mx31ads.h +++ b/include/configs/mx31ads.h @@ -62,7 +62,8 @@ #define CONFIG_HARD_SPI 1 #define CONFIG_MXC_SPI 1 -#define CONFIG_MXC_SPI_IFACE 1 /* Default SPI interface number */ +#define CONFIG_DEFAULT_SPI_BUS 1 +#define CONFIG_DEFAULT_SPI_MODE (SPI_MODE_2 | SPI_CS_HIGH) #define CONFIG_RTC_MC13783 1 diff --git a/include/spi.h b/include/spi.h index 3a55a68c4d1..7744c2e36b0 100644 --- a/include/spi.h +++ b/include/spi.h @@ -31,22 +31,87 @@ #define SPI_MODE_1 (0|SPI_CPHA) #define SPI_MODE_2 (SPI_CPOL|0) #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA) -#define SPI_CS_HIGH 0x04 /* chipselect active high? */ +#define SPI_CS_HIGH 0x04 /* CS active high */ #define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */ #define SPI_3WIRE 0x10 /* SI/SO signals shared */ #define SPI_LOOP 0x20 /* loopback mode */ -/* - * The function call pointer type used to drive the chip select. - */ -typedef void (*spi_chipsel_type)(int cs); +/* SPI transfer flags */ +#define SPI_XFER_BEGIN 0x01 /* Assert CS before transfer */ +#define SPI_XFER_END 0x02 /* Deassert CS after transfer */ +/*----------------------------------------------------------------------- + * Representation of a SPI slave, i.e. what we're communicating with. + * + * Drivers are expected to extend this with controller-specific data. + * + * bus: ID of the bus that the slave is attached to. + * cs: ID of the chip select connected to the slave. + */ +struct spi_slave { + unsigned int bus; + unsigned int cs; +}; /*----------------------------------------------------------------------- * Initialization, must be called once on start up. + * + * TODO: I don't think we really need this. */ void spi_init(void); +/*----------------------------------------------------------------------- + * Set up communications parameters for a SPI slave. + * + * This must be called once for each slave. Note that this function + * usually doesn't touch any actual hardware, it only initializes the + * contents of spi_slave so that the hardware can be easily + * initialized later. + * + * bus: Bus ID of the slave chip. + * cs: Chip select ID of the slave chip on the specified bus. + * max_hz: Maximum SCK rate in Hz. + * mode: Clock polarity, clock phase and other parameters. + * + * Returns: A spi_slave reference that can be used in subsequent SPI + * calls, or NULL if one or more of the parameters are not supported. + */ +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode); + +/*----------------------------------------------------------------------- + * Free any memory associated with a SPI slave. + * + * slave: The SPI slave + */ +void spi_free_slave(struct spi_slave *slave); + +/*----------------------------------------------------------------------- + * Claim the bus and prepare it for communication with a given slave. + * + * This must be called before doing any transfers with a SPI slave. It + * will enable and initialize any SPI hardware as necessary, and make + * sure that the SCK line is in the correct idle state. It is not + * allowed to claim the same bus for several slaves without releasing + * the bus in between. + * + * slave: The SPI slave + * + * Returns: 0 if the bus was claimed successfully, or a negative value + * if it wasn't. + */ +int spi_claim_bus(struct spi_slave *slave); + +/*----------------------------------------------------------------------- + * Release the SPI bus + * + * This must be called once for every call to spi_claim_bus() after + * all transfers have finished. It may disable any SPI hardware as + * appropriate. + * + * slave: The SPI slave + */ +void spi_release_bus(struct spi_slave *slave); /*----------------------------------------------------------------------- * SPI transfer @@ -60,28 +125,67 @@ void spi_init(void); * input data overwrites the output data (since both are buffered by * temporary variables, this is OK). * - * If the chipsel() function is not NULL, it is called with a parameter - * of '1' (chip select active) at the start of the transfer and again with - * a parameter of '0' at the end of the transfer. - * - * If the chipsel() function _is_ NULL, it the responsibility of the - * caller to make the appropriate chip select active before calling - * spi_xfer() and making it inactive after spi_xfer() returns. - * * spi_xfer() interface: - * chipsel: Routine to call to set/clear the chip select: - * if chipsel is NULL, it is not used. - * if(cs), make the chip select active (typically '0'). - * if(!cs), make the chip select inactive (typically '1'). - * dout: Pointer to a string of bits to send out. The bits are - * held in a byte array and are sent MSB first. - * din: Pointer to a string of bits that will be filled in. - * bitlen: How many bits to write and read. + * slave: The SPI slave which will be sending/receiving the data. + * bitlen: How many bits to write and read. + * dout: Pointer to a string of bits to send out. The bits are + * held in a byte array and are sent MSB first. + * din: Pointer to a string of bits that will be filled in. + * flags: A bitwise combination of SPI_XFER_* flags. * * Returns: 0 on success, not 0 on failure */ -int spi_xfer(spi_chipsel_type chipsel, int bitlen, uchar *dout, uchar *din); +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags); + +/*----------------------------------------------------------------------- + * Determine if a SPI chipselect is valid. + * This function is provided by the board if the low-level SPI driver + * needs it to determine if a given chipselect is actually valid. + * + * Returns: 1 if bus:cs identifies a valid chip on this board, 0 + * otherwise. + */ +int spi_cs_is_valid(unsigned int bus, unsigned int cs); + +/*----------------------------------------------------------------------- + * Activate a SPI chipselect. + * This function is provided by the board code when using a driver + * that can't control its chipselects automatically (e.g. + * common/soft_spi.c). When called, it should activate the chip select + * to the device identified by "slave". + */ +void spi_cs_activate(struct spi_slave *slave); + +/*----------------------------------------------------------------------- + * Deactivate a SPI chipselect. + * This function is provided by the board code when using a driver + * that can't control its chipselects automatically (e.g. + * common/soft_spi.c). When called, it should deactivate the chip + * select to the device identified by "slave". + */ +void spi_cs_deactivate(struct spi_slave *slave); + +/*----------------------------------------------------------------------- + * Write 8 bits, then read 8 bits. + * slave: The SPI slave we're communicating with + * byte: Byte to be written + * + * Returns: The value that was read, or a negative value on error. + * + * TODO: This function probably shouldn't be inlined. + */ +static inline int spi_w8r8(struct spi_slave *slave, unsigned char byte) +{ + unsigned char dout[2]; + unsigned char din[2]; + int ret; + + dout[0] = byte; + dout[1] = 0; -int spi_select(unsigned int bus, unsigned int dev, unsigned long mode); + ret = spi_xfer(slave, 16, dout, din, SPI_XFER_BEGIN | SPI_XFER_END); + return ret < 0 ? ret : din[1]; +} #endif /* _SPI_H_ */ |