mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
Merge branch 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: "I2C has a smaller pull reuest this time: - new driver for I2C virtio - removal of PMC SMP driver because platform is already gone - IRQ probing and DMAENGINE API cleanups - add SI metric prefix definitions to units.h - beginning of i801 refactorization - a few driver improvements" * 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (28 commits) i2c: cadence: Implement save restore i2c: xlp9xx: fix main IRQ check i2c: mt65xx: fix IRQ check i2c: virtio: add a virtio i2c frontend driver i2c: hix5hd2: fix IRQ check i2c: s3c2410: fix IRQ check i2c: iop3xx: fix deferred probing i2c: synquacer: fix deferred probing i2c: sun6i-pw2i: Prefer strscpy over strlcpy i2c: remove dead PMC MSP TWI/SMBus/I2C driver i2c: dev: Use sysfs_emit() in "show" functions i2c: dev: Define pr_fmt() and drop duplication substrings i2c: designware: Fix indentation in the header i2c: designware: Use DIV_ROUND_CLOSEST() macro units: Add SI metric prefix definitions i2c: at91: mark PM ops as __maybe unused i2c: sh_mobile: : use proper DMAENGINE API for termination i2c: qup: : use proper DMAENGINE API for termination i2c: mxs: : use proper DMAENGINE API for termination i2c: imx: : use proper DMAENGINE API for termination ...
This commit is contained in:
commit
871dda463c
@ -19736,6 +19736,15 @@ S: Maintained
|
||||
F: include/uapi/linux/virtio_snd.h
|
||||
F: sound/virtio/*
|
||||
|
||||
VIRTIO I2C DRIVER
|
||||
M: Jie Deng <jie.deng@intel.com>
|
||||
M: Viresh Kumar <viresh.kumar@linaro.org>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
L: virtualization@lists.linux-foundation.org
|
||||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-virtio.c
|
||||
F: include/uapi/linux/virtio_i2c.h
|
||||
|
||||
VIRTUAL BOX GUEST DEVICE DRIVER
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
M: Arnd Bergmann <arnd@arndb.de>
|
||||
|
@ -866,15 +866,6 @@ config I2C_PCA_PLATFORM
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-pca-platform.
|
||||
|
||||
config I2C_PMCMSP
|
||||
tristate "PMC MSP I2C TWI Controller"
|
||||
depends on PMC_MSP || COMPILE_TEST
|
||||
help
|
||||
This driver supports the PMC TWI controller on MSP devices.
|
||||
|
||||
This driver can also be built as module. If so, the module
|
||||
will be called i2c-pmcmsp.
|
||||
|
||||
config I2C_PNX
|
||||
tristate "I2C bus support for Philips PNX and NXP LPC targets"
|
||||
depends on ARCH_LPC32XX || COMPILE_TEST
|
||||
@ -1402,4 +1393,15 @@ config I2C_FSI
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called as i2c-fsi.
|
||||
|
||||
config I2C_VIRTIO
|
||||
tristate "Virtio I2C Adapter"
|
||||
select VIRTIO
|
||||
help
|
||||
If you say yes to this option, support will be included for the virtio
|
||||
I2C adapter driver. The hardware can be emulated by any device model
|
||||
software according to the virtio protocol.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called i2c-virtio.
|
||||
|
||||
endmenu
|
||||
|
@ -86,7 +86,6 @@ obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
|
||||
obj-$(CONFIG_I2C_OWL) += i2c-owl.o
|
||||
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
|
||||
obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o
|
||||
obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o
|
||||
obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
|
||||
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
|
||||
obj-$(CONFIG_I2C_PXA_PCI) += i2c-pxa-pci.o
|
||||
@ -146,5 +145,6 @@ obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
|
||||
obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
|
||||
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
|
||||
obj-$(CONFIG_I2C_FSI) += i2c-fsi.o
|
||||
obj-$(CONFIG_I2C_VIRTIO) += i2c-virtio.o
|
||||
|
||||
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
|
||||
|
@ -286,9 +286,7 @@ static int at91_twi_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int at91_twi_runtime_suspend(struct device *dev)
|
||||
static int __maybe_unused at91_twi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
||||
|
||||
@ -299,7 +297,7 @@ static int at91_twi_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_twi_runtime_resume(struct device *dev)
|
||||
static int __maybe_unused at91_twi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
||||
|
||||
@ -308,7 +306,7 @@ static int at91_twi_runtime_resume(struct device *dev)
|
||||
return clk_prepare_enable(twi_dev->clk);
|
||||
}
|
||||
|
||||
static int at91_twi_suspend_noirq(struct device *dev)
|
||||
static int __maybe_unused at91_twi_suspend_noirq(struct device *dev)
|
||||
{
|
||||
if (!pm_runtime_status_suspended(dev))
|
||||
at91_twi_runtime_suspend(dev);
|
||||
@ -316,7 +314,7 @@ static int at91_twi_suspend_noirq(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int at91_twi_resume_noirq(struct device *dev)
|
||||
static int __maybe_unused at91_twi_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
@ -335,18 +333,13 @@ static int at91_twi_resume_noirq(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops at91_twi_pm = {
|
||||
static const struct dev_pm_ops __maybe_unused at91_twi_pm = {
|
||||
.suspend_noirq = at91_twi_suspend_noirq,
|
||||
.resume_noirq = at91_twi_resume_noirq,
|
||||
.runtime_suspend = at91_twi_runtime_suspend,
|
||||
.runtime_resume = at91_twi_runtime_resume,
|
||||
};
|
||||
|
||||
#define at91_twi_pm_ops (&at91_twi_pm)
|
||||
#else
|
||||
#define at91_twi_pm_ops NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver at91_twi_driver = {
|
||||
.probe = at91_twi_probe,
|
||||
.remove = at91_twi_remove,
|
||||
@ -354,7 +347,7 @@ static struct platform_driver at91_twi_driver = {
|
||||
.driver = {
|
||||
.name = "at91_i2c",
|
||||
.of_match_table = of_match_ptr(atmel_twi_dt_ids),
|
||||
.pm = at91_twi_pm_ops,
|
||||
.pm = pm_ptr(&at91_twi_pm),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -138,9 +138,9 @@ static void at91_twi_dma_cleanup(struct at91_twi_dev *dev)
|
||||
|
||||
if (dma->xfer_in_progress) {
|
||||
if (dma->direction == DMA_FROM_DEVICE)
|
||||
dmaengine_terminate_all(dma->chan_rx);
|
||||
dmaengine_terminate_sync(dma->chan_rx);
|
||||
else
|
||||
dmaengine_terminate_all(dma->chan_tx);
|
||||
dmaengine_terminate_sync(dma->chan_tx);
|
||||
dma->xfer_in_progress = false;
|
||||
}
|
||||
if (dma->buf_mapped) {
|
||||
|
@ -178,6 +178,7 @@ enum cdns_i2c_slave_state {
|
||||
* @clk: Pointer to struct clk
|
||||
* @clk_rate_change_nb: Notifier block for clock rate changes
|
||||
* @quirks: flag for broken hold bit usage in r1p10
|
||||
* @ctrl_reg: Cached value of the control register.
|
||||
* @ctrl_reg_diva_divb: value of fields DIV_A and DIV_B from CR register
|
||||
* @slave: Registered slave instance.
|
||||
* @dev_mode: I2C operating role(master/slave).
|
||||
@ -202,6 +203,7 @@ struct cdns_i2c {
|
||||
struct clk *clk;
|
||||
struct notifier_block clk_rate_change_nb;
|
||||
u32 quirks;
|
||||
u32 ctrl_reg;
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
u16 ctrl_reg_diva_divb;
|
||||
struct i2c_client *slave;
|
||||
@ -1071,10 +1073,11 @@ static int cdns_i2c_setclk(unsigned long clk_in, struct cdns_i2c *id)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
|
||||
ctrl_reg = id->ctrl_reg;
|
||||
ctrl_reg &= ~(CDNS_I2C_CR_DIVA_MASK | CDNS_I2C_CR_DIVB_MASK);
|
||||
ctrl_reg |= ((div_a << CDNS_I2C_CR_DIVA_SHIFT) |
|
||||
(div_b << CDNS_I2C_CR_DIVB_SHIFT));
|
||||
id->ctrl_reg = ctrl_reg;
|
||||
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
|
||||
#if IS_ENABLED(CONFIG_I2C_SLAVE)
|
||||
id->ctrl_reg_diva_divb = ctrl_reg & (CDNS_I2C_CR_DIVA_MASK |
|
||||
@ -1162,6 +1165,26 @@ static int __maybe_unused cdns_i2c_runtime_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_init - Controller initialisation
|
||||
* @id: Device private data structure
|
||||
*
|
||||
* Initialise the i2c controller.
|
||||
*
|
||||
*/
|
||||
static void cdns_i2c_init(struct cdns_i2c *id)
|
||||
{
|
||||
cdns_i2c_writereg(id->ctrl_reg, CDNS_I2C_CR_OFFSET);
|
||||
/*
|
||||
* Cadence I2C controller has a bug wherein it generates
|
||||
* invalid read transaction after HW timeout in master receiver mode.
|
||||
* HW timeout is not used by this driver and the interrupt is disabled.
|
||||
* But the feature itself cannot be disabled. Hence maximum value
|
||||
* is written to this register to reduce the chances of error.
|
||||
*/
|
||||
cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* cdns_i2c_runtime_resume - Runtime resume
|
||||
* @dev: Address of the platform_device structure
|
||||
@ -1180,6 +1203,7 @@ static int __maybe_unused cdns_i2c_runtime_resume(struct device *dev)
|
||||
dev_err(dev, "Cannot enable clock.\n");
|
||||
return ret;
|
||||
}
|
||||
cdns_i2c_init(xi2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1279,7 +1303,7 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
||||
id->dev_mode = CDNS_I2C_MODE_MASTER;
|
||||
id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
|
||||
#endif
|
||||
cdns_i2c_writereg(CDNS_I2C_CR_MASTER_EN_MASK, CDNS_I2C_CR_OFFSET);
|
||||
id->ctrl_reg = CDNS_I2C_CR_ACK_EN | CDNS_I2C_CR_NEA | CDNS_I2C_CR_MS;
|
||||
|
||||
ret = cdns_i2c_setclk(id->input_clk, id);
|
||||
if (ret) {
|
||||
@ -1294,15 +1318,7 @@ static int cdns_i2c_probe(struct platform_device *pdev)
|
||||
dev_err(&pdev->dev, "cannot get irq %d\n", id->irq);
|
||||
goto err_clk_dis;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cadence I2C controller has a bug wherein it generates
|
||||
* invalid read transaction after HW timeout in master receiver mode.
|
||||
* HW timeout is not used by this driver and the interrupt is disabled.
|
||||
* But the feature itself cannot be disabled. Hence maximum value
|
||||
* is written to this register to reduce the chances of error.
|
||||
*/
|
||||
cdns_i2c_writereg(CDNS_I2C_TIMEOUT_MAX, CDNS_I2C_TIME_OUT_OFFSET);
|
||||
cdns_i2c_init(id);
|
||||
|
||||
ret = i2c_add_adapter(&id->adap);
|
||||
if (ret < 0)
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/swab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
@ -350,7 +351,7 @@ u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||
*
|
||||
* If your hardware is free from tHD;STA issue, try this one.
|
||||
*/
|
||||
return (ic_clk * tSYMBOL + 500000) / 1000000 - 8 + offset;
|
||||
return DIV_ROUND_CLOSEST(ic_clk * tSYMBOL, MICRO) - 8 + offset;
|
||||
else
|
||||
/*
|
||||
* Conditional expression:
|
||||
@ -366,8 +367,7 @@ u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
|
||||
* The reason why we need to take into account "tf" here,
|
||||
* is the same as described in i2c_dw_scl_lcnt().
|
||||
*/
|
||||
return (ic_clk * (tSYMBOL + tf) + 500000) / 1000000
|
||||
- 3 + offset;
|
||||
return DIV_ROUND_CLOSEST(ic_clk * (tSYMBOL + tf), MICRO) - 3 + offset;
|
||||
}
|
||||
|
||||
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
|
||||
@ -383,7 +383,7 @@ u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
|
||||
* account the fall time of SCL signal (tf). Default tf value
|
||||
* should be 0.3 us, for safety.
|
||||
*/
|
||||
return ((ic_clk * (tLOW + tf) + 500000) / 1000000) - 1 + offset;
|
||||
return DIV_ROUND_CLOSEST(ic_clk * (tLOW + tf), MICRO) - 1 + offset;
|
||||
}
|
||||
|
||||
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
|
||||
|
@ -117,7 +117,7 @@
|
||||
|
||||
#define DW_IC_ERR_TX_ABRT 0x1
|
||||
|
||||
#define DW_IC_TAR_10BITADDR_MASTER BIT(12)
|
||||
#define DW_IC_TAR_10BITADDR_MASTER BIT(12)
|
||||
|
||||
#define DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3))
|
||||
#define DW_IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2)
|
||||
@ -245,7 +245,7 @@ struct dw_i2c_dev {
|
||||
struct clk *clk;
|
||||
struct clk *pclk;
|
||||
struct reset_control *rst;
|
||||
struct i2c_client *slave;
|
||||
struct i2c_client *slave;
|
||||
u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev);
|
||||
int cmd_err;
|
||||
struct i2c_msg *msgs;
|
||||
|
@ -31,12 +31,13 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include "i2c-designware-core.h"
|
||||
|
||||
static u32 i2c_dw_get_clk_rate_khz(struct dw_i2c_dev *dev)
|
||||
{
|
||||
return clk_get_rate(dev->clk)/1000;
|
||||
return clk_get_rate(dev->clk) / KILO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
@ -270,7 +271,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
|
||||
|
||||
if (!dev->sda_hold_time && t->sda_hold_ns)
|
||||
dev->sda_hold_time =
|
||||
div_u64(clk_khz * t->sda_hold_ns + 500000, 1000000);
|
||||
DIV_S64_ROUND_CLOSEST(clk_khz * t->sda_hold_ns, MICRO);
|
||||
}
|
||||
|
||||
adap = &dev->adapter;
|
||||
|
@ -379,7 +379,7 @@ static int highlander_i2c_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
||||
dev->irq = platform_get_irq(pdev, 0);
|
||||
if (iic_force_poll)
|
||||
if (dev->irq < 0 || iic_force_poll)
|
||||
dev->irq = 0;
|
||||
|
||||
if (dev->irq) {
|
||||
|
@ -413,7 +413,7 @@ static int hix5hd2_i2c_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(priv->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0)
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
|
@ -110,6 +110,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/itco_wdt.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C_MUX_GPIO) && defined CONFIG_DMI
|
||||
#include <linux/gpio/machine.h>
|
||||
@ -503,19 +504,16 @@ static int i801_transaction(struct i801_priv *priv, int xact)
|
||||
|
||||
static int i801_block_transaction_by_block(struct i801_priv *priv,
|
||||
union i2c_smbus_data *data,
|
||||
char read_write, int command,
|
||||
int hwpec)
|
||||
char read_write, int command)
|
||||
{
|
||||
int i, len;
|
||||
int status;
|
||||
int xact = hwpec ? SMBHSTCNT_PEC_EN : 0;
|
||||
int i, len, status, xact;
|
||||
|
||||
switch (command) {
|
||||
case I2C_SMBUS_BLOCK_PROC_CALL:
|
||||
xact |= I801_BLOCK_PROC_CALL;
|
||||
xact = I801_BLOCK_PROC_CALL;
|
||||
break;
|
||||
case I2C_SMBUS_BLOCK_DATA:
|
||||
xact |= I801_BLOCK_DATA;
|
||||
xact = I801_BLOCK_DATA;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
@ -561,10 +559,6 @@ static void i801_isr_byte_done(struct i801_priv *priv)
|
||||
priv->len);
|
||||
/* FIXME: Recover */
|
||||
priv->len = I2C_SMBUS_BLOCK_MAX;
|
||||
} else {
|
||||
dev_dbg(&priv->pci_dev->dev,
|
||||
"SMBus block read size is %d\n",
|
||||
priv->len);
|
||||
}
|
||||
priv->data[-1] = priv->len;
|
||||
}
|
||||
@ -665,8 +659,7 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
|
||||
*/
|
||||
static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
|
||||
union i2c_smbus_data *data,
|
||||
char read_write, int command,
|
||||
int hwpec)
|
||||
char read_write, int command)
|
||||
{
|
||||
int i, len;
|
||||
int smbcmd;
|
||||
@ -764,9 +757,8 @@ static int i801_set_block_buffer_mode(struct i801_priv *priv)
|
||||
}
|
||||
|
||||
/* Block transaction function */
|
||||
static int i801_block_transaction(struct i801_priv *priv,
|
||||
union i2c_smbus_data *data, char read_write,
|
||||
int command, int hwpec)
|
||||
static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
|
||||
char read_write, int command)
|
||||
{
|
||||
int result = 0;
|
||||
unsigned char hostc;
|
||||
@ -802,11 +794,11 @@ static int i801_block_transaction(struct i801_priv *priv,
|
||||
&& i801_set_block_buffer_mode(priv) == 0)
|
||||
result = i801_block_transaction_by_block(priv, data,
|
||||
read_write,
|
||||
command, hwpec);
|
||||
command);
|
||||
else
|
||||
result = i801_block_transaction_byte_by_byte(priv, data,
|
||||
read_write,
|
||||
command, hwpec);
|
||||
command);
|
||||
|
||||
if (command == I2C_SMBUS_I2C_BLOCK_DATA
|
||||
&& read_write == I2C_SMBUS_WRITE) {
|
||||
@ -917,8 +909,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
||||
SMBAUXCTL(priv));
|
||||
|
||||
if (block)
|
||||
ret = i801_block_transaction(priv, data, read_write, size,
|
||||
hwpec);
|
||||
ret = i801_block_transaction(priv, data, read_write, size);
|
||||
else
|
||||
ret = i801_transaction(priv, xact);
|
||||
|
||||
@ -1498,12 +1489,11 @@ static const struct itco_wdt_platform_data spt_tco_platform_data = {
|
||||
.version = 4,
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(p2sb_spinlock);
|
||||
|
||||
static struct platform_device *
|
||||
i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev,
|
||||
struct resource *tco_res)
|
||||
{
|
||||
static DEFINE_MUTEX(p2sb_mutex);
|
||||
struct resource *res;
|
||||
unsigned int devfn;
|
||||
u64 base64_addr;
|
||||
@ -1516,7 +1506,7 @@ i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev,
|
||||
* enumerated by the PCI subsystem, so we need to unhide/hide it
|
||||
* to lookup the P2SB BAR.
|
||||
*/
|
||||
spin_lock(&p2sb_spinlock);
|
||||
mutex_lock(&p2sb_mutex);
|
||||
|
||||
devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 1);
|
||||
|
||||
@ -1534,7 +1524,7 @@ i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev,
|
||||
/* Hide the P2SB device, if it was hidden before */
|
||||
if (hidden)
|
||||
pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, hidden);
|
||||
spin_unlock(&p2sb_spinlock);
|
||||
mutex_unlock(&p2sb_mutex);
|
||||
|
||||
res = &tco_res[1];
|
||||
if (pci_dev->device == PCI_DEVICE_ID_INTEL_DNV_SMBUS)
|
||||
@ -1634,7 +1624,7 @@ i801_acpi_io_handler(u32 function, acpi_physical_address address, u32 bits,
|
||||
* BIOS is accessing the host controller so prevent it from
|
||||
* suspending automatically from now on.
|
||||
*/
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, -1);
|
||||
}
|
||||
|
||||
if ((function & ACPI_IO_MASK) == ACPI_READ)
|
||||
@ -1674,11 +1664,6 @@ static void i801_acpi_remove(struct i801_priv *priv)
|
||||
|
||||
acpi_remove_address_space_handler(adev->handle,
|
||||
ACPI_ADR_SPACE_SYSTEM_IO, i801_acpi_io_handler);
|
||||
|
||||
mutex_lock(&priv->acpi_lock);
|
||||
if (priv->acpi_reserved)
|
||||
pm_runtime_put(&priv->pci_dev->dev);
|
||||
mutex_unlock(&priv->acpi_lock);
|
||||
}
|
||||
#else
|
||||
static inline int i801_acpi_probe(struct i801_priv *priv) { return 0; }
|
||||
@ -1690,6 +1675,7 @@ static void i801_setup_hstcfg(struct i801_priv *priv)
|
||||
unsigned char hstcfg = priv->original_hstcfg;
|
||||
|
||||
hstcfg &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
|
||||
hstcfg &= ~SMBHSTCNT_PEC_EN; /* Disable software PEC */
|
||||
hstcfg |= SMBHSTCFG_HST_EN;
|
||||
pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hstcfg);
|
||||
}
|
||||
|
@ -423,7 +423,7 @@ static int i2c_imx_dma_xfer(struct imx_i2c_struct *i2c_imx,
|
||||
return 0;
|
||||
|
||||
err_submit:
|
||||
dmaengine_terminate_all(dma->chan_using);
|
||||
dmaengine_terminate_sync(dma->chan_using);
|
||||
err_desc:
|
||||
dma_unmap_single(chan_dev, dma->dma_buf,
|
||||
dma->dma_len, dma->dma_data_dir);
|
||||
@ -894,7 +894,7 @@ static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx,
|
||||
&i2c_imx->dma->cmd_complete,
|
||||
msecs_to_jiffies(DMA_TIMEOUT));
|
||||
if (time_left == 0) {
|
||||
dmaengine_terminate_all(dma->chan_using);
|
||||
dmaengine_terminate_sync(dma->chan_using);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
@ -949,7 +949,7 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx,
|
||||
&i2c_imx->dma->cmd_complete,
|
||||
msecs_to_jiffies(DMA_TIMEOUT));
|
||||
if (time_left == 0) {
|
||||
dmaengine_terminate_all(dma->chan_using);
|
||||
dmaengine_terminate_sync(dma->chan_using);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
|
@ -469,16 +469,14 @@ iop3xx_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
ret = -ENXIO;
|
||||
ret = irq;
|
||||
goto unmap;
|
||||
}
|
||||
ret = request_irq(irq, iop3xx_i2c_irq_handler, 0,
|
||||
pdev->name, adapter_data);
|
||||
|
||||
if (ret) {
|
||||
ret = -EIO;
|
||||
if (ret)
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
memcpy(new_adapter->name, pdev->name, strlen(pdev->name));
|
||||
new_adapter->owner = THIS_MODULE;
|
||||
|
@ -1211,7 +1211,7 @@ static int mtk_i2c_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(i2c->pdmabase);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0)
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
init_completion(&i2c->msg_complete);
|
||||
|
@ -290,14 +290,14 @@ read_init_dma_fail:
|
||||
select_init_dma_fail:
|
||||
dma_unmap_sg(i2c->dev, &i2c->sg_io[0], 1, DMA_TO_DEVICE);
|
||||
select_init_pio_fail:
|
||||
dmaengine_terminate_all(i2c->dmach);
|
||||
dmaengine_terminate_sync(i2c->dmach);
|
||||
return -EINVAL;
|
||||
|
||||
/* Write failpath. */
|
||||
write_init_dma_fail:
|
||||
dma_unmap_sg(i2c->dev, i2c->sg_io, 2, DMA_TO_DEVICE);
|
||||
write_init_pio_fail:
|
||||
dmaengine_terminate_all(i2c->dmach);
|
||||
dmaengine_terminate_sync(i2c->dmach);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -267,6 +267,16 @@ static void i2c_parport_attach(struct parport *port)
|
||||
int i;
|
||||
struct pardev_cb i2c_parport_cb;
|
||||
|
||||
if (type < 0) {
|
||||
pr_warn("adapter type unspecified\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type >= ARRAY_SIZE(adapter_parm)) {
|
||||
pr_warn("invalid type (%d)\n", type);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_DEVICE; i++) {
|
||||
if (parport[i] == -1)
|
||||
continue;
|
||||
@ -392,32 +402,8 @@ static struct parport_driver i2c_parport_driver = {
|
||||
.detach = i2c_parport_detach,
|
||||
.devmodel = true,
|
||||
};
|
||||
|
||||
/* ----- Module loading, unloading and information ------------------------ */
|
||||
|
||||
static int __init i2c_parport_init(void)
|
||||
{
|
||||
if (type < 0) {
|
||||
pr_warn("adapter type unspecified\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (type >= ARRAY_SIZE(adapter_parm)) {
|
||||
pr_warn("invalid type (%d)\n", type);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return parport_register_driver(&i2c_parport_driver);
|
||||
}
|
||||
|
||||
static void __exit i2c_parport_exit(void)
|
||||
{
|
||||
parport_unregister_driver(&i2c_parport_driver);
|
||||
}
|
||||
module_parport_driver(i2c_parport_driver);
|
||||
|
||||
MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>");
|
||||
MODULE_DESCRIPTION("I2C bus over parallel port");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(i2c_parport_init);
|
||||
module_exit(i2c_parport_exit);
|
||||
|
@ -1,600 +0,0 @@
|
||||
/*
|
||||
* Specific bus support for PMC-TWI compliant implementation on MSP71xx.
|
||||
*
|
||||
* Copyright 2005-2007 PMC-Sierra, Inc.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_NAME "pmcmsptwi"
|
||||
|
||||
#define MSP_TWI_SF_CLK_REG_OFFSET 0x00
|
||||
#define MSP_TWI_HS_CLK_REG_OFFSET 0x04
|
||||
#define MSP_TWI_CFG_REG_OFFSET 0x08
|
||||
#define MSP_TWI_CMD_REG_OFFSET 0x0c
|
||||
#define MSP_TWI_ADD_REG_OFFSET 0x10
|
||||
#define MSP_TWI_DAT_0_REG_OFFSET 0x14
|
||||
#define MSP_TWI_DAT_1_REG_OFFSET 0x18
|
||||
#define MSP_TWI_INT_STS_REG_OFFSET 0x1c
|
||||
#define MSP_TWI_INT_MSK_REG_OFFSET 0x20
|
||||
#define MSP_TWI_BUSY_REG_OFFSET 0x24
|
||||
|
||||
#define MSP_TWI_INT_STS_DONE (1 << 0)
|
||||
#define MSP_TWI_INT_STS_LOST_ARBITRATION (1 << 1)
|
||||
#define MSP_TWI_INT_STS_NO_RESPONSE (1 << 2)
|
||||
#define MSP_TWI_INT_STS_DATA_COLLISION (1 << 3)
|
||||
#define MSP_TWI_INT_STS_BUSY (1 << 4)
|
||||
#define MSP_TWI_INT_STS_ALL 0x1f
|
||||
|
||||
#define MSP_MAX_BYTES_PER_RW 8
|
||||
#define MSP_MAX_POLL 5
|
||||
#define MSP_POLL_DELAY 10
|
||||
#define MSP_IRQ_TIMEOUT (MSP_MAX_POLL * MSP_POLL_DELAY)
|
||||
|
||||
/* IO Operation macros */
|
||||
#define pmcmsptwi_readl __raw_readl
|
||||
#define pmcmsptwi_writel __raw_writel
|
||||
|
||||
/* TWI command type */
|
||||
enum pmcmsptwi_cmd_type {
|
||||
MSP_TWI_CMD_WRITE = 0, /* Write only */
|
||||
MSP_TWI_CMD_READ = 1, /* Read only */
|
||||
MSP_TWI_CMD_WRITE_READ = 2, /* Write then Read */
|
||||
};
|
||||
|
||||
/* The possible results of the xferCmd */
|
||||
enum pmcmsptwi_xfer_result {
|
||||
MSP_TWI_XFER_OK = 0,
|
||||
MSP_TWI_XFER_TIMEOUT,
|
||||
MSP_TWI_XFER_BUSY,
|
||||
MSP_TWI_XFER_DATA_COLLISION,
|
||||
MSP_TWI_XFER_NO_RESPONSE,
|
||||
MSP_TWI_XFER_LOST_ARBITRATION,
|
||||
};
|
||||
|
||||
/* Corresponds to a PMCTWI clock configuration register */
|
||||
struct pmcmsptwi_clock {
|
||||
u8 filter; /* Bits 15:12, default = 0x03 */
|
||||
u16 clock; /* Bits 9:0, default = 0x001f */
|
||||
};
|
||||
|
||||
struct pmcmsptwi_clockcfg {
|
||||
struct pmcmsptwi_clock standard; /* The standard/fast clock config */
|
||||
struct pmcmsptwi_clock highspeed; /* The highspeed clock config */
|
||||
};
|
||||
|
||||
/* Corresponds to the main TWI configuration register */
|
||||
struct pmcmsptwi_cfg {
|
||||
u8 arbf; /* Bits 15:12, default=0x03 */
|
||||
u8 nak; /* Bits 11:8, default=0x03 */
|
||||
u8 add10; /* Bit 7, default=0x00 */
|
||||
u8 mst_code; /* Bits 6:4, default=0x00 */
|
||||
u8 arb; /* Bit 1, default=0x01 */
|
||||
u8 highspeed; /* Bit 0, default=0x00 */
|
||||
};
|
||||
|
||||
/* A single pmctwi command to issue */
|
||||
struct pmcmsptwi_cmd {
|
||||
u16 addr; /* The slave address (7 or 10 bits) */
|
||||
enum pmcmsptwi_cmd_type type; /* The command type */
|
||||
u8 write_len; /* Number of bytes in the write buffer */
|
||||
u8 read_len; /* Number of bytes in the read buffer */
|
||||
u8 *write_data; /* Buffer of characters to send */
|
||||
u8 *read_data; /* Buffer to fill with incoming data */
|
||||
};
|
||||
|
||||
/* The private data */
|
||||
struct pmcmsptwi_data {
|
||||
void __iomem *iobase; /* iomapped base for IO */
|
||||
int irq; /* IRQ to use (0 disables) */
|
||||
struct completion wait; /* Completion for xfer */
|
||||
struct mutex lock; /* Used for threadsafeness */
|
||||
enum pmcmsptwi_xfer_result last_result; /* result of last xfer */
|
||||
};
|
||||
|
||||
/* The default settings */
|
||||
static const struct pmcmsptwi_clockcfg pmcmsptwi_defclockcfg = {
|
||||
.standard = {
|
||||
.filter = 0x3,
|
||||
.clock = 0x1f,
|
||||
},
|
||||
.highspeed = {
|
||||
.filter = 0x3,
|
||||
.clock = 0x1f,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct pmcmsptwi_cfg pmcmsptwi_defcfg = {
|
||||
.arbf = 0x03,
|
||||
.nak = 0x03,
|
||||
.add10 = 0x00,
|
||||
.mst_code = 0x00,
|
||||
.arb = 0x01,
|
||||
.highspeed = 0x00,
|
||||
};
|
||||
|
||||
static struct pmcmsptwi_data pmcmsptwi_data;
|
||||
|
||||
static struct i2c_adapter pmcmsptwi_adapter;
|
||||
|
||||
/* inline helper functions */
|
||||
static inline u32 pmcmsptwi_clock_to_reg(
|
||||
const struct pmcmsptwi_clock *clock)
|
||||
{
|
||||
return ((clock->filter & 0xf) << 12) | (clock->clock & 0x03ff);
|
||||
}
|
||||
|
||||
static inline u32 pmcmsptwi_cfg_to_reg(const struct pmcmsptwi_cfg *cfg)
|
||||
{
|
||||
return ((cfg->arbf & 0xf) << 12) |
|
||||
((cfg->nak & 0xf) << 8) |
|
||||
((cfg->add10 & 0x1) << 7) |
|
||||
((cfg->mst_code & 0x7) << 4) |
|
||||
((cfg->arb & 0x1) << 1) |
|
||||
(cfg->highspeed & 0x1);
|
||||
}
|
||||
|
||||
static inline void pmcmsptwi_reg_to_cfg(u32 reg, struct pmcmsptwi_cfg *cfg)
|
||||
{
|
||||
cfg->arbf = (reg >> 12) & 0xf;
|
||||
cfg->nak = (reg >> 8) & 0xf;
|
||||
cfg->add10 = (reg >> 7) & 0x1;
|
||||
cfg->mst_code = (reg >> 4) & 0x7;
|
||||
cfg->arb = (reg >> 1) & 0x1;
|
||||
cfg->highspeed = reg & 0x1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the current clock configuration
|
||||
*/
|
||||
static void pmcmsptwi_set_clock_config(const struct pmcmsptwi_clockcfg *cfg,
|
||||
struct pmcmsptwi_data *data)
|
||||
{
|
||||
mutex_lock(&data->lock);
|
||||
pmcmsptwi_writel(pmcmsptwi_clock_to_reg(&cfg->standard),
|
||||
data->iobase + MSP_TWI_SF_CLK_REG_OFFSET);
|
||||
pmcmsptwi_writel(pmcmsptwi_clock_to_reg(&cfg->highspeed),
|
||||
data->iobase + MSP_TWI_HS_CLK_REG_OFFSET);
|
||||
mutex_unlock(&data->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the current TWI bus configuration
|
||||
*/
|
||||
static void pmcmsptwi_get_twi_config(struct pmcmsptwi_cfg *cfg,
|
||||
struct pmcmsptwi_data *data)
|
||||
{
|
||||
mutex_lock(&data->lock);
|
||||
pmcmsptwi_reg_to_cfg(pmcmsptwi_readl(
|
||||
data->iobase + MSP_TWI_CFG_REG_OFFSET), cfg);
|
||||
mutex_unlock(&data->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the current TWI bus configuration
|
||||
*/
|
||||
static void pmcmsptwi_set_twi_config(const struct pmcmsptwi_cfg *cfg,
|
||||
struct pmcmsptwi_data *data)
|
||||
{
|
||||
mutex_lock(&data->lock);
|
||||
pmcmsptwi_writel(pmcmsptwi_cfg_to_reg(cfg),
|
||||
data->iobase + MSP_TWI_CFG_REG_OFFSET);
|
||||
mutex_unlock(&data->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses the 'int_sts' register and returns a well-defined error code
|
||||
*/
|
||||
static enum pmcmsptwi_xfer_result pmcmsptwi_get_result(u32 reg)
|
||||
{
|
||||
if (reg & MSP_TWI_INT_STS_LOST_ARBITRATION) {
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Result: Lost arbitration\n");
|
||||
return MSP_TWI_XFER_LOST_ARBITRATION;
|
||||
} else if (reg & MSP_TWI_INT_STS_NO_RESPONSE) {
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Result: No response\n");
|
||||
return MSP_TWI_XFER_NO_RESPONSE;
|
||||
} else if (reg & MSP_TWI_INT_STS_DATA_COLLISION) {
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Result: Data collision\n");
|
||||
return MSP_TWI_XFER_DATA_COLLISION;
|
||||
} else if (reg & MSP_TWI_INT_STS_BUSY) {
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Result: Bus busy\n");
|
||||
return MSP_TWI_XFER_BUSY;
|
||||
}
|
||||
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Result: Operation succeeded\n");
|
||||
return MSP_TWI_XFER_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* In interrupt mode, handle the interrupt.
|
||||
* NOTE: Assumes data->lock is held.
|
||||
*/
|
||||
static irqreturn_t pmcmsptwi_interrupt(int irq, void *ptr)
|
||||
{
|
||||
struct pmcmsptwi_data *data = ptr;
|
||||
|
||||
u32 reason = pmcmsptwi_readl(data->iobase +
|
||||
MSP_TWI_INT_STS_REG_OFFSET);
|
||||
pmcmsptwi_writel(reason, data->iobase + MSP_TWI_INT_STS_REG_OFFSET);
|
||||
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Got interrupt 0x%08x\n", reason);
|
||||
if (!(reason & MSP_TWI_INT_STS_DONE))
|
||||
return IRQ_NONE;
|
||||
|
||||
data->last_result = pmcmsptwi_get_result(reason);
|
||||
complete(&data->wait);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe for and register the device and return 0 if there is one.
|
||||
*/
|
||||
static int pmcmsptwi_probe(struct platform_device *pldev)
|
||||
{
|
||||
struct resource *res;
|
||||
int rc = -ENODEV;
|
||||
|
||||
/* get the static platform resources */
|
||||
res = platform_get_resource(pldev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pldev->dev, "IOMEM resource not found\n");
|
||||
goto ret_err;
|
||||
}
|
||||
|
||||
/* reserve the memory region */
|
||||
if (!request_mem_region(res->start, resource_size(res),
|
||||
pldev->name)) {
|
||||
dev_err(&pldev->dev,
|
||||
"Unable to get memory/io address region %pap\n",
|
||||
&res->start);
|
||||
rc = -EBUSY;
|
||||
goto ret_err;
|
||||
}
|
||||
|
||||
/* remap the memory */
|
||||
pmcmsptwi_data.iobase = ioremap(res->start,
|
||||
resource_size(res));
|
||||
if (!pmcmsptwi_data.iobase) {
|
||||
dev_err(&pldev->dev,
|
||||
"Unable to ioremap address %pap\n", &res->start);
|
||||
rc = -EIO;
|
||||
goto ret_unreserve;
|
||||
}
|
||||
|
||||
/* request the irq */
|
||||
pmcmsptwi_data.irq = platform_get_irq(pldev, 0);
|
||||
if (pmcmsptwi_data.irq) {
|
||||
rc = request_irq(pmcmsptwi_data.irq, &pmcmsptwi_interrupt,
|
||||
IRQF_SHARED, pldev->name, &pmcmsptwi_data);
|
||||
if (rc == 0) {
|
||||
/*
|
||||
* Enable 'DONE' interrupt only.
|
||||
*
|
||||
* If you enable all interrupts, you will get one on
|
||||
* error and another when the operation completes.
|
||||
* This way you only have to handle one interrupt,
|
||||
* but you can still check all result flags.
|
||||
*/
|
||||
pmcmsptwi_writel(MSP_TWI_INT_STS_DONE,
|
||||
pmcmsptwi_data.iobase +
|
||||
MSP_TWI_INT_MSK_REG_OFFSET);
|
||||
} else {
|
||||
dev_warn(&pldev->dev,
|
||||
"Could not assign TWI IRQ handler "
|
||||
"to irq %d (continuing with poll)\n",
|
||||
pmcmsptwi_data.irq);
|
||||
pmcmsptwi_data.irq = 0;
|
||||
}
|
||||
}
|
||||
|
||||
init_completion(&pmcmsptwi_data.wait);
|
||||
mutex_init(&pmcmsptwi_data.lock);
|
||||
|
||||
pmcmsptwi_set_clock_config(&pmcmsptwi_defclockcfg, &pmcmsptwi_data);
|
||||
pmcmsptwi_set_twi_config(&pmcmsptwi_defcfg, &pmcmsptwi_data);
|
||||
|
||||
printk(KERN_INFO DRV_NAME ": Registering MSP71xx I2C adapter\n");
|
||||
|
||||
pmcmsptwi_adapter.dev.parent = &pldev->dev;
|
||||
platform_set_drvdata(pldev, &pmcmsptwi_adapter);
|
||||
i2c_set_adapdata(&pmcmsptwi_adapter, &pmcmsptwi_data);
|
||||
|
||||
rc = i2c_add_adapter(&pmcmsptwi_adapter);
|
||||
if (rc)
|
||||
goto ret_unmap;
|
||||
|
||||
return 0;
|
||||
|
||||
ret_unmap:
|
||||
if (pmcmsptwi_data.irq) {
|
||||
pmcmsptwi_writel(0,
|
||||
pmcmsptwi_data.iobase + MSP_TWI_INT_MSK_REG_OFFSET);
|
||||
free_irq(pmcmsptwi_data.irq, &pmcmsptwi_data);
|
||||
}
|
||||
|
||||
iounmap(pmcmsptwi_data.iobase);
|
||||
|
||||
ret_unreserve:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
ret_err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release the device and return 0 if there is one.
|
||||
*/
|
||||
static int pmcmsptwi_remove(struct platform_device *pldev)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
i2c_del_adapter(&pmcmsptwi_adapter);
|
||||
|
||||
if (pmcmsptwi_data.irq) {
|
||||
pmcmsptwi_writel(0,
|
||||
pmcmsptwi_data.iobase + MSP_TWI_INT_MSK_REG_OFFSET);
|
||||
free_irq(pmcmsptwi_data.irq, &pmcmsptwi_data);
|
||||
}
|
||||
|
||||
iounmap(pmcmsptwi_data.iobase);
|
||||
|
||||
res = platform_get_resource(pldev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Polls the 'busy' register until the command is complete.
|
||||
* NOTE: Assumes data->lock is held.
|
||||
*/
|
||||
static void pmcmsptwi_poll_complete(struct pmcmsptwi_data *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MSP_MAX_POLL; i++) {
|
||||
u32 val = pmcmsptwi_readl(data->iobase +
|
||||
MSP_TWI_BUSY_REG_OFFSET);
|
||||
if (val == 0) {
|
||||
u32 reason = pmcmsptwi_readl(data->iobase +
|
||||
MSP_TWI_INT_STS_REG_OFFSET);
|
||||
pmcmsptwi_writel(reason, data->iobase +
|
||||
MSP_TWI_INT_STS_REG_OFFSET);
|
||||
data->last_result = pmcmsptwi_get_result(reason);
|
||||
return;
|
||||
}
|
||||
udelay(MSP_POLL_DELAY);
|
||||
}
|
||||
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Result: Poll timeout\n");
|
||||
data->last_result = MSP_TWI_XFER_TIMEOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the transfer (low level):
|
||||
* May use interrupt-driven or polling, depending on if an IRQ is
|
||||
* presently registered.
|
||||
* NOTE: Assumes data->lock is held.
|
||||
*/
|
||||
static enum pmcmsptwi_xfer_result pmcmsptwi_do_xfer(
|
||||
u32 reg, struct pmcmsptwi_data *data)
|
||||
{
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Writing cmd reg 0x%08x\n", reg);
|
||||
pmcmsptwi_writel(reg, data->iobase + MSP_TWI_CMD_REG_OFFSET);
|
||||
if (data->irq) {
|
||||
unsigned long timeleft = wait_for_completion_timeout(
|
||||
&data->wait, MSP_IRQ_TIMEOUT);
|
||||
if (timeleft == 0) {
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Result: IRQ timeout\n");
|
||||
complete(&data->wait);
|
||||
data->last_result = MSP_TWI_XFER_TIMEOUT;
|
||||
}
|
||||
} else
|
||||
pmcmsptwi_poll_complete(data);
|
||||
|
||||
return data->last_result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper routine, converts 'pmctwi_cmd' struct to register format
|
||||
*/
|
||||
static inline u32 pmcmsptwi_cmd_to_reg(const struct pmcmsptwi_cmd *cmd)
|
||||
{
|
||||
return ((cmd->type & 0x3) << 8) |
|
||||
(((cmd->write_len - 1) & 0x7) << 4) |
|
||||
((cmd->read_len - 1) & 0x7);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do the transfer (high level)
|
||||
*/
|
||||
static enum pmcmsptwi_xfer_result pmcmsptwi_xfer_cmd(
|
||||
struct pmcmsptwi_cmd *cmd,
|
||||
struct pmcmsptwi_data *data)
|
||||
{
|
||||
enum pmcmsptwi_xfer_result retval;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
dev_dbg(&pmcmsptwi_adapter.dev,
|
||||
"Setting address to 0x%04x\n", cmd->addr);
|
||||
pmcmsptwi_writel(cmd->addr, data->iobase + MSP_TWI_ADD_REG_OFFSET);
|
||||
|
||||
if (cmd->type == MSP_TWI_CMD_WRITE ||
|
||||
cmd->type == MSP_TWI_CMD_WRITE_READ) {
|
||||
u64 tmp = be64_to_cpup((__be64 *)cmd->write_data);
|
||||
tmp >>= (MSP_MAX_BYTES_PER_RW - cmd->write_len) * 8;
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Writing 0x%016llx\n", tmp);
|
||||
pmcmsptwi_writel(tmp & 0x00000000ffffffffLL,
|
||||
data->iobase + MSP_TWI_DAT_0_REG_OFFSET);
|
||||
if (cmd->write_len > 4)
|
||||
pmcmsptwi_writel(tmp >> 32,
|
||||
data->iobase + MSP_TWI_DAT_1_REG_OFFSET);
|
||||
}
|
||||
|
||||
retval = pmcmsptwi_do_xfer(pmcmsptwi_cmd_to_reg(cmd), data);
|
||||
if (retval != MSP_TWI_XFER_OK)
|
||||
goto xfer_err;
|
||||
|
||||
if (cmd->type == MSP_TWI_CMD_READ ||
|
||||
cmd->type == MSP_TWI_CMD_WRITE_READ) {
|
||||
int i;
|
||||
u64 rmsk = ~(0xffffffffffffffffLL << (cmd->read_len * 8));
|
||||
u64 tmp = (u64)pmcmsptwi_readl(data->iobase +
|
||||
MSP_TWI_DAT_0_REG_OFFSET);
|
||||
if (cmd->read_len > 4)
|
||||
tmp |= (u64)pmcmsptwi_readl(data->iobase +
|
||||
MSP_TWI_DAT_1_REG_OFFSET) << 32;
|
||||
tmp &= rmsk;
|
||||
dev_dbg(&pmcmsptwi_adapter.dev, "Read 0x%016llx\n", tmp);
|
||||
|
||||
for (i = 0; i < cmd->read_len; i++)
|
||||
cmd->read_data[i] = tmp >> i;
|
||||
}
|
||||
|
||||
xfer_err:
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* -- Algorithm functions -- */
|
||||
|
||||
/*
|
||||
* Sends an i2c command out on the adapter
|
||||
*/
|
||||
static int pmcmsptwi_master_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msg, int num)
|
||||
{
|
||||
struct pmcmsptwi_data *data = i2c_get_adapdata(adap);
|
||||
struct pmcmsptwi_cmd cmd;
|
||||
struct pmcmsptwi_cfg oldcfg, newcfg;
|
||||
int ret;
|
||||
|
||||
if (num == 2) {
|
||||
struct i2c_msg *nextmsg = msg + 1;
|
||||
|
||||
cmd.type = MSP_TWI_CMD_WRITE_READ;
|
||||
cmd.write_len = msg->len;
|
||||
cmd.write_data = msg->buf;
|
||||
cmd.read_len = nextmsg->len;
|
||||
cmd.read_data = nextmsg->buf;
|
||||
} else if (msg->flags & I2C_M_RD) {
|
||||
cmd.type = MSP_TWI_CMD_READ;
|
||||
cmd.read_len = msg->len;
|
||||
cmd.read_data = msg->buf;
|
||||
cmd.write_len = 0;
|
||||
cmd.write_data = NULL;
|
||||
} else {
|
||||
cmd.type = MSP_TWI_CMD_WRITE;
|
||||
cmd.read_len = 0;
|
||||
cmd.read_data = NULL;
|
||||
cmd.write_len = msg->len;
|
||||
cmd.write_data = msg->buf;
|
||||
}
|
||||
|
||||
cmd.addr = msg->addr;
|
||||
|
||||
if (msg->flags & I2C_M_TEN) {
|
||||
pmcmsptwi_get_twi_config(&newcfg, data);
|
||||
memcpy(&oldcfg, &newcfg, sizeof(oldcfg));
|
||||
|
||||
/* Set the special 10-bit address flag */
|
||||
newcfg.add10 = 1;
|
||||
|
||||
pmcmsptwi_set_twi_config(&newcfg, data);
|
||||
}
|
||||
|
||||
/* Execute the command */
|
||||
ret = pmcmsptwi_xfer_cmd(&cmd, data);
|
||||
|
||||
if (msg->flags & I2C_M_TEN)
|
||||
pmcmsptwi_set_twi_config(&oldcfg, data);
|
||||
|
||||
dev_dbg(&adap->dev, "I2C %s of %d bytes %s\n",
|
||||
(msg->flags & I2C_M_RD) ? "read" : "write", msg->len,
|
||||
(ret == MSP_TWI_XFER_OK) ? "succeeded" : "failed");
|
||||
|
||||
if (ret != MSP_TWI_XFER_OK) {
|
||||
/*
|
||||
* TODO: We could potentially loop and retry in the case
|
||||
* of MSP_TWI_XFER_TIMEOUT.
|
||||
*/
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static u32 pmcmsptwi_i2c_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR |
|
||||
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_PROC_CALL;
|
||||
}
|
||||
|
||||
static const struct i2c_adapter_quirks pmcmsptwi_i2c_quirks = {
|
||||
.flags = I2C_AQ_COMB_WRITE_THEN_READ | I2C_AQ_NO_ZERO_LEN,
|
||||
.max_write_len = MSP_MAX_BYTES_PER_RW,
|
||||
.max_read_len = MSP_MAX_BYTES_PER_RW,
|
||||
.max_comb_1st_msg_len = MSP_MAX_BYTES_PER_RW,
|
||||
.max_comb_2nd_msg_len = MSP_MAX_BYTES_PER_RW,
|
||||
};
|
||||
|
||||
/* -- Initialization -- */
|
||||
|
||||
static const struct i2c_algorithm pmcmsptwi_algo = {
|
||||
.master_xfer = pmcmsptwi_master_xfer,
|
||||
.functionality = pmcmsptwi_i2c_func,
|
||||
};
|
||||
|
||||
static struct i2c_adapter pmcmsptwi_adapter = {
|
||||
.owner = THIS_MODULE,
|
||||
.class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
|
||||
.algo = &pmcmsptwi_algo,
|
||||
.quirks = &pmcmsptwi_i2c_quirks,
|
||||
.name = DRV_NAME,
|
||||
};
|
||||
|
||||
static struct platform_driver pmcmsptwi_driver = {
|
||||
.probe = pmcmsptwi_probe,
|
||||
.remove = pmcmsptwi_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(pmcmsptwi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PMC MSP TWI/SMBus/I2C driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
@ -778,7 +778,7 @@ static int qup_i2c_bam_schedule_desc(struct qup_i2c_dev *qup)
|
||||
ret = -EINVAL;
|
||||
|
||||
/* abort TX descriptors */
|
||||
dmaengine_terminate_all(qup->btx.dma);
|
||||
dmaengine_terminate_sync(qup->btx.dma);
|
||||
goto desc_err;
|
||||
}
|
||||
|
||||
|
@ -1137,7 +1137,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
||||
*/
|
||||
if (!(i2c->quirks & QUIRK_POLL)) {
|
||||
i2c->irq = ret = platform_get_irq(pdev, 0);
|
||||
if (ret <= 0) {
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "cannot find IRQ\n");
|
||||
clk_unprepare(i2c->clk);
|
||||
return ret;
|
||||
|
@ -458,9 +458,9 @@ static void sh_mobile_i2c_cleanup_dma(struct sh_mobile_i2c_data *pd)
|
||||
if (pd->dma_direction == DMA_NONE)
|
||||
return;
|
||||
else if (pd->dma_direction == DMA_FROM_DEVICE)
|
||||
dmaengine_terminate_all(pd->dma_rx);
|
||||
dmaengine_terminate_sync(pd->dma_rx);
|
||||
else if (pd->dma_direction == DMA_TO_DEVICE)
|
||||
dmaengine_terminate_all(pd->dma_tx);
|
||||
dmaengine_terminate_sync(pd->dma_tx);
|
||||
|
||||
sh_mobile_i2c_dma_unmap(pd);
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ static int p2wi_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(p2wi->regs))
|
||||
return PTR_ERR(p2wi->regs);
|
||||
|
||||
strlcpy(p2wi->adapter.name, pdev->name, sizeof(p2wi->adapter.name));
|
||||
strscpy(p2wi->adapter.name, pdev->name, sizeof(p2wi->adapter.name));
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
@ -578,7 +578,7 @@ static int synquacer_i2c_probe(struct platform_device *pdev)
|
||||
|
||||
i2c->irq = platform_get_irq(pdev, 0);
|
||||
if (i2c->irq < 0)
|
||||
return -ENODEV;
|
||||
return i2c->irq;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, i2c->irq, synquacer_i2c_isr,
|
||||
0, dev_name(&pdev->dev), i2c);
|
||||
|
290
drivers/i2c/busses/i2c-virtio.c
Normal file
290
drivers/i2c/busses/i2c-virtio.c
Normal file
@ -0,0 +1,290 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Virtio I2C Bus Driver
|
||||
*
|
||||
* The Virtio I2C Specification:
|
||||
* https://raw.githubusercontent.com/oasis-tcs/virtio-spec/master/virtio-i2c.tex
|
||||
*
|
||||
* Copyright (c) 2021 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/virtio.h>
|
||||
#include <linux/virtio_ids.h>
|
||||
#include <linux/virtio_config.h>
|
||||
#include <linux/virtio_i2c.h>
|
||||
|
||||
/**
|
||||
* struct virtio_i2c - virtio I2C data
|
||||
* @vdev: virtio device for this controller
|
||||
* @completion: completion of virtio I2C message
|
||||
* @adap: I2C adapter for this controller
|
||||
* @vq: the virtio virtqueue for communication
|
||||
*/
|
||||
struct virtio_i2c {
|
||||
struct virtio_device *vdev;
|
||||
struct completion completion;
|
||||
struct i2c_adapter adap;
|
||||
struct virtqueue *vq;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct virtio_i2c_req - the virtio I2C request structure
|
||||
* @out_hdr: the OUT header of the virtio I2C message
|
||||
* @buf: the buffer into which data is read, or from which it's written
|
||||
* @in_hdr: the IN header of the virtio I2C message
|
||||
*/
|
||||
struct virtio_i2c_req {
|
||||
struct virtio_i2c_out_hdr out_hdr ____cacheline_aligned;
|
||||
uint8_t *buf ____cacheline_aligned;
|
||||
struct virtio_i2c_in_hdr in_hdr ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static void virtio_i2c_msg_done(struct virtqueue *vq)
|
||||
{
|
||||
struct virtio_i2c *vi = vq->vdev->priv;
|
||||
|
||||
complete(&vi->completion);
|
||||
}
|
||||
|
||||
static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
|
||||
struct virtio_i2c_req *reqs,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
struct scatterlist *sgs[3], out_hdr, msg_buf, in_hdr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
int outcnt = 0, incnt = 0;
|
||||
|
||||
/*
|
||||
* We don't support 0 length messages and so filter out
|
||||
* 0 length transfers by using i2c_adapter_quirks.
|
||||
*/
|
||||
if (!msgs[i].len)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Only 7-bit mode supported for this moment. For the address
|
||||
* format, Please check the Virtio I2C Specification.
|
||||
*/
|
||||
reqs[i].out_hdr.addr = cpu_to_le16(msgs[i].addr << 1);
|
||||
|
||||
if (i != num - 1)
|
||||
reqs[i].out_hdr.flags = cpu_to_le32(VIRTIO_I2C_FLAGS_FAIL_NEXT);
|
||||
|
||||
sg_init_one(&out_hdr, &reqs[i].out_hdr, sizeof(reqs[i].out_hdr));
|
||||
sgs[outcnt++] = &out_hdr;
|
||||
|
||||
reqs[i].buf = i2c_get_dma_safe_msg_buf(&msgs[i], 1);
|
||||
if (!reqs[i].buf)
|
||||
break;
|
||||
|
||||
sg_init_one(&msg_buf, reqs[i].buf, msgs[i].len);
|
||||
|
||||
if (msgs[i].flags & I2C_M_RD)
|
||||
sgs[outcnt + incnt++] = &msg_buf;
|
||||
else
|
||||
sgs[outcnt++] = &msg_buf;
|
||||
|
||||
sg_init_one(&in_hdr, &reqs[i].in_hdr, sizeof(reqs[i].in_hdr));
|
||||
sgs[outcnt + incnt++] = &in_hdr;
|
||||
|
||||
if (virtqueue_add_sgs(vq, sgs, outcnt, incnt, &reqs[i], GFP_KERNEL)) {
|
||||
i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int virtio_i2c_complete_reqs(struct virtqueue *vq,
|
||||
struct virtio_i2c_req *reqs,
|
||||
struct i2c_msg *msgs, int num,
|
||||
bool timedout)
|
||||
{
|
||||
struct virtio_i2c_req *req;
|
||||
bool failed = timedout;
|
||||
unsigned int len;
|
||||
int i, j = 0;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
/* Detach the ith request from the vq */
|
||||
req = virtqueue_get_buf(vq, &len);
|
||||
|
||||
/*
|
||||
* Condition req == &reqs[i] should always meet since we have
|
||||
* total num requests in the vq. reqs[i] can never be NULL here.
|
||||
*/
|
||||
if (!failed && (WARN_ON(req != &reqs[i]) ||
|
||||
req->in_hdr.status != VIRTIO_I2C_MSG_OK))
|
||||
failed = true;
|
||||
|
||||
i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], !failed);
|
||||
|
||||
if (!failed)
|
||||
j++;
|
||||
}
|
||||
|
||||
return timedout ? -ETIMEDOUT : j;
|
||||
}
|
||||
|
||||
static int virtio_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct virtio_i2c *vi = i2c_get_adapdata(adap);
|
||||
struct virtqueue *vq = vi->vq;
|
||||
struct virtio_i2c_req *reqs;
|
||||
unsigned long time_left;
|
||||
int count;
|
||||
|
||||
reqs = kcalloc(num, sizeof(*reqs), GFP_KERNEL);
|
||||
if (!reqs)
|
||||
return -ENOMEM;
|
||||
|
||||
count = virtio_i2c_prepare_reqs(vq, reqs, msgs, num);
|
||||
if (!count)
|
||||
goto err_free;
|
||||
|
||||
/*
|
||||
* For the case where count < num, i.e. we weren't able to queue all the
|
||||
* msgs, ideally we should abort right away and return early, but some
|
||||
* of the messages are already sent to the remote I2C controller and the
|
||||
* virtqueue will be left in undefined state in that case. We kick the
|
||||
* remote here to clear the virtqueue, so we can try another set of
|
||||
* messages later on.
|
||||
*/
|
||||
|
||||
reinit_completion(&vi->completion);
|
||||
virtqueue_kick(vq);
|
||||
|
||||
time_left = wait_for_completion_timeout(&vi->completion, adap->timeout);
|
||||
if (!time_left)
|
||||
dev_err(&adap->dev, "virtio i2c backend timeout.\n");
|
||||
|
||||
count = virtio_i2c_complete_reqs(vq, reqs, msgs, count, !time_left);
|
||||
|
||||
err_free:
|
||||
kfree(reqs);
|
||||
return count;
|
||||
}
|
||||
|
||||
static void virtio_i2c_del_vqs(struct virtio_device *vdev)
|
||||
{
|
||||
vdev->config->reset(vdev);
|
||||
vdev->config->del_vqs(vdev);
|
||||
}
|
||||
|
||||
static int virtio_i2c_setup_vqs(struct virtio_i2c *vi)
|
||||
{
|
||||
struct virtio_device *vdev = vi->vdev;
|
||||
|
||||
vi->vq = virtio_find_single_vq(vdev, virtio_i2c_msg_done, "msg");
|
||||
return PTR_ERR_OR_ZERO(vi->vq);
|
||||
}
|
||||
|
||||
static u32 virtio_i2c_func(struct i2c_adapter *adap)
|
||||
{
|
||||
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
|
||||
}
|
||||
|
||||
static struct i2c_algorithm virtio_algorithm = {
|
||||
.master_xfer = virtio_i2c_xfer,
|
||||
.functionality = virtio_i2c_func,
|
||||
};
|
||||
|
||||
static const struct i2c_adapter_quirks virtio_i2c_quirks = {
|
||||
.flags = I2C_AQ_NO_ZERO_LEN,
|
||||
};
|
||||
|
||||
static int virtio_i2c_probe(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_i2c *vi;
|
||||
int ret;
|
||||
|
||||
vi = devm_kzalloc(&vdev->dev, sizeof(*vi), GFP_KERNEL);
|
||||
if (!vi)
|
||||
return -ENOMEM;
|
||||
|
||||
vdev->priv = vi;
|
||||
vi->vdev = vdev;
|
||||
|
||||
init_completion(&vi->completion);
|
||||
|
||||
ret = virtio_i2c_setup_vqs(vi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vi->adap.owner = THIS_MODULE;
|
||||
snprintf(vi->adap.name, sizeof(vi->adap.name),
|
||||
"i2c_virtio at virtio bus %d", vdev->index);
|
||||
vi->adap.algo = &virtio_algorithm;
|
||||
vi->adap.quirks = &virtio_i2c_quirks;
|
||||
vi->adap.dev.parent = &vdev->dev;
|
||||
vi->adap.dev.of_node = vdev->dev.of_node;
|
||||
i2c_set_adapdata(&vi->adap, vi);
|
||||
|
||||
/*
|
||||
* Setup ACPI node for controlled devices which will be probed through
|
||||
* ACPI.
|
||||
*/
|
||||
ACPI_COMPANION_SET(&vi->adap.dev, ACPI_COMPANION(vdev->dev.parent));
|
||||
|
||||
ret = i2c_add_adapter(&vi->adap);
|
||||
if (ret)
|
||||
virtio_i2c_del_vqs(vdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void virtio_i2c_remove(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_i2c *vi = vdev->priv;
|
||||
|
||||
i2c_del_adapter(&vi->adap);
|
||||
virtio_i2c_del_vqs(vdev);
|
||||
}
|
||||
|
||||
static struct virtio_device_id id_table[] = {
|
||||
{ VIRTIO_ID_I2C_ADAPTER, VIRTIO_DEV_ANY_ID },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(virtio, id_table);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int virtio_i2c_freeze(struct virtio_device *vdev)
|
||||
{
|
||||
virtio_i2c_del_vqs(vdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_i2c_restore(struct virtio_device *vdev)
|
||||
{
|
||||
return virtio_i2c_setup_vqs(vdev->priv);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct virtio_driver virtio_i2c_driver = {
|
||||
.id_table = id_table,
|
||||
.probe = virtio_i2c_probe,
|
||||
.remove = virtio_i2c_remove,
|
||||
.driver = {
|
||||
.name = "i2c_virtio",
|
||||
},
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.freeze = virtio_i2c_freeze,
|
||||
.restore = virtio_i2c_restore,
|
||||
#endif
|
||||
};
|
||||
module_virtio_driver(virtio_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Jie Deng <jie.deng@intel.com>");
|
||||
MODULE_AUTHOR("Conghui Chen <conghui.chen@intel.com>");
|
||||
MODULE_DESCRIPTION("Virtio i2c bus driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -517,7 +517,7 @@ static int xlp9xx_i2c_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
priv->irq = platform_get_irq(pdev, 0);
|
||||
if (priv->irq <= 0)
|
||||
if (priv->irq < 0)
|
||||
return priv->irq;
|
||||
/* SMBAlert irq */
|
||||
priv->alert_data.irq = platform_get_irq(pdev, 1);
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
/* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk@telos.de> */
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/device.h>
|
||||
@ -68,8 +70,7 @@ static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
|
||||
struct i2c_dev *i2c_dev;
|
||||
|
||||
if (adap->nr >= I2C_MINORS) {
|
||||
printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n",
|
||||
adap->nr);
|
||||
pr_err("Out of device minors (%d)\n", adap->nr);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
@ -101,7 +102,7 @@ static ssize_t name_show(struct device *dev,
|
||||
|
||||
if (!i2c_dev)
|
||||
return -ENODEV;
|
||||
return sprintf(buf, "%s\n", i2c_dev->adap->name);
|
||||
return sysfs_emit(buf, "%s\n", i2c_dev->adap->name);
|
||||
}
|
||||
static DEVICE_ATTR_RO(name);
|
||||
|
||||
@ -145,8 +146,7 @@ static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
|
||||
if (tmp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n",
|
||||
iminor(file_inode(file)), count);
|
||||
pr_debug("i2c-%d reading %zu bytes.\n", iminor(file_inode(file)), count);
|
||||
|
||||
ret = i2c_master_recv(client, tmp, count);
|
||||
if (ret >= 0)
|
||||
@ -170,8 +170,7 @@ static ssize_t i2cdev_write(struct file *file, const char __user *buf,
|
||||
if (IS_ERR(tmp))
|
||||
return PTR_ERR(tmp);
|
||||
|
||||
pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n",
|
||||
iminor(file_inode(file)), count);
|
||||
pr_debug("i2c-%d writing %zu bytes.\n", iminor(file_inode(file)), count);
|
||||
|
||||
ret = i2c_master_send(client, tmp, count);
|
||||
kfree(tmp);
|
||||
@ -674,8 +673,7 @@ static int i2cdev_attach_adapter(struct device *dev, void *dummy)
|
||||
return res;
|
||||
}
|
||||
|
||||
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
|
||||
adap->name, adap->nr);
|
||||
pr_debug("adapter [%s] registered as minor %d\n", adap->name, adap->nr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -694,7 +692,7 @@ static int i2cdev_detach_adapter(struct device *dev, void *dummy)
|
||||
|
||||
put_i2c_dev(i2c_dev, true);
|
||||
|
||||
pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name);
|
||||
pr_debug("adapter [%s] unregistered\n", adap->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -727,7 +725,7 @@ static int __init i2c_dev_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
printk(KERN_INFO "i2c /dev entries driver\n");
|
||||
pr_info("i2c /dev entries driver\n");
|
||||
|
||||
res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
|
||||
if (res)
|
||||
@ -755,7 +753,7 @@ out_unreg_class:
|
||||
out_unreg_chrdev:
|
||||
unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
|
||||
out:
|
||||
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
|
||||
pr_err("Driver Initialisation failed\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,22 @@
|
||||
|
||||
#include <linux/math.h>
|
||||
|
||||
/* Metric prefixes in accordance with Système international (d'unités) */
|
||||
#define PETA 1000000000000000ULL
|
||||
#define TERA 1000000000000ULL
|
||||
#define GIGA 1000000000UL
|
||||
#define MEGA 1000000UL
|
||||
#define KILO 1000UL
|
||||
#define HECTO 100UL
|
||||
#define DECA 10UL
|
||||
#define DECI 10UL
|
||||
#define CENTI 100UL
|
||||
#define MILLI 1000UL
|
||||
#define MICRO 1000000UL
|
||||
#define NANO 1000000000UL
|
||||
#define PICO 1000000000000ULL
|
||||
#define FEMTO 1000000000000000ULL
|
||||
|
||||
#define MILLIWATT_PER_WATT 1000L
|
||||
#define MICROWATT_PER_MILLIWATT 1000L
|
||||
#define MICROWATT_PER_WATT 1000000L
|
||||
|
41
include/uapi/linux/virtio_i2c.h
Normal file
41
include/uapi/linux/virtio_i2c.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
|
||||
/*
|
||||
* Definitions for virtio I2C Adpter
|
||||
*
|
||||
* Copyright (c) 2021 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_LINUX_VIRTIO_I2C_H
|
||||
#define _UAPI_LINUX_VIRTIO_I2C_H
|
||||
|
||||
#include <linux/const.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* The bit 0 of the @virtio_i2c_out_hdr.@flags, used to group the requests */
|
||||
#define VIRTIO_I2C_FLAGS_FAIL_NEXT _BITUL(0)
|
||||
|
||||
/**
|
||||
* struct virtio_i2c_out_hdr - the virtio I2C message OUT header
|
||||
* @addr: the controlled device address
|
||||
* @padding: used to pad to full dword
|
||||
* @flags: used for feature extensibility
|
||||
*/
|
||||
struct virtio_i2c_out_hdr {
|
||||
__le16 addr;
|
||||
__le16 padding;
|
||||
__le32 flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct virtio_i2c_in_hdr - the virtio I2C message IN header
|
||||
* @status: the processing result from the backend
|
||||
*/
|
||||
struct virtio_i2c_in_hdr {
|
||||
__u8 status;
|
||||
};
|
||||
|
||||
/* The final status written by the device */
|
||||
#define VIRTIO_I2C_MSG_OK 0
|
||||
#define VIRTIO_I2C_MSG_ERR 1
|
||||
|
||||
#endif /* _UAPI_LINUX_VIRTIO_I2C_H */
|
@ -55,6 +55,7 @@
|
||||
#define VIRTIO_ID_FS 26 /* virtio filesystem */
|
||||
#define VIRTIO_ID_PMEM 27 /* virtio pmem */
|
||||
#define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */
|
||||
#define VIRTIO_ID_I2C_ADAPTER 34 /* virtio i2c adapter */
|
||||
#define VIRTIO_ID_BT 40 /* virtio bluetooth */
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user