diff options
Diffstat (limited to 'drivers/platform/x86/intel/int3472')
-rw-r--r-- | drivers/platform/x86/intel/int3472/Kconfig | 1 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/Makefile | 2 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/clk_and_regulator.c | 37 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/common.h | 18 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/discrete.c | 104 | ||||
-rw-r--r-- | drivers/platform/x86/intel/int3472/led.c | 75 |
6 files changed, 167 insertions, 70 deletions
diff --git a/drivers/platform/x86/intel/int3472/Kconfig b/drivers/platform/x86/intel/int3472/Kconfig index 62e5d4cf9ee5..17ae997f93ea 100644 --- a/drivers/platform/x86/intel/int3472/Kconfig +++ b/drivers/platform/x86/intel/int3472/Kconfig @@ -4,6 +4,7 @@ config INTEL_SKL_INT3472 depends on COMMON_CLK depends on I2C depends on GPIOLIB + depends on LEDS_CLASS depends on REGULATOR select MFD_CORE select REGMAP_I2C diff --git a/drivers/platform/x86/intel/int3472/Makefile b/drivers/platform/x86/intel/int3472/Makefile index cfec7784c5c9..9f16cb514397 100644 --- a/drivers/platform/x86/intel/int3472/Makefile +++ b/drivers/platform/x86/intel/int3472/Makefile @@ -1,4 +1,4 @@ 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_discrete-y := discrete.o clk_and_regulator.o led.o common.o intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o common.o diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c index b2342b3d78c7..1086c3d83494 100644 --- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c @@ -23,8 +23,6 @@ static int skl_int3472_clk_prepare(struct clk_hw *hw) struct int3472_gpio_clock *clk = to_int3472_clk(hw); gpiod_set_value_cansleep(clk->ena_gpio, 1); - gpiod_set_value_cansleep(clk->led_gpio, 1); - return 0; } @@ -33,7 +31,6 @@ static void skl_int3472_clk_unprepare(struct clk_hw *hw) struct int3472_gpio_clock *clk = to_int3472_clk(hw); gpiod_set_value_cansleep(clk->ena_gpio, 0); - gpiod_set_value_cansleep(clk->led_gpio, 0); } static int skl_int3472_clk_enable(struct clk_hw *hw) @@ -89,18 +86,37 @@ static const struct clk_ops skl_int3472_clock_ops = { .recalc_rate = skl_int3472_clk_recalc_rate, }; -int skl_int3472_register_clock(struct int3472_discrete_device *int3472) +int skl_int3472_register_clock(struct int3472_discrete_device *int3472, + struct acpi_resource_gpio *agpio, u32 polarity) { + char *path = agpio->resource_source.string_ptr; struct clk_init_data init = { .ops = &skl_int3472_clock_ops, .flags = CLK_GET_RATE_NOCACHE, }; int ret; + if (int3472->clock.cl) + return -EBUSY; + + int3472->clock.ena_gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0], + "int3472,clk-enable"); + if (IS_ERR(int3472->clock.ena_gpio)) + return dev_err_probe(int3472->dev, PTR_ERR(int3472->clock.ena_gpio), + "getting clk-enable GPIO\n"); + + if (polarity == GPIO_ACTIVE_LOW) + gpiod_toggle_active_low(int3472->clock.ena_gpio); + + /* Ensure the pin is in output mode and non-active state */ + gpiod_direction_output(int3472->clock.ena_gpio, 0); + init.name = kasprintf(GFP_KERNEL, "%s-clk", acpi_dev_name(int3472->adev)); - if (!init.name) - return -ENOMEM; + if (!init.name) { + ret = -ENOMEM; + goto out_put_gpio; + } int3472->clock.frequency = skl_int3472_get_clk_frequency(int3472); @@ -126,14 +142,20 @@ err_unregister_clk: clk_unregister(int3472->clock.clk); out_free_init_name: kfree(init.name); +out_put_gpio: + gpiod_put(int3472->clock.ena_gpio); return ret; } void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472) { + if (!int3472->clock.cl) + return; + clkdev_drop(int3472->clock.cl); clk_unregister(int3472->clock.clk); + gpiod_put(int3472->clock.ena_gpio); } int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, @@ -181,6 +203,9 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, return PTR_ERR(int3472->regulator.gpio); } + /* Ensure the pin is in output mode and non-active state */ + gpiod_direction_output(int3472->regulator.gpio, 0); + cfg.dev = &int3472->adev->dev; cfg.init_data = &init_data; cfg.ena_gpiod = int3472->regulator.gpio; diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h index 53270d19c73a..61688e450ce5 100644 --- a/drivers/platform/x86/intel/int3472/common.h +++ b/drivers/platform/x86/intel/int3472/common.h @@ -6,6 +6,7 @@ #include <linux/clk-provider.h> #include <linux/gpio/machine.h> +#include <linux/leds.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/types.h> @@ -28,6 +29,8 @@ #define GPIO_REGULATOR_NAME_LENGTH 21 #define GPIO_REGULATOR_SUPPLY_NAME_LENGTH 9 +#define INT3472_LED_MAX_NAME_LEN 32 + #define CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET 86 #define INT3472_REGULATOR(_name, _supply, _ops) \ @@ -96,10 +99,16 @@ struct int3472_discrete_device { struct clk_hw clk_hw; struct clk_lookup *cl; struct gpio_desc *ena_gpio; - struct gpio_desc *led_gpio; u32 frequency; } clock; + struct int3472_pled { + struct led_classdev classdev; + struct led_lookup_data lookup; + char name[INT3472_LED_MAX_NAME_LEN]; + struct gpio_desc *gpio; + } pled; + unsigned int ngpios; /* how many GPIOs have we seen */ unsigned int n_sensor_gpios; /* how many have we mapped to sensor */ struct gpiod_lookup_table gpios; @@ -112,11 +121,16 @@ 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); +int skl_int3472_register_clock(struct int3472_discrete_device *int3472, + struct acpi_resource_gpio *agpio, u32 polarity); void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472); int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, struct acpi_resource_gpio *agpio); void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); +int skl_int3472_register_pled(struct int3472_discrete_device *int3472, + struct acpi_resource_gpio *agpio, u32 polarity); +void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472); + #endif diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 974a132db651..f064da74f50a 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -2,8 +2,6 @@ /* Author: Dan Scally <djrscally@gmail.com> */ #include <linux/acpi.h> -#include <linux/clkdev.h> -#include <linux/clk-provider.h> #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/gpio/machine.h> @@ -80,14 +78,6 @@ skl_int3472_get_sensor_module_config(struct int3472_discrete_device *int3472) return ERR_PTR(-ENODEV); } - if (obj->string.type != ACPI_TYPE_STRING) { - dev_err(int3472->dev, - "Sensor _DSM returned a non-string value\n"); - - ACPI_FREE(obj); - return ERR_PTR(-EINVAL); - } - for (i = 0; i < ARRAY_SIZE(int3472_sensor_configs); i++) { if (!strcmp(int3472_sensor_configs[i].sensor_module_name, obj->string.pointer)) @@ -154,34 +144,34 @@ static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int347 return 0; } -static int skl_int3472_map_gpio_to_clk(struct int3472_discrete_device *int3472, - struct acpi_resource_gpio *agpio, u8 type) +static void int3472_get_func_and_polarity(u8 type, const char **func, u32 *polarity) { - char *path = agpio->resource_source.string_ptr; - u16 pin = agpio->pin_table[0]; - struct gpio_desc *gpio; - switch (type) { + case INT3472_GPIO_TYPE_RESET: + *func = "reset"; + *polarity = GPIO_ACTIVE_LOW; + break; + case INT3472_GPIO_TYPE_POWERDOWN: + *func = "powerdown"; + *polarity = GPIO_ACTIVE_LOW; + break; case INT3472_GPIO_TYPE_CLK_ENABLE: - gpio = acpi_get_and_request_gpiod(path, pin, "int3472,clk-enable"); - if (IS_ERR(gpio)) - return (PTR_ERR(gpio)); - - int3472->clock.ena_gpio = gpio; + *func = "clk-enable"; + *polarity = GPIO_ACTIVE_HIGH; break; case INT3472_GPIO_TYPE_PRIVACY_LED: - gpio = acpi_get_and_request_gpiod(path, pin, "int3472,privacy-led"); - if (IS_ERR(gpio)) - return (PTR_ERR(gpio)); - - int3472->clock.led_gpio = gpio; + *func = "privacy-led"; + *polarity = GPIO_ACTIVE_HIGH; + break; + case INT3472_GPIO_TYPE_POWER_ENABLE: + *func = "power-enable"; + *polarity = GPIO_ACTIVE_HIGH; break; default: - dev_err(int3472->dev, "Invalid GPIO type 0x%02x for clock\n", type); + *func = "unknown"; + *polarity = GPIO_ACTIVE_HIGH; break; } - - return 0; } /** @@ -222,9 +212,11 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, struct int3472_discrete_device *int3472 = data; struct acpi_resource_gpio *agpio; union acpi_object *obj; + u8 active_value, type; const char *err_msg; + const char *func; + u32 polarity; int ret; - u8 type; if (!acpi_gpio_get_io_resource(ares, &agpio)) return 1; @@ -246,26 +238,35 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, type = obj->integer.value & 0xff; + int3472_get_func_and_polarity(type, &func, &polarity); + + /* If bits 31-24 of the _DSM entry are all 0 then the signal is inverted */ + active_value = obj->integer.value >> 24; + if (!active_value) + polarity ^= GPIO_ACTIVE_LOW; + + dev_dbg(int3472->dev, "%s %s pin %d active-%s\n", func, + agpio->resource_source.string_ptr, agpio->pin_table[0], + (polarity == GPIO_ACTIVE_HIGH) ? "high" : "low"); + switch (type) { case INT3472_GPIO_TYPE_RESET: - ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "reset", - GPIO_ACTIVE_LOW); + case INT3472_GPIO_TYPE_POWERDOWN: + ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, func, polarity); if (ret) - err_msg = "Failed to map reset pin to sensor\n"; + err_msg = "Failed to map GPIO pin to sensor\n"; break; - case INT3472_GPIO_TYPE_POWERDOWN: - ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, "powerdown", - GPIO_ACTIVE_LOW); + case INT3472_GPIO_TYPE_CLK_ENABLE: + ret = skl_int3472_register_clock(int3472, agpio, polarity); if (ret) - err_msg = "Failed to map powerdown pin to sensor\n"; + err_msg = "Failed to register clock\n"; break; - case INT3472_GPIO_TYPE_CLK_ENABLE: case INT3472_GPIO_TYPE_PRIVACY_LED: - ret = skl_int3472_map_gpio_to_clk(int3472, agpio, type); + ret = skl_int3472_register_pled(int3472, agpio, polarity); if (ret) - err_msg = "Failed to map GPIO to clock\n"; + err_msg = "Failed to register LED\n"; break; case INT3472_GPIO_TYPE_POWER_ENABLE: @@ -310,21 +311,6 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) acpi_dev_free_resource_list(&resource_list); - /* - * If we find no clock enable GPIO pin then the privacy LED won't work. - * We've never seen that situation, but it's possible. Warn the user so - * it's clear what's happened. - */ - if (int3472->clock.ena_gpio) { - ret = skl_int3472_register_clock(int3472); - if (ret) - return ret; - } else { - if (int3472->clock.led_gpio) - dev_warn(int3472->dev, - "No clk GPIO. The privacy LED won't work\n"); - } - int3472->gpios.dev_id = int3472->sensor_name; gpiod_add_lookup_table(&int3472->gpios); @@ -337,12 +323,8 @@ static int skl_int3472_discrete_remove(struct platform_device *pdev) gpiod_remove_lookup_table(&int3472->gpios); - if (int3472->clock.cl) - skl_int3472_unregister_clock(int3472); - - gpiod_put(int3472->clock.ena_gpio); - gpiod_put(int3472->clock.led_gpio); - + skl_int3472_unregister_clock(int3472); + skl_int3472_unregister_pled(int3472); skl_int3472_unregister_regulator(int3472); return 0; diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c new file mode 100644 index 000000000000..bca1ce7d0d0c --- /dev/null +++ b/drivers/platform/x86/intel/int3472/led.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Author: Hans de Goede <hdegoede@redhat.com> */ + +#include <linux/acpi.h> +#include <linux/gpio/consumer.h> +#include <linux/leds.h> +#include "common.h" + +static int int3472_pled_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct int3472_discrete_device *int3472 = + container_of(led_cdev, struct int3472_discrete_device, pled.classdev); + + gpiod_set_value_cansleep(int3472->pled.gpio, brightness); + return 0; +} + +int skl_int3472_register_pled(struct int3472_discrete_device *int3472, + struct acpi_resource_gpio *agpio, u32 polarity) +{ + char *p, *path = agpio->resource_source.string_ptr; + int ret; + + if (int3472->pled.classdev.dev) + return -EBUSY; + + int3472->pled.gpio = acpi_get_and_request_gpiod(path, agpio->pin_table[0], + "int3472,privacy-led"); + if (IS_ERR(int3472->pled.gpio)) + return dev_err_probe(int3472->dev, PTR_ERR(int3472->pled.gpio), + "getting privacy LED GPIO\n"); + + if (polarity == GPIO_ACTIVE_LOW) + gpiod_toggle_active_low(int3472->pled.gpio); + + /* Ensure the pin is in output mode and non-active state */ + gpiod_direction_output(int3472->pled.gpio, 0); + + /* Generate the name, replacing the ':' in the ACPI devname with '_' */ + snprintf(int3472->pled.name, sizeof(int3472->pled.name), + "%s::privacy_led", acpi_dev_name(int3472->sensor)); + p = strchr(int3472->pled.name, ':'); + if (p) + *p = '_'; + + int3472->pled.classdev.name = int3472->pled.name; + int3472->pled.classdev.max_brightness = 1; + int3472->pled.classdev.brightness_set_blocking = int3472_pled_set; + + ret = led_classdev_register(int3472->dev, &int3472->pled.classdev); + if (ret) + goto err_free_gpio; + + int3472->pled.lookup.provider = int3472->pled.name; + int3472->pled.lookup.dev_id = int3472->sensor_name; + int3472->pled.lookup.con_id = "privacy-led"; + led_add_lookup(&int3472->pled.lookup); + + return 0; + +err_free_gpio: + gpiod_put(int3472->pled.gpio); + return ret; +} + +void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472) +{ + if (IS_ERR_OR_NULL(int3472->pled.classdev.dev)) + return; + + led_remove_lookup(&int3472->pled.lookup); + led_classdev_unregister(&int3472->pled.classdev); + gpiod_put(int3472->pled.gpio); +} |