diff options
Diffstat (limited to 'drivers/i2c')
| -rw-r--r-- | drivers/i2c/busses/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-dln2.c | 262 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-hix5hd2.c | 2 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-nomadik.c | 2 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-omap.c | 2 | ||||
| -rw-r--r-- | drivers/i2c/i2c-core.c | 1 | 
7 files changed, 276 insertions, 4 deletions
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 917c3585f45b..b4d135cc2f39 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -881,6 +881,16 @@ config I2C_DIOLAN_U2C  	  This driver can also be built as a module.  If so, the module  	  will be called i2c-diolan-u2c. +config I2C_DLN2 +       tristate "Diolan DLN-2 USB I2C adapter" +       depends on MFD_DLN2 +       help +	 If you say yes to this option, support will be included for Diolan +	 DLN2, a USB to I2C interface. + +	 This driver can also be built as a module.  If so, the module +	 will be called i2c-dln2. +  config I2C_PARPORT  	tristate "Parallel port adapter"  	depends on PARPORT diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 78d56c54ba2b..cdac7f15eab5 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -87,6 +87,7 @@ obj-$(CONFIG_I2C_RCAR)		+= i2c-rcar.o  # External I2C/SMBus adapter drivers  obj-$(CONFIG_I2C_DIOLAN_U2C)	+= i2c-diolan-u2c.o +obj-$(CONFIG_I2C_DLN2)		+= i2c-dln2.o  obj-$(CONFIG_I2C_PARPORT)	+= i2c-parport.o  obj-$(CONFIG_I2C_PARPORT_LIGHT)	+= i2c-parport-light.o  obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF)	+= i2c-robotfuzz-osif.o diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c new file mode 100644 index 000000000000..b3fb86af4cbb --- /dev/null +++ b/drivers/i2c/busses/i2c-dln2.c @@ -0,0 +1,262 @@ +/* + * Driver for the Diolan DLN-2 USB-I2C adapter + * + * Copyright (c) 2014 Intel Corporation + * + * Derived from: + *  i2c-diolan-u2c.c + *  Copyright (c) 2010-2011 Ericsson AB + * + * 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, version 2. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/mfd/dln2.h> + +#define DLN2_I2C_MODULE_ID		0x03 +#define DLN2_I2C_CMD(cmd)		DLN2_CMD(cmd, DLN2_I2C_MODULE_ID) + +/* I2C commands */ +#define DLN2_I2C_GET_PORT_COUNT		DLN2_I2C_CMD(0x00) +#define DLN2_I2C_ENABLE			DLN2_I2C_CMD(0x01) +#define DLN2_I2C_DISABLE		DLN2_I2C_CMD(0x02) +#define DLN2_I2C_IS_ENABLED		DLN2_I2C_CMD(0x03) +#define DLN2_I2C_WRITE			DLN2_I2C_CMD(0x06) +#define DLN2_I2C_READ			DLN2_I2C_CMD(0x07) +#define DLN2_I2C_SCAN_DEVICES		DLN2_I2C_CMD(0x08) +#define DLN2_I2C_PULLUP_ENABLE		DLN2_I2C_CMD(0x09) +#define DLN2_I2C_PULLUP_DISABLE		DLN2_I2C_CMD(0x0A) +#define DLN2_I2C_PULLUP_IS_ENABLED	DLN2_I2C_CMD(0x0B) +#define DLN2_I2C_TRANSFER		DLN2_I2C_CMD(0x0C) +#define DLN2_I2C_SET_MAX_REPLY_COUNT	DLN2_I2C_CMD(0x0D) +#define DLN2_I2C_GET_MAX_REPLY_COUNT	DLN2_I2C_CMD(0x0E) + +#define DLN2_I2C_MAX_XFER_SIZE		256 +#define DLN2_I2C_BUF_SIZE		(DLN2_I2C_MAX_XFER_SIZE + 16) + +struct dln2_i2c { +	struct platform_device *pdev; +	struct i2c_adapter adapter; +	u8 port; +	/* +	 * Buffer to hold the packet for read or write transfers. One is enough +	 * since we can't have multiple transfers in parallel on the i2c bus. +	 */ +	void *buf; +}; + +static int dln2_i2c_enable(struct dln2_i2c *dln2, bool enable) +{ +	u16 cmd; +	struct { +		u8 port; +	} tx; + +	tx.port = dln2->port; + +	if (enable) +		cmd = DLN2_I2C_ENABLE; +	else +		cmd = DLN2_I2C_DISABLE; + +	return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx)); +} + +static int dln2_i2c_write(struct dln2_i2c *dln2, u8 addr, +			  u8 *data, u16 data_len) +{ +	int ret; +	struct { +		u8 port; +		u8 addr; +		u8 mem_addr_len; +		__le32 mem_addr; +		__le16 buf_len; +		u8 buf[DLN2_I2C_MAX_XFER_SIZE]; +	} __packed *tx = dln2->buf; +	unsigned len; + +	BUILD_BUG_ON(sizeof(*tx) > DLN2_I2C_BUF_SIZE); + +	tx->port = dln2->port; +	tx->addr = addr; +	tx->mem_addr_len = 0; +	tx->mem_addr = 0; +	tx->buf_len = cpu_to_le16(data_len); +	memcpy(tx->buf, data, data_len); + +	len = sizeof(*tx) + data_len - DLN2_I2C_MAX_XFER_SIZE; +	ret = dln2_transfer_tx(dln2->pdev, DLN2_I2C_WRITE, tx, len); +	if (ret < 0) +		return ret; + +	return data_len; +} + +static int dln2_i2c_read(struct dln2_i2c *dln2, u16 addr, u8 *data, +			 u16 data_len) +{ +	int ret; +	struct { +		u8 port; +		u8 addr; +		u8 mem_addr_len; +		__le32 mem_addr; +		__le16 buf_len; +	} __packed tx; +	struct { +		__le16 buf_len; +		u8 buf[DLN2_I2C_MAX_XFER_SIZE]; +	} __packed *rx = dln2->buf; +	unsigned rx_len = sizeof(*rx); + +	BUILD_BUG_ON(sizeof(*rx) > DLN2_I2C_BUF_SIZE); + +	tx.port = dln2->port; +	tx.addr = addr; +	tx.mem_addr_len = 0; +	tx.mem_addr = 0; +	tx.buf_len = cpu_to_le16(data_len); + +	ret = dln2_transfer(dln2->pdev, DLN2_I2C_READ, &tx, sizeof(tx), +			    rx, &rx_len); +	if (ret < 0) +		return ret; +	if (rx_len < sizeof(rx->buf_len) + data_len) +		return -EPROTO; +	if (le16_to_cpu(rx->buf_len) != data_len) +		return -EPROTO; + +	memcpy(data, rx->buf, data_len); + +	return data_len; +} + +static int dln2_i2c_xfer(struct i2c_adapter *adapter, +			 struct i2c_msg *msgs, int num) +{ +	struct dln2_i2c *dln2 = i2c_get_adapdata(adapter); +	struct i2c_msg *pmsg; +	struct device *dev = &dln2->adapter.dev; +	int i; + +	for (i = 0; i < num; i++) { +		int ret; + +		pmsg = &msgs[i]; + +		if (pmsg->len > DLN2_I2C_MAX_XFER_SIZE) { +			dev_warn(dev, "maximum transfer size exceeded\n"); +			return -EOPNOTSUPP; +		} + +		if (pmsg->flags & I2C_M_RD) { +			ret = dln2_i2c_read(dln2, pmsg->addr, pmsg->buf, +					    pmsg->len); +			if (ret < 0) +				return ret; + +			pmsg->len = ret; +		} else { +			ret = dln2_i2c_write(dln2, pmsg->addr, pmsg->buf, +					     pmsg->len); +			if (ret != pmsg->len) +				return -EPROTO; +		} +	} + +	return num; +} + +static u32 dln2_i2c_func(struct i2c_adapter *a) +{ +	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | +		I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | +		I2C_FUNC_SMBUS_I2C_BLOCK; +} + +static const struct i2c_algorithm dln2_i2c_usb_algorithm = { +	.master_xfer = dln2_i2c_xfer, +	.functionality = dln2_i2c_func, +}; + +static int dln2_i2c_probe(struct platform_device *pdev) +{ +	int ret; +	struct dln2_i2c *dln2; +	struct device *dev = &pdev->dev; +	struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); + +	dln2 = devm_kzalloc(dev, sizeof(*dln2), GFP_KERNEL); +	if (!dln2) +		return -ENOMEM; + +	dln2->buf = devm_kmalloc(dev, DLN2_I2C_BUF_SIZE, GFP_KERNEL); +	if (!dln2->buf) +		return -ENOMEM; + +	dln2->pdev = pdev; +	dln2->port = pdata->port; + +	/* setup i2c adapter description */ +	dln2->adapter.owner = THIS_MODULE; +	dln2->adapter.class = I2C_CLASS_HWMON; +	dln2->adapter.algo = &dln2_i2c_usb_algorithm; +	dln2->adapter.dev.parent = dev; +	i2c_set_adapdata(&dln2->adapter, dln2); +	snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d", +		 "dln2-i2c", dev_name(pdev->dev.parent), dln2->port); + +	platform_set_drvdata(pdev, dln2); + +	/* initialize the i2c interface */ +	ret = dln2_i2c_enable(dln2, true); +	if (ret < 0) { +		dev_err(dev, "failed to initialize adapter: %d\n", ret); +		return ret; +	} + +	/* and finally attach to i2c layer */ +	ret = i2c_add_adapter(&dln2->adapter); +	if (ret < 0) { +		dev_err(dev, "failed to add I2C adapter: %d\n", ret); +		goto out_disable; +	} + +	return 0; + +out_disable: +	dln2_i2c_enable(dln2, false); + +	return ret; +} + +static int dln2_i2c_remove(struct platform_device *pdev) +{ +	struct dln2_i2c *dln2 = platform_get_drvdata(pdev); + +	i2c_del_adapter(&dln2->adapter); +	dln2_i2c_enable(dln2, false); + +	return 0; +} + +static struct platform_driver dln2_i2c_driver = { +	.driver.name	= "dln2-i2c", +	.probe		= dln2_i2c_probe, +	.remove		= dln2_i2c_remove, +}; + +module_platform_driver(dln2_i2c_driver); + +MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); +MODULE_DESCRIPTION("Driver for the Diolan DLN2 I2C master interface"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dln2-i2c"); diff --git a/drivers/i2c/busses/i2c-hix5hd2.c b/drivers/i2c/busses/i2c-hix5hd2.c index 9490d0f4255c..8fe78d08e01c 100644 --- a/drivers/i2c/busses/i2c-hix5hd2.c +++ b/drivers/i2c/busses/i2c-hix5hd2.c @@ -528,7 +528,7 @@ static int hix5hd2_i2c_runtime_resume(struct device *dev)  #endif  static const struct dev_pm_ops hix5hd2_i2c_pm_ops = { -	SET_PM_RUNTIME_PM_OPS(hix5hd2_i2c_runtime_suspend, +	SET_RUNTIME_PM_OPS(hix5hd2_i2c_runtime_suspend,  			      hix5hd2_i2c_runtime_resume,  			      NULL)  }; diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 9ad038d223c4..97998946c4f6 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -932,7 +932,7 @@ static int nmk_i2c_runtime_resume(struct device *dev)  static const struct dev_pm_ops nmk_i2c_pm = {  	SET_LATE_SYSTEM_SLEEP_PM_OPS(nmk_i2c_suspend_late, nmk_i2c_resume_early) -	SET_PM_RUNTIME_PM_OPS(nmk_i2c_runtime_suspend, +	SET_RUNTIME_PM_OPS(nmk_i2c_runtime_suspend,  			nmk_i2c_runtime_resume,  			NULL)  }; diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 277a2288d4a8..0e650a0d0ad0 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -1280,7 +1280,6 @@ static int omap_i2c_remove(struct platform_device *pdev)  }  #ifdef CONFIG_PM -#ifdef CONFIG_PM_RUNTIME  static int omap_i2c_runtime_suspend(struct device *dev)  {  	struct platform_device *pdev = to_platform_device(dev); @@ -1318,7 +1317,6 @@ static int omap_i2c_runtime_resume(struct device *dev)  	return 0;  } -#endif /* CONFIG_PM_RUNTIME */  static struct dev_pm_ops omap_i2c_pm_ops = {  	SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend, diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index f43b4e11647a..68aeb8eedae0 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -403,6 +403,7 @@ static int acpi_i2c_install_space_handler(struct i2c_adapter *adapter)  		return -ENOMEM;  	} +	acpi_walk_dep_device_list(handle);  	return 0;  }  | 
