commit
9dff87a297
@ -0,0 +1,288 @@ |
||||
/*
|
||||
* 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); |
||||
/* start master receive */ |
||||
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); |
||||
/* 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) |
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue