summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/drivers/bitbash/spi_bit.c51
-rw-r--r--src/drivers/nvs/threewire.c53
-rw-r--r--src/include/gpxe/spi.h8
-rw-r--r--src/include/gpxe/threewire.h1
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