mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-10 05:13:31 +00:00
4483fbab81
Add driver for class of I2C controllers found on Socionext Synquacer platform. Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
338 lines
8.5 KiB
C
338 lines
8.5 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
*/
|
|
|
|
#include <dm/device_compat.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/io.h>
|
|
#include <linux/sizes.h>
|
|
#include <linux/types.h>
|
|
#include <dm.h>
|
|
#include <fdtdec.h>
|
|
#include <i2c.h>
|
|
#include <clk.h>
|
|
|
|
#define REG_BSR 0x0
|
|
#define REG_BCR 0x4
|
|
#define REG_CCR 0x8
|
|
#define REG_ADR 0xc
|
|
#define REG_DAR 0x10
|
|
#define REG_CSR 0x14
|
|
#define REG_FSR 0x18
|
|
#define REG_BC2R 0x1c
|
|
|
|
/* I2C register bit definitions */
|
|
#define BSR_FBT BIT(0) // First Byte Transfer
|
|
#define BSR_GCA BIT(1) // General Call Address
|
|
#define BSR_AAS BIT(2) // Address as Slave
|
|
#define BSR_TRX BIT(3) // Transfer/Receive
|
|
#define BSR_LRB BIT(4) // Last Received Bit
|
|
#define BSR_AL BIT(5) // Arbitration Lost
|
|
#define BSR_RSC BIT(6) // Repeated Start Cond.
|
|
#define BSR_BB BIT(7) // Bus Busy
|
|
|
|
#define BCR_INT BIT(0) // Interrupt
|
|
#define BCR_INTE BIT(1) // Interrupt Enable
|
|
#define BCR_GCAA BIT(2) // Gen. Call Access Ack.
|
|
#define BCR_ACK BIT(3) // Acknowledge
|
|
#define BCR_MSS BIT(4) // Master Slave Select
|
|
#define BCR_SCC BIT(5) // Start Condition Cont.
|
|
#define BCR_BEIE BIT(6) // Bus Error Int Enable
|
|
#define BCR_BER BIT(7) // Bus Error
|
|
|
|
#define CCR_CS_MASK (0x1f) // CCR Clock Period Sel.
|
|
#define CCR_EN BIT(5) // Enable
|
|
#define CCR_FM BIT(6) // Speed Mode Select
|
|
|
|
#define CSR_CS_MASK (0x3f) // CSR Clock Period Sel.
|
|
|
|
#define BC2R_SCLL BIT(0) // SCL Low Drive
|
|
#define BC2R_SDAL BIT(1) // SDA Low Drive
|
|
#define BC2R_SCLS BIT(4) // SCL Status
|
|
#define BC2R_SDAS BIT(5) // SDA Status
|
|
|
|
/* PCLK frequency */
|
|
#define BUS_CLK_FR(rate) (((rate) / 20000000) + 1)
|
|
|
|
#define I2C_CLK_DEF 62500000
|
|
|
|
/* STANDARD MODE frequency */
|
|
#define CLK_MASTER_STD(rate) \
|
|
DIV_ROUND_UP(DIV_ROUND_UP((rate), I2C_SPEED_STANDARD_RATE) - 2, 2)
|
|
/* FAST MODE frequency */
|
|
#define CLK_MASTER_FAST(rate) \
|
|
DIV_ROUND_UP((DIV_ROUND_UP((rate), I2C_SPEED_FAST_RATE) - 2) * 2, 3)
|
|
|
|
/* (clkrate <= 18000000) */
|
|
/* calculate the value of CS bits in CCR register on standard mode */
|
|
#define CCR_CS_STD_MAX_18M(rate) \
|
|
((CLK_MASTER_STD(rate) - 65) \
|
|
& CCR_CS_MASK)
|
|
|
|
/* calculate the value of CS bits in CSR register on standard mode */
|
|
#define CSR_CS_STD_MAX_18M(rate) 0x00
|
|
|
|
/* calculate the value of CS bits in CCR register on fast mode */
|
|
#define CCR_CS_FAST_MAX_18M(rate) \
|
|
((CLK_MASTER_FAST(rate) - 1) \
|
|
& CCR_CS_MASK)
|
|
|
|
/* calculate the value of CS bits in CSR register on fast mode */
|
|
#define CSR_CS_FAST_MAX_18M(rate) 0x00
|
|
|
|
/* (clkrate > 18000000) */
|
|
/* calculate the value of CS bits in CCR register on standard mode */
|
|
#define CCR_CS_STD_MIN_18M(rate) \
|
|
((CLK_MASTER_STD(rate) - 1) \
|
|
& CCR_CS_MASK)
|
|
|
|
/* calculate the value of CS bits in CSR register on standard mode */
|
|
#define CSR_CS_STD_MIN_18M(rate) \
|
|
(((CLK_MASTER_STD(rate) - 1) >> 5) \
|
|
& CSR_CS_MASK)
|
|
|
|
/* calculate the value of CS bits in CCR register on fast mode */
|
|
#define CCR_CS_FAST_MIN_18M(rate) \
|
|
((CLK_MASTER_FAST(rate) - 1) \
|
|
& CCR_CS_MASK)
|
|
|
|
/* calculate the value of CS bits in CSR register on fast mode */
|
|
#define CSR_CS_FAST_MIN_18M(rate) \
|
|
(((CLK_MASTER_FAST(rate) - 1) >> 5) \
|
|
& CSR_CS_MASK)
|
|
|
|
/* min I2C clock frequency 14M */
|
|
#define MIN_CLK_RATE (14 * 1000000)
|
|
/* max I2C clock frequency 200M */
|
|
#define MAX_CLK_RATE (200 * 1000000)
|
|
/* I2C clock frequency 18M */
|
|
#define CLK_RATE_18M (18 * 1000000)
|
|
|
|
#define SPEED_FM 400 // Fast Mode
|
|
#define SPEED_SM 100 // Standard Mode
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
struct synquacer_i2c {
|
|
void __iomem *base;
|
|
unsigned long pclkrate;
|
|
unsigned long speed_khz;
|
|
};
|
|
|
|
static int wait_irq(struct udevice *dev)
|
|
{
|
|
struct synquacer_i2c *i2c = dev_get_priv(dev);
|
|
int timeout = 500000;
|
|
|
|
do {
|
|
if (readb(i2c->base + REG_BCR) & BCR_INT)
|
|
return 0;
|
|
} while (timeout--);
|
|
|
|
pr_err("%s: timeout\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
static int synquacer_i2c_xfer_start(struct synquacer_i2c *i2c,
|
|
int addr, int read)
|
|
{
|
|
u8 bsr, bcr;
|
|
|
|
writeb((addr << 1) | (read ? 1 : 0), i2c->base + REG_DAR);
|
|
|
|
bsr = readb(i2c->base + REG_BSR);
|
|
bcr = readb(i2c->base + REG_BCR);
|
|
|
|
if ((bsr & BSR_BB) && !(bcr & BCR_MSS))
|
|
return -EBUSY;
|
|
|
|
if (bsr & BSR_BB) {
|
|
writeb(bcr | BCR_SCC, i2c->base + REG_BCR);
|
|
} else {
|
|
if (bcr & BCR_MSS)
|
|
return -EAGAIN;
|
|
/* Start Condition + Enable Interrupts */
|
|
writeb(bcr | BCR_MSS | BCR_INTE | BCR_BEIE, i2c->base + REG_BCR);
|
|
}
|
|
|
|
udelay(100);
|
|
return 0;
|
|
}
|
|
|
|
static int synquacer_i2c_xfer(struct udevice *bus,
|
|
struct i2c_msg *msg, int nmsgs)
|
|
{
|
|
struct synquacer_i2c *i2c = dev_get_priv(bus);
|
|
u8 bsr, bcr;
|
|
int idx;
|
|
|
|
for (; nmsgs > 0; nmsgs--, msg++) {
|
|
synquacer_i2c_xfer_start(i2c, msg->addr, msg->flags & I2C_M_RD);
|
|
if (wait_irq(bus))
|
|
return -EREMOTEIO;
|
|
|
|
bsr = readb(i2c->base + REG_BSR);
|
|
if (bsr & BSR_LRB) {
|
|
debug("%s: No ack received\n", __func__);
|
|
return -EREMOTEIO;
|
|
}
|
|
|
|
idx = 0;
|
|
do {
|
|
bsr = readb(i2c->base + REG_BSR);
|
|
bcr = readb(i2c->base + REG_BCR);
|
|
if (bcr & BCR_BER) {
|
|
debug("%s: Bus error detected\n", __func__);
|
|
return -EREMOTEIO;
|
|
}
|
|
if ((bsr & BSR_AL) || !(bcr & BCR_MSS)) {
|
|
debug("%s: Arbitration lost\n", __func__);
|
|
return -EREMOTEIO;
|
|
}
|
|
|
|
if (msg->flags & I2C_M_RD) {
|
|
bcr = BCR_MSS | BCR_INTE | BCR_BEIE;
|
|
if (idx < msg->len - 1)
|
|
bcr |= BCR_ACK;
|
|
writeb(bcr, i2c->base + REG_BCR);
|
|
if (wait_irq(bus))
|
|
return -EREMOTEIO;
|
|
bsr = readb(i2c->base + REG_BSR);
|
|
if (!(bsr & BSR_FBT))
|
|
msg->buf[idx++] = readb(i2c->base + REG_DAR);
|
|
} else {
|
|
writeb(msg->buf[idx++], i2c->base + REG_DAR);
|
|
bcr = BCR_MSS | BCR_INTE | BCR_BEIE;
|
|
writeb(bcr, i2c->base + REG_BCR);
|
|
if (wait_irq(bus))
|
|
return -EREMOTEIO;
|
|
bsr = readb(i2c->base + REG_BSR);
|
|
if (bsr & BSR_LRB) {
|
|
debug("%s: no ack\n", __func__);
|
|
return -EREMOTEIO;
|
|
}
|
|
}
|
|
} while (idx < msg->len);
|
|
}
|
|
|
|
/* Force bus state to idle, terminating any ongoing transfer */
|
|
writeb(0, i2c->base + REG_BCR);
|
|
udelay(100);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void synquacer_i2c_hw_reset(struct synquacer_i2c *i2c)
|
|
{
|
|
/* Disable clock */
|
|
writeb(0, i2c->base + REG_CCR);
|
|
writeb(0, i2c->base + REG_CSR);
|
|
|
|
/* Set own Address */
|
|
writeb(0, i2c->base + REG_ADR);
|
|
|
|
/* Set PCLK frequency */
|
|
writeb(BUS_CLK_FR(i2c->pclkrate), i2c->base + REG_FSR);
|
|
|
|
/* clear IRQ (INT=0, BER=0), Interrupt Disable */
|
|
writeb(0, i2c->base + REG_BCR);
|
|
writeb(0, i2c->base + REG_BC2R);
|
|
}
|
|
|
|
static int synquacer_i2c_get_bus_speed(struct udevice *bus)
|
|
{
|
|
struct synquacer_i2c *i2c = dev_get_priv(bus);
|
|
|
|
return i2c->speed_khz * 1000;
|
|
}
|
|
|
|
static int synquacer_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
|
|
{
|
|
struct synquacer_i2c *i2c = dev_get_priv(bus);
|
|
u32 rt = i2c->pclkrate;
|
|
u8 ccr_cs, csr_cs;
|
|
|
|
/* Set PCLK frequency */
|
|
writeb(BUS_CLK_FR(i2c->pclkrate), i2c->base + REG_FSR);
|
|
|
|
if (speed >= SPEED_FM * 1000) {
|
|
i2c->speed_khz = SPEED_FM;
|
|
if (i2c->pclkrate <= CLK_RATE_18M) {
|
|
ccr_cs = CCR_CS_FAST_MAX_18M(rt);
|
|
csr_cs = CSR_CS_FAST_MAX_18M(rt);
|
|
} else {
|
|
ccr_cs = CCR_CS_FAST_MIN_18M(rt);
|
|
csr_cs = CSR_CS_FAST_MIN_18M(rt);
|
|
}
|
|
|
|
/* Set Clock and enable, Set fast mode */
|
|
writeb(ccr_cs | CCR_FM | CCR_EN, i2c->base + REG_CCR);
|
|
writeb(csr_cs, i2c->base + REG_CSR);
|
|
} else {
|
|
i2c->speed_khz = SPEED_SM;
|
|
if (i2c->pclkrate <= CLK_RATE_18M) {
|
|
ccr_cs = CCR_CS_STD_MAX_18M(rt);
|
|
csr_cs = CSR_CS_STD_MAX_18M(rt);
|
|
} else {
|
|
ccr_cs = CCR_CS_STD_MIN_18M(rt);
|
|
csr_cs = CSR_CS_STD_MIN_18M(rt);
|
|
}
|
|
|
|
/* Set Clock and enable, Set standard mode */
|
|
writeb(ccr_cs | CCR_EN, i2c->base + REG_CCR);
|
|
writeb(csr_cs, i2c->base + REG_CSR);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int synquacer_i2c_of_to_plat(struct udevice *bus)
|
|
{
|
|
struct synquacer_i2c *priv = dev_get_priv(bus);
|
|
struct clk ck;
|
|
int ret;
|
|
|
|
ret = clk_get_by_index(bus, 0, &ck);
|
|
if (ret < 0) {
|
|
priv->pclkrate = I2C_CLK_DEF;
|
|
} else {
|
|
clk_enable(&ck);
|
|
priv->pclkrate = clk_get_rate(&ck);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int synquacer_i2c_probe(struct udevice *bus)
|
|
{
|
|
struct synquacer_i2c *i2c = dev_get_priv(bus);
|
|
|
|
i2c->base = dev_read_addr_ptr(bus);
|
|
synquacer_i2c_hw_reset(i2c);
|
|
synquacer_i2c_set_bus_speed(bus, 400000); /* set default speed */
|
|
return 0;
|
|
}
|
|
|
|
static const struct dm_i2c_ops synquacer_i2c_ops = {
|
|
.xfer = synquacer_i2c_xfer,
|
|
.set_bus_speed = synquacer_i2c_set_bus_speed,
|
|
.get_bus_speed = synquacer_i2c_get_bus_speed,
|
|
};
|
|
|
|
static const struct udevice_id synquacer_i2c_ids[] = {
|
|
{
|
|
.compatible = "socionext,synquacer-i2c",
|
|
},
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(sni_synquacer_i2c) = {
|
|
.name = "sni_synquacer_i2c",
|
|
.id = UCLASS_I2C,
|
|
.of_match = synquacer_i2c_ids,
|
|
.of_to_plat = synquacer_i2c_of_to_plat,
|
|
.probe = synquacer_i2c_probe,
|
|
.priv_auto = sizeof(struct synquacer_i2c),
|
|
.ops = &synquacer_i2c_ops,
|
|
};
|