summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/x86-android-tablets/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/x86-android-tablets/core.c')
-rw-r--r--drivers/platform/x86/x86-android-tablets/core.c226
1 files changed, 155 insertions, 71 deletions
diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c
index 919ef4471229..6588fae30356 100644
--- a/drivers/platform/x86/x86-android-tablets/core.c
+++ b/drivers/platform/x86/x86-android-tablets/core.c
@@ -5,17 +5,19 @@
* devices typically have a bunch of things hardcoded, rather than specified
* in their DSDT.
*
- * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (C) 2021-2023 Hans de Goede <hansg@kernel.org>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
+#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
#include <linux/irq.h>
#include <linux/module.h>
+#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/serdev.h>
#include <linux/string.h>
@@ -26,19 +28,19 @@
static struct platform_device *x86_android_tablet_device;
/*
- * This helper allows getting a gpio_desc *before* the actual device consuming
- * the GPIO has been instantiated. This function _must_ only be used to handle
- * this special case such as e.g. :
+ * This helper allows getting a GPIO descriptor *before* the actual device
+ * consuming it has been instantiated. This function MUST only be used to
+ * handle this special case such as, e.g.:
*
* 1. Getting an IRQ from a GPIO for i2c_board_info.irq which is passed to
* i2c_client_new() to instantiate i2c_client-s; or
- * 2. Calling desc_to_gpio() to get an old style GPIO number for gpio_keys
+ * 2. Calling desc_to_gpio() to get an old style GPIO number for gpio-keys
* platform_data which still uses old style GPIO numbers.
*
- * Since the consuming device has not been instatiated yet a dynamic lookup
- * is generated using the special x86_android_tablet dev for dev_id.
+ * Since the consuming device has not been instantiated yet a dynamic lookup
+ * is generated using the special x86_android_tablet device for dev_id.
*
- * For normal GPIO lookups a standard static gpiod_lookup_table _must_ be used.
+ * For normal GPIO lookups a standard static struct gpiod_lookup_table MUST be used.
*/
int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id,
bool active_low, enum gpiod_flags dflags,
@@ -87,7 +89,7 @@ int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data)
/*
* The DSDT may already reference the GSI in a device skipped by
* acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI
- * to avoid EBUSY errors in this case.
+ * to avoid -EBUSY errors in this case.
*/
acpi_unregister_gsi(data->index);
irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity);
@@ -150,31 +152,71 @@ static struct i2c_client **i2c_clients;
static struct spi_device **spi_devs;
static struct platform_device **pdevs;
static struct serdev_device **serdevs;
-static struct gpio_keys_button *buttons;
-static struct gpiod_lookup_table * const *gpiod_lookup_tables;
-static const struct software_node *bat_swnode;
+static const struct software_node **gpio_button_swnodes;
+static const struct software_node **swnode_group;
+static const struct software_node **gpiochip_node_group;
static void (*exit_handler)(void);
+static __init struct i2c_adapter *
+get_i2c_adap_by_handle(const struct x86_i2c_client_info *client_info)
+{
+ acpi_handle handle;
+ acpi_status status;
+
+ status = acpi_get_handle(NULL, client_info->adapter_path, &handle);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Error could not get %s handle\n", client_info->adapter_path);
+ return NULL;
+ }
+
+ return i2c_acpi_find_adapter_by_handle(handle);
+}
+
+static __init int match_parent(struct device *dev, const void *data)
+{
+ return dev->parent == data;
+}
+
+static __init struct i2c_adapter *
+get_i2c_adap_by_pci_parent(const struct x86_i2c_client_info *client_info)
+{
+ struct i2c_adapter *adap = NULL;
+ struct device *pdev, *adap_dev;
+
+ pdev = bus_find_device_by_name(&pci_bus_type, NULL, client_info->adapter_path);
+ if (!pdev) {
+ pr_err("Error could not find %s PCI device\n", client_info->adapter_path);
+ return NULL;
+ }
+
+ adap_dev = bus_find_device(&i2c_bus_type, NULL, pdev, match_parent);
+ if (adap_dev) {
+ adap = i2c_verify_adapter(adap_dev);
+ if (!adap)
+ put_device(adap_dev);
+ }
+
+ put_device(pdev);
+
+ return adap;
+}
+
static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info,
int idx)
{
const struct x86_i2c_client_info *client_info = &dev_info->i2c_client_info[idx];
struct i2c_board_info board_info = client_info->board_info;
struct i2c_adapter *adap;
- acpi_handle handle;
- acpi_status status;
board_info.irq = x86_acpi_irq_helper_get(&client_info->irq_data);
if (board_info.irq < 0)
return board_info.irq;
- status = acpi_get_handle(NULL, client_info->adapter_path, &handle);
- if (ACPI_FAILURE(status)) {
- pr_err("Error could not get %s handle\n", client_info->adapter_path);
- return -ENODEV;
- }
+ if (dev_info->use_pci)
+ adap = get_i2c_adap_by_pci_parent(client_info);
+ else
+ adap = get_i2c_adap_by_handle(client_info);
- adap = i2c_acpi_find_adapter_by_handle(handle);
if (!adap) {
pr_err("error could not get %s adapter\n", client_info->adapter_path);
return -ENODEV;
@@ -223,21 +265,39 @@ static __init int x86_instantiate_spi_dev(const struct x86_dev_info *dev_info, i
spi_devs[idx] = spi_new_device(controller, &board_info);
put_device(&controller->dev);
if (!spi_devs[idx])
- return dev_err_probe(&controller->dev, -ENOMEM,
- "creating SPI-device %d\n", idx);
+ return -ENOMEM;
return 0;
}
-static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx)
+static __init struct device *
+get_serdev_controller_by_pci_parent(const struct x86_serdev_info *info)
+{
+ struct pci_dev *pdev;
+
+ pdev = pci_get_domain_bus_and_slot(0, 0, info->ctrl.pci.devfn);
+ if (!pdev) {
+ pr_err("error could not get PCI serdev at devfn 0x%02x\n", info->ctrl.pci.devfn);
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* This puts our reference on pdev and returns a ref on the ctrl */
+ return get_serdev_controller_from_parent(&pdev->dev, 0, info->ctrl_devname);
+}
+
+static __init int x86_instantiate_serdev(const struct x86_dev_info *dev_info, int idx)
{
+ const struct x86_serdev_info *info = &dev_info->serdev_info[idx];
struct acpi_device *serdev_adev;
struct serdev_device *serdev;
struct device *ctrl_dev;
int ret = -ENODEV;
- ctrl_dev = get_serdev_controller(info->ctrl_hid, info->ctrl_uid, 0,
- info->ctrl_devname);
+ if (dev_info->use_pci)
+ ctrl_dev = get_serdev_controller_by_pci_parent(info);
+ else
+ ctrl_dev = get_serdev_controller(info->ctrl.acpi.hid, info->ctrl.acpi.uid,
+ 0, info->ctrl_devname);
if (IS_ERR(ctrl_dev))
return PTR_ERR(ctrl_dev);
@@ -272,6 +332,34 @@ put_ctrl_dev:
return ret;
}
+const struct software_node baytrail_gpiochip_nodes[] = {
+ { .name = "INT33FC:00" },
+ { .name = "INT33FC:01" },
+ { .name = "INT33FC:02" },
+};
+
+static const struct software_node *baytrail_gpiochip_node_group[] = {
+ &baytrail_gpiochip_nodes[0],
+ &baytrail_gpiochip_nodes[1],
+ &baytrail_gpiochip_nodes[2],
+ NULL
+};
+
+const struct software_node cherryview_gpiochip_nodes[] = {
+ { .name = "INT33FF:00" },
+ { .name = "INT33FF:01" },
+ { .name = "INT33FF:02" },
+ { .name = "INT33FF:03" },
+};
+
+static const struct software_node *cherryview_gpiochip_node_group[] = {
+ &cherryview_gpiochip_nodes[0],
+ &cherryview_gpiochip_nodes[1],
+ &cherryview_gpiochip_nodes[2],
+ &cherryview_gpiochip_nodes[3],
+ NULL
+};
+
static void x86_android_tablet_remove(struct platform_device *pdev)
{
int i;
@@ -287,7 +375,6 @@ static void x86_android_tablet_remove(struct platform_device *pdev)
platform_device_unregister(pdevs[i]);
kfree(pdevs);
- kfree(buttons);
for (i = spi_dev_count - 1; i >= 0; i--)
spi_unregister_device(spi_devs[i]);
@@ -302,10 +389,9 @@ static void x86_android_tablet_remove(struct platform_device *pdev)
if (exit_handler)
exit_handler();
- for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++)
- gpiod_remove_lookup_table(gpiod_lookup_tables[i]);
-
- software_node_unregister(bat_swnode);
+ software_node_unregister_node_group(gpio_button_swnodes);
+ software_node_unregister_node_group(swnode_group);
+ software_node_unregister_node_group(gpiochip_node_group);
}
static __init int x86_android_tablet_probe(struct platform_device *pdev)
@@ -329,16 +415,28 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
for (i = 0; dev_info->modules && dev_info->modules[i]; i++)
request_module(dev_info->modules[i]);
- bat_swnode = dev_info->bat_swnode;
- if (bat_swnode) {
- ret = software_node_register(bat_swnode);
- if (ret)
- return ret;
+ switch (dev_info->gpiochip_type) {
+ case X86_GPIOCHIP_BAYTRAIL:
+ gpiochip_node_group = baytrail_gpiochip_node_group;
+ break;
+ case X86_GPIOCHIP_CHERRYVIEW:
+ gpiochip_node_group = cherryview_gpiochip_node_group;
+ break;
+ case X86_GPIOCHIP_UNSPECIFIED:
+ gpiochip_node_group = NULL;
+ break;
}
- gpiod_lookup_tables = dev_info->gpiod_lookup_tables;
- for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++)
- gpiod_add_lookup_table(gpiod_lookup_tables[i]);
+ ret = software_node_register_node_group(gpiochip_node_group);
+ if (ret)
+ return ret;
+
+ ret = software_node_register_node_group(dev_info->swnode_group);
+ if (ret) {
+ x86_android_tablet_remove(pdev);
+ return ret;
+ }
+ swnode_group = dev_info->swnode_group;
if (dev_info->init) {
ret = dev_info->init(&pdev->dev);
@@ -379,7 +477,7 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
}
}
- /* + 1 to make space for (optional) gpio_keys_button pdev */
+ /* + 1 to make space for the (optional) gpio_keys_button platform device */
pdevs = kcalloc(dev_info->pdev_count + 1, sizeof(*pdevs), GFP_KERNEL);
if (!pdevs) {
x86_android_tablet_remove(pdev);
@@ -390,8 +488,9 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
for (i = 0; i < pdev_count; i++) {
pdevs[i] = platform_device_register_full(&dev_info->pdev_info[i]);
if (IS_ERR(pdevs[i])) {
+ ret = PTR_ERR(pdevs[i]);
x86_android_tablet_remove(pdev);
- return PTR_ERR(pdevs[i]);
+ return ret;
}
}
@@ -403,48 +502,33 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev)
serdev_count = dev_info->serdev_count;
for (i = 0; i < serdev_count; i++) {
- ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i);
+ ret = x86_instantiate_serdev(dev_info, i);
if (ret < 0) {
x86_android_tablet_remove(pdev);
return ret;
}
}
- if (dev_info->gpio_button_count) {
- struct gpio_keys_platform_data pdata = { };
- struct gpio_desc *gpiod;
+ if (dev_info->gpio_button_swnodes) {
+ struct platform_device_info button_info = {
+ .name = "gpio-keys",
+ .id = PLATFORM_DEVID_AUTO,
+ };
- buttons = kcalloc(dev_info->gpio_button_count, sizeof(*buttons), GFP_KERNEL);
- if (!buttons) {
+ ret = software_node_register_node_group(dev_info->gpio_button_swnodes);
+ if (ret < 0) {
x86_android_tablet_remove(pdev);
- return -ENOMEM;
- }
-
- for (i = 0; i < dev_info->gpio_button_count; i++) {
- ret = x86_android_tablet_get_gpiod(dev_info->gpio_button[i].chip,
- dev_info->gpio_button[i].pin,
- dev_info->gpio_button[i].button.desc,
- false, GPIOD_IN, &gpiod);
- if (ret < 0) {
- x86_android_tablet_remove(pdev);
- return ret;
- }
-
- buttons[i] = dev_info->gpio_button[i].button;
- buttons[i].gpio = desc_to_gpio(gpiod);
- /* Release gpiod so that gpio-keys can request it */
- devm_gpiod_put(&x86_android_tablet_device->dev, gpiod);
+ return ret;
}
- pdata.buttons = buttons;
- pdata.nbuttons = dev_info->gpio_button_count;
+ gpio_button_swnodes = dev_info->gpio_button_swnodes;
- pdevs[pdev_count] = platform_device_register_data(&pdev->dev, "gpio-keys",
- PLATFORM_DEVID_AUTO,
- &pdata, sizeof(pdata));
+ button_info.fwnode = software_node_fwnode(dev_info->gpio_button_swnodes[0]);
+ pdevs[pdev_count] = platform_device_register_full(&button_info);
if (IS_ERR(pdevs[pdev_count])) {
+ ret = PTR_ERR(pdevs[pdev_count]);
x86_android_tablet_remove(pdev);
- return PTR_ERR(pdevs[pdev_count]);
+ return ret;
}
pdev_count++;
}
@@ -456,7 +540,7 @@ static struct platform_driver x86_android_tablet_driver = {
.driver = {
.name = KBUILD_MODNAME,
},
- .remove_new = x86_android_tablet_remove,
+ .remove = x86_android_tablet_remove,
};
static int __init x86_android_tablet_init(void)
@@ -476,6 +560,6 @@ static void __exit x86_android_tablet_exit(void)
}
module_exit(x86_android_tablet_exit);
-MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver");
MODULE_LICENSE("GPL");