diff options
author | Joonas Lahtinen <joonas.lahtinen@linux.intel.com> | 2022-02-03 10:53:49 +0300 |
---|---|---|
committer | Joonas Lahtinen <joonas.lahtinen@linux.intel.com> | 2022-02-03 10:53:49 +0300 |
commit | 876f7a438e4247a948268ad77b67c494f709cc30 (patch) | |
tree | c3fa2548657920df9d80822d08ff24666e62d37d /drivers/platform/x86/intel | |
parent | 86df4141869350edaa53fb994b3db2c2cca5065d (diff) | |
parent | 53dbee4926d3706ca9e03f3928fa85b5ec3bc0cc (diff) | |
download | linux-876f7a438e4247a948268ad77b67c494f709cc30.tar.xz |
Merge drm/drm-next into drm-intel-gt-next
Backmerge to bring in 5.17-rc2 to introduce a common baseline
to merge i915_regs changes from drm-intel-next.
Signed-off-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Diffstat (limited to 'drivers/platform/x86/intel')
21 files changed, 1071 insertions, 220 deletions
diff --git a/drivers/platform/x86/intel/Kconfig b/drivers/platform/x86/intel/Kconfig index 38ce3e344589..8e65086bb6c8 100644 --- a/drivers/platform/x86/intel/Kconfig +++ b/drivers/platform/x86/intel/Kconfig @@ -3,19 +3,6 @@ # Intel x86 Platform Specific Drivers # -menuconfig X86_PLATFORM_DRIVERS_INTEL - bool "Intel x86 Platform Specific Device Drivers" - default y - help - Say Y here to get to see options for device drivers for - various Intel x86 platforms, including vendor-specific - drivers. This option alone does not add any kernel code. - - If you say N, all options in this submenu will be skipped - and disabled. - -if X86_PLATFORM_DRIVERS_INTEL - source "drivers/platform/x86/intel/atomisp2/Kconfig" source "drivers/platform/x86/intel/int1092/Kconfig" source "drivers/platform/x86/intel/int33fe/Kconfig" @@ -184,4 +171,13 @@ config INTEL_UNCORE_FREQ_CONTROL To compile this driver as a module, choose M here: the module will be called intel-uncore-frequency. -endif # X86_PLATFORM_DRIVERS_INTEL +config INTEL_VSEC + tristate "Intel Vendor Specific Extended Capabilities Driver" + depends on PCI + select AUXILIARY_BUS + help + Adds support for feature drivers exposed using Intel PCIe VSEC and + DVSEC. + + To compile this driver as a module, choose M here: the module will + be called intel_vsec. diff --git a/drivers/platform/x86/intel/Makefile b/drivers/platform/x86/intel/Makefile index 7c24be2423d8..35f2066578b2 100644 --- a/drivers/platform/x86/intel/Makefile +++ b/drivers/platform/x86/intel/Makefile @@ -26,10 +26,14 @@ intel_int0002_vgpio-y := int0002_vgpio.o obj-$(CONFIG_INTEL_INT0002_VGPIO) += intel_int0002_vgpio.o intel_oaktrail-y := oaktrail.o obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o +intel_vsec-y := vsec.o +obj-$(CONFIG_INTEL_VSEC) += intel_vsec.o # Intel PMIC / PMC / P-Unit drivers intel_bxtwc_tmu-y := bxtwc_tmu.o obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o +intel_crystal_cove_charger-y := crystal_cove_charger.o +obj-$(CONFIG_X86_ANDROID_TABLETS) += intel_crystal_cove_charger.o intel_chtdc_ti_pwrbtn-y := chtdc_ti_pwrbtn.o obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o intel_mrfld_pwrbtn-y := mrfld_pwrbtn.o diff --git a/drivers/platform/x86/intel/crystal_cove_charger.c b/drivers/platform/x86/intel/crystal_cove_charger.c new file mode 100644 index 000000000000..0374bc742513 --- /dev/null +++ b/drivers/platform/x86/intel/crystal_cove_charger.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for the external-charger IRQ pass-through function of the + * Intel Bay Trail Crystal Cove PMIC. + * + * Note this is NOT a power_supply class driver, it just deals with IRQ + * pass-through, this requires a separate driver because the PMIC's + * level 2 interrupt for this must be explicitly acked. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/mfd/intel_soc_pmic.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define CHGRIRQ_REG 0x0a + +struct crystal_cove_charger_data { + struct mutex buslock; /* irq_bus_lock */ + struct irq_chip irqchip; + struct regmap *regmap; + struct irq_domain *irq_domain; + int irq; + int charger_irq; + bool irq_enabled; + bool irq_is_enabled; +}; + +static irqreturn_t crystal_cove_charger_irq(int irq, void *data) +{ + struct crystal_cove_charger_data *charger = data; + + /* No need to read CHGRIRQ_REG as there is only 1 IRQ */ + handle_nested_irq(charger->charger_irq); + + /* Ack CHGRIRQ 0 */ + regmap_write(charger->regmap, CHGRIRQ_REG, BIT(0)); + + return IRQ_HANDLED; +} + +static void crystal_cove_charger_irq_bus_lock(struct irq_data *data) +{ + struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data); + + mutex_lock(&charger->buslock); +} + +static void crystal_cove_charger_irq_bus_sync_unlock(struct irq_data *data) +{ + struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data); + + if (charger->irq_is_enabled != charger->irq_enabled) { + if (charger->irq_enabled) + enable_irq(charger->irq); + else + disable_irq(charger->irq); + + charger->irq_is_enabled = charger->irq_enabled; + } + + mutex_unlock(&charger->buslock); +} + +static void crystal_cove_charger_irq_unmask(struct irq_data *data) +{ + struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data); + + charger->irq_enabled = true; +} + +static void crystal_cove_charger_irq_mask(struct irq_data *data) +{ + struct crystal_cove_charger_data *charger = irq_data_get_irq_chip_data(data); + + charger->irq_enabled = false; +} + +static void crystal_cove_charger_rm_irq_domain(void *data) +{ + struct crystal_cove_charger_data *charger = data; + + irq_domain_remove(charger->irq_domain); +} + +static int crystal_cove_charger_probe(struct platform_device *pdev) +{ + struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); + struct crystal_cove_charger_data *charger; + int ret; + + charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL); + if (!charger) + return -ENOMEM; + + charger->regmap = pmic->regmap; + mutex_init(&charger->buslock); + + charger->irq = platform_get_irq(pdev, 0); + if (charger->irq < 0) + return charger->irq; + + charger->irq_domain = irq_domain_create_linear(dev_fwnode(pdev->dev.parent), 1, + &irq_domain_simple_ops, NULL); + if (!charger->irq_domain) + return -ENOMEM; + + /* Distuingish IRQ domain from others sharing (MFD) the same fwnode */ + irq_domain_update_bus_token(charger->irq_domain, DOMAIN_BUS_WAKEUP); + + ret = devm_add_action_or_reset(&pdev->dev, crystal_cove_charger_rm_irq_domain, charger); + if (ret) + return ret; + + charger->charger_irq = irq_create_mapping(charger->irq_domain, 0); + if (!charger->charger_irq) + return -ENOMEM; + + charger->irqchip.name = KBUILD_MODNAME; + charger->irqchip.irq_unmask = crystal_cove_charger_irq_unmask; + charger->irqchip.irq_mask = crystal_cove_charger_irq_mask; + charger->irqchip.irq_bus_lock = crystal_cove_charger_irq_bus_lock; + charger->irqchip.irq_bus_sync_unlock = crystal_cove_charger_irq_bus_sync_unlock; + + irq_set_chip_data(charger->charger_irq, charger); + irq_set_chip_and_handler(charger->charger_irq, &charger->irqchip, handle_simple_irq); + irq_set_nested_thread(charger->charger_irq, true); + irq_set_noprobe(charger->charger_irq); + + ret = devm_request_threaded_irq(&pdev->dev, charger->irq, NULL, + crystal_cove_charger_irq, + IRQF_ONESHOT | IRQF_NO_AUTOEN, + KBUILD_MODNAME, charger); + if (ret) + return dev_err_probe(&pdev->dev, ret, "requesting irq\n"); + + return 0; +} + +static struct platform_driver crystal_cove_charger_driver = { + .probe = crystal_cove_charger_probe, + .driver = { + .name = "crystal_cove_charger", + }, +}; +module_platform_driver(crystal_cove_charger_driver); + +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com"); +MODULE_DESCRIPTION("Intel Bay Trail Crystal Cove external charger IRQ pass-through"); +MODULE_LICENSE("GPL"); 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..f93d437fd192 --- /dev/null +++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c @@ -0,0 +1,158 @@ +// 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 int3472_tps68470_board_data surface_go3_tps68470_board_data = { + .dev_name = "i2c-INT3472:01", + .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, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"), + }, + .driver_data = (void *)&surface_go3_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; +} diff --git a/drivers/platform/x86/intel/pmc/pltdrv.c b/drivers/platform/x86/intel/pmc/pltdrv.c index 73797680b895..15ca8afdd973 100644 --- a/drivers/platform/x86/intel/pmc/pltdrv.c +++ b/drivers/platform/x86/intel/pmc/pltdrv.c @@ -65,7 +65,7 @@ static int __init pmc_core_platform_init(void) retval = platform_device_register(pmc_core_device); if (retval) - kfree(pmc_core_device); + platform_device_put(pmc_core_device); return retval; } diff --git a/drivers/platform/x86/intel/pmt/Kconfig b/drivers/platform/x86/intel/pmt/Kconfig index d630f883a717..e916fc966221 100644 --- a/drivers/platform/x86/intel/pmt/Kconfig +++ b/drivers/platform/x86/intel/pmt/Kconfig @@ -17,7 +17,7 @@ config INTEL_PMT_CLASS config INTEL_PMT_TELEMETRY tristate "Intel Platform Monitoring Technology (PMT) Telemetry driver" - depends on MFD_INTEL_PMT + depends on INTEL_VSEC select INTEL_PMT_CLASS help The Intel Platform Monitory Technology (PMT) Telemetry driver provides @@ -29,7 +29,7 @@ config INTEL_PMT_TELEMETRY config INTEL_PMT_CRASHLOG tristate "Intel Platform Monitoring Technology (PMT) Crashlog driver" - depends on MFD_INTEL_PMT + depends on INTEL_VSEC select INTEL_PMT_CLASS help The Intel Platform Monitoring Technology (PMT) crashlog driver provides diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 659b1073033c..1c9e3f3ea41c 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -13,6 +13,7 @@ #include <linux/mm.h> #include <linux/pci.h> +#include "../vsec.h" #include "class.h" #define PMT_XA_START 0 @@ -281,31 +282,29 @@ fail_dev_create: return ret; } -int intel_pmt_dev_create(struct intel_pmt_entry *entry, - struct intel_pmt_namespace *ns, - struct platform_device *pdev, int idx) +int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns, + struct intel_vsec_device *intel_vsec_dev, int idx) { + struct device *dev = &intel_vsec_dev->auxdev.dev; struct intel_pmt_header header; struct resource *disc_res; - int ret = -ENODEV; + int ret; - disc_res = platform_get_resource(pdev, IORESOURCE_MEM, idx); - if (!disc_res) - return ret; + disc_res = &intel_vsec_dev->resource[idx]; - entry->disc_table = devm_platform_ioremap_resource(pdev, idx); + entry->disc_table = devm_ioremap_resource(dev, disc_res); if (IS_ERR(entry->disc_table)) return PTR_ERR(entry->disc_table); - ret = ns->pmt_header_decode(entry, &header, &pdev->dev); + ret = ns->pmt_header_decode(entry, &header, dev); if (ret) return ret; - ret = intel_pmt_populate_entry(entry, &header, &pdev->dev, disc_res); + ret = intel_pmt_populate_entry(entry, &header, dev, disc_res); if (ret) return ret; - return intel_pmt_dev_register(entry, ns, &pdev->dev); + return intel_pmt_dev_register(entry, ns, dev); } EXPORT_SYMBOL_GPL(intel_pmt_dev_create); diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 1337019c2873..db11d58867ce 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -2,13 +2,14 @@ #ifndef _INTEL_PMT_CLASS_H #define _INTEL_PMT_CLASS_H -#include <linux/platform_device.h> #include <linux/xarray.h> #include <linux/types.h> #include <linux/bits.h> #include <linux/err.h> #include <linux/io.h> +#include "../vsec.h" + /* PMT access types */ #define ACCESS_BARID 2 #define ACCESS_LOCAL 3 @@ -47,7 +48,7 @@ struct intel_pmt_namespace { bool intel_pmt_is_early_client_hw(struct device *dev); int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns, - struct platform_device *pdev, int idx); + struct intel_vsec_device *dev, int idx); void intel_pmt_dev_destroy(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns); #endif diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index 1c1021f04d3c..34daf9df168b 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -8,6 +8,7 @@ * Author: "Alexander Duyck" <alexander.h.duyck@linux.intel.com> */ +#include <linux/auxiliary_bus.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> @@ -15,10 +16,9 @@ #include <linux/uaccess.h> #include <linux/overflow.h> +#include "../vsec.h" #include "class.h" -#define DRV_NAME "pmt_crashlog" - /* Crashlog discovery header types */ #define CRASH_TYPE_OOBMSM 1 @@ -257,34 +257,34 @@ static struct intel_pmt_namespace pmt_crashlog_ns = { /* * initialization */ -static int pmt_crashlog_remove(struct platform_device *pdev) +static void pmt_crashlog_remove(struct auxiliary_device *auxdev) { - struct pmt_crashlog_priv *priv = platform_get_drvdata(pdev); + struct pmt_crashlog_priv *priv = auxiliary_get_drvdata(auxdev); int i; for (i = 0; i < priv->num_entries; i++) intel_pmt_dev_destroy(&priv->entry[i].entry, &pmt_crashlog_ns); - - return 0; } -static int pmt_crashlog_probe(struct platform_device *pdev) +static int pmt_crashlog_probe(struct auxiliary_device *auxdev, + const struct auxiliary_device_id *id) { + struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev); struct pmt_crashlog_priv *priv; size_t size; int i, ret; - size = struct_size(priv, entry, pdev->num_resources); - priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + size = struct_size(priv, entry, intel_vsec_dev->num_resources); + priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, priv); + auxiliary_set_drvdata(auxdev, priv); - for (i = 0; i < pdev->num_resources; i++) { + for (i = 0; i < intel_vsec_dev->num_resources; i++) { struct intel_pmt_entry *entry = &priv->entry[i].entry; - ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, pdev, i); + ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, intel_vsec_dev, i); if (ret < 0) goto abort_probe; if (ret) @@ -295,26 +295,30 @@ static int pmt_crashlog_probe(struct platform_device *pdev) return 0; abort_probe: - pmt_crashlog_remove(pdev); + pmt_crashlog_remove(auxdev); return ret; } -static struct platform_driver pmt_crashlog_driver = { - .driver = { - .name = DRV_NAME, - }, - .remove = pmt_crashlog_remove, - .probe = pmt_crashlog_probe, +static const struct auxiliary_device_id pmt_crashlog_id_table[] = { + { .name = "intel_vsec.crashlog" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, pmt_crashlog_id_table); + +static struct auxiliary_driver pmt_crashlog_aux_driver = { + .id_table = pmt_crashlog_id_table, + .remove = pmt_crashlog_remove, + .probe = pmt_crashlog_probe, }; static int __init pmt_crashlog_init(void) { - return platform_driver_register(&pmt_crashlog_driver); + return auxiliary_driver_register(&pmt_crashlog_aux_driver); } static void __exit pmt_crashlog_exit(void) { - platform_driver_unregister(&pmt_crashlog_driver); + auxiliary_driver_unregister(&pmt_crashlog_aux_driver); xa_destroy(&crashlog_array); } @@ -323,5 +327,4 @@ module_exit(pmt_crashlog_exit); MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>"); MODULE_DESCRIPTION("Intel PMT Crashlog driver"); -MODULE_ALIAS("platform:" DRV_NAME); MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index 38d52651c572..6b6f3e2a617a 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -8,6 +8,7 @@ * Author: "David E. Box" <david.e.box@linux.intel.com> */ +#include <linux/auxiliary_bus.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> @@ -15,10 +16,9 @@ #include <linux/uaccess.h> #include <linux/overflow.h> +#include "../vsec.h" #include "class.h" -#define TELEM_DEV_NAME "pmt_telemetry" - #define TELEM_SIZE_OFFSET 0x0 #define TELEM_GUID_OFFSET 0x4 #define TELEM_BASE_OFFSET 0x8 @@ -79,34 +79,33 @@ static struct intel_pmt_namespace pmt_telem_ns = { .pmt_header_decode = pmt_telem_header_decode, }; -static int pmt_telem_remove(struct platform_device *pdev) +static void pmt_telem_remove(struct auxiliary_device *auxdev) { - struct pmt_telem_priv *priv = platform_get_drvdata(pdev); + struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev); int i; for (i = 0; i < priv->num_entries; i++) intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns); - - return 0; } -static int pmt_telem_probe(struct platform_device *pdev) +static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { + struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev); struct pmt_telem_priv *priv; size_t size; int i, ret; - size = struct_size(priv, entry, pdev->num_resources); - priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + size = struct_size(priv, entry, intel_vsec_dev->num_resources); + priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, priv); + auxiliary_set_drvdata(auxdev, priv); - for (i = 0; i < pdev->num_resources; i++) { + for (i = 0; i < intel_vsec_dev->num_resources; i++) { struct intel_pmt_entry *entry = &priv->entry[i]; - ret = intel_pmt_dev_create(entry, &pmt_telem_ns, pdev, i); + ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i); if (ret < 0) goto abort_probe; if (ret) @@ -117,32 +116,35 @@ static int pmt_telem_probe(struct platform_device *pdev) return 0; abort_probe: - pmt_telem_remove(pdev); + pmt_telem_remove(auxdev); return ret; } -static struct platform_driver pmt_telem_driver = { - .driver = { - .name = TELEM_DEV_NAME, - }, - .remove = pmt_telem_remove, - .probe = pmt_telem_probe, +static const struct auxiliary_device_id pmt_telem_id_table[] = { + { .name = "intel_vsec.telemetry" }, + {} +}; +MODULE_DEVICE_TABLE(auxiliary, pmt_telem_id_table); + +static struct auxiliary_driver pmt_telem_aux_driver = { + .id_table = pmt_telem_id_table, + .remove = pmt_telem_remove, + .probe = pmt_telem_probe, }; static int __init pmt_telem_init(void) { - return platform_driver_register(&pmt_telem_driver); + return auxiliary_driver_register(&pmt_telem_aux_driver); } module_init(pmt_telem_init); static void __exit pmt_telem_exit(void) { - platform_driver_unregister(&pmt_telem_driver); + auxiliary_driver_unregister(&pmt_telem_aux_driver); xa_destroy(&telem_array); } module_exit(pmt_telem_exit); MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); MODULE_DESCRIPTION("Intel PMT Telemetry driver"); -MODULE_ALIAS("platform:" TELEM_DEV_NAME); MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/uncore-frequency.c b/drivers/platform/x86/intel/uncore-frequency.c index 3ee4c5c8a64f..4cd8254f2e40 100644 --- a/drivers/platform/x86/intel/uncore-frequency.c +++ b/drivers/platform/x86/intel/uncore-frequency.c @@ -225,6 +225,7 @@ static struct attribute *uncore_attrs[] = { &min_freq_khz.attr, NULL }; +ATTRIBUTE_GROUPS(uncore); static void uncore_sysfs_entry_release(struct kobject *kobj) { @@ -236,7 +237,7 @@ static void uncore_sysfs_entry_release(struct kobject *kobj) static struct kobj_type uncore_ktype = { .release = uncore_sysfs_entry_release, .sysfs_ops = &kobj_sysfs_ops, - .default_attrs = uncore_attrs, + .default_groups = uncore_groups, }; /* Caller provides protection */ diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c new file mode 100644 index 000000000000..c3bdd75ed690 --- /dev/null +++ b/drivers/platform/x86/intel/vsec.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Vendor Specific Extended Capabilities auxiliary bus driver + * + * Copyright (c) 2021, Intel Corporation. + * All Rights Reserved. + * + * Author: David E. Box <david.e.box@linux.intel.com> + * + * This driver discovers and creates auxiliary devices for Intel defined PCIe + * "Vendor Specific" and "Designated Vendor Specific" Extended Capabilities, + * VSEC and DVSEC respectively. The driver supports features on specific PCIe + * endpoints that exist primarily to expose them. + */ + +#include <linux/auxiliary_bus.h> +#include <linux/bits.h> +#include <linux/kernel.h> +#include <linux/idr.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/types.h> + +#include "vsec.h" + +/* Intel DVSEC offsets */ +#define INTEL_DVSEC_ENTRIES 0xA +#define INTEL_DVSEC_SIZE 0xB +#define INTEL_DVSEC_TABLE 0xC +#define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) +#define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) +#define TABLE_OFFSET_SHIFT 3 + +static DEFINE_IDA(intel_vsec_ida); + +/** + * struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers. + * @rev: Revision ID of the VSEC/DVSEC register space + * @length: Length of the VSEC/DVSEC register space + * @id: ID of the feature + * @num_entries: Number of instances of the feature + * @entry_size: Size of the discovery table for each feature + * @tbir: BAR containing the discovery tables + * @offset: BAR offset of start of the first discovery table + */ +struct intel_vsec_header { + u8 rev; + u16 length; + u16 id; + u8 num_entries; + u8 entry_size; + u8 tbir; + u32 offset; +}; + +/* Platform specific data */ +struct intel_vsec_platform_info { + struct intel_vsec_header **capabilities; + unsigned long quirks; +}; + +enum intel_vsec_id { + VSEC_ID_TELEMETRY = 2, + VSEC_ID_WATCHER = 3, + VSEC_ID_CRASHLOG = 4, +}; + +static enum intel_vsec_id intel_vsec_allow_list[] = { + VSEC_ID_TELEMETRY, + VSEC_ID_WATCHER, + VSEC_ID_CRASHLOG, +}; + +static const char *intel_vsec_name(enum intel_vsec_id id) +{ + switch (id) { + case VSEC_ID_TELEMETRY: + return "telemetry"; + + case VSEC_ID_WATCHER: + return "watcher"; + + case VSEC_ID_CRASHLOG: + return "crashlog"; + + default: + return NULL; + } +} + +static bool intel_vsec_allowed(u16 id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(intel_vsec_allow_list); i++) + if (intel_vsec_allow_list[i] == id) + return true; + + return false; +} + +static bool intel_vsec_disabled(u16 id, unsigned long quirks) +{ + switch (id) { + case VSEC_ID_WATCHER: + return !!(quirks & VSEC_QUIRK_NO_WATCHER); + + case VSEC_ID_CRASHLOG: + return !!(quirks & VSEC_QUIRK_NO_CRASHLOG); + + default: + return false; + } +} + +static void intel_vsec_remove_aux(void *data) +{ + auxiliary_device_delete(data); + auxiliary_device_uninit(data); +} + +static void intel_vsec_dev_release(struct device *dev) +{ + struct intel_vsec_device *intel_vsec_dev = dev_to_ivdev(dev); + + ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); + kfree(intel_vsec_dev->resource); + kfree(intel_vsec_dev); +} + +static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *intel_vsec_dev, + const char *name) +{ + struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; + int ret; + + ret = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); + if (ret < 0) { + kfree(intel_vsec_dev); + return ret; + } + + auxdev->id = ret; + auxdev->name = name; + auxdev->dev.parent = &pdev->dev; + auxdev->dev.release = intel_vsec_dev_release; + + ret = auxiliary_device_init(auxdev); + if (ret < 0) { + ida_free(intel_vsec_dev->ida, auxdev->id); + kfree(intel_vsec_dev->resource); + kfree(intel_vsec_dev); + return ret; + } + + ret = auxiliary_device_add(auxdev); + if (ret < 0) { + auxiliary_device_uninit(auxdev); + return ret; + } + + return devm_add_action_or_reset(&pdev->dev, intel_vsec_remove_aux, auxdev); +} + +static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, + unsigned long quirks) +{ + struct intel_vsec_device *intel_vsec_dev; + struct resource *res, *tmp; + int i; + + if (!intel_vsec_allowed(header->id) || intel_vsec_disabled(header->id, quirks)) + return -EINVAL; + + if (!header->num_entries) { + dev_dbg(&pdev->dev, "Invalid 0 entry count for header id %d\n", header->id); + return -EINVAL; + } + + if (!header->entry_size) { + dev_dbg(&pdev->dev, "Invalid 0 entry size for header id %d\n", header->id); + return -EINVAL; + } + + intel_vsec_dev = kzalloc(sizeof(*intel_vsec_dev), GFP_KERNEL); + if (!intel_vsec_dev) + return -ENOMEM; + + res = kcalloc(header->num_entries, sizeof(*res), GFP_KERNEL); + if (!res) { + kfree(intel_vsec_dev); + return -ENOMEM; + } + + if (quirks & VSEC_QUIRK_TABLE_SHIFT) + header->offset >>= TABLE_OFFSET_SHIFT; + + /* + * The DVSEC/VSEC contains the starting offset and count for a block of + * discovery tables. Create a resource array of these tables to the + * auxiliary device driver. + */ + for (i = 0, tmp = res; i < header->num_entries; i++, tmp++) { + tmp->start = pdev->resource[header->tbir].start + + header->offset + i * (header->entry_size * sizeof(u32)); + tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1; + tmp->flags = IORESOURCE_MEM; + } + + intel_vsec_dev->pcidev = pdev; + intel_vsec_dev->resource = res; + intel_vsec_dev->num_resources = header->num_entries; + intel_vsec_dev->quirks = quirks; + intel_vsec_dev->ida = &intel_vsec_ida; + + return intel_vsec_add_aux(pdev, intel_vsec_dev, intel_vsec_name(header->id)); +} + +static bool intel_vsec_walk_header(struct pci_dev *pdev, unsigned long quirks, + struct intel_vsec_header **header) +{ + bool have_devices = false; + int ret; + + for ( ; *header; header++) { + ret = intel_vsec_add_dev(pdev, *header, quirks); + if (ret) + dev_info(&pdev->dev, "Could not add device for DVSEC id %d\n", + (*header)->id); + else + have_devices = true; + } + + return have_devices; +} + +static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks) +{ + bool have_devices = false; + int pos = 0; + + do { + struct intel_vsec_header header; + u32 table, hdr; + u16 vid; + int ret; + + pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_DVSEC); + if (!pos) + break; + + pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER1, &hdr); + vid = PCI_DVSEC_HEADER1_VID(hdr); + if (vid != PCI_VENDOR_ID_INTEL) + continue; + + /* Support only revision 1 */ + header.rev = PCI_DVSEC_HEADER1_REV(hdr); + if (header.rev != 1) { + dev_info(&pdev->dev, "Unsupported DVSEC revision %d\n", header.rev); + continue; + } + + header.length = PCI_DVSEC_HEADER1_LEN(hdr); + + pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); + pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); + pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); + + header.tbir = INTEL_DVSEC_TABLE_BAR(table); + header.offset = INTEL_DVSEC_TABLE_OFFSET(table); + + pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); + header.id = PCI_DVSEC_HEADER2_ID(hdr); + + ret = intel_vsec_add_dev(pdev, &header, quirks); + if (ret) + continue; + + have_devices = true; + } while (true); + + return have_devices; +} + +static bool intel_vsec_walk_vsec(struct pci_dev *pdev, unsigned long quirks) +{ + bool have_devices = false; + int pos = 0; + + do { + struct intel_vsec_header header; + u32 table, hdr; + int ret; + + pos = pci_find_next_ext_capability(pdev, pos, PCI_EXT_CAP_ID_VNDR); + if (!pos) + break; + + pci_read_config_dword(pdev, pos + PCI_VNDR_HEADER, &hdr); + + /* Support only revision 1 */ + header.rev = PCI_VNDR_HEADER_REV(hdr); + if (header.rev != 1) { + dev_info(&pdev->dev, "Unsupported VSEC revision %d\n", header.rev); + continue; + } + + header.id = PCI_VNDR_HEADER_ID(hdr); + header.length = PCI_VNDR_HEADER_LEN(hdr); + + /* entry, size, and table offset are the same as DVSEC */ + pci_read_config_byte(pdev, pos + INTEL_DVSEC_ENTRIES, &header.num_entries); + pci_read_config_byte(pdev, pos + INTEL_DVSEC_SIZE, &header.entry_size); + pci_read_config_dword(pdev, pos + INTEL_DVSEC_TABLE, &table); + + header.tbir = INTEL_DVSEC_TABLE_BAR(table); + header.offset = INTEL_DVSEC_TABLE_OFFSET(table); + + ret = intel_vsec_add_dev(pdev, &header, quirks); + if (ret) + continue; + + have_devices = true; + } while (true); + + return have_devices; +} + +static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct intel_vsec_platform_info *info; + bool have_devices = false; + unsigned long quirks = 0; + int ret; + + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + info = (struct intel_vsec_platform_info *)id->driver_data; + if (info) + quirks = info->quirks; + + if (intel_vsec_walk_dvsec(pdev, quirks)) + have_devices = true; + + if (intel_vsec_walk_vsec(pdev, quirks)) + have_devices = true; + + if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && + intel_vsec_walk_header(pdev, quirks, info->capabilities)) + have_devices = true; + + if (!have_devices) + return -ENODEV; + + return 0; +} + +/* TGL info */ +static const struct intel_vsec_platform_info tgl_info = { + .quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG | VSEC_QUIRK_TABLE_SHIFT, +}; + +/* DG1 info */ +static struct intel_vsec_header dg1_telemetry = { + .length = 0x10, + .id = 2, + .num_entries = 1, + .entry_size = 3, + .tbir = 0, + .offset = 0x466000, +}; + +static struct intel_vsec_header *dg1_capabilities[] = { + &dg1_telemetry, + NULL +}; + +static const struct intel_vsec_platform_info dg1_info = { + .capabilities = dg1_capabilities, + .quirks = VSEC_QUIRK_NO_DVSEC, +}; + +#define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d +#define PCI_DEVICE_ID_INTEL_VSEC_DG1 0x490e +#define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7 +#define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d +static const struct pci_device_id intel_vsec_pci_ids[] = { + { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, + { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, NULL) }, + { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, + { } +}; +MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids); + +static struct pci_driver intel_vsec_pci_driver = { + .name = "intel_vsec", + .id_table = intel_vsec_pci_ids, + .probe = intel_vsec_pci_probe, +}; +module_pci_driver(intel_vsec_pci_driver); + +MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); +MODULE_DESCRIPTION("Intel Extended Capabilities auxiliary bus driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h new file mode 100644 index 000000000000..4cc36678e8c5 --- /dev/null +++ b/drivers/platform/x86/intel/vsec.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _VSEC_H +#define _VSEC_H + +#include <linux/auxiliary_bus.h> +#include <linux/bits.h> + +struct pci_dev; +struct resource; + +enum intel_vsec_quirks { + /* Watcher feature not supported */ + VSEC_QUIRK_NO_WATCHER = BIT(0), + + /* Crashlog feature not supported */ + VSEC_QUIRK_NO_CRASHLOG = BIT(1), + + /* Use shift instead of mask to read discovery table offset */ + VSEC_QUIRK_TABLE_SHIFT = BIT(2), + + /* DVSEC not present (provided in driver data) */ + VSEC_QUIRK_NO_DVSEC = BIT(3), +}; + +struct intel_vsec_device { + struct auxiliary_device auxdev; + struct pci_dev *pcidev; + struct resource *resource; + struct ida *ida; + unsigned long quirks; + int num_resources; +}; + +static inline struct intel_vsec_device *dev_to_ivdev(struct device *dev) +{ + return container_of(dev, struct intel_vsec_device, auxdev.dev); +} + +static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device *auxdev) +{ + return container_of(auxdev, struct intel_vsec_device, auxdev); +} +#endif |