mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 02:15:45 +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,
 | |
| };
 |