summaryrefslogtreecommitdiff
path: root/firmware/target/coldfire/iaudio/x5/ds2411-x5.c
blob: ffc227a83f619b0ab09ff0d29d7eefa74af46a6a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2007 by Michael Sevakis
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "config.h"
#include "system.h"
#include "cpu.h"
#include "ds2411.h"
#include "logf.h"

/* Delay factor that depends on CPU frequency */
static unsigned int ds2411_delay_factor = 0;

#define DS2411_BIT (1 << 22)

/* Delay the specified number of microseconds - plus a tiny bit */
#define DELAY(uS)                \
    asm volatile(                   \
        "move.l %[x], %%d0      \n" \
        "mulu.l %[factor], %%d0 \n" \
    "1:                         \n" \
        "subq.l #1, %%d0        \n" \
        "bhi.s  1b              \n" \
        : : [factor]"d,d"(ds2411_delay_factor), [x]"r,i"(uS) : "d0");

/* Calculate the CRC of a byte */
static unsigned char ds2411_calc_crc(unsigned char byte)
{
    /* POLYNOMIAL = X^8 + X^5 + X^4 + 1 */
    static const unsigned char eor[8] =
    {
        0x5e, /* 01011110 */
        0xbc, /* 10111100 */
        0x61, /* 01100001 */
        0xc2, /* 11000010 */
        0x9d, /* 10011101 */
        0x23, /* 00100011 */
        0x46, /* 01000110 */
        0x8c, /* 10001100 */
    };

    unsigned char crc = 0;
    int i = 0;

    do
    {
        if (byte & (1 << i))
            crc ^= eor[i];
    }
    while (++i < 8);

    return crc;
} /* ds2411_calc_crc */

/* Write a byte to the DS2411 - LSb first */
static void ds2411_write_byte(unsigned char data)
{
    int i = 0;

    do
    {
        if (data & 0x01)
        {
            /* Write a "1": Hold line low, then leave line pulled up and wait
               out the remainder of Tslot */
            or_l(DS2411_BIT, &GPIO_ENABLE);
            DELAY(6);
            and_l(~DS2411_BIT, &GPIO_ENABLE);
            DELAY(60);
        }
        else
        {
            /* Write a "0": Hold line low, then leave line pulled up and wait
               out the remainder of Tslot which is just Trec */
            or_l(DS2411_BIT, &GPIO_ENABLE);
            DELAY(60);
            and_l(~DS2411_BIT, &GPIO_ENABLE);
            DELAY(6);
        }

        data >>= 1;
    }
    while (++i < 8);
} /* ds2411_write_byte */

/* Read a byte from the DS2411 - LSb first */
static unsigned char ds2411_read_byte(void)
{
    int i = 0;
    unsigned data = 0;

    do
    {
        /* Hold line low to begin bit read: Tf + Trl */
        or_l(DS2411_BIT, &GPIO_ENABLE);
        DELAY(6);

        /* Set line high and delay before sampling within the master
           sampling window: Tmsr - max 15us from Trl start */
        and_l(~DS2411_BIT, &GPIO_ENABLE);
        DELAY(9);

        /* Sample data line */
        if ((GPIO_READ & DS2411_BIT) != 0)
            data |= 1 << i;

        /* Wait out the remainder of Tslot */
        DELAY(60);   
    }
    while (++i < 8);

    return data;
} /* ds2411_read_byte */

/*
 * Byte 0:    8-bit family code (01h)
 * Bytes 1-6: 48-bit serial number
 * Byte 7:    8-bit CRC code
 */
int ds2411_read_id(struct ds2411_id *id)
{
    int level = disable_irq_save(); /* Timing sensitive */
    int i;
    unsigned char crc;

    /* Initialize delay factor based on loop time: 3*(uS-1) + 3 */
    ds2411_delay_factor = MAX(cpu_frequency / (1000000*3), 1);

    /* Init GPIO 1 wire bus for bit banging with a pullup resistor where 
     * it is set low as output and switched between input and output mode.
     * Required for bidirectional communication on a single wire.
     */
    or_l(DS2411_BIT, &GPIO_FUNCTION);   /* Set pin as GPIO            */
    and_l(~DS2411_BIT, &GPIO_ENABLE);   /* Set as input               */
    and_l(~DS2411_BIT, &GPIO_OUT);      /* Set low when set as output */

    /* Delay 100us to stabilize */
    DELAY(100);

    /* Issue reset pulse - 480uS or more to ensure standard (not overdrive)
       mode - we don't have the timing accuracy for that. */
    or_l(DS2411_BIT, &GPIO_ENABLE);
    /* Delay 560us: (Trstlmin + Trstlmax) / 2*/
    DELAY(560);
    and_l(~DS2411_BIT, &GPIO_ENABLE);
    /* Delay 66us: Tpdhmax + 6 */
    DELAY(66);

    /* Read presence pulse - line should be pulled low at proper time by the
       slave device */
    if ((GPIO_READ & DS2411_BIT) == 0)
    {
        /* Trsth + 1 - 66 = Tpdhmax + Tpdlmax + Trecmin + 1 - 66 */
        DELAY(240);

        /* ds2411 should be ready for data transfer */

        /* Send Read ROM command */
        ds2411_write_byte(0x33);

        /* Read ROM serial number and CRC */
        i = 0, crc = 0;

        do
        {
            unsigned char byte = ds2411_read_byte();
            ((unsigned char *)id)[i] = byte;
            crc = ds2411_calc_crc(crc ^ byte);
        }
        while (++i < 8);

        /* Check that family code is ok */
        if (id->family_code != 0x01)
        {
            logf("ds2411: invalid family code=%02X", (unsigned)id->family_code);
            i = DS2411_INVALID_FAMILY_CODE;
        }
        /* Check that CRC was ok */
        else if (crc != 0) /* Because last loop eors the CRC with the resulting CRC */
        {
            logf("ds2411: invalid CRC=%02X", (unsigned)id->crc);
            i = DS2411_INVALID_CRC;
        }
        else
        {
            /* Good ID read */
            i = DS2411_OK;
        }
    }
    else
    {
        logf("ds2411: no presence pulse");
        i = DS2411_NO_PRESENCE;
    }

    restore_irq(level);

    return i;
} /* ds2411_read_id */