summaryrefslogtreecommitdiff
path: root/drivers/platform/x86/intel/int3472
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86/intel/int3472')
-rw-r--r--drivers/platform/x86/intel/int3472/Kconfig1
-rw-r--r--drivers/platform/x86/intel/int3472/Makefile2
-rw-r--r--drivers/platform/x86/intel/int3472/clk_and_regulator.c37
-rw-r--r--drivers/platform/x86/intel/int3472/common.h18
-rw-r--r--drivers/platform/x86/intel/int3472/discrete.c104
-rw-r--r--drivers/platform/x86/intel/int3472/led.c75
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);
+}