mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-11-04 10:21:25 +01:00 
			
		
		
		
	Add driver for class of I2C controllers found on Socionext Synquacer platform. Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
		
			
				
	
	
		
			339 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			339 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,
 | 
						|
};
 |