1/* ********************************************************************* 2 * Broadcom Common Firmware Environment (CFE) 3 * 4 * ATMEL AT24C02 EEPROM driver File: dev_sb1250_at24c02eeprom.c 5 * 6 * This module contains a CFE driver for a ATMEL AT24C02 EEPROM 7 * 8 * Author: Binh Vo (binh@broadcom.com) 9 * 10 ********************************************************************* 11 * 12 * Copyright 2000,2001,2002,2003 13 * Broadcom Corporation. All rights reserved. 14 * 15 * This software is furnished under license and may be used and 16 * copied only in accordance with the following terms and 17 * conditions. Subject to these conditions, you may download, 18 * copy, install, use, modify and distribute modified or unmodified 19 * copies of this software in source and/or binary form. No title 20 * or ownership is transferred hereby. 21 * 22 * 1) Any source code used, modified or distributed must reproduce 23 * and retain this copyright notice and list of conditions 24 * as they appear in the source file. 25 * 26 * 2) No right is granted to use any trade name, trademark, or 27 * logo of Broadcom Corporation. The "Broadcom Corporation" 28 * name may not be used to endorse or promote products derived 29 * from this software without the prior written permission of 30 * Broadcom Corporation. 31 * 32 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR 33 * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED 34 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 35 * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT 36 * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN 37 * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, 38 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 39 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 40 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 41 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 42 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 43 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF 44 * THE POSSIBILITY OF SUCH DAMAGE. 45 ********************************************************************* */ 46 47 48#include "sbmips.h" 49#include "lib_types.h" 50#include "lib_malloc.h" 51#include "lib_printf.h" 52 53#include "cfe_iocb.h" 54#include "cfe_device.h" 55#include "cfe_ioctl.h" 56#include "cfe_timer.h" 57 58#include "sb1250_defs.h" 59#include "sb1250_regs.h" 60#include "sb1250_smbus.h" 61 62 63/* ********************************************************************* 64 * Forward Declarations 65 ********************************************************************* */ 66 67static void sb1250_at24c02eeprom_probe(cfe_driver_t *drv, 68 unsigned long probe_a, unsigned long probe_b, 69 void *probe_ptr); 70 71 72static int sb1250_at24c02eeprom_open(cfe_devctx_t *ctx); 73static int sb1250_at24c02eeprom_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer); 74static int sb1250_at24c02eeprom_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat); 75static int sb1250_at24c02eeprom_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer); 76static int sb1250_at24c02eeprom_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer); 77static int sb1250_at24c02eeprom_close(cfe_devctx_t *ctx); 78 79/* ********************************************************************* 80 * Dispatch tables 81 ********************************************************************* */ 82 83#define AT24C02_EEPROM_SIZE 256 84 85const static cfe_devdisp_t sb1250_at24c02eeprom_dispatch = { 86 sb1250_at24c02eeprom_open, 87 sb1250_at24c02eeprom_read, 88 sb1250_at24c02eeprom_inpstat, 89 sb1250_at24c02eeprom_write, 90 sb1250_at24c02eeprom_ioctl, 91 sb1250_at24c02eeprom_close, 92 NULL, 93 NULL 94}; 95 96const cfe_driver_t sb1250_at24c02eeprom = { 97 "ATMEL AT24C02 SPD EEPROM", 98 "eeprom", 99 CFE_DEV_NVRAM, 100 &sb1250_at24c02eeprom_dispatch, 101 sb1250_at24c02eeprom_probe 102}; 103 104typedef struct sb1250_at24c02eeprom_s { 105 int smbus_channel; 106 int smbus_address; 107 int env_offset; 108 int env_size; 109 unsigned char data[AT24C02_EEPROM_SIZE]; 110} sb1250_at24c02eeprom_t; 111 112 113/* ********************************************************************* 114 * smbus_init(chan) 115 * 116 * Initialize the specified SMBus channel 117 * 118 * Input parameters: 119 * chan - channel # (0 or 1) 120 * 121 * Return value: 122 * nothing 123 ********************************************************************* */ 124 125static void smbus_init(int chan) 126{ 127 uintptr_t reg; 128 129 reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_FREQ)); 130 131 SBWRITECSR(reg,K_SMB_FREQ_100KHZ); 132 133 reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_CONTROL)); 134 135 SBWRITECSR(reg,0); /* not in direct mode, no interrupts, will poll */ 136 137} 138 139/* ********************************************************************* 140 * smbus_waitready(chan) 141 * 142 * Wait until the SMBus channel is ready. We simply poll 143 * the busy bit until it clears. 144 * 145 * Input parameters: 146 * chan - channel (0 or 1) 147 * 148 * Return value: 149 * nothing 150 ********************************************************************* */ 151static int smbus_waitready(int chan) 152{ 153 uintptr_t reg; 154 uint64_t status; 155 156 reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_STATUS)); 157 158 for (;;) { 159 status = SBREADCSR(reg); 160 if (status & M_SMB_BUSY) continue; 161 break; 162 } 163 164 if (status & M_SMB_ERROR) { 165 166 SBWRITECSR(reg,(status & M_SMB_ERROR)); 167 return -1; 168 } 169 return 0; 170} 171 172/* ********************************************************************* 173 * smbus_readbyte(chan,slaveaddr,devaddr) 174 * 175 * Read a byte from the chip. 176 * 177 * Input parameters: 178 * chan - SMBus channel 179 * slaveaddr - SMBus slave address 180 * devaddr - byte within the at24c02 device to read 181 * 182 * Return value: 183 * 0 if ok 184 * else -1 185 ********************************************************************* */ 186 187static int smbus_readbyte(int chan,int slaveaddr,int devaddr) 188{ 189 uintptr_t reg; 190 int err; 191 192 /* 193 * Make sure the bus is idle (probably should 194 * ignore error here) 195 */ 196 197 if (smbus_waitready(chan) < 0) return -1; 198 199 /* 200 * Write the device address to smb_cmd field of command register 201 */ 202 203 reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_CMD)); 204 SBWRITECSR(reg,devaddr); 205 206 /* 207 * Start the command 208 */ 209 210 reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_START)); 211 SBWRITECSR(reg,V_SMB_TT(K_SMB_TT_WR1BYTE) | slaveaddr); 212 213 /* 214 * Wait till done 215 */ 216 217 err = smbus_waitready(chan); 218 if (err < 0) return err; 219 220 /* 221 * Read the data byte 222 */ 223 224 SBWRITECSR(reg,V_SMB_TT(K_SMB_TT_RD1BYTE) | slaveaddr); 225 226 err = smbus_waitready(chan); 227 if (err < 0) return err; 228 229 reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_DATA)); 230 err = SBREADCSR(reg); 231 232 return (err & 0xFF); 233} 234 235/* ********************************************************************* 236 * smbus_writebyte(chan,slaveaddr,devaddr,b) 237 * 238 * write a byte from the chip. 239 * 240 * Input parameters: 241 * chan - SMBus channel 242 * slaveaddr - SMBus slave address 243 * devaddr - byte within the at24c02 device to read 244 * b - byte to write 245 * 246 * Return value: 247 * 0 if ok 248 * else -1 249 ********************************************************************* */ 250 251static int smbus_writebyte(int chan,int slaveaddr,int devaddr,int b) 252{ 253 uintptr_t reg; 254 int err; 255 int64_t timer; 256 257 /* 258 * Make sure the bus is idle (probably should 259 * ignore error here) 260 */ 261 262 if (smbus_waitready(chan) < 0) return -1; 263 264 /* 265 * Write the device address to the controller. There are two 266 * parts, the high part goes in the "CMD" field, and the 267 * low part is the data field. 268 */ 269 270 reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_CMD)); 271 SBWRITECSR(reg,devaddr); 272 273 /* 274 * Write the data to the controller 275 */ 276 277 reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_DATA)); 278 SBWRITECSR(reg,b); 279 280 /* 281 * Start the command. Keep pounding on the device until it 282 * submits or the timer expires, whichever comes first. The 283 * datasheet says writes can take up to 10ms, so we'll give it 500. 284 */ 285 286 reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_START)); 287 SBWRITECSR(reg,V_SMB_TT(K_SMB_TT_WR2BYTE) | slaveaddr); 288 289 /* 290 * Wait till the SMBus interface is done 291 */ 292 293 err = smbus_waitready(chan); 294 if (err < 0) return err; 295 296 /* 297 * Pound on the device with a current address read 298 * to poll for the write complete 299 */ 300 301 TIMER_SET(timer,50); 302 err = -1; 303 304 while (!TIMER_EXPIRED(timer)) { 305 POLL(); 306 307 SBWRITECSR(reg,V_SMB_TT(K_SMB_TT_RD1BYTE) | slaveaddr); 308 309 err = smbus_waitready(chan); 310 if (err == 0) break; 311 } 312 313 return err; 314} 315 316/* ********************************************************************* 317 * sb1250_at24c02eeprom_probe(drv,a,b,ptr) 318 * 319 * Probe routine for this driver. This routine creates the 320 * local device context and attaches it to the driver list 321 * within CFE. 322 * 323 * Input parameters: 324 * drv - driver handle 325 * a,b - probe hints (longs) 326 * ptr - probe hint (pointer) 327 * 328 * Return value: 329 * nothing 330 ********************************************************************* */ 331 332static void sb1250_at24c02eeprom_probe(cfe_driver_t *drv, 333 unsigned long probe_a, unsigned long probe_b, 334 void *probe_ptr) 335{ 336 sb1250_at24c02eeprom_t *softc; 337 char descr[80]; 338 339 softc = (sb1250_at24c02eeprom_t *) KMALLOC(sizeof(sb1250_at24c02eeprom_t),0); 340 341 /* 342 * Probe_a is the SMBus channel number 343 * Probe_b is the SMBus device offset 344 * Probe_ptr is unused. 345 */ 346 347 softc->smbus_channel = (int)probe_a; 348 softc->smbus_address = (int)probe_b; 349 softc->env_offset = 0; 350 softc->env_size = AT24C02_EEPROM_SIZE; 351 352 xsprintf(descr,"%s on SMBus channel %d dev 0x%02X", 353 drv->drv_description,(int)probe_a,(int)probe_b); 354 cfe_attach(drv,softc,NULL,descr); 355} 356 357 358 359/* ********************************************************************* 360 * sb1250_at24c02eeprom_open(ctx) 361 * 362 * Open this device. 363 * 364 * Input parameters: 365 * ctx - device context (can obtain our softc here) 366 * 367 * Return value: 368 * 0 if ok 369 * else error code 370 ********************************************************************* */ 371 372static int sb1250_at24c02eeprom_open(cfe_devctx_t *ctx) 373{ 374 sb1250_at24c02eeprom_t *softc = ctx->dev_softc; 375 int b; 376 377 smbus_init(softc->smbus_channel); 378 379 b = smbus_readbyte(softc->smbus_channel, 380 softc->smbus_address, 381 0); 382 383 return (b < 0) ? -1 : 0; 384} 385 386/* ********************************************************************* 387 * sb1250_at24c02eeprom_read(ctx,buffer) 388 * 389 * Read bytes from the device. 390 * 391 * Input parameters: 392 * ctx - device context (can obtain our softc here) 393 * buffer - buffer descriptor (target buffer, length, offset) 394 * 395 * Return value: 396 * number of bytes read 397 * -1 if an error occured 398 ********************************************************************* */ 399 400static int sb1250_at24c02eeprom_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer) 401{ 402 sb1250_at24c02eeprom_t *softc = ctx->dev_softc; 403 unsigned char *bptr; 404 int blen; 405 int idx; 406 int b = 0; 407 408 bptr = buffer->buf_ptr; 409 blen = buffer->buf_length; 410 411 if ((buffer->buf_offset + blen) > AT24C02_EEPROM_SIZE) return -1; 412 413 idx = (int) buffer->buf_offset; 414 415 while (blen > 0) { 416 b = smbus_readbyte(softc->smbus_channel, 417 softc->smbus_address, 418 idx); 419 if (b < 0) break; 420 *bptr++ = (unsigned char) b; 421 blen--; 422 idx++; 423 } 424 425 buffer->buf_retlen = bptr - buffer->buf_ptr; 426 return (b < 0) ? -1 : 0; 427} 428 429/* ********************************************************************* 430 * sb1250_at24c02eeprom_inpstat(ctx,inpstat) 431 * 432 * Test input (read) status for the device 433 * 434 * Input parameters: 435 * ctx - device context (can obtain our softc here) 436 * inpstat - input status descriptor to receive value 437 * 438 * Return value: 439 * 0 if ok 440 * -1 if an error occured 441 ********************************************************************* */ 442 443static int sb1250_at24c02eeprom_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat) 444{ 445 inpstat->inp_status = 1; 446 447 return 0; 448} 449 450/* ********************************************************************* 451 * sb1250_at24c02eeprom_write(ctx,buffer) 452 * 453 * Write bytes from the device. 454 * 455 * Input parameters: 456 * ctx - device context (can obtain our softc here) 457 * buffer - buffer descriptor (target buffer, length, offset) 458 * 459 * Return value: 460 * number of bytes read 461 * -1 if an error occured 462 ********************************************************************* */ 463 464static int sb1250_at24c02eeprom_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer) 465{ 466 sb1250_at24c02eeprom_t *softc = ctx->dev_softc; 467 unsigned char *bptr; 468 int blen; 469 int idx; 470 int b = 0; 471 472 bptr = buffer->buf_ptr; 473 blen = buffer->buf_length; 474 475 if ((buffer->buf_offset + blen) > AT24C02_EEPROM_SIZE) return -1; 476 477 idx = (int) buffer->buf_offset; 478 479 while (blen > 0) { 480 b = *bptr++; 481 b = smbus_writebyte(softc->smbus_channel, 482 softc->smbus_address, 483 idx, 484 b); 485 if (b < 0) break; 486 blen--; 487 idx++; 488 } 489 490 buffer->buf_retlen = bptr - buffer->buf_ptr; 491 return (b < 0) ? -1 : 0; 492} 493 494/* ********************************************************************* 495 * sb1250_at24c02eeprom_ioctl(ctx,buffer) 496 * 497 * Perform miscellaneous I/O control operations on the device. 498 * 499 * Input parameters: 500 * ctx - device context (can obtain our softc here) 501 * buffer - buffer descriptor (target buffer, length, offset) 502 * 503 * Return value: 504 * number of bytes read 505 * -1 if an error occured 506 ********************************************************************* */ 507 508static int sb1250_at24c02eeprom_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer) 509{ 510 sb1250_at24c02eeprom_t *softc = ctx->dev_softc; 511 nvram_info_t *info; 512 513 switch ((int)buffer->buf_ioctlcmd) { 514 case IOCTL_NVRAM_GETINFO: 515 info = (nvram_info_t *) buffer->buf_ptr; 516 if (buffer->buf_length != sizeof(nvram_info_t)) return -1; 517 info->nvram_offset = softc->env_offset; 518 info->nvram_size = softc->env_size; 519 info->nvram_eraseflg = FALSE; 520 buffer->buf_retlen = sizeof(nvram_info_t); 521 return 0; 522 default: 523 return -1; 524 } 525} 526 527/* ********************************************************************* 528 * sb1250_at24c02eeprom_close(ctx,buffer) 529 * 530 * Close the device. 531 * 532 * Input parameters: 533 * ctx - device context (can obtain our softc here) 534 * 535 * Return value: 536 * 0 if ok 537 * -1 if an error occured 538 ********************************************************************* */ 539 540static int sb1250_at24c02eeprom_close(cfe_devctx_t *ctx) 541{ 542 return 0; 543} 544 545 546