summaryrefslogtreecommitdiffstats
path: root/src/drivers/bitbash
diff options
context:
space:
mode:
authorMichael Brown2009-05-28 15:45:32 +0200
committerMichael Brown2009-05-28 20:32:03 +0200
commit92a9978b4403628345ca03ba4edc7641352460fa (patch)
tree02626e7d3a3970b7b14cb4dd6f76211c447057cf /src/drivers/bitbash
parent[build] Add -fno-dwarf2-cfi-asm to CFLAGS if supported by the gcc in use (diff)
downloadipxe-92a9978b4403628345ca03ba4edc7641352460fa.tar.gz
ipxe-92a9978b4403628345ca03ba4edc7641352460fa.tar.xz
ipxe-92a9978b4403628345ca03ba4edc7641352460fa.zip
[spi] Add address-length autodetection to the SPI bit-bashing code
Several SPI chips will respond to an SPI read command with a dummy zero bit immediately prior to the first real data bit. This can be used to autodetect the address length, provided that the command length and data length are already known, and that the MISO data line is tied high. Tested-by: Thomas Miletich <thomas.miletich@gmail.com> Debugged-by: Thomas Miletich <thomas.miletich@gmail.com>
Diffstat (limited to 'src/drivers/bitbash')
-rw-r--r--src/drivers/bitbash/spi_bit.c51
1 files changed, 39 insertions, 12 deletions
diff --git a/src/drivers/bitbash/spi_bit.c b/src/drivers/bitbash/spi_bit.c
index 8b5060c1..8e703936 100644
--- a/src/drivers/bitbash/spi_bit.c
+++ b/src/drivers/bitbash/spi_bit.c
@@ -60,8 +60,8 @@ static void spi_bit_set_slave_select ( struct spi_bit_basher *spibit,
struct bit_basher *basher = &spibit->basher;
state ^= ( spibit->bus.mode & SPI_MODE_SSPOL );
- DBG ( "Setting slave %d select %s\n", slave,
- ( state ? "high" : "low" ) );
+ DBGC2 ( spibit, "SPIBIT %p setting slave %d select %s\n",
+ spibit, slave, ( state ? "high" : "low" ) );
spi_bit_delay();
write_bit ( basher, SPI_BIT_SS ( slave ), state );
@@ -96,7 +96,8 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
unsigned int bit;
unsigned int step;
- DBG ( "Transferring %d bits in mode %x\n", len, bus->mode );
+ DBGC2 ( spibit, "SPIBIT %p transferring %d bits in mode %#x\n",
+ spibit, len, bus->mode );
for ( step = 0 ; step < ( len * 2 ) ; step++ ) {
/* Calculate byte offset and byte mask */
@@ -113,6 +114,8 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
if ( data_out ) {
byte = ( data_out + byte_offset );
bit = ( *byte & byte_mask );
+ DBGCP ( spibit, "SPIBIT %p wrote bit %d\n",
+ spibit, ( bit ? 1 : 0 ) );
} else {
bit = 0;
}
@@ -123,6 +126,8 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
/* Shift data in */
bit = read_bit ( basher, SPI_BIT_MISO );
if ( data_in ) {
+ DBGCP ( spibit, "SPIBIT %p read bit %d\n",
+ spibit, ( bit ? 1 : 0 ) );
byte = ( data_in + byte_offset );
*byte &= ~byte_mask;
*byte |= ( bit & byte_mask );
@@ -131,7 +136,7 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
/* Toggle clock line */
spi_bit_delay();
- sclk = ~sclk;
+ sclk ^= 1;
write_bit ( basher, SPI_BIT_SCLK, sclk );
}
}
@@ -153,7 +158,9 @@ static int spi_bit_rw ( struct spi_bus *bus, struct spi_device *device,
const void *data_out, void *data_in, size_t len ) {
struct spi_bit_basher *spibit
= container_of ( bus, struct spi_bit_basher, bus );
- uint32_t tmp;
+ uint32_t tmp_command;
+ uint32_t tmp_address;
+ uint32_t tmp_address_detect;
/* Set clock line to idle state */
write_bit ( &spibit->basher, SPI_BIT_SCLK,
@@ -163,17 +170,37 @@ static int spi_bit_rw ( struct spi_bus *bus, struct spi_device *device,
spi_bit_set_slave_select ( spibit, device->slave, SELECT_SLAVE );
/* Transmit command */
- assert ( device->command_len <= ( 8 * sizeof ( tmp ) ) );
- tmp = cpu_to_le32 ( command );
- spi_bit_transfer ( spibit, &tmp, NULL, device->command_len,
+ assert ( device->command_len <= ( 8 * sizeof ( tmp_command ) ) );
+ tmp_command = cpu_to_le32 ( command );
+ spi_bit_transfer ( spibit, &tmp_command, NULL, device->command_len,
SPI_BIT_BIG_ENDIAN );
/* Transmit address, if present */
if ( address >= 0 ) {
- assert ( device->address_len <= ( 8 * sizeof ( tmp ) ) );
- tmp = cpu_to_le32 ( address );
- spi_bit_transfer ( spibit, &tmp, NULL, device->address_len,
- SPI_BIT_BIG_ENDIAN );
+ assert ( device->address_len <= ( 8 * sizeof ( tmp_address )));
+ tmp_address = cpu_to_le32 ( address );
+ if ( device->address_len == SPI_AUTODETECT_ADDRESS_LEN ) {
+ /* Autodetect address length. This relies on
+ * the device responding with a dummy zero
+ * data bit before the first real data bit.
+ */
+ DBGC ( spibit, "SPIBIT %p autodetecting device "
+ "address length\n", spibit );
+ assert ( address == 0 );
+ device->address_len = 0;
+ do {
+ spi_bit_transfer ( spibit, &tmp_address,
+ &tmp_address_detect, 1,
+ SPI_BIT_BIG_ENDIAN );
+ device->address_len++;
+ } while ( le32_to_cpu ( tmp_address_detect ) & 1 );
+ DBGC ( spibit, "SPIBIT %p autodetected device address "
+ "length %d\n", spibit, device->address_len );
+ } else {
+ spi_bit_transfer ( spibit, &tmp_address, NULL,
+ device->address_len,
+ SPI_BIT_BIG_ENDIAN );
+ }
}
/* Transmit/receive data */