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