* Re: [PATCH 06/10] i2c: i2c-mux-ptxpmb-cpld: Add device tree bindings
From: Peter Rosin @ 2016-10-11 7:36 UTC (permalink / raw)
To: Rob Herring, Pantelis Antoniou
Cc: Mark Rutland, Alexandre Courbot, Georgi Vlaev, linux-watchdog,
Wolfram Sang, devicetree, Frank Rowand, Linus Walleij,
Guenter Roeck, linux-kernel, JawaharBalaji Thirumalaisamy,
linux-gpio, Wim Van Sebroeck, linux-mtd, Debjit Ghosh, Rajat Jain,
Brian Norris, Lee Jones, David Woodhouse, Guenter Roeck,
linux-i2c
In-Reply-To: <20161010174517.GA18097@rob-hp-laptop>
On 2016-10-10 19:45, Rob Herring wrote:
> On Fri, Oct 07, 2016 at 06:17:27PM +0300, Pantelis Antoniou wrote:
>> From: Georgi Vlaev <gvlaev@juniper.net>
>>
>> Add binding document for the i2c driver of PTXPMB CPLD.
>>
>> Signed-off-by: Georgi Vlaev <gvlaev@juniper.net>
>> [Ported from Juniper kernel]
>> Signed-off-by: Pantelis Antoniou <pantelis.antoniou@konsulko.com>
>> ---
>> .../bindings/i2c/jnx,i2c-mux-ptxpmb-cpld.txt | 50 ++++++++++++++++++++++
>> 1 file changed, 50 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/i2c/jnx,i2c-mux-ptxpmb-cpld.txt
>>
>> diff --git a/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-ptxpmb-cpld.txt b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-ptxpmb-cpld.txt
>> new file mode 100644
>> index 0000000..3b201f7
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/i2c/jnx,i2c-mux-ptxpmb-cpld.txt
>> @@ -0,0 +1,50 @@
>> +* Juniper PTXPMB CPLD I2C Mux
>> +
>> +I2C mux on the PTXPMB CPLD on Juniper series of routers.
>> +
>> +Required properties:
>> +
>> + - compatible: Must contain one of the following.
>> + "jnx,i2c-mux-ptxpmb-cpld", "jnx,i2c-mux-ngpmb-bcpld"
>> + - num-enable: Number of muxes to enable.
>
> No. Use status property.
Reading the code, I understand this mux to be like a combination of
i2c switches (like pca9548) where the slave i2c busses can be
enabled individually using an "enable" bitmask in one register, and
"regular" i2c muxes (like pca9547) where you can only select one
slave i2c bus at a time using an enumeration.
num-enable would be the number of switches and num-channels would be
the number of muxes per switch channel. Or the other way around? This
is a real problem. The bindings are too tightly coupled to the way
Linux currently handles i2c switches (i.e. not very well, you can only
select one slave bus on an i2c switch, thus reducing the more
flexible switch hardware to a regular mux). The bindings should not
describe what the driver does, they should describe the hardware.
Basically, you need to describe the i2c topology of the hardware in
the bindings, because I'm just guessing all this, and I shouldn't
have to.
Cheers,
Peter
>> +
>> + The following required properties are defined externally:
>> +
>> + - Standard I2C mux properties. See i2c-mux.txt in this directory.
>> + - I2C child bus nodes. See i2c-mux.txt in this directory.
>> +
>> +Optional Properties:
>> +
>> + - num-channels: Number of channels. If not present the default is 8.
>
> What's a channel?
>
>> + - base-bus-num: Base bus number. If not present it is 0.
>
> No. Linuxism and why do you care?
>
>> + - use-force: Use the force method of the controller.
>> +
>> +
>> +Example:
>> +
>> +cpld-i2c-mux {
>
> mux {
>
>> + compatible = "jnx,i2c-mux-ptxpmb-cpld";
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + i2c-parent = <&i2c1>;
>> +
>> + num-enable = <1>;
>> +
>> + i2c@0 {
>> + reg = <0>;
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + /* PMB devices are accessed through FPC */
>> +
>> + temp-sensor@1a { /* FPC */
>> + compatible = "maxim,max6697";
>> + reg = <0x1a>;
>> + smbus-timeout-disable;
>> + resistance-cancellation;
>> + alert-mask = <0xff>;
>> + over-temperature-mask = <0xff>;
>> + };
>> + };
>> +};
>> --
>> 1.9.1
>>
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply
* [PATCH v2] i2c: i801: Fix I2C Block Read on 8-Series/C220 and later
From: Jean Delvare @ 2016-10-11 11:13 UTC (permalink / raw)
To: Linux I2C; +Cc: Jarkko Nikula, Mika Westerberg, Wolfram Sang
Starting with the 8-Series/C220 PCH (Lynx Point), the SMBus
controller includes a SPD EEPROM protection mechanism. Once the SPD
Write Disable bit is set, only reads are allowed to slave addresses
0x50-0x57.
However the legacy implementation of I2C Block Read since the ICH5
looks like a write, and is therefore blocked by the SPD protection
mechanism. This causes the eeprom and at24 drivers to fail.
So assume that I2C Block Read is implemented as an actual read on
these chipsets. I tested it on my Q87 chipset and it seems to work
just fine.
Signed-off-by: Jean Delvare <jdelvare@suse.de>
Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
Cc: Wolfram Sang <wsa@the-dreams.de>
---
Changes since v1:
* Rebased on Linus' latest tree.
Jarkko, still no information about this from your Windows or hardware
folks?
drivers/i2c/busses/i2c-i801.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
--- linux-4.8.orig/drivers/i2c/busses/i2c-i801.c 2016-10-11 10:41:43.984120197 +0200
+++ linux-4.8/drivers/i2c/busses/i2c-i801.c 2016-10-11 10:42:46.648783059 +0200
@@ -146,6 +146,7 @@
#define SMBHSTCFG_HST_EN BIT(0)
#define SMBHSTCFG_SMB_SMI_EN BIT(1)
#define SMBHSTCFG_I2C_EN BIT(2)
+#define SMBHSTCFG_SPD_WD BIT(4)
/* TCO configuration bits for TCOCTL */
#define TCOCTL_EN BIT(8)
@@ -871,9 +872,16 @@ static s32 i801_access(struct i2c_adapte
block = 1;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
- /* NB: page 240 of ICH5 datasheet shows that the R/#W
- * bit should be cleared here, even when reading */
- outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
+ /*
+ * NB: page 240 of ICH5 datasheet shows that the R/#W
+ * bit should be cleared here, even when reading.
+ * However if SPD Write Disable is set (Lynx Point and later),
+ * the read will fail if we don't set the R/#W bit.
+ */
+ outb_p(((addr & 0x7f) << 1) |
+ ((priv->original_hstcfg & SMBHSTCFG_SPD_WD) ?
+ (read_write & 0x01) : 0),
+ SMBHSTADD(priv));
if (read_write == I2C_SMBUS_READ) {
/* NB: page 240 of ICH5 datasheet also shows
* that DATA1 is the cmd field when reading */
@@ -1593,6 +1601,8 @@ static int i801_probe(struct pci_dev *de
/* Disable SMBus interrupt feature if SMBus using SMI# */
priv->features &= ~FEATURE_IRQ;
}
+ if (temp & SMBHSTCFG_SPD_WD)
+ dev_info(&dev->dev, "SPD Write Disable is set\n");
/* Clear special mode bits */
if (priv->features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER))
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply
* Re: [PATCH v2] i2c: i801: Fix I2C Block Read on 8-Series/C220 and later
From: Jarkko Nikula @ 2016-10-11 11:32 UTC (permalink / raw)
To: Jean Delvare, Linux I2C; +Cc: Mika Westerberg, Wolfram Sang
In-Reply-To: <20161011131327.0eabd227@endymion>
On 10/11/2016 02:13 PM, Jean Delvare wrote:
> Starting with the 8-Series/C220 PCH (Lynx Point), the SMBus
> controller includes a SPD EEPROM protection mechanism. Once the SPD
> Write Disable bit is set, only reads are allowed to slave addresses
> 0x50-0x57.
>
> However the legacy implementation of I2C Block Read since the ICH5
> looks like a write, and is therefore blocked by the SPD protection
> mechanism. This causes the eeprom and at24 drivers to fail.
>
> So assume that I2C Block Read is implemented as an actual read on
> these chipsets. I tested it on my Q87 chipset and it seems to work
> just fine.
>
> Signed-off-by: Jean Delvare <jdelvare@suse.de>
> Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
> Cc: Wolfram Sang <wsa@the-dreams.de>
> ---
> Changes since v1:
> * Rebased on Linus' latest tree.
>
> Jarkko, still no information about this from your Windows or hardware
> folks?
>
No update, in fact some of the contacts have left the company :-(
--
Jarkko
^ permalink raw reply
* [PATCH] Add slave mode to Synopsys I2C driver
From: Luis.Oliveira @ 2016-10-11 12:18 UTC (permalink / raw)
To: jarkko.nikula, andriy.shevchenko, mika.westerberg, wsa, linux-i2c,
linux-kernel
Cc: Luis.Oliveira, CARLOS.PALMINHA
From: Luis Oliveira <lolivei@synopsys.com>
Add support in existing I2C Synopsys Designware Core driver for I2C slave mode.
Signed-off-by: Luis Oliveira <lolivei@synopsys.com>
---
drivers/i2c/busses/i2c-designware-core.c | 250 +++++++++++++++++++++++++---
drivers/i2c/busses/i2c-designware-core.h | 8 +-
drivers/i2c/busses/i2c-designware-platdrv.c | 47 ++++--
3 files changed, 271 insertions(+), 34 deletions(-)
diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index 1fe93c4..c873a0c 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -21,6 +21,7 @@
* ----------------------------------------------------------------------------
*
*/
+
#include <linux/export.h>
#include <linux/errno.h>
#include <linux/err.h>
@@ -37,6 +38,7 @@
*/
#define DW_IC_CON 0x0
#define DW_IC_TAR 0x4
+#define DW_IC_SAR 0x8
#define DW_IC_DATA_CMD 0x10
#define DW_IC_SS_SCL_HCNT 0x14
#define DW_IC_SS_SCL_LCNT 0x18
@@ -85,15 +87,27 @@
#define DW_IC_INTR_STOP_DET 0x200
#define DW_IC_INTR_START_DET 0x400
#define DW_IC_INTR_GEN_CALL 0x800
-
-#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \
- DW_IC_INTR_TX_EMPTY | \
- DW_IC_INTR_TX_ABRT | \
- DW_IC_INTR_STOP_DET)
+#define DW_IC_INTR_RESTART_DET 0x1000
+
+#define DW_IC_INTR_MST_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \
+ DW_IC_INTR_TX_EMPTY | \
+ DW_IC_INTR_TX_ABRT | \
+ DW_IC_INTR_STOP_DET | \
+ DW_IC_INTR_RX_DONE | \
+ DW_IC_INTR_RX_UNDER | \
+ DW_IC_INTR_RD_REQ)
+
+ #define DW_IC_INTR_SLV_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \
+ DW_IC_INTR_STOP_DET | \
+ DW_IC_INTR_TX_ABRT | \
+ DW_IC_INTR_RX_DONE | \
+ DW_IC_INTR_RX_UNDER | \
+ DW_IC_INTR_RD_REQ)
#define DW_IC_STATUS_ACTIVITY 0x1
#define DW_IC_STATUS_TFE BIT(2)
#define DW_IC_STATUS_MST_ACTIVITY BIT(5)
+#define DW_IC_STATUS_SLV_ACTIVITY BIT(6)
#define DW_IC_ERR_TX_ABRT 0x1
@@ -107,7 +121,7 @@
*/
#define STATUS_IDLE 0x0
#define STATUS_WRITE_IN_PROGRESS 0x1
-#define STATUS_READ_IN_PROGRESS 0x2
+#define STATUS_READ_IN_PROGRESS 0x2
#define TIMEOUT 20 /* ms */
@@ -128,6 +142,9 @@
#define ABRT_10B_RD_NORSTRT 10
#define ABRT_MASTER_DIS 11
#define ARB_LOST 12
+#define ABRT_SLVFLUSH_TXFIFO 13
+#define ABRT_SLV_ARBLOST 14
+#define ABRT_SLVRD_INTX 15
#define DW_IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK)
#define DW_IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK)
@@ -140,6 +157,9 @@
#define DW_IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT)
#define DW_IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS)
#define DW_IC_TX_ARB_LOST (1UL << ARB_LOST)
+#define DW_IC_RX_ABRT_SLVRD_INTX (1UL << ABRT_SLVRD_INTX)
+#define DW_IC_RX_ABRT_SLV_ARBLOST (1UL << ABRT_SLV_ARBLOST)
+#define DW_IC_RX_ABRT_SLVFLUSH_TXFIFO (1UL << ABRT_SLVFLUSH_TXFIFO)
#define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \
DW_IC_TX_ABRT_10ADDR1_NOACK | \
@@ -170,6 +190,12 @@ static char *abort_sources[] = {
"trying to use disabled adapter",
[ARB_LOST] =
"lost arbitration",
+ [ABRT_SLVFLUSH_TXFIFO] =
+ "read command so flush old data in the TX FIFO",
+ [ABRT_SLV_ARBLOST] =
+ "slave lost the bus while transmitting data to a remote master",
+ [ABRT_SLVRD_INTX] =
+ "slave request for data to be transmitted and",
};
static u32 dw_readl(struct dw_i2c_dev *dev, int offset)
@@ -317,7 +343,7 @@ static void i2c_dw_release_lock(struct dw_i2c_dev *dev)
}
/**
- * i2c_dw_init() - initialize the designware i2c master hardware
+ * i2c_dw_init() - initialize the designware i2c hardware
* @dev: device private data
*
* This functions configures and enables the I2C master.
@@ -343,8 +369,8 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
/* Configure register access mode 16bit */
dev->accessor_flags |= ACCESS_16BIT;
} else if (reg != DW_IC_COMP_TYPE_VALUE) {
- dev_err(dev->dev, "Unknown Synopsys component type: "
- "0x%08x\n", reg);
+ dev_err(dev->dev, "Unknown Synopsys component type: 0x%08x\n",
+ reg);
i2c_dw_release_lock(dev);
return -ENODEV;
}
@@ -431,12 +457,30 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
"Hardware too old to adjust SDA hold time.\n");
}
- /* Configure Tx/Rx FIFO threshold levels */
- dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
- dw_writel(dev, 0, DW_IC_RX_TL);
-
- /* configure the i2c master */
- dw_writel(dev, dev->master_cfg , DW_IC_CON);
+ if ((dev->master_cfg & DW_IC_CON_MASTER) &&
+ (dev->master_cfg & DW_IC_CON_SLAVE_DISABLE)) {
+ /* IF master */
+
+ /* Configure Tx/Rx FIFO threshold levels */
+ dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
+ dw_writel(dev, 0, DW_IC_RX_TL);
+
+ /* configure the i2c master */
+ dw_writel(dev, dev->master_cfg, DW_IC_CON);
+ dw_writel(dev, DW_IC_INTR_MST_DEFAULT_MASK, DW_IC_INTR_MASK);
+ } else if (!(dev->master_cfg & DW_IC_CON_MASTER) &&
+ !(dev->master_cfg & DW_IC_CON_SLAVE_DISABLE)) {
+ /*IF slave */
+
+ /* Configure Tx/Rx FIFO threshold levels */
+ dw_writel(dev, 0, DW_IC_TX_TL);
+ dw_writel(dev, 0, DW_IC_RX_TL);
+
+ /* configure the i2c slave */
+ dw_writel(dev, dev->slave_cfg, DW_IC_CON);
+ dw_writel(dev, DW_IC_INTR_SLV_DEFAULT_MASK, DW_IC_INTR_MASK);
+ } else
+ return -EAGAIN;
i2c_dw_release_lock(dev);
@@ -520,7 +564,7 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
/* Clear and enable interrupts */
dw_readl(dev, DW_IC_CLR_INTR);
- dw_writel(dev, DW_IC_INTR_DEFAULT_MASK, DW_IC_INTR_MASK);
+ dw_writel(dev, DW_IC_INTR_MST_DEFAULT_MASK, DW_IC_INTR_MASK);
}
/*
@@ -540,7 +584,7 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
u8 *buf = dev->tx_buf;
bool need_restart = false;
- intr_mask = DW_IC_INTR_DEFAULT_MASK;
+ intr_mask = DW_IC_INTR_MST_DEFAULT_MASK;
for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {
/*
@@ -772,12 +816,64 @@ done_nolock:
static u32 i2c_dw_func(struct i2c_adapter *adap)
{
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
+
return dev->functionality;
}
+static int i2c_dw_reg_slave(struct i2c_client *slave)
+{
+ struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
+
+ if (dev->slave)
+ return -EBUSY;
+ if (slave->flags & I2C_CLIENT_TEN)
+ return -EAFNOSUPPORT;
+ /* set slave address in the IC_SAR register,
+ * the address to which the DW_apb_i2c responds
+ */
+
+ __i2c_dw_enable(dev, false);
+
+ dw_writel(dev, slave->addr, DW_IC_SAR);
+
+ pm_runtime_forbid(dev->dev);
+
+ dev->slave = slave;
+
+ __i2c_dw_enable(dev, true);
+
+ dev->cmd_err = 0;
+ dev->msg_write_idx = 0;
+ dev->msg_read_idx = 0;
+ dev->msg_err = 0;
+ dev->status = STATUS_IDLE;
+ dev->abort_source = 0;
+ dev->rx_outstanding = 0;
+
+ return 0;
+}
+
+static int i2c_dw_unreg_slave(struct i2c_client *slave)
+{
+ struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
+
+ WARN_ON(!dev->slave);
+
+ i2c_dw_disable_int(dev);
+ i2c_dw_disable(dev);
+
+ dev->slave = NULL;
+
+ pm_runtime_allow(dev->dev);
+
+ return 0;
+}
+
static struct i2c_algorithm i2c_dw_algo = {
.master_xfer = i2c_dw_xfer,
.functionality = i2c_dw_func,
+ .reg_slave = i2c_dw_reg_slave,
+ .unreg_slave = i2c_dw_unreg_slave,
};
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
@@ -811,8 +907,6 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
dw_readl(dev, DW_IC_CLR_RX_OVER);
if (stat & DW_IC_INTR_TX_OVER)
dw_readl(dev, DW_IC_CLR_TX_OVER);
- if (stat & DW_IC_INTR_RD_REQ)
- dw_readl(dev, DW_IC_CLR_RD_REQ);
if (stat & DW_IC_INTR_TX_ABRT) {
/*
* The IC_TX_ABRT_SOURCE register is cleared whenever
@@ -839,19 +933,129 @@ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
* Interrupt service routine. This gets called whenever an I2C interrupt
* occurs.
*/
+
+static bool i2c_dw_slave_irq_handler(struct dw_i2c_dev *dev)
+{
+ u32 raw_stat, stat, enabled;
+ u8 val;
+ u8 slv_act;
+
+ stat = dw_readl(dev, DW_IC_INTR_STAT);
+ enabled = dw_readl(dev, DW_IC_ENABLE);
+ raw_stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
+
+ if (!enabled || !(raw_stat & ~DW_IC_INTR_ACTIVITY))
+ return false;
+
+ slv_act = ((dw_readl(dev, DW_IC_STATUS) &
+ DW_IC_STATUS_SLV_ACTIVITY)>>6);
+
+ dev_dbg(dev->dev,
+ "%s: %#x SLAVE_ACTV=%#x : RAW_INTR_STAT=%#x : INTR_STAT=%#x\n",
+ __func__, enabled, slv_act, raw_stat, stat);
+
+ if (stat & DW_IC_INTR_START_DET)
+ dw_readl(dev, DW_IC_CLR_START_DET);
+
+ if (stat & DW_IC_INTR_ACTIVITY)
+ dw_readl(dev, DW_IC_CLR_ACTIVITY);
+
+ if (stat & DW_IC_INTR_RX_OVER)
+ dw_readl(dev, DW_IC_CLR_RX_OVER);
+
+ if ((stat & DW_IC_INTR_RX_FULL) && (stat & DW_IC_INTR_STOP_DET)) {
+ dev_dbg(dev->dev, "First write\n");
+ i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val);
+ }
+
+ if (slv_act) {
+ dev_dbg(dev->dev, "I2C GET\n");
+
+ if (stat & DW_IC_INTR_RD_REQ) {
+ if (stat & DW_IC_INTR_RX_FULL) {
+ val = dw_readl(dev, DW_IC_DATA_CMD);
+ if (!i2c_slave_event(dev->slave,
+ I2C_SLAVE_WRITE_RECEIVED, &val)) {
+ dev_dbg(dev->dev, "Byte %X acked! ", val);
+ ;
+ }
+ dev_dbg(dev->dev, "I2C GET + add");
+ dw_readl(dev, DW_IC_CLR_RD_REQ);
+ stat = i2c_dw_read_clear_intrbits(dev);
+ } else {
+ dev_dbg(dev->dev, "I2C GET + 0x00");
+ dw_readl(dev, DW_IC_CLR_RD_REQ);
+ dw_readl(dev, DW_IC_CLR_RX_UNDER);
+ stat = i2c_dw_read_clear_intrbits(dev);
+ }
+ if (!i2c_slave_event(dev->slave,
+ I2C_SLAVE_READ_REQUESTED, &val))
+ dw_writel(dev, (0x0 << 8 | val), DW_IC_DATA_CMD);
+ }
+ }
+ if (stat & DW_IC_INTR_RX_DONE) {
+
+ if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, &val))
+ dw_readl(dev, DW_IC_CLR_RX_DONE);
+
+ i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
+ dev_dbg(dev->dev, "Transmission Complete.");
+ stat = i2c_dw_read_clear_intrbits(dev);
+
+ goto done;
+ }
+
+ if (stat & DW_IC_INTR_RX_FULL) {
+ dev_dbg(dev->dev, "I2C SET");
+ val = dw_readl(dev, DW_IC_DATA_CMD);
+ if (!i2c_slave_event(dev->slave,
+ I2C_SLAVE_WRITE_RECEIVED, &val)) {
+ dev_dbg(dev->dev, "Byte %X acked! ", val);
+ ;
+ }
+ } else {
+ i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
+ dev_dbg(dev->dev, "Transmission Complete.");
+ stat = i2c_dw_read_clear_intrbits(dev);
+ }
+
+ if (stat & DW_IC_INTR_TX_OVER) {
+ dw_readl(dev, DW_IC_CLR_TX_OVER);
+ return true;
+ }
+done:
+ return true;
+}
+
static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
{
struct dw_i2c_dev *dev = dev_id;
- u32 stat, enabled;
+ u32 stat, enabled, mode;
enabled = dw_readl(dev, DW_IC_ENABLE);
+ mode = dw_readl(dev, DW_IC_CON);
stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
- dev_dbg(dev->dev, "%s: enabled=%#x stat=%#x\n", __func__, enabled, stat);
+
+ stat = i2c_dw_read_clear_intrbits(dev);
+
+ dev_dbg(dev->dev,
+ "%s: enabled=%#x stat=%#x\n", __func__, enabled, stat);
+
if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
return IRQ_NONE;
stat = i2c_dw_read_clear_intrbits(dev);
+ if (!(mode & DW_IC_CON_MASTER) && !(mode & DW_IC_CON_SLAVE_DISABLE)) {
+
+ /* slave mode*/
+ if (!i2c_dw_slave_irq_handler(dev))
+ return IRQ_NONE;
+
+ complete(&dev->cmd_complete);
+ return IRQ_HANDLED;
+ }
+
if (stat & DW_IC_INTR_TX_ABRT) {
dev->cmd_err |= DW_IC_ERR_TX_ABRT;
dev->status = STATUS_IDLE;
@@ -961,7 +1165,9 @@ int i2c_dw_probe(struct dw_i2c_dev *dev)
adap->dev.parent = dev->dev;
i2c_set_adapdata(adap, dev);
- i2c_dw_disable_int(dev);
+ if (!i2c_check_functionality(adap, I2C_FUNC_SLAVE))
+ i2c_dw_disable_int(dev);
+
r = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr,
IRQF_SHARED | IRQF_COND_SUSPEND,
dev_name(dev->dev), dev);
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 0d44d2a..5875410 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -28,9 +28,13 @@
#define DW_IC_CON_SPEED_FAST 0x4
#define DW_IC_CON_SPEED_HIGH 0x6
#define DW_IC_CON_SPEED_MASK 0x6
+#define DW_IC_CON_10BITADDR_SLAVE 0x8
#define DW_IC_CON_10BITADDR_MASTER 0x10
#define DW_IC_CON_RESTART_EN 0x20
#define DW_IC_CON_SLAVE_DISABLE 0x40
+#define DW_IC_CON_STOP_DET_IFADDRESSED 0x80
+#define DW_IC_CON_TX_EMPTY_CTRL 0x100
+#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200
/**
@@ -80,7 +84,8 @@ struct dw_i2c_dev {
void __iomem *base;
struct completion cmd_complete;
struct clk *clk;
- u32 (*get_clk_rate_khz) (struct dw_i2c_dev *dev);
+ struct i2c_client *slave;
+ u32 (*get_clk_rate_khz)(struct dw_i2c_dev *dev);
struct dw_pci_controller *controller;
int cmd_err;
struct i2c_msg *msgs;
@@ -99,6 +104,7 @@ struct dw_i2c_dev {
struct i2c_adapter adapter;
u32 functionality;
u32 master_cfg;
+ u32 slave_cfg;
unsigned int tx_fifo_depth;
unsigned int rx_fifo_depth;
int rx_outstanding;
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 0b42a12..cad0218 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -21,6 +21,7 @@
* ----------------------------------------------------------------------------
*
*/
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
@@ -158,6 +159,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
struct resource *mem;
int irq, r;
u32 acpi_speed, ht = 0;
+ bool isslave = false;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -190,6 +192,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
&dev->scl_falling_time);
device_property_read_u32(&pdev->dev, "clock-frequency",
&dev->clk_freq);
+ isslave = device_property_read_bool(&pdev->dev, "isslave");
}
acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev);
@@ -216,24 +219,46 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
dev->functionality =
I2C_FUNC_I2C |
- I2C_FUNC_10BIT_ADDR |
I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_I2C_BLOCK;
- dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
+ if (!isslave) {
+ dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
DW_IC_CON_RESTART_EN;
+ dev->functionality |= I2C_FUNC_10BIT_ADDR;
+ dev_info(&pdev->dev, "I am registed as a I2C Master!\n");
+ switch (dev->clk_freq) {
+ case 100000:
+ dev->master_cfg |= DW_IC_CON_SPEED_STD;
+ break;
+ case 3400000:
+ dev->master_cfg |= DW_IC_CON_SPEED_HIGH;
+ break;
+ default:
+ dev->master_cfg |= DW_IC_CON_SPEED_FAST;
+ }
+ } else {
+ dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |
+ DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED |
+ DW_IC_CON_SPEED_FAST;
+
+ dev->functionality |= I2C_FUNC_SLAVE;
+ dev->functionality &= ~I2C_FUNC_10BIT_ADDR;
+ dev_info(&pdev->dev, "I am registed as a I2C Slave!\n");
+
+ switch (dev->clk_freq) {
+ case 100000:
+ dev->slave_cfg |= DW_IC_CON_SPEED_STD;
+
+ case 3400000:
+ dev->slave_cfg |= DW_IC_CON_SPEED_HIGH;
+ break;
+ default:
+ dev->slave_cfg |= DW_IC_CON_SPEED_FAST;
- switch (dev->clk_freq) {
- case 100000:
- dev->master_cfg |= DW_IC_CON_SPEED_STD;
- break;
- case 3400000:
- dev->master_cfg |= DW_IC_CON_SPEED_HIGH;
- break;
- default:
- dev->master_cfg |= DW_IC_CON_SPEED_FAST;
+ }
}
dev->clk = devm_clk_get(&pdev->dev, NULL);
--
2.10.1
^ permalink raw reply related
* Re: [PATCH] Add slave mode to Synopsys I2C driver
From: Andy Shevchenko @ 2016-10-11 12:45 UTC (permalink / raw)
To: Luis.Oliveira, jarkko.nikula, mika.westerberg, wsa, linux-i2c,
linux-kernel
Cc: CARLOS.PALMINHA
In-Reply-To: <95e8074bcdf85c11db2497a2bf5218ce66446051.1476187036.git.lolivei@synopsys.com>
On Tue, 2016-10-11 at 13:18 +0100, Luis.Oliveira@synopsys.com wrote:
> From: Luis Oliveira <lolivei@synopsys.com>
>
> Add support in existing I2C Synopsys Designware Core driver for I2C
> slave mode.
My comments below.
> --- a/drivers/i2c/busses/i2c-designware-core.c
> +++ b/drivers/i2c/busses/i2c-designware-core.c
> @@ -21,6 +21,7 @@
> * ------------------------------------------------------------------
> ----------
> *
> */
> +
Doesn't belong this patch (let's call it indent fix).
> #include <linux/export.h>
> #include <linux/errno.h>
> #include <linux/err.h>
> @@ -85,15 +87,27 @@
> #define DW_IC_INTR_STOP_DET 0x200
> #define DW_IC_INTR_START_DET 0x400
> #define DW_IC_INTR_GEN_CALL 0x800
> -
> -#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL |
> \
> - DW_IC_INTR_TX_EMPTY | \
> - DW_IC_INTR_TX_ABRT | \
> - DW_IC_INTR_STOP_DET)
> +#define DW_IC_INTR_RESTART_DET 0x1000
> +
> +#define DW_IC_INTR_MST_DEFAULT_MASK (DW_IC_INTR_RX_FUL
> L | \
> + DW_IC_INTR_TX_EMPTY | \
> + DW_IC_INTR_TX_ABRT | \
> + DW_IC_INTR_STOP_DET | \
> + DW_IC_INTR_RX_DONE | \
> + DW_IC_INTR_RX_UNDER | \
> + DW_IC_INTR_RD_REQ)
> +
> + #define DW_IC_INTR_SLV_DEFAULT_MASK (DW_IC_INTR_RX_FU
> LL | \
> + DW_IC_INTR_STOP_DET | \
It would be better to keep list in the same order as in other set above.
> + DW_IC_INTR_TX_ABRT | \
> + DW_IC_INTR_RX_DONE | \
> + DW_IC_INTR_RX_UNDER | \
> + DW_IC_INTR_RD_REQ)
>
Or even split common part with previous name.
> @@ -107,7 +121,7 @@
> */
> #define STATUS_IDLE 0x0
> #define STATUS_WRITE_IN_PROGRESS 0x1
> -#define STATUS_READ_IN_PROGRESS 0x2
> +#define STATUS_READ_IN_PROGRESS 0x2
Indent fix. Not here.
> @@ -128,6 +142,9 @@
> #define ABRT_10B_RD_NORSTRT 10
> #define ABRT_MASTER_DIS 11
> #define ARB_LOST 12
> +#define ABRT_SLVFLUSH_TXFIFO 13
> +#define ABRT_SLV_ARBLOST 14
> +#define ABRT_SLVRD_INTX 15
Can we use _SLAVE_ instead? It would be in align with _MASTER_.
Same to above MASK definition.
> @@ -140,6 +157,9 @@
> #define DW_IC_TX_ABRT_10B_RD_NORSTRT (1UL <<
> ABRT_10B_RD_NORSTRT)
> #define DW_IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS)
> #define DW_IC_TX_ARB_LOST (1UL << ARB_LOST)
> +#define DW_IC_RX_ABRT_SLVRD_INTX (1UL << ABRT_SLVRD_INTX)
> +#define DW_IC_RX_ABRT_SLV_ARBLOST (1UL << ABRT_SLV_ARBLOST)
> +#define DW_IC_RX_ABRT_SLVFLUSH_TXFIFO (1UL << ABRT_SLVFLUSH_TXFIFO)
Ditto.
> @@ -343,8 +369,8 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
> /* Configure register access mode 16bit */
> dev->accessor_flags |= ACCESS_16BIT;
> } else if (reg != DW_IC_COMP_TYPE_VALUE) {
> - dev_err(dev->dev, "Unknown Synopsys component type: "
> - "0x%08x\n", reg);
> + dev_err(dev->dev, "Unknown Synopsys component type:
> 0x%08x\n",
> + reg);
Indent fix. Not here.
> @@ -431,12 +457,30 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
> "Hardware too old to adjust SDA hold
> time.\n");
> }
>
> - /* Configure Tx/Rx FIFO threshold levels */
> - dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
> - dw_writel(dev, 0, DW_IC_RX_TL);
> -
> - /* configure the i2c master */
> - dw_writel(dev, dev->master_cfg , DW_IC_CON);
> + if ((dev->master_cfg & DW_IC_CON_MASTER) &&
> + (dev->master_cfg & DW_IC_CON_SLAVE_DISABLE)) {
> + /* IF master */
> +
> + /* Configure Tx/Rx FIFO threshold levels */
> + dw_writel(dev, dev->tx_fifo_depth / 2, DW_IC_TX_TL);
> + dw_writel(dev, 0, DW_IC_RX_TL);
> +
> + /* configure the i2c master */
> + dw_writel(dev, dev->master_cfg, DW_IC_CON);
> + dw_writel(dev, DW_IC_INTR_MST_DEFAULT_MASK,
> DW_IC_INTR_MASK);
> + } else if (!(dev->master_cfg & DW_IC_CON_MASTER) &&
> + !(dev->master_cfg & DW_IC_CON_SLAVE_DISABLE)) {
> + /*IF slave */
> +
> + /* Configure Tx/Rx FIFO threshold levels */
> + dw_writel(dev, 0, DW_IC_TX_TL);
> + dw_writel(dev, 0, DW_IC_RX_TL);
> +
> + /* configure the i2c slave */
> + dw_writel(dev, dev->slave_cfg, DW_IC_CON);
> + dw_writel(dev, DW_IC_INTR_SLV_DEFAULT_MASK,
> DW_IC_INTR_MASK);
> + } else
> + return -EAGAIN;
Regarding to such blocks. Perhaps you may refactor code first to split
*_master() functions out of existing ones and add *_slave() in sequent
patch?
Same to all similar pieces in the patch.
> @@ -772,12 +816,64 @@ done_nolock:
> static u32 i2c_dw_func(struct i2c_adapter *adap)
> {
> struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
> +
Indent fix. Not here.
> return dev->functionality;
> }
>
>
> +static int i2c_dw_reg_slave(struct i2c_client *slave)
> +{
> + struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
> +
> + if (dev->slave)
> + return -EBUSY;
+ empty line.
> + if (slave->flags & I2C_CLIENT_TEN)
> + return -EAFNOSUPPORT;
> + /* set slave address in the IC_SAR register,
> + * the address to which the DW_apb_i2c responds
> + */
No, multi-line comment style is different and please indent it
correctly.
> +
> + __i2c_dw_enable(dev, false);
> +
> + dw_writel(dev, slave->addr, DW_IC_SAR);
> +
> + pm_runtime_forbid(dev->dev);
Why? Add a comment and how it recovers (returns back) from this.
> +
> + dev->slave = slave;
> +
> + __i2c_dw_enable(dev, true);
> +
> + dev->cmd_err = 0;
> + dev->msg_write_idx = 0;
> + dev->msg_read_idx = 0;
> + dev->msg_err = 0;
> + dev->status = STATUS_IDLE;
> + dev->abort_source = 0;
> + dev->rx_outstanding = 0;
> +
> + return 0;
> +}
> +
> +static int i2c_dw_unreg_slave(struct i2c_client *slave)
> +{
> + struct dw_i2c_dev *dev = i2c_get_adapdata(slave->adapter);
> +
> + WARN_ON(!dev->slave);
I doubt i2c core will allow such. Same to above.
> +
> + i2c_dw_disable_int(dev);
> + i2c_dw_disable(dev);
> +
> + dev->slave = NULL;
> +
> + pm_runtime_allow(dev->dev);
> +
> + return 0;
> +}
> +
> static struct i2c_algorithm i2c_dw_algo = {
> .master_xfer = i2c_dw_xfer,
> .functionality = i2c_dw_func,
> + .reg_slave = i2c_dw_reg_slave,
> + .unreg_slave = i2c_dw_unreg_slave,
> @@ -839,19 +933,129 @@ static u32 i2c_dw_read_clear_intrbits(struct
> dw_i2c_dev *dev)
> * Interrupt service routine. This gets called whenever an I2C
> interrupt
> * occurs.
> */
> +
> +static bool i2c_dw_slave_irq_handler(struct dw_i2c_dev *dev)
_irq_handler_slave()
> +{
> + u32 raw_stat, stat, enabled;
> + u8 val;
> + u8 slv_act;
Reversed tree. slv -> slave.
> +
> + stat = dw_readl(dev, DW_IC_INTR_STAT);
> + enabled = dw_readl(dev, DW_IC_ENABLE);
> + raw_stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
> +
> + if (!enabled || !(raw_stat & ~DW_IC_INTR_ACTIVITY))
(!(enabled && (raw_stat & ~DW_IC...))) ?
> + return false;
> +
> + slv_act = ((dw_readl(dev, DW_IC_STATUS) &
> + DW_IC_STATUS_SLV_ACTIVITY)>>6);
> +
> + dev_dbg(dev->dev,
> + "%s: %#x SLAVE_ACTV=%#x : RAW_INTR_STAT=%#x :
> INTR_STAT=%#x\n",
> + __func__, enabled, slv_act, raw_stat, stat);
Indent lines properly.
> + if ((stat & DW_IC_INTR_RX_FULL) && (stat &
> DW_IC_INTR_STOP_DET)) {
> + dev_dbg(dev->dev, "First write\n");
Is this useful?
> + i2c_slave_event(dev->slave,
> I2C_SLAVE_WRITE_REQUESTED, &val);
> + }
> +
> + if (slv_act) {
> + dev_dbg(dev->dev, "I2C GET\n");
Or this?
> +
> + if (stat & DW_IC_INTR_RD_REQ) {
> + if (stat & DW_IC_INTR_RX_FULL) {
> + val = dw_readl(dev, DW_IC_DATA_CMD);
> + if (!i2c_slave_event(dev->slave,
> + I2C_SLAVE_WRITE_RECEIVED,
> &val)) {
> + dev_dbg(dev->dev, "Byte %X
> acked! ", val);
>
> + ;
What's that?
> + }
> + dev_dbg(dev->dev, "I2C GET + add");
> + dw_readl(dev, DW_IC_CLR_RD_REQ);
> + stat =
> i2c_dw_read_clear_intrbits(dev);
> + } else {
> + dev_dbg(dev->dev, "I2C GET + 0x00");
> + dw_readl(dev, DW_IC_CLR_RD_REQ);
> + dw_readl(dev, DW_IC_CLR_RX_UNDER);
> + stat =
> i2c_dw_read_clear_intrbits(dev);
> + }
> + if (!i2c_slave_event(dev->slave,
> + I2C_SLAVE_READ_REQUESTED,
> &val))
> + dw_writel(dev, (0x0 << 8 | val),
0 << (x) == 0. What the intention?
> DW_IC_DATA_CMD);
> + }
> + }
> + if (stat & DW_IC_INTR_RX_DONE) {
>
> +
Redundant.
> + if (!i2c_slave_event(dev->slave,
> I2C_SLAVE_READ_PROCESSED, &val))
> + dw_readl(dev, DW_IC_CLR_RX_DONE);
> +
> + i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
> + dev_dbg(dev->dev, "Transmission Complete.");
> + stat = i2c_dw_read_clear_intrbits(dev);
> +
> + goto done;
> + }
> +
> + if (stat & DW_IC_INTR_RX_FULL) {
> + dev_dbg(dev->dev, "I2C SET");
> + val = dw_readl(dev, DW_IC_DATA_CMD);
> + if (!i2c_slave_event(dev->slave,
> + I2C_SLAVE_WRITE_RECEIVED, &val)) {
> + dev_dbg(dev->dev, "Byte %X acked! ", val);
> + ;
> + }
> + } else {
> + i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
> + dev_dbg(dev->dev, "Transmission Complete.");
> + stat = i2c_dw_read_clear_intrbits(dev);
> + }
> +
> + if (stat & DW_IC_INTR_TX_OVER) {
> + dw_readl(dev, DW_IC_CLR_TX_OVER);
> + return true;
> + }
>
> +done:
Useless label. Return directly.
> + return true;
> +}
The function might need a refactoring.
> +
> static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id)
> {
> struct dw_i2c_dev *dev = dev_id;
> - u32 stat, enabled;
> + u32 stat, enabled, mode;
>
> enabled = dw_readl(dev, DW_IC_ENABLE);
> + mode = dw_readl(dev, DW_IC_CON);
> stat = dw_readl(dev, DW_IC_RAW_INTR_STAT);
> - dev_dbg(dev->dev, "%s: enabled=%#x stat=%#x\n", __func__,
> enabled, stat);
> +
> + stat = i2c_dw_read_clear_intrbits(dev);
> +
> + dev_dbg(dev->dev,
> + "%s: enabled=%#x stat=%#x\n", __func__, enabled,
> stat);
> +
> if (!enabled || !(stat & ~DW_IC_INTR_ACTIVITY))
> return IRQ_NONE;
>
> stat = i2c_dw_read_clear_intrbits(dev);
>
> + if (!(mode & DW_IC_CON_MASTER) && !(mode &
> DW_IC_CON_SLAVE_DISABLE)) {
> +
>
> + /* slave mode*/
Hmm... Besides style does it really help?
> + if (!i2c_dw_slave_irq_handler(dev))
> + return IRQ_NONE;
> +
> + complete(&dev->cmd_complete);
> + return IRQ_HANDLED;
> + }
> +
> --- a/drivers/i2c/busses/i2c-designware-core.h
> +++ b/drivers/i2c/busses/i2c-designware-core.h
> @@ -28,9 +28,13 @@
> #define DW_IC_CON_SPEED_FAST 0x4
> #define DW_IC_CON_SPEED_HIGH 0x6
> #define DW_IC_CON_SPEED_MASK 0x6
> +#define DW_IC_CON_10BITADDR_SLAVE 0x8
> #define DW_IC_CON_10BITADDR_MASTER 0x10
> #define DW_IC_CON_RESTART_EN 0x20
> #define DW_IC_CON_SLAVE_DISABLE 0x40
> +#define DW_IC_CON_STOP_DET_IFADDRESSED 0x80
> +#define DW_IC_CON_TX_EMPTY_CTRL 0x100
> +#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL 0x200
>
>
> /**
> @@ -80,7 +84,8 @@ struct dw_i2c_dev {
> void __iomem *base;
> struct completion cmd_complete;
> struct clk *clk;
> - u32 (*get_clk_rate_khz) (struct
> dw_i2c_dev *dev);
> + struct i2c_client *slave;
>
> + u32 (*get_clk_rate_khz)(struct
> dw_i2c_dev *dev);
What happened to this member? Indentation fix? Not here.
> --- a/drivers/i2c/busses/i2c-designware-platdrv.c
> +++ b/drivers/i2c/busses/i2c-designware-platdrv.c
> @@ -21,6 +21,7 @@
> * ------------------------------------------------------------------
> ----------
> *
> */
> +
Ditto.
> #include <linux/kernel.h>
> #include <linux/module.h>
> #include <linux/delay.h>
> @@ -158,6 +159,7 @@ static int dw_i2c_plat_probe(struct
> platform_device *pdev)
> struct resource *mem;
> int irq, r;
> u32 acpi_speed, ht = 0;
> + bool isslave = false;
isslave -> is_slave.
>
> irq = platform_get_irq(pdev, 0);
> if (irq < 0)
> @@ -190,6 +192,7 @@ static int dw_i2c_plat_probe(struct
> platform_device *pdev)
> &dev->scl_falling_time);
> device_property_read_u32(&pdev->dev, "clock-
> frequency",
> &dev->clk_freq);
> + isslave = device_property_read_bool(&pdev->dev,
> "isslave");
This needs a separate patch against device bindings.
Moreover, check if:
- there is already standard property for such functionality
- it can/can't be discovered automatically
- consequences of use this on ACPI-enabled platforms
> @@ -216,24 +219,46 @@ static int dw_i2c_plat_probe(struct
> platform_device *pdev)
>
> dev->functionality =
> I2C_FUNC_I2C |
> - I2C_FUNC_10BIT_ADDR |
> I2C_FUNC_SMBUS_BYTE |
> I2C_FUNC_SMBUS_BYTE_DATA |
> I2C_FUNC_SMBUS_WORD_DATA |
> I2C_FUNC_SMBUS_I2C_BLOCK;
>
> - dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE
> |
> + if (!isslave) {
> + dev->master_cfg = DW_IC_CON_MASTER |
> DW_IC_CON_SLAVE_DISABLE |
> DW_IC_CON_RESTART_EN;
> + dev->functionality |= I2C_FUNC_10BIT_ADDR;
> + dev_info(&pdev->dev, "I am registed as a I2C
> Master!\n");
> + switch (dev->clk_freq) {
> + case 100000:
> + dev->master_cfg |= DW_IC_CON_SPEED_STD;
> + break;
> + case 3400000:
> + dev->master_cfg |= DW_IC_CON_SPEED_HIGH;
> + break;
> + default:
> + dev->master_cfg |= DW_IC_CON_SPEED_FAST;
> + }
> + } else {
> + dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL |
> + DW_IC_CON_RESTART_EN |
> DW_IC_CON_STOP_DET_IFADDRESSED |
> + DW_IC_CON_SPEED_FAST;
> +
> + dev->functionality |= I2C_FUNC_SLAVE;
> + dev->functionality &= ~I2C_FUNC_10BIT_ADDR;
> + dev_info(&pdev->dev, "I am registed as a I2C
> Slave!\n");
> +
> + switch (dev->clk_freq) {
> + case 100000:
> + dev->slave_cfg |= DW_IC_CON_SPEED_STD;
> +
> + case 3400000:
> + dev->slave_cfg |= DW_IC_CON_SPEED_HIGH;
> + break;
> + default:
> + dev->slave_cfg |= DW_IC_CON_SPEED_FAST;
>
> - switch (dev->clk_freq) {
> - case 100000:
> - dev->master_cfg |= DW_IC_CON_SPEED_STD;
> - break;
> - case 3400000:
> - dev->master_cfg |= DW_IC_CON_SPEED_HIGH;
> - break;
> - default:
> - dev->master_cfg |= DW_IC_CON_SPEED_FAST;
> + }
Factor out _master() part first.
In summary I see 4 patches here:
- factor out _master() parts
- enable slave
- device bindings
- indentation fix
--
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Intel Finland Oy
^ permalink raw reply
* [PATCH] drivers/i2c/i2c-dev: Fix kernel memory disclosure
From: Vlad Tsyrklevich @ 2016-10-11 12:52 UTC (permalink / raw)
To: linux-i2c; +Cc: wsa, Vlad Tsyrklevich
i2c_smbus_xfer() does not always fill an entire block, allowing
kernel stack memory disclosure through the temp variable. Clear
it before it's read to.
Signed-off-by: Vlad Tsyrklevich <vlad@tsyrklevich.net>
---
drivers/i2c/i2c-dev.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index 66f323f..62cb111 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -393,6 +393,8 @@ static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
(data_arg.read_write == I2C_SMBUS_WRITE)) {
if (copy_from_user(&temp, data_arg.data, datasize))
return -EFAULT;
+ } else {
+ memset(&temp, 0, datasize);
}
if (data_arg.size == I2C_SMBUS_I2C_BLOCK_BROKEN) {
/* Convert old I2C block commands to the new
--
2.7.0
^ permalink raw reply related
* Re: [PATCH v4 8/8] Input: elan_i2c - add Host Notify support
From: Benjamin Tissoires @ 2016-10-11 14:20 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Wolfram Sang, Jean Delvare, Jonathan Corbet, KT Liao, Linux I2C,
linux-input@vger.kernel.org, linux-doc@vger.kernel.org, lkml
In-Reply-To: <CAKdAkRRK746K_+qqi+5YYfJ8NMBwbn5MYa02N4tn-qkfjnVwGg@mail.gmail.com>
On Oct 10 2016 or thereabouts, Dmitry Torokhov wrote:
> Hi Benjamin,
>
> On Mon, Oct 10, 2016 at 9:42 AM, Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
> > The Thinkpad series 13 uses Host Notify to report the interrupt.
> > Add elan_smb_alert() to handle those interrupts and disable the irq
> > handling on this case.
> >
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> >
>
> Why do we have to do this in the driver instead of having I2C core set
> it up for us? I expect we'd be repeating this block of code for every
> driver that has an option of using SMbus notify.
I didn't want to assign blindly an IRQ for Host Notify because it's a
device (as I2C client) feature. Not all SMBus clients on the Host Notify
capable bus are capable of Host Notify, so I thought it was the
responsibility of the driver to assign it.
However, I can see your point, though I'd need some inputs (and I'll
have to resend the series as the Intel bot showed me 2 mistakes).
So, if i2c-core gets to assign itself the IRQ for Host Notify, do we:
1) assign an IRQ to any SMBus device on a Host Notify capable adapter
that doesn't have a valid provided IRQ?
2) have a new field in struct i2c_board_info that explicitly requires
Host Notify as an IRQ?
3) do not touch anything in i2c_core, let the caller of i2c_new_device
fill in the irq by a call to
i2c_smbus_host_notify_to_irq(adapter, address)?
1) has the advantage of being transparent for everybody, except we will
provide IRQs to devices that don't have one (this can be ignored), but
this may also lead to errors when IRQ is not correctly set by the caller
where it should be, and the driver/developer doesn't understand why it
is never triggered.
2) is a nice compromise, but we will need some OF binding, add some work
in for the callers of i2c_new_device() and will not work nicely with
sysfs instantiated i2c devices (the new_device sysfs entry). The sysfs
could be solved by adding some new address namespace, but that doesn't
make sense I think
3) requires less changes in i2c_core but it's going to be even harder
to add a device through sysfs that is Host Notify capable given that we
can't specify the IRQ.
After thinking a bit, I think I'd lean toward 1), but I am open to other
options as well.
Cheers,
Benjamin
>
> Thanks!
>
> > ---
> >
> > new in v4 (was submitted on linux-input with the .alert callback)
> > ---
> > drivers/input/mouse/elan_i2c_core.c | 15 +++++++++++++++
> > 1 file changed, 15 insertions(+)
> >
> > diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
> > index 6f16eb4..4aaac5d 100644
> > --- a/drivers/input/mouse/elan_i2c_core.c
> > +++ b/drivers/input/mouse/elan_i2c_core.c
> > @@ -1040,6 +1040,21 @@ static int elan_probe(struct i2c_client *client,
> > I2C_FUNC_SMBUS_BLOCK_DATA |
> > I2C_FUNC_SMBUS_I2C_BLOCK)) {
> > transport_ops = &elan_smbus_ops;
> > +
> > + if (!irq) {
> > + if (!i2c_check_functionality(client->adapter,
> > + I2C_FUNC_SMBUS_HOST_NOTIFY)) {
> > + dev_err(dev, "no Host Notify support\n");
> > + return -ENODEV;
> > + }
> > +
> > + irq = i2c_smbus_host_notify_to_irq(client);
> > + if (irq < 0) {
> > + dev_err(dev,
> > + "Unable to request a Host Notify IRQ.\n");
> > + return irq;
> > + }
> > + }
> > } else {
> > dev_err(dev, "not a supported I2C/SMBus adapter\n");
> > return -EIO;
> > --
> > 2.7.4
> >
>
>
>
> --
> Dmitry
^ permalink raw reply
* [PATCH v6 0/2] Add ACPI support and SMBus block read
From: Austin Christ @ 2016-10-11 16:27 UTC (permalink / raw)
To: nkaje, wsa, linux-i2c, linux-kernel
Cc: linux-arm-msm, timur, rruigrok, cov, Austin Christ, sricharan,
linux-arm-kernel
From: Austin Christ <austinwc@qti.qualcomm.com>
Add ACPI support to I2C QUP driver and get properties from ACPI table.
Add support to the I2C QUP driver to correctly handle SMBus block reads by
checking I2C_M_RECV_LEN flag and reading the first byte received as the
message length.
Documentation for the two properites used from ACPI has been submitted through
dsd@acpica.org to https://github.com/ahs3/dsd. The documentation can be viewed
at https://lists.acpica.org/pipermail/dsd/2016-September/000095.html.
[V6]
- correct block size for SMBus data read
[V5]
- remove warning and use correct ACPI function
[V4]
- correct error code
- remove warning for fall back to default clock frequency
[V3]
- clean up unused variables
- use constant instead of variable for smbus length field
[V2]
- rework the smbus block read and break into separate function
- clean up redundant checks and variables
Naveen Kaje (2):
i2c: qup: add ACPI support
i2c: qup: support SMBus block read
drivers/i2c/busses/i2c-qup.c | 122 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 103 insertions(+), 19 deletions(-)
--
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply
* [PATCH v6 1/2] i2c: qup: add ACPI support
From: Austin Christ @ 2016-10-11 16:27 UTC (permalink / raw)
To: nkaje, wsa, linux-i2c, linux-kernel
Cc: Austin Christ, linux-arm-msm, timur, rruigrok, cov, sricharan,
linux-arm-kernel
In-Reply-To: <1476203277-6893-1-git-send-email-austinwc@codeaurora.org>
From: Naveen Kaje <nkaje@codeaurora.org>
Add support to get the device parameters from ACPI. Assume
that the clocks are managed by firmware.
Signed-off-by: Naveen Kaje <nkaje@codeaurora.org>
Signed-off-by: Austin Christ <austinwc@codeaurora.org>
Reviewed-by: Sricharan R <sricharan@codeaurora.org>
---
drivers/i2c/busses/i2c-qup.c | 58 ++++++++++++++++++++++++++++++++------------
1 file changed, 42 insertions(+), 16 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 041050e..bf0957e 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -14,6 +14,7 @@
*
*/
+#include <linux/acpi.h>
#include <linux/atomic.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -132,6 +133,10 @@
/* Max timeout in ms for 32k bytes */
#define TOUT_MAX 300
+/* Default values. Use these if FW query fails */
+#define DEFAULT_CLK_FREQ 100000
+#define DEFAULT_SRC_CLK 20000000
+
struct qup_i2c_block {
int count;
int pos;
@@ -1356,14 +1361,13 @@ static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup)
static int qup_i2c_probe(struct platform_device *pdev)
{
static const int blk_sizes[] = {4, 16, 32};
- struct device_node *node = pdev->dev.of_node;
struct qup_i2c_dev *qup;
unsigned long one_bit_t;
struct resource *res;
u32 io_mode, hw_ver, size;
int ret, fs_div, hs_div;
- int src_clk_freq;
- u32 clk_freq = 100000;
+ u32 src_clk_freq = DEFAULT_SRC_CLK;
+ u32 clk_freq = DEFAULT_CLK_FREQ;
int blocks;
qup = devm_kzalloc(&pdev->dev, sizeof(*qup), GFP_KERNEL);
@@ -1374,7 +1378,11 @@ static int qup_i2c_probe(struct platform_device *pdev)
init_completion(&qup->xfer);
platform_set_drvdata(pdev, qup);
- of_property_read_u32(node, "clock-frequency", &clk_freq);
+ ret = device_property_read_u32(qup->dev, "clock-frequency", &clk_freq);
+ if (ret) {
+ dev_notice(qup->dev, "using default clock-frequency %d",
+ DEFAULT_CLK_FREQ);
+ }
if (of_device_is_compatible(pdev->dev.of_node, "qcom,i2c-qup-v1.1.1")) {
qup->adap.algo = &qup_i2c_algo;
@@ -1456,20 +1464,30 @@ nodma:
return qup->irq;
}
- qup->clk = devm_clk_get(qup->dev, "core");
- if (IS_ERR(qup->clk)) {
- dev_err(qup->dev, "Could not get core clock\n");
- return PTR_ERR(qup->clk);
- }
+ if (has_acpi_companion(qup->dev)) {
+ ret = device_property_read_u32(qup->dev,
+ "src-clock-hz", &src_clk_freq);
+ if (ret) {
+ dev_notice(qup->dev, "using default src-clock-hz %d",
+ DEFAULT_SRC_CLK);
+ }
+ ACPI_COMPANION_SET(&qup->adap.dev, ACPI_COMPANION(qup->dev));
+ } else {
+ qup->clk = devm_clk_get(qup->dev, "core");
+ if (IS_ERR(qup->clk)) {
+ dev_err(qup->dev, "Could not get core clock\n");
+ return PTR_ERR(qup->clk);
+ }
- qup->pclk = devm_clk_get(qup->dev, "iface");
- if (IS_ERR(qup->pclk)) {
- dev_err(qup->dev, "Could not get iface clock\n");
- return PTR_ERR(qup->pclk);
+ qup->pclk = devm_clk_get(qup->dev, "iface");
+ if (IS_ERR(qup->pclk)) {
+ dev_err(qup->dev, "Could not get iface clock\n");
+ return PTR_ERR(qup->pclk);
+ }
+ qup_i2c_enable_clocks(qup);
+ src_clk_freq = clk_get_rate(qup->clk);
}
- qup_i2c_enable_clocks(qup);
-
/*
* Bootloaders might leave a pending interrupt on certain QUP's,
* so we reset the core before registering for interrupts.
@@ -1516,7 +1534,6 @@ nodma:
size = QUP_INPUT_FIFO_SIZE(io_mode);
qup->in_fifo_sz = qup->in_blk_sz * (2 << size);
- src_clk_freq = clk_get_rate(qup->clk);
fs_div = ((src_clk_freq / clk_freq) / 2) - 3;
hs_div = 3;
qup->clk_ctl = (hs_div << 8) | (fs_div & 0xff);
@@ -1641,6 +1658,14 @@ static const struct of_device_id qup_i2c_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, qup_i2c_dt_match);
+#if IS_ENABLED(CONFIG_ACPI)
+static const struct acpi_device_id qup_i2c_acpi_match[] = {
+ { "QCOM8010"},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, qup_i2c_acpi_match);
+#endif
+
static struct platform_driver qup_i2c_driver = {
.probe = qup_i2c_probe,
.remove = qup_i2c_remove,
@@ -1648,6 +1673,7 @@ static struct platform_driver qup_i2c_driver = {
.name = "i2c_qup",
.pm = &qup_i2c_qup_pm_ops,
.of_match_table = qup_i2c_dt_match,
+ .acpi_match_table = ACPI_PTR(qup_i2c_acpi_match),
},
};
--
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply related
* [PATCH v6 2/2] i2c: qup: support SMBus block read
From: Austin Christ @ 2016-10-11 16:27 UTC (permalink / raw)
To: nkaje, wsa, linux-i2c, linux-kernel
Cc: Austin Christ, linux-arm-msm, timur, rruigrok, cov, sricharan,
linux-arm-kernel
In-Reply-To: <1476203277-6893-1-git-send-email-austinwc@codeaurora.org>
From: Naveen Kaje <nkaje@codeaurora.org>
I2C QUP driver relies on SMBus emulation support from the framework.
To handle SMBus block reads, the driver should check I2C_M_RECV_LEN
flag and should read the first byte received as the message length.
The driver configures the QUP hardware to read one byte. Once the
message length is known from this byte, the QUP hardware is configured
to read the rest.
Signed-off-by: Naveen Kaje <nkaje@codeaurora.org>
Signed-off-by: Austin Christ <austinwc@codeaurora.org>
Reviewed-by: Sricharan R <sricharan@codeaurora.org>
---
drivers/i2c/busses/i2c-qup.c | 64 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 61 insertions(+), 3 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index bf0957e..d34120a 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -517,6 +517,33 @@ static int qup_i2c_get_data_len(struct qup_i2c_dev *qup)
return data_len;
}
+static bool qup_i2c_check_msg_len(struct i2c_msg *msg)
+{
+ return ((msg->flags & I2C_M_RD) && (msg->flags & I2C_M_RECV_LEN));
+}
+
+static int qup_i2c_set_tags_smb(u16 addr, u8 *tags, struct qup_i2c_dev *qup,
+ struct i2c_msg *msg)
+{
+ int len = 0;
+
+ if (msg->len > 1) {
+ tags[len++] = QUP_TAG_V2_DATARD_STOP;
+ tags[len++] = qup_i2c_get_data_len(qup) - 1;
+ } else {
+ tags[len++] = QUP_TAG_V2_START;
+ tags[len++] = addr & 0xff;
+
+ if (msg->flags & I2C_M_TEN)
+ tags[len++] = addr >> 8;
+
+ tags[len++] = QUP_TAG_V2_DATARD;
+ /* Read 1 byte indicating the length of the SMBus message */
+ tags[len++] = 1;
+ }
+ return len;
+}
+
static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
struct i2c_msg *msg, int is_dma)
{
@@ -526,6 +553,10 @@ static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
int last = (qup->blk.pos == (qup->blk.count - 1)) && (qup->is_last);
+ /* Handle tags for SMBus block read */
+ if (qup_i2c_check_msg_len(msg))
+ return qup_i2c_set_tags_smb(addr, tags, qup, msg);
+
if (qup->blk.pos == 0) {
tags[len++] = QUP_TAG_V2_START;
tags[len++] = addr & 0xff;
@@ -1065,9 +1096,17 @@ static int qup_i2c_read_fifo_v2(struct qup_i2c_dev *qup,
struct i2c_msg *msg)
{
u32 val;
- int idx, pos = 0, ret = 0, total;
+ int idx, pos = 0, ret = 0, total, msg_offset = 0;
+ /*
+ * If the message length is already read in
+ * the first byte of the buffer, account for
+ * that by setting the offset
+ */
+ if (qup_i2c_check_msg_len(msg) && (msg->len > 1))
+ msg_offset = 1;
total = qup_i2c_get_data_len(qup);
+ total -= msg_offset;
/* 2 extra bytes for read tags */
while (pos < (total + 2)) {
@@ -1087,8 +1126,8 @@ static int qup_i2c_read_fifo_v2(struct qup_i2c_dev *qup,
if (pos >= (total + 2))
goto out;
-
- msg->buf[qup->pos++] = val & 0xff;
+ msg->buf[qup->pos + msg_offset] = val & 0xff;
+ qup->pos++;
}
}
@@ -1128,6 +1167,20 @@ static int qup_i2c_read_one_v2(struct qup_i2c_dev *qup, struct i2c_msg *msg)
goto err;
qup->blk.pos++;
+
+ /* Handle SMBus block read length */
+ if (qup_i2c_check_msg_len(msg) && (msg->len == 1)) {
+ if (msg->buf[0] > I2C_SMBUS_BLOCK_MAX) {
+ ret = -EPROTO;
+ goto err;
+ }
+ msg->len += msg->buf[0];
+ qup->pos = 0;
+ qup_i2c_set_blk_data(qup, msg);
+ /* set tag length for block read */
+ qup->blk.tx_tag_len = 2;
+ qup_i2c_set_read_mode_v2(qup, msg->buf[0]);
+ }
} while (qup->blk.pos < qup->blk.count);
err:
@@ -1210,6 +1263,11 @@ static int qup_i2c_xfer(struct i2c_adapter *adap,
goto out;
}
+ if (qup_i2c_check_msg_len(&msgs[idx])) {
+ ret = -EINVAL;
+ goto out;
+ }
+
if (msgs[idx].flags & I2C_M_RD)
ret = qup_i2c_read_one(qup, &msgs[idx]);
else
--
Qualcomm Datacenter Technologies as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the
Code Aurora Forum, a Linux Foundation Collaborative Project.
^ permalink raw reply related
* [PATCH 0/5] i2c: i2c-piix4: set of coding style fixes
From: Carlos Palminha @ 2016-10-11 17:24 UTC (permalink / raw)
To: linux-i2c, linux-kernel; +Cc: jdelvare, wsa, CARLOS.PALMINHA
This patchset fixes several coding style issues.
Carlos Palminha (5):
i2c: i2c-piix4: coding style fix - '*' adjacent to data name
i2c: i2c-piix4: coding style fix - assignment in if condition
i2c: i2c-piix4: coding style fix - long comments blocks
i2c: i2c-piix4: coding style fix - spaces with open parenthesis
i2c: i2c-piix4: coding style fix - quoted string split across lines
drivers/i2c/busses/i2c-piix4.c | 144 +++++++++++++++++++++++------------------
1 file changed, 82 insertions(+), 62 deletions(-)
--
2.9.3
^ permalink raw reply
* [PATCH 1/5] i2c: i2c-piix4: coding style fix - '*' adjacent to data name
From: Carlos Palminha @ 2016-10-11 17:24 UTC (permalink / raw)
To: linux-i2c, linux-kernel; +Cc: jdelvare, wsa, CARLOS.PALMINHA
In-Reply-To: <20161011172441.7422-1-palminha@synopsys.com>
Signed-off-by: Carlos Palminha <palminha@synopsys.com>
---
drivers/i2c/busses/i2c-piix4.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index c2268cd..6b55a8e 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -482,9 +482,9 @@ static int piix4_transaction(struct i2c_adapter *piix4_adapter)
}
/* Return negative errno on error. */
-static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
+static s32 piix4_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
- u8 command, int size, union i2c_smbus_data * data)
+ u8 command, int size, union i2c_smbus_data *data)
{
struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap);
unsigned short piix4_smba = adapdata->smba;
--
2.9.3
^ permalink raw reply related
* [PATCH 3/5] i2c: i2c-piix4: coding style fix - long comments blocks
From: Carlos Palminha @ 2016-10-11 17:24 UTC (permalink / raw)
To: linux-i2c, linux-kernel; +Cc: jdelvare, wsa, CARLOS.PALMINHA
In-Reply-To: <20161011172441.7422-1-palminha@synopsys.com>
Signed-off-by: Carlos Palminha <palminha@synopsys.com>
---
drivers/i2c/busses/i2c-piix4.c | 94 ++++++++++++++++++++++++------------------
1 file changed, 54 insertions(+), 40 deletions(-)
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 5d6f637..85b1052 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -1,32 +1,32 @@
/*
- Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
- Philip Edelbrock <phil@netroedge.com>
-
- 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 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.
-*/
+ * Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
+ * Philip Edelbrock <phil@netroedge.com>
+ *
+ * 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 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.
+ */
/*
- Supports:
- Intel PIIX4, 440MX
- Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100
- ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800
- AMD Hudson-2, ML, CZ
- SMSC Victory66
-
- Note: we assume there can only be one device, with one or more
- SMBus interfaces.
- The device can register multiple i2c_adapters (up to PIIX4_MAX_ADAPTERS).
- For devices supporting multiple ports the i2c_adapter should provide
- an i2c_algorithm to access them.
-*/
+ * Supports:
+ * Intel PIIX4, 440MX
+ * Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100
+ * ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800
+ * AMD Hudson-2, ML, CZ
+ * SMSC Victory66
+ *
+ * Note: we assume there can only be one device, with one or more
+ * SMBus interfaces.
+ * The device can register multiple i2c_adapters (up to PIIX4_MAX_ADAPTERS).
+ * For devices supporting multiple ports the i2c_adapter should provide
+ * an i2c_algorithm to access them.
+ */
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -97,14 +97,18 @@
/* insmod parameters */
-/* If force is set to anything different from 0, we forcibly enable the
- PIIX4. DANGEROUS! */
+/*
+ * If force is set to anything different from 0, we forcibly enable the
+ * PIIX4. DANGEROUS!
+ */
static int force;
module_param (force, int, 0);
MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!");
-/* If force_addr is set to anything different from 0, we forcibly enable
- the PIIX4 at the given address. VERY DANGEROUS! */
+/*
+ * If force_addr is set to anything different from 0, we forcibly enable
+ * the PIIX4 at the given address. VERY DANGEROUS!
+ */
static int force_addr;
module_param (force_addr, int, 0);
MODULE_PARM_DESC(force_addr,
@@ -132,8 +136,10 @@ static const struct dmi_system_id piix4_dmi_blacklist[] = {
{ }
};
-/* The IBM entry is in a separate table because we only check it
- on Intel-based systems */
+/*
+ * The IBM entry is in a separate table because we only check it
+ * on Intel-based systems
+ */
static const struct dmi_system_id piix4_dmi_ibm[] = {
{
.ident = "IBM",
@@ -172,8 +178,10 @@ static int piix4_setup(struct pci_dev *PIIX4_dev,
(PIIX4_dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5))
srvrworks_csb5_delay = 1;
- /* On some motherboards, it was reported that accessing the SMBus
- caused severe hardware problems */
+ /*
+ * On some motherboards, it was reported that accessing the SMBus
+ * caused severe hardware problems
+ */
if (dmi_check_system(piix4_dmi_blacklist)) {
dev_err(&PIIX4_dev->dev,
"Accessing the SMBus on this system is unsafe!\n");
@@ -215,8 +223,10 @@ static int piix4_setup(struct pci_dev *PIIX4_dev,
pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp);
- /* If force_addr is set, we program the new address here. Just to make
- sure, we disable the PIIX4 first. */
+ /*
+ * If force_addr is set, we program the new address here. Just to make
+ * sure, we disable the PIIX4 first.
+ */
if (force_addr) {
pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe);
pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba);
@@ -369,8 +379,10 @@ static int piix4_setup_aux(struct pci_dev *PIIX4_dev,
const struct pci_device_id *id,
unsigned short base_reg_addr)
{
- /* Set up auxiliary SMBus controllers found on some
- * AMD chipsets e.g. SP5100 (SB700 derivative) */
+ /*
+ * Set up auxiliary SMBus controllers found on some
+ * AMD chipsets e.g. SP5100 (SB700 derivative)
+ */
unsigned short piix4_smba;
@@ -804,8 +816,10 @@ static int piix4_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
if (retval > 0) {
- /* Try to add the aux adapter if it exists,
- * piix4_add_adapter will clean up if this fails */
+ /*
+ * Try to add the aux adapter if it exists,
+ * piix4_add_adapter will clean up if this fails
+ */
piix4_add_adapter(dev, retval, false, 0,
is_sb800 ? piix4_aux_port_name_sb800 : "",
&piix4_aux_adapter);
--
2.9.3
^ permalink raw reply related
* [PATCH 4/5] i2c: i2c-piix4: coding style fix - spaces with open parenthesis
From: Carlos Palminha @ 2016-10-11 17:24 UTC (permalink / raw)
To: linux-i2c, linux-kernel; +Cc: jdelvare, wsa, CARLOS.PALMINHA
In-Reply-To: <20161011172441.7422-1-palminha@synopsys.com>
Signed-off-by: Carlos Palminha <palminha@synopsys.com>
---
drivers/i2c/busses/i2c-piix4.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 85b1052..5cc460a 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -102,7 +102,7 @@
* PIIX4. DANGEROUS!
*/
static int force;
-module_param (force, int, 0);
+module_param(force, int, 0);
MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!");
/*
@@ -110,7 +110,7 @@ MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!");
* the PIIX4 at the given address. VERY DANGEROUS!
*/
static int force_addr;
-module_param (force_addr, int, 0);
+module_param(force_addr, int, 0);
MODULE_PARM_DESC(force_addr,
"Forcibly enable the PIIX4 at the given address. "
"EXTREMELY DANGEROUS!");
@@ -204,7 +204,7 @@ static int piix4_setup(struct pci_dev *PIIX4_dev,
} else {
pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba);
piix4_smba &= 0xfff0;
- if(piix4_smba == 0) {
+ if (piix4_smba == 0) {
dev_err(&PIIX4_dev->dev, "SMBus base address "
"uninitialized - upgrade BIOS or use "
"force_addr=0xaddr\n");
@@ -664,7 +664,7 @@ static const struct pci_device_id piix4_ids[] = {
{ 0, }
};
-MODULE_DEVICE_TABLE (pci, piix4_ids);
+MODULE_DEVICE_TABLE(pci, piix4_ids);
static struct i2c_adapter *piix4_main_adapters[PIIX4_MAX_ADAPTERS];
static struct i2c_adapter *piix4_aux_adapter;
--
2.9.3
^ permalink raw reply related
* [PATCH 5/5] i2c: i2c-piix4: coding style fix - quoted string split across lines
From: Carlos Palminha @ 2016-10-11 17:34 UTC (permalink / raw)
To: linux-i2c, linux-kernel; +Cc: jdelvare, wsa, CARLOS.PALMINHA
In-Reply-To: <20161011172441.7422-1-palminha@synopsys.com>
Signed-off-by: Carlos Palminha <palminha@synopsys.com>
---
drivers/i2c/busses/i2c-piix4.c | 29 ++++++++++++++++-------------
1 file changed, 16 insertions(+), 13 deletions(-)
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 5cc460a..235a406 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -260,7 +260,8 @@ static int piix4_setup(struct pci_dev *PIIX4_dev,
else if ((temp & 0x0E) == 0)
dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n");
else
- dev_err(&PIIX4_dev->dev, "Illegal Interrupt configuration "
+ dev_err(&PIIX4_dev->dev,
+ "Illegal Interrupt configuration "
"(or code out of date)!\n");
pci_read_config_byte(PIIX4_dev, SMBREV, &temp);
@@ -280,8 +281,8 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
/* SB800 and later SMBus does not support forcing address */
if (force || force_addr) {
- dev_err(&PIIX4_dev->dev, "SMBus does not support "
- "forcing address!\n");
+ dev_err(&PIIX4_dev->dev,
+ "SMBus does not support forcing address!\n");
return -EINVAL;
}
@@ -338,8 +339,9 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev,
/* Request the SMBus I2C bus config region */
if (!request_region(piix4_smba + i2ccfg_offset, 1, "i2ccfg")) {
- dev_err(&PIIX4_dev->dev, "SMBus I2C bus config region "
- "0x%x already in use!\n", piix4_smba + i2ccfg_offset);
+ dev_err(&PIIX4_dev->dev,
+ "SMBus I2C bus config region 0x%x already in use!\n",
+ piix4_smba + i2ccfg_offset);
release_region(piix4_smba, SMBIOSIZE);
return -EBUSY;
}
@@ -405,8 +407,9 @@ static int piix4_setup_aux(struct pci_dev *PIIX4_dev,
return -ENODEV;
if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) {
- dev_err(&PIIX4_dev->dev, "Auxiliary SMBus region 0x%x "
- "already in use!\n", piix4_smba);
+ dev_err(&PIIX4_dev->dev,
+ "Auxiliary SMBus region 0x%x already in use!\n",
+ piix4_smba);
return -EBUSY;
}
@@ -433,8 +436,8 @@ static int piix4_transaction(struct i2c_adapter *piix4_adapter)
/* Make sure the SMBus host is ready to start transmitting */
temp = inb_p(SMBHSTSTS);
if (temp != 0x00) {
- dev_dbg(&piix4_adapter->dev, "SMBus busy (%02x). "
- "Resetting...\n", temp);
+ dev_dbg(&piix4_adapter->dev,
+ "SMBus busy (%02x). Resetting...\n", temp);
outb_p(temp, SMBHSTSTS);
temp = inb_p(SMBHSTSTS);
if (temp != 0x00) {
@@ -486,8 +489,8 @@ static int piix4_transaction(struct i2c_adapter *piix4_adapter)
temp = inb_p(SMBHSTSTS);
if (temp != 0x00) {
- dev_err(&piix4_adapter->dev, "Failed reset at end of "
- "transaction (%02x)\n", temp);
+ dev_err(&piix4_adapter->dev,
+ "Failed reset at end of transaction (%02x)\n", temp);
}
dev_dbg(&piix4_adapter->dev, "Transaction (post): CNT=%02x, CMD=%02x, "
"ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT),
@@ -870,7 +873,7 @@ static struct pci_driver piix4_driver = {
module_pci_driver(piix4_driver);
-MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
- "Philip Edelbrock <phil@netroedge.com>");
+MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
+MODULE_AUTHOR("Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("PIIX4 SMBus driver");
MODULE_LICENSE("GPL");
--
2.9.3
^ permalink raw reply related
* Re: [PATCH v4 8/8] Input: elan_i2c - add Host Notify support
From: Dmitry Torokhov @ 2016-10-11 17:46 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Wolfram Sang, Jean Delvare, Jonathan Corbet, KT Liao, Linux I2C,
linux-input@vger.kernel.org, linux-doc@vger.kernel.org, lkml
In-Reply-To: <20161011142051.GL30411@mail.corp.redhat.com>
On Tue, Oct 11, 2016 at 04:20:52PM +0200, Benjamin Tissoires wrote:
> On Oct 10 2016 or thereabouts, Dmitry Torokhov wrote:
> > Hi Benjamin,
> >
> > On Mon, Oct 10, 2016 at 9:42 AM, Benjamin Tissoires
> > <benjamin.tissoires@redhat.com> wrote:
> > > The Thinkpad series 13 uses Host Notify to report the interrupt.
> > > Add elan_smb_alert() to handle those interrupts and disable the irq
> > > handling on this case.
> > >
> > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > >
> >
> > Why do we have to do this in the driver instead of having I2C core set
> > it up for us? I expect we'd be repeating this block of code for every
> > driver that has an option of using SMbus notify.
>
> I didn't want to assign blindly an IRQ for Host Notify because it's a
> device (as I2C client) feature. Not all SMBus clients on the Host Notify
> capable bus are capable of Host Notify, so I thought it was the
> responsibility of the driver to assign it.
Right, it is device's feature and it is up to the driver to decide if it
should be using interrupt-driven model or not. From I2C core I'd try the
following logic: if client->irq is not already set up and
ACPI/DT/platform data does not have any mention of IERQ and device is on
Host Notify capable bus then assign IRQ and let the dirver decide if it
wants to use it or not. Having client->irq present does not mean that we
have to use it. Driver knows the device best.
>
> However, I can see your point, though I'd need some inputs (and I'll
> have to resend the series as the Intel bot showed me 2 mistakes).
>
> So, if i2c-core gets to assign itself the IRQ for Host Notify, do we:
> 1) assign an IRQ to any SMBus device on a Host Notify capable adapter
> that doesn't have a valid provided IRQ?
> 2) have a new field in struct i2c_board_info that explicitly requires
> Host Notify as an IRQ?
> 3) do not touch anything in i2c_core, let the caller of i2c_new_device
> fill in the irq by a call to
> i2c_smbus_host_notify_to_irq(adapter, address)?
>
> 1) has the advantage of being transparent for everybody, except we will
> provide IRQs to devices that don't have one (this can be ignored), but
> this may also lead to errors when IRQ is not correctly set by the caller
> where it should be, and the driver/developer doesn't understand why it
> is never triggered.
>
> 2) is a nice compromise, but we will need some OF binding, add some work
> in for the callers of i2c_new_device() and will not work nicely with
> sysfs instantiated i2c devices (the new_device sysfs entry). The sysfs
> could be solved by adding some new address namespace, but that doesn't
> make sense I think
>
> 3) requires less changes in i2c_core but it's going to be even harder
> to add a device through sysfs that is Host Notify capable given that we
> can't specify the IRQ.
>
> After thinking a bit, I think I'd lean toward 1), but I am open to other
> options as well.
Yes, I think #1 is what we should do (but ultimately it is up to Wolfram
to decide).
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH v2] i2c: i801: Fix I2C Block Read on 8-Series/C220 and later
From: Jean Delvare @ 2016-10-11 18:20 UTC (permalink / raw)
To: Jarkko Nikula; +Cc: Linux I2C, Mika Westerberg, Wolfram Sang
In-Reply-To: <eb3c0e91-5744-3b49-515e-dd68ed5d88aa@linux.intel.com>
Hi Jarkko,
On Tue, 11 Oct 2016 14:32:55 +0300, Jarkko Nikula wrote:
> On 10/11/2016 02:13 PM, Jean Delvare wrote:
> > Starting with the 8-Series/C220 PCH (Lynx Point), the SMBus
> > controller includes a SPD EEPROM protection mechanism. Once the SPD
> > Write Disable bit is set, only reads are allowed to slave addresses
> > 0x50-0x57.
> >
> > However the legacy implementation of I2C Block Read since the ICH5
> > looks like a write, and is therefore blocked by the SPD protection
> > mechanism. This causes the eeprom and at24 drivers to fail.
> >
> > So assume that I2C Block Read is implemented as an actual read on
> > these chipsets. I tested it on my Q87 chipset and it seems to work
> > just fine.
> >
> > Signed-off-by: Jean Delvare <jdelvare@suse.de>
> > Cc: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> > Cc: Mika Westerberg <mika.westerberg@linux.intel.com>
> > Cc: Wolfram Sang <wsa@the-dreams.de>
> > ---
> > Changes since v1:
> > * Rebased on Linus' latest tree.
> >
> > Jarkko, still no information about this from your Windows or hardware
> > folks?
>
> No update, in fact some of the contacts have left the company :-(
What do we do then? Without this patch, I2C Block Read doesn't work at
all on recent chipsets. I don't think it can be worse with the patch.
And it works fine on the few machines I tested on. So I would like this
patch to go upstream.
My coverage is limited though. Did anyone at Intel test this patch on
other (recent) systems?
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply
* [PATCH 2/5] i2c: i2c-piix4: coding style fix - assignment in if condition
From: Carlos Palminha @ 2016-10-11 17:24 UTC (permalink / raw)
To: linux-i2c, linux-kernel; +Cc: jdelvare, wsa, CARLOS.PALMINHA
In-Reply-To: <20161011172441.7422-1-palminha@synopsys.com>
Signed-off-by: Carlos Palminha <palminha@synopsys.com>
---
drivers/i2c/busses/i2c-piix4.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 6b55a8e..5d6f637 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -419,11 +419,13 @@ static int piix4_transaction(struct i2c_adapter *piix4_adapter)
inb_p(SMBHSTDAT1));
/* Make sure the SMBus host is ready to start transmitting */
- if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+ temp = inb_p(SMBHSTSTS);
+ if (temp != 0x00) {
dev_dbg(&piix4_adapter->dev, "SMBus busy (%02x). "
"Resetting...\n", temp);
outb_p(temp, SMBHSTSTS);
- if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+ temp = inb_p(SMBHSTSTS);
+ if (temp != 0x00) {
dev_err(&piix4_adapter->dev, "Failed! (%02x)\n", temp);
return -EBUSY;
} else {
@@ -470,7 +472,8 @@ static int piix4_transaction(struct i2c_adapter *piix4_adapter)
if (inb_p(SMBHSTSTS) != 0x00)
outb_p(inb(SMBHSTSTS), SMBHSTSTS);
- if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
+ temp = inb_p(SMBHSTSTS);
+ if (temp != 0x00) {
dev_err(&piix4_adapter->dev, "Failed reset at end of "
"transaction (%02x)\n", temp);
}
--
2.9.3
^ permalink raw reply related
* Re: [PATCH 5/5] i2c: i2c-piix4: coding style fix - quoted string split across lines
From: Joe Perches @ 2016-10-12 2:03 UTC (permalink / raw)
To: Carlos Palminha, linux-i2c, linux-kernel; +Cc: jdelvare, wsa
In-Reply-To: <20161011173455.7803-1-palminha@synopsys.com>
On Tue, 2016-10-11 at 18:34 +0100, Carlos Palminha wrote:
[]
> diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
[]
> @@ -260,7 +260,8 @@ static int piix4_setup(struct pci_dev *PIIX4_dev,
> else if ((temp & 0x0E) == 0)
> dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n");
> else
> - dev_err(&PIIX4_dev->dev, "Illegal Interrupt configuration "
> + dev_err(&PIIX4_dev->dev,
> + "Illegal Interrupt configuration "
> "(or code out of date)!\n");
typically, this format would be coalesced into a single line.
^ permalink raw reply
* Re: [GIT PULL] i2c: 'i2c-bus' node support for v4.8-rc1
From: Wolfram Sang @ 2016-10-12 7:00 UTC (permalink / raw)
To: Jon Hunter; +Cc: Thierry Reding, linux-i2c, linux-tegra
In-Reply-To: <5e1eb22f-f1a8-71ec-b66b-422277e55ce0@nvidia.com>
[-- Attachment #1: Type: text/plain, Size: 324 bytes --]
> I was just checking the latest mainline and -next and I did not see the
> changes for the 'i2c-bus' node. Let me know if I have missed an email
> somewhere or if these were dropped for some reason.
Ooops, seems I pulled but forgot to merge :( Thanks for the pointer,
done now. Will use the current merge window for it.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCH] gpio: pca953x: add a comment explaining the need for a lockdep subclass
From: Wolfram Sang @ 2016-10-12 7:00 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Linus Walleij, Alexandre Courbot, Andy Shevchenko, Vignesh R,
Yong Li, Geert Uytterhoeven, Peter Zijlstra, Ingo Molnar,
Peter Rosin, linux-i2c, linux-gpio, LKML
In-Reply-To: <1474883655-15824-2-git-send-email-bgolaszewski@baylibre.com>
[-- Attachment #1: Type: text/plain, Size: 477 bytes --]
On Mon, Sep 26, 2016 at 11:54:15AM +0200, Bartosz Golaszewski wrote:
> This is a follow-up to commit 559b46990e76 ("gpio: pca953x: fix an
> incorrect lockdep warning"). The reason for calling
> lockdep_set_subclass() in pca953x_probe() is not explained in
> the code.
>
> Add a comment describing the problem, partial solution and required
> future extensions.
>
> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Applied to for-current, thanks!
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply
* Re: [PATCHv4] hwmon: Add tc654 driver
From: Guenter Roeck @ 2016-10-12 13:03 UTC (permalink / raw)
To: Chris Packham
Cc: linux-hwmon, iwamoto, Joshua.Scott, Kevin Tsai, Wolfram Sang,
Rob Herring, Mark Rutland, Jean Delvare, Jonathan Corbet,
linux-i2c, devicetree, linux-kernel, linux-doc
In-Reply-To: <20161010212633.5436-1-chris.packham@alliedtelesis.co.nz>
On Tue, Oct 11, 2016 at 10:26:31AM +1300, Chris Packham wrote:
> Add support for the tc654 and tc655 fan controllers from Microchip.
>
> http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf
>
> Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
> Acked-by: Rob Herring <robh@kernel.org>
Applied to -next (after fixing continuation line alignments).
Guenter
> ---
> Changes in v4:
> - tab-align values in #defines
> - ensure locking in set_pwm covers updating cached values
> - populate the cached value for the config register in tc654_probe()
>
> Changes in v3:
> - typofix in documentation
> - add missing value to tc654_pwm_map, re-generate based on datasheet.
> - remove unnecessary hwmon_dev member from struct tc654_data
> - bug fixes in set_fan_min() and show_pwm_mode()
> - miscellaneous style fixes
>
> Changes in v2:
> - Add Documentation/hwmon/tc654
> - Incorporate most of the review comments from Guenter. Additional error
> handling is added. Unused/unnecessary code is removed. I decided not
> to go down the regmap path yet. I may circle back to it when I look at
> using regmap in the adm9240 driver.
>
> .../devicetree/bindings/i2c/trivial-devices.txt | 2 +
> Documentation/hwmon/tc654 | 31 ++
> drivers/hwmon/Kconfig | 11 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/tc654.c | 514 +++++++++++++++++++++
> 5 files changed, 559 insertions(+)
> create mode 100644 Documentation/hwmon/tc654
> create mode 100644 drivers/hwmon/tc654.c
>
> diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> index 1416c6a0d2cd..833fb9f133d3 100644
> --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
> @@ -122,6 +122,8 @@ microchip,mcp4662-502 Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem
> microchip,mcp4662-103 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (10k)
> microchip,mcp4662-503 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (50k)
> microchip,mcp4662-104 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k)
> +microchip,tc654 PWM Fan Speed Controller With Fan Fault Detection
> +microchip,tc655 PWM Fan Speed Controller With Fan Fault Detection
> national,lm63 Temperature sensor with integrated fan control
> national,lm75 I2C TEMP SENSOR
> national,lm80 Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
> diff --git a/Documentation/hwmon/tc654 b/Documentation/hwmon/tc654
> new file mode 100644
> index 000000000000..91a2843f5f98
> --- /dev/null
> +++ b/Documentation/hwmon/tc654
> @@ -0,0 +1,31 @@
> +Kernel driver tc654
> +===================
> +
> +Supported chips:
> + * Microship TC654 and TC655
> + Prefix: 'tc654'
> + Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf
> +
> +Authors:
> + Chris Packham <chris.packham@alliedtelesis.co.nz>
> + Masahiko Iwamoto <iwamoto@allied-telesis.co.jp>
> +
> +Description
> +-----------
> +This driver implements support for the Microchip TC654 and TC655.
> +
> +The TC654 uses the 2-wire interface compatible with the SMBUS 2.0
> +specification. The TC654 has two (2) inputs for measuring fan RPM and
> +one (1) PWM output which can be used for fan control.
> +
> +Configuration Notes
> +-------------------
> +Ordinarily the pwm1_mode ABI is used for controlling the pwm output
> +mode. However, for this chip the output is always pwm, and the
> +pwm1_mode determines if the pwm output is controlled via the pwm1 value
> +or via the Vin analog input.
> +
> +
> +Setting pwm1_mode to 1 will cause the pwm output to be driven based on
> +the pwm1 value. Setting pwm1_mode to 0 will cause the pwm output to be
> +driven based on the Vin input.
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 45cef3d2c75c..8681bc65cde5 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -907,6 +907,17 @@ config SENSORS_MCP3021
> This driver can also be built as a module. If so, the module
> will be called mcp3021.
>
> +config SENSORS_TC654
> + tristate "Microchip TC654/TC655 and compatibles"
> + depends on I2C
> + help
> + If you say yes here you get support for TC654 and TC655.
> + The TC654 and TC655 are PWM mode fan speed controllers with
> + FanSense technology for use with brushless DC fans.
> +
> + This driver can also be built as a module. If so, the module
> + will be called tc654.
> +
> config SENSORS_MENF21BMC_HWMON
> tristate "MEN 14F021P00 BMC Hardware Monitoring"
> depends on MFD_MENF21BMC
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index aecf4ba17460..c651f0f1d047 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -122,6 +122,7 @@ obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
> obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
> obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
> obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
> +obj-$(CONFIG_SENSORS_TC654) += tc654.o
> obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
> obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
> obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
> diff --git a/drivers/hwmon/tc654.c b/drivers/hwmon/tc654.c
> new file mode 100644
> index 000000000000..04485f6d6983
> --- /dev/null
> +++ b/drivers/hwmon/tc654.c
> @@ -0,0 +1,514 @@
> +/*
> + * tc654.c - Linux kernel modules for fan speed controller
> + *
> + * Copyright (C) 2016 Allied Telesis Labs NZ
> + *
> + * 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 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 <linux/bitops.h>
> +#include <linux/err.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/i2c.h>
> +#include <linux/init.h>
> +#include <linux/jiffies.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +#include <linux/util_macros.h>
> +
> +enum tc654_regs {
> + TC654_REG_RPM1 = 0x00, /* RPM Output 1 */
> + TC654_REG_RPM2 = 0x01, /* RPM Output 2 */
> + TC654_REG_FAN_FAULT1 = 0x02, /* Fan Fault 1 Threshold */
> + TC654_REG_FAN_FAULT2 = 0x03, /* Fan Fault 2 Threshold */
> + TC654_REG_CONFIG = 0x04, /* Configuration */
> + TC654_REG_STATUS = 0x05, /* Status */
> + TC654_REG_DUTY_CYCLE = 0x06, /* Fan Speed Duty Cycle */
> + TC654_REG_MFR_ID = 0x07, /* Manufacturer Identification */
> + TC654_REG_VER_ID = 0x08, /* Version Identification */
> +};
> +
> +/* Macros to easily index the registers */
> +#define TC654_REG_RPM(idx) (TC654_REG_RPM1 + (idx))
> +#define TC654_REG_FAN_FAULT(idx) (TC654_REG_FAN_FAULT1 + (idx))
> +
> +/* Config register bits */
> +#define TC654_REG_CONFIG_RES BIT(6) /* Resolution Selection */
> +#define TC654_REG_CONFIG_DUTYC BIT(5) /* Duty Cycle Control */
> +#define TC654_REG_CONFIG_SDM BIT(0) /* Shutdown Mode */
> +
> +/* Status register bits */
> +#define TC654_REG_STATUS_F2F BIT(1) /* Fan 2 Fault */
> +#define TC654_REG_STATUS_F1F BIT(0) /* Fan 1 Fault */
> +
> +/* RPM resolution for RPM Output registers */
> +#define TC654_HIGH_RPM_RESOLUTION 25 /* 25 RPM resolution */
> +#define TC654_LOW_RPM_RESOLUTION 50 /* 50 RPM resolution */
> +
> +/* Convert to the fan fault RPM threshold from register value */
> +#define TC654_FAN_FAULT_FROM_REG(val) ((val) * 50) /* 50 RPM resolution */
> +
> +/* Convert to register value from the fan fault RPM threshold */
> +#define TC654_FAN_FAULT_TO_REG(val) (((val) / 50) & 0xff)
> +
> +/* Register data is read (and cached) at most once per second. */
> +#define TC654_UPDATE_INTERVAL HZ
> +
> +struct tc654_data {
> + struct i2c_client *client;
> +
> + /* update mutex */
> + struct mutex update_lock;
> +
> + /* tc654 register cache */
> + bool valid;
> + unsigned long last_updated; /* in jiffies */
> +
> + u8 rpm_output[2]; /* The fan RPM data for fans 1 and 2 is then
> + * written to registers RPM1 and RPM2
> + */
> + u8 fan_fault[2]; /* The Fan Fault Threshold Registers are used to
> + * set the fan fault threshold levels for fan 1
> + * and fan 2
> + */
> + u8 config; /* The Configuration Register is an 8-bit read/
> + * writable multi-function control register
> + * 7: Fan Fault Clear
> + * 1 = Clear Fan Fault
> + * 0 = Normal Operation (default)
> + * 6: Resolution Selection for RPM Output Registers
> + * RPM Output Registers (RPM1 and RPM2) will be
> + * set for
> + * 1 = 25 RPM (9-bit) resolution
> + * 0 = 50 RPM (8-bit) resolution (default)
> + * 5: Duty Cycle Control Method
> + * The V OUT duty cycle will be controlled via
> + * 1 = the SMBus interface.
> + * 0 = via the V IN analog input pin. (default)
> + * 4,3: Fan 2 Pulses Per Rotation
> + * 00 = 1
> + * 01 = 2 (default)
> + * 10 = 4
> + * 11 = 8
> + * 2,1: Fan 1 Pulses Per Rotation
> + * 00 = 1
> + * 01 = 2 (default)
> + * 10 = 4
> + * 11 = 8
> + * 0: Shutdown Mode
> + * 1 = Shutdown mode.
> + * 0 = Normal operation. (default)
> + */
> + u8 status; /* The Status register provides all the information
> + * about what is going on within the TC654/TC655
> + * devices.
> + * 7,6: Unimplemented, Read as '0'
> + * 5: Over-Temperature Fault Condition
> + * 1 = Over-Temperature condition has occurred
> + * 0 = Normal operation. V IN is less than 2.6V
> + * 4: RPM2 Counter Overflow
> + * 1 = Fault condition
> + * 0 = Normal operation
> + * 3: RPM1 Counter Overflow
> + * 1 = Fault condition
> + * 0 = Normal operation
> + * 2: V IN Input Status
> + * 1 = V IN is open
> + * 0 = Normal operation. voltage present at V IN
> + * 1: Fan 2 Fault
> + * 1 = Fault condition
> + * 0 = Normal operation
> + * 0: Fan 1 Fault
> + * 1 = Fault condition
> + * 0 = Normal operation
> + */
> + u8 duty_cycle; /* The DUTY_CYCLE register is a 4-bit read/
> + * writable register used to control the duty
> + * cycle of the V OUT output.
> + */
> +};
> +
> +/* helper to grab and cache data, at most one time per second */
> +static struct tc654_data *tc654_update_client(struct device *dev)
> +{
> + struct tc654_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + int ret = 0;
> +
> + mutex_lock(&data->update_lock);
> + if (time_before(jiffies, data->last_updated + TC654_UPDATE_INTERVAL) &&
> + likely(data->valid))
> + goto out;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(0));
> + if (ret < 0)
> + goto out;
> + data->rpm_output[0] = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_RPM(1));
> + if (ret < 0)
> + goto out;
> + data->rpm_output[1] = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(0));
> + if (ret < 0)
> + goto out;
> + data->fan_fault[0] = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_FAN_FAULT(1));
> + if (ret < 0)
> + goto out;
> + data->fan_fault[1] = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG);
> + if (ret < 0)
> + goto out;
> + data->config = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_STATUS);
> + if (ret < 0)
> + goto out;
> + data->status = ret;
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_DUTY_CYCLE);
> + if (ret < 0)
> + goto out;
> + data->duty_cycle = ret & 0x0f;
> +
> + data->last_updated = jiffies;
> + data->valid = true;
> +out:
> + mutex_unlock(&data->update_lock);
> +
> + if (ret < 0) /* upon error, encode it in return value */
> + data = ERR_PTR(ret);
> +
> + return data;
> +}
> +
> +/*
> + * sysfs attributes
> + */
> +
> +static ssize_t show_fan(struct device *dev, struct device_attribute *da,
> + char *buf)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = tc654_update_client(dev);
> + int val;
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + if (data->config & TC654_REG_CONFIG_RES)
> + val = data->rpm_output[nr] * TC654_HIGH_RPM_RESOLUTION;
> + else
> + val = data->rpm_output[nr] * TC654_LOW_RPM_RESOLUTION;
> +
> + return sprintf(buf, "%d\n", val);
> +}
> +
> +static ssize_t show_fan_min(struct device *dev, struct device_attribute *da,
> + char *buf)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = tc654_update_client(dev);
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + return sprintf(buf, "%d\n",
> + TC654_FAN_FAULT_FROM_REG(data->fan_fault[nr]));
> +}
> +
> +static ssize_t set_fan_min(struct device *dev, struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + unsigned long val;
> + int ret;
> +
> + if (kstrtoul(buf, 10, &val))
> + return -EINVAL;
> +
> + val = clamp_val(val, 0, 12750);
> +
> + mutex_lock(&data->update_lock);
> +
> + data->fan_fault[nr] = TC654_FAN_FAULT_TO_REG(val);
> + ret = i2c_smbus_write_byte_data(client, TC654_REG_FAN_FAULT(nr),
> + data->fan_fault[nr]);
> +
> + mutex_unlock(&data->update_lock);
> + return ret < 0 ? ret : count;
> +}
> +
> +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *da,
> + char *buf)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = tc654_update_client(dev);
> + int val;
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + if (nr == 0)
> + val = !!(data->status & TC654_REG_STATUS_F1F);
> + else
> + val = !!(data->status & TC654_REG_STATUS_F2F);
> +
> + return sprintf(buf, "%d\n", val);
> +}
> +
> +static const u8 TC654_FAN_PULSE_SHIFT[] = { 1, 3 };
> +
> +static ssize_t show_fan_pulses(struct device *dev, struct device_attribute *da,
> + char *buf)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = tc654_update_client(dev);
> + u8 val;
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + val = BIT((data->config >> TC654_FAN_PULSE_SHIFT[nr]) & 0x03);
> + return sprintf(buf, "%d\n", val);
> +}
> +
> +static ssize_t set_fan_pulses(struct device *dev, struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + int nr = to_sensor_dev_attr(da)->index;
> + struct tc654_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + u8 config;
> + unsigned long val;
> + int ret;
> +
> + if (kstrtoul(buf, 10, &val))
> + return -EINVAL;
> +
> + switch (val) {
> + case 1:
> + config = 0;
> + break;
> + case 2:
> + config = 1;
> + break;
> + case 4:
> + config = 2;
> + break;
> + case 8:
> + config = 3;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + mutex_lock(&data->update_lock);
> +
> + data->config &= ~(0x03 << TC654_FAN_PULSE_SHIFT[nr]);
> + data->config |= (config << TC654_FAN_PULSE_SHIFT[nr]);
> + ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
> +
> + mutex_unlock(&data->update_lock);
> + return ret < 0 ? ret : count;
> +}
> +
> +static ssize_t show_pwm_mode(struct device *dev,
> + struct device_attribute *da, char *buf)
> +{
> + struct tc654_data *data = tc654_update_client(dev);
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + return sprintf(buf, "%d\n", !!(data->config & TC654_REG_CONFIG_DUTYC));
> +}
> +
> +static ssize_t set_pwm_mode(struct device *dev,
> + struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + struct tc654_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + unsigned long val;
> + int ret;
> +
> + if (kstrtoul(buf, 10, &val))
> + return -EINVAL;
> +
> + if (val != 0 && val != 1)
> + return -EINVAL;
> +
> + mutex_lock(&data->update_lock);
> +
> + if (val)
> + data->config |= TC654_REG_CONFIG_DUTYC;
> + else
> + data->config &= ~TC654_REG_CONFIG_DUTYC;
> +
> + ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
> +
> + mutex_unlock(&data->update_lock);
> + return ret < 0 ? ret : count;
> +}
> +
> +static const int tc654_pwm_map[16] = { 77, 88, 102, 112, 124, 136, 148, 160,
> + 172, 184, 196, 207, 219, 231, 243, 255};
> +
> +static ssize_t show_pwm(struct device *dev, struct device_attribute *da,
> + char *buf)
> +{
> + struct tc654_data *data = tc654_update_client(dev);
> + int pwm;
> +
> + if (IS_ERR(data))
> + return PTR_ERR(data);
> +
> + if (data->config & TC654_REG_CONFIG_SDM)
> + pwm = 0;
> + else
> + pwm = tc654_pwm_map[data->duty_cycle];
> +
> + return sprintf(buf, "%d\n", pwm);
> +}
> +
> +static ssize_t set_pwm(struct device *dev, struct device_attribute *da,
> + const char *buf, size_t count)
> +{
> + struct tc654_data *data = dev_get_drvdata(dev);
> + struct i2c_client *client = data->client;
> + unsigned long val;
> + int ret;
> +
> + if (kstrtoul(buf, 10, &val))
> + return -EINVAL;
> + if (val > 255)
> + return -EINVAL;
> +
> + mutex_lock(&data->update_lock);
> +
> + if (val == 0)
> + data->config |= TC654_REG_CONFIG_SDM;
> + else
> + data->config &= ~TC654_REG_CONFIG_SDM;
> +
> + data->duty_cycle = find_closest(val, tc654_pwm_map,
> + ARRAY_SIZE(tc654_pwm_map));
> +
> + ret = i2c_smbus_write_byte_data(client, TC654_REG_CONFIG, data->config);
> + if (ret < 0)
> + goto out;
> +
> + ret = i2c_smbus_write_byte_data(client, TC654_REG_DUTY_CYCLE,
> + data->duty_cycle);
> +
> +out:
> + mutex_unlock(&data->update_lock);
> + return ret < 0 ? ret : count;
> +}
> +
> +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
> +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
> +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min,
> + set_fan_min, 0);
> +static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min,
> + set_fan_min, 1);
> +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0);
> +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1);
> +static SENSOR_DEVICE_ATTR(fan1_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
> + set_fan_pulses, 0);
> +static SENSOR_DEVICE_ATTR(fan2_pulses, S_IWUSR | S_IRUGO, show_fan_pulses,
> + set_fan_pulses, 1);
> +static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
> + show_pwm_mode, set_pwm_mode, 0);
> +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm,
> + set_pwm, 0);
> +
> +/* Driver data */
> +static struct attribute *tc654_attrs[] = {
> + &sensor_dev_attr_fan1_input.dev_attr.attr,
> + &sensor_dev_attr_fan2_input.dev_attr.attr,
> + &sensor_dev_attr_fan1_min.dev_attr.attr,
> + &sensor_dev_attr_fan2_min.dev_attr.attr,
> + &sensor_dev_attr_fan1_alarm.dev_attr.attr,
> + &sensor_dev_attr_fan2_alarm.dev_attr.attr,
> + &sensor_dev_attr_fan1_pulses.dev_attr.attr,
> + &sensor_dev_attr_fan2_pulses.dev_attr.attr,
> + &sensor_dev_attr_pwm1_mode.dev_attr.attr,
> + &sensor_dev_attr_pwm1.dev_attr.attr,
> + NULL
> +};
> +
> +ATTRIBUTE_GROUPS(tc654);
> +
> +/*
> + * device probe and removal
> + */
> +
> +static int tc654_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct device *dev = &client->dev;
> + struct tc654_data *data;
> + struct device *hwmon_dev;
> + int ret;
> +
> + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
> + return -ENODEV;
> +
> + data = devm_kzalloc(dev, sizeof(struct tc654_data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->client = client;
> + mutex_init(&data->update_lock);
> +
> + ret = i2c_smbus_read_byte_data(client, TC654_REG_CONFIG);
> + if (ret < 0)
> + return ret;
> +
> + data->config = ret;
> +
> + hwmon_dev =
> + devm_hwmon_device_register_with_groups(dev, client->name, data,
> + tc654_groups);
> + return PTR_ERR_OR_ZERO(hwmon_dev);
> +}
> +
> +static const struct i2c_device_id tc654_id[] = {
> + {"tc654", 0},
> + {"tc655", 0},
> + {}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, tc654_id);
> +
> +static struct i2c_driver tc654_driver = {
> + .driver = {
> + .name = "tc654",
> + },
> + .probe = tc654_probe,
> + .id_table = tc654_id,
> +};
> +
> +module_i2c_driver(tc654_driver);
> +
> +MODULE_AUTHOR("Allied Telesis Labs");
> +MODULE_DESCRIPTION("Microchip TC654/TC655 driver");
> +MODULE_LICENSE("GPL");
^ permalink raw reply
* Re: [PATCH v3 2/2] i2c: aspeed: added documentation for Aspeed I2C driver
From: Brendan Higgins @ 2016-10-12 20:01 UTC (permalink / raw)
To: Rob Herring
Cc: Wolfram Sang, mark.rutland, linux-i2c, devicetree, linux-kernel,
OpenBMC Maillist, Joel Stanley, Jeremy Kerr
In-Reply-To: <20160923180219.GA9401@rob-hp-laptop>
I never got an acknowledgement to this email:
http://www.spinics.net/lists/devicetree/msg143944.html
The "bus" number is something that is defined as part of the hardware
specification for this device.
It seems we will not be able to do away with the "bus" number parameter.
Joel suggested going back to the way I was originally doing it:
http://www.spinics.net/lists/devicetree/msg144134.html
Cheers
^ permalink raw reply
* Re: [PATCHv4] hwmon: Add tc654 driver
From: Chris Packham @ 2016-10-12 20:17 UTC (permalink / raw)
To: Guenter Roeck
Cc: linux-hwmon-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Masahiko Iwamoto, Joshua Scott, Kevin Tsai, Wolfram Sang,
Rob Herring, Mark Rutland, Jean Delvare, Jonathan Corbet,
linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-doc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <20161012130301.GA21060@roeck-us.net>
On 10/13/2016 02:03 AM, Guenter Roeck wrote:
> On Tue, Oct 11, 2016 at 10:26:31AM +1300, Chris Packham wrote:
>> > Add support for the tc654 and tc655 fan controllers from Microchip.
>> >
>> > http://ww1.microchip.com/downloads/en/DeviceDoc/20001734C.pdf
>> >
>> > Signed-off-by: Chris Packham <chris.packham-6g8wRflRTwXFdCa3tKVlE6U/zSkkHjvu@public.gmane.org>
>> > Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Applied to -next (after fixing continuation line alignments).
>
Sorry about those. I'll try to do better next time.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 5/5] i2c: i2c-piix4: coding style fix - quoted string split across lines
From: Jean Delvare @ 2016-10-13 11:34 UTC (permalink / raw)
To: Joe Perches; +Cc: Carlos Palminha, linux-i2c, linux-kernel, wsa
In-Reply-To: <1476237836.2116.8.camel@perches.com>
On Tue, 11 Oct 2016 19:03:56 -0700, Joe Perches wrote:
> On Tue, 2016-10-11 at 18:34 +0100, Carlos Palminha wrote:
> []
> > diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
> []
> > @@ -260,7 +260,8 @@ static int piix4_setup(struct pci_dev *PIIX4_dev,
> > else if ((temp & 0x0E) == 0)
> > dev_dbg(&PIIX4_dev->dev, "Using SMI# for SMBus\n");
> > else
> > - dev_err(&PIIX4_dev->dev, "Illegal Interrupt configuration "
> > + dev_err(&PIIX4_dev->dev,
> > + "Illegal Interrupt configuration "
> > "(or code out of date)!\n");
>
> typically, this format would be coalesced into a single line.
Especially when the patch claims to "fix quoted string split across
lines" :-D
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox