mirror of https://gerrit.osmocom.org/simtrace2
222 lines
3.7 KiB
C
222 lines
3.7 KiB
C
/* I2C EEPROM memory read and write utilities
|
|
*
|
|
* 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 program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
#include "board.h"
|
|
#include <stdbool.h>
|
|
|
|
/* Low-Level I2C Routines */
|
|
|
|
static const Pin pin_sda = {PIO_PA30, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_OPENDRAIN };
|
|
static const Pin pin_sda_in = {PIO_PA30, PIOA, ID_PIOA, PIO_INPUT, PIO_DEFAULT };
|
|
static const Pin pin_scl = {PIO_PA31, PIOA, ID_PIOA, PIO_OUTPUT_1, PIO_OPENDRAIN };
|
|
|
|
static void i2c_delay()
|
|
{
|
|
volatile int v;
|
|
int i;
|
|
|
|
/* 100 cycles results in SCL peak length of 44us, so it's about
|
|
* 440ns per cycle here */
|
|
for (i = 0; i < 14; i++) {
|
|
v = 0;
|
|
}
|
|
}
|
|
|
|
void i2c_pin_init(void)
|
|
{
|
|
PIO_Configure(&pin_scl, PIO_LISTSIZE(pin_scl));
|
|
PIO_Configure(&pin_sda, PIO_LISTSIZE(pin_sda));
|
|
}
|
|
|
|
static void set_scl(void)
|
|
{
|
|
PIO_Set(&pin_scl);
|
|
i2c_delay();
|
|
}
|
|
|
|
static void set_sda(void)
|
|
{
|
|
PIO_Set(&pin_sda);
|
|
i2c_delay();
|
|
}
|
|
|
|
static void clear_scl(void)
|
|
{
|
|
PIO_Clear(&pin_scl);
|
|
i2c_delay();
|
|
}
|
|
|
|
static void clear_sda(void)
|
|
{
|
|
PIO_Clear(&pin_sda);
|
|
i2c_delay();
|
|
}
|
|
|
|
static bool read_sda(void)
|
|
{
|
|
bool ret;
|
|
|
|
PIO_Configure(&pin_sda_in, PIO_LISTSIZE(pin_sda_in));
|
|
if (PIO_Get(&pin_sda_in))
|
|
ret = true;
|
|
else
|
|
ret = false;
|
|
PIO_Configure(&pin_sda, PIO_LISTSIZE(pin_sda));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Core I2C Routines */
|
|
|
|
static bool i2c_started = false;
|
|
|
|
static void i2c_start_cond(void)
|
|
{
|
|
if (i2c_started) {
|
|
set_sda();
|
|
set_scl();
|
|
}
|
|
|
|
clear_sda();
|
|
i2c_delay();
|
|
clear_scl();
|
|
i2c_started = true;
|
|
}
|
|
|
|
void i2c_stop_cond(void)
|
|
{
|
|
clear_sda();
|
|
set_scl();
|
|
set_sda();
|
|
i2c_delay();
|
|
i2c_started = false;
|
|
}
|
|
|
|
static void i2c_write_bit(bool bit)
|
|
{
|
|
if (bit)
|
|
set_sda();
|
|
else
|
|
clear_sda();
|
|
i2c_delay(); // ?
|
|
set_scl();
|
|
clear_scl();
|
|
}
|
|
|
|
static bool i2c_read_bit(void)
|
|
{
|
|
bool bit;
|
|
|
|
set_sda();
|
|
set_scl();
|
|
bit = read_sda();
|
|
clear_scl();
|
|
|
|
return bit;
|
|
}
|
|
|
|
bool i2c_write_byte(bool send_start, bool send_stop, uint8_t byte)
|
|
{
|
|
uint8_t bit;
|
|
bool nack;
|
|
|
|
if (send_start)
|
|
i2c_start_cond();
|
|
|
|
for (bit = 0; bit < 8; bit++) {
|
|
i2c_write_bit((byte & 0x80) != 0);
|
|
byte <<= 1;
|
|
}
|
|
|
|
nack = i2c_read_bit();
|
|
|
|
if (send_stop)
|
|
i2c_stop_cond();
|
|
|
|
return nack;
|
|
}
|
|
|
|
uint8_t i2c_read_byte(bool nack, bool send_stop)
|
|
{
|
|
uint8_t byte = 0;
|
|
uint8_t bit;
|
|
|
|
for (bit = 0; bit < 8; bit++) {
|
|
byte = (byte << 1) | i2c_read_bit();
|
|
}
|
|
|
|
i2c_write_bit(nack);
|
|
|
|
if (send_stop)
|
|
i2c_stop_cond();
|
|
|
|
return byte;
|
|
}
|
|
|
|
|
|
/* EEPROM related code */
|
|
|
|
int eeprom_write_byte(uint8_t slave, uint8_t addr, uint8_t byte)
|
|
{
|
|
bool nack;
|
|
|
|
WDT_Restart(WDT);
|
|
|
|
/* Write slave address */
|
|
nack = i2c_write_byte(true, false, slave << 1);
|
|
if (nack)
|
|
goto out_stop;
|
|
nack = i2c_write_byte(false, false, addr);
|
|
if (nack)
|
|
goto out_stop;
|
|
nack = i2c_write_byte(false, true, byte);
|
|
if (nack)
|
|
goto out_stop;
|
|
/* Wait tWR time to ensure EEPROM is writing correctly (tWR = 5 ms for AT24C02) */
|
|
mdelay(5);
|
|
|
|
out_stop:
|
|
i2c_stop_cond();
|
|
if (nack)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int eeprom_read_byte(uint8_t slave, uint8_t addr)
|
|
{
|
|
bool nack;
|
|
|
|
WDT_Restart(WDT);
|
|
|
|
/* dummy write cycle */
|
|
nack = i2c_write_byte(true, false, slave << 1);
|
|
if (nack)
|
|
goto out_stop;
|
|
nack = i2c_write_byte(false, false, addr);
|
|
if (nack)
|
|
goto out_stop;
|
|
/* Re-start with read */
|
|
nack = i2c_write_byte(true, false, (slave << 1) | 1);
|
|
if (nack)
|
|
goto out_stop;
|
|
|
|
return i2c_read_byte(true, true);
|
|
|
|
out_stop:
|
|
i2c_stop_cond();
|
|
if (nack)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|