atii2c.c revision 32b578d3
1/*
2 * Copyright 2003 through 2004 by Marc Aurele La France (TSI @ UQV), tsi@xfree86.org
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of Marc Aurele La France not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  Marc Aurele La France makes no representations
11 * about the suitability of this software for any purpose.  It is provided
12 * "as-is" without express or implied warranty.
13 *
14 * MARC AURELE LA FRANCE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO
16 * EVENT SHALL MARC AURELE LA FRANCE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20 * PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "atii2c.h"
28#include "atimach64i2c.h"
29#include "atistruct.h"
30
31#include "xf86.h"
32
33/* This is derived from GATOS code, with a liberal sprinkling of bug fixes */
34
35/*
36 * Some local macros for use by the mid-level I2C functions.
37 */
38
39#define ATII2CDelay                                            \
40    (*pI2CBus->I2CUDelay)(pI2CBus, pI2CBus->HoldTime)
41
42
43#define ATII2CSCLDirOff                                        \
44    if (pATII2C->SCLDir != 0)                                  \
45        (*pATII2C->I2CSetBits)(pATII2C, pATI,                  \
46            pATII2C->I2CCur & ~pATII2C->SCLDir)
47
48#define ATII2CSCLDirOn                                         \
49    if (pATII2C->SCLDir != 0)                                  \
50        (*pATII2C->I2CSetBits)(pATII2C, pATI,                  \
51            pATII2C->I2CCur | pATII2C->SCLDir)
52
53#define ATII2CSDADirOff                                        \
54    if (pATII2C->SDADir != 0)                                  \
55        (*pATII2C->I2CSetBits)(pATII2C, pATI,                  \
56            pATII2C->I2CCur & ~pATII2C->SDADir)
57
58#define ATII2CSDADirOn                                         \
59    if (pATII2C->SDADir != 0)                                  \
60        (*pATII2C->I2CSetBits)(pATII2C, pATI,                  \
61            pATII2C->I2CCur | pATII2C->SDADir)
62
63
64#define ATII2CSCLBitGet                                        \
65    ((*pATII2C->I2CGetBits)(pATI) & pATII2C->SCLGet)
66
67#define ATII2CSCLBitOff                                        \
68    do                                                         \
69    {                                                          \
70        (*pATII2C->I2CSetBits)(pATII2C, pATI,                  \
71            pATII2C->I2CCur & ~pATII2C->SCLSet);               \
72        ATII2CDelay;                                           \
73    } while (0)
74
75#define ATII2CSCLBitOn                                         \
76    do                                                         \
77    {                                                          \
78        (*pATII2C->I2CSetBits)(pATII2C, pATI,                  \
79            pATII2C->I2CCur | pATII2C->SCLSet);                \
80        do      /* Wait until all devices have released SCL */ \
81        {                                                      \
82            ATII2CDelay;                                       \
83        } while (ATII2CSCLBitGet == 0);                        \
84    } while (0)
85
86
87#define ATII2CSDABitGet                                        \
88    ((*pATII2C->I2CGetBits)(pATI) & pATII2C->SDAGet)
89
90#define ATII2CSDABitOff                                        \
91    do                                                         \
92    {                                                          \
93        (*pATII2C->I2CSetBits)(pATII2C, pATI,                  \
94            pATII2C->I2CCur & ~pATII2C->SDASet);               \
95        ATII2CDelay;                                           \
96    } while (0)
97
98#define ATII2CSDABitOn                                         \
99    do                                                         \
100    {                                                          \
101        (*pATII2C->I2CSetBits)(pATII2C, pATI,                  \
102            pATII2C->I2CCur | pATII2C->SDASet);                \
103        ATII2CDelay;                                           \
104    } while (0)
105
106#define ATII2CSDABitSet(_flag)                                 \
107    do                                                         \
108    {                                                          \
109        if (_flag)                                             \
110            ATII2CSDABitOn;                                    \
111        else                                                   \
112            ATII2CSDABitOff;                                   \
113    } while (0)
114
115
116/*
117 * ATII2CStart --
118 *
119 * This function puts a start signal on the I2C bus.
120 */
121static Bool
122ATII2CStart
123(
124    I2CBusPtr pI2CBus,
125    int       timeout
126)
127{
128    ATII2CPtr pATII2C = pI2CBus->DriverPrivate.ptr;
129    ATIPtr    pATI    = pATII2C->pATI;
130
131    (void)timeout;
132
133    /*
134     * Set I2C line directions to out-bound.  SCL will remain out-bound until
135     * next I2C Stop.
136     */
137    ATII2CSCLDirOn;
138    ATII2CSDADirOn;
139
140    /*
141     * Send Start bit.  This is a pull-down of the data line while the clock
142     * line is pulled up.
143     */
144    ATII2CSDABitOn;
145    ATII2CSCLBitOn;
146    ATII2CSDABitOff;
147    ATII2CSCLBitOff;
148
149    return TRUE;
150}
151
152/*
153 * ATII2CAddress --
154 *
155 * This function puts an 8-bit address on the I2C bus.
156 */
157static Bool
158ATII2CAddress
159(
160    I2CDevPtr    pI2CDev,
161    I2CSlaveAddr Address
162)
163{
164    I2CBusPtr pI2CBus = pI2CDev->pI2CBus;
165
166    /* Send low byte of device address */
167    if ((*pI2CBus->I2CPutByte)(pI2CDev, (I2CByte)Address))
168    {
169        /* Send top byte of address, if appropriate */
170        if (((Address & 0x00F8U) != 0x00F0U) &&
171            ((Address & 0x00FEU) != 0x0000U))
172            return TRUE;
173
174        if ((*pI2CBus->I2CPutByte)(pI2CDev, (I2CByte)(Address >> 8)))
175            return TRUE;
176    }
177
178    /* Kill I2C transaction on failure */
179    (*pI2CBus->I2CStop)(pI2CDev);
180    return FALSE;
181}
182
183/*
184 * ATII2CStop --
185 *
186 * This function puts a stop signal on the I2C bus.
187 */
188static void
189ATII2CStop
190(
191    I2CDevPtr pI2CDev
192)
193{
194    I2CBusPtr pI2CBus = pI2CDev->pI2CBus;
195    ATII2CPtr pATII2C = pI2CBus->DriverPrivate.ptr;
196    ATIPtr    pATI    = pATII2C->pATI;
197
198    ATII2CSDADirOn;             /* Set data line direction to out-bound */
199
200    /*
201     * Send Stop bit.  This is a pull-up of the data line while the clock line
202     * is pulled up.
203     */
204    ATII2CSDABitOff;
205    ATII2CSCLBitOn;
206    ATII2CSDABitOn;
207    ATII2CSCLBitOff;
208
209    /* Reset I2C line directions to in-bound */
210    ATII2CSCLDirOff;
211    ATII2CSDADirOff;
212}
213
214/*
215 * ATII2CPutByte --
216 *
217 * This function puts an 8-bit value on the I2C bus, starting with its MSB.
218 */
219static Bool
220ATII2CPutByte
221(
222    I2CDevPtr pI2CDev,
223    I2CByte   Data
224)
225{
226    I2CBusPtr pI2CBus = pI2CDev->pI2CBus;
227    ATII2CPtr pATII2C = pI2CBus->DriverPrivate.ptr;
228    ATIPtr    pATI    = pATII2C->pATI;
229    int       i;
230    Bool      Result;
231
232    ATII2CSDADirOn;             /* Set data line direction to out-bound */
233
234    /* Send data byte */
235    for (i = 0;  i < 8;  i++)
236    {
237        ATII2CSDABitSet(Data & 0x80U);
238        ATII2CSCLBitOn;
239        ATII2CSCLBitOff;
240
241        Data <<= 1;
242    }
243
244    ATII2CSDABitOn;             /* Release data line */
245
246    ATII2CSDADirOff;            /* Set data line direction to in-bound */
247
248    ATII2CSCLBitOn;             /* Start bit-read clock pulse */
249
250    /* Get [N]ACK bit */
251    if (ATII2CSDABitGet)
252        Result = FALSE;
253    else
254        Result = TRUE;
255
256    ATII2CSCLBitOff;            /* End clock pulse */
257
258    return Result;
259}
260
261/*
262 * ATII2CGetByte --
263 *
264 * This function retrieves an 8-bit value from the I2C bus.
265 */
266static Bool
267ATII2CGetByte
268(
269    I2CDevPtr pI2CDev,
270    I2CByte   *pData,
271    Bool      Last
272)
273{
274    I2CBusPtr     pI2CBus = pI2CDev->pI2CBus;
275    ATII2CPtr     pATII2C = pI2CBus->DriverPrivate.ptr;
276    ATIPtr        pATI    = pATII2C->pATI;
277    unsigned long Value   = 1;
278
279    do
280    {
281        ATII2CSCLBitOn;         /* Start bit-read clock pulse */
282
283        /* Accumulate bit into byte value */
284        Value <<= 1;
285        if (ATII2CSDABitGet)
286            Value++;
287
288        ATII2CSCLBitOff;        /* End clock pulse */
289    } while (Value <= (unsigned long)((I2CByte)(-1)));
290
291    *pData = (I2CByte)Value;
292
293    ATII2CSDADirOn;             /* Set data line direction to out-bound */
294
295    /* Send [N]ACK bit */
296    ATII2CSDABitSet(Last);
297    ATII2CSCLBitOn;
298    ATII2CSCLBitOff;
299
300    if (!Last)
301        ATII2CSDABitOn;         /* Release data line */
302
303    ATII2CSDADirOff;            /* Set data line direction to in-bound */
304
305    return TRUE;
306}
307
308/*
309 * ATICreateI2CBusRec --
310 *
311 * This function is called to initialise an I2CBusRec.
312 */
313I2CBusPtr
314ATICreateI2CBusRec
315(
316    int    iScreen,
317    ATIPtr pATI,
318    char   *BusName
319)
320{
321    I2CBusPtr pI2CBus;
322    ATII2CPtr pATII2C = xnfcalloc(1, SizeOf(ATII2CRec));
323
324    if (!(pI2CBus = xf86CreateI2CBusRec()))
325    {
326        xf86DrvMsg(iScreen, X_WARNING, "Unable to allocate I2C Bus record.\n");
327        xfree(pATII2C);
328        return NULL;
329    }
330
331    /* Fill in generic structure fields */
332    pI2CBus->BusName           = BusName;
333    pI2CBus->scrnIndex         = iScreen;
334
335    pI2CBus->I2CAddress        = ATII2CAddress;
336    pI2CBus->I2CStart          = ATII2CStart;
337    pI2CBus->I2CStop           = ATII2CStop;
338    pI2CBus->I2CPutByte        = ATII2CPutByte;
339    pI2CBus->I2CGetByte        = ATII2CGetByte;
340
341    pI2CBus->DriverPrivate.ptr = pATII2C;
342
343    pATII2C->pATI              = pATI;
344
345    if (xf86I2CBusInit(pI2CBus))
346        return pI2CBus;
347
348    xf86DrvMsg(iScreen, X_WARNING,
349        "I2C bus %s initialisation failure.\n", BusName);
350    xf86DestroyI2CBusRec(pI2CBus, TRUE, TRUE);
351    xfree(pATII2C);
352    return NULL;
353}
354
355/*
356 * ATII2CPreInit --
357 *
358 * This is called by ATIPreInit() to create I2C bus record(s) for the adapter.
359 */
360void
361ATII2CPreInit
362(
363    ScrnInfoPtr pScreenInfo,
364    ATIPtr      pATI
365)
366{
367            if (!xf86LoadSubModule(pScreenInfo, "i2c"))
368                return;
369
370            ATIMach64I2CPreInit(pScreenInfo, pATI);
371}
372
373/*
374 * ATII2CFreeScreen --
375 *
376 * This is called by ATIFreeScreen() to remove the driver's I2C interface.
377 */
378void
379ATII2CFreeScreen
380(
381    int iScreen
382)
383{
384    I2CBusPtr pI2CBus, *ppI2CBus;
385    ATII2CPtr pATII2C;
386    int nI2CBus;
387
388    nI2CBus = xf86I2CGetScreenBuses(iScreen, &ppI2CBus);
389    while (--nI2CBus >= 0)
390    {
391        pI2CBus = ppI2CBus[nI2CBus];
392        pATII2C = pI2CBus->DriverPrivate.ptr;
393
394        xf86DestroyI2CBusRec(pI2CBus, TRUE, TRUE);
395        xfree(pATII2C);
396    }
397
398    xfree(ppI2CBus);
399}
400