From c690be1cb48cf5a95c34c879841cc6e2c4fbc425 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 22 Jul 2009 14:58:39 +0000 Subject: i2c: change i2c-sh_mobile.c module_init() to subsys_initcall() Convert the i2c-sh_mobile i2c bus driver to use subsys_initcall() instead of module_init(). This change makes the driver register a bit earlier which together with earlier platform data moves the time for probe(). The earlier probe() makes it possible to use i2c_get_adapter() and i2c_transfer() from device_initcall(). The same strategy is used by other i2c bus drivers such as i2c-pxa.c and i2c-s3c2410.c. Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/i2c/busses/i2c-sh_mobile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 4f3d99cd169..820487d0d5c 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -637,7 +637,7 @@ static void __exit sh_mobile_i2c_adap_exit(void) platform_driver_unregister(&sh_mobile_i2c_driver); } -module_init(sh_mobile_i2c_adap_init); +subsys_initcall(sh_mobile_i2c_adap_init); module_exit(sh_mobile_i2c_adap_exit); MODULE_DESCRIPTION("SuperH Mobile I2C Bus Controller driver"); -- cgit v1.2.3 From f1a3b994f9dfd12111dc034402aed256fac66dfe Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Fri, 14 Aug 2009 10:48:59 +0000 Subject: i2c: Runtime PM for SuperH Mobile I2C This patch modifies the SuperH Mobile I2C driver to support Runtime PM. These changes is all that is needed for proper Runtime PM support in this driver. Driver callbacks for Runtime PM are empty because the device registers are always re-initialized after pm_runtime_get_sync(). Signed-off-by: Magnus Damm Signed-off-by: Paul Mundt --- drivers/i2c/busses/i2c-sh_mobile.c | 39 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 820487d0d5c..86a9d4e8147 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -165,7 +166,8 @@ static void activate_ch(struct sh_mobile_i2c_data *pd) u_int32_t denom; u_int32_t tmp; - /* Make sure the clock is enabled */ + /* Wake up device and enable clock */ + pm_runtime_get_sync(pd->dev); clk_enable(pd->clk); /* Get clock rate after clock is enabled */ @@ -213,8 +215,9 @@ static void deactivate_ch(struct sh_mobile_i2c_data *pd) /* Disable channel */ iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd)); - /* Disable clock */ + /* Disable clock and mark device as idle */ clk_disable(pd->clk); + pm_runtime_put_sync(pd->dev); } static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, @@ -572,6 +575,19 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) goto err_irq; } + /* Enable Runtime PM for this device. + * + * Also tell the Runtime PM core to ignore children + * for this device since it is valid for us to suspend + * this I2C master driver even though the slave devices + * on the I2C bus may not be suspended. + * + * The state of the I2C hardware bus is unaffected by + * the Runtime PM state. + */ + pm_suspend_ignore_children(&dev->dev, true); + pm_runtime_enable(&dev->dev); + /* setup the private data */ adap = &pd->adap; i2c_set_adapdata(adap, pd); @@ -614,14 +630,33 @@ static int sh_mobile_i2c_remove(struct platform_device *dev) iounmap(pd->reg); sh_mobile_i2c_hook_irqs(dev, 0); clk_put(pd->clk); + pm_runtime_disable(&dev->dev); kfree(pd); return 0; } +static int sh_mobile_i2c_runtime_nop(struct device *dev) +{ + /* Runtime PM callback shared between ->runtime_suspend() + * and ->runtime_resume(). Simply returns success. + * + * This driver re-initializes all registers after + * pm_runtime_get_sync() anyway so there is no need + * to save and restore registers here. + */ + return 0; +} + +static struct dev_pm_ops sh_mobile_i2c_dev_pm_ops = { + .runtime_suspend = sh_mobile_i2c_runtime_nop, + .runtime_resume = sh_mobile_i2c_runtime_nop, +}; + static struct platform_driver sh_mobile_i2c_driver = { .driver = { .name = "i2c-sh_mobile", .owner = THIS_MODULE, + .pm = &sh_mobile_i2c_dev_pm_ops, }, .probe = sh_mobile_i2c_probe, .remove = sh_mobile_i2c_remove, -- cgit v1.2.3 From 27693ce5f372c0af3b0730f5152b35432afa0fd7 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 18 Sep 2009 22:45:43 +0200 Subject: i2c-taos-evm: Switch echo off to improve performance When echo is on, we waste time reading back our orders. Switching echo off makes performance much better: SMBus byte data transactions are 47% faster and byte transactions are 24% faster. Signed-off-by: Jean Delvare --- drivers/i2c/busses/i2c-taos-evm.c | 45 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 21 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-taos-evm.c b/drivers/i2c/busses/i2c-taos-evm.c index 224aa12ee7c..dd39c1eb03e 100644 --- a/drivers/i2c/busses/i2c-taos-evm.c +++ b/drivers/i2c/busses/i2c-taos-evm.c @@ -32,10 +32,12 @@ #define TAOS_STATE_INIT 0 #define TAOS_STATE_IDLE 1 -#define TAOS_STATE_SEND 2 +#define TAOS_STATE_EOFF 2 #define TAOS_STATE_RECV 3 #define TAOS_CMD_RESET 0x12 +#define TAOS_CMD_ECHO_ON '+' +#define TAOS_CMD_ECHO_OFF '-' static DECLARE_WAIT_QUEUE_HEAD(wq); @@ -102,17 +104,9 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr, /* Send the transaction to the TAOS EVM */ dev_dbg(&adapter->dev, "Command buffer: %s\n", taos->buffer); - taos->pos = 0; - taos->state = TAOS_STATE_SEND; - serio_write(serio, taos->buffer[0]); - wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, - msecs_to_jiffies(250)); - if (taos->state != TAOS_STATE_IDLE) { - dev_err(&adapter->dev, "Transaction failed " - "(state=%d, pos=%d)\n", taos->state, taos->pos); - taos->addr = 0; - return -EIO; - } + for (p = taos->buffer; *p; p++) + serio_write(serio, *p); + taos->addr = addr; /* Start the transaction and read the answer */ @@ -122,7 +116,7 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr, wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, msecs_to_jiffies(150)); if (taos->state != TAOS_STATE_IDLE - || taos->pos != 6) { + || taos->pos != 5) { dev_err(&adapter->dev, "Transaction timeout (pos=%d)\n", taos->pos); return -EIO; @@ -130,7 +124,7 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr, dev_dbg(&adapter->dev, "Answer buffer: %s\n", taos->buffer); /* Interpret the returned string */ - p = taos->buffer + 2; + p = taos->buffer + 1; p[3] = '\0'; if (!strcmp(p, "NAK")) return -ENODEV; @@ -173,13 +167,9 @@ static irqreturn_t taos_interrupt(struct serio *serio, unsigned char data, wake_up_interruptible(&wq); } break; - case TAOS_STATE_SEND: - if (taos->buffer[++taos->pos]) - serio_write(serio, taos->buffer[taos->pos]); - else { - taos->state = TAOS_STATE_IDLE; - wake_up_interruptible(&wq); - } + case TAOS_STATE_EOFF: + taos->state = TAOS_STATE_IDLE; + wake_up_interruptible(&wq); break; case TAOS_STATE_RECV: taos->buffer[taos->pos++] = data; @@ -257,6 +247,19 @@ static int taos_connect(struct serio *serio, struct serio_driver *drv) } strlcpy(adapter->name, name, sizeof(adapter->name)); + /* Turn echo off for better performance */ + taos->state = TAOS_STATE_EOFF; + serio_write(serio, TAOS_CMD_ECHO_OFF); + + wait_event_interruptible_timeout(wq, taos->state == TAOS_STATE_IDLE, + msecs_to_jiffies(250)); + if (taos->state != TAOS_STATE_IDLE) { + err = -ENODEV; + dev_err(&adapter->dev, "Echo off failed " + "(state=%d)\n", taos->state); + goto exit_close; + } + err = i2c_add_adapter(adapter); if (err) goto exit_close; -- cgit v1.2.3 From fce96f3e5d34b0e3195f9d1bf2b3c7e3841e90ff Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 18 Sep 2009 22:45:47 +0200 Subject: i2c/scx200_acb: Provide more information on bus errors Upon a bus error, it's rather hard to guess what happened. Dumping the address, length and status provides a lot of value for troubleshooting issues. Signed-off-by: Willy Tarreau Signed-off-by: Jean Delvare --- drivers/i2c/busses/scx200_acb.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c index 648ecc6f60e..cf994bd01d9 100644 --- a/drivers/i2c/busses/scx200_acb.c +++ b/drivers/i2c/busses/scx200_acb.c @@ -217,8 +217,10 @@ static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status) return; error: - dev_err(&iface->adapter.dev, "%s in state %s\n", errmsg, - scx200_acb_state_name[iface->state]); + dev_err(&iface->adapter.dev, + "%s in state %s (addr=0x%02x, len=%d, status=0x%02x)\n", errmsg, + scx200_acb_state_name[iface->state], iface->address_byte, + iface->len, status); iface->state = state_idle; iface->result = -EIO; -- cgit v1.2.3 From 76b3e28fa728bb68895cbd8375f5ce233bd891de Mon Sep 17 00:00:00 2001 From: Crane Cai Date: Fri, 18 Sep 2009 22:45:50 +0200 Subject: i2c-piix4: Add AMD SB900 SMBus device ID Add new SMBus device ID for AMD SB900. Signed-off-by: Crane Cai Signed-off-by: Jean Delvare --- drivers/i2c/busses/Kconfig | 3 ++- drivers/i2c/busses/i2c-piix4.c | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8206442fbab..bb216c5fe30 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -113,7 +113,7 @@ config I2C_ISCH will be called i2c-isch. config I2C_PIIX4 - tristate "Intel PIIX4 and compatible (ATI/Serverworks/Broadcom/SMSC)" + tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)" depends on PCI help If you say yes to this option, support will be included for the Intel @@ -128,6 +128,7 @@ config I2C_PIIX4 ATI SB600 ATI SB700 ATI SB800 + AMD SB900 Serverworks OSB4 Serverworks CSB5 Serverworks CSB6 diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 0249a7d762b..a782c7a08f9 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -22,6 +22,7 @@ Intel PIIX4, 440MX Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 ATI IXP200, IXP300, IXP400, SB600, SB700, SB800 + AMD SB900 SMSC Victory66 Note: we assume there can only be one device, with one SMBus interface. @@ -479,6 +480,7 @@ static struct pci_device_id piix4_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SB900_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4) }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, @@ -499,9 +501,10 @@ static int __devinit piix4_probe(struct pci_dev *dev, { int retval; - if ((dev->vendor == PCI_VENDOR_ID_ATI) && - (dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS) && - (dev->revision >= 0x40)) + if ((dev->vendor == PCI_VENDOR_ID_ATI && + dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && + dev->revision >= 0x40) || + dev->vendor == PCI_VENDOR_ID_AMD) /* base address location etc changed in SB800 */ retval = piix4_setup_sb800(dev, id); else -- cgit v1.2.3 From 449d2c759ddba46a89b698bdc64bfc2f7cc5bb66 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 18 Sep 2009 22:45:51 +0200 Subject: i2c-pnx: Correct use of request_region/request_mem_region request_mem_region should be used when ioremap is used subsequently. release_region is then correspondingly replaced by release_mem_region. The semantic patch that fixes this problem is as follows: (http://coccinelle.lip6.fr/) // @r@ expression start,E; @@ - request_region + request_mem_region (start,...) ... when != request_mem_region(start,...) when != start = E ioremap(start,...) @@ expression r.start; @@ - release_region + release_mem_region (start,...) // Signed-off-by: Julia Lawall Cc: Vitaly Wool Signed-off-by: Jean Delvare --- drivers/i2c/busses/i2c-pnx.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index ec15cff556b..6ff6c20f1e7 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -586,7 +586,8 @@ static int __devinit i2c_pnx_probe(struct platform_device *pdev) alg_data->mif.timer.data = (unsigned long)i2c_pnx->adapter; /* Register I/O resource */ - if (!request_region(alg_data->base, I2C_PNX_REGION_SIZE, pdev->name)) { + if (!request_mem_region(alg_data->base, I2C_PNX_REGION_SIZE, + pdev->name)) { dev_err(&pdev->dev, "I/O region 0x%08x for I2C already in use.\n", alg_data->base); @@ -650,7 +651,7 @@ out_clock: out_unmap: iounmap((void *)alg_data->ioaddr); out_release: - release_region(alg_data->base, I2C_PNX_REGION_SIZE); + release_mem_region(alg_data->base, I2C_PNX_REGION_SIZE); out_drvdata: platform_set_drvdata(pdev, NULL); out: @@ -667,7 +668,7 @@ static int __devexit i2c_pnx_remove(struct platform_device *pdev) i2c_del_adapter(adap); i2c_pnx->set_clock_stop(pdev); iounmap((void *)alg_data->ioaddr); - release_region(alg_data->base, I2C_PNX_REGION_SIZE); + release_mem_region(alg_data->base, I2C_PNX_REGION_SIZE); platform_set_drvdata(pdev, NULL); return 0; -- cgit v1.2.3 From dc9854212e0d7318d7133697906d98b78f3088b6 Mon Sep 17 00:00:00 2001 From: Crane Cai Date: Fri, 18 Sep 2009 22:45:51 +0200 Subject: i2c: Add driver for SMBus Control Method Interface This driver supports the SMBus Control Method Interface. It needs BIOS declare ACPI control methods which described in SMBus Control Method Interface Spec. http://smbus.org/specs/smbus_cmi10.pdf Signed-off-by: Crane Cai Signed-off-by: Jean Delvare --- drivers/i2c/busses/Kconfig | 11 ++ drivers/i2c/busses/Makefile | 3 + drivers/i2c/busses/i2c-scmi.c | 430 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 444 insertions(+) create mode 100644 drivers/i2c/busses/i2c-scmi.c (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bb216c5fe30..838bd1e4efb 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -762,4 +762,15 @@ config SCx200_ACB This support is also available as a module. If so, the module will be called scx200_acb. +config I2C_SCMI + tristate "SMBus Control Method Interface" + depends on ACPI + help + This driver supports the SMBus Control Method Interface. It needs the + BIOS to declare ACPI control methods as described in the SMBus Control + Method Interface specification. + + To compile this driver as a module, choose M here: + the module will be called i2c-scmi. + endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index e654263bfc0..060da43cf7d 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -2,6 +2,9 @@ # Makefile for the i2c bus drivers. # +# SMBus CMI driver +obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o + # PC SMBus host controller drivers obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c new file mode 100644 index 00000000000..276a046ac93 --- /dev/null +++ b/drivers/i2c/busses/i2c-scmi.c @@ -0,0 +1,430 @@ +/* + * SMBus driver for ACPI SMBus CMI + * + * Copyright (C) 2009 Crane Cai + * + * 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 +#include +#include +#include +#include +#include +#include + +#define ACPI_SMBUS_HC_CLASS "smbus" +#define ACPI_SMBUS_HC_DEVICE_NAME "cmi" + +ACPI_MODULE_NAME("smbus_cmi"); + +struct smbus_methods_t { + char *mt_info; + char *mt_sbr; + char *mt_sbw; +}; + +struct acpi_smbus_cmi { + acpi_handle handle; + struct i2c_adapter adapter; + u8 cap_info:1; + u8 cap_read:1; + u8 cap_write:1; +}; + +static const struct smbus_methods_t smbus_methods = { + .mt_info = "_SBI", + .mt_sbr = "_SBR", + .mt_sbw = "_SBW", +}; + +static const struct acpi_device_id acpi_smbus_cmi_ids[] = { + {"SMBUS01", 0}, + {"", 0} +}; + +#define ACPI_SMBUS_STATUS_OK 0x00 +#define ACPI_SMBUS_STATUS_FAIL 0x07 +#define ACPI_SMBUS_STATUS_DNAK 0x10 +#define ACPI_SMBUS_STATUS_DERR 0x11 +#define ACPI_SMBUS_STATUS_CMD_DENY 0x12 +#define ACPI_SMBUS_STATUS_UNKNOWN 0x13 +#define ACPI_SMBUS_STATUS_ACC_DENY 0x17 +#define ACPI_SMBUS_STATUS_TIMEOUT 0x18 +#define ACPI_SMBUS_STATUS_NOTSUP 0x19 +#define ACPI_SMBUS_STATUS_BUSY 0x1a +#define ACPI_SMBUS_STATUS_PEC 0x1f + +#define ACPI_SMBUS_PRTCL_WRITE 0x00 +#define ACPI_SMBUS_PRTCL_READ 0x01 +#define ACPI_SMBUS_PRTCL_QUICK 0x02 +#define ACPI_SMBUS_PRTCL_BYTE 0x04 +#define ACPI_SMBUS_PRTCL_BYTE_DATA 0x06 +#define ACPI_SMBUS_PRTCL_WORD_DATA 0x08 +#define ACPI_SMBUS_PRTCL_BLOCK_DATA 0x0a + + +static int +acpi_smbus_cmi_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data *data) +{ + int result = 0; + struct acpi_smbus_cmi *smbus_cmi = adap->algo_data; + unsigned char protocol; + acpi_status status = 0; + struct acpi_object_list input; + union acpi_object mt_params[5]; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + union acpi_object *pkg; + char *method; + int len = 0; + + dev_dbg(&adap->dev, "access size: %d %s\n", size, + (read_write) ? "READ" : "WRITE"); + switch (size) { + case I2C_SMBUS_QUICK: + protocol = ACPI_SMBUS_PRTCL_QUICK; + command = 0; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 0; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = 0; + } + break; + + case I2C_SMBUS_BYTE: + protocol = ACPI_SMBUS_PRTCL_BYTE; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 0; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = 0; + } else { + command = 0; + } + break; + + case I2C_SMBUS_BYTE_DATA: + protocol = ACPI_SMBUS_PRTCL_BYTE_DATA; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 1; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = data->byte; + } + break; + + case I2C_SMBUS_WORD_DATA: + protocol = ACPI_SMBUS_PRTCL_WORD_DATA; + if (read_write == I2C_SMBUS_WRITE) { + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = 2; + mt_params[4].type = ACPI_TYPE_INTEGER; + mt_params[4].integer.value = data->word; + } + break; + + case I2C_SMBUS_BLOCK_DATA: + protocol = ACPI_SMBUS_PRTCL_BLOCK_DATA; + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + mt_params[3].type = ACPI_TYPE_INTEGER; + mt_params[3].integer.value = len; + mt_params[4].type = ACPI_TYPE_BUFFER; + mt_params[4].buffer.pointer = data->block + 1; + } + break; + + default: + dev_warn(&adap->dev, "Unsupported transaction %d\n", size); + return -EOPNOTSUPP; + } + + if (read_write == I2C_SMBUS_READ) { + protocol |= ACPI_SMBUS_PRTCL_READ; + method = smbus_methods.mt_sbr; + input.count = 3; + } else { + protocol |= ACPI_SMBUS_PRTCL_WRITE; + method = smbus_methods.mt_sbw; + input.count = 5; + } + + input.pointer = mt_params; + mt_params[0].type = ACPI_TYPE_INTEGER; + mt_params[0].integer.value = protocol; + mt_params[1].type = ACPI_TYPE_INTEGER; + mt_params[1].integer.value = addr; + mt_params[2].type = ACPI_TYPE_INTEGER; + mt_params[2].integer.value = command; + + status = acpi_evaluate_object(smbus_cmi->handle, method, &input, + &buffer); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Evaluating %s: %i", method, status)); + return -EIO; + } + + pkg = buffer.pointer; + if (pkg && pkg->type == ACPI_TYPE_PACKAGE) + obj = pkg->package.elements; + else { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + + result = obj->integer.value; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s return status: %i\n", + method, result)); + + switch (result) { + case ACPI_SMBUS_STATUS_OK: + result = 0; + break; + case ACPI_SMBUS_STATUS_BUSY: + result = -EBUSY; + goto out; + case ACPI_SMBUS_STATUS_TIMEOUT: + result = -ETIMEDOUT; + goto out; + case ACPI_SMBUS_STATUS_DNAK: + result = -ENXIO; + goto out; + default: + result = -EIO; + goto out; + } + + if (read_write == I2C_SMBUS_WRITE || size == I2C_SMBUS_QUICK) + goto out; + + obj = pkg->package.elements + 1; + if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + + len = obj->integer.value; + obj = pkg->package.elements + 2; + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + case I2C_SMBUS_WORD_DATA: + if (obj == NULL || obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + if (len == 2) + data->word = obj->integer.value; + else + data->byte = obj->integer.value; + break; + case I2C_SMBUS_BLOCK_DATA: + if (obj == NULL || obj->type != ACPI_TYPE_BUFFER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + result = -EIO; + goto out; + } + if (len == 0 || len > I2C_SMBUS_BLOCK_MAX) + return -EPROTO; + data->block[0] = len; + memcpy(data->block + 1, obj->buffer.pointer, len); + break; + } + +out: + kfree(buffer.pointer); + dev_dbg(&adap->dev, "Transaction status: %i\n", result); + return result; +} + +static u32 acpi_smbus_cmi_func(struct i2c_adapter *adapter) +{ + struct acpi_smbus_cmi *smbus_cmi = adapter->algo_data; + u32 ret; + + ret = smbus_cmi->cap_read | smbus_cmi->cap_write ? + I2C_FUNC_SMBUS_QUICK : 0; + + ret |= smbus_cmi->cap_read ? + (I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA | + I2C_FUNC_SMBUS_READ_BLOCK_DATA) : 0; + + ret |= smbus_cmi->cap_write ? + (I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_WORD_DATA | + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA) : 0; + + return ret; +} + +static const struct i2c_algorithm acpi_smbus_cmi_algorithm = { + .smbus_xfer = acpi_smbus_cmi_access, + .functionality = acpi_smbus_cmi_func, +}; + + +static int acpi_smbus_cmi_add_cap(struct acpi_smbus_cmi *smbus_cmi, + const char *name) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *obj; + acpi_status status; + + if (!strcmp(name, smbus_methods.mt_info)) { + status = acpi_evaluate_object(smbus_cmi->handle, + smbus_methods.mt_info, + NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_ERROR((AE_INFO, "Evaluating %s: %i", + smbus_methods.mt_info, status)); + return -EIO; + } + + obj = buffer.pointer; + if (obj && obj->type == ACPI_TYPE_PACKAGE) + obj = obj->package.elements; + else { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + kfree(buffer.pointer); + return -EIO; + } + + if (obj->type != ACPI_TYPE_INTEGER) { + ACPI_ERROR((AE_INFO, "Invalid argument type")); + kfree(buffer.pointer); + return -EIO; + } else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SMBus CMI Version %x" + "\n", (int)obj->integer.value)); + + kfree(buffer.pointer); + smbus_cmi->cap_info = 1; + } else if (!strcmp(name, smbus_methods.mt_sbr)) + smbus_cmi->cap_read = 1; + else if (!strcmp(name, smbus_methods.mt_sbw)) + smbus_cmi->cap_write = 1; + else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported CMI method: %s\n", + name)); + + return 0; +} + +static acpi_status acpi_smbus_cmi_query_methods(acpi_handle handle, u32 level, + void *context, void **return_value) +{ + char node_name[5]; + struct acpi_buffer buffer = { sizeof(node_name), node_name }; + struct acpi_smbus_cmi *smbus_cmi = context; + acpi_status status; + + status = acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + + if (ACPI_SUCCESS(status)) + acpi_smbus_cmi_add_cap(smbus_cmi, node_name); + + return AE_OK; +} + +static int acpi_smbus_cmi_add(struct acpi_device *device) +{ + struct acpi_smbus_cmi *smbus_cmi; + + smbus_cmi = kzalloc(sizeof(struct acpi_smbus_cmi), GFP_KERNEL); + if (!smbus_cmi) + return -ENOMEM; + + smbus_cmi->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_SMBUS_HC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_SMBUS_HC_CLASS); + device->driver_data = smbus_cmi; + smbus_cmi->cap_info = 0; + smbus_cmi->cap_read = 0; + smbus_cmi->cap_write = 0; + + acpi_walk_namespace(ACPI_TYPE_METHOD, smbus_cmi->handle, 1, + acpi_smbus_cmi_query_methods, smbus_cmi, NULL); + + if (smbus_cmi->cap_info == 0) + goto err; + + snprintf(smbus_cmi->adapter.name, sizeof(smbus_cmi->adapter.name), + "SMBus CMI adapter %s (%s)", + acpi_device_name(device), + acpi_device_uid(device)); + smbus_cmi->adapter.owner = THIS_MODULE; + smbus_cmi->adapter.algo = &acpi_smbus_cmi_algorithm; + smbus_cmi->adapter.algo_data = smbus_cmi; + smbus_cmi->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + smbus_cmi->adapter.dev.parent = &device->dev; + + if (i2c_add_adapter(&smbus_cmi->adapter)) { + dev_err(&device->dev, "Couldn't register adapter!\n"); + goto err; + } + + return 0; + +err: + kfree(smbus_cmi); + device->driver_data = NULL; + return -EIO; +} + +static int acpi_smbus_cmi_remove(struct acpi_device *device, int type) +{ + struct acpi_smbus_cmi *smbus_cmi = acpi_driver_data(device); + + i2c_del_adapter(&smbus_cmi->adapter); + kfree(smbus_cmi); + device->driver_data = NULL; + + return 0; +} + +static struct acpi_driver acpi_smbus_cmi_driver = { + .name = ACPI_SMBUS_HC_DEVICE_NAME, + .class = ACPI_SMBUS_HC_CLASS, + .ids = acpi_smbus_cmi_ids, + .ops = { + .add = acpi_smbus_cmi_add, + .remove = acpi_smbus_cmi_remove, + }, +}; + +static int __init acpi_smbus_cmi_init(void) +{ + return acpi_bus_register_driver(&acpi_smbus_cmi_driver); +} + +static void __exit acpi_smbus_cmi_exit(void) +{ + acpi_bus_unregister_driver(&acpi_smbus_cmi_driver); +} + +module_init(acpi_smbus_cmi_init); +module_exit(acpi_smbus_cmi_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Crane Cai "); +MODULE_DESCRIPTION("ACPI SMBus CMI driver"); -- cgit v1.2.3 From cfd550ed3d3bd509b475c7a9d425fc63bf843a7c Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 18 Sep 2009 22:45:52 +0200 Subject: i2c: Clearly mark ACPI drivers as such Now that we have ACPI-based SMBus controller drivers, and we will start telling users to use them instead of native drivers when I/O resources conflict, I think it would be good to clearly mark ACPI drivers as such in Kconfig. This is exactly the same as we just did for hwmon drivers. Signed-off-by: Jean Delvare Cc: Crane Cai --- drivers/i2c/busses/Kconfig | 27 ++++++++++++++++----------- drivers/i2c/busses/Makefile | 2 +- 2 files changed, 17 insertions(+), 12 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 838bd1e4efb..6bedd2fcfc1 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -232,6 +232,22 @@ config I2C_VIAPRO This driver can also be built as a module. If so, the module will be called i2c-viapro. +if ACPI + +comment "ACPI drivers" + +config I2C_SCMI + tristate "SMBus Control Method Interface" + help + This driver supports the SMBus Control Method Interface. It needs the + BIOS to declare ACPI control methods as described in the SMBus Control + Method Interface specification. + + To compile this driver as a module, choose M here: + the module will be called i2c-scmi. + +endif # ACPI + comment "Mac SMBus host controller drivers" depends on PPC_CHRP || PPC_PMAC @@ -762,15 +778,4 @@ config SCx200_ACB This support is also available as a module. If so, the module will be called scx200_acb. -config I2C_SCMI - tristate "SMBus Control Method Interface" - depends on ACPI - help - This driver supports the SMBus Control Method Interface. It needs the - BIOS to declare ACPI control methods as described in the SMBus Control - Method Interface specification. - - To compile this driver as a module, choose M here: - the module will be called i2c-scmi. - endmenu diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 060da43cf7d..ff937ac69f5 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -2,7 +2,7 @@ # Makefile for the i2c bus drivers. # -# SMBus CMI driver +# ACPI drivers obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o # PC SMBus host controller drivers -- cgit v1.2.3 From b0999cc55bd49e315ec82d2fb770a0d9ef7cbed8 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Mon, 7 Sep 2009 12:00:13 +0200 Subject: i2c-mv64xxx: correct mv64xxx_i2c_intr() return type The mv64xxx_i2c_intr() irq handler in drivers/i2c/busses/i2c-mv64xxx.c is declared as returning 'int', resulting in this compile-time warning: drivers/i2c/busses/i2c-mv64xxx.c: In function 'mv64xxx_i2c_probe': drivers/i2c/busses/i2c-mv64xxx.c:540: warning: passing argument 2 of 'request_irq' from incompatible pointer type Fix: correct the return type to 'irqreturn_t'. Signed-off-by: Mikael Pettersson Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-mv64xxx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index c3869d94ad4..bbab0e16663 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -293,13 +293,13 @@ mv64xxx_i2c_do_action(struct mv64xxx_i2c_data *drv_data) } } -static int +static irqreturn_t mv64xxx_i2c_intr(int irq, void *dev_id) { struct mv64xxx_i2c_data *drv_data = dev_id; unsigned long flags; u32 status; - int rc = IRQ_NONE; + irqreturn_t rc = IRQ_NONE; spin_lock_irqsave(&drv_data->lock, flags); while (readl(drv_data->reg_base + MV64XXX_I2C_REG_CONTROL) & -- cgit v1.2.3 From 5d3f33318a6c1f79f89e3dd2c7ddc11e0da14895 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 19 Sep 2009 09:09:50 +0200 Subject: [PATCH] i2c-imx: make bus available early As I2C is used by PMICs also, make the busses available early via subsys_initcall(). Signed-off-by: Wolfram Sang Signed-off-by: Ben Dooks --- drivers/i2c/busses/i2c-imx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/i2c/busses') diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 0b486a63460..4afba3ec2a6 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -609,13 +609,12 @@ static int __init i2c_adap_imx_init(void) { return platform_driver_probe(&i2c_imx_driver, i2c_imx_probe); } +subsys_initcall(i2c_adap_imx_init); static void __exit i2c_adap_imx_exit(void) { platform_driver_unregister(&i2c_imx_driver); } - -module_init(i2c_adap_imx_init); module_exit(i2c_adap_imx_exit); MODULE_LICENSE("GPL"); -- cgit v1.2.3