diff options
-rw-r--r-- | src/drivers/bitbash/spi_bit.c | 51 | ||||
-rw-r--r-- | src/drivers/nvs/threewire.c | 53 | ||||
-rw-r--r-- | src/include/gpxe/spi.h | 8 | ||||
-rw-r--r-- | src/include/gpxe/threewire.h | 1 |
4 files changed, 95 insertions, 18 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 */ diff --git a/src/drivers/nvs/threewire.c b/src/drivers/nvs/threewire.c index f7a20bbe..8e521389 100644 --- a/src/drivers/nvs/threewire.c +++ b/src/drivers/nvs/threewire.c @@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <stddef.h> +#include <string.h> #include <assert.h> #include <unistd.h> #include <gpxe/threewire.h> @@ -42,13 +43,21 @@ int threewire_read ( struct nvs_device *nvs, unsigned int address, void *data, size_t len ) { struct spi_device *device = nvs_to_spi ( nvs ); struct spi_bus *bus = device->bus; + int rc; assert ( bus->mode == SPI_MODE_THREEWIRE ); - DBG ( "3wire %p reading %zd bytes at %04x\n", device, len, address ); + DBGC ( device, "3wire %p reading %zd bytes at %04x\n", + device, len, address ); + + if ( ( rc = bus->rw ( bus, device, THREEWIRE_READ, address, + NULL, data, len ) ) != 0 ) { + DBGC ( device, "3wire %p could not read: %s\n", + device, strerror ( rc ) ); + return rc; + } - return bus->rw ( bus, device, THREEWIRE_READ, address, - NULL, data, len ); + return 0; } /** @@ -68,17 +77,24 @@ int threewire_write ( struct nvs_device *nvs, unsigned int address, assert ( bus->mode == SPI_MODE_THREEWIRE ); - DBG ( "3wire %p writing %zd bytes at %04x\n", device, len, address ); + DBGC ( device, "3wire %p writing %zd bytes at %04x\n", + device, len, address ); /* Enable device for writing */ if ( ( rc = bus->rw ( bus, device, THREEWIRE_EWEN, - THREEWIRE_EWEN_ADDRESS, NULL, NULL, 0 ) ) != 0 ) + THREEWIRE_EWEN_ADDRESS, NULL, NULL, 0 ) ) != 0 ){ + DBGC ( device, "3wire %p could not enable writing: %s\n", + device, strerror ( rc ) ); return rc; + } /* Write data */ if ( ( rc = bus->rw ( bus, device, THREEWIRE_WRITE, address, - data, NULL, len ) ) != 0 ) + data, NULL, len ) ) != 0 ) { + DBGC ( device, "3wire %p could not write: %s\n", + device, strerror ( rc ) ); return rc; + } /* Our model of an SPI bus doesn't provide a mechanism for * "assert CS, wait for MISO to become high, so just wait for @@ -88,3 +104,28 @@ int threewire_write ( struct nvs_device *nvs, unsigned int address, return 0; } + +/** + * Autodetect device address length + * + * @v device SPI device + * @ret rc Return status code + */ +int threewire_detect_address_len ( struct spi_device *device ) { + struct nvs_device *nvs = &device->nvs; + int rc; + + DBGC ( device, "3wire %p autodetecting address length\n", device ); + + device->address_len = SPI_AUTODETECT_ADDRESS_LEN; + if ( ( rc = threewire_read ( nvs, 0, NULL, + ( 1 << nvs->word_len_log2 ) ) ) != 0 ) { + DBGC ( device, "3wire %p could not autodetect address " + "length: %s\n", device, strerror ( rc ) ); + return rc; + } + + DBGC ( device, "3wire %p autodetected address length %d\n", + device, device->address_len ); + return 0; +} diff --git a/src/include/gpxe/spi.h b/src/include/gpxe/spi.h index ebfc3226..8e4a6763 100644 --- a/src/include/gpxe/spi.h +++ b/src/include/gpxe/spi.h @@ -104,6 +104,14 @@ struct spi_device { unsigned int munge_address : 1; }; +/** + * SPI magic autodetection address length + * + * Set @c spi_device::address_len to @c SPI_AUTODETECT_ADDRESS_LEN if + * the address length should be autodetected. + */ +#define SPI_AUTODETECT_ADDRESS_LEN 0 + static inline __attribute__ (( always_inline )) struct spi_device * nvs_to_spi ( struct nvs_device *nvs ) { return container_of ( nvs, struct spi_device, nvs ); diff --git a/src/include/gpxe/threewire.h b/src/include/gpxe/threewire.h index 4dc755c2..2db67260 100644 --- a/src/include/gpxe/threewire.h +++ b/src/include/gpxe/threewire.h @@ -45,6 +45,7 @@ extern int threewire_read ( struct nvs_device *nvs, unsigned int address, void *data, size_t len ); extern int threewire_write ( struct nvs_device *nvs, unsigned int address, const void *data, size_t len ); +extern int threewire_detect_address_len ( struct spi_device *device ); /** * @defgroup tdevs Three-wire device types |