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