blob: e83ab57014451c4f2a5434c71efdfe03189afa66 [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* (C) Copyright 2002
* David Mueller, ELSOFT AG, d.mueller@elsoft.ch
*
* 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; version 2 of the License.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <console/console.h>
#include <delay.h>
#include <arch/io.h>
#include <device/i2c.h>
#include "clk.h"
#include "i2c.h"
#define I2C_WRITE 0
#define I2C_READ 1
#define I2C_OK 0
#define I2C_NOK 1
#define I2C_NACK 2
#define I2C_NOK_LA 3 /* Lost arbitration */
#define I2C_NOK_TOUT 4 /* time out */
#define I2CSTAT_BSY 0x20 /* Busy bit */
#define I2CSTAT_NACK 0x01 /* Nack bit */
#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */
#define I2CCON_IRPND 0x10 /* Interrupt pending bit */
#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */
#define I2C_MODE_MR 0x80 /* Master Receive Mode */
#define I2C_START_STOP 0x20 /* START / STOP */
#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */
/* The timeouts we live by */
enum {
I2C_XFER_TIMEOUT_MS = 35, /* xfer to complete */
I2C_INIT_TIMEOUT_MS = 1000, /* bus free on init */
I2C_IDLE_TIMEOUT_MS = 100, /* waiting for bus idle */
I2C_STOP_TIMEOUT_US = 200, /* waiting for stop events */
};
static struct s3c24x0_i2c_bus i2c_buses[] = {
{
.bus_num = 0,
.regs = (struct s3c24x0_i2c *)0x12c60000,
.periph_id = PERIPH_ID_I2C0,
},
{
.bus_num = 1,
.regs = (struct s3c24x0_i2c *)0x12c70000,
.periph_id = PERIPH_ID_I2C1,
},
{
.bus_num = 2,
.regs = (struct s3c24x0_i2c *)0x12c80000,
.periph_id = PERIPH_ID_I2C2,
},
{
.bus_num = 3,
.regs = (struct s3c24x0_i2c *)0x12c90000,
.periph_id = PERIPH_ID_I2C3,
},
{
.bus_num = 4,
.regs = (struct s3c24x0_i2c *)0x12ca0000,
.periph_id = PERIPH_ID_I2C4,
},
{
.bus_num = 5,
.regs = (struct s3c24x0_i2c *)0x12cb0000,
.periph_id = PERIPH_ID_I2C5,
},
{
.bus_num = 6,
.regs = (struct s3c24x0_i2c *)0x12cc0000,
.periph_id = PERIPH_ID_I2C6,
},
{
.bus_num = 7,
.regs = (struct s3c24x0_i2c *)0x12cd0000,
.periph_id = PERIPH_ID_I2C7,
},
};
static int WaitForXfer(struct s3c24x0_i2c *i2c)
{
int i;
i = I2C_XFER_TIMEOUT_MS * 20;
while (!(readl(&i2c->iiccon) & I2CCON_IRPND)) {
if (i == 0) {
printk(BIOS_ERR, "%s: i2c xfer timeout\n", __func__);
return I2C_NOK_TOUT;
}
udelay(50);
i--;
}
return I2C_OK;
}
static int IsACK(struct s3c24x0_i2c *i2c)
{
return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
}
static void ReadWriteByte(struct s3c24x0_i2c *i2c)
{
uint32_t x;
x = readl(&i2c->iiccon);
writel(x & ~I2CCON_IRPND, &i2c->iiccon);
}
static void i2c_ch_init(struct s3c24x0_i2c_bus *bus, int speed, int slaveadd)
{
unsigned long freq, pres = 16, div;
unsigned long val;
freq = clock_get_periph_rate(bus->periph_id);
/* calculate prescaler and divisor values */
if ((freq / pres / (16 + 1)) > speed)
/* set prescaler to 512 */
pres = 512;
div = 0;
while ((freq / pres / (div + 1)) > speed)
div++;
/* set prescaler, divisor according to freq, also set ACKGEN, IRQ */
val = (div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0);
writel(val, &bus->regs->iiccon);
/* init to SLAVE RECEIVE mode and clear I2CADDn */
writel(0, &bus->regs->iicstat);
writel(slaveadd, &bus->regs->iicadd);
/* program Master Transmit (and implicit STOP) */
writel(I2C_MODE_MT | I2C_TXRX_ENA, &bus->regs->iicstat);
}
/*
* MULTI BUS I2C support
*/
static void i2c_bus_init(struct s3c24x0_i2c_bus *bus, int speed, int slaveadd)
{
i2c_ch_init(bus, speed, slaveadd);
}
/*
* Verify the whether I2C ACK was received or not
*
* @param i2c pointer to I2C register base
* @param buf array of data
* @param len length of data
* return I2C_OK when transmission done
* I2C_NACK otherwise
*/
static int i2c_send_verify(struct s3c24x0_i2c *i2c, unsigned char buf[],
unsigned char len)
{
int i, result = I2C_OK;
if (IsACK(i2c)) {
for (i = 0; (i < len) && (result == I2C_OK); i++) {
writel(buf[i], &i2c->iicds);
ReadWriteByte(i2c);
result = WaitForXfer(i2c);
if (result == I2C_OK && !IsACK(i2c))
result = I2C_NACK;
}
} else {
result = I2C_NACK;
}
return result;
}
void i2c_init(unsigned bus_num, int speed, int slaveadd)
{
struct s3c24x0_i2c_bus *i2c;
int i;
i2c = &i2c_buses[bus_num];
i2c_bus_init(i2c, speed, slaveadd);
/* wait for some time to give previous transfer a chance to finish */
i = I2C_INIT_TIMEOUT_MS * 20;
while ((readl(&i2c->regs->iicstat) & I2CSTAT_BSY) && (i > 0)) {
udelay(50);
i--;
}
i2c_ch_init(i2c, speed, slaveadd);
}
/*
* Send a STOP event and wait for it to have completed
*
* @param mode If it is a master transmitter or receiver
* @return I2C_OK if the line became idle before timeout I2C_NOK_TOUT otherwise
*/
static int i2c_send_stop(struct s3c24x0_i2c *i2c, int mode)
{
int timeout;
/* Setting the STOP event to fire */
writel(mode | I2C_TXRX_ENA, &i2c->iicstat);
ReadWriteByte(i2c);
/* Wait for the STOP to send and the bus to go idle */
for (timeout = I2C_STOP_TIMEOUT_US; timeout > 0; timeout -= 5) {
if (!(readl(&i2c->iicstat) & I2CSTAT_BSY))
return I2C_OK;
udelay(5);
}
return I2C_NOK_TOUT;
}
/*
* cmd_type is 0 for write, 1 for read.
*
* addr_len can take any value from 0-255, it is only limited
* by the char, we could make it larger if needed. If it is
* 0 we skip the address write cycle.
*/
static int i2c_transfer(struct s3c24x0_i2c *i2c,
unsigned char cmd_type,
unsigned char chip,
unsigned char addr[],
unsigned char addr_len,
unsigned char data[],
unsigned short data_len)
{
int i, result, stop_bit_result;
uint32_t x;
if (data == 0 || data_len == 0) {
/* Don't support data transfer of no length or to address 0 */
printk(BIOS_ERR, "i2c_transfer: bad call\n");
return I2C_NOK;
}
/* Check I2C bus idle */
i = I2C_IDLE_TIMEOUT_MS * 20;
while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) {
udelay(50);
i--;
}
if (readl(&i2c->iicstat) & I2CSTAT_BSY) {
printk(BIOS_ERR, "%s: bus busy\n", __func__);
return I2C_NOK_TOUT;
}
x = readl(&i2c->iiccon);
writel(x | I2CCON_ACKGEN, &i2c->iiccon);
if (addr && addr_len) {
writel(chip, &i2c->iicds);
/* send START */
writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
&i2c->iicstat);
if (WaitForXfer(i2c) == I2C_OK)
result = i2c_send_verify(i2c, addr, addr_len);
else
result = I2C_NACK;
} else
result = I2C_NACK;
switch (cmd_type) {
case I2C_WRITE:
if (result == I2C_OK)
result = i2c_send_verify(i2c, data, data_len);
else {
writel(chip, &i2c->iicds);
/* send START */
writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP,
&i2c->iicstat);
if (WaitForXfer(i2c) == I2C_OK)
result = i2c_send_verify(i2c, data, data_len);
}
if (result == I2C_OK)
result = WaitForXfer(i2c);
stop_bit_result = i2c_send_stop(i2c, I2C_MODE_MT);
break;
case I2C_READ:
{
int was_ok = (result == I2C_OK);
writel(chip, &i2c->iicds);
/* resend START */
writel(I2C_MODE_MR | I2C_TXRX_ENA |
I2C_START_STOP, &i2c->iicstat);
ReadWriteByte(i2c);
result = WaitForXfer(i2c);
if (was_ok || IsACK(i2c)) {
i = 0;
while ((i < data_len) && (result == I2C_OK)) {
/* disable ACK for final READ */
if (i == data_len - 1) {
x = readl(&i2c->iiccon) & ~I2CCON_ACKGEN;
writel(x, &i2c->iiccon);
}
ReadWriteByte(i2c);
result = WaitForXfer(i2c);
data[i] = readl(&i2c->iicds);
i++;
}
} else {
result = I2C_NACK;
}
stop_bit_result = i2c_send_stop(i2c, I2C_MODE_MR);
break;
}
default:
printk(BIOS_ERR, "i2c_transfer: bad call\n");
result = stop_bit_result = I2C_NOK;
break;
}
/*
* If the transmission went fine, then only the stop bit was left to
* fail. Otherwise, the real failure we're interested in came before
* that, during the actual transmission.
*/
return (result == I2C_OK) ? stop_bit_result : result;
}
int i2c_read(unsigned bus, unsigned chip, unsigned addr,
unsigned alen, uint8_t *buf, unsigned len)
{
struct s3c24x0_i2c_bus *i2c;
unsigned char xaddr[4];
int ret;
if (alen > 4) {
printk(BIOS_ERR, "I2C read: addr len %d not supported\n", alen);
return 1;
}
if (alen > 0) {
xaddr[0] = (addr >> 24) & 0xFF;
xaddr[1] = (addr >> 16) & 0xFF;
xaddr[2] = (addr >> 8) & 0xFF;
xaddr[3] = addr & 0xFF;
}
i2c = &i2c_buses[bus];
ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1, &xaddr[4 - alen],
alen, buf, len);
if (ret) {
printk(BIOS_ERR, "I2c read: failed %d\n", ret);
return 1;
}
return 0;
}
int i2c_write(unsigned bus, unsigned chip, unsigned addr,
unsigned alen, const uint8_t *buf, unsigned len)
{
struct s3c24x0_i2c_bus *i2c;
unsigned char xaddr[4];
int ret;
if (alen > 4) {
printk(BIOS_ERR, "I2C write: addr len %d not supported\n",
alen);
return 1;
}
if (alen > 0) {
xaddr[0] = (addr >> 24) & 0xFF;
xaddr[1] = (addr >> 16) & 0xFF;
xaddr[2] = (addr >> 8) & 0xFF;
xaddr[3] = addr & 0xFF;
}
i2c = &i2c_buses[bus];
ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1, &xaddr[4 - alen],
alen, (void *)buf, len);
return ret != 0;
}