diff options
author | Mauro Carvalho Chehab | 2021-12-16 20:54:02 +0100 |
---|---|---|
committer | Mauro Carvalho Chehab | 2021-12-16 20:54:02 +0100 |
commit | 3a956f0b123c1c2b165314a3173755aa3404036c (patch) | |
tree | 7ebb39dcecb7dc76747e0224b4591190a0d269f2 /drivers | |
parent | b9aafbd46eb92b3174781661c33bdb2c17d484f1 (diff) | |
parent | 97c2259ec7757ec24a90f0ef8fc5ea7fa1c6acca (diff) |
Merge tag 'platform-drivers-x86-int3472-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86 into media_tree
Signed tag for the immutable platform-drivers-x86-int3472 branch
This branch contains 5.16-rc1 + the pending ACPI/i2c, tps68570 platform_data
and INT3472 driver patches.
* tag 'platform-drivers-x86-int3472-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86:
platform/x86: int3472: Deal with probe ordering issues
platform/x86: int3472: Pass tps68470_regulator_platform_data to the tps68470-regulator MFD-cell
platform/x86: int3472: Pass tps68470_clk_platform_data to the tps68470-regulator MFD-cell
platform/x86: int3472: Add get_sensor_adev_and_name() helper
platform/x86: int3472: Split into 2 drivers
platform_data: Add linux/platform_data/tps68470.h file
i2c: acpi: Add i2c_acpi_new_device_by_fwnode() function
i2c: acpi: Use acpi_dev_ready_for_enumeration() helper
ACPI: delay enumeration of devices with a _DEP pointing to an INT3472 device
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/acpi/scan.c | 37 | ||||
-rw-r--r-- | drivers/i2c/i2c-core-acpi.c | 22 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/Makefile | 9 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/clk_and_regulator.c (renamed from drivers/platform/x86/intel/int3472/intel_skl_int3472_clk_and_regulator.c) | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/common.c | 82 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/common.h (renamed from drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h) | 6 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/discrete.c (renamed from drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c) | 51 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/intel_skl_int3472_common.c | 106 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/tps68470.c (renamed from drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c) | 92 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/tps68470.h | 25 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/tps68470_board_data.c | 145 |
11 files changed, 421 insertions, 156 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 2c80765670bc..89c4ae6ef3ec 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -797,6 +797,12 @@ static const char * const acpi_ignore_dep_ids[] = { NULL }; +/* List of HIDs for which we honor deps of matching ACPI devs, when checking _DEP lists. */ +static const char * const acpi_honor_dep_ids[] = { + "INT3472", /* Camera sensor PMIC / clk and regulator info */ + NULL +}; + static struct acpi_device *acpi_bus_get_parent(acpi_handle handle) { struct acpi_device *device = NULL; @@ -1762,8 +1768,12 @@ static void acpi_scan_dep_init(struct acpi_device *adev) struct acpi_dep_data *dep; list_for_each_entry(dep, &acpi_dep_list, node) { - if (dep->consumer == adev->handle) + if (dep->consumer == adev->handle) { + if (dep->honor_dep) + adev->flags.honor_deps = 1; + adev->dep_unmet++; + } } } @@ -1967,7 +1977,7 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) for (count = 0, i = 0; i < dep_devices.count; i++) { struct acpi_device_info *info; struct acpi_dep_data *dep; - bool skip; + bool skip, honor_dep; status = acpi_get_object_info(dep_devices.handles[i], &info); if (ACPI_FAILURE(status)) { @@ -1976,6 +1986,7 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) } skip = acpi_info_matches_ids(info, acpi_ignore_dep_ids); + honor_dep = acpi_info_matches_ids(info, acpi_honor_dep_ids); kfree(info); if (skip) @@ -1989,6 +2000,7 @@ static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep) dep->supplier = dep_devices.handles[i]; dep->consumer = handle; + dep->honor_dep = honor_dep; mutex_lock(&acpi_dep_list_lock); list_add_tail(&dep->node , &acpi_dep_list); @@ -2155,8 +2167,8 @@ static void acpi_bus_attach(struct acpi_device *device, bool first_pass) register_dock_dependent_device(device, ejd); acpi_bus_get_status(device); - /* Skip devices that are not present. */ - if (!acpi_device_is_present(device)) { + /* Skip devices that are not ready for enumeration (e.g. not present) */ + if (!acpi_dev_ready_for_enumeration(device)) { device->flags.initialized = false; acpi_device_clear_enumerated(device); device->flags.power_manageable = 0; @@ -2319,6 +2331,23 @@ void acpi_dev_clear_dependencies(struct acpi_device *supplier) EXPORT_SYMBOL_GPL(acpi_dev_clear_dependencies); /** + * acpi_dev_ready_for_enumeration - Check if the ACPI device is ready for enumeration + * @device: Pointer to the &struct acpi_device to check + * + * Check if the device is present and has no unmet dependencies. + * + * Return true if the device is ready for enumeratino. Otherwise, return false. + */ +bool acpi_dev_ready_for_enumeration(const struct acpi_device *device) +{ + if (device->flags.honor_deps && device->dep_unmet) + return false; + + return acpi_device_is_present(device); +} +EXPORT_SYMBOL_GPL(acpi_dev_ready_for_enumeration); + +/** * acpi_dev_get_first_consumer_dev - Return ACPI device dependent on @supplier * @supplier: Pointer to the dependee device * diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 92c1cc07ed46..c87ce2276007 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -144,9 +144,12 @@ static int i2c_acpi_do_lookup(struct acpi_device *adev, struct list_head resource_list; int ret; - if (acpi_bus_get_status(adev) || !adev->status.present) + if (acpi_bus_get_status(adev)) return -EINVAL; + if (!acpi_dev_ready_for_enumeration(adev)) + return -ENODEV; + if (acpi_match_device_ids(adev, i2c_acpi_ignored_device_ids) == 0) return -ENODEV; @@ -473,8 +476,8 @@ struct notifier_block i2c_acpi_notifier = { }; /** - * i2c_acpi_new_device - Create i2c-client for the Nth I2cSerialBus resource - * @dev: Device owning the ACPI resources to get the client from + * i2c_acpi_new_device_by_fwnode - Create i2c-client for the Nth I2cSerialBus resource + * @fwnode: fwnode with the ACPI resources to get the client from * @index: Index of ACPI resource to get * @info: describes the I2C device; note this is modified (addr gets set) * Context: can sleep @@ -490,15 +493,20 @@ struct notifier_block i2c_acpi_notifier = { * Returns a pointer to the new i2c-client, or error pointer in case of failure. * Specifically, -EPROBE_DEFER is returned if the adapter is not found. */ -struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, - struct i2c_board_info *info) +struct i2c_client *i2c_acpi_new_device_by_fwnode(struct fwnode_handle *fwnode, + int index, + struct i2c_board_info *info) { - struct acpi_device *adev = ACPI_COMPANION(dev); struct i2c_acpi_lookup lookup; struct i2c_adapter *adapter; + struct acpi_device *adev; LIST_HEAD(resource_list); int ret; + adev = to_acpi_device_node(fwnode); + if (!adev) + return ERR_PTR(-ENODEV); + memset(&lookup, 0, sizeof(lookup)); lookup.info = info; lookup.device_handle = acpi_device_handle(adev); @@ -520,7 +528,7 @@ struct i2c_client *i2c_acpi_new_device(struct device *dev, int index, return i2c_new_client_device(adapter, info); } -EXPORT_SYMBOL_GPL(i2c_acpi_new_device); +EXPORT_SYMBOL_GPL(i2c_acpi_new_device_by_fwnode); bool i2c_acpi_waive_d0_probe(struct device *dev) { diff --git a/drivers/platform/x86/intel/int3472/Makefile b/drivers/platform/x86/intel/int3472/Makefile index 2362e04db18d..cfec7784c5c9 100644 --- a/drivers/platform/x86/intel/int3472/Makefile +++ b/drivers/platform/x86/intel/int3472/Makefile @@ -1,5 +1,4 @@ -obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472.o -intel_skl_int3472-y := intel_skl_int3472_common.o \ - intel_skl_int3472_discrete.o \ - intel_skl_int3472_tps68470.o \ - intel_skl_int3472_clk_and_regulator.o +obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \ + intel_skl_int3472_tps68470.o +intel_skl_int3472_discrete-y := discrete.o clk_and_regulator.o common.o +intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o common.o diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c index 1700e7557a82..1cf958983e86 100644 --- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_clk_and_regulator.c +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c @@ -9,7 +9,7 @@ #include <linux/regulator/driver.h> #include <linux/slab.h> -#include "intel_skl_int3472_common.h" +#include "common.h" /* * The regulators have to have .ops to be valid, but the only ops we actually diff --git a/drivers/platform/x86/intel/int3472/common.c b/drivers/platform/x86/intel/int3472/common.c new file mode 100644 index 000000000000..77cf058e4168 --- /dev/null +++ b/drivers/platform/x86/intel/int3472/common.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Author: Dan Scally <djrscally@gmail.com> */ + +#include <linux/acpi.h> +#include <linux/slab.h> + +#include "common.h" + +union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *id) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_handle handle = adev->handle; + union acpi_object *obj; + acpi_status status; + + status = acpi_evaluate_object(handle, id, NULL, &buffer); + if (ACPI_FAILURE(status)) + return ERR_PTR(-ENODEV); + + obj = buffer.pointer; + if (!obj) + return ERR_PTR(-ENODEV); + + if (obj->type != ACPI_TYPE_BUFFER) { + acpi_handle_err(handle, "%s object is not an ACPI buffer\n", id); + kfree(obj); + return ERR_PTR(-EINVAL); + } + + return obj; +} + +int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb) +{ + union acpi_object *obj; + int ret; + + obj = skl_int3472_get_acpi_buffer(adev, "CLDB"); + if (IS_ERR(obj)) + return PTR_ERR(obj); + + if (obj->buffer.length > sizeof(*cldb)) { + acpi_handle_err(adev->handle, "The CLDB buffer is too large\n"); + ret = -EINVAL; + goto out_free_obj; + } + + memcpy(cldb, obj->buffer.pointer, obj->buffer.length); + ret = 0; + +out_free_obj: + kfree(obj); + return ret; +} + +/* sensor_adev_ret may be NULL, name_ret must not be NULL */ +int skl_int3472_get_sensor_adev_and_name(struct device *dev, + struct acpi_device **sensor_adev_ret, + const char **name_ret) +{ + struct acpi_device *adev = ACPI_COMPANION(dev); + struct acpi_device *sensor; + int ret = 0; + + sensor = acpi_dev_get_first_consumer_dev(adev); + if (!sensor) { + dev_err(dev, "INT3472 seems to have no dependents.\n"); + return -ENODEV; + } + + *name_ret = devm_kasprintf(dev, GFP_KERNEL, I2C_DEV_NAME_FORMAT, + acpi_dev_name(sensor)); + if (!*name_ret) + ret = -ENOMEM; + + if (ret == 0 && sensor_adev_ret) + *sensor_adev_ret = sensor; + else + acpi_dev_put(sensor); + + return ret; +} diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h b/drivers/platform/x86/intel/int3472/common.h index 714fde73b524..53270d19c73a 100644 --- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.h +++ b/drivers/platform/x86/intel/int3472/common.h @@ -105,12 +105,12 @@ struct int3472_discrete_device { struct gpiod_lookup_table gpios; }; -int skl_int3472_discrete_probe(struct platform_device *pdev); -int skl_int3472_discrete_remove(struct platform_device *pdev); -int skl_int3472_tps68470_probe(struct i2c_client *client); union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *id); int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb); +int skl_int3472_get_sensor_adev_and_name(struct device *dev, + struct acpi_device **sensor_adev_ret, + const char **name_ret); int skl_int3472_register_clock(struct int3472_discrete_device *int3472); void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472); diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index e59d79c7e82f..5b514fa01a97 100644 --- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -14,7 +14,7 @@ #include <linux/platform_device.h> #include <linux/uuid.h> -#include "intel_skl_int3472_common.h" +#include "common.h" /* * 79234640-9e10-4fea-a5c1-b5aa8b19756f @@ -332,7 +332,9 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) return 0; } -int skl_int3472_discrete_probe(struct platform_device *pdev) +static int skl_int3472_discrete_remove(struct platform_device *pdev); + +static int skl_int3472_discrete_probe(struct platform_device *pdev) { struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); struct int3472_discrete_device *int3472; @@ -361,19 +363,10 @@ int skl_int3472_discrete_probe(struct platform_device *pdev) int3472->dev = &pdev->dev; platform_set_drvdata(pdev, int3472); - int3472->sensor = acpi_dev_get_first_consumer_dev(adev); - if (!int3472->sensor) { - dev_err(&pdev->dev, "INT3472 seems to have no dependents.\n"); - return -ENODEV; - } - - int3472->sensor_name = devm_kasprintf(int3472->dev, GFP_KERNEL, - I2C_DEV_NAME_FORMAT, - acpi_dev_name(int3472->sensor)); - if (!int3472->sensor_name) { - ret = -ENOMEM; - goto err_put_sensor; - } + ret = skl_int3472_get_sensor_adev_and_name(&pdev->dev, &int3472->sensor, + &int3472->sensor_name); + if (ret) + return ret; /* * Initialising this list means we can call gpiod_remove_lookup_table() @@ -387,15 +380,11 @@ int skl_int3472_discrete_probe(struct platform_device *pdev) return ret; } + acpi_dev_clear_dependencies(adev); return 0; - -err_put_sensor: - acpi_dev_put(int3472->sensor); - - return ret; } -int skl_int3472_discrete_remove(struct platform_device *pdev) +static int skl_int3472_discrete_remove(struct platform_device *pdev) { struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev); @@ -411,3 +400,23 @@ int skl_int3472_discrete_remove(struct platform_device *pdev) return 0; } + +static const struct acpi_device_id int3472_device_id[] = { + { "INT3472", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, int3472_device_id); + +static struct platform_driver int3472_discrete = { + .driver = { + .name = "int3472-discrete", + .acpi_match_table = int3472_device_id, + }, + .probe = skl_int3472_discrete_probe, + .remove = skl_int3472_discrete_remove, +}; +module_platform_driver(int3472_discrete); + +MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Discrete Device Driver"); +MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.c b/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.c deleted file mode 100644 index 497e74fba75f..000000000000 --- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_common.c +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Author: Dan Scally <djrscally@gmail.com> */ - -#include <linux/acpi.h> -#include <linux/i2c.h> -#include <linux/platform_device.h> -#include <linux/slab.h> - -#include "intel_skl_int3472_common.h" - -union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *id) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - acpi_handle handle = adev->handle; - union acpi_object *obj; - acpi_status status; - - status = acpi_evaluate_object(handle, id, NULL, &buffer); - if (ACPI_FAILURE(status)) - return ERR_PTR(-ENODEV); - - obj = buffer.pointer; - if (!obj) - return ERR_PTR(-ENODEV); - - if (obj->type != ACPI_TYPE_BUFFER) { - acpi_handle_err(handle, "%s object is not an ACPI buffer\n", id); - kfree(obj); - return ERR_PTR(-EINVAL); - } - - return obj; -} - -int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb) -{ - union acpi_object *obj; - int ret; - - obj = skl_int3472_get_acpi_buffer(adev, "CLDB"); - if (IS_ERR(obj)) - return PTR_ERR(obj); - - if (obj->buffer.length > sizeof(*cldb)) { - acpi_handle_err(adev->handle, "The CLDB buffer is too large\n"); - ret = -EINVAL; - goto out_free_obj; - } - - memcpy(cldb, obj->buffer.pointer, obj->buffer.length); - ret = 0; - -out_free_obj: - kfree(obj); - return ret; -} - -static const struct acpi_device_id int3472_device_id[] = { - { "INT3472", 0 }, - { } -}; -MODULE_DEVICE_TABLE(acpi, int3472_device_id); - -static struct platform_driver int3472_discrete = { - .driver = { - .name = "int3472-discrete", - .acpi_match_table = int3472_device_id, - }, - .probe = skl_int3472_discrete_probe, - .remove = skl_int3472_discrete_remove, -}; - -static struct i2c_driver int3472_tps68470 = { - .driver = { - .name = "int3472-tps68470", - .acpi_match_table = int3472_device_id, - }, - .probe_new = skl_int3472_tps68470_probe, -}; - -static int skl_int3472_init(void) -{ - int ret; - - ret = platform_driver_register(&int3472_discrete); - if (ret) - return ret; - - ret = i2c_register_driver(THIS_MODULE, &int3472_tps68470); - if (ret) - platform_driver_unregister(&int3472_discrete); - - return ret; -} -module_init(skl_int3472_init); - -static void skl_int3472_exit(void) -{ - platform_driver_unregister(&int3472_discrete); - i2c_del_driver(&int3472_tps68470); -} -module_exit(skl_int3472_exit); - -MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Device Driver"); -MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c index c05b4cf502fe..22f61b47f9e5 100644 --- a/drivers/platform/x86/intel/int3472/intel_skl_int3472_tps68470.c +++ b/drivers/platform/x86/intel/int3472/tps68470.c @@ -2,27 +2,27 @@ /* Author: Dan Scally <djrscally@gmail.com> */ #include <linux/i2c.h> +#include <linux/kernel.h> #include <linux/mfd/core.h> #include <linux/mfd/tps68470.h> #include <linux/platform_device.h> +#include <linux/platform_data/tps68470.h> #include <linux/regmap.h> +#include <linux/string.h> -#include "intel_skl_int3472_common.h" +#include "common.h" +#include "tps68470.h" #define DESIGNED_FOR_CHROMEOS 1 #define DESIGNED_FOR_WINDOWS 2 +#define TPS68470_WIN_MFD_CELL_COUNT 3 + static const struct mfd_cell tps68470_cros[] = { { .name = "tps68470-gpio" }, { .name = "tps68470_pmic_opregion" }, }; -static const struct mfd_cell tps68470_win[] = { - { .name = "tps68470-gpio" }, - { .name = "tps68470-clk" }, - { .name = "tps68470-regulator" }, -}; - static const struct regmap_config tps68470_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -95,13 +95,21 @@ static int skl_int3472_tps68470_calc_type(struct acpi_device *adev) return DESIGNED_FOR_WINDOWS; } -int skl_int3472_tps68470_probe(struct i2c_client *client) +static int skl_int3472_tps68470_probe(struct i2c_client *client) { struct acpi_device *adev = ACPI_COMPANION(&client->dev); + const struct int3472_tps68470_board_data *board_data; + struct tps68470_clk_platform_data clk_pdata = {}; + struct mfd_cell *cells; struct regmap *regmap; int device_type; int ret; + ret = skl_int3472_get_sensor_adev_and_name(&client->dev, NULL, + &clk_pdata.consumer_dev_name); + if (ret) + return ret; + regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config); if (IS_ERR(regmap)) { dev_err(&client->dev, "Failed to create regmap: %ld\n", PTR_ERR(regmap)); @@ -119,9 +127,38 @@ int skl_int3472_tps68470_probe(struct i2c_client *client) device_type = skl_int3472_tps68470_calc_type(adev); switch (device_type) { case DESIGNED_FOR_WINDOWS: + board_data = int3472_tps68470_get_board_data(dev_name(&client->dev)); + if (!board_data) + return dev_err_probe(&client->dev, -ENODEV, "No board-data found for this model\n"); + + cells = kcalloc(TPS68470_WIN_MFD_CELL_COUNT, sizeof(*cells), GFP_KERNEL); + if (!cells) + return -ENOMEM; + + /* + * The order of the cells matters here! The clk must be first + * because the regulator depends on it. The gpios must be last, + * acpi_gpiochip_add() calls acpi_dev_clear_dependencies() and + * the clk + regulators must be ready when this happens. + */ + cells[0].name = "tps68470-clk"; + cells[0].platform_data = &clk_pdata; + cells[0].pdata_size = sizeof(clk_pdata); + cells[1].name = "tps68470-regulator"; + cells[1].platform_data = (void *)board_data->tps68470_regulator_pdata; + cells[1].pdata_size = sizeof(struct tps68470_regulator_platform_data); + cells[2].name = "tps68470-gpio"; + + gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_table); + ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, - tps68470_win, ARRAY_SIZE(tps68470_win), + cells, TPS68470_WIN_MFD_CELL_COUNT, NULL, 0, NULL); + kfree(cells); + + if (ret) + gpiod_remove_lookup_table(board_data->tps68470_gpio_lookup_table); + break; case DESIGNED_FOR_CHROMEOS: ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE, @@ -133,5 +170,42 @@ int skl_int3472_tps68470_probe(struct i2c_client *client) return device_type; } + /* + * No acpi_dev_clear_dependencies() here, since the acpi_gpiochip_add() + * for the GPIO cell already does this. + */ + return ret; } + +static int skl_int3472_tps68470_remove(struct i2c_client *client) +{ + const struct int3472_tps68470_board_data *board_data; + + board_data = int3472_tps68470_get_board_data(dev_name(&client->dev)); + if (board_data) + gpiod_remove_lookup_table(board_data->tps68470_gpio_lookup_table); + + return 0; +} + +static const struct acpi_device_id int3472_device_id[] = { + { "INT3472", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, int3472_device_id); + +static struct i2c_driver int3472_tps68470 = { + .driver = { + .name = "int3472-tps68470", + .acpi_match_table = int3472_device_id, + }, + .probe_new = skl_int3472_tps68470_probe, + .remove = skl_int3472_tps68470_remove, +}; +module_i2c_driver(int3472_tps68470); + +MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI TPS68470 Device Driver"); +MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_SOFTDEP("pre: clk-tps68470 tps68470-regulator"); diff --git a/drivers/platform/x86/intel/int3472/tps68470.h b/drivers/platform/x86/intel/int3472/tps68470.h new file mode 100644 index 000000000000..cfd33eb62740 --- /dev/null +++ b/drivers/platform/x86/intel/int3472/tps68470.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * TI TPS68470 PMIC platform data definition. + * + * Copyright (c) 2021 Red Hat Inc. + * + * Red Hat authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#ifndef _INTEL_SKL_INT3472_TPS68470_H +#define _INTEL_SKL_INT3472_TPS68470_H + +struct gpiod_lookup_table; +struct tps68470_regulator_platform_data; + +struct int3472_tps68470_board_data { + const char *dev_name; + struct gpiod_lookup_table *tps68470_gpio_lookup_table; + const struct tps68470_regulator_platform_data *tps68470_regulator_pdata; +}; + +const struct int3472_tps68470_board_data *int3472_tps68470_get_board_data(const char *dev_name); + +#endif diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c new file mode 100644 index 000000000000..faa5570f6e6b --- /dev/null +++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * TI TPS68470 PMIC platform data definition. + * + * Copyright (c) 2021 Dan Scally <djrscally@gmail.com> + * Copyright (c) 2021 Red Hat Inc. + * + * Red Hat authors: + * Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/dmi.h> +#include <linux/gpio/machine.h> +#include <linux/platform_data/tps68470.h> +#include <linux/regulator/machine.h> +#include "tps68470.h" + +static struct regulator_consumer_supply int347a_core_consumer_supplies[] = { + REGULATOR_SUPPLY("dvdd", "i2c-INT347A:00"), +}; + +static struct regulator_consumer_supply int347a_ana_consumer_supplies[] = { + REGULATOR_SUPPLY("avdd", "i2c-INT347A:00"), +}; + +static struct regulator_consumer_supply int347a_vcm_consumer_supplies[] = { + REGULATOR_SUPPLY("vdd", "i2c-INT347A:00-VCM"), +}; + +static struct regulator_consumer_supply int347a_vsio_consumer_supplies[] = { + REGULATOR_SUPPLY("dovdd", "i2c-INT347A:00"), + REGULATOR_SUPPLY("vsio", "i2c-INT347A:00-VCM"), +}; + +static const struct regulator_init_data surface_go_tps68470_core_reg_init_data = { + .constraints = { + .min_uV = 1200000, + .max_uV = 1200000, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(int347a_core_consumer_supplies), + .consumer_supplies = int347a_core_consumer_supplies, +}; + +static const struct regulator_init_data surface_go_tps68470_ana_reg_init_data = { + .constraints = { + .min_uV = 2815200, + .max_uV = 2815200, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(int347a_ana_consumer_supplies), + .consumer_supplies = int347a_ana_consumer_supplies, +}; + +static const struct regulator_init_data surface_go_tps68470_vcm_reg_init_data = { + .constraints = { + .min_uV = 2815200, + .max_uV = 2815200, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(int347a_vcm_consumer_supplies), + .consumer_supplies = int347a_vcm_consumer_supplies, +}; + +/* Ensure the always-on VIO regulator has the same voltage as VSIO */ +static const struct regulator_init_data surface_go_tps68470_vio_reg_init_data = { + .constraints = { + .min_uV = 1800600, + .max_uV = 1800600, + .apply_uV = true, + .always_on = true, + }, +}; + +static const struct regulator_init_data surface_go_tps68470_vsio_reg_init_data = { + .constraints = { + .min_uV = 1800600, + .max_uV = 1800600, + .apply_uV = true, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(int347a_vsio_consumer_supplies), + .consumer_supplies = int347a_vsio_consumer_supplies, +}; + +static const struct tps68470_regulator_platform_data surface_go_tps68470_pdata = { + .reg_init_data = { + [TPS68470_CORE] = &surface_go_tps68470_core_reg_init_data, + [TPS68470_ANA] = &surface_go_tps68470_ana_reg_init_data, + [TPS68470_VCM] = &surface_go_tps68470_vcm_reg_init_data, + [TPS68470_VIO] = &surface_go_tps68470_vio_reg_init_data, + [TPS68470_VSIO] = &surface_go_tps68470_vsio_reg_init_data, + }, +}; + +static struct gpiod_lookup_table surface_go_tps68470_gpios = { + .dev_id = "i2c-INT347A:00", + .table = { + GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW), + GPIO_LOOKUP("tps68470-gpio", 7, "powerdown", GPIO_ACTIVE_LOW) + } +}; + +static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = { + .dev_name = "i2c-INT3472:05", + .tps68470_gpio_lookup_table = &surface_go_tps68470_gpios, + .tps68470_regulator_pdata = &surface_go_tps68470_pdata, +}; + +static const struct dmi_system_id int3472_tps68470_board_data_table[] = { + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Go"), + }, + .driver_data = (void *)&surface_go_tps68470_board_data, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Go 2"), + }, + .driver_data = (void *)&surface_go_tps68470_board_data, + }, + { } +}; + +const struct int3472_tps68470_board_data *int3472_tps68470_get_board_data(const char *dev_name) +{ + const struct int3472_tps68470_board_data *board_data; + const struct dmi_system_id *match; + + for (match = dmi_first_match(int3472_tps68470_board_data_table); + match; + match = dmi_first_match(match + 1)) { + board_data = match->driver_data; + if (strcmp(board_data->dev_name, dev_name) == 0) + return board_data; + } + + return NULL; +} |