Browse Source

[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>
tags/v0.9.8
Michael Brown 15 years ago
parent
commit
92a9978b44
4 changed files with 95 additions and 18 deletions
  1. 39
    12
      src/drivers/bitbash/spi_bit.c
  2. 47
    6
      src/drivers/nvs/threewire.c
  3. 8
    0
      src/include/gpxe/spi.h
  4. 1
    0
      src/include/gpxe/threewire.h

+ 39
- 12
src/drivers/bitbash/spi_bit.c View File

60
 	struct bit_basher *basher = &spibit->basher;
60
 	struct bit_basher *basher = &spibit->basher;
61
 
61
 
62
 	state ^= ( spibit->bus.mode & SPI_MODE_SSPOL );
62
 	state ^= ( spibit->bus.mode & SPI_MODE_SSPOL );
63
-	DBG ( "Setting slave %d select %s\n", slave,
64
-	      ( state ? "high" : "low" ) );
63
+	DBGC2 ( spibit, "SPIBIT %p setting slave %d select %s\n",
64
+		spibit, slave, ( state ? "high" : "low" ) );
65
 
65
 
66
 	spi_bit_delay();
66
 	spi_bit_delay();
67
 	write_bit ( basher, SPI_BIT_SS ( slave ), state );
67
 	write_bit ( basher, SPI_BIT_SS ( slave ), state );
96
 	unsigned int bit;
96
 	unsigned int bit;
97
 	unsigned int step;
97
 	unsigned int step;
98
 
98
 
99
-	DBG ( "Transferring %d bits in mode %x\n", len, bus->mode );
99
+	DBGC2 ( spibit, "SPIBIT %p transferring %d bits in mode %#x\n",
100
+		spibit, len, bus->mode );
100
 
101
 
101
 	for ( step = 0 ; step < ( len * 2 ) ; step++ ) {
102
 	for ( step = 0 ; step < ( len * 2 ) ; step++ ) {
102
 		/* Calculate byte offset and byte mask */
103
 		/* Calculate byte offset and byte mask */
113
 			if ( data_out ) {
114
 			if ( data_out ) {
114
 				byte = ( data_out + byte_offset );
115
 				byte = ( data_out + byte_offset );
115
 				bit = ( *byte & byte_mask );
116
 				bit = ( *byte & byte_mask );
117
+				DBGCP ( spibit, "SPIBIT %p wrote bit %d\n",
118
+					spibit, ( bit ? 1 : 0 ) );
116
 			} else {
119
 			} else {
117
 				bit = 0;
120
 				bit = 0;
118
 			}
121
 			}
123
 			/* Shift data in */
126
 			/* Shift data in */
124
 			bit = read_bit ( basher, SPI_BIT_MISO );
127
 			bit = read_bit ( basher, SPI_BIT_MISO );
125
 			if ( data_in ) {
128
 			if ( data_in ) {
129
+				DBGCP ( spibit, "SPIBIT %p read bit %d\n",
130
+					spibit, ( bit ? 1 : 0 ) );
126
 				byte = ( data_in + byte_offset );
131
 				byte = ( data_in + byte_offset );
127
 				*byte &= ~byte_mask;
132
 				*byte &= ~byte_mask;
128
 				*byte |= ( bit & byte_mask );
133
 				*byte |= ( bit & byte_mask );
131
 
136
 
132
 		/* Toggle clock line */
137
 		/* Toggle clock line */
133
 		spi_bit_delay();
138
 		spi_bit_delay();
134
-		sclk = ~sclk;
139
+		sclk ^= 1;
135
 		write_bit ( basher, SPI_BIT_SCLK, sclk );
140
 		write_bit ( basher, SPI_BIT_SCLK, sclk );
136
 	}
141
 	}
137
 }
142
 }
153
 			const void *data_out, void *data_in, size_t len ) {
158
 			const void *data_out, void *data_in, size_t len ) {
154
 	struct spi_bit_basher *spibit
159
 	struct spi_bit_basher *spibit
155
 		= container_of ( bus, struct spi_bit_basher, bus );
160
 		= container_of ( bus, struct spi_bit_basher, bus );
156
-	uint32_t tmp;
161
+	uint32_t tmp_command;
162
+	uint32_t tmp_address;
163
+	uint32_t tmp_address_detect;
157
 
164
 
158
 	/* Set clock line to idle state */
165
 	/* Set clock line to idle state */
159
 	write_bit ( &spibit->basher, SPI_BIT_SCLK, 
166
 	write_bit ( &spibit->basher, SPI_BIT_SCLK, 
163
 	spi_bit_set_slave_select ( spibit, device->slave, SELECT_SLAVE );
170
 	spi_bit_set_slave_select ( spibit, device->slave, SELECT_SLAVE );
164
 
171
 
165
 	/* Transmit command */
172
 	/* Transmit command */
166
-	assert ( device->command_len <= ( 8 * sizeof ( tmp ) ) );
167
-	tmp = cpu_to_le32 ( command );
168
-	spi_bit_transfer ( spibit, &tmp, NULL, device->command_len,
173
+	assert ( device->command_len <= ( 8 * sizeof ( tmp_command ) ) );
174
+	tmp_command = cpu_to_le32 ( command );
175
+	spi_bit_transfer ( spibit, &tmp_command, NULL, device->command_len,
169
 			   SPI_BIT_BIG_ENDIAN );
176
 			   SPI_BIT_BIG_ENDIAN );
170
 
177
 
171
 	/* Transmit address, if present */
178
 	/* Transmit address, if present */
172
 	if ( address >= 0 ) {
179
 	if ( address >= 0 ) {
173
-		assert ( device->address_len <= ( 8 * sizeof ( tmp ) ) );
174
-		tmp = cpu_to_le32 ( address );
175
-		spi_bit_transfer ( spibit, &tmp, NULL, device->address_len,
176
-				   SPI_BIT_BIG_ENDIAN );
180
+		assert ( device->address_len <= ( 8 * sizeof ( tmp_address )));
181
+		tmp_address = cpu_to_le32 ( address );
182
+		if ( device->address_len == SPI_AUTODETECT_ADDRESS_LEN ) {
183
+			/* Autodetect address length.  This relies on
184
+			 * the device responding with a dummy zero
185
+			 * data bit before the first real data bit.
186
+			 */
187
+			DBGC ( spibit, "SPIBIT %p autodetecting device "
188
+			       "address length\n", spibit );
189
+			assert ( address == 0 );
190
+			device->address_len = 0;
191
+			do {
192
+				spi_bit_transfer ( spibit, &tmp_address,
193
+						   &tmp_address_detect, 1,
194
+						   SPI_BIT_BIG_ENDIAN );
195
+				device->address_len++;
196
+			} while ( le32_to_cpu ( tmp_address_detect ) & 1 );
197
+			DBGC ( spibit, "SPIBIT %p autodetected device address "
198
+			       "length %d\n", spibit, device->address_len );
199
+		} else {
200
+			spi_bit_transfer ( spibit, &tmp_address, NULL,
201
+					   device->address_len,
202
+					   SPI_BIT_BIG_ENDIAN );
203
+		}
177
 	}
204
 	}
178
 
205
 
179
 	/* Transmit/receive data */
206
 	/* Transmit/receive data */

+ 47
- 6
src/drivers/nvs/threewire.c View File

19
 FILE_LICENCE ( GPL2_OR_LATER );
19
 FILE_LICENCE ( GPL2_OR_LATER );
20
 
20
 
21
 #include <stddef.h>
21
 #include <stddef.h>
22
+#include <string.h>
22
 #include <assert.h>
23
 #include <assert.h>
23
 #include <unistd.h>
24
 #include <unistd.h>
24
 #include <gpxe/threewire.h>
25
 #include <gpxe/threewire.h>
42
 		     void *data, size_t len ) {
43
 		     void *data, size_t len ) {
43
 	struct spi_device *device = nvs_to_spi ( nvs );
44
 	struct spi_device *device = nvs_to_spi ( nvs );
44
 	struct spi_bus *bus = device->bus;
45
 	struct spi_bus *bus = device->bus;
46
+	int rc;
45
 
47
 
46
 	assert ( bus->mode == SPI_MODE_THREEWIRE );
48
 	assert ( bus->mode == SPI_MODE_THREEWIRE );
47
 
49
 
48
-	DBG ( "3wire %p reading %zd bytes at %04x\n", device, len, address );
50
+	DBGC ( device, "3wire %p reading %zd bytes at %04x\n",
51
+	       device, len, address );
52
+
53
+	if ( ( rc = bus->rw ( bus, device, THREEWIRE_READ, address,
54
+			      NULL, data, len ) ) != 0 ) {
55
+		DBGC ( device, "3wire %p could not read: %s\n",
56
+		       device, strerror ( rc ) );
57
+		return rc;
58
+	}
49
 
59
 
50
-	return bus->rw ( bus, device, THREEWIRE_READ, address,
51
-			 NULL, data, len );
60
+	return 0;
52
 }
61
 }
53
 
62
 
54
 /**
63
 /**
68
 
77
 
69
 	assert ( bus->mode == SPI_MODE_THREEWIRE );
78
 	assert ( bus->mode == SPI_MODE_THREEWIRE );
70
 
79
 
71
-	DBG ( "3wire %p writing %zd bytes at %04x\n", device, len, address );
80
+	DBGC ( device, "3wire %p writing %zd bytes at %04x\n",
81
+	       device, len, address );
72
 
82
 
73
 	/* Enable device for writing */
83
 	/* Enable device for writing */
74
 	if ( ( rc = bus->rw ( bus, device, THREEWIRE_EWEN,
84
 	if ( ( rc = bus->rw ( bus, device, THREEWIRE_EWEN,
75
-			      THREEWIRE_EWEN_ADDRESS, NULL, NULL, 0 ) ) != 0 )
85
+			      THREEWIRE_EWEN_ADDRESS, NULL, NULL, 0 ) ) != 0 ){
86
+		DBGC ( device, "3wire %p could not enable writing: %s\n",
87
+		       device, strerror ( rc ) );
76
 		return rc;
88
 		return rc;
89
+	}
77
 
90
 
78
 	/* Write data */
91
 	/* Write data */
79
 	if ( ( rc = bus->rw ( bus, device, THREEWIRE_WRITE, address,
92
 	if ( ( rc = bus->rw ( bus, device, THREEWIRE_WRITE, address,
80
-			      data, NULL, len ) ) != 0 )
93
+			      data, NULL, len ) ) != 0 ) {
94
+		DBGC ( device, "3wire %p could not write: %s\n",
95
+		       device, strerror ( rc ) );
81
 		return rc;
96
 		return rc;
97
+	}
82
 
98
 
83
 	/* Our model of an SPI bus doesn't provide a mechanism for
99
 	/* Our model of an SPI bus doesn't provide a mechanism for
84
 	 * "assert CS, wait for MISO to become high, so just wait for
100
 	 * "assert CS, wait for MISO to become high, so just wait for
88
 
104
 
89
 	return 0;
105
 	return 0;
90
 }
106
 }
107
+
108
+/**
109
+ * Autodetect device address length
110
+ *
111
+ * @v device		SPI device
112
+ * @ret rc		Return status code
113
+ */
114
+int threewire_detect_address_len ( struct spi_device *device ) {
115
+	struct nvs_device *nvs = &device->nvs;
116
+	int rc;
117
+
118
+	DBGC ( device, "3wire %p autodetecting address length\n", device );
119
+
120
+	device->address_len = SPI_AUTODETECT_ADDRESS_LEN;
121
+	if ( ( rc = threewire_read ( nvs, 0, NULL,
122
+				     ( 1 << nvs->word_len_log2 ) ) ) != 0 ) {
123
+		DBGC ( device, "3wire %p could not autodetect address "
124
+		       "length: %s\n", device, strerror ( rc ) );
125
+		return rc;
126
+	}
127
+
128
+	DBGC ( device, "3wire %p autodetected address length %d\n",
129
+	       device, device->address_len );
130
+	return 0;
131
+}

+ 8
- 0
src/include/gpxe/spi.h View File

104
 	unsigned int munge_address : 1;
104
 	unsigned int munge_address : 1;
105
 };
105
 };
106
 
106
 
107
+/**
108
+ * SPI magic autodetection address length
109
+ *
110
+ * Set @c spi_device::address_len to @c SPI_AUTODETECT_ADDRESS_LEN if
111
+ * the address length should be autodetected.
112
+ */
113
+#define SPI_AUTODETECT_ADDRESS_LEN 0
114
+
107
 static inline __attribute__ (( always_inline )) struct spi_device *
115
 static inline __attribute__ (( always_inline )) struct spi_device *
108
 nvs_to_spi ( struct nvs_device *nvs ) {
116
 nvs_to_spi ( struct nvs_device *nvs ) {
109
 	return container_of ( nvs, struct spi_device, nvs );
117
 	return container_of ( nvs, struct spi_device, nvs );

+ 1
- 0
src/include/gpxe/threewire.h View File

45
 			    void *data, size_t len );
45
 			    void *data, size_t len );
46
 extern int threewire_write ( struct nvs_device *nvs, unsigned int address,
46
 extern int threewire_write ( struct nvs_device *nvs, unsigned int address,
47
 			     const void *data, size_t len );
47
 			     const void *data, size_t len );
48
+extern int threewire_detect_address_len ( struct spi_device *device );
48
 
49
 
49
 /**
50
 /**
50
  * @defgroup tdevs Three-wire device types
51
  * @defgroup tdevs Three-wire device types

Loading…
Cancel
Save