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. */ 17 enum { 18 EC_DATA_IN = 0x00, 19 EC_RAM_OUT = 0x80, 20 EC_RAM_IN = 0x81, 21 }; 22 23 /* EC RAM registers. */ 24 enum { 25 EC_MODEL = 0x30, 26 EC_VERSION_MAJ = 0x31, 27 EC_VERSION_MIN = 0x32, 28 }; 29 30 struct kb3930 { 31 struct i2c_client *client; 32 struct regmap *ram_regmap; 33 struct gpio_descs *off_gpios; 34 }; 35 36 static 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 44 static 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 63 static 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 70 static void kb3930_pm_power_off(void) 71 { 72 kb3930_off(kb3930_power_off, EC_OFF_MODE_POWER); 73 } 74 75 static struct notifier_block kb3930_restart_nb = { 76 .notifier_call = kb3930_restart, 77 }; 78 79 static const struct mfd_cell ariel_ec_cells[] = { 80 { .name = "dell-wyse-ariel-led", }, 81 { .name = "dell-wyse-ariel-power", }, 82 }; 83 84 static 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 93 static 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 111 static 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 122 static 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 180 static void 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 192 static const struct of_device_id kb3930_dt_ids[] = { 193 { .compatible = "ene,kb3930" }, 194 { } 195 }; 196 MODULE_DEVICE_TABLE(of, kb3930_dt_ids); 197 198 static struct i2c_driver kb3930_driver = { 199 .probe_new = kb3930_probe, 200 .remove = kb3930_remove, 201 .driver = { 202 .name = "ene-kb3930", 203 .of_match_table = kb3930_dt_ids, 204 }, 205 }; 206 module_i2c_driver(kb3930_driver); 207 208 MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>"); 209 MODULE_DESCRIPTION("ENE KB3930 Embedded Controller Driver"); 210 MODULE_LICENSE("Dual BSD/GPL"); 211