Commit b33752c3 authored by Douglas Anderson's avatar Douglas Anderson Committed by Benjamin Tissoires
Browse files

HID: i2c-hid: Reorganize so ACPI and OF are separate modules



This patch rejiggers the i2c-hid code so that the OF (Open Firmware
aka Device Tree) and ACPI support is separated out a bit.  The OF and
ACPI drivers are now separate modules that wrap the core module.

Essentially, what we're doing here:
* Make "power up" and "power down" a function that can be (optionally)
  implemented by a given user of the i2c-hid core.
* The OF and ACPI modules are drivers on their own, so they implement
  probe / remove / suspend / resume / shutdown.  The core code
  provides implementations that OF and ACPI can call into.

We'll organize this so that we now have 3 modules: the old i2c-hid
module becomes the "core" module and two new modules will depend on
it, handling probing the specific device.

As part of this work, we'll remove the i2c-hid "platform data"
concept since it's not needed.

Signed-off-by: default avatarDouglas Anderson <dianders@chromium.org>
Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarBenjamin Tissoires <benjamin.tissoires@redhat.com>
parent 2bbe17ae
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -138,7 +138,7 @@ obj-$(CONFIG_USB_HID) += usbhid/
obj-$(CONFIG_USB_MOUSE)		+= usbhid/
obj-$(CONFIG_USB_KBD)		+= usbhid/

obj-$(CONFIG_I2C_HID)		+= i2c-hid/
obj-$(CONFIG_I2C_HID_CORE)	+= i2c-hid/

obj-$(CONFIG_INTEL_ISH_HID)	+= intel-ish-hid/
obj-$(INTEL_ISH_FIRMWARE_DOWNLOADER)	+= intel-ish-hid/
+27 −5
Original line number Diff line number Diff line
@@ -2,18 +2,40 @@
menu "I2C HID support"
	depends on I2C

config I2C_HID
	tristate "HID over I2C transport layer"
config I2C_HID_ACPI
	tristate "HID over I2C transport layer ACPI driver"
	default n
	depends on I2C && INPUT
	select HID
	depends on I2C && INPUT && ACPI
	help
	  Say Y here if you use a keyboard, a touchpad, a touchscreen, or any
	  other HID based devices which is connected to your computer via I2C.
	  This driver supports ACPI-based systems.

	  If unsure, say N.

	  This support is also available as a module.  If so, the module
	  will be called i2c-hid-acpi.  It will also build/depend on the
	  module i2c-hid.

config I2C_HID_OF
	tristate "HID over I2C transport layer Open Firmware driver"
	default n
	depends on I2C && INPUT && OF
	help
	  Say Y here if you use a keyboard, a touchpad, a touchscreen, or any
	  other HID based devices which is connected to your computer via I2C.
	  This driver supports Open Firmware (Device Tree)-based systems.

	  If unsure, say N.

	  This support is also available as a module.  If so, the module
	  will be called i2c-hid.
	  will be called i2c-hid-of.  It will also build/depend on the
	  module i2c-hid.

endmenu

config I2C_HID_CORE
	tristate
	default y if I2C_HID_ACPI=y || I2C_HID_OF=y
	default m if I2C_HID_ACPI=m || I2C_HID_OF=m
	select HID
+4 −1
Original line number Diff line number Diff line
@@ -3,7 +3,10 @@
# Makefile for the I2C input drivers
#

obj-$(CONFIG_I2C_HID)				+= i2c-hid.o
obj-$(CONFIG_I2C_HID_CORE)			+= i2c-hid.o

i2c-hid-objs					=  i2c-hid-core.o
i2c-hid-$(CONFIG_DMI)				+= i2c-hid-dmi-quirks.o

obj-$(CONFIG_I2C_HID_ACPI)			+= i2c-hid-acpi.o
obj-$(CONFIG_I2C_HID_OF)			+= i2c-hid-of.o
+143 −0
Original line number Diff line number Diff line
/*
 * HID over I2C ACPI Subclass
 *
 * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
 * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
 * Copyright (c) 2012 Red Hat, Inc
 *
 * This code was forked out of the core code, which was partly based on
 * "USB HID support for Linux":
 *
 *  Copyright (c) 1999 Andreas Gal
 *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
 *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
 *  Copyright (c) 2007-2008 Oliver Neukum
 *  Copyright (c) 2006-2010 Jiri Kosina
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of this archive for
 * more details.
 */

#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pm.h>

#include "i2c-hid.h"

struct i2c_hid_acpi {
	struct i2chid_ops ops;
	struct i2c_client *client;
};

static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
	/*
	 * The CHPN0001 ACPI device, which is used to describe the Chipone
	 * ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible.
	 */
	{"CHPN0001", 0 },
	{ },
};

static int i2c_hid_acpi_get_descriptor(struct i2c_client *client)
{
	static guid_t i2c_hid_guid =
		GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
			  0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
	union acpi_object *obj;
	struct acpi_device *adev;
	acpi_handle handle;
	u16 hid_descriptor_address;

	handle = ACPI_HANDLE(&client->dev);
	if (!handle || acpi_bus_get_device(handle, &adev)) {
		dev_err(&client->dev, "Error could not get ACPI device\n");
		return -ENODEV;
	}

	if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
		return -ENODEV;

	obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL,
				      ACPI_TYPE_INTEGER);
	if (!obj) {
		dev_err(&client->dev, "Error _DSM call to get HID descriptor address failed\n");
		return -ENODEV;
	}

	hid_descriptor_address = obj->integer.value;
	ACPI_FREE(obj);

	return hid_descriptor_address;
}

static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops)
{
	struct i2c_hid_acpi *ihid_acpi =
		container_of(ops, struct i2c_hid_acpi, ops);
	struct device *dev = &ihid_acpi->client->dev;
	acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D3_COLD);
}

static int i2c_hid_acpi_probe(struct i2c_client *client,
			      const struct i2c_device_id *dev_id)
{
	struct device *dev = &client->dev;
	struct i2c_hid_acpi *ihid_acpi;
	struct acpi_device *adev;
	u16 hid_descriptor_address;
	int ret;

	ihid_acpi = devm_kzalloc(&client->dev, sizeof(*ihid_acpi), GFP_KERNEL);
	if (!ihid_acpi)
		return -ENOMEM;

	ihid_acpi->client = client;
	ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail;

	ret = i2c_hid_acpi_get_descriptor(client);
	if (ret < 0)
		return ret;
	hid_descriptor_address = ret;

	adev = ACPI_COMPANION(dev);
	if (adev)
		acpi_device_fix_up_power(adev);

	if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) {
		device_set_wakeup_capable(dev, true);
		device_set_wakeup_enable(dev, false);
	}

	return i2c_hid_core_probe(client, &ihid_acpi->ops,
				  hid_descriptor_address);
}

static const struct acpi_device_id i2c_hid_acpi_match[] = {
	{"ACPI0C50", 0 },
	{"PNP0C50", 0 },
	{ },
};
MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match);

static struct i2c_driver i2c_hid_acpi_driver = {
	.driver = {
		.name	= "i2c_hid_acpi",
		.pm	= &i2c_hid_core_pm,
		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
		.acpi_match_table = ACPI_PTR(i2c_hid_acpi_match),
	},

	.probe		= i2c_hid_acpi_probe,
	.remove		= i2c_hid_core_remove,
	.shutdown	= i2c_hid_core_shutdown,
};

module_i2c_driver(i2c_hid_acpi_driver);

MODULE_DESCRIPTION("HID over I2C ACPI driver");
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
MODULE_LICENSE("GPL");
+39 −213
Original line number Diff line number Diff line
@@ -35,11 +35,6 @@
#include <linux/kernel.h>
#include <linux/hid.h>
#include <linux/mutex.h>
#include <linux/acpi.h>
#include <linux/of.h>
#include <linux/regulator/consumer.h>

#include <linux/platform_data/i2c-hid.h>

#include "../hid-ids.h"
#include "i2c-hid.h"
@@ -156,10 +151,10 @@ struct i2c_hid {

	wait_queue_head_t	wait;		/* For waiting the interrupt */

	struct i2c_hid_platform_data pdata;

	bool			irq_wake_enabled;
	struct mutex		reset_lock;

	struct i2chid_ops	*ops;
};

static const struct i2c_hid_quirks {
@@ -884,144 +879,36 @@ static int i2c_hid_fetch_hid_descriptor(struct i2c_hid *ihid)
	return 0;
}

#ifdef CONFIG_ACPI
static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
	/*
	 * The CHPN0001 ACPI device, which is used to describe the Chipone
	 * ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible.
	 */
	{"CHPN0001", 0 },
	{ },
};

static int i2c_hid_acpi_pdata(struct i2c_client *client,
		struct i2c_hid_platform_data *pdata)
static int i2c_hid_core_power_up(struct i2c_hid *ihid)
{
	static guid_t i2c_hid_guid =
		GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
			  0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
	union acpi_object *obj;
	struct acpi_device *adev;
	acpi_handle handle;

	handle = ACPI_HANDLE(&client->dev);
	if (!handle || acpi_bus_get_device(handle, &adev)) {
		dev_err(&client->dev, "Error could not get ACPI device\n");
		return -ENODEV;
	}

	if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
		return -ENODEV;

	obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL,
				      ACPI_TYPE_INTEGER);
	if (!obj) {
		dev_err(&client->dev, "Error _DSM call to get HID descriptor address failed\n");
		return -ENODEV;
	}

	pdata->hid_descriptor_address = obj->integer.value;
	ACPI_FREE(obj);

	if (!ihid->ops->power_up)
		return 0;
}

static void i2c_hid_acpi_fix_up_power(struct device *dev)
{
	struct acpi_device *adev;

	adev = ACPI_COMPANION(dev);
	if (adev)
		acpi_device_fix_up_power(adev);
}

static void i2c_hid_acpi_enable_wakeup(struct device *dev)
{
	if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) {
		device_set_wakeup_capable(dev, true);
		device_set_wakeup_enable(dev, false);
	}
}

static void i2c_hid_acpi_shutdown(struct device *dev)
{
	acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D3_COLD);
}

static const struct acpi_device_id i2c_hid_acpi_match[] = {
	{"ACPI0C50", 0 },
	{"PNP0C50", 0 },
	{ },
};
MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match);
#else
static inline int i2c_hid_acpi_pdata(struct i2c_client *client,
		struct i2c_hid_platform_data *pdata)
{
	return -ENODEV;
	return ihid->ops->power_up(ihid->ops);
}

static inline void i2c_hid_acpi_fix_up_power(struct device *dev) {}

static inline void i2c_hid_acpi_enable_wakeup(struct device *dev) {}

static inline void i2c_hid_acpi_shutdown(struct device *dev) {}
#endif

#ifdef CONFIG_OF
static int i2c_hid_of_probe(struct i2c_client *client,
		struct i2c_hid_platform_data *pdata)
static void i2c_hid_core_power_down(struct i2c_hid *ihid)
{
	struct device *dev = &client->dev;
	u32 val;
	int ret;

	ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val);
	if (ret) {
		dev_err(&client->dev, "HID register address not provided\n");
		return -ENODEV;
	}
	if (val >> 16) {
		dev_err(&client->dev, "Bad HID register address: 0x%08x\n",
			val);
		return -EINVAL;
	}
	pdata->hid_descriptor_address = val;

	return 0;
}
	if (!ihid->ops->power_down)
		return;

static const struct of_device_id i2c_hid_of_match[] = {
	{ .compatible = "hid-over-i2c" },
	{},
};
MODULE_DEVICE_TABLE(of, i2c_hid_of_match);
#else
static inline int i2c_hid_of_probe(struct i2c_client *client,
		struct i2c_hid_platform_data *pdata)
{
	return -ENODEV;
	ihid->ops->power_down(ihid->ops);
}
#endif

static void i2c_hid_fwnode_probe(struct i2c_client *client,
				 struct i2c_hid_platform_data *pdata)
static void i2c_hid_core_shutdown_tail(struct i2c_hid *ihid)
{
	u32 val;
	if (!ihid->ops->shutdown_tail)
		return;

	if (!device_property_read_u32(&client->dev, "post-power-on-delay-ms",
				      &val))
		pdata->post_power_delay_ms = val;
	ihid->ops->shutdown_tail(ihid->ops);
}

static int i2c_hid_probe(struct i2c_client *client,
			 const struct i2c_device_id *dev_id)
int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops,
		       u16 hid_descriptor_address)
{
	int ret;
	struct i2c_hid *ihid;
	struct hid_device *hid;
	__u16 hidRegister;
	struct i2c_hid_platform_data *platform_data = client->dev.platform_data;

	dbg_hid("HID probe called for i2c 0x%02x\n", client->addr);

@@ -1042,44 +929,17 @@ static int i2c_hid_probe(struct i2c_client *client,
	if (!ihid)
		return -ENOMEM;

	if (client->dev.of_node) {
		ret = i2c_hid_of_probe(client, &ihid->pdata);
		if (ret)
			return ret;
	} else if (!platform_data) {
		ret = i2c_hid_acpi_pdata(client, &ihid->pdata);
		if (ret)
			return ret;
	} else {
		ihid->pdata = *platform_data;
	}

	/* Parse platform agnostic common properties from ACPI / device tree */
	i2c_hid_fwnode_probe(client, &ihid->pdata);

	ihid->pdata.supplies[0].supply = "vdd";
	ihid->pdata.supplies[1].supply = "vddl";
	ihid->ops = ops;

	ret = devm_regulator_bulk_get(&client->dev,
				      ARRAY_SIZE(ihid->pdata.supplies),
				      ihid->pdata.supplies);
	ret = i2c_hid_core_power_up(ihid);
	if (ret)
		return ret;

	ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
				    ihid->pdata.supplies);
	if (ret < 0)
		return ret;

	if (ihid->pdata.post_power_delay_ms)
		msleep(ihid->pdata.post_power_delay_ms);

	i2c_set_clientdata(client, ihid);

	ihid->client = client;

	hidRegister = ihid->pdata.hid_descriptor_address;
	ihid->wHIDDescRegister = cpu_to_le16(hidRegister);
	ihid->wHIDDescRegister = cpu_to_le16(hid_descriptor_address);

	init_waitqueue_head(&ihid->wait);
	mutex_init(&ihid->reset_lock);
@@ -1089,11 +949,7 @@ static int i2c_hid_probe(struct i2c_client *client,
	 * real computation later. */
	ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE);
	if (ret < 0)
		goto err_regulator;

	i2c_hid_acpi_fix_up_power(&client->dev);

	i2c_hid_acpi_enable_wakeup(&client->dev);
		goto err_powered;

	device_enable_async_suspend(&client->dev);

@@ -1102,19 +958,19 @@ static int i2c_hid_probe(struct i2c_client *client,
	if (ret < 0) {
		dev_dbg(&client->dev, "nothing at this address: %d\n", ret);
		ret = -ENXIO;
		goto err_regulator;
		goto err_powered;
	}

	ret = i2c_hid_fetch_hid_descriptor(ihid);
	if (ret < 0) {
		dev_err(&client->dev,
			"Failed to fetch the HID Descriptor\n");
		goto err_regulator;
		goto err_powered;
	}

	ret = i2c_hid_init_irq(client);
	if (ret < 0)
		goto err_regulator;
		goto err_powered;

	hid = hid_allocate_device();
	if (IS_ERR(hid)) {
@@ -1153,14 +1009,14 @@ static int i2c_hid_probe(struct i2c_client *client,
err_irq:
	free_irq(client->irq, ihid);

err_regulator:
	regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
			       ihid->pdata.supplies);
err_powered:
	i2c_hid_core_power_down(ihid);
	i2c_hid_free_buffers(ihid);
	return ret;
}
EXPORT_SYMBOL_GPL(i2c_hid_core_probe);

static int i2c_hid_remove(struct i2c_client *client)
int i2c_hid_core_remove(struct i2c_client *client)
{
	struct i2c_hid *ihid = i2c_get_clientdata(client);
	struct hid_device *hid;
@@ -1173,24 +1029,25 @@ static int i2c_hid_remove(struct i2c_client *client)
	if (ihid->bufsize)
		i2c_hid_free_buffers(ihid);

	regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
			       ihid->pdata.supplies);
	i2c_hid_core_power_down(ihid);

	return 0;
}
EXPORT_SYMBOL_GPL(i2c_hid_core_remove);

static void i2c_hid_shutdown(struct i2c_client *client)
void i2c_hid_core_shutdown(struct i2c_client *client)
{
	struct i2c_hid *ihid = i2c_get_clientdata(client);

	i2c_hid_set_power(client, I2C_HID_PWR_SLEEP);
	free_irq(client->irq, ihid);

	i2c_hid_acpi_shutdown(&client->dev);
	i2c_hid_core_shutdown_tail(ihid);
}
EXPORT_SYMBOL_GPL(i2c_hid_core_shutdown);

#ifdef CONFIG_PM_SLEEP
static int i2c_hid_suspend(struct device *dev)
static int i2c_hid_core_suspend(struct device *dev)
{
	struct i2c_client *client = to_i2c_client(dev);
	struct i2c_hid *ihid = i2c_get_clientdata(client);
@@ -1217,14 +1074,13 @@ static int i2c_hid_suspend(struct device *dev)
			hid_warn(hid, "Failed to enable irq wake: %d\n",
				wake_status);
	} else {
		regulator_bulk_disable(ARRAY_SIZE(ihid->pdata.supplies),
				       ihid->pdata.supplies);
		i2c_hid_core_power_down(ihid);
	}

	return 0;
}

static int i2c_hid_resume(struct device *dev)
static int i2c_hid_core_resume(struct device *dev)
{
	int ret;
	struct i2c_client *client = to_i2c_client(dev);
@@ -1233,13 +1089,7 @@ static int i2c_hid_resume(struct device *dev)
	int wake_status;

	if (!device_may_wakeup(&client->dev)) {
		ret = regulator_bulk_enable(ARRAY_SIZE(ihid->pdata.supplies),
					    ihid->pdata.supplies);
		if (ret)
			hid_warn(hid, "Failed to enable supplies: %d\n", ret);

		if (ihid->pdata.post_power_delay_ms)
			msleep(ihid->pdata.post_power_delay_ms);
		i2c_hid_core_power_up(ihid);
	} else if (ihid->irq_wake_enabled) {
		wake_status = disable_irq_wake(client->irq);
		if (!wake_status)
@@ -1276,34 +1126,10 @@ static int i2c_hid_resume(struct device *dev)
}
#endif

static const struct dev_pm_ops i2c_hid_pm = {
	SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_suspend, i2c_hid_resume)
const struct dev_pm_ops i2c_hid_core_pm = {
	SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_core_suspend, i2c_hid_core_resume)
};

static const struct i2c_device_id i2c_hid_id_table[] = {
	{ "hid", 0 },
	{ "hid-over-i2c", 0 },
	{ },
};
MODULE_DEVICE_TABLE(i2c, i2c_hid_id_table);


static struct i2c_driver i2c_hid_driver = {
	.driver = {
		.name	= "i2c_hid",
		.pm	= &i2c_hid_pm,
		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
		.acpi_match_table = ACPI_PTR(i2c_hid_acpi_match),
		.of_match_table = of_match_ptr(i2c_hid_of_match),
	},

	.probe		= i2c_hid_probe,
	.remove		= i2c_hid_remove,
	.shutdown	= i2c_hid_shutdown,
	.id_table	= i2c_hid_id_table,
};

module_i2c_driver(i2c_hid_driver);
EXPORT_SYMBOL_GPL(i2c_hid_core_pm);

MODULE_DESCRIPTION("HID over I2C core driver");
MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
Loading