added stdio.h to includes for DBG compilation
[people/xl0/gpxe.git] / src / drivers / bitbash / spi_bit.c
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stdio.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <string.h>
23 #include <errno.h>
24 #include <assert.h>
25 #include <timer.h>
26 #include <gpxe/bitbash.h>
27 #include <gpxe/spi.h>
28
29 /** @file
30  *
31  * SPI bit-bashing interface
32  *
33  */
34
35 /** Delay between SCLK changes and around SS changes */
36 static void spi_delay ( void ) {
37         udelay ( SPI_UDELAY );
38 }
39
40 /**
41  * Select/deselect slave
42  *
43  * @v spi               SPI bit-bashing interface
44  * @v slave             Slave number
45  * @v state             Slave select state
46  *
47  * @c state must be set to zero to select the specified slave, or to
48  * @c SPI_MODE_SSPOL to deselect the slave.
49  */
50 static void spi_bit_set_slave_select ( struct spi_bit_basher *spibit,
51                                        unsigned int slave,
52                                        unsigned int state ) {
53         struct bit_basher *basher = &spibit->basher;
54
55         state ^= ( spibit->spi.mode & SPI_MODE_SSPOL );
56         DBG ( "Setting slave %d select %s\n", slave,
57               ( state ? "high" : "low" ) );
58
59         spi_delay();
60         write_bit ( basher, SPI_BIT_SS ( slave ), state );
61         spi_delay();
62 }
63
64 /**
65  * Select slave
66  *
67  * @v spi               SPI interface
68  * @v slave             Slave number
69  */
70 static void spi_bit_select_slave ( struct spi_interface *spi,
71                                    unsigned int slave ) {
72         struct spi_bit_basher *spibit
73                 = container_of ( spi, struct spi_bit_basher, spi );
74
75         spibit->slave = slave;
76         spi_bit_set_slave_select ( spibit, slave, 0 );
77 }
78
79 /**
80  * Deselect slave
81  *
82  * @v spi               SPI interface
83  */
84 static void spi_bit_deselect_slave ( struct spi_interface *spi ) {
85         struct spi_bit_basher *spibit
86                 = container_of ( spi, struct spi_bit_basher, spi );
87
88         spi_bit_set_slave_select ( spibit, spibit->slave, SPI_MODE_SSPOL );
89 }
90
91 /**
92  * Transfer bits over SPI bit-bashing interface
93  *
94  * @v spi               SPI interface
95  * @v data_out          TX data buffer (or NULL)
96  * @v data_in           RX data buffer (or NULL)
97  * @v len               Length of transfer (in @b bits)
98  *
99  * This issues @c len clock cycles on the SPI bus, shifting out data
100  * from the @c data_out buffer to the MOSI line and shifting in data
101  * from the MISO line to the @c data_in buffer.  If @c data_out is
102  * NULL, then the data sent will be all zeroes.  If @c data_in is
103  * NULL, then the incoming data will be discarded.
104  */
105 static void spi_bit_transfer ( struct spi_interface *spi, const void *data_out,
106                                void *data_in, unsigned int len ) {
107         struct spi_bit_basher *spibit
108                 = container_of ( spi, struct spi_bit_basher, spi );
109         struct bit_basher *basher = &spibit->basher;
110         unsigned int sclk = ( ( spi->mode & SPI_MODE_CPOL ) ? 1 : 0 );
111         unsigned int cpha = ( ( spi->mode & SPI_MODE_CPHA ) ? 1 : 0 );
112         unsigned int offset;
113         unsigned int mask;
114         unsigned int bit;
115         int step;
116
117         DBG ( "Transferring %d bits in mode %x\n", len, spi->mode );
118
119         for ( step = ( ( len * 2 ) - 1 ) ; step >= 0 ; step-- ) {
120                 /* Calculate byte offset within data and bit mask */
121                 offset = ( step / 16 );
122                 mask = ( 1 << ( ( step % 16 ) / 2 ) );
123                 
124                 /* Shift data in or out */
125                 if ( sclk == cpha ) {
126                         const uint8_t *byte;
127
128                         /* Shift data out */
129                         if ( data_out ) {
130                                 byte = ( data_out + offset );
131                                 bit = ( *byte & mask );
132                         } else {
133                                 bit = 0;
134                         }
135                         write_bit ( basher, SPI_BIT_MOSI, bit );
136                 } else {
137                         uint8_t *byte;
138
139                         /* Shift data in */
140                         bit = read_bit ( basher, SPI_BIT_MISO );
141                         if ( data_in ) {
142                                 byte = ( data_in + offset );
143                                 *byte &= ~mask;
144                                 *byte |= ( bit & mask );
145                         }
146                 }
147
148                 /* Toggle clock line */
149                 spi_delay();
150                 sclk = ~sclk;
151                 write_bit ( basher, SPI_BIT_SCLK, sclk );
152         }
153 }
154
155 /**
156  * Initialise SPI bit-bashing interface
157  *
158  * @v spibit            SPI bit-bashing interface
159  */
160 void init_spi_bit_basher ( struct spi_bit_basher *spibit ) {
161         assert ( &spibit->basher.read != NULL );
162         assert ( &spibit->basher.write != NULL );
163         spibit->spi.select_slave = spi_bit_select_slave;
164         spibit->spi.deselect_slave = spi_bit_deselect_slave;
165         spibit->spi.transfer = spi_bit_transfer;
166 }