1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  BCM91125CFM8 SPI driver                          File: cfm8_spi.c
5    *
6    *  Basic operations for BCM91125CFM8 SPI interface.
7    *
8    *********************************************************************
9    *
10    *  Copyright 2004
11    *  Broadcom Corporation. All rights reserved.
12    *
13    *  This software is furnished under license and may be used and
14    *  copied only in accordance with the following terms and
15    *  conditions.  Subject to these conditions, you may download,
16    *  copy, install, use, modify and distribute modified or unmodified
17    *  copies of this software in source and/or binary form.  No title
18    *  or ownership is transferred hereby.
19    *
20    *  1) Any source code used, modified or distributed must reproduce
21    *     and retain this copyright notice and list of conditions
22    *     as they appear in the source file.
23    *
24    *  2) No right is granted to use any trade name, trademark, or
25    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
26    *     name may not be used to endorse or promote products derived
27    *     from this software without the prior written permission of
28    *     Broadcom Corporation.
29    *
30    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
31    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
32    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
33    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
34    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
35    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
36    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
37    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
38    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
39    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
40    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
41    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
42    *     THE POSSIBILITY OF SUCH DAMAGE.
43    ********************************************************************* */
44
45#include "cfe.h"
46
47#include "cfe_spi.h"
48
49/*  *********************************************************************
50    *  Types
51    ********************************************************************* */
52
53typedef struct cfm8_spi_softc_s {
54    unsigned long base;         /* physical base address */
55    volatile uint8_t *addr;     /* logical base address (uncached) */
56    uint8_t data_out;           /* current write value */
57    uint8_t data_in;            /* last read value */
58} cfm8_spi_softc_t;
59
60/*  *********************************************************************
61    *  Access macros
62    ********************************************************************* */
63
64#define SPI_DELAY(s,c) \
65do { \
66    uint8_t _i,_t; \
67    for (_i = 0; _i < c; _i++) { \
68        _t = *(s)->addr; \
69        if (_t); \
70    }  \
71} while (0)
72
73#define SPI_DATA_SCK            0x01 /* Slave Clock bit */
74#define SPI_DATA_SS             0x02 /* Slave Select bit */
75#define SPI_DATA_MOSI           0x04 /* Master-Out Slave-In bit */
76#define SPI_DATA_MISO           0x08 /* Master-In Slave-Out bit */
77
78#define SPI_ASSERT_CS(s)        (s)->data_out = SPI_DATA_SS | SPI_DATA_SCK
79#define SPI_DEASSERT_CS(s)      (s)->data_out = SPI_DATA_SCK
80#define SPI_CLK_INIT(s)         (s)->data_out = SPI_DATA_SCK
81#define SPI_CLK_TOGGLE(s)       (s)->data_out ^= SPI_DATA_SCK
82#define SPI_CLK_DELAY(s)        SPI_DELAY(s,4)
83
84#define SPI_DATA_GET(s)         ((s)->data_in & SPI_DATA_MISO) ? 1 : 0
85#define SPI_DATA_SET(s,v) \
86    do { if (v) { \
87            (s)->data_out |= SPI_DATA_MOSI; \
88        } else { \
89            (s)->data_out &= ~SPI_DATA_MOSI; \
90        } \
91    } while (0)
92
93#define SPI_DATA_IN(s)          (s)->data_in = *(s)->addr
94#define SPI_DATA_OUT(s)         *(s)->addr = (s)->data_out
95
96/*  *********************************************************************
97    *  Forward declarations
98    ********************************************************************* */
99
100static cfe_spi_channel_t *cfm8_spi_attach(cfe_spi_t *ops,uint64_t probe_a,uint64_t probe_b);
101static int cfm8_spi_init(cfe_spi_channel_t *chan);
102static int cfm8_spi_enable(cfe_spi_channel_t *chan,uint8_t slave);
103static int cfm8_spi_disable(cfe_spi_channel_t *chan,uint8_t slave);
104static int cfm8_spi_read(cfe_spi_channel_t *chan,uint8_t *buf,int len,uint8_t data_out);
105static int cfm8_spi_write(cfe_spi_channel_t *chan,uint8_t *buf,int len);
106
107/*  *********************************************************************
108    *  SPI operations
109    ********************************************************************* */
110
111cfe_spi_t cfm8_spi = {
112    cfm8_spi_attach,
113    cfm8_spi_init,
114    cfm8_spi_enable,
115    cfm8_spi_disable,
116    cfm8_spi_write,
117    cfm8_spi_read
118};
119
120
121static cfe_spi_channel_t *cfm8_spi_attach(cfe_spi_t *ops,uint64_t probe_a,uint64_t probe_b)
122{
123    cfe_spi_channel_t *chan;
124    cfm8_spi_softc_t *softc;
125
126    chan = KMALLOC(sizeof(cfe_spi_channel_t)+sizeof(cfm8_spi_softc_t),0);
127
128    if (!chan) return NULL;
129
130    chan->ops = ops;
131    chan->softc = (void *) (chan+1);
132
133    softc = chan->softc;
134    softc->base = probe_a;
135    softc->addr = (volatile uint8_t *)PHYS_TO_K1(softc->base);
136
137    return chan;
138}
139
140static int cfm8_spi_init(cfe_spi_channel_t *chan)
141{
142    return 0;
143}
144
145static int cfm8_spi_enable(cfe_spi_channel_t *chan,uint8_t slave)
146{
147    cfm8_spi_softc_t *softc = chan->softc;
148
149    /* Set up initial clock state before asserting chip select */
150    SPI_CLK_INIT(softc);
151    SPI_DATA_OUT(softc);
152    SPI_CLK_DELAY(softc);
153
154    /* Assert chip select */
155    SPI_ASSERT_CS(softc);
156    SPI_DATA_OUT(softc);
157    SPI_CLK_DELAY(softc);
158
159    return 0;
160}
161
162static int cfm8_spi_disable(cfe_spi_channel_t *chan,uint8_t slave)
163{
164    cfm8_spi_softc_t *softc = chan->softc;
165
166    /* Deassert chip select */
167    SPI_DEASSERT_CS(softc);
168    SPI_DATA_OUT(softc);
169    SPI_CLK_DELAY(softc);
170
171    /* Extra delay is solely for debug purposes */
172    SPI_CLK_DELAY(softc);
173    SPI_CLK_DELAY(softc);
174
175    return 0;
176}
177
178static int cfm8_spi_read(cfe_spi_channel_t *chan,uint8_t *buf,int len,uint8_t data_out)
179{
180    cfm8_spi_softc_t *softc = chan->softc;
181    int i;
182    uint8_t mask;
183
184    for (i = 0; i < len; i++) {
185        buf[i] = 0;
186        for (mask = 0x80; mask; mask >>= 1) {
187            /* Dummy read to even up duty-cycle */
188            SPI_DATA_IN(softc);
189            /* Output zeros when reading */
190            SPI_DATA_SET(softc,(mask & data_out));
191            SPI_CLK_TOGGLE(softc);
192            /* Input data is sampled on falling edge */
193	    SPI_DATA_OUT(softc);
194            /* Allow data to stabilize */
195            SPI_CLK_DELAY(softc);
196            SPI_DATA_IN(softc);
197            if (SPI_DATA_GET(softc)) {
198  		buf[i] |= mask;
199            }
200            SPI_CLK_TOGGLE(softc);
201	    SPI_DATA_OUT(softc);
202            SPI_CLK_DELAY(softc);
203        }
204    }
205    return 0;
206}
207
208static int cfm8_spi_write(cfe_spi_channel_t *chan,uint8_t *buf,int len)
209{
210    cfm8_spi_softc_t *softc = chan->softc;
211    int i;
212    uint8_t mask;
213
214    for (i = 0; i < len; i++) {
215        for (mask = 0x80; mask; mask >>= 1) {
216            /* Dummy read to even up duty-cycle */
217            SPI_DATA_IN(softc);
218            /* Set up data on falling edge to allow it to stabilize */
219            SPI_DATA_SET(softc,mask & buf[i]);
220            SPI_CLK_TOGGLE(softc);
221            /* Input data is sampled on falling edge */
222	    SPI_DATA_OUT(softc);
223            /* Allow data to stabilize */
224            SPI_CLK_DELAY(softc);
225            /* Dummy read to even up duty-cycle */
226            SPI_DATA_IN(softc);
227            /* Output data is sampled on rising edge */
228            SPI_CLK_TOGGLE(softc);
229	    SPI_DATA_OUT(softc);
230            SPI_CLK_DELAY(softc);
231        }
232    }
233    return 0;
234}
235