1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  Microchip 24lc128 EEPROM driver 	File: dev_sb1250_24lc128eeprom.c
5    *
6    *  This module contains a CFE driver for a Microchip 24LC128 EEPROM
7    *
8    *  Author:  Mitch Lichtenberg (mpl@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_24lc128eeprom_probe(cfe_driver_t *drv,
68				     unsigned long probe_a, unsigned long probe_b,
69				     void *probe_ptr);
70
71
72static int sb1250_24lc128eeprom_open(cfe_devctx_t *ctx);
73static int sb1250_24lc128eeprom_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
74static int sb1250_24lc128eeprom_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat);
75static int sb1250_24lc128eeprom_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
76static int sb1250_24lc128eeprom_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer);
77static int sb1250_24lc128eeprom_close(cfe_devctx_t *ctx);
78
79/*  *********************************************************************
80    *  Dispatch tables
81    ********************************************************************* */
82
83#define M24LC128_EEPROM_SIZE	16384
84
85const static cfe_devdisp_t sb1250_24lc128eeprom_dispatch = {
86    sb1250_24lc128eeprom_open,
87    sb1250_24lc128eeprom_read,
88    sb1250_24lc128eeprom_inpstat,
89    sb1250_24lc128eeprom_write,
90    sb1250_24lc128eeprom_ioctl,
91    sb1250_24lc128eeprom_close,
92    NULL,
93    NULL
94};
95
96const cfe_driver_t sb1250_24lc128eeprom = {
97    "Microchip 24LC128 EEPROM",
98    "eeprom",
99    CFE_DEV_NVRAM,
100    &sb1250_24lc128eeprom_dispatch,
101    sb1250_24lc128eeprom_probe
102};
103
104typedef struct sb1250_24lc128eeprom_s {
105    int smbus_channel;
106    int smbus_address;
107    int env_offset;
108    int env_size;
109    unsigned char data[M24LC128_EEPROM_SIZE];
110} sb1250_24lc128eeprom_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.  The 'slaveaddr' parameter determines
176    *  whether we're reading from the RTC section or the EEPROM section.
177    *
178    *  Input parameters:
179    *  	   chan - SMBus channel
180    *  	   slaveaddr -  SMBus slave address
181    *  	   devaddr - byte with in the device to read
182    *
183    *  Return value:
184    *  	   0 if ok
185    *  	   else -1
186    ********************************************************************* */
187
188static int smbus_readbyte(int chan,int slaveaddr,int devaddr)
189{
190    uintptr_t reg;
191    int err;
192
193    /*
194     * Make sure the bus is idle (probably should
195     * ignore error here)
196     */
197
198    if (smbus_waitready(chan) < 0) return -1;
199
200    /*
201     * Write the device address to the controller. There are two
202     * parts, the high part goes in the "CMD" field, and the
203     * low part is the data field.
204     */
205
206    reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_CMD));
207    SBWRITECSR(reg,((devaddr >> 8) & 0x3F));
208
209    /*
210     * Write the data to the controller
211     */
212
213    reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_DATA));
214    SBWRITECSR(reg,((devaddr & 0xFF) & 0xFF));
215
216    /*
217     * Start the command
218     */
219
220    reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_START));
221    SBWRITECSR(reg,V_SMB_TT(K_SMB_TT_WR2BYTE) | slaveaddr);
222
223    /*
224     * Wait till done
225     */
226
227    err = smbus_waitready(chan);
228    if (err < 0) return err;
229
230    /*
231     * Read the data byte
232     */
233
234    SBWRITECSR(reg,V_SMB_TT(K_SMB_TT_RD1BYTE) | slaveaddr);
235
236    err = smbus_waitready(chan);
237    if (err < 0) return err;
238
239    reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_DATA));
240    err = SBREADCSR(reg);
241
242    return (err & 0xFF);
243}
244
245/*  *********************************************************************
246    *  smbus_writebyte(chan,slaveaddr,devaddr,b)
247    *
248    *  write a byte from the chip.  The 'slaveaddr' parameter determines
249    *  whethe we're writing to the RTC section or the EEPROM section.
250    *
251    *  Input parameters:
252    *  	   chan - SMBus channel
253    *  	   slaveaddr -  SMBus slave address
254    *  	   devaddr - byte with in the device to read
255    *      b - byte to write
256    *
257    *  Return value:
258    *  	   0 if ok
259    *  	   else -1
260    ********************************************************************* */
261
262
263static int smbus_writebyte(int chan,int slaveaddr,int devaddr,int b)
264{
265    uintptr_t reg;
266    int err;
267    int64_t timer;
268
269    /*
270     * Make sure the bus is idle (probably should
271     * ignore error here)
272     */
273
274    if (smbus_waitready(chan) < 0) return -1;
275
276    /*
277     * Write the device address to the controller. There are two
278     * parts, the high part goes in the "CMD" field, and the
279     * low part is the data field.
280     */
281
282    reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_CMD));
283    SBWRITECSR(reg,((devaddr >> 8) & 0x3F));
284
285    /*
286     * Write the data to the controller
287     */
288
289    reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_DATA));
290    SBWRITECSR(reg,((devaddr & 0xFF) | ((b & 0xFF) << 8)));
291
292    /*
293     * Start the command.  Keep pounding on the device until it
294     * submits or the timer expires, whichever comes first.  The
295     * datasheet says writes can take up to 10ms, so we'll give it 500.
296     */
297
298    reg = PHYS_TO_K1(A_SMB_REGISTER(chan,R_SMB_START));
299    SBWRITECSR(reg,V_SMB_TT(K_SMB_TT_WR3BYTE) | slaveaddr);
300
301    /*
302     * Wait till the SMBus interface is done
303     */
304
305    err = smbus_waitready(chan);
306    if (err < 0) return err;
307
308    /*
309     * Pound on the device with a quick command (R/W=0)
310     * to poll for the write complete.  See sect 7.0 of the
311     * 24LC128 manual.
312     */
313
314    TIMER_SET(timer,50);
315    err = -1;
316
317    while (!TIMER_EXPIRED(timer)) {
318	POLL();
319
320	SBWRITECSR(reg,V_SMB_TT(K_SMB_TT_QUICKCMD) | slaveaddr);
321
322	err = smbus_waitready(chan);
323	if (err == 0) break;
324	}
325
326    return err;
327}
328
329
330/*  *********************************************************************
331    *  sb1250_24lc128eeprom_probe(drv,a,b,ptr)
332    *
333    *  Probe routine for this driver.  This routine creates the
334    *  local device context and attaches it to the driver list
335    *  within CFE.
336    *
337    *  Input parameters:
338    *  	   drv - driver handle
339    *  	   a,b - probe hints (longs)
340    *  	   ptr - probe hint (pointer)
341    *
342    *  Return value:
343    *  	   nothing
344    ********************************************************************* */
345
346static void sb1250_24lc128eeprom_probe(cfe_driver_t *drv,
347				     unsigned long probe_a, unsigned long probe_b,
348				     void *probe_ptr)
349{
350    sb1250_24lc128eeprom_t *softc;
351    char descr[80];
352
353    softc = (sb1250_24lc128eeprom_t *) KMALLOC(sizeof(sb1250_24lc128eeprom_t),0);
354
355    /*
356     * Probe_a is the SMBus channel number
357     * Probe_b is the SMBus device offset
358     * Probe_ptr is unused.
359     */
360
361    softc->smbus_channel = (int)probe_a;
362    softc->smbus_address = (int)probe_b;
363    softc->env_offset  = 0;
364    softc->env_size = M24LC128_EEPROM_SIZE;
365
366    xsprintf(descr,"%s on SMBus channel %d dev 0x%02X",
367	     drv->drv_description,(int)probe_a,(int)probe_b);
368    cfe_attach(drv,softc,NULL,descr);
369}
370
371
372
373/*  *********************************************************************
374    *  sb1250_24lc128eeprom_open(ctx)
375    *
376    *  Open this device.  For the X1240, we do a quick test
377    *  read to be sure the device is out there.
378    *
379    *  Input parameters:
380    *  	   ctx - device context (can obtain our softc here)
381    *
382    *  Return value:
383    *  	   0 if ok
384    *  	   else error code
385    ********************************************************************* */
386
387static int sb1250_24lc128eeprom_open(cfe_devctx_t *ctx)
388{
389    sb1250_24lc128eeprom_t *softc = ctx->dev_softc;
390    int b;
391
392    smbus_init(softc->smbus_channel);
393
394    b = smbus_readbyte(softc->smbus_channel,
395		       softc->smbus_address,
396		       0);
397
398    return (b < 0) ? -1 : 0;
399}
400
401/*  *********************************************************************
402    *  sb1250_24lc128eeprom_read(ctx,buffer)
403    *
404    *  Read bytes from the device.
405    *
406    *  Input parameters:
407    *  	   ctx - device context (can obtain our softc here)
408    *  	   buffer - buffer descriptor (target buffer, length, offset)
409    *
410    *  Return value:
411    *  	   number of bytes read
412    *  	   -1 if an error occured
413    ********************************************************************* */
414
415static int sb1250_24lc128eeprom_read(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
416{
417    sb1250_24lc128eeprom_t *softc = ctx->dev_softc;
418    unsigned char *bptr;
419    int blen;
420    int idx;
421    int b = 0;
422
423    bptr = buffer->buf_ptr;
424    blen = buffer->buf_length;
425
426    if ((buffer->buf_offset + blen) > M24LC128_EEPROM_SIZE) return -1;
427
428    idx = (int) buffer->buf_offset;
429
430    while (blen > 0) {
431	b = smbus_readbyte(softc->smbus_channel,
432			  softc->smbus_address,
433			  idx);
434	if (b < 0) break;
435	*bptr++ = (unsigned char) b;
436	blen--;
437	idx++;
438	}
439
440    buffer->buf_retlen = bptr - buffer->buf_ptr;
441    return (b < 0) ? -1 : 0;
442}
443
444/*  *********************************************************************
445    *  sb1250_24lc128eeprom_inpstat(ctx,inpstat)
446    *
447    *  Test input (read) status for the device
448    *
449    *  Input parameters:
450    *  	   ctx - device context (can obtain our softc here)
451    *  	   inpstat - input status descriptor to receive value
452    *
453    *  Return value:
454    *  	   0 if ok
455    *  	   -1 if an error occured
456    ********************************************************************* */
457
458static int sb1250_24lc128eeprom_inpstat(cfe_devctx_t *ctx,iocb_inpstat_t *inpstat)
459{
460    inpstat->inp_status = 1;
461
462    return 0;
463}
464
465/*  *********************************************************************
466    *  sb1250_24lc128eeprom_write(ctx,buffer)
467    *
468    *  Write bytes from the device.
469    *
470    *  Input parameters:
471    *  	   ctx - device context (can obtain our softc here)
472    *  	   buffer - buffer descriptor (target buffer, length, offset)
473    *
474    *  Return value:
475    *  	   number of bytes read
476    *  	   -1 if an error occured
477    ********************************************************************* */
478
479static int sb1250_24lc128eeprom_write(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
480{
481    sb1250_24lc128eeprom_t *softc = ctx->dev_softc;
482    unsigned char *bptr;
483    int blen;
484    int idx;
485    int b = 0;
486
487    bptr = buffer->buf_ptr;
488    blen = buffer->buf_length;
489
490    if ((buffer->buf_offset + blen) > M24LC128_EEPROM_SIZE) return -1;
491
492    idx = (int) buffer->buf_offset;
493
494    while (blen > 0) {
495	b = *bptr++;
496	b = smbus_writebyte(softc->smbus_channel,
497			   softc->smbus_address,
498			   idx,
499			   b);
500	if (b < 0) break;
501	blen--;
502	idx++;
503	}
504
505    buffer->buf_retlen = bptr - buffer->buf_ptr;
506    return (b < 0) ? -1 : 0;
507}
508
509/*  *********************************************************************
510    *  sb1250_24lc128eeprom_ioctl(ctx,buffer)
511    *
512    *  Perform miscellaneous I/O control operations on the device.
513    *
514    *  Input parameters:
515    *  	   ctx - device context (can obtain our softc here)
516    *  	   buffer - buffer descriptor (target buffer, length, offset)
517    *
518    *  Return value:
519    *  	   number of bytes read
520    *  	   -1 if an error occured
521    ********************************************************************* */
522
523static int sb1250_24lc128eeprom_ioctl(cfe_devctx_t *ctx,iocb_buffer_t *buffer)
524{
525    sb1250_24lc128eeprom_t *softc = ctx->dev_softc;
526    nvram_info_t *info;
527
528    switch ((int)buffer->buf_ioctlcmd) {
529	case IOCTL_NVRAM_GETINFO:
530	    info = (nvram_info_t *) buffer->buf_ptr;
531	    if (buffer->buf_length != sizeof(nvram_info_t)) return -1;
532	    info->nvram_offset = softc->env_offset;
533	    info->nvram_size =   softc->env_size;
534	    info->nvram_eraseflg = FALSE;
535	    buffer->buf_retlen = sizeof(nvram_info_t);
536	    return 0;
537	default:
538	    return -1;
539	}
540}
541
542/*  *********************************************************************
543    *  sb1250_24lc128eeprom_close(ctx,buffer)
544    *
545    *  Close the device.
546    *
547    *  Input parameters:
548    *  	   ctx - device context (can obtain our softc here)
549    *
550    *  Return value:
551    *  	   0 if ok
552    *  	   -1 if an error occured
553    ********************************************************************* */
554
555static int sb1250_24lc128eeprom_close(cfe_devctx_t *ctx)
556{
557    return 0;
558}
559
560
561