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

axp20x_ac_power.c (11125B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * AXP20X and AXP22X PMICs' ACIN power supply driver
      4 *
      5 * Copyright (C) 2016 Free Electrons
      6 *	Quentin Schulz <quentin.schulz@free-electrons.com>
      7 */
      8
      9#include <linux/device.h>
     10#include <linux/init.h>
     11#include <linux/interrupt.h>
     12#include <linux/kernel.h>
     13#include <linux/mfd/axp20x.h>
     14#include <linux/module.h>
     15#include <linux/of.h>
     16#include <linux/of_device.h>
     17#include <linux/platform_device.h>
     18#include <linux/pm.h>
     19#include <linux/power_supply.h>
     20#include <linux/regmap.h>
     21#include <linux/slab.h>
     22#include <linux/iio/consumer.h>
     23
     24#define AXP20X_PWR_STATUS_ACIN_PRESENT	BIT(7)
     25#define AXP20X_PWR_STATUS_ACIN_AVAIL	BIT(6)
     26
     27#define AXP813_ACIN_PATH_SEL		BIT(7)
     28#define AXP813_ACIN_PATH_SEL_TO_BIT(x)	(!!(x) << 7)
     29
     30#define AXP813_VHOLD_MASK		GENMASK(5, 3)
     31#define AXP813_VHOLD_UV_TO_BIT(x)	((((x) / 100000) - 40) << 3)
     32#define AXP813_VHOLD_REG_TO_UV(x)	\
     33	(((((x) & AXP813_VHOLD_MASK) >> 3) + 40) * 100000)
     34
     35#define AXP813_CURR_LIMIT_MASK		GENMASK(2, 0)
     36#define AXP813_CURR_LIMIT_UA_TO_BIT(x)	(((x) / 500000) - 3)
     37#define AXP813_CURR_LIMIT_REG_TO_UA(x)	\
     38	((((x) & AXP813_CURR_LIMIT_MASK) + 3) * 500000)
     39
     40#define DRVNAME "axp20x-ac-power-supply"
     41
     42struct axp20x_ac_power {
     43	struct regmap *regmap;
     44	struct power_supply *supply;
     45	struct iio_channel *acin_v;
     46	struct iio_channel *acin_i;
     47	bool has_acin_path_sel;
     48	unsigned int num_irqs;
     49	unsigned int irqs[];
     50};
     51
     52static irqreturn_t axp20x_ac_power_irq(int irq, void *devid)
     53{
     54	struct axp20x_ac_power *power = devid;
     55
     56	power_supply_changed(power->supply);
     57
     58	return IRQ_HANDLED;
     59}
     60
     61static int axp20x_ac_power_get_property(struct power_supply *psy,
     62					enum power_supply_property psp,
     63					union power_supply_propval *val)
     64{
     65	struct axp20x_ac_power *power = power_supply_get_drvdata(psy);
     66	int ret, reg;
     67
     68	switch (psp) {
     69	case POWER_SUPPLY_PROP_HEALTH:
     70		ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &reg);
     71		if (ret)
     72			return ret;
     73
     74		if (reg & AXP20X_PWR_STATUS_ACIN_PRESENT) {
     75			val->intval = POWER_SUPPLY_HEALTH_GOOD;
     76			return 0;
     77		}
     78
     79		val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
     80		return 0;
     81
     82	case POWER_SUPPLY_PROP_PRESENT:
     83		ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &reg);
     84		if (ret)
     85			return ret;
     86
     87		val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_PRESENT);
     88		return 0;
     89
     90	case POWER_SUPPLY_PROP_ONLINE:
     91		ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &reg);
     92		if (ret)
     93			return ret;
     94
     95		val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL);
     96
     97		/* ACIN_PATH_SEL disables ACIN even if ACIN_AVAIL is set. */
     98		if (val->intval && power->has_acin_path_sel) {
     99			ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL,
    100					  &reg);
    101			if (ret)
    102				return ret;
    103
    104			val->intval = !!(reg & AXP813_ACIN_PATH_SEL);
    105		}
    106
    107		return 0;
    108
    109	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    110		ret = iio_read_channel_processed(power->acin_v, &val->intval);
    111		if (ret)
    112			return ret;
    113
    114		/* IIO framework gives mV but Power Supply framework gives uV */
    115		val->intval *= 1000;
    116
    117		return 0;
    118
    119	case POWER_SUPPLY_PROP_CURRENT_NOW:
    120		ret = iio_read_channel_processed(power->acin_i, &val->intval);
    121		if (ret)
    122			return ret;
    123
    124		/* IIO framework gives mA but Power Supply framework gives uA */
    125		val->intval *= 1000;
    126
    127		return 0;
    128
    129	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
    130		ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, &reg);
    131		if (ret)
    132			return ret;
    133
    134		val->intval = AXP813_VHOLD_REG_TO_UV(reg);
    135
    136		return 0;
    137
    138	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
    139		ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, &reg);
    140		if (ret)
    141			return ret;
    142
    143		val->intval = AXP813_CURR_LIMIT_REG_TO_UA(reg);
    144		/* AXP813 datasheet defines values 11x as 4000mA */
    145		if (val->intval > 4000000)
    146			val->intval = 4000000;
    147
    148		return 0;
    149
    150	default:
    151		return -EINVAL;
    152	}
    153
    154	return -EINVAL;
    155}
    156
    157static int axp813_ac_power_set_property(struct power_supply *psy,
    158					enum power_supply_property psp,
    159					const union power_supply_propval *val)
    160{
    161	struct axp20x_ac_power *power = power_supply_get_drvdata(psy);
    162
    163	switch (psp) {
    164	case POWER_SUPPLY_PROP_ONLINE:
    165		return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
    166					  AXP813_ACIN_PATH_SEL,
    167					  AXP813_ACIN_PATH_SEL_TO_BIT(val->intval));
    168
    169	case POWER_SUPPLY_PROP_VOLTAGE_MIN:
    170		if (val->intval < 4000000 || val->intval > 4700000)
    171			return -EINVAL;
    172
    173		return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
    174					  AXP813_VHOLD_MASK,
    175					  AXP813_VHOLD_UV_TO_BIT(val->intval));
    176
    177	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
    178		if (val->intval < 1500000 || val->intval > 4000000)
    179			return -EINVAL;
    180
    181		return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
    182					  AXP813_CURR_LIMIT_MASK,
    183					  AXP813_CURR_LIMIT_UA_TO_BIT(val->intval));
    184
    185	default:
    186		return -EINVAL;
    187	}
    188
    189	return -EINVAL;
    190}
    191
    192static int axp813_ac_power_prop_writeable(struct power_supply *psy,
    193					  enum power_supply_property psp)
    194{
    195	return psp == POWER_SUPPLY_PROP_ONLINE ||
    196	       psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
    197	       psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
    198}
    199
    200static enum power_supply_property axp20x_ac_power_properties[] = {
    201	POWER_SUPPLY_PROP_HEALTH,
    202	POWER_SUPPLY_PROP_PRESENT,
    203	POWER_SUPPLY_PROP_ONLINE,
    204	POWER_SUPPLY_PROP_VOLTAGE_NOW,
    205	POWER_SUPPLY_PROP_CURRENT_NOW,
    206};
    207
    208static enum power_supply_property axp22x_ac_power_properties[] = {
    209	POWER_SUPPLY_PROP_HEALTH,
    210	POWER_SUPPLY_PROP_PRESENT,
    211	POWER_SUPPLY_PROP_ONLINE,
    212};
    213
    214static enum power_supply_property axp813_ac_power_properties[] = {
    215	POWER_SUPPLY_PROP_HEALTH,
    216	POWER_SUPPLY_PROP_PRESENT,
    217	POWER_SUPPLY_PROP_ONLINE,
    218	POWER_SUPPLY_PROP_VOLTAGE_MIN,
    219	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
    220};
    221
    222static const struct power_supply_desc axp20x_ac_power_desc = {
    223	.name = "axp20x-ac",
    224	.type = POWER_SUPPLY_TYPE_MAINS,
    225	.properties = axp20x_ac_power_properties,
    226	.num_properties = ARRAY_SIZE(axp20x_ac_power_properties),
    227	.get_property = axp20x_ac_power_get_property,
    228};
    229
    230static const struct power_supply_desc axp22x_ac_power_desc = {
    231	.name = "axp22x-ac",
    232	.type = POWER_SUPPLY_TYPE_MAINS,
    233	.properties = axp22x_ac_power_properties,
    234	.num_properties = ARRAY_SIZE(axp22x_ac_power_properties),
    235	.get_property = axp20x_ac_power_get_property,
    236};
    237
    238static const struct power_supply_desc axp813_ac_power_desc = {
    239	.name = "axp813-ac",
    240	.type = POWER_SUPPLY_TYPE_MAINS,
    241	.properties = axp813_ac_power_properties,
    242	.num_properties = ARRAY_SIZE(axp813_ac_power_properties),
    243	.property_is_writeable = axp813_ac_power_prop_writeable,
    244	.get_property = axp20x_ac_power_get_property,
    245	.set_property = axp813_ac_power_set_property,
    246};
    247
    248static const char * const axp20x_irq_names[] = {
    249	"ACIN_PLUGIN",
    250	"ACIN_REMOVAL",
    251};
    252
    253struct axp_data {
    254	const struct power_supply_desc	*power_desc;
    255	const char * const		*irq_names;
    256	unsigned int			num_irq_names;
    257	bool				acin_adc;
    258	bool				acin_path_sel;
    259};
    260
    261static const struct axp_data axp20x_data = {
    262	.power_desc	= &axp20x_ac_power_desc,
    263	.irq_names	= axp20x_irq_names,
    264	.num_irq_names	= ARRAY_SIZE(axp20x_irq_names),
    265	.acin_adc	= true,
    266	.acin_path_sel	= false,
    267};
    268
    269static const struct axp_data axp22x_data = {
    270	.power_desc	= &axp22x_ac_power_desc,
    271	.irq_names	= axp20x_irq_names,
    272	.num_irq_names	= ARRAY_SIZE(axp20x_irq_names),
    273	.acin_adc	= false,
    274	.acin_path_sel	= false,
    275};
    276
    277static const struct axp_data axp813_data = {
    278	.power_desc	= &axp813_ac_power_desc,
    279	.irq_names	= axp20x_irq_names,
    280	.num_irq_names	= ARRAY_SIZE(axp20x_irq_names),
    281	.acin_adc	= false,
    282	.acin_path_sel	= true,
    283};
    284
    285#ifdef CONFIG_PM_SLEEP
    286static int axp20x_ac_power_suspend(struct device *dev)
    287{
    288	struct axp20x_ac_power *power = dev_get_drvdata(dev);
    289	int i = 0;
    290
    291	/*
    292	 * Allow wake via ACIN_PLUGIN only.
    293	 *
    294	 * As nested threaded IRQs are not automatically disabled during
    295	 * suspend, we must explicitly disable the remainder of the IRQs.
    296	 */
    297	if (device_may_wakeup(&power->supply->dev))
    298		enable_irq_wake(power->irqs[i++]);
    299	while (i < power->num_irqs)
    300		disable_irq(power->irqs[i++]);
    301
    302	return 0;
    303}
    304
    305static int axp20x_ac_power_resume(struct device *dev)
    306{
    307	struct axp20x_ac_power *power = dev_get_drvdata(dev);
    308	int i = 0;
    309
    310	if (device_may_wakeup(&power->supply->dev))
    311		disable_irq_wake(power->irqs[i++]);
    312	while (i < power->num_irqs)
    313		enable_irq(power->irqs[i++]);
    314
    315	return 0;
    316}
    317#endif
    318
    319static SIMPLE_DEV_PM_OPS(axp20x_ac_power_pm_ops, axp20x_ac_power_suspend,
    320						 axp20x_ac_power_resume);
    321
    322static int axp20x_ac_power_probe(struct platform_device *pdev)
    323{
    324	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
    325	struct power_supply_config psy_cfg = {};
    326	struct axp20x_ac_power *power;
    327	const struct axp_data *axp_data;
    328	int i, irq, ret;
    329
    330	if (!of_device_is_available(pdev->dev.of_node))
    331		return -ENODEV;
    332
    333	if (!axp20x) {
    334		dev_err(&pdev->dev, "Parent drvdata not set\n");
    335		return -EINVAL;
    336	}
    337
    338	axp_data = of_device_get_match_data(&pdev->dev);
    339
    340	power = devm_kzalloc(&pdev->dev,
    341			     struct_size(power, irqs, axp_data->num_irq_names),
    342			     GFP_KERNEL);
    343	if (!power)
    344		return -ENOMEM;
    345
    346	if (axp_data->acin_adc) {
    347		power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v");
    348		if (IS_ERR(power->acin_v)) {
    349			if (PTR_ERR(power->acin_v) == -ENODEV)
    350				return -EPROBE_DEFER;
    351			return PTR_ERR(power->acin_v);
    352		}
    353
    354		power->acin_i = devm_iio_channel_get(&pdev->dev, "acin_i");
    355		if (IS_ERR(power->acin_i)) {
    356			if (PTR_ERR(power->acin_i) == -ENODEV)
    357				return -EPROBE_DEFER;
    358			return PTR_ERR(power->acin_i);
    359		}
    360	}
    361
    362	power->regmap = dev_get_regmap(pdev->dev.parent, NULL);
    363	power->has_acin_path_sel = axp_data->acin_path_sel;
    364	power->num_irqs = axp_data->num_irq_names;
    365
    366	platform_set_drvdata(pdev, power);
    367
    368	psy_cfg.of_node = pdev->dev.of_node;
    369	psy_cfg.drv_data = power;
    370
    371	power->supply = devm_power_supply_register(&pdev->dev,
    372						   axp_data->power_desc,
    373						   &psy_cfg);
    374	if (IS_ERR(power->supply))
    375		return PTR_ERR(power->supply);
    376
    377	/* Request irqs after registering, as irqs may trigger immediately */
    378	for (i = 0; i < axp_data->num_irq_names; i++) {
    379		irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]);
    380		if (irq < 0)
    381			return irq;
    382
    383		power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
    384		ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i],
    385						   axp20x_ac_power_irq, 0,
    386						   DRVNAME, power);
    387		if (ret < 0) {
    388			dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n",
    389				axp_data->irq_names[i], ret);
    390			return ret;
    391		}
    392	}
    393
    394	return 0;
    395}
    396
    397static const struct of_device_id axp20x_ac_power_match[] = {
    398	{
    399		.compatible = "x-powers,axp202-ac-power-supply",
    400		.data = &axp20x_data,
    401	}, {
    402		.compatible = "x-powers,axp221-ac-power-supply",
    403		.data = &axp22x_data,
    404	}, {
    405		.compatible = "x-powers,axp813-ac-power-supply",
    406		.data = &axp813_data,
    407	}, { /* sentinel */ }
    408};
    409MODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
    410
    411static struct platform_driver axp20x_ac_power_driver = {
    412	.probe = axp20x_ac_power_probe,
    413	.driver = {
    414		.name		= DRVNAME,
    415		.of_match_table	= axp20x_ac_power_match,
    416		.pm		= &axp20x_ac_power_pm_ops,
    417	},
    418};
    419
    420module_platform_driver(axp20x_ac_power_driver);
    421
    422MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
    423MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver");
    424MODULE_LICENSE("GPL");