forked from Minki/linux
bf51a8c5e0
i2c_del_adapter() always returns 0. So all checks testing whether it will be non zero will always evaluate to false and the conditional code is dead code. This patch updates all callers of i2c_del_mux_adapter() to ignore the return value and assume that it will always succeed (which it will). In a subsequent patch the return type of i2c_del_adapter() will be made void. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Acked-by: Ben Hutchings <bhutchings@solarflare.com> Reviewed-by: Jean Delvare <khali@linux-fr.org> Acked-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
284 lines
6.0 KiB
C
284 lines
6.0 KiB
C
/*
|
|
* I2C driver for PKUnity-v3 SoC
|
|
* Code specific to PKUnity SoC and UniCore ISA
|
|
*
|
|
* Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
|
|
* Copyright (C) 2001-2010 Guan Xuetao
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/err.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/init.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/io.h>
|
|
#include <mach/hardware.h>
|
|
|
|
/*
|
|
* Poll the i2c status register until the specified bit is set.
|
|
* Returns 0 if timed out (100 msec).
|
|
*/
|
|
static short poll_status(unsigned long bit)
|
|
{
|
|
int loop_cntr = 1000;
|
|
|
|
if (bit & I2C_STATUS_TFNF) {
|
|
do {
|
|
udelay(10);
|
|
} while (!(readl(I2C_STATUS) & bit) && (--loop_cntr > 0));
|
|
} else {
|
|
/* RXRDY handler */
|
|
do {
|
|
if (readl(I2C_TAR) == I2C_TAR_EEPROM)
|
|
msleep(20);
|
|
else
|
|
udelay(10);
|
|
} while (!(readl(I2C_RXFLR) & 0xf) && (--loop_cntr > 0));
|
|
}
|
|
|
|
return (loop_cntr > 0);
|
|
}
|
|
|
|
static int xfer_read(struct i2c_adapter *adap, unsigned char *buf, int length)
|
|
{
|
|
int i2c_reg = *buf;
|
|
|
|
/* Read data */
|
|
while (length--) {
|
|
if (!poll_status(I2C_STATUS_TFNF)) {
|
|
dev_dbg(&adap->dev, "Tx FIFO Not Full timeout\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
/* send addr */
|
|
writel(i2c_reg | I2C_DATACMD_WRITE, I2C_DATACMD);
|
|
|
|
/* get ready to next write */
|
|
i2c_reg++;
|
|
|
|
/* send read CMD */
|
|
writel(I2C_DATACMD_READ, I2C_DATACMD);
|
|
|
|
/* wait until the Rx FIFO have available */
|
|
if (!poll_status(I2C_STATUS_RFNE)) {
|
|
dev_dbg(&adap->dev, "RXRDY timeout\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
/* read the data to buf */
|
|
*buf = (readl(I2C_DATACMD) & I2C_DATACMD_DAT_MASK);
|
|
buf++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int xfer_write(struct i2c_adapter *adap, unsigned char *buf, int length)
|
|
{
|
|
int i2c_reg = *buf;
|
|
|
|
/* Do nothing but storing the reg_num to a static variable */
|
|
if (i2c_reg == -1) {
|
|
printk(KERN_WARNING "Error i2c reg\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
if (length == 1)
|
|
return 0;
|
|
|
|
buf++;
|
|
length--;
|
|
while (length--) {
|
|
/* send addr */
|
|
writel(i2c_reg | I2C_DATACMD_WRITE, I2C_DATACMD);
|
|
|
|
/* send write CMD */
|
|
writel(*buf | I2C_DATACMD_WRITE, I2C_DATACMD);
|
|
|
|
/* wait until the Rx FIFO have available */
|
|
msleep(20);
|
|
|
|
/* read the data to buf */
|
|
i2c_reg++;
|
|
buf++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Generic i2c master transfer entrypoint.
|
|
*
|
|
*/
|
|
static int puv3_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg,
|
|
int num)
|
|
{
|
|
int i, ret;
|
|
unsigned char swap;
|
|
|
|
/* Disable i2c */
|
|
writel(I2C_ENABLE_DISABLE, I2C_ENABLE);
|
|
|
|
/* Set the work mode and speed*/
|
|
writel(I2C_CON_MASTER | I2C_CON_SPEED_STD | I2C_CON_SLAVEDISABLE, I2C_CON);
|
|
|
|
writel(pmsg->addr, I2C_TAR);
|
|
|
|
/* Enable i2c */
|
|
writel(I2C_ENABLE_ENABLE, I2C_ENABLE);
|
|
|
|
dev_dbg(&adap->dev, "puv3_i2c_xfer: processing %d messages:\n", num);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i,
|
|
pmsg->flags & I2C_M_RD ? "read" : "writ",
|
|
pmsg->len, pmsg->len > 1 ? "s" : "",
|
|
pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr);
|
|
|
|
if (pmsg->len && pmsg->buf) { /* sanity check */
|
|
if (pmsg->flags & I2C_M_RD)
|
|
ret = xfer_read(adap, pmsg->buf, pmsg->len);
|
|
else
|
|
ret = xfer_write(adap, pmsg->buf, pmsg->len);
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
}
|
|
dev_dbg(&adap->dev, "transfer complete\n");
|
|
pmsg++; /* next message */
|
|
}
|
|
|
|
/* XXX: fixup be16_to_cpu in bq27x00_battery.c */
|
|
if (pmsg->addr == I2C_TAR_PWIC) {
|
|
swap = pmsg->buf[0];
|
|
pmsg->buf[0] = pmsg->buf[1];
|
|
pmsg->buf[1] = swap;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* Return list of supported functionality.
|
|
*/
|
|
static u32 puv3_i2c_func(struct i2c_adapter *adapter)
|
|
{
|
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
|
}
|
|
|
|
static struct i2c_algorithm puv3_i2c_algorithm = {
|
|
.master_xfer = puv3_i2c_xfer,
|
|
.functionality = puv3_i2c_func,
|
|
};
|
|
|
|
/*
|
|
* Main initialization routine.
|
|
*/
|
|
static int puv3_i2c_probe(struct platform_device *pdev)
|
|
{
|
|
struct i2c_adapter *adapter;
|
|
struct resource *mem;
|
|
int rc;
|
|
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!mem)
|
|
return -ENODEV;
|
|
|
|
if (!request_mem_region(mem->start, resource_size(mem), "puv3_i2c"))
|
|
return -EBUSY;
|
|
|
|
adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
|
|
if (adapter == NULL) {
|
|
dev_err(&pdev->dev, "can't allocate inteface!\n");
|
|
rc = -ENOMEM;
|
|
goto fail_nomem;
|
|
}
|
|
snprintf(adapter->name, sizeof(adapter->name), "PUV3-I2C at 0x%08x",
|
|
mem->start);
|
|
adapter->algo = &puv3_i2c_algorithm;
|
|
adapter->class = I2C_CLASS_HWMON;
|
|
adapter->dev.parent = &pdev->dev;
|
|
|
|
platform_set_drvdata(pdev, adapter);
|
|
|
|
adapter->nr = pdev->id;
|
|
rc = i2c_add_numbered_adapter(adapter);
|
|
if (rc) {
|
|
dev_err(&pdev->dev, "Adapter '%s' registration failed\n",
|
|
adapter->name);
|
|
goto fail_add_adapter;
|
|
}
|
|
|
|
dev_info(&pdev->dev, "PKUnity v3 i2c bus adapter.\n");
|
|
return 0;
|
|
|
|
fail_add_adapter:
|
|
kfree(adapter);
|
|
fail_nomem:
|
|
release_mem_region(mem->start, resource_size(mem));
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int puv3_i2c_remove(struct platform_device *pdev)
|
|
{
|
|
struct i2c_adapter *adapter = platform_get_drvdata(pdev);
|
|
struct resource *mem;
|
|
|
|
i2c_del_adapter(adapter);
|
|
|
|
put_device(&pdev->dev);
|
|
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
release_mem_region(mem->start, resource_size(mem));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
static int puv3_i2c_suspend(struct device *dev)
|
|
{
|
|
int poll_count;
|
|
/* Disable the IIC */
|
|
writel(I2C_ENABLE_DISABLE, I2C_ENABLE);
|
|
for (poll_count = 0; poll_count < 50; poll_count++) {
|
|
if (readl(I2C_ENSTATUS) & I2C_ENSTATUS_ENABLE)
|
|
udelay(25);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static SIMPLE_DEV_PM_OPS(puv3_i2c_pm, puv3_i2c_suspend, NULL);
|
|
#define PUV3_I2C_PM (&puv3_i2c_pm)
|
|
|
|
#else
|
|
#define PUV3_I2C_PM NULL
|
|
#endif
|
|
|
|
static struct platform_driver puv3_i2c_driver = {
|
|
.probe = puv3_i2c_probe,
|
|
.remove = puv3_i2c_remove,
|
|
.driver = {
|
|
.name = "PKUnity-v3-I2C",
|
|
.owner = THIS_MODULE,
|
|
.pm = PUV3_I2C_PM,
|
|
}
|
|
};
|
|
|
|
module_platform_driver(puv3_i2c_driver);
|
|
|
|
MODULE_DESCRIPTION("PKUnity v3 I2C driver");
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_ALIAS("platform:puv3_i2c");
|