- Fix ACPI device object reference counting in (recently updated) skl_int3472_fill_clk_pdata() (Andy Shevchenko). - Fix a memory leak in APEI by avoiding to add a task_work to kernel threads running when an asynchronous error is detected (Shuai Xue). - Add ACPI support for handling system wakeups via GPIO wake capable IRQs in addition to GPEs (Raul E Rangel). - Make the system reboot code put ACPI-enabled systems into the S5 (system off) state which is necessary for some platforms to work as expected (Kai-Heng Feng). - Make the white space usage in the ACPI thermal driver more consistent and drop redundant code from it (Rafael Wysocki). -----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEE4fcc61cGeeHD/fCwgsRv/nhiVHEFAmNEVJMSHHJqd0Byand5 c29ja2kubmV0AAoJEILEb/54YlRxQ1YP/0abCMN+M3TVoLC2CKr4ejVUbDlzpFpx NN7CQVl6rCO68XN3JiYjlxasEl3gWI2SKNsjbN8flIXsIYh1WYgDaEHlN1urIzXW 0dpMa8lryMashWYYt5FNv05vRW33xta4HwmnHwqr8ASp7MClvB9I+s94CMZryeEh UTqIu9HPJ/I8VZlNgZHHisQkL1IOT+3QRbVMJr7QMkAKy01UiJco0e962T+h3nBU RPSg0FctjorfCnPO5hmjVA99AAOskfB1HSh3CRYy9TSrx4MFEAsttxNeEofNHGTn Gyjgugy5DJYvvfe0A/BgWWqwiUXoLmfRIjeARuv+JL1vPQfSJaF7PqnNPPAhexLG 62wF1CE38zKSbbKAg3fNywXQnXnuBLPlNTUxt3tH3PBdI+un1zuH5/9C0YgkRlRO Pwv3tAs52HKFIqenHz2axxZQrQObrHvSiJJN+q1TlURt5QHyb1n6FJYg/1MCQ6fz 5etirjbptTD+q2YAqDodGge9HaHvKndrUi3oAs0wsKWFKVlawtqzGPeqNkRNTIYt RU4kEtCcOkUgwNmKRRly4nWf2UgF/YxGugX1FW3Y7p6nHj7VY7BnaQaTXbtLaTXU PTbT7GZKSIcGQO1xtxDSL3uXYVurqyr4LtM8JBbJSjbwNKAJuVpzgx6iDdg08vVc mg/pOlmgMpoO =pwnm -----END PGP SIGNATURE----- Merge tag 'acpi-6.1-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm Pull more ACPI updates from Rafael Wysocki: "These fix two issues, in APEI and in the int3472 driver, clean up the ACPI thermal driver, add ACPI support for non-GPE system wakeup events and make the system reboot code use the S5 (system off) state by default. Specifics: - Fix ACPI device object reference counting in (recently updated) skl_int3472_fill_clk_pdata() (Andy Shevchenko). - Fix a memory leak in APEI by avoiding to add a task_work to kernel threads running when an asynchronous error is detected (Shuai Xue). - Add ACPI support for handling system wakeups via GPIO wake capable IRQs in addition to GPEs (Raul E Rangel). - Make the system reboot code put ACPI-enabled systems into the S5 (system off) state which is necessary for some platforms to work as expected (Kai-Heng Feng). - Make the white space usage in the ACPI thermal driver more consistent and drop redundant code from it (Rafael Wysocki)" * tag 'acpi-6.1-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: ACPI: thermal: Drop some redundant code ACPI: thermal: Drop redundant parens from expressions ACPI: thermal: Use white space more consistently platform/x86: int3472: Don't leak reference on error ACPI: APEI: do not add task_work to kernel thread to avoid memory leak PM: ACPI: reboot: Reinstate S5 for reboot kernel/reboot: Add SYS_OFF_MODE_RESTART_PREPARE mode ACPI: PM: Take wake IRQ into consideration when entering suspend-to-idle i2c: acpi: Use ACPI wake capability bit to set wake_irq ACPI: resources: Add wake_capable parameter to acpi_dev_irq_flags gpiolib: acpi: Add wake_capable variants of acpi_dev_gpio_irq_get
262 lines
7.1 KiB
C
262 lines
7.1 KiB
C
// 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/regmap.h>
|
|
#include <linux/string.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 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;
|
|
|
|
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 = 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 = 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";
|
|
|
|
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_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");
|