From c81109332068b44db4fd7b650c0e6bc0fc1411f1 Mon Sep 17 00:00:00 2001 From: Tushar Behera Date: Mon, 19 Nov 2012 16:31:42 +0530 Subject: i2c: core: Remove definition of i2c_smbus_process_call i2c_smbus_process_call has no users in the kernel, so this can be removed. Documentation for the same has been updated accordingly. Fixes following sparse warning. drivers/i2c/i2c-core.c:1871:5: warning: symbol 'i2c_smbus_process_call' was not declared. Should it be static? [wsa: updated the documentation] Signed-off-by: Tushar Behera Signed-off-by: Wolfram Sang --- Documentation/i2c/smbus-protocol | 4 ++-- Documentation/i2c/writing-clients | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/i2c/smbus-protocol b/Documentation/i2c/smbus-protocol index d1f22618e14b..6012b12b3510 100644 --- a/Documentation/i2c/smbus-protocol +++ b/Documentation/i2c/smbus-protocol @@ -137,8 +137,8 @@ available for writes where the two data bytes are the other way around (not SMBus compliant, but very popular.) -SMBus Process Call: i2c_smbus_process_call() -============================================= +SMBus Process Call: +=================== This command selects a device register (through the Comm byte), sends 16 bits of data to it, and reads 16 bits of data in return. diff --git a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients index 3a94b0e6f601..6b344b516bff 100644 --- a/Documentation/i2c/writing-clients +++ b/Documentation/i2c/writing-clients @@ -365,8 +365,6 @@ in terms of it. Never use this function directly! s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command); s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value); - s32 i2c_smbus_process_call(struct i2c_client *client, - u8 command, u16 value); s32 i2c_smbus_read_block_data(struct i2c_client *client, u8 command, u8 *values); s32 i2c_smbus_write_block_data(struct i2c_client *client, @@ -381,6 +379,8 @@ These ones were removed from i2c-core because they had no users, but could be added back later if needed: s32 i2c_smbus_write_quick(struct i2c_client *client, u8 value); + s32 i2c_smbus_process_call(struct i2c_client *client, + u8 command, u16 value); s32 i2c_smbus_block_process_call(struct i2c_client *client, u8 command, u8 length, u8 *values); -- cgit v1.2.3-59-g8ed1b From faf93ff6ed5f6406ca51ec6d60f85fa166fe545c Mon Sep 17 00:00:00 2001 From: Giridhar Maruthy Date: Thu, 24 Jan 2013 11:27:51 -0800 Subject: i2c: s3c2410: Add quirk to exclude GPIO config for exynos5440 Signed-off-by: Giridhar Maruthy Signed-off-by: Kukjin Kim Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt | 2 ++ drivers/i2c/busses/i2c-s3c2410.c | 2 ++ 2 files changed, 4 insertions(+) (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt b/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt index e9611ace8792..f98d4c5b5cca 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-s3c2410.txt @@ -8,6 +8,8 @@ Required properties: (b) "samsung, s3c2440-i2c", for i2c compatible with s3c2440 i2c. (c) "samsung, s3c2440-hdmiphy-i2c", for s3c2440-like i2c used inside HDMIPHY block found on several samsung SoCs + (d) "samsung, exynos5440-i2c", for s3c2440-like i2c used + on EXYNOS5440 which does not need GPIO configuration. - reg: physical base address of the controller and length of memory mapped region. - interrupts: interrupt number to the cpu. diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 3479ca76d2ae..5e33df7c6c82 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -111,6 +111,8 @@ static const struct of_device_id s3c24xx_i2c_match[] = { { .compatible = "samsung,s3c2440-i2c", .data = (void *)QUIRK_S3C2440 }, { .compatible = "samsung,s3c2440-hdmiphy-i2c", .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) }, + { .compatible = "samsung,exynos5440-i2c", + .data = (void *)(QUIRK_S3C2440 | QUIRK_NO_GPIO) }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); -- cgit v1.2.3-59-g8ed1b From c2db409cbc8751ccc7e6d2cc2e41af0d12ea637f Mon Sep 17 00:00:00 2001 From: Seth Heasley Date: Wed, 30 Jan 2013 15:25:32 +0000 Subject: i2c: i801: SMBus patch for Intel Avoton DeviceIDs This patch adds the PCU SMBus DeviceID for the Intel Avoton SOC. Signed-off-by: Seth Heasley Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-i801 | 1 + drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-i801.c | 3 +++ 3 files changed, 5 insertions(+) (limited to 'Documentation') diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index 157416e78cc4..8d71d5705b2a 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -22,6 +22,7 @@ Supported adapters: * Intel Panther Point (PCH) * Intel Lynx Point (PCH) * Intel Lynx Point-LP (PCH) + * Intel Avoton (SOC) Datasheets: Publicly available at the Intel website On Intel Patsburg and later chipsets, both the normal host SMBus controller diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bdca5111eb9d..77d28873b76f 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -106,6 +106,7 @@ config I2C_I801 Panther Point (PCH) Lynx Point (PCH) Lynx Point-LP (PCH) + Avoton (SOC) This driver can also be built as a module. If so, the module will be called i2c-i801. diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 3092387f6ef4..b00c29d8a5f1 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -53,6 +53,7 @@ Panther Point (PCH) 0x1e22 32 hard yes yes yes Lynx Point (PCH) 0x8c22 32 hard yes yes yes Lynx Point-LP (PCH) 0x9c22 32 hard yes yes yes + Avoton (SOC) 0x1f3c 32 hard yes yes yes Features supported by this driver: Software PEC no @@ -162,6 +163,7 @@ #define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1 0x1d71 #define PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2 0x1d72 #define PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS 0x1e22 +#define PCI_DEVICE_ID_INTEL_AVOTON_SMBUS 0x1f3c #define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 @@ -798,6 +800,7 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_PANTHERPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMBUS) }, { 0, } }; -- cgit v1.2.3-59-g8ed1b From 13f35ac14cd0a9a1c4f0034c4c40d0ae98844ce9 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Mon, 4 Feb 2013 14:54:10 -0500 Subject: i2c: Adding support for Intel iSMT SMBus 2.0 host controller The iSMT (Intel SMBus Message Transport) supports multi-master I2C/SMBus, as well as IPMI. It's operation is DMA-based and utilizes descriptors to initiate transactions on the bus. The iSMT hardware can act as both a master and a target, although this driver only supports being a master. Signed-off-by: Neil Horman Signed-off-by: Bill Brown Tested-by: Seth Heasley Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-ismt | 36 ++ drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-ismt.c | 963 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 1010 insertions(+) create mode 100644 Documentation/i2c/busses/i2c-ismt create mode 100644 drivers/i2c/busses/i2c-ismt.c (limited to 'Documentation') diff --git a/Documentation/i2c/busses/i2c-ismt b/Documentation/i2c/busses/i2c-ismt new file mode 100644 index 000000000000..737355822c0b --- /dev/null +++ b/Documentation/i2c/busses/i2c-ismt @@ -0,0 +1,36 @@ +Kernel driver i2c-ismt + +Supported adapters: + * Intel S12xx series SOCs + +Authors: + Bill Brown + + +Module Parameters +----------------- + +* bus_speed (unsigned int) +Allows changing of the bus speed. Normally, the bus speed is set by the BIOS +and never needs to be changed. However, some SMBus analyzers are too slow for +monitoring the bus during debug, thus the need for this module parameter. +Specify the bus speed in kHz. +Available bus frequency settings: + 0 no change + 80 kHz + 100 kHz + 400 kHz + 1000 kHz + + +Description +----------- + +The S12xx series of SOCs have a pair of integrated SMBus 2.0 controllers +targeted primarily at the microserver and storage markets. + +The S12xx series contain a pair of PCI functions. An output of lspci will show +something similar to the following: + + 00:13.0 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 0 + 00:13.1 System peripheral: Intel Corporation Centerton SMBus 2.0 Controller 1 diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 77d28873b76f..87df863ba110 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -122,6 +122,16 @@ config I2C_ISCH This driver can also be built as a module. If so, the module will be called i2c-isch. +config I2C_ISMT + tristate "Intel iSMT SMBus Controller" + depends on PCI && X86 + help + If you say yes to this option, support will be included for the Intel + iSMT SMBus host controller interface. + + This driver can also be built as a module. If so, the module will be + called i2c-ismt. + config I2C_PIIX4 tristate "Intel PIIX4 and compatible (ATI/AMD/Serverworks/Broadcom/SMSC)" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 6181f3ff263f..23c9d55bc880 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o obj-$(CONFIG_I2C_I801) += i2c-i801.o obj-$(CONFIG_I2C_ISCH) += i2c-isch.o +obj-$(CONFIG_I2C_ISMT) += i2c-ismt.o obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c new file mode 100644 index 000000000000..3f7a9cb6dfdd --- /dev/null +++ b/drivers/i2c/busses/i2c-ismt.c @@ -0,0 +1,963 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2012 Intel Corporation. All rights reserved. + * + * GPL LICENSE SUMMARY + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/* + * Supports the SMBus Message Transport (SMT) in the Intel Atom Processor + * S12xx Product Family. + * + * Features supported by this driver: + * Hardware PEC yes + * Block buffer yes + * Block process call transaction no + * Slave mode no + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* PCI Address Constants */ +#define SMBBAR 0 + +/* PCI DIDs for the Intel SMBus Message Transport (SMT) Devices */ +#define PCI_DEVICE_ID_INTEL_S1200_SMT0 0x0c59 +#define PCI_DEVICE_ID_INTEL_S1200_SMT1 0x0c5a + +#define ISMT_DESC_ENTRIES 32 /* number of descriptor entries */ +#define ISMT_MAX_RETRIES 3 /* number of SMBus retries to attempt */ + +/* Hardware Descriptor Constants - Control Field */ +#define ISMT_DESC_CWRL 0x01 /* Command/Write Length */ +#define ISMT_DESC_BLK 0X04 /* Perform Block Transaction */ +#define ISMT_DESC_FAIR 0x08 /* Set fairness flag upon successful arbit. */ +#define ISMT_DESC_PEC 0x10 /* Packet Error Code */ +#define ISMT_DESC_I2C 0x20 /* I2C Enable */ +#define ISMT_DESC_INT 0x40 /* Interrupt */ +#define ISMT_DESC_SOE 0x80 /* Stop On Error */ + +/* Hardware Descriptor Constants - Status Field */ +#define ISMT_DESC_SCS 0x01 /* Success */ +#define ISMT_DESC_DLTO 0x04 /* Data Low Time Out */ +#define ISMT_DESC_NAK 0x08 /* NAK Received */ +#define ISMT_DESC_CRC 0x10 /* CRC Error */ +#define ISMT_DESC_CLTO 0x20 /* Clock Low Time Out */ +#define ISMT_DESC_COL 0x40 /* Collisions */ +#define ISMT_DESC_LPR 0x80 /* Large Packet Received */ + +/* Macros */ +#define ISMT_DESC_ADDR_RW(addr, rw) (((addr) << 1) | (rw)) + +/* iSMT General Register address offsets (SMBBAR + ) */ +#define ISMT_GR_GCTRL 0x000 /* General Control */ +#define ISMT_GR_SMTICL 0x008 /* SMT Interrupt Cause Location */ +#define ISMT_GR_ERRINTMSK 0x010 /* Error Interrupt Mask */ +#define ISMT_GR_ERRAERMSK 0x014 /* Error AER Mask */ +#define ISMT_GR_ERRSTS 0x018 /* Error Status */ +#define ISMT_GR_ERRINFO 0x01c /* Error Information */ + +/* iSMT Master Registers */ +#define ISMT_MSTR_MDBA 0x100 /* Master Descriptor Base Address */ +#define ISMT_MSTR_MCTRL 0x108 /* Master Control */ +#define ISMT_MSTR_MSTS 0x10c /* Master Status */ +#define ISMT_MSTR_MDS 0x110 /* Master Descriptor Size */ +#define ISMT_MSTR_RPOLICY 0x114 /* Retry Policy */ + +/* iSMT Miscellaneous Registers */ +#define ISMT_SPGT 0x300 /* SMBus PHY Global Timing */ + +/* General Control Register (GCTRL) bit definitions */ +#define ISMT_GCTRL_TRST 0x04 /* Target Reset */ +#define ISMT_GCTRL_KILL 0x08 /* Kill */ +#define ISMT_GCTRL_SRST 0x40 /* Soft Reset */ + +/* Master Control Register (MCTRL) bit definitions */ +#define ISMT_MCTRL_SS 0x01 /* Start/Stop */ +#define ISMT_MCTRL_MEIE 0x10 /* Master Error Interrupt Enable */ +#define ISMT_MCTRL_FMHP 0x00ff0000 /* Firmware Master Head Ptr (FMHP) */ + +/* Master Status Register (MSTS) bit definitions */ +#define ISMT_MSTS_HMTP 0xff0000 /* HW Master Tail Pointer (HMTP) */ +#define ISMT_MSTS_MIS 0x20 /* Master Interrupt Status (MIS) */ +#define ISMT_MSTS_MEIS 0x10 /* Master Error Int Status (MEIS) */ +#define ISMT_MSTS_IP 0x01 /* In Progress */ + +/* Master Descriptor Size (MDS) bit definitions */ +#define ISMT_MDS_MASK 0xff /* Master Descriptor Size mask (MDS) */ + +/* SMBus PHY Global Timing Register (SPGT) bit definitions */ +#define ISMT_SPGT_SPD_MASK 0xc0000000 /* SMBus Speed mask */ +#define ISMT_SPGT_SPD_80K 0x00 /* 80 kHz */ +#define ISMT_SPGT_SPD_100K (0x1 << 30) /* 100 kHz */ +#define ISMT_SPGT_SPD_400K (0x2 << 30) /* 400 kHz */ +#define ISMT_SPGT_SPD_1M (0x3 << 30) /* 1 MHz */ + + +/* MSI Control Register (MSICTL) bit definitions */ +#define ISMT_MSICTL_MSIE 0x01 /* MSI Enable */ + +/* iSMT Hardware Descriptor */ +struct ismt_desc { + u8 tgtaddr_rw; /* target address & r/w bit */ + u8 wr_len_cmd; /* write length in bytes or a command */ + u8 rd_len; /* read length */ + u8 control; /* control bits */ + u8 status; /* status bits */ + u8 retry; /* collision retry and retry count */ + u8 rxbytes; /* received bytes */ + u8 txbytes; /* transmitted bytes */ + u32 dptr_low; /* lower 32 bit of the data pointer */ + u32 dptr_high; /* upper 32 bit of the data pointer */ +} __packed; + +struct ismt_priv { + struct i2c_adapter adapter; + void *smba; /* PCI BAR */ + struct pci_dev *pci_dev; + struct ismt_desc *hw; /* descriptor virt base addr */ + dma_addr_t io_rng_dma; /* descriptor HW base addr */ + u8 head; /* ring buffer head pointer */ + struct completion cmp; /* interrupt completion */ + u8 dma_buffer[I2C_SMBUS_BLOCK_MAX + 1]; /* temp R/W data buffer */ + bool using_msi; /* type of interrupt flag */ +}; + +/** + * ismt_ids - PCI device IDs supported by this driver + */ +static const DEFINE_PCI_DEVICE_TABLE(ismt_ids) = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_S1200_SMT1) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, ismt_ids); + +/* Bus speed control bits for slow debuggers - refer to the docs for usage */ +static unsigned int bus_speed; +module_param(bus_speed, uint, S_IRUGO); +MODULE_PARM_DESC(bus_speed, "Bus Speed in kHz (0 = BIOS default)"); + +/** + * __ismt_desc_dump() - dump the contents of a specific descriptor + */ +static void __ismt_desc_dump(struct device *dev, const struct ismt_desc *desc) +{ + + dev_dbg(dev, "Descriptor struct: %p\n", desc); + dev_dbg(dev, "\ttgtaddr_rw=0x%02X\n", desc->tgtaddr_rw); + dev_dbg(dev, "\twr_len_cmd=0x%02X\n", desc->wr_len_cmd); + dev_dbg(dev, "\trd_len= 0x%02X\n", desc->rd_len); + dev_dbg(dev, "\tcontrol= 0x%02X\n", desc->control); + dev_dbg(dev, "\tstatus= 0x%02X\n", desc->status); + dev_dbg(dev, "\tretry= 0x%02X\n", desc->retry); + dev_dbg(dev, "\trxbytes= 0x%02X\n", desc->rxbytes); + dev_dbg(dev, "\ttxbytes= 0x%02X\n", desc->txbytes); + dev_dbg(dev, "\tdptr_low= 0x%08X\n", desc->dptr_low); + dev_dbg(dev, "\tdptr_high= 0x%08X\n", desc->dptr_high); +} +/** + * ismt_desc_dump() - dump the contents of a descriptor for debug purposes + * @priv: iSMT private data + */ +static void ismt_desc_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + struct ismt_desc *desc = &priv->hw[priv->head]; + + dev_dbg(dev, "Dump of the descriptor struct: 0x%X\n", priv->head); + __ismt_desc_dump(dev, desc); +} + +/** + * ismt_gen_reg_dump() - dump the iSMT General Registers + * @priv: iSMT private data + */ +static void ismt_gen_reg_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + + dev_dbg(dev, "Dump of the iSMT General Registers\n"); + dev_dbg(dev, " GCTRL.... : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_GCTRL, + readl(priv->smba + ISMT_GR_GCTRL)); + dev_dbg(dev, " SMTICL... : (0x%p)=0x%016llX\n", + priv->smba + ISMT_GR_SMTICL, + (long long unsigned int)readq(priv->smba + ISMT_GR_SMTICL)); + dev_dbg(dev, " ERRINTMSK : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRINTMSK, + readl(priv->smba + ISMT_GR_ERRINTMSK)); + dev_dbg(dev, " ERRAERMSK : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRAERMSK, + readl(priv->smba + ISMT_GR_ERRAERMSK)); + dev_dbg(dev, " ERRSTS... : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRSTS, + readl(priv->smba + ISMT_GR_ERRSTS)); + dev_dbg(dev, " ERRINFO.. : (0x%p)=0x%X\n", + priv->smba + ISMT_GR_ERRINFO, + readl(priv->smba + ISMT_GR_ERRINFO)); +} + +/** + * ismt_mstr_reg_dump() - dump the iSMT Master Registers + * @priv: iSMT private data + */ +static void ismt_mstr_reg_dump(struct ismt_priv *priv) +{ + struct device *dev = &priv->pci_dev->dev; + + dev_dbg(dev, "Dump of the iSMT Master Registers\n"); + dev_dbg(dev, " MDBA..... : (0x%p)=0x%016llX\n", + priv->smba + ISMT_MSTR_MDBA, + (long long unsigned int)readq(priv->smba + ISMT_MSTR_MDBA)); + dev_dbg(dev, " MCTRL.... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MCTRL, + readl(priv->smba + ISMT_MSTR_MCTRL)); + dev_dbg(dev, " MSTS..... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MSTS, + readl(priv->smba + ISMT_MSTR_MSTS)); + dev_dbg(dev, " MDS...... : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_MDS, + readl(priv->smba + ISMT_MSTR_MDS)); + dev_dbg(dev, " RPOLICY.. : (0x%p)=0x%X\n", + priv->smba + ISMT_MSTR_RPOLICY, + readl(priv->smba + ISMT_MSTR_RPOLICY)); + dev_dbg(dev, " SPGT..... : (0x%p)=0x%X\n", + priv->smba + ISMT_SPGT, + readl(priv->smba + ISMT_SPGT)); +} + +/** + * ismt_submit_desc() - add a descriptor to the ring + * @priv: iSMT private data + */ +static void ismt_submit_desc(struct ismt_priv *priv) +{ + uint fmhp; + uint val; + + ismt_desc_dump(priv); + ismt_gen_reg_dump(priv); + ismt_mstr_reg_dump(priv); + + /* Set the FMHP (Firmware Master Head Pointer)*/ + fmhp = ((priv->head + 1) % ISMT_DESC_ENTRIES) << 16; + val = readl(priv->smba + ISMT_MSTR_MCTRL); + writel((val & ~ISMT_MCTRL_FMHP) | fmhp, + priv->smba + ISMT_MSTR_MCTRL); + + /* Set the start bit */ + val = readl(priv->smba + ISMT_MSTR_MCTRL); + writel(val | ISMT_MCTRL_SS, + priv->smba + ISMT_MSTR_MCTRL); +} + +/** + * ismt_process_desc() - handle the completion of the descriptor + * @desc: the iSMT hardware descriptor + * @data: data buffer from the upper layer + * @priv: ismt_priv struct holding our dma buffer + * @size: SMBus transaction type + * @read_write: flag to indicate if this is a read or write + */ +static int ismt_process_desc(const struct ismt_desc *desc, + union i2c_smbus_data *data, + struct ismt_priv *priv, int size, + char read_write) +{ + u8 *dma_buffer = priv->dma_buffer; + + dev_dbg(&priv->pci_dev->dev, "Processing completed descriptor\n"); + __ismt_desc_dump(&priv->pci_dev->dev, desc); + + if (desc->status & ISMT_DESC_SCS) { + if (read_write == I2C_SMBUS_WRITE && + size != I2C_SMBUS_PROC_CALL) + return 0; + + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + data->byte = dma_buffer[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = dma_buffer[0] | (dma_buffer[1] << 8); + break; + case I2C_SMBUS_BLOCK_DATA: + memcpy(&data->block[1], dma_buffer, desc->rxbytes); + data->block[0] = desc->rxbytes; + break; + } + return 0; + } + + if (likely(desc->status & ISMT_DESC_NAK)) + return -ENXIO; + + if (desc->status & ISMT_DESC_CRC) + return -EBADMSG; + + if (desc->status & ISMT_DESC_COL) + return -EAGAIN; + + if (desc->status & ISMT_DESC_LPR) + return -EPROTO; + + if (desc->status & (ISMT_DESC_DLTO | ISMT_DESC_CLTO)) + return -ETIMEDOUT; + + return -EIO; +} + +/** + * ismt_access() - process an SMBus command + * @adap: the i2c host adapter + * @addr: address of the i2c/SMBus target + * @flags: command options + * @read_write: read from or write to device + * @command: the i2c/SMBus command to issue + * @size: SMBus transaction type + * @data: read/write data buffer + */ +static int ismt_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data *data) +{ + int ret; + dma_addr_t dma_addr = 0; /* address of the data buffer */ + u8 dma_size = 0; + enum dma_data_direction dma_direction = 0; + struct ismt_desc *desc; + struct ismt_priv *priv = i2c_get_adapdata(adap); + struct device *dev = &priv->pci_dev->dev; + + desc = &priv->hw[priv->head]; + + /* Initialize the descriptor */ + memset(desc, 0, sizeof(struct ismt_desc)); + desc->tgtaddr_rw = ISMT_DESC_ADDR_RW(addr, read_write); + + /* Initialize common control bits */ + if (likely(priv->using_msi)) + desc->control = ISMT_DESC_INT | ISMT_DESC_FAIR; + else + desc->control = ISMT_DESC_FAIR; + + if ((flags & I2C_CLIENT_PEC) && (size != I2C_SMBUS_QUICK) + && (size != I2C_SMBUS_I2C_BLOCK_DATA)) + desc->control |= ISMT_DESC_PEC; + + switch (size) { + case I2C_SMBUS_QUICK: + dev_dbg(dev, "I2C_SMBUS_QUICK\n"); + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) { + /* + * Send Byte + * The command field contains the write data + */ + dev_dbg(dev, "I2C_SMBUS_BYTE: WRITE\n"); + desc->control |= ISMT_DESC_CWRL; + desc->wr_len_cmd = command; + } else { + /* Receive Byte */ + dev_dbg(dev, "I2C_SMBUS_BYTE: READ\n"); + dma_size = 1; + dma_direction = DMA_FROM_DEVICE; + desc->rd_len = 1; + } + break; + + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_WRITE) { + /* + * Write Byte + * Command plus 1 data byte + */ + dev_dbg(dev, "I2C_SMBUS_BYTE_DATA: WRITE\n"); + desc->wr_len_cmd = 2; + dma_size = 2; + dma_direction = DMA_TO_DEVICE; + priv->dma_buffer[0] = command; + priv->dma_buffer[1] = data->byte; + } else { + /* Read Byte */ + dev_dbg(dev, "I2C_SMBUS_BYTE_DATA: READ\n"); + desc->control |= ISMT_DESC_CWRL; + desc->wr_len_cmd = command; + desc->rd_len = 1; + dma_size = 1; + dma_direction = DMA_FROM_DEVICE; + } + break; + + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_WRITE) { + /* Write Word */ + dev_dbg(dev, "I2C_SMBUS_WORD_DATA: WRITE\n"); + desc->wr_len_cmd = 3; + dma_size = 3; + dma_direction = DMA_TO_DEVICE; + priv->dma_buffer[0] = command; + priv->dma_buffer[1] = data->word & 0xff; + priv->dma_buffer[2] = data->word >> 8; + } else { + /* Read Word */ + dev_dbg(dev, "I2C_SMBUS_WORD_DATA: READ\n"); + desc->wr_len_cmd = command; + desc->control |= ISMT_DESC_CWRL; + desc->rd_len = 2; + dma_size = 2; + dma_direction = DMA_FROM_DEVICE; + } + break; + + case I2C_SMBUS_PROC_CALL: + dev_dbg(dev, "I2C_SMBUS_PROC_CALL\n"); + desc->wr_len_cmd = 3; + desc->rd_len = 2; + dma_size = 3; + dma_direction = DMA_BIDIRECTIONAL; + priv->dma_buffer[0] = command; + priv->dma_buffer[1] = data->word & 0xff; + priv->dma_buffer[2] = data->word >> 8; + break; + + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_WRITE) { + /* Block Write */ + dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA: WRITE\n"); + dma_size = data->block[0] + 1; + dma_direction = DMA_TO_DEVICE; + desc->wr_len_cmd = dma_size; + desc->control |= ISMT_DESC_BLK; + priv->dma_buffer[0] = command; + memcpy(&priv->dma_buffer[1], &data->block[1], dma_size); + } else { + /* Block Read */ + dev_dbg(dev, "I2C_SMBUS_BLOCK_DATA: READ\n"); + dma_size = I2C_SMBUS_BLOCK_MAX; + dma_direction = DMA_FROM_DEVICE; + desc->rd_len = dma_size; + desc->wr_len_cmd = command; + desc->control |= (ISMT_DESC_BLK | ISMT_DESC_CWRL); + } + break; + + default: + dev_err(dev, "Unsupported transaction %d\n", + size); + return -EOPNOTSUPP; + } + + /* map the data buffer */ + if (dma_size != 0) { + dev_dbg(dev, " dev=%p\n", dev); + dev_dbg(dev, " data=%p\n", data); + dev_dbg(dev, " dma_buffer=%p\n", priv->dma_buffer); + dev_dbg(dev, " dma_size=%d\n", dma_size); + dev_dbg(dev, " dma_direction=%d\n", dma_direction); + + dma_addr = dma_map_single(dev, + priv->dma_buffer, + dma_size, + dma_direction); + + if (dma_mapping_error(dev, dma_addr)) { + dev_err(dev, "Error in mapping dma buffer %p\n", + priv->dma_buffer); + return -EIO; + } + + dev_dbg(dev, " dma_addr = 0x%016llX\n", + dma_addr); + + desc->dptr_low = lower_32_bits(dma_addr); + desc->dptr_high = upper_32_bits(dma_addr); + } + + INIT_COMPLETION(priv->cmp); + + /* Add the descriptor */ + ismt_submit_desc(priv); + + /* Now we wait for interrupt completion, 1s */ + ret = wait_for_completion_timeout(&priv->cmp, HZ*1); + + /* unmap the data buffer */ + if (dma_size != 0) + dma_unmap_single(&adap->dev, dma_addr, dma_size, dma_direction); + + if (unlikely(!ret)) { + dev_err(dev, "completion wait timed out\n"); + ret = -ETIMEDOUT; + goto out; + } + + /* do any post processing of the descriptor here */ + ret = ismt_process_desc(desc, data, priv, size, read_write); + +out: + /* Update the ring pointer */ + priv->head++; + priv->head %= ISMT_DESC_ENTRIES; + + return ret; +} + +/** + * ismt_func() - report which i2c commands are supported by this adapter + * @adap: the i2c host adapter + */ +static u32 ismt_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_QUICK | + I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_PEC; +} + +/** + * smbus_algorithm - the adapter algorithm and supported functionality + * @smbus_xfer: the adapter algorithm + * @functionality: functionality supported by the adapter + */ +static const struct i2c_algorithm smbus_algorithm = { + .smbus_xfer = ismt_access, + .functionality = ismt_func, +}; + +/** + * ismt_handle_isr() - interrupt handler bottom half + * @priv: iSMT private data + */ +static irqreturn_t ismt_handle_isr(struct ismt_priv *priv) +{ + complete(&priv->cmp); + + return IRQ_HANDLED; +} + + +/** + * ismt_do_interrupt() - IRQ interrupt handler + * @vec: interrupt vector + * @data: iSMT private data + */ +static irqreturn_t ismt_do_interrupt(int vec, void *data) +{ + u32 val; + struct ismt_priv *priv = data; + + /* + * check to see it's our interrupt, return IRQ_NONE if not ours + * since we are sharing interrupt + */ + val = readl(priv->smba + ISMT_MSTR_MSTS); + + if (!(val & (ISMT_MSTS_MIS | ISMT_MSTS_MEIS))) + return IRQ_NONE; + else + writel(val | ISMT_MSTS_MIS | ISMT_MSTS_MEIS, + priv->smba + ISMT_MSTR_MSTS); + + return ismt_handle_isr(priv); +} + +/** + * ismt_do_msi_interrupt() - MSI interrupt handler + * @vec: interrupt vector + * @data: iSMT private data + */ +static irqreturn_t ismt_do_msi_interrupt(int vec, void *data) +{ + return ismt_handle_isr(data); +} + +/** + * ismt_hw_init() - initialize the iSMT hardware + * @priv: iSMT private data + */ +static void ismt_hw_init(struct ismt_priv *priv) +{ + u32 val; + struct device *dev = &priv->pci_dev->dev; + + /* initialize the Master Descriptor Base Address (MDBA) */ + writeq(priv->io_rng_dma, priv->smba + ISMT_MSTR_MDBA); + + /* initialize the Master Control Register (MCTRL) */ + writel(ISMT_MCTRL_MEIE, priv->smba + ISMT_MSTR_MCTRL); + + /* initialize the Master Status Register (MSTS) */ + writel(0, priv->smba + ISMT_MSTR_MSTS); + + /* initialize the Master Descriptor Size (MDS) */ + val = readl(priv->smba + ISMT_MSTR_MDS); + writel((val & ~ISMT_MDS_MASK) | (ISMT_DESC_ENTRIES - 1), + priv->smba + ISMT_MSTR_MDS); + + /* + * Set the SMBus speed (could use this for slow HW debuggers) + */ + + val = readl(priv->smba + ISMT_SPGT); + + switch (bus_speed) { + case 0: + break; + + case 80: + dev_dbg(dev, "Setting SMBus clock to 80 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_80K), + priv->smba + ISMT_SPGT); + break; + + case 100: + dev_dbg(dev, "Setting SMBus clock to 100 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_100K), + priv->smba + ISMT_SPGT); + break; + + case 400: + dev_dbg(dev, "Setting SMBus clock to 400 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_400K), + priv->smba + ISMT_SPGT); + break; + + case 1000: + dev_dbg(dev, "Setting SMBus clock to 1000 kHz\n"); + writel(((val & ~ISMT_SPGT_SPD_MASK) | ISMT_SPGT_SPD_1M), + priv->smba + ISMT_SPGT); + break; + + default: + dev_warn(dev, "Invalid SMBus clock speed, only 0, 80, 100, 400, and 1000 are valid\n"); + break; + } + + val = readl(priv->smba + ISMT_SPGT); + + switch (val & ISMT_SPGT_SPD_MASK) { + case ISMT_SPGT_SPD_80K: + bus_speed = 80; + break; + case ISMT_SPGT_SPD_100K: + bus_speed = 100; + break; + case ISMT_SPGT_SPD_400K: + bus_speed = 400; + break; + case ISMT_SPGT_SPD_1M: + bus_speed = 1000; + break; + } + dev_dbg(dev, "SMBus clock is running at %d kHz\n", bus_speed); +} + +/** + * ismt_dev_init() - initialize the iSMT data structures + * @priv: iSMT private data + */ +static int ismt_dev_init(struct ismt_priv *priv) +{ + /* allocate memory for the descriptor */ + priv->hw = dmam_alloc_coherent(&priv->pci_dev->dev, + (ISMT_DESC_ENTRIES + * sizeof(struct ismt_desc)), + &priv->io_rng_dma, + GFP_KERNEL); + if (!priv->hw) + return -ENOMEM; + + memset(priv->hw, 0, (ISMT_DESC_ENTRIES * sizeof(struct ismt_desc))); + + priv->head = 0; + init_completion(&priv->cmp); + + return 0; +} + +/** + * ismt_int_init() - initialize interrupts + * @priv: iSMT private data + */ +static int ismt_int_init(struct ismt_priv *priv) +{ + int err; + + /* Try using MSI interrupts */ + err = pci_enable_msi(priv->pci_dev); + if (err) { + dev_warn(&priv->pci_dev->dev, + "Unable to use MSI interrupts, falling back to legacy\n"); + goto intx; + } + + err = devm_request_irq(&priv->pci_dev->dev, + priv->pci_dev->irq, + ismt_do_msi_interrupt, + 0, + "ismt-msi", + priv); + if (err) { + pci_disable_msi(priv->pci_dev); + goto intx; + } + + priv->using_msi = true; + goto done; + + /* Try using legacy interrupts */ +intx: + err = devm_request_irq(&priv->pci_dev->dev, + priv->pci_dev->irq, + ismt_do_interrupt, + IRQF_SHARED, + "ismt-intx", + priv); + if (err) { + dev_err(&priv->pci_dev->dev, "no usable interrupts\n"); + return -ENODEV; + } + + priv->using_msi = false; + +done: + return 0; +} + +static struct pci_driver ismt_driver; + +/** + * ismt_probe() - probe for iSMT devices + * @pdev: PCI-Express device + * @id: PCI-Express device ID + */ +static int +ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int err; + struct ismt_priv *priv; + unsigned long start, len; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + pci_set_drvdata(pdev, priv); + i2c_set_adapdata(&priv->adapter, priv); + priv->adapter.owner = THIS_MODULE; + + priv->adapter.class = I2C_CLASS_HWMON; + + priv->adapter.algo = &smbus_algorithm; + + /* set up the sysfs linkage to our parent device */ + priv->adapter.dev.parent = &pdev->dev; + + /* number of retries on lost arbitration */ + priv->adapter.retries = ISMT_MAX_RETRIES; + + priv->pci_dev = pdev; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Failed to enable SMBus PCI device (%d)\n", + err); + return err; + } + + /* enable bus mastering */ + pci_set_master(pdev); + + /* Determine the address of the SMBus area */ + start = pci_resource_start(pdev, SMBBAR); + len = pci_resource_len(pdev, SMBBAR); + if (!start || !len) { + dev_err(&pdev->dev, + "SMBus base address uninitialized, upgrade BIOS\n"); + return -ENODEV; + } + + snprintf(priv->adapter.name, sizeof(priv->adapter.name), + "SMBus iSMT adapter at %lx", start); + + dev_dbg(&priv->pci_dev->dev, " start=0x%lX\n", start); + dev_dbg(&priv->pci_dev->dev, " len=0x%lX\n", len); + + err = acpi_check_resource_conflict(&pdev->resource[SMBBAR]); + if (err) { + dev_err(&pdev->dev, "ACPI resource conflict!\n"); + return err; + } + + err = pci_request_region(pdev, SMBBAR, ismt_driver.name); + if (err) { + dev_err(&pdev->dev, + "Failed to request SMBus region 0x%lx-0x%lx\n", + start, start + len); + return err; + } + + priv->smba = pcim_iomap(pdev, SMBBAR, len); + if (!priv->smba) { + dev_err(&pdev->dev, "Unable to ioremap SMBus BAR\n"); + err = -ENODEV; + goto fail; + } + + if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) != 0) || + (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)) != 0)) { + if ((pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) != 0) || + (pci_set_consistent_dma_mask(pdev, + DMA_BIT_MASK(32)) != 0)) { + dev_err(&pdev->dev, "pci_set_dma_mask fail %p\n", + pdev); + goto fail; + } + } + + err = ismt_dev_init(priv); + if (err) + goto fail; + + ismt_hw_init(priv); + + err = ismt_int_init(priv); + if (err) + goto fail; + + err = i2c_add_adapter(&priv->adapter); + if (err) { + dev_err(&pdev->dev, "Failed to add SMBus iSMT adapter\n"); + err = -ENODEV; + goto fail; + } + return 0; + +fail: + pci_release_region(pdev, SMBBAR); + return err; +} + +/** + * ismt_remove() - release driver resources + * @pdev: PCI-Express device + */ +static void ismt_remove(struct pci_dev *pdev) +{ + struct ismt_priv *priv = pci_get_drvdata(pdev); + + i2c_del_adapter(&priv->adapter); + pci_release_region(pdev, SMBBAR); +} + +/** + * ismt_suspend() - place the device in suspend + * @pdev: PCI-Express device + * @mesg: PM message + */ +#ifdef CONFIG_PM +static int ismt_suspend(struct pci_dev *pdev, pm_message_t mesg) +{ + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, mesg)); + return 0; +} + +/** + * ismt_resume() - PCI resume code + * @pdev: PCI-Express device + */ +static int ismt_resume(struct pci_dev *pdev) +{ + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + return pci_enable_device(pdev); +} + +#else + +#define ismt_suspend NULL +#define ismt_resume NULL + +#endif + +static struct pci_driver ismt_driver = { + .name = "ismt_smbus", + .id_table = ismt_ids, + .probe = ismt_probe, + .remove = ismt_remove, + .suspend = ismt_suspend, + .resume = ismt_resume, +}; + +module_pci_driver(ismt_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Bill E. Brown "); +MODULE_DESCRIPTION("Intel SMBus Message Transport (iSMT) driver"); -- cgit v1.2.3-59-g8ed1b From 974d6a3797001c88e59ccb78567c6d71ac526c43 Mon Sep 17 00:00:00 2001 From: Amaury Decrême Date: Mon, 28 Jan 2013 22:21:05 +0100 Subject: i2c: sis630: Add SIS964 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Amaury Decrême Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-sis630 | 9 ++++ drivers/i2c/busses/Kconfig | 4 +- drivers/i2c/busses/i2c-sis630.c | 88 ++++++++++++++++++++++++------------- 3 files changed, 69 insertions(+), 32 deletions(-) (limited to 'Documentation') diff --git a/Documentation/i2c/busses/i2c-sis630 b/Documentation/i2c/busses/i2c-sis630 index 0b9697366930..ee7943631074 100644 --- a/Documentation/i2c/busses/i2c-sis630 +++ b/Documentation/i2c/busses/i2c-sis630 @@ -4,9 +4,11 @@ Supported adapters: * Silicon Integrated Systems Corp (SiS) 630 chipset (Datasheet: available at http://www.sfr-fresh.com/linux) 730 chipset + 964 chipset * Possible other SiS chipsets ? Author: Alexander Malysh + Amaury Decrême - SiS964 support Module Parameters ----------------- @@ -18,6 +20,7 @@ Module Parameters * high_clock = [1|0] Forcibly set Host Master Clock to 56KHz (default, what your BIOS use). DANGEROUS! This should be a bit faster, but freeze some systems (i.e. my Laptop). + SIS630/730 chip only. Description @@ -36,6 +39,12 @@ or like this: 00:00.0 Host bridge: Silicon Integrated Systems [SiS] 730 Host (rev 02) 00:01.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513 +or like this: + +00:00.0 Host bridge: Silicon Integrated Systems [SiS] 760/M760 Host (rev 02) +00:02.0 ISA bridge: Silicon Integrated Systems [SiS] SiS964 [MuTIOL Media IO] + LPC Controller (rev 36) + in your 'lspci' output , then this driver is for your chipset. Thank You diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 87df863ba110..74e18bc0bf62 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -197,11 +197,11 @@ config I2C_SIS5595 will be called i2c-sis5595. config I2C_SIS630 - tristate "SiS 630/730" + tristate "SiS 630/730/964" depends on PCI help If you say yes to this option, support will be included for the - SiS630 and SiS730 SMBus (a subset of I2C) interface. + SiS630, SiS730 and SiS964 SMBus (a subset of I2C) interface. This driver can also be built as a module. If so, the module will be called i2c-sis630. diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index de6dddb9f865..df8e20ab3a09 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -41,6 +41,20 @@ Supports: SIS 630 SIS 730 + SIS 964 + + Notable differences between chips: + +------------------------+--------------------+-------------------+ + | | SIS630/730 | SIS964 | + +------------------------+--------------------+-------------------+ + | Clock | 14kHz/56kHz | 55.56kHz/27.78kHz | + | SMBus registers offset | 0x80 | 0xE0 | + | SMB_CNT | Bit 1 = Slave Busy | Bit 1 = Bus probe | + | (not used yet) | Bit 3 is reserved | Bit 3 = Last byte | + | SMB_PCOUNT | Offset + 0x06 | Offset + 0x14 | + | SMB_COUNT | 4:0 bits | 5:0 bits | + +------------------------+--------------------+-------------------+ + (Other differences don't affect the functions provided by the driver) Note: we assume there can only be one device, with one SMBus interface. */ @@ -55,22 +69,21 @@ #include #include -/* SIS630 SMBus registers */ -#define SMB_STS 0x80 /* status */ -#define SMB_EN 0x81 /* status enable */ -#define SMB_CNT 0x82 -#define SMBHOST_CNT 0x83 -#define SMB_ADDR 0x84 -#define SMB_CMD 0x85 -#define SMB_PCOUNT 0x86 /* processed count */ -#define SMB_COUNT 0x87 -#define SMB_BYTE 0x88 /* ~0x8F data byte field */ -#define SMBDEV_ADDR 0x90 -#define SMB_DB0 0x91 -#define SMB_DB1 0x92 -#define SMB_SAA 0x93 - -/* register count for request_region */ +/* SIS964 id is defined here as we are the only file using it */ +#define PCI_DEVICE_ID_SI_964 0x0964 + +/* SIS630/730/964 SMBus registers */ +#define SMB_STS 0x00 /* status */ +#define SMB_CNT 0x02 /* control */ +#define SMBHOST_CNT 0x03 /* host control */ +#define SMB_ADDR 0x04 /* address */ +#define SMB_CMD 0x05 /* command */ +#define SMB_COUNT 0x07 /* byte count */ +#define SMB_BYTE 0x08 /* ~0x8F data byte field */ + +/* register count for request_region + * As we don't use SMB_PCOUNT, 20 is ok for SiS630 and SiS964 + */ #define SIS630_SMB_IOREGION 20 /* PCI address constants */ @@ -96,28 +109,30 @@ static struct pci_driver sis630_driver; static bool high_clock; static bool force; module_param(high_clock, bool, 0); -MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz)."); +MODULE_PARM_DESC(high_clock, + "Set Host Master Clock to 56KHz (default 14KHz) (SIS630/730 only)."); module_param(force, bool, 0); MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!"); -/* acpi base address */ -static unsigned short acpi_base; +/* SMBus base adress */ +static unsigned short smbus_base; /* supported chips */ static int supported[] = { PCI_DEVICE_ID_SI_630, PCI_DEVICE_ID_SI_730, + PCI_DEVICE_ID_SI_760, 0 /* terminates the list */ }; static inline u8 sis630_read(u8 reg) { - return inb(acpi_base + reg); + return inb(smbus_base + reg); } static inline void sis630_write(u8 reg, u8 data) { - outb(data, acpi_base + reg); + outb(data, smbus_base + reg); } static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldclock) @@ -394,6 +409,8 @@ static int sis630_setup(struct pci_dev *sis630_dev) unsigned char b; struct pci_dev *dummy = NULL; int retval, i; + /* acpi base address */ + unsigned short acpi_base; /* check for supported SiS devices */ for (i=0; supported[i] > 0 ; i++) { @@ -438,16 +455,25 @@ static int sis630_setup(struct pci_dev *sis630_dev) dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base); - retval = acpi_check_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, + if (supported[i] == PCI_DEVICE_ID_SI_760) + smbus_base = acpi_base + 0xE0; + else + smbus_base = acpi_base + 0x80; + + dev_dbg(&sis630_dev->dev, "SMBus base at 0x%04hx\n", smbus_base); + + retval = acpi_check_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION, sis630_driver.name); if (retval) goto exit; /* Everything is happy, let's grab the memory and set things up. */ - if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, + if (!request_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION, sis630_driver.name)) { - dev_err(&sis630_dev->dev, "SMBus registers 0x%04x-0x%04x already " - "in use!\n", acpi_base + SMB_STS, acpi_base + SMB_SAA); + dev_err(&sis630_dev->dev, + "I/O Region 0x%04hx-0x%04hx for SMBus already in use.\n", + smbus_base + SMB_STS, + smbus_base + SMB_STS + SIS630_SMB_IOREGION - 1); retval = -EBUSY; goto exit; } @@ -456,7 +482,7 @@ static int sis630_setup(struct pci_dev *sis630_dev) exit: if (retval) - acpi_base = 0; + smbus_base = 0; return retval; } @@ -470,11 +496,13 @@ static struct i2c_adapter sis630_adapter = { .owner = THIS_MODULE, .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, .algo = &smbus_algorithm, + .retries = 3 }; static DEFINE_PCI_DEVICE_TABLE(sis630_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC) }, + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_964) }, { 0, } }; @@ -491,17 +519,17 @@ static int sis630_probe(struct pci_dev *dev, const struct pci_device_id *id) sis630_adapter.dev.parent = &dev->dev; snprintf(sis630_adapter.name, sizeof(sis630_adapter.name), - "SMBus SIS630 adapter at %04x", acpi_base + SMB_STS); + "SMBus SIS630 adapter at %04hx", smbus_base + SMB_STS); return i2c_add_adapter(&sis630_adapter); } static void sis630_remove(struct pci_dev *dev) { - if (acpi_base) { + if (smbus_base) { i2c_del_adapter(&sis630_adapter); - release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION); - acpi_base = 0; + release_region(smbus_base + SMB_STS, SIS630_SMB_IOREGION); + smbus_base = 0; } } -- cgit v1.2.3-59-g8ed1b From f3b54b9a066edeac5c06e1cdcd82e1cb1224aaef Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 11 Feb 2013 19:47:56 -0700 Subject: i2c: add bcm2835 driver This implements a very basic I2C host driver for the BCM2835 SoC. Missing features so far are: * 10-bit addressing. * DMA. Reviewed-by: Grant Likely Signed-off-by: Stephen Warren Signed-off-by: Wolfram Sang --- .../devicetree/bindings/i2c/brcm,bcm2835-i2c.txt | 20 ++ drivers/i2c/busses/Kconfig | 12 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm2835.c | 342 +++++++++++++++++++++ 4 files changed, 375 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm2835.c (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt new file mode 100644 index 000000000000..e9de3756752b --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt @@ -0,0 +1,20 @@ +Broadcom BCM2835 I2C controller + +Required properties: +- compatible : Should be "brcm,bcm2835-i2c". +- reg: Should contain register location and length. +- interrupts: Should contain interrupt. +- clocks : The clock feeding the I2C controller. + +Recommended properties: +- clock-frequency : desired I2C bus clock frequency in Hz. + +Example: + +i2c@20205000 { + compatible = "brcm,bcm2835-i2c"; + reg = <0x7e205000 0x1000>; + interrupts = <2 21>; + clocks = <&clk_i2c>; + clock-frequency = <100000>; +}; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 74e18bc0bf62..8cbd9cd1e0fa 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -330,6 +330,18 @@ config I2C_AU1550 This driver can also be built as a module. If so, the module will be called i2c-au1550. +config I2C_BCM2835 + tristate "Broadcom BCM2835 I2C controller" + depends on ARCH_BCM2835 + help + If you say yes to this option, support will be included for the + BCM2835 I2C controller. + + If you don't know what to do here, say N. + + This support is also available as a module. If so, the module + will be called i2c-bcm2835. + config I2C_BLACKFIN_TWI tristate "Blackfin TWI I2C support" depends on BLACKFIN diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 23c9d55bc880..8f4fc23b85b1 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o # Embedded system I2C/SMBus host controller drivers obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o +obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c new file mode 100644 index 000000000000..ea4b08fc3353 --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -0,0 +1,342 @@ +/* + * BCM2835 master mode driver + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BCM2835_I2C_C 0x0 +#define BCM2835_I2C_S 0x4 +#define BCM2835_I2C_DLEN 0x8 +#define BCM2835_I2C_A 0xc +#define BCM2835_I2C_FIFO 0x10 +#define BCM2835_I2C_DIV 0x14 +#define BCM2835_I2C_DEL 0x18 +#define BCM2835_I2C_CLKT 0x1c + +#define BCM2835_I2C_C_READ BIT(0) +#define BCM2835_I2C_C_CLEAR BIT(4) /* bits 4 and 5 both clear */ +#define BCM2835_I2C_C_ST BIT(7) +#define BCM2835_I2C_C_INTD BIT(8) +#define BCM2835_I2C_C_INTT BIT(9) +#define BCM2835_I2C_C_INTR BIT(10) +#define BCM2835_I2C_C_I2CEN BIT(15) + +#define BCM2835_I2C_S_TA BIT(0) +#define BCM2835_I2C_S_DONE BIT(1) +#define BCM2835_I2C_S_TXW BIT(2) +#define BCM2835_I2C_S_RXR BIT(3) +#define BCM2835_I2C_S_TXD BIT(4) +#define BCM2835_I2C_S_RXD BIT(5) +#define BCM2835_I2C_S_TXE BIT(6) +#define BCM2835_I2C_S_RXF BIT(7) +#define BCM2835_I2C_S_ERR BIT(8) +#define BCM2835_I2C_S_CLKT BIT(9) +#define BCM2835_I2C_S_LEN BIT(10) /* Fake bit for SW error reporting */ + +#define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000)) + +struct bcm2835_i2c_dev { + struct device *dev; + void __iomem *regs; + struct clk *clk; + int irq; + struct i2c_adapter adapter; + struct completion completion; + u32 msg_err; + u8 *msg_buf; + size_t msg_buf_remaining; +}; + +static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev, + u32 reg, u32 val) +{ + writel(val, i2c_dev->regs + reg); +} + +static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg) +{ + return readl(i2c_dev->regs + reg); +} + +static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev) +{ + u32 val; + + while (i2c_dev->msg_buf_remaining) { + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + if (!(val & BCM2835_I2C_S_TXD)) + break; + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_FIFO, + *i2c_dev->msg_buf); + i2c_dev->msg_buf++; + i2c_dev->msg_buf_remaining--; + } +} + +static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev) +{ + u32 val; + + while (i2c_dev->msg_buf_remaining) { + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + if (!(val & BCM2835_I2C_S_RXD)) + break; + *i2c_dev->msg_buf = bcm2835_i2c_readl(i2c_dev, + BCM2835_I2C_FIFO); + i2c_dev->msg_buf++; + i2c_dev->msg_buf_remaining--; + } +} + +static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data) +{ + struct bcm2835_i2c_dev *i2c_dev = data; + u32 val, err; + + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val); + + err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR); + if (err) { + i2c_dev->msg_err = err; + complete(&i2c_dev->completion); + return IRQ_HANDLED; + } + + if (val & BCM2835_I2C_S_RXD) { + bcm2835_drain_rxfifo(i2c_dev); + if (!(val & BCM2835_I2C_S_DONE)) + return IRQ_HANDLED; + } + + if (val & BCM2835_I2C_S_DONE) { + if (i2c_dev->msg_buf_remaining) + i2c_dev->msg_err = BCM2835_I2C_S_LEN; + else + i2c_dev->msg_err = 0; + complete(&i2c_dev->completion); + return IRQ_HANDLED; + } + + if (val & BCM2835_I2C_S_TXD) { + bcm2835_fill_txfifo(i2c_dev); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev, + struct i2c_msg *msg) +{ + u32 c; + int time_left; + + i2c_dev->msg_buf = msg->buf; + i2c_dev->msg_buf_remaining = msg->len; + INIT_COMPLETION(i2c_dev->completion); + + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR); + + if (msg->flags & I2C_M_RD) { + c = BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR; + } else { + c = BCM2835_I2C_C_INTT; + bcm2835_fill_txfifo(i2c_dev); + } + c |= BCM2835_I2C_C_ST | BCM2835_I2C_C_INTD | BCM2835_I2C_C_I2CEN; + + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c); + + time_left = wait_for_completion_timeout(&i2c_dev->completion, + BCM2835_I2C_TIMEOUT); + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR); + if (!time_left) { + dev_err(i2c_dev->dev, "i2c transfer timed out\n"); + return -ETIMEDOUT; + } + + if (likely(!i2c_dev->msg_err)) + return 0; + + if ((i2c_dev->msg_err & BCM2835_I2C_S_ERR) && + (msg->flags & I2C_M_IGNORE_NAK)) + return 0; + + dev_err(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err); + + if (i2c_dev->msg_err & BCM2835_I2C_S_ERR) + return -EREMOTEIO; + else + return -EIO; +} + +static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap); + int i; + int ret = 0; + + for (i = 0; i < num; i++) { + ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i]); + if (ret) + break; + } + + return ret ?: i; +} + +static u32 bcm2835_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm2835_i2c_algo = { + .master_xfer = bcm2835_i2c_xfer, + .functionality = bcm2835_i2c_func, +}; + +static int bcm2835_i2c_probe(struct platform_device *pdev) +{ + struct bcm2835_i2c_dev *i2c_dev; + struct resource *mem, *requested, *irq; + u32 bus_clk_rate, divider; + int ret; + struct i2c_adapter *adap; + + i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); + if (!i2c_dev) { + dev_err(&pdev->dev, "Cannot allocate i2c_dev\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, i2c_dev); + i2c_dev->dev = &pdev->dev; + init_completion(&i2c_dev->completion); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No mem resource\n"); + return -ENODEV; + } + + requested = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), + dev_name(&pdev->dev)); + if (!requested) { + dev_err(&pdev->dev, "Could not claim register region\n"); + return -EBUSY; + } + + i2c_dev->regs = devm_ioremap(&pdev->dev, mem->start, + resource_size(mem)); + if (!i2c_dev->regs) { + dev_err(&pdev->dev, "Could not map registers\n"); + return -ENOMEM; + } + + i2c_dev->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(i2c_dev->clk)) { + dev_err(&pdev->dev, "Could not get clock\n"); + return PTR_ERR(i2c_dev->clk); + } + + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", + &bus_clk_rate); + if (ret < 0) { + dev_warn(&pdev->dev, + "Could not read clock-frequency property\n"); + bus_clk_rate = 100000; + } + + divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), bus_clk_rate); + /* + * Per the datasheet, the register is always interpreted as an even + * number, by rounding down. In other words, the LSB is ignored. So, + * if the LSB is set, increment the divider to avoid any issue. + */ + if (divider & 1) + divider++; + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider); + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "No IRQ resource\n"); + return -ENODEV; + } + i2c_dev->irq = irq->start; + + ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED, + dev_name(&pdev->dev), i2c_dev); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return -ENODEV; + } + + adap = &i2c_dev->adapter; + i2c_set_adapdata(adap, i2c_dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name)); + adap->algo = &bcm2835_i2c_algo; + adap->dev.parent = &pdev->dev; + + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0); + + ret = i2c_add_adapter(adap); + if (ret) + free_irq(i2c_dev->irq, i2c_dev); + + return ret; +} + +static int bcm2835_i2c_remove(struct platform_device *pdev) +{ + struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev); + + free_irq(i2c_dev->irq, i2c_dev); + i2c_del_adapter(&i2c_dev->adapter); + + return 0; +} + +static const struct of_device_id bcm2835_i2c_of_match[] = { + { .compatible = "brcm,bcm2835-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm2835_i2c_of_match); + +static struct platform_driver bcm2835_i2c_driver = { + .probe = bcm2835_i2c_probe, + .remove = bcm2835_i2c_remove, + .driver = { + .name = "i2c-bcm2835", + .owner = THIS_MODULE, + .of_match_table = bcm2835_i2c_of_match, + }, +}; +module_platform_driver(bcm2835_i2c_driver); + +MODULE_AUTHOR("Stephen Warren "); +MODULE_DESCRIPTION("BCM2835 I2C bus adapter"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:i2c-bcm2835"); -- cgit v1.2.3-59-g8ed1b From a3fc0ff00a46c4b32e7214961a5be9a1dc39b60e Mon Sep 17 00:00:00 2001 From: James Ralston Date: Thu, 14 Feb 2013 09:15:33 +0000 Subject: i2c: i801: Add Device IDs for Intel Wellsburg PCH This patch adds the SMBus Device IDs for the Intel Wellsburg PCH Signed-off-by: James Ralston Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-i801 | 1 + drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-i801.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+) (limited to 'Documentation') diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index 8d71d5705b2a..d55b8ab2d10f 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -23,6 +23,7 @@ Supported adapters: * Intel Lynx Point (PCH) * Intel Lynx Point-LP (PCH) * Intel Avoton (SOC) + * Intel Wellsburg (PCH) Datasheets: Publicly available at the Intel website On Intel Patsburg and later chipsets, both the normal host SMBus controller diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8cbd9cd1e0fa..b588317f2393 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -107,6 +107,7 @@ config I2C_I801 Lynx Point (PCH) Lynx Point-LP (PCH) Avoton (SOC) + Wellsburg (PCH) This driver can also be built as a module. If so, the module will be called i2c-i801. diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index b00c29d8a5f1..76febfb09760 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -54,6 +54,10 @@ Lynx Point (PCH) 0x8c22 32 hard yes yes yes Lynx Point-LP (PCH) 0x9c22 32 hard yes yes yes Avoton (SOC) 0x1f3c 32 hard yes yes yes + Wellsburg (PCH) 0x8d22 32 hard yes yes yes + Wellsburg (PCH) MS 0x8d7d 32 hard yes yes yes + Wellsburg (PCH) MS 0x8d7e 32 hard yes yes yes + Wellsburg (PCH) MS 0x8d7f 32 hard yes yes yes Features supported by this driver: Software PEC no @@ -167,6 +171,10 @@ #define PCI_DEVICE_ID_INTEL_DH89XXCC_SMBUS 0x2330 #define PCI_DEVICE_ID_INTEL_5_3400_SERIES_SMBUS 0x3b30 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS 0x8d22 +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0 0x8d7d +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1 0x8d7e +#define PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2 0x8d7f #define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS 0x9c22 struct i801_mux_config { @@ -801,6 +809,10 @@ static DEFINE_PCI_DEVICE_TABLE(i801_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_AVOTON_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2) }, { 0, } }; @@ -1106,6 +1118,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF0: case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF1: case PCI_DEVICE_ID_INTEL_PATSBURG_SMBUS_IDF2: + case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS0: + case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS1: + case PCI_DEVICE_ID_INTEL_WELLSBURG_SMBUS_MS2: priv->features |= FEATURE_IDF; /* fall through */ default: -- cgit v1.2.3-59-g8ed1b