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

ene-kb3930.c (4895B)


      1// SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0-or-later
      2/*
      3 * ENE KB3930 Embedded Controller Driver
      4 *
      5 * Copyright (C) 2020 Lubomir Rintel
      6 */
      7
      8#include <linux/delay.h>
      9#include <linux/gpio/consumer.h>
     10#include <linux/i2c.h>
     11#include <linux/mfd/core.h>
     12#include <linux/module.h>
     13#include <linux/reboot.h>
     14#include <linux/regmap.h>
     15
     16/* I2C registers that are multiplexing access to the EC RAM. */
     17enum {
     18	EC_DATA_IN	= 0x00,
     19	EC_RAM_OUT	= 0x80,
     20	EC_RAM_IN	= 0x81,
     21};
     22
     23/* EC RAM registers. */
     24enum {
     25	EC_MODEL	= 0x30,
     26	EC_VERSION_MAJ	= 0x31,
     27	EC_VERSION_MIN	= 0x32,
     28};
     29
     30struct kb3930 {
     31	struct i2c_client *client;
     32	struct regmap *ram_regmap;
     33	struct gpio_descs *off_gpios;
     34};
     35
     36static struct kb3930 *kb3930_power_off;
     37
     38#define EC_GPIO_WAVE		0
     39#define EC_GPIO_OFF_MODE	1
     40
     41#define EC_OFF_MODE_REBOOT	0
     42#define EC_OFF_MODE_POWER	1
     43
     44static void kb3930_off(struct kb3930 *ddata, int off_mode)
     45{
     46	gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_OFF_MODE],
     47			       off_mode);
     48
     49	/*
     50	 * This creates a 10 Hz wave on EC_GPIO_WAVE that signals a
     51	 * shutdown request to the EC. Once the EC detects it, it will
     52	 * proceed to turn the power off or reset the board depending on
     53	 * the value of EC_GPIO_OFF_MODE.
     54	 */
     55	while (1) {
     56		mdelay(50);
     57		gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_WAVE], 0);
     58		mdelay(50);
     59		gpiod_direction_output(ddata->off_gpios->desc[EC_GPIO_WAVE], 1);
     60	}
     61}
     62
     63static int kb3930_restart(struct notifier_block *this,
     64			  unsigned long mode, void *cmd)
     65{
     66	kb3930_off(kb3930_power_off, EC_OFF_MODE_REBOOT);
     67	return NOTIFY_DONE;
     68}
     69
     70static void kb3930_pm_power_off(void)
     71{
     72	kb3930_off(kb3930_power_off, EC_OFF_MODE_POWER);
     73}
     74
     75static struct notifier_block kb3930_restart_nb = {
     76	.notifier_call = kb3930_restart,
     77};
     78
     79static const struct mfd_cell ariel_ec_cells[] = {
     80	{ .name = "dell-wyse-ariel-led", },
     81	{ .name = "dell-wyse-ariel-power", },
     82};
     83
     84static int kb3930_ec_ram_reg_write(void *context, unsigned int reg,
     85				   unsigned int val)
     86{
     87	struct kb3930 *ddata = context;
     88
     89	return i2c_smbus_write_word_data(ddata->client, EC_RAM_OUT,
     90					 (val << 8) | reg);
     91}
     92
     93static int kb3930_ec_ram_reg_read(void *context, unsigned int reg,
     94				  unsigned int *val)
     95{
     96	struct kb3930 *ddata = context;
     97	int ret;
     98
     99	ret = i2c_smbus_write_word_data(ddata->client, EC_RAM_IN, reg);
    100	if (ret < 0)
    101		return ret;
    102
    103	ret = i2c_smbus_read_word_data(ddata->client, EC_DATA_IN);
    104	if (ret < 0)
    105		return ret;
    106
    107	*val = ret >> 8;
    108	return 0;
    109}
    110
    111static const struct regmap_config kb3930_ram_regmap_config = {
    112	.name = "ec_ram",
    113	.reg_bits = 8,
    114	.val_bits = 8,
    115	.reg_stride = 1,
    116	.max_register = 0xff,
    117	.reg_write = kb3930_ec_ram_reg_write,
    118	.reg_read = kb3930_ec_ram_reg_read,
    119	.fast_io = false,
    120};
    121
    122static int kb3930_probe(struct i2c_client *client)
    123{
    124	struct device *dev = &client->dev;
    125	struct device_node *np = dev->of_node;
    126	struct kb3930 *ddata;
    127	unsigned int model;
    128	int ret;
    129
    130	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
    131	if (!ddata)
    132		return -ENOMEM;
    133
    134	kb3930_power_off = ddata;
    135	ddata->client = client;
    136	i2c_set_clientdata(client, ddata);
    137
    138	ddata->ram_regmap = devm_regmap_init(dev, NULL, ddata,
    139					     &kb3930_ram_regmap_config);
    140	if (IS_ERR(ddata->ram_regmap))
    141		return PTR_ERR(ddata->ram_regmap);
    142
    143	ret = regmap_read(ddata->ram_regmap, EC_MODEL, &model);
    144	if (ret < 0)
    145		return ret;
    146
    147	/* Currently we only support the cells present on Dell Ariel model. */
    148	if (model != 'J') {
    149		dev_err(dev, "unknown board model: %02x\n", model);
    150		return -ENODEV;
    151	}
    152
    153	ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
    154				   ariel_ec_cells,
    155				   ARRAY_SIZE(ariel_ec_cells),
    156				   NULL, 0, NULL);
    157	if (ret)
    158		return ret;
    159
    160	if (of_property_read_bool(np, "system-power-controller")) {
    161		ddata->off_gpios =
    162			devm_gpiod_get_array_optional(dev, "off", GPIOD_IN);
    163		if (IS_ERR(ddata->off_gpios))
    164			return PTR_ERR(ddata->off_gpios);
    165		if (ddata->off_gpios->ndescs < 2) {
    166			dev_err(dev, "invalid off-gpios property\n");
    167			return -EINVAL;
    168		}
    169	}
    170
    171	if (ddata->off_gpios) {
    172		register_restart_handler(&kb3930_restart_nb);
    173		if (!pm_power_off)
    174			pm_power_off = kb3930_pm_power_off;
    175	}
    176
    177	return 0;
    178}
    179
    180static int kb3930_remove(struct i2c_client *client)
    181{
    182	struct kb3930 *ddata = i2c_get_clientdata(client);
    183
    184	if (ddata->off_gpios) {
    185		if (pm_power_off == kb3930_pm_power_off)
    186			pm_power_off = NULL;
    187		unregister_restart_handler(&kb3930_restart_nb);
    188	}
    189	kb3930_power_off = NULL;
    190
    191	return 0;
    192}
    193
    194static const struct of_device_id kb3930_dt_ids[] = {
    195	{ .compatible = "ene,kb3930" },
    196	{ }
    197};
    198MODULE_DEVICE_TABLE(of, kb3930_dt_ids);
    199
    200static struct i2c_driver kb3930_driver = {
    201	.probe_new = kb3930_probe,
    202	.remove = kb3930_remove,
    203	.driver = {
    204		.name = "ene-kb3930",
    205		.of_match_table = kb3930_dt_ids,
    206	},
    207};
    208module_i2c_driver(kb3930_driver);
    209
    210MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
    211MODULE_DESCRIPTION("ENE KB3930 Embedded Controller Driver");
    212MODULE_LICENSE("Dual BSD/GPL");