| /*************************************************************************** |
| * __________ __ ___. |
| * 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 */ |