From patchwork Fri Jun 8 21:11:12 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 927054 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-i2c-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 412ZpM4KWTz9s0W for ; Sat, 9 Jun 2018 07:11:23 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753160AbeFHVLV (ORCPT ); Fri, 8 Jun 2018 17:11:21 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:47750 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753121AbeFHVLU (ORCPT ); Fri, 8 Jun 2018 17:11:20 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 302B54022931; Fri, 8 Jun 2018 21:11:20 +0000 (UTC) Received: from dhcp-44-202.space.revspace.nl (ovpn-116-56.ams2.redhat.com [10.36.116.56]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8E6AB202322E; Fri, 8 Jun 2018 21:11:18 +0000 (UTC) From: Hans de Goede To: Wolfram Sang , Andy Shevchenko , Mika Westerberg Cc: Hans de Goede , linux-acpi@vger.kernel.org, linux-i2c@vger.kernel.org Subject: [PATCH 1/2] i2c: Add new I2C_CLIENT_IGNORE_BUSY flag Date: Fri, 8 Jun 2018 23:11:12 +0200 Message-Id: <20180608211113.10238-1-hdegoede@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Fri, 08 Jun 2018 21:11:20 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.6]); Fri, 08 Jun 2018 21:11:20 +0000 (UTC) for IP:'10.11.54.4' DOMAIN:'int-mx04.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'hdegoede@redhat.com' RCPT:'' Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org Normally i2c_new_device will check that a client at the same address does not already exists. On systems with ACPI instantiated i2c-clients, normally there is 1 fw_node per i2c-device and that fw-node contains 1 I2cSerialBus resource for that 1 i2c-device. But in some rare cases the manufacturer has decided to describe multiple i2c-devices in a single ACPI fwnode with multiple I2cSerialBus resources. After an earlier attempt to fix this in the i2c-core which resulted in a lot of extra code to support this corner-case it was decided to go for a different approach. The next patch in this series introduces a new special i2c-multi-instantiate driver which can be build as a module and which will bind to the i2c-client which gets instantiated by the core for the first I2cSerialBus resource in the fwnode. This driver will instantiate a new i2c-client per I2cSerialBus resource, using the driver_data from the acpi_device_id it is binding to to tell it which chip-type (and optional irq-resource) to use when instantiating. Since it binds to the core instantiated i2c-client and claims the acpi_device_id of the fwnode, it also needs to instantiate a new i2c-client for the first I2cSerialBus resource uses the chip-id for the real driver, so that that can bind to the i2c-device described by the first I2cSerialBus resource, this requires the new I2C_CLIENT_IGNORE_BUSY flag. Note this means that we will be instantiating 2 i2c_client-s for the first I2cSerialBus resource, this is not pretty, but since the multi-instantiate driver does exactly 0 i2c-transfers this is not a problem. Signed-off-by: Hans de Goede --- drivers/i2c/i2c-core-base.c | 11 +++++++---- include/linux/i2c.h | 3 +++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index a17f46a95f73..77d70b611bc5 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -721,7 +721,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) if (info->archdata) client->dev.archdata = *info->archdata; - client->flags = info->flags; + client->flags = info->flags & ~I2C_CLIENT_IGNORE_BUSY; client->addr = info->addr; client->irq = info->irq; @@ -739,9 +739,12 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) } /* Check for address business */ - status = i2c_check_addr_busy(adap, i2c_encode_flags_to_addr(client)); - if (status) - goto out_err; + if (!(info->flags & I2C_CLIENT_IGNORE_BUSY)) { + status = i2c_check_addr_busy(adap, + i2c_encode_flags_to_addr(client)); + if (status) + goto out_err; + } client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 44ad14e016b5..3ad73cf1b187 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -775,6 +775,9 @@ i2c_unlock_adapter(struct i2c_adapter *adapter) #define I2C_CLIENT_SLAVE 0x20 /* we are the slave */ #define I2C_CLIENT_HOST_NOTIFY 0x40 /* We want to use I2C host notify */ #define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */ +#define I2C_CLIENT_IGNORE_BUSY 0x100 /* For board_info; skip busy check */ + /* Do NOT use, reserved for use by */ + /* drv/i2c/i2c-multi-instantiate.c */ #define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */ /* Must match I2C_M_STOP|IGNORE_NAK */ From patchwork Fri Jun 8 21:11:13 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans de Goede X-Patchwork-Id: 927055 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-i2c-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 412ZpN6Q15z9s0W for ; Sat, 9 Jun 2018 07:11:24 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753164AbeFHVLY (ORCPT ); Fri, 8 Jun 2018 17:11:24 -0400 Received: from mx3-rdu2.redhat.com ([66.187.233.73]:60818 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1753121AbeFHVLW (ORCPT ); Fri, 8 Jun 2018 17:11:22 -0400 Received: from smtp.corp.redhat.com (int-mx04.intmail.prod.int.rdu2.redhat.com [10.11.54.4]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3F3A940BC046; Fri, 8 Jun 2018 21:11:22 +0000 (UTC) Received: from dhcp-44-202.space.revspace.nl (ovpn-116-56.ams2.redhat.com [10.36.116.56]) by smtp.corp.redhat.com (Postfix) with ESMTP id 8CDEA202322E; Fri, 8 Jun 2018 21:11:20 +0000 (UTC) From: Hans de Goede To: Wolfram Sang , Andy Shevchenko , Mika Westerberg Cc: Hans de Goede , linux-acpi@vger.kernel.org, linux-i2c@vger.kernel.org Subject: [PATCH 2/2] i2c: Add multi-instantiate pseudo driver Date: Fri, 8 Jun 2018 23:11:13 +0200 Message-Id: <20180608211113.10238-2-hdegoede@redhat.com> In-Reply-To: <20180608211113.10238-1-hdegoede@redhat.com> References: <20180608211113.10238-1-hdegoede@redhat.com> X-Scanned-By: MIMEDefang 2.78 on 10.11.54.4 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Fri, 08 Jun 2018 21:11:22 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.11.55.7]); Fri, 08 Jun 2018 21:11:22 +0000 (UTC) for IP:'10.11.54.4' DOMAIN:'int-mx04.intmail.prod.int.rdu2.redhat.com' HELO:'smtp.corp.redhat.com' FROM:'hdegoede@redhat.com' RCPT:'' Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org On systems with ACPI instantiated i2c-clients, normally there is 1 fw_node per i2c-device and that fw-node contains 1 I2cSerialBus resource for that 1 i2c-device. But in some rare cases the manufacturer has decided to describe multiple i2c-devices in a single ACPI fwnode with multiple I2cSerialBus resources. An earlier attempt to fix this in the i2c-core resulted in a lot of extra code to support this corner-case. This commitintroduces a new i2c-multi-instantiate driver which fixes this in a different way. This new driver can be built as a module which will only loaded on affected systems. This driver will instantiate a new i2c-client per I2cSerialBus resource, using the driver_data from the acpi_device_id it is binding to to tell it which chip-type (and optional irq-resource) to use when instantiating. Note this means that we will be instantiating 2 i2c_client-s for the first I2cSerialBus resource, this is not pretty, but since the multi-instantiate driver does exactly 0 i2c-transfers this is not a problem. Signed-off-by: Hans de Goede --- drivers/i2c/Kconfig | 8 ++ drivers/i2c/Makefile | 1 + drivers/i2c/i2c-multi-instantiate.c | 125 ++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 drivers/i2c/i2c-multi-instantiate.c diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index efc3354d60ae..12ca950e7549 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -120,6 +120,14 @@ config I2C_SLAVE_EEPROM endif +config I2C_MULTI_INSTANTIATE + tristate "I2C multi instantiate pseudo device driver" + depends on ACPI + help + Some ACPI bases systems list multiple i2c-devices in a single ACPI + firmware-node. This driver will instantiate separate i2c-clients + for each device in the firmware-node. + config I2C_DEBUG_CORE bool "I2C Core debugging messages" help diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 72c94c60fdd1..d9c5bae1d64f 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_I2C_MUX) += i2c-mux.o obj-y += algos/ busses/ muxes/ obj-$(CONFIG_I2C_STUB) += i2c-stub.o obj-$(CONFIG_I2C_SLAVE_EEPROM) += i2c-slave-eeprom.o +obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG CFLAGS_i2c-core-base.o := -Wno-deprecated-declarations diff --git a/drivers/i2c/i2c-multi-instantiate.c b/drivers/i2c/i2c-multi-instantiate.c new file mode 100644 index 000000000000..480b7a694eae --- /dev/null +++ b/drivers/i2c/i2c-multi-instantiate.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * I2C multi-instantiate driver, pseudo driver to instantiate multiple + * i2c-clients from a single fwnode. + * + * Copyright 2018 Hans de Goede + */ + +#include +#include +#include +#include +#include + +struct i2c_inst_data { + const char *type; + int irq_idx; +}; + +struct i2c_multi_inst_data { + int no_clients; + struct i2c_client *clients[0]; +}; + +static int i2c_multi_inst_probe(struct i2c_client *client) +{ + struct i2c_multi_inst_data *multi; + const struct i2c_inst_data *inst_data; + struct i2c_board_info board_info = {}; + struct device *dev = &client->dev; + struct acpi_device *adev; + char name[32]; + int i, ret; + + inst_data = acpi_device_get_match_data(dev); + if (!inst_data) { + dev_err(dev, "Error ACPI match data is missing\n"); + return -ENODEV; + } + + adev = ACPI_COMPANION(dev); + + /* Count number of clients to instantiate */ + for (i = 0; inst_data[i].type; i++) {} + + multi = devm_kmalloc(dev, + offsetof(struct i2c_multi_inst_data, clients[i]), + GFP_KERNEL); + if (!multi) + return -ENOMEM; + + multi->no_clients = i; + + for (i = 0; i < multi->no_clients; i++) { + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, inst_data[i].type, I2C_NAME_SIZE); + board_info.flags = (i == 0) ? I2C_CLIENT_IGNORE_BUSY : 0; + snprintf(name, sizeof(name), "%s-%s", client->name, + inst_data[i].type); + board_info.dev_name = name; + board_info.irq = 0; + if (inst_data[i].irq_idx != -1) { + ret = acpi_dev_gpio_irq_get(adev, inst_data[i].irq_idx); + if (ret < 0) { + dev_err(dev, "Error requesting irq at index %d: %d\n", + inst_data[i].irq_idx, ret); + goto error; + } + board_info.irq = ret; + } + multi->clients[i] = i2c_acpi_new_device(dev, i, &board_info); + if (!multi->clients[i]) { + dev_err(dev, "Error creating i2c-client, idx %d\n", i); + ret = -ENODEV; + goto error; + } + } + + return 0; + +error: + while (--i >= 0) + i2c_unregister_device(multi->clients[i]); + + return ret; +} + +static int i2c_multi_inst_remove(struct i2c_client *i2c) +{ + struct i2c_multi_inst_data *multi = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < multi->no_clients; i++) + i2c_unregister_device(multi->clients[i]); + + return 0; +} + +static const struct i2c_inst_data bsg1160_data[] = { + { "bmc150_accel", 0 }, + { "bmc150_magn", -1 }, + { "bmg160", -1 }, + {} +}; + +static const struct acpi_device_id i2c_multi_inst_acpi_ids[] = { + { "BSG1160", (unsigned long)bsg1160_data }, + { } +}; +MODULE_DEVICE_TABLE(acpi, i2c_multi_inst_acpi_ids); + +static struct i2c_driver i2c_multi_inst_driver = { + .driver = { + .name = "I2C multi instantiate pseudo device driver", + .acpi_match_table = ACPI_PTR(i2c_multi_inst_acpi_ids), + }, + .probe_new = i2c_multi_inst_probe, + .remove = i2c_multi_inst_remove, +}; + +module_i2c_driver(i2c_multi_inst_driver); + +MODULE_DESCRIPTION("I2C multi instantiate pseudo device driver"); +MODULE_AUTHOR("Hans de Goede "); +MODULE_LICENSE("GPL");