Browse Source

Support cards such as natsemi which treat the data as little-endian

(i.e. LSB transmitted first on the wire), even though SPI commands and
addresses always have to be big-endian.
tags/v0.9.3
Michael Brown 17 years ago
parent
commit
95cb7aaacf
2 changed files with 39 additions and 17 deletions
  1. 24
    17
      src/drivers/bitbash/spi_bit.c
  2. 15
    0
      src/include/gpxe/spi_bit.h

+ 24
- 17
src/drivers/bitbash/spi_bit.c View File

73
  * @v data_out		TX data buffer (or NULL)
73
  * @v data_out		TX data buffer (or NULL)
74
  * @v data_in		RX data buffer (or NULL)
74
  * @v data_in		RX data buffer (or NULL)
75
  * @v len		Length of transfer (in @b bits)
75
  * @v len		Length of transfer (in @b bits)
76
+ * @v endianness	Endianness of this data transfer
76
  *
77
  *
77
  * This issues @c len clock cycles on the SPI bus, shifting out data
78
  * This issues @c len clock cycles on the SPI bus, shifting out data
78
  * from the @c data_out buffer to the MOSI line and shifting in data
79
  * from the @c data_out buffer to the MOSI line and shifting in data
82
  */
83
  */
83
 static void spi_bit_transfer ( struct spi_bit_basher *spibit,
84
 static void spi_bit_transfer ( struct spi_bit_basher *spibit,
84
 			       const void *data_out, void *data_in,
85
 			       const void *data_out, void *data_in,
85
-			       unsigned int len ) {
86
+			       unsigned int len, int endianness ) {
86
 	struct spi_bus *bus = &spibit->bus;
87
 	struct spi_bus *bus = &spibit->bus;
87
 	struct bit_basher *basher = &spibit->basher;
88
 	struct bit_basher *basher = &spibit->basher;
88
 	unsigned int sclk = ( ( bus->mode & SPI_MODE_CPOL ) ? 1 : 0 );
89
 	unsigned int sclk = ( ( bus->mode & SPI_MODE_CPOL ) ? 1 : 0 );
89
 	unsigned int cpha = ( ( bus->mode & SPI_MODE_CPHA ) ? 1 : 0 );
90
 	unsigned int cpha = ( ( bus->mode & SPI_MODE_CPHA ) ? 1 : 0 );
90
-	unsigned int offset;
91
-	unsigned int mask;
91
+	unsigned int bit_offset;
92
+	unsigned int byte_offset;
93
+	unsigned int byte_mask;
92
 	unsigned int bit;
94
 	unsigned int bit;
93
-	int step;
95
+	unsigned int step;
94
 
96
 
95
 	DBG ( "Transferring %d bits in mode %x\n", len, bus->mode );
97
 	DBG ( "Transferring %d bits in mode %x\n", len, bus->mode );
96
 
98
 
97
-	for ( step = ( ( len * 2 ) - 1 ) ; step >= 0 ; step-- ) {
98
-		/* Calculate byte offset within data and bit mask */
99
-		offset = ( step / 16 );
100
-		mask = ( 1 << ( ( step % 16 ) / 2 ) );
101
-		
99
+	for ( step = 0 ; step < ( len * 2 ) ; step++ ) {
100
+		/* Calculate byte offset and byte mask */
101
+		bit_offset = ( ( endianness == SPI_BIT_BIG_ENDIAN ) ?
102
+			       ( len - ( step / 2 ) - 1 ) : ( step / 2 ) );
103
+		byte_offset = ( bit_offset / 8 );
104
+		byte_mask = ( 1 << ( bit_offset % 8 ) );
105
+
102
 		/* Shift data in or out */
106
 		/* Shift data in or out */
103
 		if ( sclk == cpha ) {
107
 		if ( sclk == cpha ) {
104
 			const uint8_t *byte;
108
 			const uint8_t *byte;
105
 
109
 
106
 			/* Shift data out */
110
 			/* Shift data out */
107
 			if ( data_out ) {
111
 			if ( data_out ) {
108
-				byte = ( data_out + offset );
109
-				bit = ( *byte & mask );
112
+				byte = ( data_out + byte_offset );
113
+				bit = ( *byte & byte_mask );
110
 			} else {
114
 			} else {
111
 				bit = 0;
115
 				bit = 0;
112
 			}
116
 			}
117
 			/* Shift data in */
121
 			/* Shift data in */
118
 			bit = read_bit ( basher, SPI_BIT_MISO );
122
 			bit = read_bit ( basher, SPI_BIT_MISO );
119
 			if ( data_in ) {
123
 			if ( data_in ) {
120
-				byte = ( data_in + offset );
121
-				*byte &= ~mask;
122
-				*byte |= ( bit & mask );
124
+				byte = ( data_in + byte_offset );
125
+				*byte &= ~byte_mask;
126
+				*byte |= ( bit & byte_mask );
123
 			}
127
 			}
124
 		}
128
 		}
125
 
129
 
155
 	/* Transmit command */
159
 	/* Transmit command */
156
 	assert ( device->command_len <= ( 8 * sizeof ( tmp ) ) );
160
 	assert ( device->command_len <= ( 8 * sizeof ( tmp ) ) );
157
 	tmp = cpu_to_le32 ( command );
161
 	tmp = cpu_to_le32 ( command );
158
-	spi_bit_transfer ( spibit, &tmp, NULL, device->command_len );
162
+	spi_bit_transfer ( spibit, &tmp, NULL, device->command_len,
163
+			   SPI_BIT_BIG_ENDIAN );
159
 
164
 
160
 	/* Transmit address, if present */
165
 	/* Transmit address, if present */
161
 	if ( address >= 0 ) {
166
 	if ( address >= 0 ) {
162
 		assert ( device->address_len <= ( 8 * sizeof ( tmp ) ) );
167
 		assert ( device->address_len <= ( 8 * sizeof ( tmp ) ) );
163
 		tmp = cpu_to_le32 ( address );
168
 		tmp = cpu_to_le32 ( address );
164
-		spi_bit_transfer ( spibit, &tmp, NULL, device->address_len );
169
+		spi_bit_transfer ( spibit, &tmp, NULL, device->address_len,
170
+				   SPI_BIT_BIG_ENDIAN );
165
 	}
171
 	}
166
 
172
 
167
 	/* Transmit/receive data */
173
 	/* Transmit/receive data */
168
-	spi_bit_transfer ( spibit, data_out, data_in, ( len * 8 ) );
174
+	spi_bit_transfer ( spibit, data_out, data_in, ( len * 8 ),
175
+			   spibit->endianness );
169
 
176
 
170
 	/* Deassert chip select on specified slave */
177
 	/* Deassert chip select on specified slave */
171
 	spi_bit_set_slave_select ( spibit, device->slave, DESELECT_SLAVE );
178
 	spi_bit_set_slave_select ( spibit, device->slave, DESELECT_SLAVE );

+ 15
- 0
src/include/gpxe/spi_bit.h View File

16
 	struct spi_bus bus;
16
 	struct spi_bus bus;
17
 	/** Bit-bashing interface */
17
 	/** Bit-bashing interface */
18
 	struct bit_basher basher;
18
 	struct bit_basher basher;
19
+	/** Endianness of data
20
+	 *
21
+	 * SPI commands and addresses are always big-endian (i.e. MSB
22
+	 * transmitted first on the wire), but some cards
23
+	 * (e.g. natsemi) choose to regard the data stored in the
24
+	 * EEPROM as little-endian (i.e. LSB transmitted first on the
25
+	 * wire).
26
+	 */
27
+	int endianness;
19
 };
28
 };
20
 
29
 
21
 /** Bit indices used for SPI bit-bashing interface */
30
 /** Bit indices used for SPI bit-bashing interface */
41
 /** Delay between SCLK transitions */
50
 /** Delay between SCLK transitions */
42
 #define SPI_BIT_UDELAY 1
51
 #define SPI_BIT_UDELAY 1
43
 
52
 
53
+/** SPI bit basher treats data as big-endian */
54
+#define SPI_BIT_BIG_ENDIAN 0
55
+
56
+/** SPI bit basher treats data as little-endian */
57
+#define SPI_BIT_LITTLE_ENDIAN 1
58
+
44
 extern void init_spi_bit_basher ( struct spi_bit_basher *spibit );
59
 extern void init_spi_bit_basher ( struct spi_bit_basher *spibit );
45
 
60
 
46
 #endif /* _GPXE_SPI_BIT_H */
61
 #endif /* _GPXE_SPI_BIT_H */

Loading…
Cancel
Save