mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-05 02:51:00 +00:00
da1ed0d20e
Signed-off-by: Hisashi Nakamura <hisashi.nakamura.ak@renesas.com> Signed-off-by: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
290 lines
6.8 KiB
C
290 lines
6.8 KiB
C
/*
|
|
* drivers/i2c/rcar_i2c.c
|
|
*
|
|
* Copyright (C) 2013 Renesas Electronics Corporation
|
|
* Copyright (C) 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <i2c.h>
|
|
#include <asm/io.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
struct rcar_i2c {
|
|
u32 icscr;
|
|
u32 icmcr;
|
|
u32 icssr;
|
|
u32 icmsr;
|
|
u32 icsier;
|
|
u32 icmier;
|
|
u32 icccr;
|
|
u32 icsar;
|
|
u32 icmar;
|
|
u32 icrxdtxd;
|
|
u32 icccr2;
|
|
u32 icmpr;
|
|
u32 ichpr;
|
|
u32 iclpr;
|
|
};
|
|
|
|
#define MCR_MDBS 0x80 /* non-fifo mode switch */
|
|
#define MCR_FSCL 0x40 /* override SCL pin */
|
|
#define MCR_FSDA 0x20 /* override SDA pin */
|
|
#define MCR_OBPC 0x10 /* override pins */
|
|
#define MCR_MIE 0x08 /* master if enable */
|
|
#define MCR_TSBE 0x04
|
|
#define MCR_FSB 0x02 /* force stop bit */
|
|
#define MCR_ESG 0x01 /* en startbit gen. */
|
|
|
|
#define MSR_MASK 0x7f
|
|
#define MSR_MNR 0x40 /* nack received */
|
|
#define MSR_MAL 0x20 /* arbitration lost */
|
|
#define MSR_MST 0x10 /* sent a stop */
|
|
#define MSR_MDE 0x08
|
|
#define MSR_MDT 0x04
|
|
#define MSR_MDR 0x02
|
|
#define MSR_MAT 0x01 /* slave addr xfer done */
|
|
|
|
static const struct rcar_i2c *i2c_dev[CONFIF_SYS_RCAR_I2C_NUM_CONTROLLERS] = {
|
|
(struct rcar_i2c *)CONFIG_SYS_RCAR_I2C0_BASE,
|
|
(struct rcar_i2c *)CONFIG_SYS_RCAR_I2C1_BASE,
|
|
(struct rcar_i2c *)CONFIG_SYS_RCAR_I2C2_BASE,
|
|
(struct rcar_i2c *)CONFIG_SYS_RCAR_I2C3_BASE,
|
|
};
|
|
|
|
static void rcar_i2c_raw_rw_common(struct rcar_i2c *dev, u8 chip, uint addr)
|
|
{
|
|
/* set slave address */
|
|
writel(chip << 1, &dev->icmar);
|
|
/* set register address */
|
|
writel(addr, &dev->icrxdtxd);
|
|
/* clear status */
|
|
writel(0, &dev->icmsr);
|
|
/* start master send */
|
|
writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr);
|
|
|
|
while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDE))
|
|
!= (MSR_MAT | MSR_MDE))
|
|
udelay(10);
|
|
|
|
/* clear ESG */
|
|
writel(MCR_MDBS | MCR_MIE, &dev->icmcr);
|
|
/* start SCLclk */
|
|
writel(~(MSR_MAT | MSR_MDE), &dev->icmsr);
|
|
|
|
while (!(readl(&dev->icmsr) & MSR_MDE))
|
|
udelay(10);
|
|
}
|
|
|
|
static void rcar_i2c_raw_rw_finish(struct rcar_i2c *dev)
|
|
{
|
|
while (!(readl(&dev->icmsr) & MSR_MST))
|
|
udelay(10);
|
|
|
|
writel(0, &dev->icmcr);
|
|
}
|
|
|
|
static int
|
|
rcar_i2c_raw_write(struct rcar_i2c *dev, u8 chip, uint addr, u8 *val, int size)
|
|
{
|
|
rcar_i2c_raw_rw_common(dev, chip, addr);
|
|
|
|
/* set send date */
|
|
writel(*val, &dev->icrxdtxd);
|
|
/* start SCLclk */
|
|
writel(~MSR_MDE, &dev->icmsr);
|
|
|
|
while (!(readl(&dev->icmsr) & MSR_MDE))
|
|
udelay(10);
|
|
|
|
/* set stop condition */
|
|
writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr);
|
|
/* start SCLclk */
|
|
writel(~MSR_MDE, &dev->icmsr);
|
|
|
|
rcar_i2c_raw_rw_finish(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u8
|
|
rcar_i2c_raw_read(struct rcar_i2c *dev, u8 chip, uint addr)
|
|
{
|
|
u8 ret;
|
|
|
|
rcar_i2c_raw_rw_common(dev, chip, addr);
|
|
|
|
/* set slave address, receive */
|
|
writel((chip << 1) | 1, &dev->icmar);
|
|
/* clear status */
|
|
writel(0, &dev->icmsr);
|
|
/* start master receive */
|
|
writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr);
|
|
|
|
while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDR))
|
|
!= (MSR_MAT | MSR_MDR))
|
|
udelay(10);
|
|
|
|
/* clear ESG */
|
|
writel(MCR_MDBS | MCR_MIE, &dev->icmcr);
|
|
/* prepare stop condition */
|
|
writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr);
|
|
/* start SCLclk */
|
|
writel(~(MSR_MAT | MSR_MDR), &dev->icmsr);
|
|
|
|
while (!(readl(&dev->icmsr) & MSR_MDR))
|
|
udelay(10);
|
|
|
|
/* get receive data */
|
|
ret = (u8)readl(&dev->icrxdtxd);
|
|
/* start SCLclk */
|
|
writel(~MSR_MDR, &dev->icmsr);
|
|
|
|
rcar_i2c_raw_rw_finish(dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* SCL = iicck / (20 + SCGD * 8 + F[(ticf + tr + intd) * iicck])
|
|
* iicck : I2C internal clock < 20 MHz
|
|
* ticf : I2C SCL falling time: 35 ns
|
|
* tr : I2C SCL rising time: 200 ns
|
|
* intd : LSI internal delay: I2C0: 50 ns I2C1-3: 5
|
|
* F[n] : n rounded up to an integer
|
|
*/
|
|
static u32 rcar_clock_gen(int i2c_no, u32 bus_speed)
|
|
{
|
|
u32 iicck, f, scl, scgd;
|
|
u32 intd = 5;
|
|
|
|
int bit = 0, cdf_width = 3;
|
|
for (bit = 0; bit < (1 << cdf_width); bit++) {
|
|
iicck = CONFIG_HP_CLK_FREQ / (1 + bit);
|
|
if (iicck < 20000000)
|
|
break;
|
|
}
|
|
|
|
if (bit > (1 << cdf_width)) {
|
|
puts("rcar-i2c: Can not get CDF\n");
|
|
return 0;
|
|
}
|
|
|
|
if (i2c_no == 0)
|
|
intd = 50;
|
|
|
|
f = (35 + 200 + intd) * (iicck / 1000000000);
|
|
|
|
for (scgd = 0; scgd < 0x40; scgd++) {
|
|
scl = iicck / (20 + (scgd * 8) + f);
|
|
if (scl <= bus_speed)
|
|
break;
|
|
}
|
|
|
|
if (scgd > 0x40) {
|
|
puts("rcar-i2c: Can not get SDGB\n");
|
|
return 0;
|
|
}
|
|
|
|
debug("%s: scl: %d\n", __func__, scl);
|
|
debug("%s: bit %x\n", __func__, bit);
|
|
debug("%s: scgd %x\n", __func__, scgd);
|
|
debug("%s: iccr %x\n", __func__, (scgd << (cdf_width) | bit));
|
|
|
|
return scgd << (cdf_width) | bit;
|
|
}
|
|
|
|
static void
|
|
rcar_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
|
|
{
|
|
struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
|
|
u32 icccr = 0;
|
|
|
|
/* No i2c support prior to relocation */
|
|
if (!(gd->flags & GD_FLG_RELOC))
|
|
return;
|
|
|
|
/*
|
|
* reset slave mode.
|
|
* slave mode is not used on this driver
|
|
*/
|
|
writel(0, &dev->icsier);
|
|
writel(0, &dev->icsar);
|
|
writel(0, &dev->icscr);
|
|
writel(0, &dev->icssr);
|
|
|
|
/* reset master mode */
|
|
writel(0, &dev->icmier);
|
|
writel(0, &dev->icmcr);
|
|
writel(0, &dev->icmsr);
|
|
writel(0, &dev->icmar);
|
|
|
|
icccr = rcar_clock_gen(adap->hwadapnr, adap->speed);
|
|
if (icccr == 0)
|
|
puts("I2C: Init failed\n");
|
|
else
|
|
writel(icccr, &dev->icccr);
|
|
}
|
|
|
|
static int rcar_i2c_read(struct i2c_adapter *adap, uint8_t chip,
|
|
uint addr, int alen, u8 *data, int len)
|
|
{
|
|
struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
data[i] = rcar_i2c_raw_read(dev, chip, addr + i);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rcar_i2c_write(struct i2c_adapter *adap, uint8_t chip, uint addr,
|
|
int alen, u8 *data, int len)
|
|
{
|
|
struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
|
|
return rcar_i2c_raw_write(dev, chip, addr, data, len);
|
|
}
|
|
|
|
static int
|
|
rcar_i2c_probe(struct i2c_adapter *adap, u8 dev)
|
|
{
|
|
return rcar_i2c_read(adap, dev, 0, 0, NULL, 0);
|
|
}
|
|
|
|
static unsigned int rcar_i2c_set_bus_speed(struct i2c_adapter *adap,
|
|
unsigned int speed)
|
|
{
|
|
struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr];
|
|
u32 icccr;
|
|
int ret = 0;
|
|
|
|
rcar_i2c_raw_rw_finish(dev);
|
|
|
|
icccr = rcar_clock_gen(adap->hwadapnr, speed);
|
|
if (icccr == 0) {
|
|
puts("I2C: Init failed\n");
|
|
ret = -1;
|
|
} else {
|
|
writel(icccr, &dev->icccr);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Register RCAR i2c adapters
|
|
*/
|
|
U_BOOT_I2C_ADAP_COMPLETE(rcar_0, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
|
|
rcar_i2c_write, rcar_i2c_set_bus_speed,
|
|
CONFIG_SYS_RCAR_I2C0_SPEED, 0, 0)
|
|
U_BOOT_I2C_ADAP_COMPLETE(rcar_1, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
|
|
rcar_i2c_write, rcar_i2c_set_bus_speed,
|
|
CONFIG_SYS_RCAR_I2C1_SPEED, 0, 1)
|
|
U_BOOT_I2C_ADAP_COMPLETE(rcar_2, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
|
|
rcar_i2c_write, rcar_i2c_set_bus_speed,
|
|
CONFIG_SYS_RCAR_I2C2_SPEED, 0, 2)
|
|
U_BOOT_I2C_ADAP_COMPLETE(rcar_3, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read,
|
|
rcar_i2c_write, rcar_i2c_set_bus_speed,
|
|
CONFIG_SYS_RCAR_I2C3_SPEED, 0, 3)
|