iBoot/drivers/iic/swiic.c

356 lines
7.3 KiB
C

/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
/* if no hardware IIC, use the software fallback */
#if !WITH_HW_IIC
#include <debug.h>
#include <drivers/iic.h>
#include <platform.h>
#include <platform/gpio.h>
#include <platform/gpiodef.h>
#include <sys.h>
#include <sys/task.h>
#ifndef IICS_MASK
#define IICS_MASK ((1 << IICS_COUNT) - 1)
#endif
static struct iic_soft_device *get_iic_soft_device(int port);
static void iic_soft_setup(struct iic_soft_device *dev);
static void iic_soft_reset_bus(struct iic_soft_device *dev);
static void _setSCL(struct iic_soft_device *dev, bool assert);
static void _setSDA(struct iic_soft_device *dev, bool assert);
static u_int32_t _getSDA(struct iic_soft_device *dev);
static void _sendStart(struct iic_soft_device *dev);
static void _sendRestart(struct iic_soft_device *dev);
static void _sendStop(struct iic_soft_device *dev);
static void _sendAck(struct iic_soft_device *dev, bool ack);
static bool _writeByte(struct iic_soft_device *dev, u_int8_t data);
static u_int8_t _readByte(struct iic_soft_device *dev, bool ack);
/* struct definition */
struct iic_soft_device {
u_int32_t _i2cPeriod;
u_int32_t _i2cSlaveDevice;
u_int32_t iicAddressLength;
const u_int8_t *iicAddressBuffer;
u_int32_t iicDataLength;
u_int8_t *iicDataBuffer;
u_int32_t _i2cDelay;
u_int32_t _i2cSDA;
gpio_t gpio_scl;
gpio_t gpio_sda;
};
void iic_init(void)
{
int iic;
dprintf(DEBUG_INFO, "iic_soft_init()\n");
for (iic = 0; iic < IICS_COUNT; iic++)
iic_soft_setup(get_iic_soft_device(iic));
}
void iic_set_filter(int iic, uint32_t value) {}
void iic_set_frequency(int iic, u_int32_t frequency) {}
int iic_read(int iic, u_int8_t address, const void *send_data, size_t send_len, void *data, size_t len, iic_fmt_t fmt)
{
struct iic_soft_device *dev = get_iic_soft_device(iic);
u_int32_t cnt = 0;
if (dev == 0) return -1;
if (send_len == 0) return -1;
dev->_i2cSlaveDevice = address;
dev->iicAddressLength = send_len;
dev->iicAddressBuffer = (const u_int8_t *)send_data;
dev->iicDataLength = len;
dev->iicDataBuffer = (u_int8_t *)data;
/* Send slave address */
_sendStart(dev);
if (!_writeByte(dev, dev->_i2cSlaveDevice & ~(0x1))) {
_sendStop(dev);
return -1;
}
/* Send address bytes */
for (cnt = 0; cnt < dev->iicAddressLength; cnt++) {
if (!_writeByte(dev, dev->iicAddressBuffer[cnt])) {
_sendStop(dev);
return -1;
}
}
if (fmt == IIC_NORMAL) {
/* Stop */
_sendStop(dev);
_sendStart(dev);
} else {
_sendRestart(dev);
}
/* Send slave address */
if (!_writeByte(dev, dev->_i2cSlaveDevice | (0x1))) {
_sendStop(dev);
return -1;
}
/* clock data in */
for (cnt = 0; cnt < dev->iicDataLength; cnt++) {
dev->iicDataBuffer[cnt] = _readByte(dev, cnt < (dev->iicDataLength -1));
}
/* Stop */
_sendStop(dev);
return 0;
}
int iic_write(int iic, u_int8_t address, const void *data, size_t len)
{
struct iic_soft_device *dev = get_iic_soft_device(iic);
u_int32_t cnt =0;
if (dev == 0) return -1;
dev->_i2cSlaveDevice = address;
dev->iicAddressLength = 0;
dev->iicAddressBuffer = 0;
dev->iicDataLength = len;
dev->iicDataBuffer = (u_int8_t *)data;
/* Send slave address */
_sendStart(dev);
if (!_writeByte(dev, dev->_i2cSlaveDevice & ~(0x1))) {
_sendStop(dev);
return -1;
}
/* Send data bytes */
for (cnt = 0; cnt < dev->iicDataLength; cnt++) {
if (!_writeByte(dev, dev->iicDataBuffer[cnt])) {
_sendStop(dev);
return -1;
}
}
/* Send stop */
_sendStop(dev);
return 0;
}
bool iic_probe(int iic, u_int8_t address)
{
/* might be nice to implement this */
return false;
}
/* Private */
static struct iic_soft_device _iic_soft_device[] = {
#if IICS_COUNT > 0
{
.gpio_scl = GPIO_IIC0_SCL,
.gpio_sda = GPIO_IIC0_SDA,
},
#endif
#if IICS_COUNT > 1
{
.gpio_scl = GPIO_IIC1_SCL,
.gpio_sda = GPIO_IIC1_SDA,
},
#endif
#if IICS_COUNT > 2
{
.gpio_scl = GPIO_IIC2_SCL,
.gpio_sda = GPIO_IIC2_SDA,
}
#endif
};
static struct iic_soft_device *get_iic_soft_device(int port)
{
if (!((1 << port) & IICS_MASK))
return 0;
return &_iic_soft_device[port];
}
static void iic_soft_setup(struct iic_soft_device *dev)
{
if (dev == 0) return;
/* set the clock as 400kHz */
// dev->_i2cPeriod = 2500;
// dev->_i2cDelay = (dev->_i2cPeriod + 3)/4;
/*reset the bus */
iic_soft_reset_bus(dev);
}
static void iic_soft_reset_bus(struct iic_soft_device *dev)
{
u_int32_t cnt;
// Write 00 so all devices will complete any partial transaction
// SDA starts low and goes high after the loop
// SCL start and ends the loop high
// The first and last iterations of the loop produce the
// start and stop conditions
gpio_configure(dev->gpio_sda, GPIO_CFG_OUT_0);
for (cnt = 0; cnt < (2 * 9 + 1); cnt++) {
gpio_configure(dev->gpio_scl, (cnt & 1) ? GPIO_CFG_OUT_0 : GPIO_CFG_IN);
spin(5);
}
gpio_configure(dev->gpio_scl, GPIO_CFG_IN);
gpio_configure(dev->gpio_sda, GPIO_CFG_IN);
}
static void _setSCL(struct iic_soft_device *dev, bool assert)
{
if (assert) {
gpio_configure(dev->gpio_scl, GPIO_CFG_IN);
} else {
gpio_configure(dev->gpio_scl, GPIO_CFG_OUT_0);
}
}
static void _setSDA(struct iic_soft_device *dev, bool assert)
{
if (assert) {
gpio_configure(dev->gpio_sda, GPIO_CFG_IN);
} else {
gpio_configure(dev->gpio_sda, GPIO_CFG_OUT_0);
}
}
static u_int32_t _getSDA(struct iic_soft_device *dev)
{
u_int32_t tmp;
gpio_configure(dev->gpio_sda, GPIO_CFG_IN);
tmp = gpio_read(dev->gpio_sda);
return tmp;
}
/* Send Start: SDA High -> Low while SCL is high */
static void _sendStart(struct iic_soft_device *dev)
{
spin(1);
spin(1);
_setSDA(dev, false);
spin(1);
}
/* Send Restart: XXX */
static void _sendRestart(struct iic_soft_device *dev)
{
_setSCL(dev, false);
spin(1);
_setSDA(dev, true);
spin(1);
_setSCL(dev, true);
spin(1);
_setSDA(dev, false);
}
/* Send Stop: SDA Low -> High while SCL is high */
static void _sendStop(struct iic_soft_device *dev)
{
_setSCL(dev, false);
spin(1);
_setSDA(dev, false);
spin(1);
_setSCL(dev, true);
spin(1);
_setSDA(dev, true);
}
static void _sendAck(struct iic_soft_device *dev, bool ack)
{
_setSCL(dev, false);
spin(1);
_setSDA(dev, !ack);
spin(1);
_setSCL(dev, true);
spin(1);
spin(1);
}
static bool _writeByte(struct iic_soft_device *dev, u_int8_t data)
{
u_int32_t cnt;
u_int32_t ack;
/* Send the byte */
for (cnt = 0; cnt < 8; cnt++) {
_setSCL(dev, false);
spin(1);
_setSDA(dev, data & 0x80);
spin(1);
_setSCL(dev, true);
spin(1);
spin(1);
data = (data << 1);
}
/* Look for the ACK */
_setSCL(dev, false);
spin(1);
_setSDA(dev, true);
spin(1);
_setSCL(dev, true);
spin(1);
ack = !_getSDA(dev);
spin(1);
return (ack != 0);
}
static u_int8_t _readByte(struct iic_soft_device *dev, bool ack)
{
u_int32_t cnt;
u_int8_t data = 0;
for (cnt = 0; cnt < 8; cnt++) {
_setSCL(dev, false);
spin(1);
_setSDA(dev, true);
spin(1);
_setSCL(dev, true);
spin(1);
data <<= 1;
data |= _getSDA(dev);
spin(1);
}
_sendAck(dev, ack);
return data;
}
#endif /* !WITH_HW_IIC */