i2c: Replace the i2c API.

The new API is in use in depthcharge and is based around the "i2c_transfer"
function instead of i2c_read and i2c_write. The new function takes an array of
i2c_seg structures which represent each portion of the transfer after a start
bit and before the stop bit. If there's more than one segment, they're
seperated by repeated starts.

Some wrapper functions have also been added which make certain common
operations easy. These include reading or writing a byte from a register or
reading or writing a blob of raw data. The i2c device drivers generally use
these wrappers but can call the i2c_transfer function directly if the need
something different.

The tegra i2c driver was very similar to the one in depthcharge and was simple
to convert. The Exynos 5250 and 5420 drivers were ported from depthcharge and
replace the ones in coreboot. The Exynos 5420 driver was ported from the high
speed portion of the one in coreboot and was straightforward to port back. The
low speed portion and the Exynos 5250 drivers had been transplanted from U-Boot
and were replaced with the depthcharge implementation.

BUG=None
TEST=Built and booted on nyan with and without EFS. Built and booted on, pit
and daisy.
BRANCH=None

Original-Change-Id: I1e98c3fa2560be25444ab3d0394bb214b9d56e93
Original-Signed-off-by: Gabe Black <gabeblack@google.com>
Original-Reviewed-on: https://chromium-review.googlesource.com/193561
Original-Reviewed-by: David Hendricks <dhendrix@chromium.org>
Original-Reviewed-by: Jimmy Zhang <jimmzhang@nvidia.com>
Original-Tested-by: Jimmy Zhang <jimmzhang@nvidia.com>
Original-Reviewed-by: Hung-Te Lin <hungte@chromium.org>
Original-Commit-Queue: Gabe Black <gabeblack@chromium.org>
Original-Tested-by: Gabe Black <gabeblack@chromium.org>
(cherry picked from commit 00c423fb2c06c69d580ee3ec0a3892ebf164a5fe)

This cherry-pick required additional changes to the following:
src/cpu/allwinner/a10/twi.c
src/drivers/xpowers/axp209/axp209.c

Signed-off-by: Marc Jones <marc.jones@se-eng.com>

Change-Id: I691959c66308eeeec219b1bec463b8b365a246d7
Reviewed-on: http://review.coreboot.org/7751
Tested-by: build bot (Jenkins)
Reviewed-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
This commit is contained in:
Gabe Black 2014-04-07 18:45:14 -07:00 committed by Marc Jones
parent be6f8cb0f0
commit cdb61a6f5d
20 changed files with 826 additions and 951 deletions

View File

@ -109,8 +109,8 @@ static void i2c_send_stop(struct a1x_twi *twi)
write32(reg32, &twi->ctl); write32(reg32, &twi->ctl);
} }
int i2c_read(unsigned bus, unsigned chip, unsigned addr, static int i2c_read(unsigned bus, unsigned chip, unsigned addr,
unsigned alen, uint8_t *buf, unsigned len) uint8_t *buf, unsigned len)
{ {
unsigned count = len; unsigned count = len;
enum twi_status expected_status; enum twi_status expected_status;
@ -169,8 +169,8 @@ int i2c_read(unsigned bus, unsigned chip, unsigned addr,
return len; return len;
} }
int i2c_write(unsigned bus, unsigned chip, unsigned addr, static int i2c_write(unsigned bus, unsigned chip, unsigned addr,
unsigned alen, const uint8_t *buf, unsigned len) const uint8_t *buf, unsigned len)
{ {
unsigned count = len; unsigned count = len;
struct a1x_twi *twi = (void *)TWI_BASE(bus); struct a1x_twi *twi = (void *)TWI_BASE(bus);
@ -204,3 +204,35 @@ int i2c_write(unsigned bus, unsigned chip, unsigned addr,
return len; return len;
} }
/*
* This transfer function is not complete or correct, but it provides
* the basic support that the above read and write functions previously
* provided directly. It is extremely limited and not useful for
* advanced drivers like TPM.
*
* TODO: Rewite the i2c_transfer and supporting functions
*
*/
int i2c_transfer(unsigned bus, struct i2c_seg *segments, int count)
{
struct i2c_seg *seg = segments;
if (seg->read) {
/* Read has one buf for the addr and one for the data */
if (count != 2)
return -1;
if(i2c_read(bus, seg->chip, *seg->buf, seg[1].buf, seg[1].len) < 0)
return -1;
} else {
/* Write buf has adder and data. */
if (count != 1)
return -1;
if(i2c_write(bus, seg->chip, *seg->buf, seg->buf+1, seg->len-1) < 0)
return -1;
}
return 0;
}

View File

@ -79,10 +79,7 @@ int tis_init(void)
* Probe TPM twice; the first probing might fail because TPM is asleep, * Probe TPM twice; the first probing might fail because TPM is asleep,
* and the probing can wake up TPM. * and the probing can wake up TPM.
*/ */
uint8_t tmp = 0; if (i2c_writeb(bus, chip, 0, 0) && i2c_writeb(bus, chip, 0, 0))
if (i2c_write(bus, chip, 0, 1, &tmp, sizeof(tmp)) &&
i2c_write(bus, chip, 0, 1, &tmp, sizeof(tmp)))
return -1; return -1;
return 0; return 0;

View File

@ -120,8 +120,7 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len)
if ((tpm_dev.chip_type == SLB9635) || (tpm_dev.chip_type == UNKNOWN)) { if ((tpm_dev.chip_type == SLB9635) || (tpm_dev.chip_type == UNKNOWN)) {
/* slb9635 protocol should work in both cases */ /* slb9635 protocol should work in both cases */
for (count = 0; count < MAX_COUNT; count++) { for (count = 0; count < MAX_COUNT; count++) {
rc = i2c_write(tpm_dev.bus, tpm_dev.addr, rc = i2c_write_raw(tpm_dev.bus, tpm_dev.addr, &addr, 1);
0, 0, &addr, 1);
if (rc == 0) if (rc == 0)
break; /* success, break to skip sleep */ break; /* success, break to skip sleep */
@ -137,8 +136,8 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len)
*/ */
for (count = 0; count < MAX_COUNT; count++) { for (count = 0; count < MAX_COUNT; count++) {
udelay(SLEEP_DURATION); udelay(SLEEP_DURATION);
rc = i2c_read(tpm_dev.bus, tpm_dev.addr, rc = i2c_read_raw(tpm_dev.bus, tpm_dev.addr,
0, 0, buffer, len); buffer, len);
if (rc == 0) if (rc == 0)
break; /* success, break to skip sleep */ break; /* success, break to skip sleep */
@ -150,9 +149,13 @@ static int iic_tpm_read(uint8_t addr, uint8_t *buffer, size_t len)
* retries should usually not be needed, but are kept just to * retries should usually not be needed, but are kept just to
* be safe on the safe side. * be safe on the safe side.
*/ */
struct i2c_seg aseg = { .read = 0, .chip = tpm_dev.addr,
.buf = &addr, .len = 1 };
struct i2c_seg dseg = { .read = 1, .chip = tpm_dev.addr,
.buf = buffer, .len = len };
for (count = 0; count < MAX_COUNT; count++) { for (count = 0; count < MAX_COUNT; count++) {
rc = i2c_read(tpm_dev.bus, tpm_dev.addr, rc = i2c_transfer(tpm_dev.bus, &aseg, 1) ||
addr, 1, buffer, len); i2c_transfer(tpm_dev.bus, &dseg, 1);
if (rc == 0) if (rc == 0)
break; /* break here to skip sleep */ break; /* break here to skip sleep */
udelay(SLEEP_DURATION); udelay(SLEEP_DURATION);
@ -186,7 +189,7 @@ static int iic_tpm_write_generic(uint8_t addr, uint8_t *buffer, size_t len,
if (!tpm_dev.bus) if (!tpm_dev.bus)
return -1; return -1;
for (count = 0; count < max_count; count++) { for (count = 0; count < max_count; count++) {
rc = i2c_write(tpm_dev.bus, tpm_dev.addr, 0, 0, rc = i2c_write_raw(tpm_dev.bus, tpm_dev.addr,
tpm_dev.buf, len + 1); tpm_dev.buf, len + 1);
if (rc == 0) if (rc == 0)
break; /* success, break to skip sleep */ break; /* success, break to skip sleep */

View File

@ -93,7 +93,7 @@ struct max77686_para max77686_param[] = {/*{vol_addr, vol_bitpos,
static inline int max77686_i2c_write(unsigned int bus, unsigned char chip_addr, static inline int max77686_i2c_write(unsigned int bus, unsigned char chip_addr,
unsigned int reg, unsigned char val) unsigned int reg, unsigned char val)
{ {
return i2c_write(bus, chip_addr, reg, 1, &val, 1); return i2c_writeb(bus, chip_addr, reg, val);
} }
/* /*
@ -107,7 +107,7 @@ static inline int max77686_i2c_write(unsigned int bus, unsigned char chip_addr,
static inline int max77686_i2c_read(unsigned int bus, unsigned char chip_addr, static inline int max77686_i2c_read(unsigned int bus, unsigned char chip_addr,
unsigned int reg, unsigned char *val) unsigned int reg, unsigned char *val)
{ {
return i2c_read(bus, chip_addr, reg, 1, val, 1); return i2c_readb(bus, chip_addr, reg, (uint8_t *)val);
} }
/* /*

View File

@ -31,7 +31,6 @@ void parade_ps8625_bridge_setup(unsigned bus, unsigned chip_base,
for (i = 0; i < parade_write_count; i++) { for (i = 0; i < parade_write_count; i++) {
const struct parade_write *w = &parade_writes[i]; const struct parade_write *w = &parade_writes[i];
i2c_write(bus, chip_base + w->offset, w->reg, sizeof(w->reg), i2c_writeb(bus, chip_base + w->offset, w->reg, w->val);
&w->val, sizeof(w->val));
} }
} }

View File

@ -64,7 +64,7 @@ static int tps65090_i2c_write(unsigned int bus,
{ {
int ret; int ret;
ret = i2c_write(bus, TPS65090_I2C_ADDR, reg_addr, 1, &value, 1); ret = i2c_writeb(bus, TPS65090_I2C_ADDR, reg_addr, value);
printk(BIOS_DEBUG, "%s: reg=%#x, value=%#x, ret=%d\n", printk(BIOS_DEBUG, "%s: reg=%#x, value=%#x, ret=%d\n",
__func__, reg_addr, value, ret); __func__, reg_addr, value, ret);
return ret; return ret;
@ -76,7 +76,7 @@ static int tps65090_i2c_read(unsigned int bus,
int ret; int ret;
printk(BIOS_DEBUG, "%s: reg=%#x, ", __func__, reg_addr); printk(BIOS_DEBUG, "%s: reg=%#x, ", __func__, reg_addr);
ret = i2c_read(bus, TPS65090_I2C_ADDR, reg_addr, 1, value, 1); ret = i2c_readb(bus, TPS65090_I2C_ADDR, reg_addr, value);
if (ret) if (ret)
printk(BIOS_DEBUG, "fail, ret=%d\n", ret); printk(BIOS_DEBUG, "fail, ret=%d\n", ret);
else else

View File

@ -47,12 +47,12 @@ enum registers {
*/ */
static int axp209_read(u8 bus, u8 reg, u8 *val) static int axp209_read(u8 bus, u8 reg, u8 *val)
{ {
return i2c_read(bus, AXP209_I2C_ADDR, reg, 1, val, 1); return i2c_readb(bus, AXP209_I2C_ADDR, reg, val);
} }
static int axp209_write(u8 bus, u8 reg, u8 val) static int axp209_write(u8 bus, u8 reg, u8 val)
{ {
return i2c_write(bus, AXP209_I2C_ADDR, reg, 1, &val, 1); return i2c_writeb(bus, AXP209_I2C_ADDR, reg, val);
} }
/** /**

View File

@ -116,13 +116,13 @@ int google_chromeec_command(struct chromeec_command *cec_command)
/* Start I2C communication */ /* Start I2C communication */
i2c_dump(bus, chip, (const uint8_t *)&cmd, size_i2c_cmd); i2c_dump(bus, chip, (const uint8_t *)&cmd, size_i2c_cmd);
if (i2c_write(bus, chip, 0, 0, (uint8_t *)&cmd, size_i2c_cmd) != 0) { if (i2c_write_raw(bus, chip, (uint8_t *)&cmd, size_i2c_cmd) != 0) {
printk(BIOS_ERR, "%s: Cannot complete write to i2c-%d:%#x\n", printk(BIOS_ERR, "%s: Cannot complete write to i2c-%d:%#x\n",
__func__, bus, chip); __func__, bus, chip);
cec_command->cmd_code = EC_RES_ERROR; cec_command->cmd_code = EC_RES_ERROR;
return 1; return 1;
} }
if (i2c_read(bus, chip, 0, 0, (uint8_t *)&resp, size_i2c_resp) != 0) { if (i2c_read_raw(bus, chip, (uint8_t *)&resp, size_i2c_resp) != 0) {
printk(BIOS_ERR, "%s: Cannot complete read from i2c-%d:%#x\n", printk(BIOS_ERR, "%s: Cannot complete read from i2c-%d:%#x\n",
__func__, bus, chip); __func__, bus, chip);
cec_command->cmd_code = EC_RES_ERROR; cec_command->cmd_code = EC_RES_ERROR;

View File

@ -1,7 +1,7 @@
/* /*
* This file is part of the coreboot project. * This file is part of the coreboot project.
* *
* Copyright (C) 2013 Google, Inc. * Copyright (C) 2014 Google, Inc.
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -21,11 +21,83 @@
#define _DEVICE_I2C_H_ #define _DEVICE_I2C_H_
#include <stdint.h> #include <stdint.h>
#include <stdlib.h>
/* note: chip is the 7-bit I2C address */ struct i2c_seg
int i2c_read(unsigned bus, unsigned chip, unsigned addr, {
unsigned alen, uint8_t *buf, unsigned len); int read;
int i2c_write(unsigned bus, unsigned chip, unsigned addr, uint8_t chip;
unsigned alen, const uint8_t *buf, unsigned len); uint8_t *buf;
int len;
};
int i2c_transfer(unsigned bus, struct i2c_seg *segments, int count);
/*
* Read a raw chunk of data in one segment and one frame.
*
* [start][slave addr][r][data][stop]
*/
static inline int i2c_read_raw(unsigned bus, uint8_t chip, uint8_t *data,
int len)
{
struct i2c_seg seg =
{ .read = 1, .chip = chip, .buf = data, .len = len };
return i2c_transfer(bus, &seg, 1);
}
/*
* Write a raw chunk of data in one segment and one frame.
*
* [start][slave addr][w][data][stop]
*/
static inline int i2c_write_raw(unsigned bus, uint8_t chip, uint8_t *data,
int len)
{
struct i2c_seg seg =
{ .read = 0, .chip = chip, .buf = data, .len = len };
return i2c_transfer(bus, &seg, 1);
}
/**
* Read a byte with two segments in one frame
*
* [start][slave addr][w][register addr][start][slave addr][r][data][stop]
*/
static inline int i2c_readb(unsigned bus, uint8_t chip, uint8_t reg,
uint8_t *data)
{
struct i2c_seg seg[2];
seg[0].read = 0;
seg[0].chip = chip;
seg[0].buf = &reg;
seg[0].len = 1;
seg[1].read = 1;
seg[1].chip = chip;
seg[1].buf = data;
seg[1].len = 1;
return i2c_transfer(bus, seg, ARRAY_SIZE(seg));
}
/**
* Write a byte with one segment in one frame.
*
* [start][slave addr][w][register addr][data][stop]
*/
static inline int i2c_writeb(unsigned bus, uint8_t chip, uint8_t reg,
uint8_t data)
{
struct i2c_seg seg;
uint8_t buf[] = {reg, data};
seg.read = 0;
seg.chip = chip;
seg.buf = buf;
seg.len = ARRAY_SIZE(buf);
return i2c_transfer(bus, &seg, 1);
}
#endif /* _DEVICE_I2C_H_ */ #endif /* _DEVICE_I2C_H_ */

View File

@ -34,6 +34,7 @@
#include <soc/samsung/exynos5250/i2c.h> #include <soc/samsung/exynos5250/i2c.h>
#include <soc/samsung/exynos5250/dp-core.h> #include <soc/samsung/exynos5250/dp-core.h>
#include <soc/samsung/exynos5250/dp.h> #include <soc/samsung/exynos5250/dp.h>
#include <soc/samsung/exynos5250/periph.h>
#include <soc/samsung/exynos5250/usb.h> #include <soc/samsung/exynos5250/usb.h>
#include "exynos5250.h" #include "exynos5250.h"

View File

@ -59,7 +59,7 @@ static struct as3722_init_reg init_list[] = {
static void pmic_write_reg(unsigned bus, uint8_t reg, uint8_t val, int do_delay) static void pmic_write_reg(unsigned bus, uint8_t reg, uint8_t val, int do_delay)
{ {
i2c_write(bus, AS3722_I2C_ADDR, reg, 1, &val, 1); i2c_writeb(bus, AS3722_I2C_ADDR, reg, val);
if (do_delay) if (do_delay)
udelay(500); udelay(500);
} }

View File

@ -59,7 +59,7 @@ static struct as3722_init_reg init_list[] = {
static void pmic_write_reg(unsigned bus, uint8_t reg, uint8_t val, int do_delay) static void pmic_write_reg(unsigned bus, uint8_t reg, uint8_t val, int do_delay)
{ {
i2c_write(bus, AS3722_I2C_ADDR, reg, 1, &val, 1); i2c_writeb(bus, AS3722_I2C_ADDR, reg, val);
if (do_delay) if (do_delay)
udelay(500); udelay(500);
} }

View File

@ -59,7 +59,7 @@ static struct as3722_init_reg init_list[] = {
static void pmic_write_reg(unsigned bus, uint8_t reg, uint8_t val, int do_delay) static void pmic_write_reg(unsigned bus, uint8_t reg, uint8_t val, int do_delay)
{ {
i2c_write(bus, AS3722_I2C_ADDR, reg, 1, &val, 1); i2c_writeb(bus, AS3722_I2C_ADDR, reg, val);
if (do_delay) if (do_delay)
udelay(500); udelay(500);
} }

View File

@ -32,6 +32,7 @@
#include <soc/samsung/exynos5420/cpu.h> #include <soc/samsung/exynos5420/cpu.h>
#include <soc/samsung/exynos5420/gpio.h> #include <soc/samsung/exynos5420/gpio.h>
#include <soc/samsung/exynos5420/power.h> #include <soc/samsung/exynos5420/power.h>
#include <soc/samsung/exynos5420/periph.h>
#include <soc/samsung/exynos5420/i2c.h> #include <soc/samsung/exynos5420/i2c.h>
#include <soc/samsung/exynos5420/dp.h> #include <soc/samsung/exynos5420/dp.h>
#include <soc/samsung/exynos5420/fimd.h> #include <soc/samsung/exynos5420/fimd.h>

View File

@ -97,13 +97,10 @@ static int setup_power(int is_resume)
uint8_t reg = pmic_writes[i].reg; uint8_t reg = pmic_writes[i].reg;
if (pmic_writes[i].or_orig) if (pmic_writes[i].or_orig)
error |= i2c_read(4, MAX77802_I2C_ADDR, error |= i2c_readb(4, MAX77802_I2C_ADDR, reg, &data);
reg, sizeof(reg),
&data, sizeof(data));
data |= pmic_writes[i].val; data |= pmic_writes[i].val;
error |= i2c_write(4, MAX77802_I2C_ADDR, error |= i2c_writeb(4, MAX77802_I2C_ADDR, reg, data);
reg, sizeof(reg),
&data, sizeof(data));
} }
return error; return error;

View File

@ -130,43 +130,35 @@ static int tegra_i2c_request(int bus, unsigned chip, int cont, int restart,
data, data_len); data, data_len);
} }
static int i2c_readwrite(unsigned bus, unsigned chip, unsigned addr, static int i2c_transfer_segment(unsigned bus, unsigned chip, int restart,
unsigned alen, uint8_t *buf, unsigned len, int read) int read, void *buf, int len)
{ {
const uint32_t max_payload = const uint32_t max_payload =
(IOHEADER_PAYLOADSIZE_MASK + 1) >> IOHEADER_PAYLOADSIZE_SHIFT; (IOHEADER_PAYLOADSIZE_MASK + 1) >> IOHEADER_PAYLOADSIZE_SHIFT;
uint8_t abuf[sizeof(addr)];
int i;
for (i = 0; i < alen; i++)
abuf[i] = addr >> ((alen - i - 1) * 8);
if (tegra_i2c_request(bus, chip, !read, 0, 0, abuf, alen))
return -1;
while (len) { while (len) {
int todo = MIN(len, max_payload); int todo = MIN(len, max_payload);
int cont = (todo < len); int cont = (todo < len);
if (tegra_i2c_request(bus, chip, cont, 0, read, buf, todo)) { if (tegra_i2c_request(bus, chip, cont, restart,
// We should reset the controller here. read, buf, todo))
return -1; return -1;
}
len -= todo; len -= todo;
buf += todo; buf += todo;
} }
return 0; return 0;
} }
int i2c_read(unsigned bus, unsigned chip, unsigned addr, int i2c_transfer(unsigned bus, struct i2c_seg *segments, int count)
unsigned alen, uint8_t *buf, unsigned len)
{ {
return i2c_readwrite(bus, chip, addr, alen, buf, len, 1); struct i2c_seg *seg = segments;
}
int i2c_write(unsigned bus, unsigned chip, unsigned addr, int i;
unsigned alen, const uint8_t *buf, unsigned len) for (i = 0; i < count; seg++, i++) {
{ if (i2c_transfer_segment(bus, seg->chip, i < count - 1,
return i2c_readwrite(bus, chip, addr, alen, (void *)buf, len, 0); seg->read, seg->buf, seg->len))
return -1;
}
return 0;
} }
void i2c_init(unsigned bus) void i2c_init(unsigned bus)

View File

@ -18,119 +18,263 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include <assert.h>
#include <console/console.h> #include <console/console.h>
#include <delay.h> #include <delay.h>
#include <arch/io.h> #include <arch/io.h>
#include <device/i2c.h> #include <device/i2c.h>
#include "clk.h" #include "clk.h"
#include "i2c.h" #include "i2c.h"
#include "periph.h"
#define I2C_WRITE 0 struct __attribute__ ((packed)) i2c_regs
#define I2C_READ 1 {
uint8_t con;
#define I2C_OK 0 uint8_t _1[3];
#define I2C_NOK 1 uint8_t stat;
#define I2C_NACK 2 uint8_t _2[3];
#define I2C_NOK_LA 3 /* Lost arbitration */ uint8_t add;
#define I2C_NOK_TOUT 4 /* time out */ uint8_t _3[3];
uint8_t ds;
#define I2CSTAT_BSY 0x20 /* Busy bit */ uint8_t _4[3];
#define I2CSTAT_NACK 0x01 /* Nack bit */ uint8_t lc;
#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ uint8_t _5[3];
#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[] = { struct s3c24x0_i2c_bus {
int bus_num;
struct i2c_regs *regs;
enum periph_id periph_id;
};
enum {
I2cConIntPending = 0x1 << 4,
I2cConIntEn = 0x1 << 5,
I2cConAckGen = 0x1 << 7
};
enum {
I2cStatAck = 0x1 << 0,
I2cStatAddrZero = 0x1 << 1,
I2cStatAddrSlave = 0x1 << 2,
I2cStatArb = 0x1 << 3,
I2cStatEnable = 0x1 << 4,
I2cStatStartStop = 0x1 << 5,
I2cStatBusy = 0x1 << 5,
I2cStatModeMask = 0x3 << 6,
I2cStatSlaveRecv = 0x0 << 6,
I2cStatSlaveXmit = 0x1 << 6,
I2cStatMasterRecv = 0x2 << 6,
I2cStatMasterXmit = 0x3 << 6
};
static struct s3c24x0_i2c_bus i2c_busses[] = {
{ {
.bus_num = 0, .bus_num = 0,
.regs = (struct s3c24x0_i2c *)0x12c60000, .regs = (void *)0x12c60000,
.periph_id = PERIPH_ID_I2C0, .periph_id = PERIPH_ID_I2C0,
}, },
{ {
.bus_num = 1, .bus_num = 1,
.regs = (struct s3c24x0_i2c *)0x12c70000, .regs = (void *)0x12c70000,
.periph_id = PERIPH_ID_I2C1, .periph_id = PERIPH_ID_I2C1,
}, },
{ {
.bus_num = 2, .bus_num = 2,
.regs = (struct s3c24x0_i2c *)0x12c80000, .regs = (void *)0x12c80000,
.periph_id = PERIPH_ID_I2C2, .periph_id = PERIPH_ID_I2C2,
}, },
{ {
.bus_num = 3, .bus_num = 3,
.regs = (struct s3c24x0_i2c *)0x12c90000, .regs = (void *)0x12c90000,
.periph_id = PERIPH_ID_I2C3, .periph_id = PERIPH_ID_I2C3,
}, },
{ {
.bus_num = 4, .bus_num = 4,
.regs = (struct s3c24x0_i2c *)0x12ca0000, .regs = (void *)0x12ca0000,
.periph_id = PERIPH_ID_I2C4, .periph_id = PERIPH_ID_I2C4,
}, },
{ {
.bus_num = 5, .bus_num = 5,
.regs = (struct s3c24x0_i2c *)0x12cb0000, .regs = (void *)0x12cb0000,
.periph_id = PERIPH_ID_I2C5, .periph_id = PERIPH_ID_I2C5,
}, },
{ {
.bus_num = 6, .bus_num = 6,
.regs = (struct s3c24x0_i2c *)0x12cc0000, .regs = (void *)0x12cc0000,
.periph_id = PERIPH_ID_I2C6, .periph_id = PERIPH_ID_I2C6,
}, },
{ {
.bus_num = 7, .bus_num = 7,
.regs = (struct s3c24x0_i2c *)0x12cd0000, .regs = (void *)0x12cd0000,
.periph_id = PERIPH_ID_I2C7, .periph_id = PERIPH_ID_I2C7,
}, },
}; };
static int WaitForXfer(struct s3c24x0_i2c *i2c)
static int i2c_int_pending(struct i2c_regs *regs)
{ {
return readb(&regs->con) & I2cConIntPending;
}
static void i2c_clear_int(struct i2c_regs *regs)
{
writeb(readb(&regs->con) & ~I2cConIntPending, &regs->con);
}
static void i2c_ack_enable(struct i2c_regs *regs)
{
writeb(readb(&regs->con) | I2cConAckGen, &regs->con);
}
static void i2c_ack_disable(struct i2c_regs *regs)
{
writeb(readb(&regs->con) & ~I2cConAckGen, &regs->con);
}
static int i2c_got_ack(struct i2c_regs *regs)
{
return !(readb(&regs->stat) & I2cStatAck);
}
static int i2c_wait_for_idle(struct i2c_regs *regs)
{
int timeout = 1000 * 100; // 1s.
while (timeout--) {
if (!(readb(&regs->stat) & I2cStatBusy))
return 0;
udelay(10);
}
printk(BIOS_ERR, "I2C timeout waiting for idle.\n");
return 1;
}
static int i2c_wait_for_int(struct i2c_regs *regs)
{
int timeout = 1000 * 100; // 1s.
while (timeout--) {
if (i2c_int_pending(regs))
return 0;
udelay(10);
}
printk(BIOS_ERR, "I2C timeout waiting for I2C interrupt.\n");
return 1;
}
static int i2c_send_stop(struct i2c_regs *regs)
{
uint8_t mode = readb(&regs->stat) & (I2cStatModeMask);
writeb(mode | I2cStatEnable, &regs->stat);
i2c_clear_int(regs);
return i2c_wait_for_idle(regs);
}
static int i2c_send_start(struct i2c_regs *regs, int read, int chip)
{
writeb(chip << 1, &regs->ds);
uint8_t mode = read ? I2cStatMasterRecv : I2cStatMasterXmit;
writeb(mode | I2cStatStartStop | I2cStatEnable, &regs->stat);
i2c_clear_int(regs);
if (i2c_wait_for_int(regs))
return 1;
if (!i2c_got_ack(regs)) {
// Nobody home, but they may just be asleep.
return 1;
}
return 0;
}
static int i2c_xmit_buf(struct i2c_regs *regs, uint8_t *data, int len)
{
ASSERT(len);
i2c_ack_enable(regs);
int i; int i;
for (i = 0; i < len; i++) {
writeb(data[i], &regs->ds);
i = I2C_XFER_TIMEOUT_MS * 20; i2c_clear_int(regs);
while (!(readl(&i2c->iiccon) & I2CCON_IRPND)) { if (i2c_wait_for_int(regs))
if (i == 0) { return 1;
printk(BIOS_ERR, "%s: i2c xfer timeout\n", __func__);
return I2C_NOK_TOUT; if (!i2c_got_ack(regs)) {
printk(BIOS_INFO, "I2c nacked.\n");
return 1;
} }
udelay(50);
i--;
} }
return I2C_OK; return 0;
} }
static int IsACK(struct s3c24x0_i2c *i2c) static int i2c_recv_buf(struct i2c_regs *regs, uint8_t *data, int len)
{ {
return !(readl(&i2c->iicstat) & I2CSTAT_NACK); ASSERT(len);
i2c_ack_enable(regs);
int i;
for (i = 0; i < len; i++) {
if (i == len - 1)
i2c_ack_disable(regs);
i2c_clear_int(regs);
if (i2c_wait_for_int(regs))
return 1;
data[i] = readb(&regs->ds);
} }
static void ReadWriteByte(struct s3c24x0_i2c *i2c) return 0;
{
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) int i2c_transfer(unsigned bus, struct i2c_seg *segments, int seg_count)
{ {
struct s3c24x0_i2c_bus *i2c = &i2c_busses[bus];
struct i2c_regs *regs = i2c->regs;
int res = 0;
if (!regs || i2c_wait_for_idle(regs))
return 1;
writeb(I2cStatMasterXmit | I2cStatEnable, &regs->stat);
int i;
for (i = 0; i < seg_count; i++) {
struct i2c_seg *seg = &segments[i];
res = i2c_send_start(regs, seg->read, seg->chip);
if (res)
break;
if (seg->read)
res = i2c_recv_buf(regs, seg->buf, seg->len);
else
res = i2c_xmit_buf(regs, seg->buf, seg->len);
if (res)
break;
}
return i2c_send_stop(regs) || res;
}
void i2c_init(unsigned bus, int speed, int slaveadd)
{
struct s3c24x0_i2c_bus *i2c = &i2c_busses[bus];
unsigned long freq, pres = 16, div; unsigned long freq, pres = 16, div;
unsigned long val; unsigned long val;
freq = clock_get_periph_rate(bus->periph_id); freq = clock_get_periph_rate(i2c->periph_id);
/* calculate prescaler and divisor values */ // Calculate prescaler and divisor values.
if ((freq / pres / (16 + 1)) > speed) if ((freq / pres / (16 + 1)) > speed)
/* set prescaler to 512 */ /* set prescaler to 512 */
pres = 512; pres = 512;
@ -140,264 +284,13 @@ static void i2c_ch_init(struct s3c24x0_i2c_bus *bus, int speed, int slaveadd)
while ((freq / pres / (div + 1)) > speed) while ((freq / pres / (div + 1)) > speed)
div++; div++;
/* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ // Set prescaler, divisor according to freq, also set ACKGEN, IRQ.
val = (div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0); val = (div & 0x0f) | 0xa0 | ((pres == 512) ? 0x40 : 0);
writel(val, &bus->regs->iiccon); writel(val, &i2c->regs->con);
/* init to SLAVE RECEIVE mode and clear I2CADDn */ // Init to SLAVE RECEIVE mode and clear I2CADDn.
writel(0, &bus->regs->iicstat); writel(0, &i2c->regs->stat);
writel(slaveadd, &bus->regs->iicadd); writel(slaveadd, &i2c->regs->add);
/* program Master Transmit (and implicit STOP) */ // program Master Transmit (and implicit STOP).
writel(I2C_MODE_MT | I2C_TXRX_ENA, &bus->regs->iicstat); writel(I2cStatMasterXmit | I2cStatEnable, &i2c->regs->stat);
}
/*
* 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;
} }

View File

@ -20,22 +20,6 @@
#ifndef CPU_SAMSUNG_EXYNOS5250_I2C_H #ifndef CPU_SAMSUNG_EXYNOS5250_I2C_H
#define CPU_SAMSUNG_EXYNOS5250_I2C_H #define CPU_SAMSUNG_EXYNOS5250_I2C_H
#include "periph.h"
struct s3c24x0_i2c {
u32 iiccon;
u32 iicstat;
u32 iicadd;
u32 iicds;
u32 iiclc;
};
struct s3c24x0_i2c_bus {
int bus_num;
struct s3c24x0_i2c *regs;
enum periph_id periph_id;
};
void i2c_init(unsigned bus, int speed, int slaveadd); void i2c_init(unsigned bus, int speed, int slaveadd);
#endif /* CPU_SAMSUNG_EXYNOS5250_I2C_H */ #endif /* CPU_SAMSUNG_EXYNOS5250_I2C_H */

File diff suppressed because it is too large Load Diff

View File

@ -20,60 +20,6 @@
#ifndef CPU_SAMSUNG_EXYNOS5420_I2C_H #ifndef CPU_SAMSUNG_EXYNOS5420_I2C_H
#define CPU_SAMSUNG_EXYNOS5420_I2C_H #define CPU_SAMSUNG_EXYNOS5420_I2C_H
#include "periph.h"
struct s3c24x0_i2c {
u32 iiccon;
u32 iicstat;
u32 iicadd;
u32 iicds;
u32 iiclc;
} __attribute__ ((packed));
struct exynos5_hsi2c {
u32 usi_ctl;
u32 usi_fifo_ctl;
u32 usi_trailing_ctl;
u32 usi_clk_ctl;
u32 usi_clk_slot;
u32 spi_ctl;
u32 uart_ctl;
u32 res1;
u32 usi_int_en;
u32 usi_int_stat;
u32 usi_modem_stat;
u32 usi_error_stat;
u32 usi_fifo_stat;
u32 usi_txdata;
u32 usi_rxdata;
u32 res2;
u32 usi_conf;
u32 usi_auto_conf;
u32 usi_timeout;
u32 usi_manual_cmd;
u32 usi_trans_status;
u32 usi_timing_hs1;
u32 usi_timing_hs2;
u32 usi_timing_hs3;
u32 usi_timing_fs1;
u32 usi_timing_fs2;
u32 usi_timing_fs3;
u32 usi_timing_sla;
u32 i2c_addr;
} __attribute__ ((packed));
check_member(exynos5_hsi2c, i2c_addr, 0x70);
struct s3c24x0_i2c_bus {
int bus_num;
struct s3c24x0_i2c *regs;
enum periph_id periph_id;
struct exynos5_hsi2c *hsregs;
int is_highspeed; /* High speed type, rather than I2C */
int id;
unsigned clk_cycle;
unsigned clk_div;
};
void i2c_init(unsigned bus, int speed, int slaveadd); void i2c_init(unsigned bus, int speed, int slaveadd);
#endif /* CPU_SAMSUNG_EXYNOS5420_I2C_H */ #endif /* CPU_SAMSUNG_EXYNOS5420_I2C_H */