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