563 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* Copyright (c) 2024, Nikita Travkin <nikita@trvn.ru> */3 4#include <linux/unaligned.h>5#include <drm/drm_bridge.h>6#include <linux/bits.h>7#include <linux/delay.h>8#include <linux/i2c.h>9#include <linux/input.h>10#include <linux/module.h>11#include <linux/platform_device.h>12#include <linux/power_supply.h>13#include <linux/usb/typec_mux.h>14#include <linux/workqueue_types.h>15 16#define MILLI_TO_MICRO 100017 18#define ASPIRE_EC_EVENT 0x0519 20#define ASPIRE_EC_EVENT_WATCHDOG 0x2021#define ASPIRE_EC_EVENT_KBD_BKL_ON 0x5722#define ASPIRE_EC_EVENT_KBD_BKL_OFF 0x5823#define ASPIRE_EC_EVENT_LID_CLOSE 0x9b24#define ASPIRE_EC_EVENT_LID_OPEN 0x9c25#define ASPIRE_EC_EVENT_BKL_UNBLANKED 0x9d26#define ASPIRE_EC_EVENT_BKL_BLANKED 0x9e27#define ASPIRE_EC_EVENT_FG_INF_CHG 0x8528#define ASPIRE_EC_EVENT_FG_STA_CHG 0xc629#define ASPIRE_EC_EVENT_HPD_DIS 0xa330#define ASPIRE_EC_EVENT_HPD_CON 0xa431 32#define ASPIRE_EC_FG_DYNAMIC 0x0733#define ASPIRE_EC_FG_STATIC 0x0834 35#define ASPIRE_EC_FG_FLAG_PRESENT BIT(0)36#define ASPIRE_EC_FG_FLAG_FULL BIT(1)37#define ASPIRE_EC_FG_FLAG_DISCHARGING BIT(2)38#define ASPIRE_EC_FG_FLAG_CHARGING BIT(3)39 40#define ASPIRE_EC_RAM_READ 0x2041#define ASPIRE_EC_RAM_WRITE 0x2142 43#define ASPIRE_EC_RAM_WATCHDOG 0x1944#define ASPIRE_EC_WATCHDOG_BIT BIT(6)45 46#define ASPIRE_EC_RAM_KBD_MODE 0x4347 48#define ASPIRE_EC_RAM_KBD_FN_EN BIT(0)49#define ASPIRE_EC_RAM_KBD_MEDIA_ON_TOP BIT(5)50#define ASPIRE_EC_RAM_KBD_ALWAYS_SET BIT(6)51#define ASPIRE_EC_RAM_KBD_NUM_LAYER_EN BIT(7)52 53#define ASPIRE_EC_RAM_KBD_MODE_2 0x6054 55#define ASPIRE_EC_RAM_KBD_MEDIA_NOTIFY BIT(3)56 57#define ASPIRE_EC_RAM_HPD_STATUS 0xf458#define ASPIRE_EC_HPD_CONNECTED 0x0359 60#define ASPIRE_EC_RAM_LID_STATUS 0x4c61#define ASPIRE_EC_LID_OPEN BIT(6)62 63#define ASPIRE_EC_RAM_ADP 0x4064#define ASPIRE_EC_AC_STATUS BIT(0)65 66struct aspire_ec {67 struct i2c_client *client;68 struct power_supply *bat_psy;69 struct power_supply *adp_psy;70 struct input_dev *idev;71 72 bool bridge_configured;73 struct drm_bridge bridge;74 struct work_struct work;75};76 77static int aspire_ec_ram_read(struct i2c_client *client, u8 off, u8 *data, u8 data_len)78{79 i2c_smbus_write_byte_data(client, ASPIRE_EC_RAM_READ, off);80 i2c_smbus_read_i2c_block_data(client, ASPIRE_EC_RAM_READ, data_len, data);81 return 0;82}83 84static int aspire_ec_ram_write(struct i2c_client *client, u8 off, u8 data)85{86 u8 tmp[2] = {off, data};87 88 i2c_smbus_write_i2c_block_data(client, ASPIRE_EC_RAM_WRITE, sizeof(tmp), tmp);89 return 0;90}91 92static irqreturn_t aspire_ec_irq_handler(int irq, void *data)93{94 struct aspire_ec *ec = data;95 int id;96 u8 tmp;97 98 /*99 * The original ACPI firmware actually has a small sleep in the handler.100 *101 * It seems like in most cases it's not needed but when the device102 * just exits suspend, our i2c driver has a brief time where data103 * transfer is not possible yet. So this delay allows us to suppress104 * quite a bunch of spurious error messages in dmesg. Thus it's kept.105 */106 usleep_range(15000, 30000);107 108 id = i2c_smbus_read_byte_data(ec->client, ASPIRE_EC_EVENT);109 if (id < 0) {110 dev_err(&ec->client->dev, "Failed to read event id: %pe\n", ERR_PTR(id));111 return IRQ_HANDLED;112 }113 114 switch (id) {115 case 0x0: /* No event */116 break;117 118 case ASPIRE_EC_EVENT_WATCHDOG:119 /*120 * Here acpi responds to the event and clears some bit.121 * Notify (\_SB.I2C3.BAT1, 0x81) // Information Change122 * Notify (\_SB.I2C3.ADP1, 0x80) // Status Change123 */124 aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_WATCHDOG, &tmp, sizeof(tmp));125 tmp &= ~ASPIRE_EC_WATCHDOG_BIT;126 aspire_ec_ram_write(ec->client, ASPIRE_EC_RAM_WATCHDOG, tmp);127 break;128 129 case ASPIRE_EC_EVENT_LID_CLOSE:130 /* Notify (\_SB.LID0, 0x80) // Status Change */131 input_report_switch(ec->idev, SW_LID, 1);132 input_sync(ec->idev);133 break;134 135 case ASPIRE_EC_EVENT_LID_OPEN:136 /* Notify (\_SB.LID0, 0x80) // Status Change */137 input_report_switch(ec->idev, SW_LID, 0);138 input_sync(ec->idev);139 break;140 141 case ASPIRE_EC_EVENT_FG_INF_CHG:142 /* Notify (\_SB.I2C3.BAT1, 0x81) // Information Change */143 fallthrough;144 case ASPIRE_EC_EVENT_FG_STA_CHG:145 /* Notify (\_SB.I2C3.BAT1, 0x80) // Status Change */146 power_supply_changed(ec->bat_psy);147 power_supply_changed(ec->adp_psy);148 break;149 150 case ASPIRE_EC_EVENT_HPD_DIS:151 if (ec->bridge_configured)152 drm_bridge_hpd_notify(&ec->bridge, connector_status_disconnected);153 break;154 155 case ASPIRE_EC_EVENT_HPD_CON:156 if (ec->bridge_configured)157 drm_bridge_hpd_notify(&ec->bridge, connector_status_connected);158 break;159 160 case ASPIRE_EC_EVENT_BKL_BLANKED:161 case ASPIRE_EC_EVENT_BKL_UNBLANKED:162 /* Display backlight blanked on FN+F6. No action needed. */163 break;164 165 case ASPIRE_EC_EVENT_KBD_BKL_ON:166 case ASPIRE_EC_EVENT_KBD_BKL_OFF:167 /*168 * There is a keyboard backlight connector on Aspire 1 that is169 * controlled by FN+F8. There is no kb backlight on the device though.170 * Seems like this is used on other devices like Acer Spin 7.171 * No action needed.172 */173 break;174 175 default:176 dev_warn(&ec->client->dev, "Unknown event id=0x%x\n", id);177 }178 179 return IRQ_HANDLED;180}181 182/*183 * Power supply.184 */185 186struct aspire_ec_bat_psy_static_data {187 u8 unk1;188 u8 flags;189 __le16 unk2;190 __le16 voltage_design;191 __le16 capacity_full;192 __le16 unk3;193 __le16 serial;194 u8 model_id;195 u8 vendor_id;196} __packed;197 198static const char * const aspire_ec_bat_psy_battery_model[] = {199 "AP18C4K",200 "AP18C8K",201 "AP19B8K",202 "AP16M4J",203 "AP16M5J",204};205 206static const char * const aspire_ec_bat_psy_battery_vendor[] = {207 "SANYO",208 "SONY",209 "PANASONIC",210 "SAMSUNG",211 "SIMPLO",212 "MOTOROLA",213 "CELXPERT",214 "LGC",215 "GETAC",216 "MURATA",217};218 219struct aspire_ec_bat_psy_dynamic_data {220 u8 unk1;221 u8 flags;222 u8 unk2;223 __le16 capacity_now;224 __le16 voltage_now;225 __le16 current_now;226 __le16 unk3;227 __le16 unk4;228} __packed;229 230static int aspire_ec_bat_psy_get_property(struct power_supply *psy,231 enum power_supply_property psp,232 union power_supply_propval *val)233{234 struct aspire_ec *ec = power_supply_get_drvdata(psy);235 struct aspire_ec_bat_psy_static_data sdat;236 struct aspire_ec_bat_psy_dynamic_data ddat;237 int str_index = 0;238 239 i2c_smbus_read_i2c_block_data(ec->client, ASPIRE_EC_FG_STATIC, sizeof(sdat), (u8 *)&sdat);240 i2c_smbus_read_i2c_block_data(ec->client, ASPIRE_EC_FG_DYNAMIC, sizeof(ddat), (u8 *)&ddat);241 242 switch (psp) {243 case POWER_SUPPLY_PROP_STATUS:244 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;245 if (ddat.flags & ASPIRE_EC_FG_FLAG_CHARGING)246 val->intval = POWER_SUPPLY_STATUS_CHARGING;247 else if (ddat.flags & ASPIRE_EC_FG_FLAG_DISCHARGING)248 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;249 else if (ddat.flags & ASPIRE_EC_FG_FLAG_FULL)250 val->intval = POWER_SUPPLY_STATUS_FULL;251 break;252 253 case POWER_SUPPLY_PROP_VOLTAGE_NOW:254 val->intval = get_unaligned_le16(&ddat.voltage_now) * MILLI_TO_MICRO;255 break;256 257 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:258 val->intval = le16_to_cpu(sdat.voltage_design) * MILLI_TO_MICRO;259 break;260 261 case POWER_SUPPLY_PROP_CHARGE_NOW:262 val->intval = get_unaligned_le16(&ddat.capacity_now) * MILLI_TO_MICRO;263 break;264 265 case POWER_SUPPLY_PROP_CHARGE_FULL:266 val->intval = le16_to_cpu(sdat.capacity_full) * MILLI_TO_MICRO;267 break;268 269 case POWER_SUPPLY_PROP_CAPACITY:270 val->intval = get_unaligned_le16(&ddat.capacity_now) * 100;271 val->intval /= le16_to_cpu(sdat.capacity_full);272 break;273 274 case POWER_SUPPLY_PROP_CURRENT_NOW:275 val->intval = (s16)get_unaligned_le16(&ddat.current_now) * MILLI_TO_MICRO;276 break;277 278 case POWER_SUPPLY_PROP_PRESENT:279 val->intval = !!(ddat.flags & ASPIRE_EC_FG_FLAG_PRESENT);280 break;281 282 case POWER_SUPPLY_PROP_SCOPE:283 val->intval = POWER_SUPPLY_SCOPE_SYSTEM;284 break;285 286 case POWER_SUPPLY_PROP_MODEL_NAME:287 str_index = sdat.model_id - 1;288 289 if (str_index >= 0 && str_index < ARRAY_SIZE(aspire_ec_bat_psy_battery_model))290 val->strval = aspire_ec_bat_psy_battery_model[str_index];291 else292 val->strval = "Unknown";293 break;294 295 case POWER_SUPPLY_PROP_MANUFACTURER:296 str_index = sdat.vendor_id - 3; /* ACPI uses 3 as an offset here. */297 298 if (str_index >= 0 && str_index < ARRAY_SIZE(aspire_ec_bat_psy_battery_vendor))299 val->strval = aspire_ec_bat_psy_battery_vendor[str_index];300 else301 val->strval = "Unknown";302 break;303 304 default:305 return -EINVAL;306 }307 308 return 0;309}310 311static enum power_supply_property aspire_ec_bat_psy_props[] = {312 POWER_SUPPLY_PROP_STATUS,313 POWER_SUPPLY_PROP_VOLTAGE_NOW,314 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,315 POWER_SUPPLY_PROP_CHARGE_NOW,316 POWER_SUPPLY_PROP_CHARGE_FULL,317 POWER_SUPPLY_PROP_CAPACITY,318 POWER_SUPPLY_PROP_CURRENT_NOW,319 POWER_SUPPLY_PROP_PRESENT,320 POWER_SUPPLY_PROP_SCOPE,321 POWER_SUPPLY_PROP_MODEL_NAME,322 POWER_SUPPLY_PROP_MANUFACTURER,323};324 325static const struct power_supply_desc aspire_ec_bat_psy_desc = {326 .name = "aspire-ec-bat",327 .type = POWER_SUPPLY_TYPE_BATTERY,328 .get_property = aspire_ec_bat_psy_get_property,329 .properties = aspire_ec_bat_psy_props,330 .num_properties = ARRAY_SIZE(aspire_ec_bat_psy_props),331};332 333static int aspire_ec_adp_psy_get_property(struct power_supply *psy,334 enum power_supply_property psp,335 union power_supply_propval *val)336{337 struct aspire_ec *ec = power_supply_get_drvdata(psy);338 u8 tmp;339 340 switch (psp) {341 case POWER_SUPPLY_PROP_ONLINE:342 aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_ADP, &tmp, sizeof(tmp));343 val->intval = !!(tmp & ASPIRE_EC_AC_STATUS);344 break;345 346 default:347 return -EINVAL;348 }349 350 return 0;351}352 353static enum power_supply_property aspire_ec_adp_psy_props[] = {354 POWER_SUPPLY_PROP_ONLINE,355};356 357static const struct power_supply_desc aspire_ec_adp_psy_desc = {358 .name = "aspire-ec-adp",359 .type = POWER_SUPPLY_TYPE_MAINS,360 .get_property = aspire_ec_adp_psy_get_property,361 .properties = aspire_ec_adp_psy_props,362 .num_properties = ARRAY_SIZE(aspire_ec_adp_psy_props),363};364 365/*366 * USB-C DP Alt mode HPD.367 */368 369static int aspire_ec_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags)370{371 return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL;372}373 374static void aspire_ec_bridge_update_hpd_work(struct work_struct *work)375{376 struct aspire_ec *ec = container_of(work, struct aspire_ec, work);377 u8 tmp;378 379 aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_HPD_STATUS, &tmp, sizeof(tmp));380 if (tmp == ASPIRE_EC_HPD_CONNECTED)381 drm_bridge_hpd_notify(&ec->bridge, connector_status_connected);382 else383 drm_bridge_hpd_notify(&ec->bridge, connector_status_disconnected);384}385 386static void aspire_ec_bridge_hpd_enable(struct drm_bridge *bridge)387{388 struct aspire_ec *ec = container_of(bridge, struct aspire_ec, bridge);389 390 schedule_work(&ec->work);391}392 393static const struct drm_bridge_funcs aspire_ec_bridge_funcs = {394 .hpd_enable = aspire_ec_bridge_hpd_enable,395 .attach = aspire_ec_bridge_attach,396};397 398/*399 * Sysfs attributes.400 */401 402static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, char *buf)403{404 struct aspire_ec *ec = i2c_get_clientdata(to_i2c_client(dev));405 u8 tmp;406 407 aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_KBD_MODE, &tmp, sizeof(tmp));408 409 return sysfs_emit(buf, "%u\n", !(tmp & ASPIRE_EC_RAM_KBD_MEDIA_ON_TOP));410}411 412static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr,413 const char *buf, size_t count)414{415 struct aspire_ec *ec = i2c_get_clientdata(to_i2c_client(dev));416 u8 tmp;417 418 bool state;419 int ret;420 421 ret = kstrtobool(buf, &state);422 if (ret)423 return ret;424 425 aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_KBD_MODE, &tmp, sizeof(tmp));426 427 if (state)428 tmp &= ~ASPIRE_EC_RAM_KBD_MEDIA_ON_TOP;429 else430 tmp |= ASPIRE_EC_RAM_KBD_MEDIA_ON_TOP;431 432 aspire_ec_ram_write(ec->client, ASPIRE_EC_RAM_KBD_MODE, tmp);433 434 return count;435}436 437static DEVICE_ATTR_RW(fn_lock);438 439static struct attribute *aspire_ec_attrs[] = {440 &dev_attr_fn_lock.attr,441 NULL442};443ATTRIBUTE_GROUPS(aspire_ec);444 445static int aspire_ec_probe(struct i2c_client *client)446{447 struct power_supply_config psy_cfg = {0};448 struct device *dev = &client->dev;449 struct fwnode_handle *fwnode;450 struct aspire_ec *ec;451 int ret;452 u8 tmp;453 454 ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);455 if (!ec)456 return -ENOMEM;457 458 ec->client = client;459 i2c_set_clientdata(client, ec);460 461 /* Battery status reports */462 psy_cfg.drv_data = ec;463 ec->bat_psy = devm_power_supply_register(dev, &aspire_ec_bat_psy_desc, &psy_cfg);464 if (IS_ERR(ec->bat_psy))465 return dev_err_probe(dev, PTR_ERR(ec->bat_psy),466 "Failed to register battery power supply\n");467 468 ec->adp_psy = devm_power_supply_register(dev, &aspire_ec_adp_psy_desc, &psy_cfg);469 if (IS_ERR(ec->adp_psy))470 return dev_err_probe(dev, PTR_ERR(ec->adp_psy),471 "Failed to register AC power supply\n");472 473 /* Lid switch */474 ec->idev = devm_input_allocate_device(dev);475 if (!ec->idev)476 return -ENOMEM;477 478 ec->idev->name = "aspire-ec";479 ec->idev->phys = "aspire-ec/input0";480 input_set_capability(ec->idev, EV_SW, SW_LID);481 482 ret = input_register_device(ec->idev);483 if (ret)484 return dev_err_probe(dev, ret, "Input device register failed\n");485 486 /* Enable the keyboard fn keys */487 tmp = ASPIRE_EC_RAM_KBD_FN_EN | ASPIRE_EC_RAM_KBD_ALWAYS_SET;488 tmp |= ASPIRE_EC_RAM_KBD_MEDIA_ON_TOP;489 aspire_ec_ram_write(client, ASPIRE_EC_RAM_KBD_MODE, tmp);490 491 aspire_ec_ram_read(client, ASPIRE_EC_RAM_KBD_MODE_2, &tmp, sizeof(tmp));492 tmp |= ASPIRE_EC_RAM_KBD_MEDIA_NOTIFY;493 aspire_ec_ram_write(client, ASPIRE_EC_RAM_KBD_MODE_2, tmp);494 495 /* External Type-C display attach reports */496 fwnode = device_get_named_child_node(dev, "connector");497 if (fwnode) {498 INIT_WORK(&ec->work, aspire_ec_bridge_update_hpd_work);499 ec->bridge.funcs = &aspire_ec_bridge_funcs;500 ec->bridge.of_node = to_of_node(fwnode);501 ec->bridge.ops = DRM_BRIDGE_OP_HPD;502 ec->bridge.type = DRM_MODE_CONNECTOR_USB;503 504 ret = devm_drm_bridge_add(dev, &ec->bridge);505 if (ret) {506 fwnode_handle_put(fwnode);507 return dev_err_probe(dev, ret, "Failed to register drm bridge\n");508 }509 510 ec->bridge_configured = true;511 }512 513 ret = devm_request_threaded_irq(dev, client->irq, NULL,514 aspire_ec_irq_handler, IRQF_ONESHOT,515 dev_name(dev), ec);516 if (ret)517 return dev_err_probe(dev, ret, "Failed to request irq\n");518 519 return 0;520}521 522static int aspire_ec_resume(struct device *dev)523{524 struct aspire_ec *ec = i2c_get_clientdata(to_i2c_client(dev));525 u8 tmp;526 527 aspire_ec_ram_read(ec->client, ASPIRE_EC_RAM_LID_STATUS, &tmp, sizeof(tmp));528 input_report_switch(ec->idev, SW_LID, !!(tmp & ASPIRE_EC_LID_OPEN));529 input_sync(ec->idev);530 531 return 0;532}533 534static const struct i2c_device_id aspire_ec_id[] = {535 { "aspire1-ec", },536 { }537};538MODULE_DEVICE_TABLE(i2c, aspire_ec_id);539 540static const struct of_device_id aspire_ec_of_match[] = {541 { .compatible = "acer,aspire1-ec", },542 { }543};544MODULE_DEVICE_TABLE(of, aspire_ec_of_match);545 546static DEFINE_SIMPLE_DEV_PM_OPS(aspire_ec_pm_ops, NULL, aspire_ec_resume);547 548static struct i2c_driver aspire_ec_driver = {549 .driver = {550 .name = "aspire-ec",551 .of_match_table = aspire_ec_of_match,552 .pm = pm_sleep_ptr(&aspire_ec_pm_ops),553 .dev_groups = aspire_ec_groups,554 },555 .probe = aspire_ec_probe,556 .id_table = aspire_ec_id,557};558module_i2c_driver(aspire_ec_driver);559 560MODULE_DESCRIPTION("Acer Aspire 1 embedded controller");561MODULE_AUTHOR("Nikita Travkin <nikita@trvn.ru>");562MODULE_LICENSE("GPL");563