mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-23 18:38:51 +09:00
![Tom Rini](/assets/img/avatar_default.png)
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
377 lines
7.4 KiB
C
377 lines
7.4 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright (C) 2012 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
|
|
* Copyright (C) 2012 Renesas Solutions Corp.
|
|
*
|
|
* NOTE: This driver should be converted to driver model before June 2017.
|
|
* Please see doc/driver-model/i2c-howto.txt for instructions.
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <i2c.h>
|
|
#include <asm/io.h>
|
|
|
|
struct sh_i2c {
|
|
u8 iccr1;
|
|
u8 iccr2;
|
|
u8 icmr;
|
|
u8 icier;
|
|
u8 icsr;
|
|
u8 sar;
|
|
u8 icdrt;
|
|
u8 icdrr;
|
|
u8 nf2cyc;
|
|
u8 __pad0;
|
|
u8 __pad1;
|
|
};
|
|
|
|
static struct sh_i2c *base;
|
|
static u8 iccr1_cks, nf2cyc;
|
|
|
|
/* ICCR1 */
|
|
#define SH_I2C_ICCR1_ICE (1 << 7)
|
|
#define SH_I2C_ICCR1_RCVD (1 << 6)
|
|
#define SH_I2C_ICCR1_MST (1 << 5)
|
|
#define SH_I2C_ICCR1_TRS (1 << 4)
|
|
#define SH_I2C_ICCR1_MTRS \
|
|
(SH_I2C_ICCR1_MST | SH_I2C_ICCR1_TRS)
|
|
|
|
/* ICCR1 */
|
|
#define SH_I2C_ICCR2_BBSY (1 << 7)
|
|
#define SH_I2C_ICCR2_SCP (1 << 6)
|
|
#define SH_I2C_ICCR2_SDAO (1 << 5)
|
|
#define SH_I2C_ICCR2_SDAOP (1 << 4)
|
|
#define SH_I2C_ICCR2_SCLO (1 << 3)
|
|
#define SH_I2C_ICCR2_IICRST (1 << 1)
|
|
|
|
#define SH_I2C_ICIER_TIE (1 << 7)
|
|
#define SH_I2C_ICIER_TEIE (1 << 6)
|
|
#define SH_I2C_ICIER_RIE (1 << 5)
|
|
#define SH_I2C_ICIER_NAKIE (1 << 4)
|
|
#define SH_I2C_ICIER_STIE (1 << 3)
|
|
#define SH_I2C_ICIER_ACKE (1 << 2)
|
|
#define SH_I2C_ICIER_ACKBR (1 << 1)
|
|
#define SH_I2C_ICIER_ACKBT (1 << 0)
|
|
|
|
#define SH_I2C_ICSR_TDRE (1 << 7)
|
|
#define SH_I2C_ICSR_TEND (1 << 6)
|
|
#define SH_I2C_ICSR_RDRF (1 << 5)
|
|
#define SH_I2C_ICSR_NACKF (1 << 4)
|
|
#define SH_I2C_ICSR_STOP (1 << 3)
|
|
#define SH_I2C_ICSR_ALOVE (1 << 2)
|
|
#define SH_I2C_ICSR_AAS (1 << 1)
|
|
#define SH_I2C_ICSR_ADZ (1 << 0)
|
|
|
|
#define IRQ_WAIT 1000
|
|
|
|
static void sh_i2c_send_stop(struct sh_i2c *base)
|
|
{
|
|
clrbits_8(&base->iccr2, SH_I2C_ICCR2_BBSY | SH_I2C_ICCR2_SCP);
|
|
}
|
|
|
|
static int check_icsr_bits(struct sh_i2c *base, u8 bits)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < IRQ_WAIT; i++) {
|
|
if (bits & readb(&base->icsr))
|
|
return 0;
|
|
udelay(10);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int check_stop(struct sh_i2c *base)
|
|
{
|
|
int ret = check_icsr_bits(base, SH_I2C_ICSR_STOP);
|
|
clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int check_tend(struct sh_i2c *base, int stop)
|
|
{
|
|
int ret = check_icsr_bits(base, SH_I2C_ICSR_TEND);
|
|
|
|
if (stop) {
|
|
clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
|
|
sh_i2c_send_stop(base);
|
|
}
|
|
|
|
clrbits_8(&base->icsr, SH_I2C_ICSR_TEND);
|
|
return ret;
|
|
}
|
|
|
|
static int check_tdre(struct sh_i2c *base)
|
|
{
|
|
return check_icsr_bits(base, SH_I2C_ICSR_TDRE);
|
|
}
|
|
|
|
static int check_rdrf(struct sh_i2c *base)
|
|
{
|
|
return check_icsr_bits(base, SH_I2C_ICSR_RDRF);
|
|
}
|
|
|
|
static int check_bbsy(struct sh_i2c *base)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0 ; i < IRQ_WAIT ; i++) {
|
|
if (!(SH_I2C_ICCR2_BBSY & readb(&base->iccr2)))
|
|
return 0;
|
|
udelay(10);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int check_ackbr(struct sh_i2c *base)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0 ; i < IRQ_WAIT ; i++) {
|
|
if (!(SH_I2C_ICIER_ACKBR & readb(&base->icier)))
|
|
return 0;
|
|
udelay(10);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void sh_i2c_reset(struct sh_i2c *base)
|
|
{
|
|
setbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST);
|
|
|
|
udelay(100);
|
|
|
|
clrbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST);
|
|
}
|
|
|
|
static int i2c_set_addr(struct sh_i2c *base, u8 id, u8 reg)
|
|
{
|
|
if (check_bbsy(base)) {
|
|
puts("i2c bus busy\n");
|
|
goto fail;
|
|
}
|
|
|
|
setbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
|
|
clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY);
|
|
|
|
writeb((id << 1), &base->icdrt);
|
|
|
|
if (check_tend(base, 0)) {
|
|
puts("TEND check fail...\n");
|
|
goto fail;
|
|
}
|
|
|
|
if (check_ackbr(base)) {
|
|
check_tend(base, 0);
|
|
sh_i2c_send_stop(base);
|
|
goto fail;
|
|
}
|
|
|
|
writeb(reg, &base->icdrt);
|
|
|
|
if (check_tdre(base)) {
|
|
puts("TDRE check fail...\n");
|
|
goto fail;
|
|
}
|
|
|
|
if (check_tend(base, 0)) {
|
|
puts("TEND check fail...\n");
|
|
goto fail;
|
|
}
|
|
|
|
return 0;
|
|
fail:
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
i2c_raw_write(struct sh_i2c *base, u8 id, u8 reg, u8 *val, int size)
|
|
{
|
|
int i;
|
|
|
|
if (i2c_set_addr(base, id, reg)) {
|
|
puts("Fail set slave address\n");
|
|
return 1;
|
|
}
|
|
|
|
for (i = 0; i < size; i++) {
|
|
writeb(val[i], &base->icdrt);
|
|
check_tdre(base);
|
|
}
|
|
|
|
check_tend(base, 1);
|
|
check_stop(base);
|
|
|
|
udelay(100);
|
|
|
|
clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
|
|
clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
|
|
sh_i2c_reset(base);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u8 i2c_raw_read(struct sh_i2c *base, u8 id, u8 reg)
|
|
{
|
|
u8 ret = 0;
|
|
|
|
if (i2c_set_addr(base, id, reg)) {
|
|
puts("Fail set slave address\n");
|
|
goto fail;
|
|
}
|
|
|
|
clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY);
|
|
writeb((id << 1) | 1, &base->icdrt);
|
|
|
|
if (check_tend(base, 0))
|
|
puts("TDRE check fail...\n");
|
|
|
|
clrsetbits_8(&base->iccr1, SH_I2C_ICCR1_TRS, SH_I2C_ICCR1_MST);
|
|
clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
|
|
setbits_8(&base->icier, SH_I2C_ICIER_ACKBT);
|
|
setbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD);
|
|
|
|
/* read data (dummy) */
|
|
ret = readb(&base->icdrr);
|
|
|
|
if (check_rdrf(base)) {
|
|
puts("check RDRF error\n");
|
|
goto fail;
|
|
}
|
|
|
|
clrbits_8(&base->icsr, SH_I2C_ICSR_STOP);
|
|
udelay(1000);
|
|
|
|
sh_i2c_send_stop(base);
|
|
|
|
if (check_stop(base)) {
|
|
puts("check STOP error\n");
|
|
goto fail;
|
|
}
|
|
|
|
clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS);
|
|
clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE);
|
|
|
|
/* data read */
|
|
ret = readb(&base->icdrr);
|
|
|
|
fail:
|
|
clrbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_I2C_MULTI_BUS
|
|
static unsigned int current_bus;
|
|
|
|
/**
|
|
* i2c_set_bus_num - change active I2C bus
|
|
* @bus: bus index, zero based
|
|
* @returns: 0 on success, non-0 on failure
|
|
*/
|
|
int i2c_set_bus_num(unsigned int bus)
|
|
{
|
|
switch (bus) {
|
|
case 0:
|
|
base = (void *)CONFIG_SH_I2C_BASE0;
|
|
break;
|
|
case 1:
|
|
base = (void *)CONFIG_SH_I2C_BASE1;
|
|
break;
|
|
default:
|
|
printf("Bad bus: %d\n", bus);
|
|
return -1;
|
|
}
|
|
|
|
current_bus = bus;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* i2c_get_bus_num - returns index of active I2C bus
|
|
*/
|
|
unsigned int i2c_get_bus_num(void)
|
|
{
|
|
return current_bus;
|
|
}
|
|
#endif
|
|
|
|
void i2c_init(int speed, int slaveaddr)
|
|
{
|
|
#ifdef CONFIG_I2C_MULTI_BUS
|
|
current_bus = 0;
|
|
#endif
|
|
base = (struct sh_i2c *)CONFIG_SH_I2C_BASE0;
|
|
|
|
if (speed == 400000)
|
|
iccr1_cks = 0x07;
|
|
else
|
|
iccr1_cks = 0x0F;
|
|
|
|
nf2cyc = 1;
|
|
|
|
/* Reset */
|
|
sh_i2c_reset(base);
|
|
|
|
/* ICE enable and set clock */
|
|
writeb(SH_I2C_ICCR1_ICE | iccr1_cks, &base->iccr1);
|
|
writeb(nf2cyc, &base->nf2cyc);
|
|
}
|
|
|
|
/*
|
|
* i2c_read: - Read multiple bytes from an i2c device
|
|
*
|
|
* The higher level routines take into account that this function is only
|
|
* called with len < page length of the device (see configuration file)
|
|
*
|
|
* @chip: address of the chip which is to be read
|
|
* @addr: i2c data address within the chip
|
|
* @alen: length of the i2c data address (1..2 bytes)
|
|
* @buffer: where to write the data
|
|
* @len: how much byte do we want to read
|
|
* @return: 0 in case of success
|
|
*/
|
|
int i2c_read(u8 chip, u32 addr, int alen, u8 *buffer, int len)
|
|
{
|
|
int i = 0;
|
|
for (i = 0; i < len; i++)
|
|
buffer[i] = i2c_raw_read(base, chip, addr + i);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* i2c_write: - Write multiple bytes to an i2c device
|
|
*
|
|
* The higher level routines take into account that this function is only
|
|
* called with len < page length of the device (see configuration file)
|
|
*
|
|
* @chip: address of the chip which is to be written
|
|
* @addr: i2c data address within the chip
|
|
* @alen: length of the i2c data address (1..2 bytes)
|
|
* @buffer: where to find the data to be written
|
|
* @len: how much byte do we want to read
|
|
* @return: 0 in case of success
|
|
*/
|
|
int i2c_write(u8 chip, u32 addr, int alen, u8 *buffer, int len)
|
|
{
|
|
return i2c_raw_write(base, chip, addr, buffer, len);
|
|
}
|
|
|
|
/*
|
|
* i2c_probe: - Test if a chip answers for a given i2c address
|
|
*
|
|
* @chip: address of the chip which is searched for
|
|
* @return: 0 if a chip was found, -1 otherwhise
|
|
*/
|
|
int i2c_probe(u8 chip)
|
|
{
|
|
u8 byte;
|
|
return i2c_read(chip, 0, 0, &byte, 1);
|
|
}
|