瀏覽代碼

[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 年之前
父節點
當前提交
92a9978b44
共有 4 個文件被更改,包括 95 次插入18 次删除
  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 查看文件

@@ -60,8 +60,8 @@ static void spi_bit_set_slave_select ( struct spi_bit_basher *spibit,
60 60
 	struct bit_basher *basher = &spibit->basher;
61 61
 
62 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 66
 	spi_bit_delay();
67 67
 	write_bit ( basher, SPI_BIT_SS ( slave ), state );
@@ -96,7 +96,8 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
96 96
 	unsigned int bit;
97 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 102
 	for ( step = 0 ; step < ( len * 2 ) ; step++ ) {
102 103
 		/* Calculate byte offset and byte mask */
@@ -113,6 +114,8 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
113 114
 			if ( data_out ) {
114 115
 				byte = ( data_out + byte_offset );
115 116
 				bit = ( *byte & byte_mask );
117
+				DBGCP ( spibit, "SPIBIT %p wrote bit %d\n",
118
+					spibit, ( bit ? 1 : 0 ) );
116 119
 			} else {
117 120
 				bit = 0;
118 121
 			}
@@ -123,6 +126,8 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
123 126
 			/* Shift data in */
124 127
 			bit = read_bit ( basher, SPI_BIT_MISO );
125 128
 			if ( data_in ) {
129
+				DBGCP ( spibit, "SPIBIT %p read bit %d\n",
130
+					spibit, ( bit ? 1 : 0 ) );
126 131
 				byte = ( data_in + byte_offset );
127 132
 				*byte &= ~byte_mask;
128 133
 				*byte |= ( bit & byte_mask );
@@ -131,7 +136,7 @@ static void spi_bit_transfer ( struct spi_bit_basher *spibit,
131 136
 
132 137
 		/* Toggle clock line */
133 138
 		spi_bit_delay();
134
-		sclk = ~sclk;
139
+		sclk ^= 1;
135 140
 		write_bit ( basher, SPI_BIT_SCLK, sclk );
136 141
 	}
137 142
 }
@@ -153,7 +158,9 @@ static int spi_bit_rw ( struct spi_bus *bus, struct spi_device *device,
153 158
 			const void *data_out, void *data_in, size_t len ) {
154 159
 	struct spi_bit_basher *spibit
155 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 165
 	/* Set clock line to idle state */
159 166
 	write_bit ( &spibit->basher, SPI_BIT_SCLK, 
@@ -163,17 +170,37 @@ static int spi_bit_rw ( struct spi_bus *bus, struct spi_device *device,
163 170
 	spi_bit_set_slave_select ( spibit, device->slave, SELECT_SLAVE );
164 171
 
165 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 176
 			   SPI_BIT_BIG_ENDIAN );
170 177
 
171 178
 	/* Transmit address, if present */
172 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 206
 	/* Transmit/receive data */

+ 47
- 6
src/drivers/nvs/threewire.c 查看文件

@@ -19,6 +19,7 @@
19 19
 FILE_LICENCE ( GPL2_OR_LATER );
20 20
 
21 21
 #include <stddef.h>
22
+#include <string.h>
22 23
 #include <assert.h>
23 24
 #include <unistd.h>
24 25
 #include <gpxe/threewire.h>
@@ -42,13 +43,21 @@ int threewire_read ( struct nvs_device *nvs, unsigned int address,
42 43
 		     void *data, size_t len ) {
43 44
 	struct spi_device *device = nvs_to_spi ( nvs );
44 45
 	struct spi_bus *bus = device->bus;
46
+	int rc;
45 47
 
46 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,17 +77,24 @@ int threewire_write ( struct nvs_device *nvs, unsigned int address,
68 77
 
69 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 83
 	/* Enable device for writing */
74 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 88
 		return rc;
89
+	}
77 90
 
78 91
 	/* Write data */
79 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 96
 		return rc;
97
+	}
82 98
 
83 99
 	/* Our model of an SPI bus doesn't provide a mechanism for
84 100
 	 * "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,
88 104
 
89 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 查看文件

@@ -104,6 +104,14 @@ struct spi_device {
104 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 115
 static inline __attribute__ (( always_inline )) struct spi_device *
108 116
 nvs_to_spi ( struct nvs_device *nvs ) {
109 117
 	return container_of ( nvs, struct spi_device, nvs );

+ 1
- 0
src/include/gpxe/threewire.h 查看文件

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

Loading…
取消
儲存