1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  RoboSwitch SPI driver			File: dev_robo_spi.c
5    *
6    *  This is a simple driver for accessing RoboSwitch registers
7    *  using the Motorola SPI serial protocol.
8    *
9    *********************************************************************
10    *
11    *  Copyright 2004
12    *  Broadcom Corporation. All rights reserved.
13    *
14    *  This software is furnished under license and may be used and
15    *  copied only in accordance with the following terms and
16    *  conditions.  Subject to these conditions, you may download,
17    *  copy, install, use, modify and distribute modified or unmodified
18    *  copies of this software in source and/or binary form.  No title
19    *  or ownership is transferred hereby.
20    *
21    *  1) Any source code used, modified or distributed must reproduce
22    *     and retain this copyright notice and list of conditions
23    *     as they appear in the source file.
24    *
25    *  2) No right is granted to use any trade name, trademark, or
26    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
27    *     name may not be used to endorse or promote products derived
28    *     from this software without the prior written permission of
29    *     Broadcom Corporation.
30    *
31    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
32    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
33    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
34    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
35    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
36    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
37    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
39    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
40    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
41    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
42    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
43    *     THE POSSIBILITY OF SUCH DAMAGE.
44    ********************************************************************* */
45
46
47#include "cfe.h"
48
49#include "cfe_spi.h"
50
51#undef _ROBOSPI_DEBUG_
52
53#ifndef CFG_ROBO_FAST_SPI
54#define CFG_ROBO_FAST_SPI 1
55#endif
56
57/*  *********************************************************************
58    *  Robo definitions
59    ********************************************************************* */
60
61#define _DD_MAKEMASK1(n) (1 << (n))
62#define _DD_MAKEMASK(v,n) ((((1)<<(v))-1) << (n))
63#define _DD_MAKEVALUE(v,n) ((v) << (n))
64#define _DD_GETVALUE(v,n,m) (((v) & (m)) >> (n))
65
66/* Global Robo registers */
67#define R_ROBOSPI_SPI_DATA      0xf0
68#define R_ROBOSPI_SPI_STATUS    0xfe
69#define R_ROBOSPI_PAGE          0xff
70
71/* Robo SPI Status registers */
72#define M_SPISTAT_TXRDY         _DD_MAKEMASK1(0)
73#define M_SPISTAT_RXRDY         _DD_MAKEMASK1(1)
74#define M_SPISTAT_MDIO_START    _DD_MAKEMASK1(2)
75#define M_SPISTAT_RACK          _DD_MAKEMASK1(5)
76#define M_SPISTAT_WCOL          _DD_MAKEMASK1(6)
77#define M_SPISTAT_SPIF          _DD_MAKEMASK1(7)
78
79/* Robo Fast SPI read response */
80#define M_SPISTAT_FAST_RACK     _DD_MAKEMASK1(0)
81
82#define POLL_DELAY()            cfe_usleep(10)
83
84/* Convenience macro for writing a single byte */
85#define SPI_WRITE8(c,b) do { uint8_t _d8 = b; SPI_WRITE(c,&_d8,1); } while (0)
86
87/*  *********************************************************************
88    *  Forward declarations
89    ********************************************************************* */
90
91static void robo_probe(cfe_driver_t *drv,
92                       unsigned long probe_a,unsigned long probe_b,
93                       void *probe_ptr);
94
95static int robo_open(cfe_devctx_t *ctx);
96static int robo_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
97static int robo_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
98static int robo_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
99static int robo_close(cfe_devctx_t *ctx);
100
101
102/*  *********************************************************************
103    *  Device Dispatch
104    ********************************************************************* */
105
106static cfe_devdisp_t robo_dispatch = {
107    robo_open,
108    robo_read,
109    NULL,
110    robo_write,
111    robo_ioctl,
112    robo_close,
113    NULL,
114    NULL
115};
116
117const cfe_driver_t spi_robo = {
118    "Robo Management",
119    "robo",
120    CFE_DEV_OTHER,
121    &robo_dispatch,
122    robo_probe
123};
124
125typedef struct robo_s {
126    cfe_spi_channel_t *spi_channel;
127    int cur_page;
128    int fast_spi;
129} robo_spi_t;
130
131static void _revert_buffer(uint8_t *buf,int len)
132{
133    uint8_t tbuf[8];
134    int i;
135
136    if (len < 2 || len > 8) return;
137
138    memcpy(tbuf,buf,len);
139    for (i = 0; i < len; i++) {
140        buf[i] = tbuf[len-i-1];
141    }
142}
143
144/*  *********************************************************************
145    *  Robo SPI Protocol
146    *
147    ********************************************************************* */
148
149static int robo_read_reg(robo_spi_t *softc,int reg,uint8_t *buf,int len)
150{
151    cfe_spi_channel_t *chan = softc->spi_channel;
152
153    SPI_ENABLE(chan,0);
154
155    SPI_WRITE8(chan,0x60);
156    SPI_WRITE8(chan,reg);
157    SPI_READ(chan,buf,len,0);
158
159    SPI_DISABLE(chan,0);
160
161    return 0;
162}
163
164static int robo_write_reg(robo_spi_t *softc,int reg,uint8_t *buf,int len)
165{
166    cfe_spi_channel_t *chan = softc->spi_channel;
167
168    SPI_ENABLE(chan,0);
169
170    SPI_WRITE8(chan,0x61);
171    SPI_WRITE8(chan,reg);
172    SPI_WRITE(chan,buf,len);
173
174    SPI_DISABLE(chan,0);
175
176    return 0;
177}
178
179#if CFG_ROBO_FAST_SPI
180static int robo_fast_rack_poll(robo_spi_t *softc)
181{
182    cfe_spi_channel_t *chan = softc->spi_channel;
183    uint8_t status;
184    int timeout = 10;
185
186    while (timeout-- > 0) {
187
188        SPI_READ(chan,&status,1,0);
189
190        if (status & M_SPISTAT_FAST_RACK) {
191            return 0;
192        }
193
194        POLL_DELAY();
195    }
196#ifdef _ROBOSPI_DEBUG_
197    printf("SPI: fast RACK poll FAILED status=0x%02X\n",status);
198#endif
199
200    return -1;
201}
202#endif
203
204static int robo_status_poll(robo_spi_t *softc,uint8_t mask,uint8_t val)
205{
206    uint8_t status;
207    int timeout = 100;
208
209    while (timeout-- > 0) {
210
211        robo_read_reg(softc,R_ROBOSPI_SPI_STATUS,&status,1);
212
213        if ((status & mask) == val) {
214            return 0;
215        }
216
217        POLL_DELAY();
218    }
219#ifdef _ROBOSPI_DEBUG_
220    printf("SPI: poll status FAILED status=0x%02X\n",status);
221#endif
222
223    return -1;
224}
225
226static int robo_select_page(robo_spi_t *softc,int page)
227{
228    uint8_t data;
229
230    if (softc->cur_page == page) {
231        return 0;
232    }
233    softc->cur_page = page;
234
235    data = page;
236    robo_write_reg(softc,R_ROBOSPI_PAGE,&data,1);
237
238    return 0;
239}
240
241static int robo_reset(robo_spi_t *softc)
242{
243    /* Force page change */
244    softc->cur_page = -1;
245    robo_select_page(softc,0);
246    softc->cur_page = -1;
247
248    return 0;
249}
250
251/*  *********************************************************************
252    *  robo_probe(drv,probe_a,probe_b,probe_ptr)
253    *
254    *  Our probe routine.  Attach a SPI device to the firmware.
255    *
256    *  Input parameters:
257    *  	   drv - driver structure
258    *  	   probe_a - SPI channel
259    *  	   probe_b - slave id (currently not used)
260    *  	   probe_ptr - not used
261    *
262    *  Return value:
263    *  	   nothing
264    ********************************************************************* */
265
266static void robo_probe(cfe_driver_t *drv,
267                       unsigned long probe_a,unsigned long probe_b,
268                       void *probe_ptr)
269{
270    robo_spi_t *softc;
271    char descr[80];
272
273    softc = (robo_spi_t *) KMALLOC(sizeof(robo_spi_t),0);
274
275    if (!softc) return;
276
277    /*
278     * Probe_a is the SPI channel number
279     * Probe_b is unused
280     * Probe_ptr is unused.
281     */
282
283    softc->spi_channel = SPI_CHANNEL((int)probe_a);
284    softc->cur_page = -1;
285
286    xsprintf(descr,"%s on SPI channel %d",
287	     drv->drv_description,(int)probe_a);
288    cfe_attach(drv,softc,NULL,descr);
289}
290
291/*  *********************************************************************
292    *  robo_open(ctx)
293    *
294    *  Open this device.
295    *
296    *  Input parameters:
297    *  	   ctx - device context (can obtain our softc here)
298    *
299    *  Return value:
300    *  	   0 if ok
301    *  	   else error code
302    ********************************************************************* */
303
304static int robo_open(cfe_devctx_t *ctx)
305{
306    robo_spi_t *softc = ctx->dev_softc;
307
308    return softc->spi_channel ? 0 : -1;
309}
310
311/*  *********************************************************************
312    *  robo_read(ctx,buffer)
313    *
314    *  Read bytes from the device.
315    *
316    *  Input parameters:
317    *  	   ctx - device context (can obtain our softc here)
318    *  	   buffer - buffer descriptor (target buffer, length, offset)
319    *
320    *  Return value:
321    *  	   number of bytes read
322    *  	   -1 if an error occured
323    ********************************************************************* */
324
325static int robo_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
326{
327    robo_spi_t *softc = ctx->dev_softc;
328#if CFG_ROBO_FAST_SPI
329    cfe_spi_channel_t *chan = softc->spi_channel;
330#endif
331    unsigned char *bptr;
332    int blen;
333    int page;
334    int reg;
335
336    bptr = HSADDR2PTR(buffer->buf_ptr);		/* XXX not 64-bit compliant */
337    blen = buffer->buf_length;
338
339    page = (buffer->buf_offset >> 8) & 0xFF;
340    reg = buffer->buf_offset & 0xFF;
341
342#ifdef _ROBOSPI_DEBUG_
343    printf("robo_read: page=0x%02X reg=0x%02X\n",page,reg);
344#endif
345
346    if (blen > 8) return -1;
347
348    if (robo_status_poll(softc,M_SPISTAT_MDIO_START,0) < 0) {
349        /* Timeout */
350        robo_reset(softc);
351        return -1;
352    }
353
354    robo_select_page(softc,page);
355
356#if CFG_ROBO_FAST_SPI
357
358    SPI_ENABLE(chan,0);
359
360    SPI_WRITE8(chan,0x10);
361    SPI_WRITE8(chan,reg);
362
363    if (robo_fast_rack_poll(softc) < 0) {
364        /* Timeout */
365        SPI_DISABLE(chan,0);
366        robo_reset(softc);
367        return -3;
368    }
369
370    /* Read registers */
371    SPI_READ(chan,bptr,blen,0);
372
373    SPI_DISABLE(chan,0);
374
375#else
376
377    /* Discard first read */
378    robo_read_reg(softc,reg,bptr,1);
379
380    if (robo_status_poll(softc,M_SPISTAT_RACK,M_SPISTAT_RACK) < 0) {
381        /* Timeout */
382        robo_reset(softc);
383        return -2;
384    }
385
386    /* Read registers from dataport */
387    robo_read_reg(softc,R_ROBOSPI_SPI_DATA,bptr,blen);
388
389#endif
390
391    /* Multi-byte Robo registers have LSB first */
392    _revert_buffer(bptr,blen);
393
394    return 0;
395}
396
397/*  *********************************************************************
398    *  robo_write(ctx,buffer)
399    *
400    *  Write bytes from the device.
401    *
402    *  Input parameters:
403    *  	   ctx - device context (can obtain our softc here)
404    *  	   buffer - buffer descriptor (target buffer, length, offset)
405    *
406    *  Return value:
407    *  	   number of bytes read
408    *  	   -1 if an error occured
409    ********************************************************************* */
410
411static int robo_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
412{
413    robo_spi_t *softc = ctx->dev_softc;
414    unsigned char *bptr;
415    int blen;
416    int page;
417    int reg;
418
419    bptr = HSADDR2PTR(buffer->buf_ptr);		/* XXX not 64-bit compliant */
420    blen = buffer->buf_length;
421
422    page = (buffer->buf_offset >> 8) & 0xFF;
423    reg = buffer->buf_offset & 0xFF;
424
425#ifdef _ROBOSPI_DEBUG_
426    printf("robo_write: page=0x%02X reg=0x%02X\n",page,reg);
427#endif
428
429    if (blen > 8) return -1;
430
431    /* Multi-byte Robo registers must have LSB first */
432    _revert_buffer(bptr,blen);
433
434    if (robo_status_poll(softc,M_SPISTAT_MDIO_START,0) < 0) {
435        /* Timeout */
436        robo_reset(softc);
437        return -1;
438    }
439
440    robo_select_page(softc,page);
441
442    robo_write_reg(softc,reg,bptr,blen);
443
444    return 0;
445}
446
447/*  *********************************************************************
448    *  robo_ioctl(ctx,buffer)
449    *
450    *  Perform miscellaneous I/O control operations on the device.
451    *
452    *  Input parameters:
453    *  	   ctx - device context (can obtain our softc here)
454    *  	   buffer - buffer descriptor (target buffer, length, offset)
455    *
456    *  Return value:
457    *  	   number of bytes read
458    *  	   -1 if an error occured
459    ********************************************************************* */
460
461static int robo_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
462{
463    return -1;
464}
465
466/*  *********************************************************************
467    *  robo_close(ctx,buffer)
468    *
469    *  Close the device.
470    *
471    *  Input parameters:
472    *  	   ctx - device context (can obtain our softc here)
473    *
474    *  Return value:
475    *  	   0 if ok
476    *  	   -1 if an error occured
477    ********************************************************************* */
478
479static int robo_close(cfe_devctx_t *ctx)
480{
481    return 0;
482}
483
484
485