i2c: Add Microchip PolarFire SoC I2C driver
Add I2C driver code for the Microchip PolarFire SoC. This driver supports I2C data transfer and probe for I2C slave addresses. Signed-off-by: Padmarao Begari <padmarao.begari@microchip.com> Reviewed-by: Leo Yu-Chi Liang <ycliang@andestech.com>
This commit is contained in:
		
							parent
							
								
									0d914ad10d
								
							
						
					
					
						commit
						0dc0d1e094
					
				|  | @ -250,6 +250,12 @@ config SYS_I2C_MESON | ||||||
| 	  internal buffer holding up to 8 bytes for transfers and supports | 	  internal buffer holding up to 8 bytes for transfers and supports | ||||||
| 	  both 7-bit and 10-bit addresses. | 	  both 7-bit and 10-bit addresses. | ||||||
| 
 | 
 | ||||||
|  | config SYS_I2C_MICROCHIP | ||||||
|  | 	bool "Microchip I2C driver" | ||||||
|  | 	help | ||||||
|  | 	  Add support for the Microchip I2C driver. This is operating on | ||||||
|  | 	  standard mode up to 100 kbits/s and fast mode up to 400 kbits/s. | ||||||
|  | 
 | ||||||
| config SYS_I2C_MXC | config SYS_I2C_MXC | ||||||
| 	bool "NXP MXC I2C driver" | 	bool "NXP MXC I2C driver" | ||||||
| 	help | 	help | ||||||
|  |  | ||||||
|  | @ -28,6 +28,7 @@ obj-$(CONFIG_SYS_I2C_IPROC) += iproc_i2c.o | ||||||
| obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o | obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o | ||||||
| obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o | obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o | ||||||
| obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o | obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o | ||||||
|  | obj-$(CONFIG_SYS_I2C_MICROCHIP) += i2c-microchip.o | ||||||
| obj-$(CONFIG_SYS_I2C_MV) += mv_i2c.o | obj-$(CONFIG_SYS_I2C_MV) += mv_i2c.o | ||||||
| obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o | obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o | ||||||
| obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o | obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o | ||||||
|  |  | ||||||
|  | @ -0,0 +1,482 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0+
 | ||||||
|  | /*
 | ||||||
|  |  * Microchip I2C controller driver | ||||||
|  |  * | ||||||
|  |  * Copyright (C) 2021 Microchip Technology Inc. | ||||||
|  |  * Padmarao Begari <padmarao.begari@microchip.com> | ||||||
|  |  */ | ||||||
|  | #include <common.h> | ||||||
|  | #include <clk.h> | ||||||
|  | #include <dm.h> | ||||||
|  | #include <i2c.h> | ||||||
|  | #include <asm/io.h> | ||||||
|  | #include <dm/device_compat.h> | ||||||
|  | #include <linux/bitops.h> | ||||||
|  | #include <linux/delay.h> | ||||||
|  | #include <linux/err.h> | ||||||
|  | 
 | ||||||
|  | #define	MICROCHIP_I2C_TIMEOUT	(1000 * 60) | ||||||
|  | 
 | ||||||
|  | #define MPFS_I2C_CTRL	(0x00) | ||||||
|  | #define	CTRL_CR0		(0x00) | ||||||
|  | #define	CTRL_CR1		(0x01) | ||||||
|  | #define	CTRL_AA			BIT(2) | ||||||
|  | #define	CTRL_SI			BIT(3) | ||||||
|  | #define	CTRL_STO		BIT(4) | ||||||
|  | #define	CTRL_STA		BIT(5) | ||||||
|  | #define	CTRL_ENS1		BIT(6) | ||||||
|  | #define	CTRL_CR2		(0x07) | ||||||
|  | #define MPFS_I2C_STATUS							(0x04) | ||||||
|  | #define	STATUS_BUS_ERROR						(0x00) | ||||||
|  | #define	STATUS_M_START_SENT						(0x08) | ||||||
|  | #define	STATUS_M_REPEATED_START_SENT			(0x10) | ||||||
|  | #define	STATUS_M_SLAW_ACK						(0x18) | ||||||
|  | #define	STATUS_M_SLAW_NACK						(0x20) | ||||||
|  | #define	STATUS_M_TX_DATA_ACK					(0x28) | ||||||
|  | #define	STATUS_M_TX_DATA_NACK					(0x30) | ||||||
|  | #define	STATUS_M_ARB_LOST						(0x38) | ||||||
|  | #define	STATUS_M_SLAR_ACK						(0x40) | ||||||
|  | #define	STATUS_M_SLAR_NACK						(0x48) | ||||||
|  | #define	STATUS_M_RX_DATA_ACKED					(0x50) | ||||||
|  | #define	STATUS_M_RX_DATA_NACKED					(0x58) | ||||||
|  | #define	STATUS_S_SLAW_ACKED						(0x60) | ||||||
|  | #define	STATUS_S_ARB_LOST_SLAW_ACKED			(0x68) | ||||||
|  | #define	STATUS_S_GENERAL_CALL_ACKED				(0x70) | ||||||
|  | #define	STATUS_S_ARB_LOST_GENERAL_CALL_ACKED	(0x78) | ||||||
|  | #define	STATUS_S_RX_DATA_ACKED					(0x80) | ||||||
|  | #define	STATUS_S_RX_DATA_NACKED					(0x88) | ||||||
|  | #define	STATUS_S_GENERAL_CALL_RX_DATA_ACKED		(0x90) | ||||||
|  | #define	STATUS_S_GENERAL_CALL_RX_DATA_NACKED	(0x98) | ||||||
|  | #define	STATUS_S_RX_STOP						(0xA0) | ||||||
|  | #define	STATUS_S_SLAR_ACKED						(0xA8) | ||||||
|  | #define	STATUS_S_ARB_LOST_SLAR_ACKED			(0xB0) | ||||||
|  | #define	STATUS_S_TX_DATA_ACK					(0xb8) | ||||||
|  | #define	STATUS_S_TX_DATA_NACK					(0xC0) | ||||||
|  | #define	STATUS_LAST_DATA_ACK					(0xC8) | ||||||
|  | #define	STATUS_M_SMB_MASTER_RESET				(0xD0) | ||||||
|  | #define	STATUS_S_SCL_LOW_TIMEOUT				(0xD8) | ||||||
|  | #define	STATUS_NO_STATE_INFO					(0xF8) | ||||||
|  | #define MPFS_I2C_DATA			(0x08) | ||||||
|  | #define MPFS_I2C_SLAVE0_ADDR	(0x0c) | ||||||
|  | #define MPFS_I2C_SMBUS			(0x10) | ||||||
|  | #define MPFS_I2C_FREQ			(0x14) | ||||||
|  | #define MPFS_I2C_GLITCHREG		(0x18) | ||||||
|  | #define MPFS_I2C_SLAVE1_ADDR	(0x1c) | ||||||
|  | 
 | ||||||
|  | #define PCLK_DIV_256	((0 << CTRL_CR0) | (0 << CTRL_CR1) | (0 << CTRL_CR2)) | ||||||
|  | #define PCLK_DIV_224	((1 << CTRL_CR0) | (0 << CTRL_CR1) | (0 << CTRL_CR2)) | ||||||
|  | #define PCLK_DIV_192	((0 << CTRL_CR0) | (1 << CTRL_CR1) | (0 << CTRL_CR2)) | ||||||
|  | #define PCLK_DIV_160	((1 << CTRL_CR0) | (1 << CTRL_CR1) | (0 << CTRL_CR2)) | ||||||
|  | #define PCLK_DIV_960	((0 << CTRL_CR0) | (0 << CTRL_CR1) | (1 << CTRL_CR2)) | ||||||
|  | #define PCLK_DIV_120	((1 << CTRL_CR0) | (0 << CTRL_CR1) | (1 << CTRL_CR2)) | ||||||
|  | #define PCLK_DIV_60		((0 << CTRL_CR0) | (1 << CTRL_CR1) | (1 << CTRL_CR2)) | ||||||
|  | #define BCLK_DIV_8		((1 << CTRL_CR0) | (1 << CTRL_CR1) | (1 << CTRL_CR2)) | ||||||
|  | #define CLK_MASK		((1 << CTRL_CR0) | (1 << CTRL_CR1) | (1 << CTRL_CR2)) | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * mpfs_i2c_bus - I2C bus context | ||||||
|  |  * @base: pointer to register struct | ||||||
|  |  * @msg_len: number of bytes transferred in msg | ||||||
|  |  * @msg_err: error code for completed message | ||||||
|  |  * @i2c_clk: clock reference for i2c input clock | ||||||
|  |  * @clk_rate: current i2c bus clock rate | ||||||
|  |  * @buf: ptr to msg buffer for easier use. | ||||||
|  |  * @addr: i2c address. | ||||||
|  |  * @isr_status: cached copy of local ISR status. | ||||||
|  |  */ | ||||||
|  | struct mpfs_i2c_bus { | ||||||
|  | 	void __iomem *base; | ||||||
|  | 	size_t msg_len; | ||||||
|  | 	int msg_err; | ||||||
|  | 	struct clk i2c_clk; | ||||||
|  | 	u32 clk_rate; | ||||||
|  | 	u8 *buf; | ||||||
|  | 	u8 addr; | ||||||
|  | 	u32 isr_status; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg) | ||||||
|  | { | ||||||
|  | 	return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mpfs_i2c_int_clear(struct mpfs_i2c_bus *bus) | ||||||
|  | { | ||||||
|  | 	u8 ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	ctrl &= ~CTRL_SI; | ||||||
|  | 	writel(ctrl, bus->base + MPFS_I2C_CTRL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mpfs_i2c_core_disable(struct mpfs_i2c_bus *bus) | ||||||
|  | { | ||||||
|  | 	u8 ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	ctrl &= ~CTRL_ENS1; | ||||||
|  | 	writel(ctrl, bus->base + MPFS_I2C_CTRL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mpfs_i2c_core_enable(struct mpfs_i2c_bus *bus) | ||||||
|  | { | ||||||
|  | 	u8 ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	ctrl |= CTRL_ENS1; | ||||||
|  | 	writel(ctrl, bus->base + MPFS_I2C_CTRL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mpfs_i2c_reset(struct mpfs_i2c_bus *bus) | ||||||
|  | { | ||||||
|  | 	mpfs_i2c_core_disable(bus); | ||||||
|  | 	mpfs_i2c_core_enable(bus); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline void mpfs_i2c_stop(struct mpfs_i2c_bus *bus) | ||||||
|  | { | ||||||
|  | 	u8 ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	ctrl |= CTRL_STO; | ||||||
|  | 	writel(ctrl, bus->base + MPFS_I2C_CTRL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline int mpfs_generate_divisor(u32 rate, u8 *code) | ||||||
|  | { | ||||||
|  | 	int ret = 0; | ||||||
|  | 
 | ||||||
|  | 	if (rate >= 960) | ||||||
|  | 		*code = PCLK_DIV_960; | ||||||
|  | 	else if (rate >= 256) | ||||||
|  | 		*code = PCLK_DIV_256; | ||||||
|  | 	else if (rate >= 224) | ||||||
|  | 		*code = PCLK_DIV_224; | ||||||
|  | 	else if (rate >= 192) | ||||||
|  | 		*code = PCLK_DIV_192; | ||||||
|  | 	else if (rate >= 160) | ||||||
|  | 		*code = PCLK_DIV_160; | ||||||
|  | 	else if (rate >= 120) | ||||||
|  | 		*code = PCLK_DIV_120; | ||||||
|  | 	else if (rate >= 60) | ||||||
|  | 		*code = PCLK_DIV_60; | ||||||
|  | 	else if (rate >= 8) | ||||||
|  | 		*code = BCLK_DIV_8; | ||||||
|  | 	else | ||||||
|  | 		ret = -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mpfs_i2c_init(struct mpfs_i2c_bus *bus, struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	u32 clk_rate, divisor; | ||||||
|  | 	u8 clkval, ctrl; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = clk_get_by_index(dev, 0, &bus->i2c_clk); | ||||||
|  | 	if (ret) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	ret = clk_enable(&bus->i2c_clk); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	clk_rate = clk_get_rate(&bus->i2c_clk); | ||||||
|  | 	if (!clk_rate) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	clk_free(&bus->i2c_clk); | ||||||
|  | 
 | ||||||
|  | 	divisor = clk_rate / bus->clk_rate; | ||||||
|  | 
 | ||||||
|  | 	ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	ctrl &= ~CLK_MASK; | ||||||
|  | 
 | ||||||
|  | 	ret = mpfs_generate_divisor(divisor, &clkval); | ||||||
|  | 	if (ret) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	ctrl |= clkval; | ||||||
|  | 
 | ||||||
|  | 	writel(ctrl, bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	/* Reset I2C core */ | ||||||
|  | 	mpfs_i2c_reset(bus); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mpfs_i2c_transfer(struct mpfs_i2c_bus *bus, u32 data) | ||||||
|  | { | ||||||
|  | 	if (bus->msg_len > 0) | ||||||
|  | 		writel(data, bus->base + MPFS_I2C_DATA); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mpfs_i2c_empty_rx(struct mpfs_i2c_bus *bus) | ||||||
|  | { | ||||||
|  | 	u8 ctrl; | ||||||
|  | 	u8 data_read; | ||||||
|  | 
 | ||||||
|  | 	if (bus->msg_len > 0) { | ||||||
|  | 		data_read = readl(bus->base + MPFS_I2C_DATA); | ||||||
|  | 		*bus->buf++ = data_read; | ||||||
|  | 		bus->msg_len--; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (bus->msg_len == 0) { | ||||||
|  | 		ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 		ctrl &= ~CTRL_AA; | ||||||
|  | 		writel(ctrl, bus->base + MPFS_I2C_CTRL); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mpfs_i2c_fill_tx(struct mpfs_i2c_bus *bus) | ||||||
|  | { | ||||||
|  | 	mpfs_i2c_transfer(bus, *bus->buf++); | ||||||
|  | 	bus->msg_len--; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mpfs_i2c_service_handler(struct mpfs_i2c_bus *bus) | ||||||
|  | { | ||||||
|  | 	bool finish = false; | ||||||
|  | 	u32 status; | ||||||
|  | 	u8 ctrl; | ||||||
|  | 
 | ||||||
|  | 	status = bus->isr_status; | ||||||
|  | 
 | ||||||
|  | 	switch (status)	{ | ||||||
|  | 	case STATUS_M_START_SENT: | ||||||
|  | 	case STATUS_M_REPEATED_START_SENT: | ||||||
|  | 		ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 		ctrl &= ~CTRL_STA; | ||||||
|  | 		writel(bus->addr, bus->base + MPFS_I2C_DATA); | ||||||
|  | 		writel(ctrl, bus->base + MPFS_I2C_CTRL); | ||||||
|  | 		break; | ||||||
|  | 	case STATUS_M_SLAW_ACK: | ||||||
|  | 	case STATUS_M_TX_DATA_ACK: | ||||||
|  | 		if (bus->msg_len > 0) { | ||||||
|  | 			mpfs_i2c_fill_tx(bus); | ||||||
|  | 		} else { | ||||||
|  | 			/* On the last byte to be transmitted, send STOP */ | ||||||
|  | 			mpfs_i2c_stop(bus); | ||||||
|  | 			finish = true; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	case STATUS_M_SLAR_ACK: | ||||||
|  | 		ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 		ctrl |= CTRL_AA; | ||||||
|  | 		writel(ctrl, bus->base + MPFS_I2C_CTRL); | ||||||
|  | 		if (bus->msg_len == 0) { | ||||||
|  | 			/* On the last byte to be transmitted, send STOP */ | ||||||
|  | 			mpfs_i2c_stop(bus); | ||||||
|  | 			finish = true; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	case STATUS_M_RX_DATA_ACKED: | ||||||
|  | 		mpfs_i2c_empty_rx(bus); | ||||||
|  | 		if (bus->msg_len == 0) { | ||||||
|  | 			/* On the last byte to be transmitted, send STOP */ | ||||||
|  | 			mpfs_i2c_stop(bus); | ||||||
|  | 			finish = true; | ||||||
|  | 		} | ||||||
|  | 		break; | ||||||
|  | 	case STATUS_M_TX_DATA_NACK: | ||||||
|  | 	case STATUS_M_RX_DATA_NACKED: | ||||||
|  | 	case STATUS_M_SLAR_NACK: | ||||||
|  | 	case STATUS_M_SLAW_NACK: | ||||||
|  | 		bus->msg_err = -ENXIO; | ||||||
|  | 		mpfs_i2c_stop(bus); | ||||||
|  | 		finish = true; | ||||||
|  | 		break; | ||||||
|  | 
 | ||||||
|  | 	case STATUS_M_ARB_LOST: | ||||||
|  | 		/* Handle Lost Arbitration */ | ||||||
|  | 		bus->msg_err = -EAGAIN; | ||||||
|  | 		finish = true; | ||||||
|  | 		break; | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (finish) { | ||||||
|  | 		ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 		ctrl &= ~CTRL_AA; | ||||||
|  | 		writel(ctrl, bus->base + MPFS_I2C_CTRL); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mpfs_i2c_service(struct mpfs_i2c_bus *bus) | ||||||
|  | { | ||||||
|  | 	int ret = 0; | ||||||
|  | 	int si_bit; | ||||||
|  | 
 | ||||||
|  | 	si_bit = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 	if (si_bit & CTRL_SI) { | ||||||
|  | 		bus->isr_status = readl(bus->base + MPFS_I2C_STATUS); | ||||||
|  | 		ret = mpfs_i2c_service_handler(bus); | ||||||
|  | 	} | ||||||
|  | 	/* Clear the si flag */ | ||||||
|  | 	mpfs_i2c_int_clear(bus); | ||||||
|  | 	si_bit = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mpfs_i2c_check_service_change(struct mpfs_i2c_bus *bus) | ||||||
|  | { | ||||||
|  | 	u8 ctrl; | ||||||
|  | 	u32 count = 0; | ||||||
|  | 
 | ||||||
|  | 	while (1) { | ||||||
|  | 		ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 		if (ctrl & CTRL_SI) | ||||||
|  | 			break; | ||||||
|  | 		udelay(1); | ||||||
|  | 		count += 1; | ||||||
|  | 		if (count == MICROCHIP_I2C_TIMEOUT) | ||||||
|  | 			return -ETIMEDOUT; | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mpfs_i2c_poll_device(struct mpfs_i2c_bus *bus) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	while (1) { | ||||||
|  | 		ret = mpfs_i2c_check_service_change(bus); | ||||||
|  | 		if (ret) | ||||||
|  | 			return ret; | ||||||
|  | 
 | ||||||
|  | 		ret = mpfs_i2c_service(bus); | ||||||
|  | 		if (!ret) | ||||||
|  | 			/* all messages have been transferred */ | ||||||
|  | 			return ret; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mpfs_i2c_xfer_msg(struct mpfs_i2c_bus *bus, struct i2c_msg *msg) | ||||||
|  | { | ||||||
|  | 	u8 ctrl; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	if (!msg->len || !msg->buf) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	bus->addr = i2c_8bit_addr_from_msg(msg); | ||||||
|  | 	bus->msg_len = msg->len; | ||||||
|  | 	bus->buf = msg->buf; | ||||||
|  | 	bus->msg_err = 0; | ||||||
|  | 
 | ||||||
|  | 	mpfs_i2c_core_enable(bus); | ||||||
|  | 
 | ||||||
|  | 	ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	ctrl |= CTRL_STA; | ||||||
|  | 
 | ||||||
|  | 	writel(ctrl, bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	ret = mpfs_i2c_poll_device(bus); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	return bus->msg_err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mpfs_i2c_xfer(struct udevice *dev, struct i2c_msg *msgs, int num_msgs) | ||||||
|  | { | ||||||
|  | 	struct mpfs_i2c_bus *bus = dev_get_priv(dev); | ||||||
|  | 	int idx, ret; | ||||||
|  | 
 | ||||||
|  | 	if (!msgs || !num_msgs) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	for (idx = 0; idx < num_msgs; idx++) { | ||||||
|  | 		ret = mpfs_i2c_xfer_msg(bus, msgs++); | ||||||
|  | 		if (ret) | ||||||
|  | 			return ret; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mpfs_i2c_probe_chip(struct udevice *dev, uint addr, uint flags) | ||||||
|  | { | ||||||
|  | 	struct mpfs_i2c_bus *bus = dev_get_priv(dev); | ||||||
|  | 	int ret; | ||||||
|  | 	u8 ctrl, reg = 0; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Send the chip address and verify that the | ||||||
|  | 	 * address was <ACK>ed. | ||||||
|  | 	 */ | ||||||
|  | 	bus->addr = addr << 1 | I2C_M_RD; | ||||||
|  | 	bus->buf = ® | ||||||
|  | 	bus->msg_len = 0; | ||||||
|  | 	bus->msg_err = 0; | ||||||
|  | 
 | ||||||
|  | 	mpfs_i2c_core_enable(bus); | ||||||
|  | 
 | ||||||
|  | 	ctrl = readl(bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	ctrl |= CTRL_STA; | ||||||
|  | 
 | ||||||
|  | 	writel(ctrl, bus->base + MPFS_I2C_CTRL); | ||||||
|  | 
 | ||||||
|  | 	ret = mpfs_i2c_poll_device(bus); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	return bus->msg_err; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int mpfs_i2c_probe(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 	u32 val; | ||||||
|  | 	struct mpfs_i2c_bus *bus = dev_get_priv(dev); | ||||||
|  | 
 | ||||||
|  | 	bus->base = dev_read_addr_ptr(dev); | ||||||
|  | 	if (!bus->base) | ||||||
|  | 		return -EINVAL; | ||||||
|  | 
 | ||||||
|  | 	val = dev_read_u32(dev, "clock-frequency", &bus->clk_rate); | ||||||
|  | 	if (val) { | ||||||
|  | 		printf("Default to 100kHz\n"); | ||||||
|  | 		/* default clock rate */ | ||||||
|  | 		bus->clk_rate = 100000; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (bus->clk_rate > 400000 || bus->clk_rate <= 0) { | ||||||
|  | 		printf("Invalid clock-frequency %d\n", bus->clk_rate); | ||||||
|  | 		return -EINVAL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ret = mpfs_i2c_init(bus, dev); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct dm_i2c_ops mpfs_i2c_ops = { | ||||||
|  | 	.xfer = mpfs_i2c_xfer, | ||||||
|  | 	.probe_chip = mpfs_i2c_probe_chip, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const struct udevice_id mpfs_i2c_ids[] = { | ||||||
|  | 	{.compatible = "microchip,mpfs-i2c"}, | ||||||
|  | 	{} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | U_BOOT_DRIVER(mpfs_i2c) = { | ||||||
|  | 	.name = "mpfs_i2c", | ||||||
|  | 	.id = UCLASS_I2C, | ||||||
|  | 	.of_match = mpfs_i2c_ids, | ||||||
|  | 	.ops = &mpfs_i2c_ops, | ||||||
|  | 	.probe = mpfs_i2c_probe, | ||||||
|  | 	.priv_auto = sizeof(struct mpfs_i2c_bus), | ||||||
|  | }; | ||||||
		Loading…
	
		Reference in New Issue