OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [scsi/] [cpqfcTSi2c.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/* Copyright(c) 2000, Compaq Computer Corporation
2
 * Fibre Channel Host Bus Adapter
3
 * 64-bit, 66MHz PCI
4
 * Originally developed and tested on:
5
 * (front): [chip] Tachyon TS HPFC-5166A/1.2  L2C1090 ...
6
 *          SP# P225CXCBFIEL6T, Rev XC
7
 *          SP# 161290-001, Rev XD
8
 * (back): Board No. 010008-001 A/W Rev X5, FAB REV X5
9
 *
10
 * This program is free software; you can redistribute it and/or modify it
11
 * under the terms of the GNU General Public License as published by the
12
 * Free Software Foundation; either version 2, or (at your option) any
13
 * later version.
14
 *
15
 * This program is distributed in the hope that it will be useful, but
16
 * WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18
 * General Public License for more details.
19
 * Written by Don Zimmerman
20
*/
21
// These functions control the NVRAM I2C hardware on 
22
// non-intelligent Fibre Host Adapters.
23
// The primary purpose is to read the HBA's NVRAM to get adapter's 
24
// manufactured WWN to copy into Tachyon chip registers
25
// Orignal source author unknown
26
 
27
#include <linux/types.h>
28
#include <linux/string.h>
29
#include <linux/pci.h>
30
#include <linux/delay.h>
31
#include <linux/sched.h>
32
#include <asm/io.h>             // struct pt_regs for IRQ handler & Port I/O
33
 
34
#include "cpqfcTSchip.h"
35
 
36
static void tl_i2c_tx_byte(void *GPIOout, u8 data);
37
 
38
/*
39
 * Tachlite GPIO2, GPIO3 (I2C) DEFINES
40
 * The NVRAM chip NM24C03 defines SCL (serial clock) and SDA (serial data)
41
 * GPIO2 drives SDA, and GPIO3 drives SCL
42
 *
43
 * Since Tachlite inverts the state of the GPIO 0-3 outputs, SET writes 0
44
 * and clear writes 1. The input lines (read in TL status) is NOT inverted
45
 * This really helps confuse the code and debugging.
46
 */
47
 
48
#define SET_DATA_HI             0x0
49
#define SET_DATA_LO             0x8
50
#define SET_CLOCK_HI            0x0
51
#define SET_CLOCK_LO            0x4
52
 
53
#define SENSE_DATA_HI           0x8
54
#define SENSE_DATA_LO           0x0
55
#define SENSE_CLOCK_HI          0x4
56
#define SENSE_CLOCK_LO          0x0
57
 
58
#define SLAVE_READ_ADDRESS      0xA1
59
#define SLAVE_WRITE_ADDRESS     0xA0
60
 
61
 
62
static void tl_i2c_clock_pulse(u8, void *GPIOout);
63
static u8 tl_read_i2c_data(void *);
64
 
65
 
66
//-----------------------------------------------------------------------------
67
//
68
//      Name:   tl_i2c_rx_ack
69
//
70
//      This routine receives an acknowledge over the I2C bus.
71
//
72
//-----------------------------------------------------------------------------
73
static unsigned short tl_i2c_rx_ack(void *GPIOin, void *GPIOout)
74
{
75
        unsigned long value;
76
 
77
        // do clock pulse, let data line float high
78
        tl_i2c_clock_pulse(SET_DATA_HI, GPIOout);
79
 
80
        // slave must drive data low for acknowledge
81
        value = tl_read_i2c_data(GPIOin);
82
        if (value & SENSE_DATA_HI)
83
                return 0;
84
 
85
        return 1;
86
}
87
 
88
//-----------------------------------------------------------------------------
89
//
90
//      Name:   tl_read_i2c_reg
91
//
92
//      This routine reads the I2C control register using the global
93
//      IO address stored in gpioreg.
94
//
95
//-----------------------------------------------------------------------------
96
static u8 tl_read_i2c_data(void *gpioreg)
97
{
98
        return ((u8) (readl(gpioreg) & 0x08L)); // GPIO3
99
}
100
 
101
//-----------------------------------------------------------------------------
102
//
103
//      Name:   tl_write_i2c_reg
104
//
105
//      This routine writes the I2C control register using the global
106
//      IO address stored in gpioreg.
107
//      In Tachlite, we don't want to modify other bits in TL Control reg.
108
//
109
//-----------------------------------------------------------------------------
110
static void tl_write_i2c_reg(void *gpioregOUT, u8 value)
111
{
112
        u32 temp;
113
 
114
        // First read the register and clear out the old bits
115
        temp = readl(gpioregOUT) & 0xfffffff3L;
116
 
117
        // Now or in the new data and send it back out
118
        writel(temp | value, gpioregOUT);
119
 
120
        /* PCI posting ???? */
121
}
122
 
123
//-----------------------------------------------------------------------------
124
//
125
//      Name:   tl_i2c_tx_start
126
//
127
//      This routine transmits a start condition over the I2C bus.
128
//      1. Set SCL (clock, GPIO2) HIGH, set SDA (data, GPIO3) HIGH,
129
//      wait 5us to stabilize.
130
//      2. With SCL still HIGH, drive SDA low.  The low transition marks
131
//         the start condition to NM24Cxx (the chip)
132
//      NOTE! In TL control reg., output 1 means chip sees LOW
133
//
134
//-----------------------------------------------------------------------------
135
static unsigned short tl_i2c_tx_start(void *GPIOin, void *GPIOout)
136
{
137
        unsigned short i;
138
        u32 value;
139
 
140
        if (!(tl_read_i2c_data(GPIOin) & SENSE_DATA_HI)) {
141
                // start with clock high, let data float high
142
                tl_write_i2c_reg(GPIOout, SET_DATA_HI | SET_CLOCK_HI);
143
 
144
                // keep sending clock pulses if slave is driving data line
145
                for (i = 0; i < 10; i++) {
146
                        tl_i2c_clock_pulse(SET_DATA_HI, GPIOout);
147
 
148
                        if (tl_read_i2c_data(GPIOin) & SENSE_DATA_HI)
149
                                break;
150
                }
151
 
152
                // if he's still driving data low after 10 clocks, abort
153
                value = tl_read_i2c_data(GPIOin);       // read status
154
                if (!(value & 0x08))
155
                        return 0;
156
        }
157
 
158
        // To START, bring data low while clock high
159
        tl_write_i2c_reg(GPIOout, SET_CLOCK_HI | SET_DATA_LO);
160
 
161
        udelay(5);
162
 
163
        return 1;               // TX start successful
164
}
165
 
166
//-----------------------------------------------------------------------------
167
//
168
//      Name:   tl_i2c_tx_stop
169
//
170
//      This routine transmits a stop condition over the I2C bus.
171
//
172
//-----------------------------------------------------------------------------
173
 
174
static unsigned short tl_i2c_tx_stop(void *GPIOin, void *GPIOout)
175
{
176
        int i;
177
 
178
        for (i = 0; i < 10; i++) {
179
                // Send clock pulse, drive data line low
180
                tl_i2c_clock_pulse(SET_DATA_LO, GPIOout);
181
 
182
                // To STOP, bring data high while clock high
183
                tl_write_i2c_reg(GPIOout, SET_DATA_HI | SET_CLOCK_HI);
184
 
185
                // Give the data line time to float high
186
                udelay(5);
187
 
188
                // If slave is driving data line low, there's a problem; retry
189
                if (tl_read_i2c_data(GPIOin) & SENSE_DATA_HI)
190
                        return 1;       // TX STOP successful!
191
        }
192
 
193
        return 0;                // error
194
}
195
 
196
//-----------------------------------------------------------------------------
197
//
198
//      Name:   tl_i2c_tx_byte
199
//
200
//      This routine transmits a byte across the I2C bus.
201
//
202
//-----------------------------------------------------------------------------
203
static void tl_i2c_tx_byte(void *GPIOout, u8 data)
204
{
205
        u8 bit;
206
 
207
        for (bit = 0x80; bit; bit >>= 1) {
208
                if (data & bit)
209
                        tl_i2c_clock_pulse((u8) SET_DATA_HI, GPIOout);
210
                else
211
                        tl_i2c_clock_pulse((u8) SET_DATA_LO, GPIOout);
212
        }
213
}
214
 
215
//-----------------------------------------------------------------------------
216
//
217
//      Name:   tl_i2c_rx_byte
218
//
219
//      This routine receives a byte across the I2C bus.
220
//
221
//-----------------------------------------------------------------------------
222
static u8 tl_i2c_rx_byte(void *GPIOin, void *GPIOout)
223
{
224
        u8 bit;
225
        u8 data = 0;
226
 
227
 
228
        for (bit = 0x80; bit; bit >>= 1) {
229
                // do clock pulse, let data line float high
230
                tl_i2c_clock_pulse(SET_DATA_HI, GPIOout);
231
 
232
                // read data line
233
                if (tl_read_i2c_data(GPIOin) & 0x08)
234
                        data |= bit;
235
        }
236
 
237
        return (data);
238
}
239
 
240
//*****************************************************************************
241
//*****************************************************************************
242
// Function:   read_i2c_nvram
243
// Arguments:  u8 count     number of bytes to read
244
//             u8 *buf      area to store the bytes read
245
// Returns:    0 - failed
246
//             1 - success
247
//*****************************************************************************
248
//*****************************************************************************
249
unsigned long cpqfcTS_ReadNVRAM(void *GPIOin, void *GPIOout, u16 count, u8 * buf)
250
{
251
        unsigned short i;
252
 
253
        if (!(tl_i2c_tx_start(GPIOin, GPIOout)))
254
                return 0;
255
 
256
        // Select the NVRAM for "dummy" write, to set the address
257
        tl_i2c_tx_byte(GPIOout, SLAVE_WRITE_ADDRESS);
258
        if (!tl_i2c_rx_ack(GPIOin, GPIOout))
259
                return 0;
260
 
261
        // Now send the address where we want to start reading  
262
        tl_i2c_tx_byte(GPIOout, 0);
263
        if (!tl_i2c_rx_ack(GPIOin, GPIOout))
264
                return 0;
265
 
266
        // Send a repeated start condition and select the
267
        //  slave for reading now.
268
        if (tl_i2c_tx_start(GPIOin, GPIOout))
269
                tl_i2c_tx_byte(GPIOout, SLAVE_READ_ADDRESS);
270
 
271
        if (!tl_i2c_rx_ack(GPIOin, GPIOout))
272
                return 0;
273
 
274
        // this loop will now read out the data and store it
275
        //  in the buffer pointed to by buf
276
        for (i = 0; i < count; i++) {
277
                *buf++ = tl_i2c_rx_byte(GPIOin, GPIOout);
278
 
279
                // Send ACK by holding data line low for 1 clock
280
                if (i < (count - 1))
281
                        tl_i2c_clock_pulse(0x08, GPIOout);
282
                else {
283
                        // Don't send ack for final byte
284
                        tl_i2c_clock_pulse(SET_DATA_HI, GPIOout);
285
                }
286
        }
287
 
288
        tl_i2c_tx_stop(GPIOin, GPIOout);
289
 
290
        return 1;
291
}
292
 
293
//****************************************************************
294
//
295
//
296
//
297
// routines to set and clear the data and clock bits
298
//
299
//
300
//
301
//****************************************************************
302
 
303
static void tl_set_clock(void *gpioreg)
304
{
305
        u32 ret_val;
306
 
307
        ret_val = readl(gpioreg);
308
        ret_val &= 0xffffffFBL; // clear GPIO2 (SCL)
309
        writel(ret_val, gpioreg);
310
}
311
 
312
static void tl_clr_clock(void *gpioreg)
313
{
314
        u32 ret_val;
315
 
316
        ret_val = readl(gpioreg);
317
        ret_val |= SET_CLOCK_LO;
318
        writel(ret_val, gpioreg);
319
}
320
 
321
//*****************************************************************
322
//
323
//
324
// This routine will advance the clock by one period
325
//
326
//
327
//*****************************************************************
328
static void tl_i2c_clock_pulse(u8 value, void *GPIOout)
329
{
330
        u32 ret_val;
331
 
332
        // clear the clock bit
333
        tl_clr_clock(GPIOout);
334
 
335
        udelay(5);
336
 
337
 
338
        // read the port to preserve non-I2C bits
339
        ret_val = readl(GPIOout);
340
 
341
        // clear the data & clock bits
342
        ret_val &= 0xFFFFFFf3;
343
 
344
        // write the value passed in...
345
        // data can only change while clock is LOW!
346
        ret_val |= value;       // the data
347
        ret_val |= SET_CLOCK_LO;        // the clock
348
        writel(ret_val, GPIOout);
349
 
350
        udelay(5);
351
 
352
 
353
        //set clock bit
354
        tl_set_clock(GPIOout);
355
}
356
 
357
 
358
 
359
 
360
//*****************************************************************
361
//
362
//
363
// This routine returns the 64-bit WWN
364
//
365
//
366
//*****************************************************************
367
int cpqfcTS_GetNVRAM_data(u8 * wwnbuf, u8 * buf)
368
{
369
        u32 len;
370
        u32 sub_len;
371
        u32 ptr_inc;
372
        u32 i;
373
        u32 j;
374
        u8 *data_ptr;
375
        u8 z;
376
        u8 name;
377
        u8 sub_name;
378
        u8 done;
379
        int ret = 0;     // def. 0 offset is failure to find WWN field
380
 
381
 
382
 
383
        data_ptr = (u8 *) buf;
384
 
385
        done = 0;
386
        i = 0;
387
 
388
        while (i < 128 && !done) {
389
                z = data_ptr[i];
390
                if (!(z & 0x80)) {
391
                        len = 1 + (z & 0x07);
392
 
393
                        name = (z & 0x78) >> 3;
394
                        if (name == 0x0F)
395
                                done = 1;
396
                } else {
397
                        name = z & 0x7F;
398
                        len = 3 + data_ptr[i + 1] + (data_ptr[i + 2] << 8);
399
 
400
                        switch (name) {
401
                        case 0x0D:
402
                                //
403
                                j = i + 3;
404
                                //
405
                                if (data_ptr[j] == 0x3b) {
406
                                        len = 6;
407
                                        break;
408
                                }
409
 
410
                                while (j < (i + len)) {
411
                                        sub_name = (data_ptr[j] & 0x3f);
412
                                        sub_len = data_ptr[j + 1] + (data_ptr[j + 2] << 8);
413
                                        ptr_inc = sub_len + 3;
414
                                        switch (sub_name) {
415
                                        case 0x3C:
416
                                                memcpy(wwnbuf, &data_ptr[j + 3], 8);
417
                                                ret = j + 3;
418
                                                break;
419
                                        default:
420
                                                break;
421
                                        }
422
                                        j += ptr_inc;
423
                                }
424
                                break;
425
                        default:
426
                                break;
427
                        }
428
                }
429
                //
430
                i += len;
431
        }                       // end while 
432
        return ret;
433
}
434
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.