628 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			628 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
#include <common.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <dm.h>
 | 
						|
#include <i2c.h>
 | 
						|
#include <log.h>
 | 
						|
#include <asm/arch/nexell.h>
 | 
						|
#include <asm/arch/reset.h>
 | 
						|
#include <asm/arch/clk.h>
 | 
						|
#include <asm/arch/nx_gpio.h>
 | 
						|
#include <asm/global_data.h>
 | 
						|
#include <linux/delay.h>
 | 
						|
 | 
						|
#define I2C_WRITE       0
 | 
						|
#define I2C_READ        1
 | 
						|
 | 
						|
#define I2CSTAT_MTM     0xC0    /* Master Transmit Mode */
 | 
						|
#define I2CSTAT_MRM     0x80    /* Master Receive Mode */
 | 
						|
#define I2CSTAT_BSY     0x20    /* Read: Bus Busy */
 | 
						|
#define I2CSTAT_SS      0x20    /* Write: START (1) / STOP (0) */
 | 
						|
#define I2CSTAT_RXTXEN  0x10    /* Rx/Tx enable */
 | 
						|
#define I2CSTAT_ABT	0x08	/* Arbitration bit */
 | 
						|
#define I2CSTAT_NACK    0x01    /* Nack bit */
 | 
						|
#define I2CCON_IRCLR    0x100   /* Interrupt Clear bit  */
 | 
						|
#define I2CCON_ACKGEN   0x80    /* Acknowledge generation */
 | 
						|
#define I2CCON_TCP256	0x40    /* Tx-clock prescaler: 16 (0) / 256 (1) */
 | 
						|
#define I2CCON_IRENB	0x20	/* Interrupt Enable bit  */
 | 
						|
#define I2CCON_IRPND    0x10    /* Interrupt pending bit */
 | 
						|
#define I2CCON_TCDMSK	0x0F    /* I2C-bus transmit clock divider bit mask */
 | 
						|
 | 
						|
#ifdef CONFIG_ARCH_S5P6818
 | 
						|
#define SDADLY_CLKSTEP	5	/* SDA delay: Reg. val. is multiple of 5 clks */
 | 
						|
#define SDADLY_MAX	3	/* SDA delay: Max. reg. value is 3 */
 | 
						|
#define I2CLC_FILTER	0x04	/* SDA filter on */
 | 
						|
#else
 | 
						|
#define STOPCON_CLR	0x01	/* Clock Line Release */
 | 
						|
#define STOPCON_DLR	0x02	/* Data Line Release */
 | 
						|
#define STOPCON_NAG	0x04	/* not-ackn. generation and data shift cont. */
 | 
						|
#endif
 | 
						|
 | 
						|
#define I2C_TIMEOUT_MS	10      /* 10 ms */
 | 
						|
 | 
						|
#define I2C_M_NOSTOP	0x100
 | 
						|
 | 
						|
#define MAX_I2C_NUM 3
 | 
						|
 | 
						|
#define DEFAULT_SPEED   100000  /* default I2C speed [Hz] */
 | 
						|
 | 
						|
DECLARE_GLOBAL_DATA_PTR;
 | 
						|
 | 
						|
struct nx_i2c_regs {
 | 
						|
	uint     iiccon;
 | 
						|
	uint     iicstat;
 | 
						|
	uint     iicadd;
 | 
						|
	uint     iicds;
 | 
						|
#ifdef CONFIG_ARCH_S5P6818
 | 
						|
	/* S5P6818: Offset 0x10 is Line Control Register (SDA-delay, Filter) */
 | 
						|
	uint     iiclc;
 | 
						|
#else
 | 
						|
	/* S5P4418: Offset 0x10 is Stop Control Register */
 | 
						|
	uint     iicstopcon;
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
struct nx_i2c_bus {
 | 
						|
	uint bus_num;
 | 
						|
	struct nx_i2c_regs *regs;
 | 
						|
	uint speed;
 | 
						|
	uint target_speed;
 | 
						|
#ifdef CONFIG_ARCH_S5P6818
 | 
						|
	uint sda_delay;
 | 
						|
#else
 | 
						|
	/* setup time for Stop condition [us] */
 | 
						|
	uint tsu_stop;
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
/* s5pxx18 i2c must be reset before enabled */
 | 
						|
static void i2c_reset(int ch)
 | 
						|
{
 | 
						|
	int rst_id = RESET_ID_I2C0 + ch;
 | 
						|
 | 
						|
	nx_rstcon_setrst(rst_id, 0);
 | 
						|
	nx_rstcon_setrst(rst_id, 1);
 | 
						|
}
 | 
						|
 | 
						|
static uint i2c_get_clkrate(struct nx_i2c_bus *bus)
 | 
						|
{
 | 
						|
	struct clk *clk;
 | 
						|
	int index = bus->bus_num;
 | 
						|
	char name[50] = {0, };
 | 
						|
 | 
						|
	sprintf(name, "%s.%d", DEV_NAME_I2C, index);
 | 
						|
	clk = clk_get((const char *)name);
 | 
						|
	if (!clk)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	return clk_get_rate(clk);
 | 
						|
}
 | 
						|
 | 
						|
static uint i2c_set_clk(struct nx_i2c_bus *bus, uint enb)
 | 
						|
{
 | 
						|
	struct clk *clk;
 | 
						|
	char name[50];
 | 
						|
 | 
						|
	sprintf(name, "%s.%d", DEV_NAME_I2C, bus->bus_num);
 | 
						|
	clk = clk_get((const char *)name);
 | 
						|
	if (!clk) {
 | 
						|
		debug("%s(): clk_get(%s) error!\n",
 | 
						|
		      __func__, (const char *)name);
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	clk_disable(clk);
 | 
						|
	if (enb)
 | 
						|
		clk_enable(clk);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef CONFIG_ARCH_S5P6818
 | 
						|
/* Set SDA line delay, not available at S5P4418 */
 | 
						|
static int nx_i2c_set_sda_delay(struct nx_i2c_bus *bus)
 | 
						|
{
 | 
						|
	struct nx_i2c_regs *i2c = bus->regs;
 | 
						|
	uint pclk = 0;
 | 
						|
	uint t_pclk = 0;
 | 
						|
	uint delay = 0;
 | 
						|
 | 
						|
	/* get input clock of the I2C-controller */
 | 
						|
	pclk = i2c_get_clkrate(bus);
 | 
						|
 | 
						|
	if (bus->sda_delay) {
 | 
						|
		/* t_pclk = period time of one pclk [ns] */
 | 
						|
		t_pclk = DIV_ROUND_UP(1000, pclk / 1000000);
 | 
						|
		/* delay = number of pclks required for sda_delay [ns] */
 | 
						|
		delay = DIV_ROUND_UP(bus->sda_delay, t_pclk);
 | 
						|
		/* delay = register value (step of 5 clocks) */
 | 
						|
		delay = DIV_ROUND_UP(delay, SDADLY_CLKSTEP);
 | 
						|
		/* max. possible register value = 3 */
 | 
						|
		if (delay > SDADLY_MAX) {
 | 
						|
			delay = SDADLY_MAX;
 | 
						|
			debug("%s(): sda-delay des.: %dns, sat. to max.: %dns (granularity: %dns)\n",
 | 
						|
			      __func__, bus->sda_delay, t_pclk * delay * SDADLY_CLKSTEP,
 | 
						|
			      t_pclk * SDADLY_CLKSTEP);
 | 
						|
		} else {
 | 
						|
			debug("%s(): sda-delay des.: %dns, act.: %dns (granularity: %dns)\n",
 | 
						|
			      __func__, bus->sda_delay, t_pclk * delay * SDADLY_CLKSTEP,
 | 
						|
			      t_pclk * SDADLY_CLKSTEP);
 | 
						|
		}
 | 
						|
 | 
						|
		delay |= I2CLC_FILTER;
 | 
						|
	} else {
 | 
						|
		delay = 0;
 | 
						|
		debug("%s(): sda-delay = 0\n", __func__);
 | 
						|
	}
 | 
						|
 | 
						|
	delay &= 0x7;
 | 
						|
	writel(delay, &i2c->iiclc);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static int nx_i2c_set_bus_speed(struct udevice *dev, uint speed)
 | 
						|
{
 | 
						|
	struct nx_i2c_bus *bus = dev_get_priv(dev);
 | 
						|
	struct nx_i2c_regs *i2c = bus->regs;
 | 
						|
	unsigned long pclk, pres = 16, div;
 | 
						|
 | 
						|
	if (i2c_set_clk(bus, 1))
 | 
						|
		return -EINVAL;
 | 
						|
 | 
						|
	/* get input clock of the I2C-controller */
 | 
						|
	pclk = i2c_get_clkrate(bus);
 | 
						|
 | 
						|
	/* calculate prescaler and divisor values */
 | 
						|
	if ((pclk / pres / (16 + 1)) > speed)
 | 
						|
		/* prescaler value 16 is too less --> set to 256 */
 | 
						|
		pres = 256;
 | 
						|
 | 
						|
	div = 0;
 | 
						|
	/* actual divider = div + 1 */
 | 
						|
	while ((pclk / pres / (div + 1)) > speed)
 | 
						|
		div++;
 | 
						|
 | 
						|
	if (div > 0xF) {
 | 
						|
		debug("%s(): pres==%ld, div==0x%lx is saturated to 0xF !)\n",
 | 
						|
		      __func__, pres, div);
 | 
						|
		div = 0xF;
 | 
						|
	} else {
 | 
						|
		debug("%s(): pres==%ld, div==0x%lx)\n", __func__, pres, div);
 | 
						|
	}
 | 
						|
 | 
						|
	/* set Tx-clock divisor and prescaler values */
 | 
						|
	writel((div & I2CCON_TCDMSK) | ((pres == 256) ? I2CCON_TCP256 : 0),
 | 
						|
	       &i2c->iiccon);
 | 
						|
 | 
						|
	/* init to SLAVE REVEIVE and set slaveaddr */
 | 
						|
	writel(0, &i2c->iicstat);
 | 
						|
	writel(0x00, &i2c->iicadd);
 | 
						|
 | 
						|
	/* program Master Transmit (and implicit STOP) */
 | 
						|
	writel(I2CSTAT_MTM | I2CSTAT_RXTXEN, &i2c->iicstat);
 | 
						|
 | 
						|
	/* calculate actual I2C speed [Hz] */
 | 
						|
	bus->speed = pclk / ((div + 1) * pres);
 | 
						|
	debug("%s(): speed des.: %dHz, act.: %dHz\n",
 | 
						|
	      __func__, speed, bus->speed);
 | 
						|
 | 
						|
#ifdef CONFIG_ARCH_S5P6818
 | 
						|
	nx_i2c_set_sda_delay(bus);
 | 
						|
#else
 | 
						|
	/* setup time for Stop condition [us], min. 4us @ 100kHz I2C-clock */
 | 
						|
	bus->tsu_stop = DIV_ROUND_UP(400, bus->speed / 1000);
 | 
						|
#endif
 | 
						|
 | 
						|
	if (i2c_set_clk(bus, 0))
 | 
						|
		return -EINVAL;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void i2c_process_node(struct udevice *dev)
 | 
						|
{
 | 
						|
	struct nx_i2c_bus *bus = dev_get_priv(dev);
 | 
						|
 | 
						|
	bus->target_speed = dev_read_s32_default(dev, "clock-frequency",
 | 
						|
						 DEFAULT_SPEED);
 | 
						|
#ifdef CONFIG_ARCH_S5P6818
 | 
						|
	bus->sda_delay = dev_read_s32_default(dev, "i2c-sda-delay-ns", 0);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
static int nx_i2c_probe(struct udevice *dev)
 | 
						|
{
 | 
						|
	struct nx_i2c_bus *bus = dev_get_priv(dev);
 | 
						|
	fdt_addr_t addr;
 | 
						|
 | 
						|
	/* get regs = i2c base address */
 | 
						|
	addr = devfdt_get_addr(dev);
 | 
						|
	if (addr == FDT_ADDR_T_NONE)
 | 
						|
		return -EINVAL;
 | 
						|
	bus->regs = (struct nx_i2c_regs *)addr;
 | 
						|
 | 
						|
	bus->bus_num = dev_seq(dev);
 | 
						|
 | 
						|
	/* i2c node parsing */
 | 
						|
	i2c_process_node(dev);
 | 
						|
	if (!bus->target_speed)
 | 
						|
		return -ENODEV;
 | 
						|
 | 
						|
	/* reset */
 | 
						|
	i2c_reset(bus->bus_num);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* i2c bus busy check */
 | 
						|
static int i2c_is_busy(struct nx_i2c_regs *i2c)
 | 
						|
{
 | 
						|
	ulong start_time;
 | 
						|
 | 
						|
	start_time = get_timer(0);
 | 
						|
	while (readl(&i2c->iicstat) & I2CSTAT_BSY) {
 | 
						|
		if (get_timer(start_time) > I2C_TIMEOUT_MS) {
 | 
						|
			debug("Timeout\n");
 | 
						|
			return -EBUSY;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* irq enable/disable functions */
 | 
						|
static void i2c_enable_irq(struct nx_i2c_regs *i2c)
 | 
						|
{
 | 
						|
	unsigned int reg;
 | 
						|
 | 
						|
	reg = readl(&i2c->iiccon);
 | 
						|
	reg |= I2CCON_IRENB;
 | 
						|
	writel(reg, &i2c->iiccon);
 | 
						|
}
 | 
						|
 | 
						|
/* irq clear function */
 | 
						|
static void i2c_clear_irq(struct nx_i2c_regs *i2c)
 | 
						|
{
 | 
						|
	unsigned int reg;
 | 
						|
 | 
						|
	reg = readl(&i2c->iiccon);
 | 
						|
	/* reset interrupt pending flag */
 | 
						|
	reg &= ~(I2CCON_IRPND);
 | 
						|
	/*
 | 
						|
	 * Interrupt must also be cleared!
 | 
						|
	 * Otherwise linux boot may hang after:
 | 
						|
	 *     [    0.436000] NetLabel:  unlabeled traffic allowed by default
 | 
						|
	 * Next would be:
 | 
						|
	 *     [    0.442000] clocksource: Switched to clocksource source timer
 | 
						|
	 */
 | 
						|
	reg |= I2CCON_IRCLR;
 | 
						|
	writel(reg, &i2c->iiccon);
 | 
						|
}
 | 
						|
 | 
						|
/* ack enable functions */
 | 
						|
static void i2c_enable_ack(struct nx_i2c_regs *i2c)
 | 
						|
{
 | 
						|
	unsigned int reg;
 | 
						|
 | 
						|
	reg = readl(&i2c->iiccon);
 | 
						|
	reg |= I2CCON_ACKGEN;
 | 
						|
	writel(reg, &i2c->iiccon);
 | 
						|
}
 | 
						|
 | 
						|
static void i2c_send_stop(struct nx_i2c_bus *bus)
 | 
						|
{
 | 
						|
	struct nx_i2c_regs *i2c = bus->regs;
 | 
						|
 | 
						|
	if (IS_ENABLED(CONFIG_ARCH_S5P6818)) {
 | 
						|
		unsigned int reg;
 | 
						|
 | 
						|
		reg = readl(&i2c->iicstat);
 | 
						|
		reg |= I2CSTAT_MRM | I2CSTAT_RXTXEN;
 | 
						|
		reg &= (~I2CSTAT_SS);
 | 
						|
 | 
						|
		writel(reg, &i2c->iicstat);
 | 
						|
		i2c_clear_irq(i2c);
 | 
						|
	} else {  /* S5P4418 */
 | 
						|
		writel(STOPCON_NAG, &i2c->iicstopcon);
 | 
						|
 | 
						|
		i2c_clear_irq(i2c);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Clock Line Release --> SDC changes from Low to High and
 | 
						|
		 * SDA from High to Low
 | 
						|
		 */
 | 
						|
		writel(STOPCON_CLR, &i2c->iicstopcon);
 | 
						|
 | 
						|
		/* Hold SDA Low (Setup Time for Stop condition) */
 | 
						|
		udelay(bus->tsu_stop);
 | 
						|
 | 
						|
		i2c_clear_irq(i2c);
 | 
						|
 | 
						|
		/* Master Receive Mode Stop --> SDA becomes High */
 | 
						|
		writel(I2CSTAT_MRM, &i2c->iicstat);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int wait_for_xfer(struct nx_i2c_regs *i2c)
 | 
						|
{
 | 
						|
	unsigned long start_time = get_timer(0);
 | 
						|
 | 
						|
	do {
 | 
						|
		if (readl(&i2c->iiccon) & I2CCON_IRPND)
 | 
						|
			/* return -EREMOTEIO if not Acknowledged, otherwise 0 */
 | 
						|
			return (readl(&i2c->iicstat) & I2CSTAT_NACK) ?
 | 
						|
				-EREMOTEIO : 0;
 | 
						|
	} while (get_timer(start_time) < I2C_TIMEOUT_MS);
 | 
						|
 | 
						|
	return -ETIMEDOUT;
 | 
						|
}
 | 
						|
 | 
						|
static int i2c_transfer(struct nx_i2c_regs *i2c,
 | 
						|
			uchar cmd_type,
 | 
						|
			uchar chip_addr,
 | 
						|
			uchar addr[],
 | 
						|
			uchar addr_len,
 | 
						|
			uchar data[],
 | 
						|
			unsigned short data_len,
 | 
						|
			uint seq)
 | 
						|
{
 | 
						|
	uint status;
 | 
						|
	int i = 0, result;
 | 
						|
 | 
						|
	/* Note: data_len = 0 is supported for "probe_chip" */
 | 
						|
 | 
						|
	i2c_enable_irq(i2c);
 | 
						|
	i2c_enable_ack(i2c);
 | 
						|
 | 
						|
	/* Get the slave chip address going */
 | 
						|
	/* Enable Rx/Tx */
 | 
						|
	writel(I2CSTAT_RXTXEN, &i2c->iicstat);
 | 
						|
 | 
						|
	writel(chip_addr, &i2c->iicds);
 | 
						|
	status = I2CSTAT_RXTXEN | I2CSTAT_SS;
 | 
						|
	if (cmd_type == I2C_WRITE || (addr && addr_len))
 | 
						|
		status |= I2CSTAT_MTM;
 | 
						|
	else
 | 
						|
		status |= I2CSTAT_MRM;
 | 
						|
 | 
						|
	writel(status, &i2c->iicstat);
 | 
						|
	if (seq)
 | 
						|
		i2c_clear_irq(i2c);
 | 
						|
 | 
						|
	/* Wait for chip address to transmit. */
 | 
						|
	result = wait_for_xfer(i2c);
 | 
						|
	if (result) {
 | 
						|
		debug("%s: transmitting chip address failed\n", __func__);
 | 
						|
		goto bailout;
 | 
						|
	}
 | 
						|
 | 
						|
	/* If register address needs to be transmitted - do it now. */
 | 
						|
	if (addr && addr_len) {  /* register addr */
 | 
						|
		while ((i < addr_len) && !result) {
 | 
						|
			writel(addr[i++], &i2c->iicds);
 | 
						|
			i2c_clear_irq(i2c);
 | 
						|
			result = wait_for_xfer(i2c);
 | 
						|
		}
 | 
						|
 | 
						|
		i = 0;
 | 
						|
		if (result) {
 | 
						|
			debug("%s: transmitting register address failed\n",
 | 
						|
			      __func__);
 | 
						|
			goto bailout;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	switch (cmd_type) {
 | 
						|
	case I2C_WRITE:
 | 
						|
		while ((i < data_len) && !result) {
 | 
						|
			writel(data[i++], &i2c->iicds);
 | 
						|
			i2c_clear_irq(i2c);
 | 
						|
			result = wait_for_xfer(i2c);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	case I2C_READ:
 | 
						|
		if (addr && addr_len) {
 | 
						|
			/*
 | 
						|
			 * Register address has been sent, now send slave chip
 | 
						|
			 * address again to start the actual read transaction.
 | 
						|
			 */
 | 
						|
			writel(chip_addr, &i2c->iicds);
 | 
						|
 | 
						|
			/* Generate a re-START. */
 | 
						|
			writel(I2CSTAT_MRM | I2CSTAT_RXTXEN |
 | 
						|
			       I2CSTAT_SS, &i2c->iicstat);
 | 
						|
			i2c_clear_irq(i2c);
 | 
						|
			result = wait_for_xfer(i2c);
 | 
						|
			if (result) {
 | 
						|
				debug("%s: I2C_READ: sending chip addr. failed\n",
 | 
						|
				      __func__);
 | 
						|
				goto bailout;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		while ((i < data_len) && !result) {
 | 
						|
			/* disable ACK for final READ */
 | 
						|
			if (i == data_len - 1)
 | 
						|
				clrbits_le32(&i2c->iiccon, I2CCON_ACKGEN);
 | 
						|
 | 
						|
			i2c_clear_irq(i2c);
 | 
						|
			result = wait_for_xfer(i2c);
 | 
						|
			data[i++] = readb(&i2c->iicds);
 | 
						|
		}
 | 
						|
 | 
						|
		if (result == -EREMOTEIO)
 | 
						|
			 /* Not Acknowledged --> normal terminated read. */
 | 
						|
			result = 0;
 | 
						|
		else if (result == -ETIMEDOUT)
 | 
						|
			debug("%s: I2C_READ: time out\n", __func__);
 | 
						|
		else
 | 
						|
			debug("%s: I2C_READ: read not terminated with NACK\n",
 | 
						|
			      __func__);
 | 
						|
		break;
 | 
						|
 | 
						|
	default:
 | 
						|
		debug("%s: bad call\n", __func__);
 | 
						|
		result = -EINVAL;
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
bailout:
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
static int nx_i2c_read(struct udevice *dev, uchar chip_addr, uint addr,
 | 
						|
		       uint alen, uchar *buffer, uint len, uint seq)
 | 
						|
{
 | 
						|
	struct nx_i2c_bus *i2c;
 | 
						|
	uchar xaddr[4];
 | 
						|
	int ret;
 | 
						|
 | 
						|
	i2c = dev_get_priv(dev);
 | 
						|
	if (!i2c)
 | 
						|
		return -EFAULT;
 | 
						|
 | 
						|
	if (alen > 4) {
 | 
						|
		debug("I2C read: addr len %d not supported\n", alen);
 | 
						|
		return -EADDRNOTAVAIL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (alen > 0)
 | 
						|
		xaddr[0] = (addr >> 24) & 0xFF;
 | 
						|
 | 
						|
	if (alen > 0) {
 | 
						|
		xaddr[0] = (addr >> 24) & 0xFF;
 | 
						|
		xaddr[1] = (addr >> 16) & 0xFF;
 | 
						|
		xaddr[2] = (addr >> 8) & 0xFF;
 | 
						|
		xaddr[3] = addr & 0xFF;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = i2c_transfer(i2c->regs, I2C_READ, chip_addr << 1,
 | 
						|
			   &xaddr[4 - alen], alen, buffer, len, seq);
 | 
						|
 | 
						|
	if (ret) {
 | 
						|
		debug("I2C read failed %d\n", ret);
 | 
						|
		return -EIO;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int nx_i2c_write(struct udevice *dev, uchar chip_addr, uint addr,
 | 
						|
			uint alen, uchar *buffer, uint len, uint seq)
 | 
						|
{
 | 
						|
	struct nx_i2c_bus *i2c;
 | 
						|
	uchar xaddr[4];
 | 
						|
	int ret;
 | 
						|
 | 
						|
	i2c = dev_get_priv(dev);
 | 
						|
	if (!i2c)
 | 
						|
		return -EFAULT;
 | 
						|
 | 
						|
	if (alen > 4) {
 | 
						|
		debug("I2C write: addr len %d not supported\n", alen);
 | 
						|
		return -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (alen > 0) {
 | 
						|
		xaddr[0] = (addr >> 24) & 0xFF;
 | 
						|
		xaddr[1] = (addr >> 16) & 0xFF;
 | 
						|
		xaddr[2] = (addr >> 8) & 0xFF;
 | 
						|
		xaddr[3] = addr & 0xFF;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = i2c_transfer(i2c->regs, I2C_WRITE, chip_addr << 1,
 | 
						|
			   &xaddr[4 - alen], alen, buffer, len, seq);
 | 
						|
	if (ret) {
 | 
						|
		debug("I2C write failed %d\n", ret);
 | 
						|
		return -EIO;
 | 
						|
	}
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int nx_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
 | 
						|
{
 | 
						|
	struct nx_i2c_bus *bus = dev_get_priv(dev);
 | 
						|
	struct nx_i2c_regs *i2c = bus->regs;
 | 
						|
	int ret;
 | 
						|
	int i;
 | 
						|
 | 
						|
	/* The power loss by the clock, only during on/off. */
 | 
						|
	ret = i2c_set_clk(bus, 1);
 | 
						|
 | 
						|
	if (!ret)
 | 
						|
		/* Bus State(Busy) check  */
 | 
						|
		ret = i2c_is_busy(i2c);
 | 
						|
	if (!ret) {
 | 
						|
		for (i = 0; i < nmsgs; msg++, i++) {
 | 
						|
			if (msg->flags & I2C_M_RD) {
 | 
						|
				ret = nx_i2c_read(dev, msg->addr, 0, 0,
 | 
						|
						  msg->buf, msg->len, i);
 | 
						|
			} else {
 | 
						|
				ret = nx_i2c_write(dev, msg->addr, 0, 0,
 | 
						|
						   msg->buf, msg->len, i);
 | 
						|
			}
 | 
						|
 | 
						|
			if (ret) {
 | 
						|
				debug("i2c_xfer: error sending\n");
 | 
						|
				ret = -EREMOTEIO;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		i2c_send_stop(bus);
 | 
						|
		if (i2c_set_clk(bus, 0))
 | 
						|
			ret = -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
};
 | 
						|
 | 
						|
static int nx_i2c_probe_chip(struct udevice *dev, u32 chip_addr,
 | 
						|
			     u32 chip_flags)
 | 
						|
{
 | 
						|
	int ret;
 | 
						|
	struct nx_i2c_bus *bus = dev_get_priv(dev);
 | 
						|
 | 
						|
	ret = i2c_set_clk(bus, 1);
 | 
						|
 | 
						|
	if (!ret) {
 | 
						|
		/*
 | 
						|
		 * Send Chip Address only
 | 
						|
		 * --> I2C transfer with data length and address length = 0.
 | 
						|
		 * If there is a Slave, i2c_transfer() returns 0 (acknowledge
 | 
						|
		 * transfer).
 | 
						|
		 * I2C_WRITE must be used in order Master Transmit Mode is
 | 
						|
		 * selected. Otherwise (in Master Receive Mode, I2C_READ)
 | 
						|
		 * sending the stop condition below is not working (SDA does
 | 
						|
		 * not transit to High).
 | 
						|
		 */
 | 
						|
		ret = i2c_transfer(bus->regs, I2C_WRITE, (uchar)chip_addr << 1,
 | 
						|
				   NULL, 0, NULL, 0, 0);
 | 
						|
 | 
						|
		i2c_send_stop(bus);
 | 
						|
		if (i2c_set_clk(bus, 0))
 | 
						|
			ret = -EINVAL;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
static const struct dm_i2c_ops nx_i2c_ops = {
 | 
						|
	.xfer		= nx_i2c_xfer,
 | 
						|
	.probe_chip	= nx_i2c_probe_chip,
 | 
						|
	.set_bus_speed	= nx_i2c_set_bus_speed,
 | 
						|
};
 | 
						|
 | 
						|
static const struct udevice_id nx_i2c_ids[] = {
 | 
						|
	{ .compatible = "nexell,s5pxx18-i2c" },
 | 
						|
	{ }
 | 
						|
};
 | 
						|
 | 
						|
U_BOOT_DRIVER(i2c_nexell) = {
 | 
						|
	.name		= "i2c_nexell",
 | 
						|
	.id		= UCLASS_I2C,
 | 
						|
	.of_match	= nx_i2c_ids,
 | 
						|
	.probe		= nx_i2c_probe,
 | 
						|
	.priv_auto	= sizeof(struct nx_i2c_bus),
 | 
						|
	.ops		= &nx_i2c_ops,
 | 
						|
};
 |