Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,31 @@
config INTEL_SKL_INT3472
tristate "Intel SkyLake ACPI INT3472 Driver"
depends on ACPI
depends on COMMON_CLK
depends on I2C
depends on GPIOLIB
depends on LEDS_CLASS
depends on REGULATOR
select MFD_CORE
select REGMAP_I2C
help
This driver adds power controller support for the Intel SkyCam
devices found on the Intel SkyLake platforms.
The INT3472 is a camera power controller, a logical device found on
Intel Skylake-based systems that can map to different hardware
devices depending on the platform. On machines designed for Chrome OS
it maps to a TPS68470 camera PMIC. On machines designed for Windows,
it maps to either a TP68470 camera PMIC, a uP6641Q sensor PMIC, or a
set of discrete GPIOs and power gates.
If your device was designed for Chrome OS, this driver will provide
an ACPI OpRegion, which must be available before any of the devices
using it are probed. For this reason, you should select Y if your
device was designed for ChromeOS. For the same reason the
I2C_DESIGNWARE_PLATFORM option must be set to Y too.
Say Y or M here if you have a SkyLake device designed for use
with Windows or ChromeOS. Say N here if you are not sure.
The module will be named "intel-skl-int3472".
@@ -0,0 +1,8 @@
obj-$(CONFIG_INTEL_SKL_INT3472) += intel_skl_int3472_discrete.o \
intel_skl_int3472_tps68470.o \
intel_skl_int3472_common.o
intel_skl_int3472_discrete-y := discrete.o discrete_quirks.o \
clk_and_regulator.o led.o
intel_skl_int3472_tps68470-y := tps68470.o tps68470_board_data.o
intel_skl_int3472_common-y += common.o
@@ -0,0 +1,256 @@
// SPDX-License-Identifier: GPL-2.0
/* 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/platform_data/x86/int3472.h>
#include <linux/regulator/driver.h>
#include <linux/slab.h>
/*
* 82c0d13a-78c5-4244-9bb1-eb8b539a8d11
* This _DSM GUID allows controlling the sensor clk when it is not controlled
* through a GPIO.
*/
static const guid_t img_clk_guid =
GUID_INIT(0x82c0d13a, 0x78c5, 0x4244,
0x9b, 0xb1, 0xeb, 0x8b, 0x53, 0x9a, 0x8d, 0x11);
static void skl_int3472_enable_clk(struct int3472_clock *clk, int enable)
{
struct int3472_discrete_device *int3472 = to_int3472_device(clk);
union acpi_object args[3];
union acpi_object argv4;
if (clk->ena_gpio) {
gpiod_set_value_cansleep(clk->ena_gpio, enable);
return;
}
args[0].integer.type = ACPI_TYPE_INTEGER;
args[0].integer.value = clk->imgclk_index;
args[1].integer.type = ACPI_TYPE_INTEGER;
args[1].integer.value = enable;
args[2].integer.type = ACPI_TYPE_INTEGER;
args[2].integer.value = 1;
argv4.type = ACPI_TYPE_PACKAGE;
argv4.package.count = 3;
argv4.package.elements = args;
acpi_evaluate_dsm(acpi_device_handle(int3472->adev), &img_clk_guid,
0, 1, &argv4);
}
/*
* The regulators have to have .ops to be valid, but the only ops we actually
* support are .enable and .disable which are handled via .ena_gpiod. Pass an
* empty struct to clear the check without lying about capabilities.
*/
static const struct regulator_ops int3472_gpio_regulator_ops;
static int skl_int3472_clk_prepare(struct clk_hw *hw)
{
skl_int3472_enable_clk(to_int3472_clk(hw), 1);
return 0;
}
static void skl_int3472_clk_unprepare(struct clk_hw *hw)
{
skl_int3472_enable_clk(to_int3472_clk(hw), 0);
}
static int skl_int3472_clk_enable(struct clk_hw *hw)
{
/*
* We're just turning a GPIO on to enable the clock, which operation
* has the potential to sleep. Given .enable() cannot sleep, but
* .prepare() can, we toggle the GPIO in .prepare() instead. Thus,
* nothing to do here.
*/
return 0;
}
static void skl_int3472_clk_disable(struct clk_hw *hw)
{
/* Likewise, nothing to do here... */
}
static unsigned int skl_int3472_get_clk_frequency(struct int3472_discrete_device *int3472)
{
union acpi_object *obj;
unsigned int freq;
obj = skl_int3472_get_acpi_buffer(int3472->sensor, "SSDB");
if (IS_ERR(obj))
return 0; /* report rate as 0 on error */
if (obj->buffer.length < CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET + sizeof(u32)) {
dev_err(int3472->dev, "The buffer is too small\n");
kfree(obj);
return 0;
}
freq = *(u32 *)(obj->buffer.pointer + CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET);
kfree(obj);
return freq;
}
static unsigned long skl_int3472_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct int3472_clock *clk = to_int3472_clk(hw);
return clk->frequency;
}
static const struct clk_ops skl_int3472_clock_ops = {
.prepare = skl_int3472_clk_prepare,
.unprepare = skl_int3472_clk_unprepare,
.enable = skl_int3472_clk_enable,
.disable = skl_int3472_clk_disable,
.recalc_rate = skl_int3472_clk_recalc_rate,
};
static int skl_int3472_register_clock(struct int3472_discrete_device *int3472)
{
struct acpi_device *adev = int3472->adev;
struct clk_init_data init = {
.ops = &skl_int3472_clock_ops,
.flags = CLK_GET_RATE_NOCACHE,
};
int ret;
init.name = kasprintf(GFP_KERNEL, "%s-clk", acpi_dev_name(adev));
if (!init.name)
return -ENOMEM;
int3472->clock.frequency = skl_int3472_get_clk_frequency(int3472);
int3472->clock.clk_hw.init = &init;
int3472->clock.clk = clk_register(&adev->dev, &int3472->clock.clk_hw);
if (IS_ERR(int3472->clock.clk)) {
ret = PTR_ERR(int3472->clock.clk);
goto out_free_init_name;
}
int3472->clock.cl = clkdev_create(int3472->clock.clk, NULL, int3472->sensor_name);
if (!int3472->clock.cl) {
ret = -ENOMEM;
goto err_unregister_clk;
}
kfree(init.name);
return 0;
err_unregister_clk:
clk_unregister(int3472->clock.clk);
out_free_init_name:
kfree(init.name);
return ret;
}
int skl_int3472_register_dsm_clock(struct int3472_discrete_device *int3472)
{
if (int3472->clock.cl)
return 0; /* A GPIO controlled clk has already been registered */
if (!acpi_check_dsm(int3472->adev->handle, &img_clk_guid, 0, BIT(1)))
return 0; /* DSM clock control is not available */
return skl_int3472_register_clock(int3472);
}
int skl_int3472_register_gpio_clock(struct int3472_discrete_device *int3472,
struct gpio_desc *gpio)
{
if (int3472->clock.cl)
return -EBUSY;
int3472->clock.ena_gpio = gpio;
return skl_int3472_register_clock(int3472);
}
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,
struct gpio_desc *gpio,
unsigned int enable_time,
const char *supply_name,
const char *second_sensor)
{
struct regulator_init_data init_data = { };
struct int3472_gpio_regulator *regulator;
struct regulator_config cfg = { };
int i, j;
if (int3472->n_regulator_gpios >= INT3472_MAX_REGULATORS) {
dev_err(int3472->dev, "Too many regulators mapped\n");
return -EINVAL;
}
if (strlen(supply_name) >= GPIO_SUPPLY_NAME_LENGTH) {
dev_err(int3472->dev, "supply-name '%s' length too long\n", supply_name);
return -E2BIG;
}
regulator = &int3472->regulators[int3472->n_regulator_gpios];
string_upper(regulator->supply_name_upper, supply_name);
/* The below code assume that map-count is 2 (upper- and lower-case) */
static_assert(GPIO_REGULATOR_SUPPLY_MAP_COUNT == 2);
for (i = 0, j = 0; i < GPIO_REGULATOR_SUPPLY_MAP_COUNT; i++) {
const char *supply = i ? regulator->supply_name_upper : supply_name;
regulator->supply_map[j].supply = supply;
regulator->supply_map[j].dev_name = int3472->sensor_name;
j++;
if (second_sensor) {
regulator->supply_map[j].supply = supply;
regulator->supply_map[j].dev_name = second_sensor;
j++;
}
}
init_data.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
init_data.consumer_supplies = regulator->supply_map;
init_data.num_consumer_supplies = j;
snprintf(regulator->regulator_name, sizeof(regulator->regulator_name), "%s-%s",
acpi_dev_name(int3472->adev), supply_name);
regulator->rdesc = INT3472_REGULATOR(regulator->regulator_name,
&int3472_gpio_regulator_ops,
enable_time, GPIO_REGULATOR_OFF_ON_DELAY);
cfg.dev = &int3472->adev->dev;
cfg.init_data = &init_data;
cfg.ena_gpiod = gpio;
regulator->rdev = regulator_register(int3472->dev, &regulator->rdesc, &cfg);
if (IS_ERR(regulator->rdev))
return PTR_ERR(regulator->rdev);
int3472->n_regulator_gpios++;
return 0;
}
void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472)
{
for (int i = 0; i < int3472->n_regulator_gpios; i++)
regulator_unregister(int3472->regulators[i].rdev);
}
@@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0
/* Author: Dan Scally <djrscally@gmail.com> */
#include <linux/acpi.h>
#include <linux/platform_data/x86/int3472.h>
#include <linux/slab.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;
}
EXPORT_SYMBOL_NS_GPL(skl_int3472_get_acpi_buffer, "INTEL_INT3472");
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;
}
EXPORT_SYMBOL_NS_GPL(skl_int3472_fill_cldb, "INTEL_INT3472");
/* 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_next_consumer_dev(adev, NULL);
if (!sensor) {
dev_err(dev, "INT3472 seems to have no dependents.\n");
return -ENODEV;
}
dev_dbg(dev, "Sensor name %s\n", acpi_dev_name(sensor));
*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;
}
EXPORT_SYMBOL_NS_GPL(skl_int3472_get_sensor_adev_and_name, "INTEL_INT3472");
MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Device Driver library");
MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
MODULE_LICENSE("GPL");
@@ -0,0 +1,531 @@
// SPDX-License-Identifier: GPL-2.0
/* Author: Dan Scally <djrscally@gmail.com> */
#include <linux/acpi.h>
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/machine.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/overflow.h>
#include <linux/platform_data/x86/int3472.h>
#include <linux/platform_device.h>
#include <linux/string_choices.h>
#include <linux/uuid.h>
/*
* 79234640-9e10-4fea-a5c1-b5aa8b19756f
* This _DSM GUID returns information about the GPIO lines mapped to a
* discrete INT3472 device. Function number 1 returns a count of the GPIO
* lines that are mapped. Subsequent functions return 32 bit ints encoding
* information about the GPIO line, including its purpose.
*/
static const guid_t int3472_gpio_guid =
GUID_INIT(0x79234640, 0x9e10, 0x4fea,
0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f);
#define INT3472_GPIO_DSM_TYPE GENMASK(7, 0)
#define INT3472_GPIO_DSM_PIN GENMASK(15, 8)
#define INT3472_GPIO_DSM_SENSOR_ON_VAL GENMASK(31, 24)
/*
* 822ace8f-2814-4174-a56b-5f029fe079ee
* This _DSM GUID returns a string from the sensor device, which acts as a
* module identifier.
*/
static const guid_t cio2_sensor_module_guid =
GUID_INIT(0x822ace8f, 0x2814, 0x4174,
0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee);
static void skl_int3472_log_sensor_module_name(struct int3472_discrete_device *int3472)
{
union acpi_object *obj;
obj = acpi_evaluate_dsm_typed(int3472->sensor->handle,
&cio2_sensor_module_guid, 0x00,
0x01, NULL, ACPI_TYPE_STRING);
if (obj) {
dev_dbg(int3472->dev, "Sensor module id: '%s'\n", obj->string.pointer);
ACPI_FREE(obj);
}
}
static int skl_int3472_fill_gpiod_lookup(struct gpiod_lookup *table_entry,
struct acpi_resource_gpio *agpio,
const char *con_id, unsigned long gpio_flags)
{
char *path = agpio->resource_source.string_ptr;
struct acpi_device *adev;
acpi_handle handle;
acpi_status status;
status = acpi_get_handle(NULL, path, &handle);
if (ACPI_FAILURE(status))
return -EINVAL;
adev = acpi_fetch_acpi_dev(handle);
if (!adev)
return -ENODEV;
*table_entry = GPIO_LOOKUP(acpi_dev_name(adev), agpio->pin_table[0], con_id, gpio_flags);
return 0;
}
static int skl_int3472_map_gpio_to_sensor(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio,
const char *con_id, unsigned long gpio_flags)
{
int ret;
if (int3472->n_sensor_gpios >= INT3472_MAX_SENSOR_GPIOS) {
dev_warn(int3472->dev, "Too many GPIOs mapped\n");
return -EINVAL;
}
ret = skl_int3472_fill_gpiod_lookup(&int3472->gpios.table[int3472->n_sensor_gpios],
agpio, con_id, gpio_flags);
if (ret)
return ret;
int3472->n_sensor_gpios++;
return 0;
}
/* This should *really* only be used when there's no other way... */
static struct gpio_desc *
skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472,
struct acpi_resource_gpio *agpio,
const char *con_id, unsigned long gpio_flags)
{
struct gpio_desc *desc;
int ret;
struct gpiod_lookup_table *lookup __free(kfree) =
kzalloc_flex(*lookup, table, 2);
if (!lookup)
return ERR_PTR(-ENOMEM);
lookup->dev_id = dev_name(int3472->dev);
ret = skl_int3472_fill_gpiod_lookup(&lookup->table[0], agpio, con_id, gpio_flags);
if (ret)
return ERR_PTR(ret);
gpiod_add_lookup_table(lookup);
desc = gpiod_get(int3472->dev, con_id, GPIOD_OUT_LOW);
gpiod_remove_lookup_table(lookup);
return desc;
}
/**
* struct int3472_gpio_map - Map GPIOs to whatever is expected by the
* sensor driver (as in DT bindings)
* @hid: The ACPI HID of the device without the instance number e.g. INT347E
* @type_from: The GPIO type from ACPI ?SDT
* @type_to: The assigned GPIO type, typically same as @type_from
* @enable_time_us: Enable time in usec for GPIOs mapped to regulators
* @con_id: The name of the GPIO for the device
* @polarity_low: GPIO_ACTIVE_LOW true if the @polarity_low is true,
* GPIO_ACTIVE_HIGH otherwise
*/
struct int3472_gpio_map {
const char *hid;
u8 type_from;
u8 type_to;
bool polarity_low;
unsigned int enable_time_us;
const char *con_id;
};
static const struct int3472_gpio_map int3472_gpio_map[] = {
{ /* mt9m114 designs declare a powerdown pin which controls the regulators */
.hid = "INT33F0",
.type_from = INT3472_GPIO_TYPE_POWERDOWN,
.type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
.con_id = "vdd",
.enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
},
{ /* ov7251 driver / DT-bindings expect "enable" as con_id for reset */
.hid = "INT347E",
.type_from = INT3472_GPIO_TYPE_RESET,
.type_to = INT3472_GPIO_TYPE_RESET,
.con_id = "enable",
},
{ /* ov08x40's handshake pin needs a 45 ms delay on some HP laptops */
.hid = "OVTI08F4",
.type_from = INT3472_GPIO_TYPE_HANDSHAKE,
.type_to = INT3472_GPIO_TYPE_HANDSHAKE,
.con_id = "dvdd",
.enable_time_us = 45 * USEC_PER_MSEC,
},
};
static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type,
const char **con_id, unsigned long *gpio_flags,
unsigned int *enable_time_us)
{
struct acpi_device *adev = int3472->sensor;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(int3472_gpio_map); i++) {
/*
* Map the firmware-provided GPIO to whatever a driver expects
* (as in DT bindings). First check if the type matches with the
* GPIO map, then further check that the device _HID matches.
*/
if (*type != int3472_gpio_map[i].type_from)
continue;
if (!acpi_dev_hid_uid_match(adev, int3472_gpio_map[i].hid, NULL))
continue;
dev_dbg(int3472->dev, "mapping type 0x%02x pin to 0x%02x %s\n",
*type, int3472_gpio_map[i].type_to, int3472_gpio_map[i].con_id);
*type = int3472_gpio_map[i].type_to;
*gpio_flags = int3472_gpio_map[i].polarity_low ?
GPIO_ACTIVE_LOW : GPIO_ACTIVE_HIGH;
*con_id = int3472_gpio_map[i].con_id;
*enable_time_us = int3472_gpio_map[i].enable_time_us;
return;
}
*enable_time_us = GPIO_REGULATOR_ENABLE_TIME;
switch (*type) {
case INT3472_GPIO_TYPE_RESET:
*con_id = "reset";
*gpio_flags = GPIO_ACTIVE_LOW;
break;
case INT3472_GPIO_TYPE_POWERDOWN:
*con_id = "powerdown";
*gpio_flags = GPIO_ACTIVE_LOW;
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
*con_id = "clk-enable";
*gpio_flags = GPIO_ACTIVE_HIGH;
break;
case INT3472_GPIO_TYPE_PRIVACY_LED:
*con_id = "privacy";
*gpio_flags = GPIO_ACTIVE_HIGH;
break;
case INT3472_GPIO_TYPE_STROBE:
*con_id = "ir_flood";
*gpio_flags = GPIO_ACTIVE_HIGH;
break;
case INT3472_GPIO_TYPE_HOTPLUG_DETECT:
*con_id = "hpd";
*gpio_flags = GPIO_ACTIVE_HIGH;
break;
case INT3472_GPIO_TYPE_POWER_ENABLE:
*con_id = "avdd";
*gpio_flags = GPIO_ACTIVE_HIGH;
break;
case INT3472_GPIO_TYPE_DOVDD:
*con_id = "dovdd";
*gpio_flags = GPIO_ACTIVE_HIGH;
break;
case INT3472_GPIO_TYPE_HANDSHAKE:
*con_id = "dvdd";
*gpio_flags = GPIO_ACTIVE_HIGH;
/* Setups using a handshake pin need 25 ms enable delay */
*enable_time_us = 25 * USEC_PER_MSEC;
break;
default:
*con_id = "unknown";
*gpio_flags = GPIO_ACTIVE_HIGH;
break;
}
}
/**
* skl_int3472_handle_gpio_resources: Map PMIC resources to consuming sensor
* @ares: A pointer to a &struct acpi_resource
* @data: A pointer to a &struct int3472_discrete_device
*
* This function handles GPIO resources that are against an INT3472
* ACPI device, by checking the value of the corresponding _DSM entry.
* This will return a 32bit int, where the lowest byte represents the
* function of the GPIO pin:
*
* 0x00 Reset
* 0x01 Power down
* 0x02 Strobe
* 0x0b Power enable
* 0x0c Clock enable
* 0x0d Privacy LED
* 0x10 DOVDD (digital I/O voltage)
* 0x13 Hotplug detect
*
* There are some known platform specific quirks where that does not quite
* hold up; for example where a pin with type 0x01 (Power down) is mapped to
* a sensor pin that performs a reset function or entries in _CRS and _DSM that
* do not actually correspond to a physical connection. These will be handled
* by the mapping sub-functions.
*
* GPIOs will either be mapped directly to the sensor device or else used
* to create clocks and regulators via the usual frameworks.
*
* Return:
* * 1 - Continue the loop without adding a copy of the resource to
* * the list passed to acpi_dev_get_resources()
* * 0 - Continue the loop after adding a copy of the resource to
* * the list passed to acpi_dev_get_resources()
* * -errno - Error, break loop
*/
static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
void *data)
{
struct int3472_discrete_device *int3472 = data;
const char *second_sensor = NULL;
struct acpi_resource_gpio *agpio;
unsigned int enable_time_us;
u8 active_value, pin, type;
unsigned long gpio_flags;
union acpi_object *obj;
struct gpio_desc *gpio;
const char *err_msg;
const char *con_id;
int ret;
if (!acpi_gpio_get_io_resource(ares, &agpio))
return 1;
/*
* ngpios + 2 because the index of this _DSM function is 1-based and
* the first function is just a count.
*/
obj = acpi_evaluate_dsm_typed(int3472->adev->handle,
&int3472_gpio_guid, 0x00,
int3472->ngpios + 2,
NULL, ACPI_TYPE_INTEGER);
if (!obj) {
dev_warn(int3472->dev, "No _DSM entry for GPIO pin %u\n",
agpio->pin_table[0]);
return 1;
}
type = FIELD_GET(INT3472_GPIO_DSM_TYPE, obj->integer.value);
int3472_get_con_id_and_polarity(int3472, &type, &con_id, &gpio_flags, &enable_time_us);
pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj->integer.value);
/* Pin field is not really used under Windows and wraps around at 8 bits */
if (pin != (agpio->pin_table[0] & 0xff))
dev_dbg(int3472->dev, FW_BUG "%s %s pin number mismatch _DSM %d resource %d\n",
con_id, agpio->resource_source.string_ptr, pin, agpio->pin_table[0]);
active_value = FIELD_GET(INT3472_GPIO_DSM_SENSOR_ON_VAL, obj->integer.value);
if (!active_value)
gpio_flags ^= GPIO_ACTIVE_LOW;
dev_dbg(int3472->dev, "%s %s pin %d active-%s\n", con_id,
agpio->resource_source.string_ptr, agpio->pin_table[0],
str_high_low(gpio_flags == GPIO_ACTIVE_HIGH));
switch (type) {
case INT3472_GPIO_TYPE_RESET:
case INT3472_GPIO_TYPE_POWERDOWN:
case INT3472_GPIO_TYPE_HOTPLUG_DETECT:
ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, con_id, gpio_flags);
if (ret)
err_msg = "Failed to map GPIO pin to sensor\n";
break;
case INT3472_GPIO_TYPE_CLK_ENABLE:
case INT3472_GPIO_TYPE_PRIVACY_LED:
case INT3472_GPIO_TYPE_STROBE:
case INT3472_GPIO_TYPE_POWER_ENABLE:
case INT3472_GPIO_TYPE_DOVDD:
case INT3472_GPIO_TYPE_HANDSHAKE:
gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, con_id, gpio_flags);
if (IS_ERR(gpio)) {
ret = PTR_ERR(gpio);
err_msg = "Failed to get GPIO\n";
break;
}
switch (type) {
case INT3472_GPIO_TYPE_CLK_ENABLE:
ret = skl_int3472_register_gpio_clock(int3472, gpio);
if (ret)
err_msg = "Failed to register clock\n";
break;
case INT3472_GPIO_TYPE_PRIVACY_LED:
case INT3472_GPIO_TYPE_STROBE:
ret = skl_int3472_register_led(int3472, gpio, con_id);
if (ret)
err_msg = "Failed to register LED\n";
break;
case INT3472_GPIO_TYPE_POWER_ENABLE:
second_sensor = int3472->quirks.avdd_second_sensor;
fallthrough;
case INT3472_GPIO_TYPE_DOVDD:
case INT3472_GPIO_TYPE_HANDSHAKE:
ret = skl_int3472_register_regulator(int3472, gpio, enable_time_us,
con_id, second_sensor);
if (ret)
err_msg = "Failed to register regulator\n";
break;
default: /* Never reached */
ret = -EINVAL;
break;
}
if (ret)
gpiod_put(gpio);
break;
default:
dev_warn(int3472->dev,
"GPIO type 0x%02x unknown; the sensor may not work\n",
type);
ret = 1;
break;
}
int3472->ngpios++;
ACPI_FREE(obj);
if (ret < 0)
return dev_err_probe(int3472->dev, ret, err_msg);
/* Tell acpi_dev_get_resources() to not make a copy of the resource */
return 1;
}
int int3472_discrete_parse_crs(struct int3472_discrete_device *int3472)
{
LIST_HEAD(resource_list);
int ret;
skl_int3472_log_sensor_module_name(int3472);
ret = acpi_dev_get_resources(int3472->adev, &resource_list,
skl_int3472_handle_gpio_resources,
int3472);
if (ret < 0)
return ret;
acpi_dev_free_resource_list(&resource_list);
/* Register _DSM based clock (no-op if a GPIO clock was already registered) */
ret = skl_int3472_register_dsm_clock(int3472);
if (ret < 0)
return ret;
int3472->gpios.dev_id = int3472->sensor_name;
gpiod_add_lookup_table(&int3472->gpios);
return 0;
}
EXPORT_SYMBOL_NS_GPL(int3472_discrete_parse_crs, "INTEL_INT3472_DISCRETE");
void int3472_discrete_cleanup(struct int3472_discrete_device *int3472)
{
gpiod_remove_lookup_table(&int3472->gpios);
skl_int3472_unregister_clock(int3472);
skl_int3472_unregister_leds(int3472);
skl_int3472_unregister_regulator(int3472);
}
EXPORT_SYMBOL_NS_GPL(int3472_discrete_cleanup, "INTEL_INT3472_DISCRETE");
static void skl_int3472_discrete_remove(struct platform_device *pdev)
{
int3472_discrete_cleanup(platform_get_drvdata(pdev));
}
static int skl_int3472_discrete_probe(struct platform_device *pdev)
{
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
const struct int3472_discrete_quirks *quirks = NULL;
struct int3472_discrete_device *int3472;
const struct dmi_system_id *id;
struct int3472_cldb cldb;
int ret;
if (!adev)
return -ENODEV;
id = dmi_first_match(skl_int3472_discrete_quirks);
if (id)
quirks = id->driver_data;
ret = skl_int3472_fill_cldb(adev, &cldb);
if (ret) {
dev_err(&pdev->dev, "Couldn't fill CLDB structure\n");
return ret;
}
if (cldb.control_logic_type != 1) {
dev_err(&pdev->dev, "Unsupported control logic type %u\n",
cldb.control_logic_type);
return -EINVAL;
}
/* Max num GPIOs we've seen plus a terminator */
int3472 = devm_kzalloc(&pdev->dev, struct_size(int3472, gpios.table,
INT3472_MAX_SENSOR_GPIOS + 1), GFP_KERNEL);
if (!int3472)
return -ENOMEM;
int3472->adev = adev;
int3472->dev = &pdev->dev;
platform_set_drvdata(pdev, int3472);
int3472->clock.imgclk_index = cldb.clock_source;
if (quirks)
int3472->quirks = *quirks;
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()
* in failure paths without issue.
*/
INIT_LIST_HEAD(&int3472->gpios.list);
ret = int3472_discrete_parse_crs(int3472);
if (ret) {
skl_int3472_discrete_remove(pdev);
return ret;
}
acpi_dev_clear_dependencies(adev);
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");
MODULE_IMPORT_NS("INTEL_INT3472");
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: GPL-2.0
/* Author: Hans de Goede <hansg@kernel.org> */
#include <linux/dmi.h>
#include <linux/platform_data/x86/int3472.h>
static const struct int3472_discrete_quirks lenovo_miix_510_quirks = {
.avdd_second_sensor = "i2c-OVTI2680:00",
};
const struct dmi_system_id skl_int3472_discrete_quirks[] = {
{
/* Lenovo Miix 510-12IKB */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_VERSION, "MIIX 510-12IKB"),
},
.driver_data = (void *)&lenovo_miix_510_quirks,
},
{ }
};
@@ -0,0 +1,63 @@
// 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 <linux/platform_data/x86/int3472.h>
static int int3472_led_set(struct led_classdev *led_cdev, enum led_brightness brightness)
{
struct int3472_led *led = container_of(led_cdev, struct int3472_led, classdev);
gpiod_set_value_cansleep(led->gpio, brightness);
return 0;
}
int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio,
const char *con_id)
{
struct int3472_led *led;
char *p;
int ret;
if (int3472->n_leds >= INT3472_MAX_LEDS)
return -ENOSPC;
led = &int3472->leds[int3472->n_leds];
led->gpio = gpio;
/* Generate the name, replacing the ':' in the ACPI devname with '_' */
snprintf(led->name, sizeof(led->name),
"%s::%s_led", acpi_dev_name(int3472->sensor), con_id);
p = strchr(led->name, ':');
if (p)
*p = '_';
led->classdev.name = led->name;
led->classdev.max_brightness = 1;
led->classdev.brightness_set_blocking = int3472_led_set;
ret = led_classdev_register(int3472->dev, &led->classdev);
if (ret)
return ret;
led->lookup.provider = led->name;
led->lookup.dev_id = int3472->sensor_name;
led->lookup.con_id = con_id;
led_add_lookup(&led->lookup);
int3472->n_leds++;
return 0;
}
void skl_int3472_unregister_leds(struct int3472_discrete_device *int3472)
{
for (unsigned int i = 0; i < int3472->n_leds; i++) {
struct int3472_led *led = &int3472->leds[i];
led_remove_lookup(&led->lookup);
led_classdev_unregister(&led->classdev);
gpiod_put(led->gpio);
}
}
@@ -0,0 +1,266 @@
// SPDX-License-Identifier: GPL-2.0
/* Author: Dan Scally <djrscally@gmail.com> */
#include <linux/acpi.h>
#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/platform_data/x86/int3472.h>
#include <linux/regmap.h>
#include <linux/string.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 regmap_config tps68470_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = TPS68470_REG_MAX,
};
static int tps68470_chip_init(struct device *dev, struct regmap *regmap)
{
unsigned int version;
int ret;
/* Force software reset */
ret = regmap_write(regmap, TPS68470_REG_RESET, TPS68470_REG_RESET_MASK);
if (ret)
return ret;
ret = regmap_read(regmap, TPS68470_REG_REVID, &version);
if (ret) {
dev_err(dev, "Failed to read revision register: %d\n", ret);
return ret;
}
dev_info(dev, "TPS68470 REVID: 0x%02x\n", version);
return 0;
}
/** skl_int3472_tps68470_calc_type: Check what platform a device is designed for
* @adev: A pointer to a &struct acpi_device
*
* Check CLDB buffer against the PMIC's adev. If present, then we check
* the value of control_logic_type field and follow one of the
* following scenarios:
*
* 1. No CLDB - likely ACPI tables designed for ChromeOS. We
* create platform devices for the GPIOs and OpRegion drivers.
*
* 2. CLDB, with control_logic_type = 2 - probably ACPI tables
* made for Windows 2-in-1 platforms. Register pdevs for GPIO,
* Clock and Regulator drivers to bind to.
*
* 3. Any other value in control_logic_type, we should never have
* gotten to this point; fail probe and return.
*
* Return:
* * 1 Device intended for ChromeOS
* * 2 Device intended for Windows
* * -EINVAL Where @adev has an object named CLDB but it does not conform to
* our expectations
*/
static int skl_int3472_tps68470_calc_type(struct acpi_device *adev)
{
struct int3472_cldb cldb = { 0 };
int ret;
/*
* A CLDB buffer that exists, but which does not match our expectations
* should trigger an error so we don't blindly continue.
*/
ret = skl_int3472_fill_cldb(adev, &cldb);
if (ret && ret != -ENODEV)
return ret;
if (ret)
return DESIGNED_FOR_CHROMEOS;
if (cldb.control_logic_type != 2)
return -EINVAL;
return DESIGNED_FOR_WINDOWS;
}
/*
* Return the size of the flexible array member, because we'll need that later
* on to pass .pdata_size to cells.
*/
static int
skl_int3472_fill_clk_pdata(struct device *dev, struct tps68470_clk_platform_data **clk_pdata)
{
struct acpi_device *adev = ACPI_COMPANION(dev);
struct acpi_device *consumer;
unsigned int n_consumers = 0;
const char *sensor_name;
unsigned int i = 0;
for_each_acpi_consumer_dev(adev, consumer)
n_consumers++;
if (!n_consumers) {
dev_err(dev, "INT3472 seems to have no dependents\n");
return -ENODEV;
}
*clk_pdata = devm_kzalloc(dev, struct_size(*clk_pdata, consumers, n_consumers),
GFP_KERNEL);
if (!*clk_pdata)
return -ENOMEM;
(*clk_pdata)->n_consumers = n_consumers;
i = 0;
for_each_acpi_consumer_dev(adev, consumer) {
sensor_name = devm_kasprintf(dev, GFP_KERNEL, I2C_DEV_NAME_FORMAT,
acpi_dev_name(consumer));
if (!sensor_name) {
acpi_dev_put(consumer);
return -ENOMEM;
}
(*clk_pdata)->consumers[i].consumer_dev_name = sensor_name;
i++;
}
return n_consumers;
}
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 n_consumers;
int device_type;
int ret;
int i;
if (!adev)
return -ENODEV;
n_consumers = skl_int3472_fill_clk_pdata(&client->dev, &clk_pdata);
if (n_consumers < 0)
return n_consumers;
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));
return PTR_ERR(regmap);
}
i2c_set_clientdata(client, regmap);
ret = tps68470_chip_init(&client->dev, regmap);
if (ret < 0) {
dev_err(&client->dev, "TPS68470 init error %d\n", ret);
return ret;
}
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 = kzalloc_objs(*cells, TPS68470_WIN_MFD_CELL_COUNT);
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 = struct_size(clk_pdata, consumers, n_consumers);
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";
cells[2].swnode = board_data->tps68470_gpio_swnode;
for (i = 0; i < board_data->n_gpiod_lookups; i++)
gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_tables[i]);
ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
cells, TPS68470_WIN_MFD_CELL_COUNT,
NULL, 0, NULL);
kfree(cells);
if (ret) {
for (i = 0; i < board_data->n_gpiod_lookups; i++)
gpiod_remove_lookup_table(board_data->tps68470_gpio_lookup_tables[i]);
}
break;
case DESIGNED_FOR_CHROMEOS:
ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
tps68470_cros, ARRAY_SIZE(tps68470_cros),
NULL, 0, NULL);
break;
default:
dev_err(&client->dev, "Failed to add MFD devices\n");
return device_type;
}
/*
* No acpi_dev_clear_dependencies() here, since the acpi_gpiochip_add()
* for the GPIO cell already does this.
*/
return ret;
}
static void skl_int3472_tps68470_remove(struct i2c_client *client)
{
const struct int3472_tps68470_board_data *board_data;
int i;
board_data = int3472_tps68470_get_board_data(dev_name(&client->dev));
if (board_data) {
for (i = 0; i < board_data->n_gpiod_lookups; i++)
gpiod_remove_lookup_table(board_data->tps68470_gpio_lookup_tables[i]);
}
}
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 = 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_IMPORT_NS("INTEL_INT3472");
MODULE_SOFTDEP("pre: clk-tps68470 tps68470-regulator");
@@ -0,0 +1,27 @@
/* 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;
const struct tps68470_regulator_platform_data *tps68470_regulator_pdata;
const struct software_node *tps68470_gpio_swnode;
unsigned int n_gpiod_lookups;
struct gpiod_lookup_table *tps68470_gpio_lookup_tables[];
};
const struct int3472_tps68470_board_data *int3472_tps68470_get_board_data(const char *dev_name);
#endif
@@ -0,0 +1,549 @@
// 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/property.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"),
REGULATOR_SUPPLY("vddd", "i2c-INT347E:00"),
};
static struct regulator_consumer_supply int347a_aux1_consumer_supplies[] = {
REGULATOR_SUPPLY("vdda", "i2c-INT347E:00"),
};
static struct regulator_consumer_supply int347a_aux2_consumer_supplies[] = {
REGULATOR_SUPPLY("vdddo", "i2c-INT347E:00"),
};
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 regulator_init_data surface_go_tps68470_aux1_reg_init_data = {
.constraints = {
.min_uV = 2815200,
.max_uV = 2815200,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(int347a_aux1_consumer_supplies),
.consumer_supplies = int347a_aux1_consumer_supplies,
};
static const struct regulator_init_data surface_go_tps68470_aux2_reg_init_data = {
.constraints = {
.min_uV = 1800600,
.max_uV = 1800600,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(int347a_aux2_consumer_supplies),
.consumer_supplies = int347a_aux2_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,
[TPS68470_AUX1] = &surface_go_tps68470_aux1_reg_init_data,
[TPS68470_AUX2] = &surface_go_tps68470_aux2_reg_init_data,
},
};
/* Settings for Dell 7212 Tablet */
static struct regulator_consumer_supply int3479_vsio_consumer_supplies[] = {
REGULATOR_SUPPLY("avdd", "i2c-INT3479:00"),
};
static struct regulator_consumer_supply int3479_aux1_consumer_supplies[] = {
REGULATOR_SUPPLY("dvdd", "i2c-INT3479:00"),
};
static struct regulator_consumer_supply int3479_aux2_consumer_supplies[] = {
REGULATOR_SUPPLY("dovdd", "i2c-INT3479:00"),
};
static const struct regulator_init_data dell_7212_tps68470_core_reg_init_data = {
.constraints = {
.min_uV = 1200000,
.max_uV = 1200000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
};
static const struct regulator_init_data dell_7212_tps68470_ana_reg_init_data = {
.constraints = {
.min_uV = 2815200,
.max_uV = 2815200,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
};
static const struct regulator_init_data dell_7212_tps68470_vcm_reg_init_data = {
.constraints = {
.min_uV = 2815200,
.max_uV = 2815200,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
};
static const struct regulator_init_data dell_7212_tps68470_vio_reg_init_data = {
.constraints = {
.min_uV = 1800600,
.max_uV = 1800600,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
};
static const struct regulator_init_data dell_7212_tps68470_vsio_reg_init_data = {
.constraints = {
.min_uV = 1800600,
.max_uV = 1800600,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(int3479_vsio_consumer_supplies),
.consumer_supplies = int3479_vsio_consumer_supplies,
};
static const struct regulator_init_data dell_7212_tps68470_aux1_reg_init_data = {
.constraints = {
.min_uV = 1213200,
.max_uV = 1213200,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(int3479_aux1_consumer_supplies),
.consumer_supplies = int3479_aux1_consumer_supplies,
};
static const struct regulator_init_data dell_7212_tps68470_aux2_reg_init_data = {
.constraints = {
.min_uV = 1800600,
.max_uV = 1800600,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(int3479_aux2_consumer_supplies),
.consumer_supplies = int3479_aux2_consumer_supplies,
};
static const struct tps68470_regulator_platform_data dell_7212_tps68470_pdata = {
.reg_init_data = {
[TPS68470_CORE] = &dell_7212_tps68470_core_reg_init_data,
[TPS68470_ANA] = &dell_7212_tps68470_ana_reg_init_data,
[TPS68470_VCM] = &dell_7212_tps68470_vcm_reg_init_data,
[TPS68470_VIO] = &dell_7212_tps68470_vio_reg_init_data,
[TPS68470_VSIO] = &dell_7212_tps68470_vsio_reg_init_data,
[TPS68470_AUX1] = &dell_7212_tps68470_aux1_reg_init_data,
[TPS68470_AUX2] = &dell_7212_tps68470_aux2_reg_init_data,
},
};
/* Settings for MSI Prestige 14 AI+ Evo C2VMG laptop. */
static struct regulator_consumer_supply ovti5675_avdd_consumer_supplies[] = {
REGULATOR_SUPPLY("avdd", "i2c-OVTI5675:00"),
};
static struct regulator_consumer_supply ovti5675_dovdd_consumer_supplies[] = {
REGULATOR_SUPPLY("dovdd", "i2c-OVTI5675:00"),
};
static struct regulator_consumer_supply ovti5675_dvdd_consumer_supplies[] = {
REGULATOR_SUPPLY("dvdd", "i2c-OVTI5675:00"),
};
static const struct regulator_init_data msi_prestige_ai_evo_tps68470_core_reg_init_data = {
.constraints = {
.min_uV = 1200000,
.max_uV = 1200000,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(ovti5675_dvdd_consumer_supplies),
.consumer_supplies = ovti5675_dvdd_consumer_supplies,
};
static const struct regulator_init_data msi_prestige_ai_evo_tps68470_ana_reg_init_data = {
.constraints = {
.min_uV = 2815200,
.max_uV = 2815200,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(ovti5675_avdd_consumer_supplies),
.consumer_supplies = ovti5675_avdd_consumer_supplies,
};
static const struct regulator_init_data msi_prestige_ai_evo_tps68470_vio_reg_init_data = {
.constraints = {
.min_uV = 1800600,
.max_uV = 1800600,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
};
static const struct regulator_init_data msi_prestige_ai_evo_tps68470_vsio_reg_init_data = {
.constraints = {
.min_uV = 1800600,
.max_uV = 1800600,
.apply_uV = 1,
.valid_ops_mask = REGULATOR_CHANGE_STATUS,
},
.num_consumer_supplies = ARRAY_SIZE(ovti5675_dovdd_consumer_supplies),
.consumer_supplies = ovti5675_dovdd_consumer_supplies,
};
static const struct tps68470_regulator_platform_data msi_prestige_ai_evo_tps68470_pdata = {
.reg_init_data = {
[TPS68470_CORE] = &msi_prestige_ai_evo_tps68470_core_reg_init_data,
[TPS68470_ANA] = &msi_prestige_ai_evo_tps68470_ana_reg_init_data,
[TPS68470_VIO] = &msi_prestige_ai_evo_tps68470_vio_reg_init_data,
[TPS68470_VSIO] = &msi_prestige_ai_evo_tps68470_vsio_reg_init_data,
},
};
/* Settings for Intel NVL platform */
static struct regulator_consumer_supply ovti13b1_core_consumer_supplies[] = {
REGULATOR_SUPPLY("dvdd", "i2c-OVTI13B1:01"),
};
static struct regulator_consumer_supply ovti13b1_ana_consumer_supplies[] = {
REGULATOR_SUPPLY("avdd", "i2c-OVTI13B1:01"),
};
static struct regulator_consumer_supply ovti13b1_vcm_consumer_supplies[] = {
REGULATOR_SUPPLY("vcc", "i2c-OVTI13B1:01-VCM"),
};
static struct regulator_consumer_supply ovti13b1_vsio_consumer_supplies[] = {
REGULATOR_SUPPLY("dovdd", "i2c-OVTI13B1:01"),
};
static const struct regulator_init_data intel_nvl_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(ovti13b1_core_consumer_supplies),
.consumer_supplies = ovti13b1_core_consumer_supplies,
};
static const struct regulator_init_data intel_nvl_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(ovti13b1_ana_consumer_supplies),
.consumer_supplies = ovti13b1_ana_consumer_supplies,
};
static const struct regulator_init_data intel_nvl_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(ovti13b1_vcm_consumer_supplies),
.consumer_supplies = ovti13b1_vcm_consumer_supplies,
};
/* Ensure the always-on VIO regulator has the same voltage as VSIO */
static const struct regulator_init_data intel_nvl_tps68470_vio_reg_init_data = {
.constraints = {
.min_uV = 1800600,
.max_uV = 1800600,
.apply_uV = true,
.always_on = true,
},
};
static const struct regulator_init_data intel_nvl_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(ovti13b1_vsio_consumer_supplies),
.consumer_supplies = ovti13b1_vsio_consumer_supplies,
};
static const struct tps68470_regulator_platform_data intel_nvl_tps68470_pdata = {
.reg_init_data = {
[TPS68470_CORE] = &intel_nvl_tps68470_core_reg_init_data,
[TPS68470_ANA] = &intel_nvl_tps68470_ana_reg_init_data,
[TPS68470_VCM] = &intel_nvl_tps68470_vcm_reg_init_data,
[TPS68470_VIO] = &intel_nvl_tps68470_vio_reg_init_data,
[TPS68470_VSIO] = &intel_nvl_tps68470_vsio_reg_init_data,
},
};
static struct gpiod_lookup_table surface_go_int347a_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 struct gpiod_lookup_table surface_go_int347e_gpios = {
.dev_id = "i2c-INT347E:00",
.table = {
GPIO_LOOKUP("tps68470-gpio", 5, "enable", GPIO_ACTIVE_HIGH),
{ }
}
};
static struct gpiod_lookup_table dell_7212_int3479_gpios = {
.dev_id = "i2c-INT3479:00",
.table = {
GPIO_LOOKUP("tps68470-gpio", 3, "reset", GPIO_ACTIVE_LOW),
GPIO_LOOKUP("tps68470-gpio", 4, "powerdown", GPIO_ACTIVE_LOW),
{ }
}
};
static struct gpiod_lookup_table msi_prestige_ai_evo_ovti5675_gpios = {
.dev_id = "i2c-OVTI5675:00",
.table = {
GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW),
{ }
}
};
static struct gpiod_lookup_table intel_nvl_tps68470_gpios = {
.dev_id = "i2c-OVTI13B1:01",
.table = {
GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW),
{ }
}
};
static const struct property_entry int3472_tps68470_daisy_chain_gpio_props[] = {
PROPERTY_ENTRY_BOOL("daisy-chain-enable"),
{ }
};
static const struct software_node int3472_tps68470_daisy_chain_gpio_swnode = {
.properties = int3472_tps68470_daisy_chain_gpio_props,
};
static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = {
.dev_name = "i2c-INT3472:05",
.tps68470_regulator_pdata = &surface_go_tps68470_pdata,
.n_gpiod_lookups = 2,
.tps68470_gpio_lookup_tables = {
&surface_go_int347a_gpios,
&surface_go_int347e_gpios,
},
};
static const struct int3472_tps68470_board_data surface_go3_tps68470_board_data = {
.dev_name = "i2c-INT3472:01",
.tps68470_regulator_pdata = &surface_go_tps68470_pdata,
.n_gpiod_lookups = 2,
.tps68470_gpio_lookup_tables = {
&surface_go_int347a_gpios,
&surface_go_int347e_gpios,
},
};
static const struct int3472_tps68470_board_data dell_7212_tps68470_board_data = {
.dev_name = "i2c-INT3472:05",
.tps68470_regulator_pdata = &dell_7212_tps68470_pdata,
.n_gpiod_lookups = 1,
.tps68470_gpio_lookup_tables = {
&dell_7212_int3479_gpios,
},
};
static const struct int3472_tps68470_board_data msi_prestige_ai_evo_tps68470_board_data = {
.dev_name = "i2c-INT3472:06",
.tps68470_regulator_pdata = &msi_prestige_ai_evo_tps68470_pdata,
.tps68470_gpio_swnode = &int3472_tps68470_daisy_chain_gpio_swnode,
.n_gpiod_lookups = 1,
.tps68470_gpio_lookup_tables = {
&msi_prestige_ai_evo_ovti5675_gpios,
},
};
static const struct int3472_tps68470_board_data intel_nvl_tps68470_board_data = {
.dev_name = "i2c-INT3472:04",
.tps68470_regulator_pdata = &intel_nvl_tps68470_pdata,
.tps68470_gpio_swnode = &int3472_tps68470_daisy_chain_gpio_swnode,
.n_gpiod_lookups = 1,
.tps68470_gpio_lookup_tables = {
&intel_nvl_tps68470_gpios,
},
};
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,
},
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 7212 Rugged Extreme Tablet"),
},
.driver_data = (void *)&dell_7212_tps68470_board_data,
},
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 13 AI+ Evo A2VMG"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-13Q3"),
},
.driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data,
},
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 14 AI+ Evo C2VMG"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-14N3"),
},
.driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data,
},
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 16 AI+ Evo B2VMG"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-15A3"),
},
.driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data,
},
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Nova Lake Client Platform"),
},
.driver_data = (void *)&intel_nvl_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;
}