cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tps68470.c (5964B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* Author: Dan Scally <djrscally@gmail.com> */
      3
      4#include <linux/i2c.h>
      5#include <linux/kernel.h>
      6#include <linux/mfd/core.h>
      7#include <linux/mfd/tps68470.h>
      8#include <linux/platform_device.h>
      9#include <linux/platform_data/tps68470.h>
     10#include <linux/regmap.h>
     11#include <linux/string.h>
     12
     13#include "common.h"
     14#include "tps68470.h"
     15
     16#define DESIGNED_FOR_CHROMEOS		1
     17#define DESIGNED_FOR_WINDOWS		2
     18
     19#define TPS68470_WIN_MFD_CELL_COUNT	3
     20
     21static const struct mfd_cell tps68470_cros[] = {
     22	{ .name = "tps68470-gpio" },
     23	{ .name = "tps68470_pmic_opregion" },
     24};
     25
     26static const struct regmap_config tps68470_regmap_config = {
     27	.reg_bits = 8,
     28	.val_bits = 8,
     29	.max_register = TPS68470_REG_MAX,
     30};
     31
     32static int tps68470_chip_init(struct device *dev, struct regmap *regmap)
     33{
     34	unsigned int version;
     35	int ret;
     36
     37	/* Force software reset */
     38	ret = regmap_write(regmap, TPS68470_REG_RESET, TPS68470_REG_RESET_MASK);
     39	if (ret)
     40		return ret;
     41
     42	ret = regmap_read(regmap, TPS68470_REG_REVID, &version);
     43	if (ret) {
     44		dev_err(dev, "Failed to read revision register: %d\n", ret);
     45		return ret;
     46	}
     47
     48	dev_info(dev, "TPS68470 REVID: 0x%02x\n", version);
     49
     50	return 0;
     51}
     52
     53/** skl_int3472_tps68470_calc_type: Check what platform a device is designed for
     54 * @adev: A pointer to a &struct acpi_device
     55 *
     56 * Check CLDB buffer against the PMIC's adev. If present, then we check
     57 * the value of control_logic_type field and follow one of the
     58 * following scenarios:
     59 *
     60 *	1. No CLDB - likely ACPI tables designed for ChromeOS. We
     61 *	create platform devices for the GPIOs and OpRegion drivers.
     62 *
     63 *	2. CLDB, with control_logic_type = 2 - probably ACPI tables
     64 *	made for Windows 2-in-1 platforms. Register pdevs for GPIO,
     65 *	Clock and Regulator drivers to bind to.
     66 *
     67 *	3. Any other value in control_logic_type, we should never have
     68 *	gotten to this point; fail probe and return.
     69 *
     70 * Return:
     71 * * 1		Device intended for ChromeOS
     72 * * 2		Device intended for Windows
     73 * * -EINVAL	Where @adev has an object named CLDB but it does not conform to
     74 *		our expectations
     75 */
     76static int skl_int3472_tps68470_calc_type(struct acpi_device *adev)
     77{
     78	struct int3472_cldb cldb = { 0 };
     79	int ret;
     80
     81	/*
     82	 * A CLDB buffer that exists, but which does not match our expectations
     83	 * should trigger an error so we don't blindly continue.
     84	 */
     85	ret = skl_int3472_fill_cldb(adev, &cldb);
     86	if (ret && ret != -ENODEV)
     87		return ret;
     88
     89	if (ret)
     90		return DESIGNED_FOR_CHROMEOS;
     91
     92	if (cldb.control_logic_type != 2)
     93		return -EINVAL;
     94
     95	return DESIGNED_FOR_WINDOWS;
     96}
     97
     98static int skl_int3472_tps68470_probe(struct i2c_client *client)
     99{
    100	struct acpi_device *adev = ACPI_COMPANION(&client->dev);
    101	const struct int3472_tps68470_board_data *board_data;
    102	struct tps68470_clk_platform_data clk_pdata = {};
    103	struct mfd_cell *cells;
    104	struct regmap *regmap;
    105	int device_type;
    106	int ret;
    107
    108	ret = skl_int3472_get_sensor_adev_and_name(&client->dev, NULL,
    109						   &clk_pdata.consumer_dev_name);
    110	if (ret)
    111		return ret;
    112
    113	regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config);
    114	if (IS_ERR(regmap)) {
    115		dev_err(&client->dev, "Failed to create regmap: %ld\n", PTR_ERR(regmap));
    116		return PTR_ERR(regmap);
    117	}
    118
    119	i2c_set_clientdata(client, regmap);
    120
    121	ret = tps68470_chip_init(&client->dev, regmap);
    122	if (ret < 0) {
    123		dev_err(&client->dev, "TPS68470 init error %d\n", ret);
    124		return ret;
    125	}
    126
    127	device_type = skl_int3472_tps68470_calc_type(adev);
    128	switch (device_type) {
    129	case DESIGNED_FOR_WINDOWS:
    130		board_data = int3472_tps68470_get_board_data(dev_name(&client->dev));
    131		if (!board_data)
    132			return dev_err_probe(&client->dev, -ENODEV, "No board-data found for this model\n");
    133
    134		cells = kcalloc(TPS68470_WIN_MFD_CELL_COUNT, sizeof(*cells), GFP_KERNEL);
    135		if (!cells)
    136			return -ENOMEM;
    137
    138		/*
    139		 * The order of the cells matters here! The clk must be first
    140		 * because the regulator depends on it. The gpios must be last,
    141		 * acpi_gpiochip_add() calls acpi_dev_clear_dependencies() and
    142		 * the clk + regulators must be ready when this happens.
    143		 */
    144		cells[0].name = "tps68470-clk";
    145		cells[0].platform_data = &clk_pdata;
    146		cells[0].pdata_size = sizeof(clk_pdata);
    147		cells[1].name = "tps68470-regulator";
    148		cells[1].platform_data = (void *)board_data->tps68470_regulator_pdata;
    149		cells[1].pdata_size = sizeof(struct tps68470_regulator_platform_data);
    150		cells[2].name = "tps68470-gpio";
    151
    152		gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_table);
    153
    154		ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
    155					   cells, TPS68470_WIN_MFD_CELL_COUNT,
    156					   NULL, 0, NULL);
    157		kfree(cells);
    158
    159		if (ret)
    160			gpiod_remove_lookup_table(board_data->tps68470_gpio_lookup_table);
    161
    162		break;
    163	case DESIGNED_FOR_CHROMEOS:
    164		ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_NONE,
    165					   tps68470_cros, ARRAY_SIZE(tps68470_cros),
    166					   NULL, 0, NULL);
    167		break;
    168	default:
    169		dev_err(&client->dev, "Failed to add MFD devices\n");
    170		return device_type;
    171	}
    172
    173	/*
    174	 * No acpi_dev_clear_dependencies() here, since the acpi_gpiochip_add()
    175	 * for the GPIO cell already does this.
    176	 */
    177
    178	return ret;
    179}
    180
    181static int skl_int3472_tps68470_remove(struct i2c_client *client)
    182{
    183	const struct int3472_tps68470_board_data *board_data;
    184
    185	board_data = int3472_tps68470_get_board_data(dev_name(&client->dev));
    186	if (board_data)
    187		gpiod_remove_lookup_table(board_data->tps68470_gpio_lookup_table);
    188
    189	return 0;
    190}
    191
    192static const struct acpi_device_id int3472_device_id[] = {
    193	{ "INT3472", 0 },
    194	{ }
    195};
    196MODULE_DEVICE_TABLE(acpi, int3472_device_id);
    197
    198static struct i2c_driver int3472_tps68470 = {
    199	.driver = {
    200		.name = "int3472-tps68470",
    201		.acpi_match_table = int3472_device_id,
    202	},
    203	.probe_new = skl_int3472_tps68470_probe,
    204	.remove = skl_int3472_tps68470_remove,
    205};
    206module_i2c_driver(int3472_tps68470);
    207
    208MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI TPS68470 Device Driver");
    209MODULE_AUTHOR("Daniel Scally <djrscally@gmail.com>");
    210MODULE_LICENSE("GPL v2");
    211MODULE_SOFTDEP("pre: clk-tps68470 tps68470-regulator");