Release 4.7 drivers/i2c/busses/i2c-sirf.c
  
  
/*
 * I2C bus driver for CSR SiRFprimaII
 *
 * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
 *
 * Licensed under GPLv2 or later.
 */
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#define SIRFSOC_I2C_CLK_CTRL		0x00
#define SIRFSOC_I2C_STATUS		0x0C
#define SIRFSOC_I2C_CTRL		0x10
#define SIRFSOC_I2C_IO_CTRL		0x14
#define SIRFSOC_I2C_SDA_DELAY		0x18
#define SIRFSOC_I2C_CMD_START		0x1C
#define SIRFSOC_I2C_CMD_BUF		0x30
#define SIRFSOC_I2C_DATA_BUF		0x80
#define SIRFSOC_I2C_CMD_BUF_MAX		16
#define SIRFSOC_I2C_DATA_BUF_MAX	16
#define SIRFSOC_I2C_CMD(x)		(SIRFSOC_I2C_CMD_BUF + (x)*0x04)
#define SIRFSOC_I2C_DATA_MASK(x)        (0xFF<<(((x)&3)*8))
#define SIRFSOC_I2C_DATA_SHIFT(x)       (((x)&3)*8)
#define SIRFSOC_I2C_DIV_MASK		(0xFFFF)
/* I2C status flags */
#define SIRFSOC_I2C_STAT_BUSY		BIT(0)
#define SIRFSOC_I2C_STAT_TIP		BIT(1)
#define SIRFSOC_I2C_STAT_NACK		BIT(2)
#define SIRFSOC_I2C_STAT_TR_INT		BIT(4)
#define SIRFSOC_I2C_STAT_STOP		BIT(6)
#define SIRFSOC_I2C_STAT_CMD_DONE	BIT(8)
#define SIRFSOC_I2C_STAT_ERR		BIT(9)
#define SIRFSOC_I2C_CMD_INDEX		(0x1F<<16)
/* I2C control flags */
#define SIRFSOC_I2C_RESET		BIT(0)
#define SIRFSOC_I2C_CORE_EN		BIT(1)
#define SIRFSOC_I2C_MASTER_MODE		BIT(2)
#define SIRFSOC_I2C_CMD_DONE_EN		BIT(11)
#define SIRFSOC_I2C_ERR_INT_EN		BIT(12)
#define SIRFSOC_I2C_SDA_DELAY_MASK	(0xFF)
#define SIRFSOC_I2C_SCLF_FILTER		(3<<8)
#define SIRFSOC_I2C_START_CMD		BIT(0)
#define SIRFSOC_I2C_CMD_RP(x)		((x)&0x7)
#define SIRFSOC_I2C_NACK		BIT(3)
#define SIRFSOC_I2C_WRITE		BIT(4)
#define SIRFSOC_I2C_READ		BIT(5)
#define SIRFSOC_I2C_STOP		BIT(6)
#define SIRFSOC_I2C_START		BIT(7)
#define SIRFSOC_I2C_DEFAULT_SPEED 100000
#define SIRFSOC_I2C_ERR_NOACK      1
#define SIRFSOC_I2C_ERR_TIMEOUT    2
struct sirfsoc_i2c {
	
void __iomem *base;
	
struct clk *clk;
	
u32 cmd_ptr;		/* Current position in CMD buffer */
	
u8 *buf;		/* Buffer passed by user */
	
u32 msg_len;		/* Message length */
	
u32 finished_len;	/* number of bytes read/written */
	
u32 read_cmd_len;	/* number of read cmd sent */
	
int msg_read;		/* 1 indicates a read message */
	
int err_status;		/* 1 indicates an error on bus */
	
u32 sda_delay;		/* For suspend/resume */
	
u32 clk_div;
	
int last;		/* Last message in transfer, STOP cmd can be sent */
	
struct completion done;	/* indicates completion of message transfer */
	
struct i2c_adapter adapter;
};
static void i2c_sirfsoc_read_data(struct sirfsoc_i2c *siic)
{
	u32 data = 0;
	int i;
	for (i = 0; i < siic->read_cmd_len; i++) {
		if (!(i & 0x3))
			data = readl(siic->base + SIRFSOC_I2C_DATA_BUF + i);
		siic->buf[siic->finished_len++] =
			(u8)((data & SIRFSOC_I2C_DATA_MASK(i)) >>
				SIRFSOC_I2C_DATA_SHIFT(i));
	}
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 87 | 100.00% | 1 | 100.00% | 
 | Total | 87 | 100.00% | 1 | 100.00% | 
static void i2c_sirfsoc_queue_cmd(struct sirfsoc_i2c *siic)
{
	u32 regval;
	int i = 0;
	if (siic->msg_read) {
		while (((siic->finished_len + i) < siic->msg_len)
				&& (siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX)) {
			regval = SIRFSOC_I2C_READ | SIRFSOC_I2C_CMD_RP(0);
			if (((siic->finished_len + i) ==
					(siic->msg_len - 1)) && siic->last)
				regval |= SIRFSOC_I2C_STOP | SIRFSOC_I2C_NACK;
			writel(regval,
				siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
			i++;
		}
		siic->read_cmd_len = i;
	} else {
		while ((siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX - 1)
				&& (siic->finished_len < siic->msg_len)) {
			regval = SIRFSOC_I2C_WRITE | SIRFSOC_I2C_CMD_RP(0);
			if ((siic->finished_len == (siic->msg_len - 1))
				&& siic->last)
				regval |= SIRFSOC_I2C_STOP;
			writel(regval,
				siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
			writel(siic->buf[siic->finished_len++],
				siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
		}
	}
	siic->cmd_ptr = 0;
	/* Trigger the transfer */
	writel(SIRFSOC_I2C_START_CMD, siic->base + SIRFSOC_I2C_CMD_START);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 238 | 100.00% | 1 | 100.00% | 
 | Total | 238 | 100.00% | 1 | 100.00% | 
static irqreturn_t i2c_sirfsoc_irq(int irq, void *dev_id)
{
	struct sirfsoc_i2c *siic = (struct sirfsoc_i2c *)dev_id;
	u32 i2c_stat = readl(siic->base + SIRFSOC_I2C_STATUS);
	if (i2c_stat & SIRFSOC_I2C_STAT_ERR) {
		/* Error conditions */
		siic->err_status = SIRFSOC_I2C_ERR_NOACK;
		writel(SIRFSOC_I2C_STAT_ERR, siic->base + SIRFSOC_I2C_STATUS);
		if (i2c_stat & SIRFSOC_I2C_STAT_NACK)
			dev_dbg(&siic->adapter.dev, "ACK not received\n");
		else
			dev_err(&siic->adapter.dev, "I2C error\n");
		/*
                 * Due to hardware ANOMALY, we need to reset I2C earlier after
                 * we get NOACK while accessing non-existing clients, otherwise
                 * we will get errors even we access existing clients later
                 */
		writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET,
				siic->base + SIRFSOC_I2C_CTRL);
		while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
			cpu_relax();
		complete(&siic->done);
	} else if (i2c_stat & SIRFSOC_I2C_STAT_CMD_DONE) {
		/* CMD buffer execution complete */
		if (siic->msg_read)
			i2c_sirfsoc_read_data(siic);
		if (siic->finished_len == siic->msg_len)
			complete(&siic->done);
		else /* Fill a new CMD buffer for left data */
			i2c_sirfsoc_queue_cmd(siic);
		writel(SIRFSOC_I2C_STAT_CMD_DONE, siic->base + SIRFSOC_I2C_STATUS);
	}
	return IRQ_HANDLED;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 199 | 100.00% | 2 | 100.00% | 
 | Total | 199 | 100.00% | 2 | 100.00% | 
static void i2c_sirfsoc_set_address(struct sirfsoc_i2c *siic,
	struct i2c_msg *msg)
{
	unsigned char addr;
	u32 regval = SIRFSOC_I2C_START | SIRFSOC_I2C_CMD_RP(0) | SIRFSOC_I2C_WRITE;
	/* no data and last message -> add STOP */
	if (siic->last && (msg->len == 0))
		regval |= SIRFSOC_I2C_STOP;
	writel(regval, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
	addr = i2c_8bit_addr_from_msg(msg);
	/* Reverse direction bit */
	if (msg->flags & I2C_M_REV_DIR_ADDR)
		addr ^= 1;
	writel(addr, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 102 | 97.14% | 2 | 66.67% | 
| wolfram sang | wolfram sang | 3 | 2.86% | 1 | 33.33% | 
 | Total | 105 | 100.00% | 3 | 100.00% | 
static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg)
{
	u32 regval = readl(siic->base + SIRFSOC_I2C_CTRL);
	/* timeout waiting for the xfer to finish or fail */
	int timeout = msecs_to_jiffies((msg->len + 1) * 50);
	i2c_sirfsoc_set_address(siic, msg);
	writel(regval | SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN,
		siic->base + SIRFSOC_I2C_CTRL);
	i2c_sirfsoc_queue_cmd(siic);
	if (wait_for_completion_timeout(&siic->done, timeout) == 0) {
		siic->err_status = SIRFSOC_I2C_ERR_TIMEOUT;
		dev_err(&siic->adapter.dev, "Transfer timeout\n");
	}
	writel(regval & ~(SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN),
		siic->base + SIRFSOC_I2C_CTRL);
	writel(0, siic->base + SIRFSOC_I2C_CMD_START);
	/* i2c control doesn't response, reset it */
	if (siic->err_status == SIRFSOC_I2C_ERR_TIMEOUT) {
		writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET,
			siic->base + SIRFSOC_I2C_CTRL);
		while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
			cpu_relax();
	}
	return siic->err_status ? -EAGAIN : 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 192 | 100.00% | 3 | 100.00% | 
 | Total | 192 | 100.00% | 3 | 100.00% | 
static u32 i2c_sirfsoc_func(struct i2c_adapter *adap)
{
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 16 | 100.00% | 1 | 100.00% | 
 | Total | 16 | 100.00% | 1 | 100.00% | 
static int i2c_sirfsoc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
	int num)
{
	struct sirfsoc_i2c *siic = adap->algo_data;
	int i, ret;
	clk_enable(siic->clk);
	for (i = 0; i < num; i++) {
		siic->buf = msgs[i].buf;
		siic->msg_len = msgs[i].len;
		siic->msg_read = !!(msgs[i].flags & I2C_M_RD);
		siic->err_status = 0;
		siic->cmd_ptr = 0;
		siic->finished_len = 0;
		siic->last = (i == (num - 1));
		ret = i2c_sirfsoc_xfer_msg(siic, &msgs[i]);
		if (ret) {
			clk_disable(siic->clk);
			return ret;
		}
	}
	clk_disable(siic->clk);
	return num;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 165 | 100.00% | 1 | 100.00% | 
 | Total | 165 | 100.00% | 1 | 100.00% | 
/* I2C algorithms associated with this master controller driver */
static const struct i2c_algorithm i2c_sirfsoc_algo = {
	.master_xfer = i2c_sirfsoc_xfer,
	.functionality = i2c_sirfsoc_func,
};
static int i2c_sirfsoc_probe(struct platform_device *pdev)
{
	struct sirfsoc_i2c *siic;
	struct i2c_adapter *adap;
	struct resource *mem_res;
	struct clk *clk;
	int bitrate;
	int ctrl_speed;
	int irq;
	int err;
	u32 regval;
	clk = clk_get(&pdev->dev, NULL);
	if (IS_ERR(clk)) {
		err = PTR_ERR(clk);
		dev_err(&pdev->dev, "Clock get failed\n");
		goto err_get_clk;
	}
	err = clk_prepare(clk);
	if (err) {
		dev_err(&pdev->dev, "Clock prepare failed\n");
		goto err_clk_prep;
	}
	err = clk_enable(clk);
	if (err) {
		dev_err(&pdev->dev, "Clock enable failed\n");
		goto err_clk_en;
	}
	ctrl_speed = clk_get_rate(clk);
	siic = devm_kzalloc(&pdev->dev, sizeof(*siic), GFP_KERNEL);
	if (!siic) {
		err = -ENOMEM;
		goto out;
	}
	adap = &siic->adapter;
	adap->class = I2C_CLASS_DEPRECATED;
	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	siic->base = devm_ioremap_resource(&pdev->dev, mem_res);
	if (IS_ERR(siic->base)) {
		err = PTR_ERR(siic->base);
		goto out;
	}
	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		err = irq;
		goto out;
	}
	err = devm_request_irq(&pdev->dev, irq, i2c_sirfsoc_irq, 0,
		dev_name(&pdev->dev), siic);
	if (err)
		goto out;
	adap->algo = &i2c_sirfsoc_algo;
	adap->algo_data = siic;
	adap->retries = 3;
	adap->dev.of_node = pdev->dev.of_node;
	adap->dev.parent = &pdev->dev;
	adap->nr = pdev->id;
	strlcpy(adap->name, "sirfsoc-i2c", sizeof(adap->name));
	platform_set_drvdata(pdev, adap);
	init_completion(&siic->done);
	/* Controller Initalisation */
	writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
	while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
		cpu_relax();
	writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE,
		siic->base + SIRFSOC_I2C_CTRL);
	siic->clk = clk;
	err = of_property_read_u32(pdev->dev.of_node,
		"clock-frequency", &bitrate);
	if (err < 0)
		bitrate = SIRFSOC_I2C_DEFAULT_SPEED;
	/*
         * Due to some hardware design issues, we need to tune the formula.
         * Since i2c is open drain interface that allows the slave to
         * stall the transaction by holding the SCL line at '0', the RTL
         * implementation is waiting for SCL feedback from the pin after
         * setting it to High-Z ('1'). This wait adds to the high-time
         * interval counter few cycles of the input synchronization
         * (depending on the SCL_FILTER_REG field), and also the time it
         * takes for the board pull-up resistor to rise the SCL line.
         * For slow SCL settings these additions are negligible,
         * but they start to affect the speed when clock is set to faster
         * frequencies.
         * Through the actual tests, use the different user_div value(which
         * in the divider formular 'Fio / (Fi2c * user_div)') to adapt
         * the different ranges of i2c bus clock frequency, to make the SCL
         * more accurate.
         */
	if (bitrate <= 30000)
		regval = ctrl_speed / (bitrate * 5);
	else if (bitrate > 30000 && bitrate <= 280000)
		regval = (2 * ctrl_speed) / (bitrate * 11);
	else
		regval = ctrl_speed / (bitrate * 6);
	writel(regval, siic->base + SIRFSOC_I2C_CLK_CTRL);
	if (regval > 0xFF)
		writel(0xFF, siic->base + SIRFSOC_I2C_SDA_DELAY);
	else
		writel(regval, siic->base + SIRFSOC_I2C_SDA_DELAY);
	err = i2c_add_numbered_adapter(adap);
	if (err < 0) {
		dev_err(&pdev->dev, "Can't add new i2c adapter\n");
		goto out;
	}
	clk_disable(clk);
	dev_info(&pdev->dev, " I2C adapter ready to operate\n");
	return 0;
out:
	clk_disable(clk);
err_clk_en:
	clk_unprepare(clk);
err_clk_prep:
	clk_put(clk);
err_get_clk:
	return err;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 569 | 92.37% | 2 | 33.33% | 
| guoying zhang | guoying zhang | 25 | 4.06% | 1 | 16.67% | 
| barry song | barry song | 12 | 1.95% | 1 | 16.67% | 
| thierry reding | thierry reding | 9 | 1.46% | 1 | 16.67% | 
| wolfram sang | wolfram sang | 1 | 0.16% | 1 | 16.67% | 
 | Total | 616 | 100.00% | 6 | 100.00% | 
static int i2c_sirfsoc_remove(struct platform_device *pdev)
{
	struct i2c_adapter *adapter = platform_get_drvdata(pdev);
	struct sirfsoc_i2c *siic = adapter->algo_data;
	writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
	i2c_del_adapter(adapter);
	clk_unprepare(siic->clk);
	clk_put(siic->clk);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 63 | 100.00% | 1 | 100.00% | 
 | Total | 63 | 100.00% | 1 | 100.00% | 
#ifdef CONFIG_PM
static int i2c_sirfsoc_suspend(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct i2c_adapter *adapter = platform_get_drvdata(pdev);
	struct sirfsoc_i2c *siic = adapter->algo_data;
	clk_enable(siic->clk);
	siic->sda_delay = readl(siic->base + SIRFSOC_I2C_SDA_DELAY);
	siic->clk_div = readl(siic->base + SIRFSOC_I2C_CLK_CTRL);
	clk_disable(siic->clk);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 83 | 100.00% | 1 | 100.00% | 
 | Total | 83 | 100.00% | 1 | 100.00% | 
static int i2c_sirfsoc_resume(struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);
	struct i2c_adapter *adapter = platform_get_drvdata(pdev);
	struct sirfsoc_i2c *siic = adapter->algo_data;
	clk_enable(siic->clk);
	writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
	while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
		cpu_relax();
	writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE,
		siic->base + SIRFSOC_I2C_CTRL);
	writel(siic->clk_div, siic->base + SIRFSOC_I2C_CLK_CTRL);
	writel(siic->sda_delay, siic->base + SIRFSOC_I2C_SDA_DELAY);
	clk_disable(siic->clk);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 123 | 100.00% | 2 | 100.00% | 
 | Total | 123 | 100.00% | 2 | 100.00% | 
static const struct dev_pm_ops i2c_sirfsoc_pm_ops = {
	.suspend = i2c_sirfsoc_suspend,
	.resume = i2c_sirfsoc_resume,
};
#endif
static const struct of_device_id sirfsoc_i2c_of_match[] = {
	{ .compatible = "sirf,prima2-i2c", },
	{},
};
MODULE_DEVICE_TABLE(of, sirfsoc_i2c_of_match);
static struct platform_driver i2c_sirfsoc_driver = {
	.driver = {
		.name = "sirfsoc_i2c",
#ifdef CONFIG_PM
		.pm = &i2c_sirfsoc_pm_ops,
#endif
		.of_match_table = sirfsoc_i2c_of_match,
        },
	.probe = i2c_sirfsoc_probe,
	.remove = i2c_sirfsoc_remove,
};
module_platform_driver(i2c_sirfsoc_driver);
MODULE_DESCRIPTION("SiRF SoC I2C master controller driver");
MODULE_AUTHOR("Zhiwu Song <Zhiwu.Song@csr.com>, "
	"Xiangzhen Ye <Xiangzhen.Ye@csr.com>");
MODULE_LICENSE("GPL v2");
Overall Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| zhiwu song | zhiwu song | 2230 | 97.81% | 5 | 50.00% | 
| guoying zhang | guoying zhang | 25 | 1.10% | 1 | 10.00% | 
| barry song | barry song | 12 | 0.53% | 1 | 10.00% | 
| thierry reding | thierry reding | 9 | 0.39% | 1 | 10.00% | 
| wolfram sang | wolfram sang | 4 | 0.18% | 2 | 20.00% | 
| bill pemberton | bill pemberton |  | 0.00% | 0 | 0.00% | 
 | Total | 2280 | 100.00% | 10 | 100.00% | 
  
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.