![Lars-Peter Clausen](/assets/img/avatar_default.png)
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>
480 lines
12 KiB
C
480 lines
12 KiB
C
/*
|
|
* Nano River Technologies viperboard i2c master driver
|
|
*
|
|
* (C) 2012 by Lemonage GmbH
|
|
* Author: Lars Poeschel <poeschel@lemonage.de>
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/types.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/usb.h>
|
|
#include <linux/i2c.h>
|
|
|
|
#include <linux/mfd/viperboard.h>
|
|
|
|
struct vprbrd_i2c {
|
|
struct i2c_adapter i2c;
|
|
u8 bus_freq_param;
|
|
};
|
|
|
|
/* i2c bus frequency module parameter */
|
|
static u8 i2c_bus_param;
|
|
static unsigned int i2c_bus_freq = 100;
|
|
module_param(i2c_bus_freq, int, 0);
|
|
MODULE_PARM_DESC(i2c_bus_freq,
|
|
"i2c bus frequency in khz (default is 100) valid values: 10, 100, 200, 400, 1000, 3000, 6000");
|
|
|
|
static int vprbrd_i2c_status(struct i2c_adapter *i2c,
|
|
struct vprbrd_i2c_status *status, bool prev_error)
|
|
{
|
|
u16 bytes_xfer;
|
|
int ret;
|
|
struct vprbrd *vb = (struct vprbrd *)i2c->algo_data;
|
|
|
|
/* check for protocol error */
|
|
bytes_xfer = sizeof(struct vprbrd_i2c_status);
|
|
|
|
ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0),
|
|
VPRBRD_USB_REQUEST_I2C, VPRBRD_USB_TYPE_IN, 0x0000, 0x0000,
|
|
status, bytes_xfer, VPRBRD_USB_TIMEOUT_MS);
|
|
|
|
if (ret != bytes_xfer)
|
|
prev_error = true;
|
|
|
|
if (prev_error) {
|
|
dev_err(&i2c->dev, "failure in usb communication\n");
|
|
return -EREMOTEIO;
|
|
}
|
|
|
|
dev_dbg(&i2c->dev, " status = %d\n", status->status);
|
|
if (status->status != 0x00) {
|
|
dev_err(&i2c->dev, "failure: i2c protocol error\n");
|
|
return -EPROTO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int vprbrd_i2c_receive(struct usb_device *usb_dev,
|
|
struct vprbrd_i2c_read_msg *rmsg, int bytes_xfer)
|
|
{
|
|
int ret, bytes_actual;
|
|
int error = 0;
|
|
|
|
/* send the read request */
|
|
ret = usb_bulk_msg(usb_dev,
|
|
usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), rmsg,
|
|
sizeof(struct vprbrd_i2c_read_hdr), &bytes_actual,
|
|
VPRBRD_USB_TIMEOUT_MS);
|
|
|
|
if ((ret < 0)
|
|
|| (bytes_actual != sizeof(struct vprbrd_i2c_read_hdr))) {
|
|
dev_err(&usb_dev->dev, "failure transmitting usb\n");
|
|
error = -EREMOTEIO;
|
|
}
|
|
|
|
/* read the actual data */
|
|
ret = usb_bulk_msg(usb_dev,
|
|
usb_rcvbulkpipe(usb_dev, VPRBRD_EP_IN), rmsg,
|
|
bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS);
|
|
|
|
if ((ret < 0) || (bytes_xfer != bytes_actual)) {
|
|
dev_err(&usb_dev->dev, "failure receiving usb\n");
|
|
error = -EREMOTEIO;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
static int vprbrd_i2c_addr(struct usb_device *usb_dev,
|
|
struct vprbrd_i2c_addr_msg *amsg)
|
|
{
|
|
int ret, bytes_actual;
|
|
|
|
ret = usb_bulk_msg(usb_dev,
|
|
usb_sndbulkpipe(usb_dev, VPRBRD_EP_OUT), amsg,
|
|
sizeof(struct vprbrd_i2c_addr_msg), &bytes_actual,
|
|
VPRBRD_USB_TIMEOUT_MS);
|
|
|
|
if ((ret < 0) ||
|
|
(sizeof(struct vprbrd_i2c_addr_msg) != bytes_actual)) {
|
|
dev_err(&usb_dev->dev, "failure transmitting usb\n");
|
|
return -EREMOTEIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int vprbrd_i2c_read(struct vprbrd *vb, struct i2c_msg *msg)
|
|
{
|
|
int ret;
|
|
u16 remain_len, bytes_xfer, len1, len2,
|
|
start = 0x0000;
|
|
struct vprbrd_i2c_read_msg *rmsg =
|
|
(struct vprbrd_i2c_read_msg *)vb->buf;
|
|
|
|
remain_len = msg->len;
|
|
rmsg->header.cmd = VPRBRD_I2C_CMD_READ;
|
|
while (remain_len > 0) {
|
|
rmsg->header.addr = cpu_to_le16(start + 0x4000);
|
|
if (remain_len <= 255) {
|
|
len1 = remain_len;
|
|
len2 = 0x00;
|
|
rmsg->header.len0 = remain_len;
|
|
rmsg->header.len1 = 0x00;
|
|
rmsg->header.len2 = 0x00;
|
|
rmsg->header.len3 = 0x00;
|
|
rmsg->header.len4 = 0x00;
|
|
rmsg->header.len5 = 0x00;
|
|
remain_len = 0;
|
|
} else if (remain_len <= 510) {
|
|
len1 = remain_len;
|
|
len2 = 0x00;
|
|
rmsg->header.len0 = remain_len - 255;
|
|
rmsg->header.len1 = 0xff;
|
|
rmsg->header.len2 = 0x00;
|
|
rmsg->header.len3 = 0x00;
|
|
rmsg->header.len4 = 0x00;
|
|
rmsg->header.len5 = 0x00;
|
|
remain_len = 0;
|
|
} else if (remain_len <= 512) {
|
|
len1 = remain_len;
|
|
len2 = 0x00;
|
|
rmsg->header.len0 = remain_len - 510;
|
|
rmsg->header.len1 = 0xff;
|
|
rmsg->header.len2 = 0xff;
|
|
rmsg->header.len3 = 0x00;
|
|
rmsg->header.len4 = 0x00;
|
|
rmsg->header.len5 = 0x00;
|
|
remain_len = 0;
|
|
} else if (remain_len <= 767) {
|
|
len1 = 512;
|
|
len2 = remain_len - 512;
|
|
rmsg->header.len0 = 0x02;
|
|
rmsg->header.len1 = 0xff;
|
|
rmsg->header.len2 = 0xff;
|
|
rmsg->header.len3 = remain_len - 512;
|
|
rmsg->header.len4 = 0x00;
|
|
rmsg->header.len5 = 0x00;
|
|
bytes_xfer = remain_len;
|
|
remain_len = 0;
|
|
} else if (remain_len <= 1022) {
|
|
len1 = 512;
|
|
len2 = remain_len - 512;
|
|
rmsg->header.len0 = 0x02;
|
|
rmsg->header.len1 = 0xff;
|
|
rmsg->header.len2 = 0xff;
|
|
rmsg->header.len3 = remain_len - 767;
|
|
rmsg->header.len4 = 0xff;
|
|
rmsg->header.len5 = 0x00;
|
|
remain_len = 0;
|
|
} else if (remain_len <= 1024) {
|
|
len1 = 512;
|
|
len2 = remain_len - 512;
|
|
rmsg->header.len0 = 0x02;
|
|
rmsg->header.len1 = 0xff;
|
|
rmsg->header.len2 = 0xff;
|
|
rmsg->header.len3 = remain_len - 1022;
|
|
rmsg->header.len4 = 0xff;
|
|
rmsg->header.len5 = 0xff;
|
|
remain_len = 0;
|
|
} else {
|
|
len1 = 512;
|
|
len2 = 512;
|
|
rmsg->header.len0 = 0x02;
|
|
rmsg->header.len1 = 0xff;
|
|
rmsg->header.len2 = 0xff;
|
|
rmsg->header.len3 = 0x02;
|
|
rmsg->header.len4 = 0xff;
|
|
rmsg->header.len5 = 0xff;
|
|
remain_len -= 1024;
|
|
start += 1024;
|
|
}
|
|
rmsg->header.tf1 = cpu_to_le16(len1);
|
|
rmsg->header.tf2 = cpu_to_le16(len2);
|
|
|
|
/* first read transfer */
|
|
ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len1);
|
|
if (ret < 0)
|
|
return ret;
|
|
/* copy the received data */
|
|
memcpy(msg->buf + start, rmsg, len1);
|
|
|
|
/* second read transfer if neccessary */
|
|
if (len2 > 0) {
|
|
ret = vprbrd_i2c_receive(vb->usb_dev, rmsg, len2);
|
|
if (ret < 0)
|
|
return ret;
|
|
/* copy the received data */
|
|
memcpy(msg->buf + start + 512, rmsg, len2);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int vprbrd_i2c_write(struct vprbrd *vb, struct i2c_msg *msg)
|
|
{
|
|
int ret, bytes_actual;
|
|
u16 remain_len, bytes_xfer,
|
|
start = 0x0000;
|
|
struct vprbrd_i2c_write_msg *wmsg =
|
|
(struct vprbrd_i2c_write_msg *)vb->buf;
|
|
|
|
remain_len = msg->len;
|
|
wmsg->header.cmd = VPRBRD_I2C_CMD_WRITE;
|
|
wmsg->header.last = 0x00;
|
|
wmsg->header.chan = 0x00;
|
|
wmsg->header.spi = 0x0000;
|
|
while (remain_len > 0) {
|
|
wmsg->header.addr = cpu_to_le16(start + 0x4000);
|
|
if (remain_len > 503) {
|
|
wmsg->header.len1 = 0xff;
|
|
wmsg->header.len2 = 0xf8;
|
|
remain_len -= 503;
|
|
bytes_xfer = 503 + sizeof(struct vprbrd_i2c_write_hdr);
|
|
start += 503;
|
|
} else if (remain_len > 255) {
|
|
wmsg->header.len1 = 0xff;
|
|
wmsg->header.len2 = (remain_len - 255);
|
|
bytes_xfer = remain_len +
|
|
sizeof(struct vprbrd_i2c_write_hdr);
|
|
remain_len = 0;
|
|
} else {
|
|
wmsg->header.len1 = remain_len;
|
|
wmsg->header.len2 = 0x00;
|
|
bytes_xfer = remain_len +
|
|
sizeof(struct vprbrd_i2c_write_hdr);
|
|
remain_len = 0;
|
|
}
|
|
memcpy(wmsg->data, msg->buf + start,
|
|
bytes_xfer - sizeof(struct vprbrd_i2c_write_hdr));
|
|
|
|
ret = usb_bulk_msg(vb->usb_dev,
|
|
usb_sndbulkpipe(vb->usb_dev,
|
|
VPRBRD_EP_OUT), wmsg,
|
|
bytes_xfer, &bytes_actual, VPRBRD_USB_TIMEOUT_MS);
|
|
if ((ret < 0) || (bytes_xfer != bytes_actual))
|
|
return -EREMOTEIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int vprbrd_i2c_xfer(struct i2c_adapter *i2c, struct i2c_msg *msgs,
|
|
int num)
|
|
{
|
|
struct i2c_msg *pmsg;
|
|
int i, ret,
|
|
error = 0;
|
|
struct vprbrd *vb = (struct vprbrd *)i2c->algo_data;
|
|
struct vprbrd_i2c_addr_msg *amsg =
|
|
(struct vprbrd_i2c_addr_msg *)vb->buf;
|
|
struct vprbrd_i2c_status *smsg = (struct vprbrd_i2c_status *)vb->buf;
|
|
|
|
dev_dbg(&i2c->dev, "master xfer %d messages:\n", num);
|
|
|
|
for (i = 0 ; i < num ; i++) {
|
|
pmsg = &msgs[i];
|
|
|
|
dev_dbg(&i2c->dev,
|
|
" %d: %s (flags %d) %d bytes to 0x%02x\n",
|
|
i, pmsg->flags & I2C_M_RD ? "read" : "write",
|
|
pmsg->flags, pmsg->len, pmsg->addr);
|
|
|
|
/* msgs longer than 2048 bytes are not supported by adapter */
|
|
if (pmsg->len > 2048)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&vb->lock);
|
|
/* directly send the message */
|
|
if (pmsg->flags & I2C_M_RD) {
|
|
/* read data */
|
|
amsg->cmd = VPRBRD_I2C_CMD_ADDR;
|
|
amsg->unknown2 = 0x00;
|
|
amsg->unknown3 = 0x00;
|
|
amsg->addr = pmsg->addr;
|
|
amsg->unknown1 = 0x01;
|
|
amsg->len = cpu_to_le16(pmsg->len);
|
|
/* send the addr and len, we're interested to board */
|
|
ret = vprbrd_i2c_addr(vb->usb_dev, amsg);
|
|
if (ret < 0)
|
|
error = ret;
|
|
|
|
ret = vprbrd_i2c_read(vb, pmsg);
|
|
if (ret < 0)
|
|
error = ret;
|
|
|
|
ret = vprbrd_i2c_status(i2c, smsg, error);
|
|
if (ret < 0)
|
|
error = ret;
|
|
/* in case of protocol error, return the error */
|
|
if (error < 0)
|
|
goto error;
|
|
} else {
|
|
/* write data */
|
|
ret = vprbrd_i2c_write(vb, pmsg);
|
|
|
|
amsg->cmd = VPRBRD_I2C_CMD_ADDR;
|
|
amsg->unknown2 = 0x00;
|
|
amsg->unknown3 = 0x00;
|
|
amsg->addr = pmsg->addr;
|
|
amsg->unknown1 = 0x00;
|
|
amsg->len = cpu_to_le16(pmsg->len);
|
|
/* send the addr, the data goes to to board */
|
|
ret = vprbrd_i2c_addr(vb->usb_dev, amsg);
|
|
if (ret < 0)
|
|
error = ret;
|
|
|
|
ret = vprbrd_i2c_status(i2c, smsg, error);
|
|
if (ret < 0)
|
|
error = ret;
|
|
|
|
if (error < 0)
|
|
goto error;
|
|
}
|
|
mutex_unlock(&vb->lock);
|
|
}
|
|
return 0;
|
|
error:
|
|
mutex_unlock(&vb->lock);
|
|
return error;
|
|
}
|
|
|
|
static u32 vprbrd_i2c_func(struct i2c_adapter *i2c)
|
|
{
|
|
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
|
|
}
|
|
|
|
/* This is the actual algorithm we define */
|
|
static const struct i2c_algorithm vprbrd_algorithm = {
|
|
.master_xfer = vprbrd_i2c_xfer,
|
|
.functionality = vprbrd_i2c_func,
|
|
};
|
|
|
|
static int vprbrd_i2c_probe(struct platform_device *pdev)
|
|
{
|
|
struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
|
|
struct vprbrd_i2c *vb_i2c;
|
|
int ret;
|
|
int pipe;
|
|
|
|
vb_i2c = kzalloc(sizeof(*vb_i2c), GFP_KERNEL);
|
|
if (vb_i2c == NULL)
|
|
return -ENOMEM;
|
|
|
|
/* setup i2c adapter description */
|
|
vb_i2c->i2c.owner = THIS_MODULE;
|
|
vb_i2c->i2c.class = I2C_CLASS_HWMON;
|
|
vb_i2c->i2c.algo = &vprbrd_algorithm;
|
|
vb_i2c->i2c.algo_data = vb;
|
|
/* save the param in usb capabable memory */
|
|
vb_i2c->bus_freq_param = i2c_bus_param;
|
|
|
|
snprintf(vb_i2c->i2c.name, sizeof(vb_i2c->i2c.name),
|
|
"viperboard at bus %03d device %03d",
|
|
vb->usb_dev->bus->busnum, vb->usb_dev->devnum);
|
|
|
|
/* setting the bus frequency */
|
|
if ((i2c_bus_param <= VPRBRD_I2C_FREQ_10KHZ)
|
|
&& (i2c_bus_param >= VPRBRD_I2C_FREQ_6MHZ)) {
|
|
pipe = usb_sndctrlpipe(vb->usb_dev, 0);
|
|
ret = usb_control_msg(vb->usb_dev, pipe,
|
|
VPRBRD_USB_REQUEST_I2C_FREQ, VPRBRD_USB_TYPE_OUT,
|
|
0x0000, 0x0000, &vb_i2c->bus_freq_param, 1,
|
|
VPRBRD_USB_TIMEOUT_MS);
|
|
if (ret != 1) {
|
|
dev_err(&pdev->dev,
|
|
"failure setting i2c_bus_freq to %d\n", i2c_bus_freq);
|
|
ret = -EIO;
|
|
goto error;
|
|
}
|
|
} else {
|
|
dev_err(&pdev->dev,
|
|
"invalid i2c_bus_freq setting:%d\n", i2c_bus_freq);
|
|
ret = -EIO;
|
|
goto error;
|
|
}
|
|
|
|
vb_i2c->i2c.dev.parent = &pdev->dev;
|
|
|
|
/* attach to i2c layer */
|
|
i2c_add_adapter(&vb_i2c->i2c);
|
|
|
|
platform_set_drvdata(pdev, vb_i2c);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
kfree(vb_i2c);
|
|
return ret;
|
|
}
|
|
|
|
static int vprbrd_i2c_remove(struct platform_device *pdev)
|
|
{
|
|
struct vprbrd_i2c *vb_i2c = platform_get_drvdata(pdev);
|
|
|
|
i2c_del_adapter(&vb_i2c->i2c);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver vprbrd_i2c_driver = {
|
|
.driver.name = "viperboard-i2c",
|
|
.driver.owner = THIS_MODULE,
|
|
.probe = vprbrd_i2c_probe,
|
|
.remove = vprbrd_i2c_remove,
|
|
};
|
|
|
|
static int __init vprbrd_i2c_init(void)
|
|
{
|
|
switch (i2c_bus_freq) {
|
|
case 6000:
|
|
i2c_bus_param = VPRBRD_I2C_FREQ_6MHZ;
|
|
break;
|
|
case 3000:
|
|
i2c_bus_param = VPRBRD_I2C_FREQ_3MHZ;
|
|
break;
|
|
case 1000:
|
|
i2c_bus_param = VPRBRD_I2C_FREQ_1MHZ;
|
|
break;
|
|
case 400:
|
|
i2c_bus_param = VPRBRD_I2C_FREQ_400KHZ;
|
|
break;
|
|
case 200:
|
|
i2c_bus_param = VPRBRD_I2C_FREQ_200KHZ;
|
|
break;
|
|
case 100:
|
|
i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ;
|
|
break;
|
|
case 10:
|
|
i2c_bus_param = VPRBRD_I2C_FREQ_10KHZ;
|
|
break;
|
|
default:
|
|
pr_warn("invalid i2c_bus_freq (%d)\n", i2c_bus_freq);
|
|
i2c_bus_param = VPRBRD_I2C_FREQ_100KHZ;
|
|
}
|
|
|
|
return platform_driver_register(&vprbrd_i2c_driver);
|
|
}
|
|
subsys_initcall(vprbrd_i2c_init);
|
|
|
|
static void __exit vprbrd_i2c_exit(void)
|
|
{
|
|
platform_driver_unregister(&vprbrd_i2c_driver);
|
|
}
|
|
module_exit(vprbrd_i2c_exit);
|
|
|
|
MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
|
|
MODULE_DESCRIPTION("I2C master driver for Nano River Techs Viperboard");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("platform:viperboard-i2c");
|