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

nvec_power.c (10509B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * nvec_power: power supply driver for a NVIDIA compliant embedded controller
      4 *
      5 * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
      6 *
      7 * Authors:  Ilya Petrov <ilya.muromec@gmail.com>
      8 *           Marc Dietrich <marvin24@gmx.de>
      9 */
     10
     11#include <linux/module.h>
     12#include <linux/platform_device.h>
     13#include <linux/err.h>
     14#include <linux/power_supply.h>
     15#include <linux/slab.h>
     16#include <linux/workqueue.h>
     17#include <linux/delay.h>
     18
     19#include "nvec.h"
     20
     21#define GET_SYSTEM_STATUS 0x00
     22
     23struct nvec_power {
     24	struct notifier_block notifier;
     25	struct delayed_work poller;
     26	struct nvec_chip *nvec;
     27	int on;
     28	int bat_present;
     29	int bat_status;
     30	int bat_voltage_now;
     31	int bat_current_now;
     32	int bat_current_avg;
     33	int time_remain;
     34	int charge_full_design;
     35	int charge_last_full;
     36	int critical_capacity;
     37	int capacity_remain;
     38	int bat_temperature;
     39	int bat_cap;
     40	int bat_type_enum;
     41	char bat_manu[30];
     42	char bat_model[30];
     43	char bat_type[30];
     44};
     45
     46enum {
     47	SLOT_STATUS,
     48	VOLTAGE,
     49	TIME_REMAINING,
     50	CURRENT,
     51	AVERAGE_CURRENT,
     52	AVERAGING_TIME_INTERVAL,
     53	CAPACITY_REMAINING,
     54	LAST_FULL_CHARGE_CAPACITY,
     55	DESIGN_CAPACITY,
     56	CRITICAL_CAPACITY,
     57	TEMPERATURE,
     58	MANUFACTURER,
     59	MODEL,
     60	TYPE,
     61};
     62
     63enum {
     64	AC,
     65	BAT,
     66};
     67
     68struct bat_response {
     69	u8 event_type;
     70	u8 length;
     71	u8 sub_type;
     72	u8 status;
     73	/* payload */
     74	union {
     75		char plc[30];
     76		u16 plu;
     77		s16 pls;
     78	};
     79};
     80
     81static struct power_supply *nvec_bat_psy;
     82static struct power_supply *nvec_psy;
     83
     84static int nvec_power_notifier(struct notifier_block *nb,
     85			       unsigned long event_type, void *data)
     86{
     87	struct nvec_power *power =
     88	    container_of(nb, struct nvec_power, notifier);
     89	struct bat_response *res = data;
     90
     91	if (event_type != NVEC_SYS)
     92		return NOTIFY_DONE;
     93
     94	if (res->sub_type == 0) {
     95		if (power->on != res->plu) {
     96			power->on = res->plu;
     97			power_supply_changed(nvec_psy);
     98		}
     99		return NOTIFY_STOP;
    100	}
    101	return NOTIFY_OK;
    102}
    103
    104static const int bat_init[] = {
    105	LAST_FULL_CHARGE_CAPACITY, DESIGN_CAPACITY, CRITICAL_CAPACITY,
    106	MANUFACTURER, MODEL, TYPE,
    107};
    108
    109static void get_bat_mfg_data(struct nvec_power *power)
    110{
    111	int i;
    112	char buf[] = { NVEC_BAT, SLOT_STATUS };
    113
    114	for (i = 0; i < ARRAY_SIZE(bat_init); i++) {
    115		buf[1] = bat_init[i];
    116		nvec_write_async(power->nvec, buf, 2);
    117	}
    118}
    119
    120static int nvec_power_bat_notifier(struct notifier_block *nb,
    121				   unsigned long event_type, void *data)
    122{
    123	struct nvec_power *power =
    124	    container_of(nb, struct nvec_power, notifier);
    125	struct bat_response *res = data;
    126	int status_changed = 0;
    127
    128	if (event_type != NVEC_BAT)
    129		return NOTIFY_DONE;
    130
    131	switch (res->sub_type) {
    132	case SLOT_STATUS:
    133		if (res->plc[0] & 1) {
    134			if (power->bat_present == 0) {
    135				status_changed = 1;
    136				get_bat_mfg_data(power);
    137			}
    138
    139			power->bat_present = 1;
    140
    141			switch ((res->plc[0] >> 1) & 3) {
    142			case 0:
    143				power->bat_status =
    144				    POWER_SUPPLY_STATUS_NOT_CHARGING;
    145				break;
    146			case 1:
    147				power->bat_status =
    148				    POWER_SUPPLY_STATUS_CHARGING;
    149				break;
    150			case 2:
    151				power->bat_status =
    152				    POWER_SUPPLY_STATUS_DISCHARGING;
    153				break;
    154			default:
    155				power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
    156			}
    157		} else {
    158			if (power->bat_present == 1)
    159				status_changed = 1;
    160
    161			power->bat_present = 0;
    162			power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
    163		}
    164		power->bat_cap = res->plc[1];
    165		if (status_changed)
    166			power_supply_changed(nvec_bat_psy);
    167		break;
    168	case VOLTAGE:
    169		power->bat_voltage_now = res->plu * 1000;
    170		break;
    171	case TIME_REMAINING:
    172		power->time_remain = res->plu * 3600;
    173		break;
    174	case CURRENT:
    175		power->bat_current_now = res->pls * 1000;
    176		break;
    177	case AVERAGE_CURRENT:
    178		power->bat_current_avg = res->pls * 1000;
    179		break;
    180	case CAPACITY_REMAINING:
    181		power->capacity_remain = res->plu * 1000;
    182		break;
    183	case LAST_FULL_CHARGE_CAPACITY:
    184		power->charge_last_full = res->plu * 1000;
    185		break;
    186	case DESIGN_CAPACITY:
    187		power->charge_full_design = res->plu * 1000;
    188		break;
    189	case CRITICAL_CAPACITY:
    190		power->critical_capacity = res->plu * 1000;
    191		break;
    192	case TEMPERATURE:
    193		power->bat_temperature = res->plu - 2732;
    194		break;
    195	case MANUFACTURER:
    196		memcpy(power->bat_manu, &res->plc, res->length - 2);
    197		power->bat_model[res->length - 2] = '\0';
    198		break;
    199	case MODEL:
    200		memcpy(power->bat_model, &res->plc, res->length - 2);
    201		power->bat_model[res->length - 2] = '\0';
    202		break;
    203	case TYPE:
    204		memcpy(power->bat_type, &res->plc, res->length - 2);
    205		power->bat_type[res->length - 2] = '\0';
    206		/*
    207		 * This differs a little from the spec fill in more if you find
    208		 * some.
    209		 */
    210		if (!strncmp(power->bat_type, "Li", 30))
    211			power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_LION;
    212		else
    213			power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
    214		break;
    215	default:
    216		return NOTIFY_STOP;
    217	}
    218
    219	return NOTIFY_STOP;
    220}
    221
    222static int nvec_power_get_property(struct power_supply *psy,
    223				   enum power_supply_property psp,
    224				   union power_supply_propval *val)
    225{
    226	struct nvec_power *power = dev_get_drvdata(psy->dev.parent);
    227
    228	switch (psp) {
    229	case POWER_SUPPLY_PROP_ONLINE:
    230		val->intval = power->on;
    231		break;
    232	default:
    233		return -EINVAL;
    234	}
    235	return 0;
    236}
    237
    238static int nvec_battery_get_property(struct power_supply *psy,
    239				     enum power_supply_property psp,
    240				     union power_supply_propval *val)
    241{
    242	struct nvec_power *power = dev_get_drvdata(psy->dev.parent);
    243
    244	switch (psp) {
    245	case POWER_SUPPLY_PROP_STATUS:
    246		val->intval = power->bat_status;
    247		break;
    248	case POWER_SUPPLY_PROP_CAPACITY:
    249		val->intval = power->bat_cap;
    250		break;
    251	case POWER_SUPPLY_PROP_PRESENT:
    252		val->intval = power->bat_present;
    253		break;
    254	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    255		val->intval = power->bat_voltage_now;
    256		break;
    257	case POWER_SUPPLY_PROP_CURRENT_NOW:
    258		val->intval = power->bat_current_now;
    259		break;
    260	case POWER_SUPPLY_PROP_CURRENT_AVG:
    261		val->intval = power->bat_current_avg;
    262		break;
    263	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
    264		val->intval = power->time_remain;
    265		break;
    266	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
    267		val->intval = power->charge_full_design;
    268		break;
    269	case POWER_SUPPLY_PROP_CHARGE_FULL:
    270		val->intval = power->charge_last_full;
    271		break;
    272	case POWER_SUPPLY_PROP_CHARGE_EMPTY:
    273		val->intval = power->critical_capacity;
    274		break;
    275	case POWER_SUPPLY_PROP_CHARGE_NOW:
    276		val->intval = power->capacity_remain;
    277		break;
    278	case POWER_SUPPLY_PROP_TEMP:
    279		val->intval = power->bat_temperature;
    280		break;
    281	case POWER_SUPPLY_PROP_MANUFACTURER:
    282		val->strval = power->bat_manu;
    283		break;
    284	case POWER_SUPPLY_PROP_MODEL_NAME:
    285		val->strval = power->bat_model;
    286		break;
    287	case POWER_SUPPLY_PROP_TECHNOLOGY:
    288		val->intval = power->bat_type_enum;
    289		break;
    290	default:
    291		return -EINVAL;
    292	}
    293	return 0;
    294}
    295
    296static enum power_supply_property nvec_power_props[] = {
    297	POWER_SUPPLY_PROP_ONLINE,
    298};
    299
    300static enum power_supply_property nvec_battery_props[] = {
    301	POWER_SUPPLY_PROP_STATUS,
    302	POWER_SUPPLY_PROP_PRESENT,
    303	POWER_SUPPLY_PROP_CAPACITY,
    304	POWER_SUPPLY_PROP_VOLTAGE_NOW,
    305	POWER_SUPPLY_PROP_CURRENT_NOW,
    306#ifdef EC_FULL_DIAG
    307	POWER_SUPPLY_PROP_CURRENT_AVG,
    308	POWER_SUPPLY_PROP_TEMP,
    309	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
    310#endif
    311	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
    312	POWER_SUPPLY_PROP_CHARGE_FULL,
    313	POWER_SUPPLY_PROP_CHARGE_EMPTY,
    314	POWER_SUPPLY_PROP_CHARGE_NOW,
    315	POWER_SUPPLY_PROP_MANUFACTURER,
    316	POWER_SUPPLY_PROP_MODEL_NAME,
    317	POWER_SUPPLY_PROP_TECHNOLOGY,
    318};
    319
    320static char *nvec_power_supplied_to[] = {
    321	"battery",
    322};
    323
    324static const struct power_supply_desc nvec_bat_psy_desc = {
    325	.name = "battery",
    326	.type = POWER_SUPPLY_TYPE_BATTERY,
    327	.properties = nvec_battery_props,
    328	.num_properties = ARRAY_SIZE(nvec_battery_props),
    329	.get_property = nvec_battery_get_property,
    330};
    331
    332static const struct power_supply_desc nvec_psy_desc = {
    333	.name = "ac",
    334	.type = POWER_SUPPLY_TYPE_MAINS,
    335	.properties = nvec_power_props,
    336	.num_properties = ARRAY_SIZE(nvec_power_props),
    337	.get_property = nvec_power_get_property,
    338};
    339
    340static int counter;
    341static const int bat_iter[] = {
    342	SLOT_STATUS, VOLTAGE, CURRENT, CAPACITY_REMAINING,
    343#ifdef EC_FULL_DIAG
    344	AVERAGE_CURRENT, TEMPERATURE, TIME_REMAINING,
    345#endif
    346};
    347
    348static void nvec_power_poll(struct work_struct *work)
    349{
    350	char buf[] = { NVEC_SYS, GET_SYSTEM_STATUS };
    351	struct nvec_power *power = container_of(work, struct nvec_power,
    352						poller.work);
    353
    354	if (counter >= ARRAY_SIZE(bat_iter))
    355		counter = 0;
    356
    357	/* AC status via sys req */
    358	nvec_write_async(power->nvec, buf, 2);
    359	msleep(100);
    360
    361	/*
    362	 * Select a battery request function via round robin doing it all at
    363	 * once seems to overload the power supply.
    364	 */
    365	buf[0] = NVEC_BAT;
    366	buf[1] = bat_iter[counter++];
    367	nvec_write_async(power->nvec, buf, 2);
    368
    369	schedule_delayed_work(to_delayed_work(work), msecs_to_jiffies(5000));
    370};
    371
    372static int nvec_power_probe(struct platform_device *pdev)
    373{
    374	struct power_supply **psy;
    375	const struct power_supply_desc *psy_desc;
    376	struct nvec_power *power;
    377	struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
    378	struct power_supply_config psy_cfg = {};
    379
    380	power = devm_kzalloc(&pdev->dev, sizeof(struct nvec_power), GFP_NOWAIT);
    381	if (!power)
    382		return -ENOMEM;
    383
    384	dev_set_drvdata(&pdev->dev, power);
    385	power->nvec = nvec;
    386
    387	switch (pdev->id) {
    388	case AC:
    389		psy = &nvec_psy;
    390		psy_desc = &nvec_psy_desc;
    391		psy_cfg.supplied_to = nvec_power_supplied_to;
    392		psy_cfg.num_supplicants = ARRAY_SIZE(nvec_power_supplied_to);
    393
    394		power->notifier.notifier_call = nvec_power_notifier;
    395
    396		INIT_DELAYED_WORK(&power->poller, nvec_power_poll);
    397		schedule_delayed_work(&power->poller, msecs_to_jiffies(5000));
    398		break;
    399	case BAT:
    400		psy = &nvec_bat_psy;
    401		psy_desc = &nvec_bat_psy_desc;
    402
    403		power->notifier.notifier_call = nvec_power_bat_notifier;
    404		break;
    405	default:
    406		return -ENODEV;
    407	}
    408
    409	nvec_register_notifier(nvec, &power->notifier, NVEC_SYS);
    410
    411	if (pdev->id == BAT)
    412		get_bat_mfg_data(power);
    413
    414	*psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg);
    415
    416	return PTR_ERR_OR_ZERO(*psy);
    417}
    418
    419static int nvec_power_remove(struct platform_device *pdev)
    420{
    421	struct nvec_power *power = platform_get_drvdata(pdev);
    422
    423	cancel_delayed_work_sync(&power->poller);
    424	nvec_unregister_notifier(power->nvec, &power->notifier);
    425	switch (pdev->id) {
    426	case AC:
    427		power_supply_unregister(nvec_psy);
    428		break;
    429	case BAT:
    430		power_supply_unregister(nvec_bat_psy);
    431	}
    432
    433	return 0;
    434}
    435
    436static struct platform_driver nvec_power_driver = {
    437	.probe = nvec_power_probe,
    438	.remove = nvec_power_remove,
    439	.driver = {
    440		   .name = "nvec-power",
    441	}
    442};
    443
    444module_platform_driver(nvec_power_driver);
    445
    446MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>");
    447MODULE_LICENSE("GPL");
    448MODULE_DESCRIPTION("NVEC battery and AC driver");
    449MODULE_ALIAS("platform:nvec-power");