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

rn5t618_power.c (19951B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Power supply driver for the RICOH RN5T618 power management chip family
      4 *
      5 * Copyright (C) 2020 Andreas Kemnade
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/device.h>
     10#include <linux/bitops.h>
     11#include <linux/errno.h>
     12#include <linux/iio/consumer.h>
     13#include <linux/init.h>
     14#include <linux/interrupt.h>
     15#include <linux/module.h>
     16#include <linux/mfd/rn5t618.h>
     17#include <linux/of_device.h>
     18#include <linux/platform_device.h>
     19#include <linux/power_supply.h>
     20#include <linux/regmap.h>
     21#include <linux/slab.h>
     22
     23#define CHG_STATE_ADP_INPUT 0x40
     24#define CHG_STATE_USB_INPUT 0x80
     25#define CHG_STATE_MASK	0x1f
     26#define CHG_STATE_CHG_OFF	0
     27#define CHG_STATE_CHG_READY_VADP	1
     28#define CHG_STATE_CHG_TRICKLE	2
     29#define CHG_STATE_CHG_RAPID	3
     30#define CHG_STATE_CHG_COMPLETE	4
     31#define CHG_STATE_SUSPEND	5
     32#define CHG_STATE_VCHG_OVER_VOL	6
     33#define CHG_STATE_BAT_ERROR	7
     34#define CHG_STATE_NO_BAT	8
     35#define CHG_STATE_BAT_OVER_VOL	9
     36#define CHG_STATE_BAT_TEMP_ERR	10
     37#define CHG_STATE_DIE_ERR	11
     38#define CHG_STATE_DIE_SHUTDOWN	12
     39#define CHG_STATE_NO_BAT2	13
     40#define CHG_STATE_CHG_READY_VUSB	14
     41
     42#define GCHGDET_TYPE_MASK 0x30
     43#define GCHGDET_TYPE_SDP 0x00
     44#define GCHGDET_TYPE_CDP 0x10
     45#define GCHGDET_TYPE_DCP 0x20
     46
     47#define FG_ENABLE 1
     48
     49/*
     50 * Formula seems accurate for battery current, but for USB current around 70mA
     51 * per step was seen on Kobo Clara HD but all sources show the same formula
     52 * also fur USB current. To avoid accidentially unwanted high currents we stick
     53 * to that formula
     54 */
     55#define TO_CUR_REG(x) ((x) / 100000 - 1)
     56#define FROM_CUR_REG(x) ((((x) & 0x1f) + 1) * 100000)
     57#define CHG_MIN_CUR 100000
     58#define CHG_MAX_CUR 1800000
     59#define ADP_MAX_CUR 2500000
     60#define USB_MAX_CUR 1400000
     61
     62
     63struct rn5t618_power_info {
     64	struct rn5t618 *rn5t618;
     65	struct platform_device *pdev;
     66	struct power_supply *battery;
     67	struct power_supply *usb;
     68	struct power_supply *adp;
     69	struct iio_channel *channel_vusb;
     70	struct iio_channel *channel_vadp;
     71	int irq;
     72};
     73
     74static enum power_supply_usb_type rn5t618_usb_types[] = {
     75	POWER_SUPPLY_USB_TYPE_SDP,
     76	POWER_SUPPLY_USB_TYPE_DCP,
     77	POWER_SUPPLY_USB_TYPE_CDP,
     78	POWER_SUPPLY_USB_TYPE_UNKNOWN
     79};
     80
     81static enum power_supply_property rn5t618_usb_props[] = {
     82	/* input current limit is not very accurate */
     83	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
     84	POWER_SUPPLY_PROP_VOLTAGE_NOW,
     85	POWER_SUPPLY_PROP_STATUS,
     86	POWER_SUPPLY_PROP_USB_TYPE,
     87	POWER_SUPPLY_PROP_ONLINE,
     88};
     89
     90static enum power_supply_property rn5t618_adp_props[] = {
     91	/* input current limit is not very accurate */
     92	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
     93	POWER_SUPPLY_PROP_VOLTAGE_NOW,
     94	POWER_SUPPLY_PROP_STATUS,
     95	POWER_SUPPLY_PROP_ONLINE,
     96};
     97
     98
     99static enum power_supply_property rn5t618_battery_props[] = {
    100	POWER_SUPPLY_PROP_STATUS,
    101	POWER_SUPPLY_PROP_PRESENT,
    102	POWER_SUPPLY_PROP_VOLTAGE_NOW,
    103	POWER_SUPPLY_PROP_CURRENT_NOW,
    104	POWER_SUPPLY_PROP_CAPACITY,
    105	POWER_SUPPLY_PROP_TEMP,
    106	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
    107	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
    108	POWER_SUPPLY_PROP_TECHNOLOGY,
    109	POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
    110	POWER_SUPPLY_PROP_CHARGE_FULL,
    111	POWER_SUPPLY_PROP_CHARGE_NOW,
    112};
    113
    114static int rn5t618_battery_read_doublereg(struct rn5t618_power_info *info,
    115					  u8 reg, u16 *result)
    116{
    117	int ret, i;
    118	u8 data[2];
    119	u16 old, new;
    120
    121	old = 0;
    122	/* Prevent races when registers are changing. */
    123	for (i = 0; i < 3; i++) {
    124		ret = regmap_bulk_read(info->rn5t618->regmap,
    125				       reg, data, sizeof(data));
    126		if (ret)
    127			return ret;
    128
    129		new = data[0] << 8;
    130		new |= data[1];
    131		if (new == old)
    132			break;
    133
    134		old = new;
    135	}
    136
    137	*result = new;
    138
    139	return 0;
    140}
    141
    142static int rn5t618_decode_status(unsigned int status)
    143{
    144	switch (status & CHG_STATE_MASK) {
    145	case CHG_STATE_CHG_OFF:
    146	case CHG_STATE_SUSPEND:
    147	case CHG_STATE_VCHG_OVER_VOL:
    148	case CHG_STATE_DIE_SHUTDOWN:
    149		return POWER_SUPPLY_STATUS_DISCHARGING;
    150
    151	case CHG_STATE_CHG_TRICKLE:
    152	case CHG_STATE_CHG_RAPID:
    153		return POWER_SUPPLY_STATUS_CHARGING;
    154
    155	case CHG_STATE_CHG_COMPLETE:
    156		return POWER_SUPPLY_STATUS_FULL;
    157
    158	default:
    159		return POWER_SUPPLY_STATUS_NOT_CHARGING;
    160	}
    161}
    162
    163static int rn5t618_battery_status(struct rn5t618_power_info *info,
    164				  union power_supply_propval *val)
    165{
    166	unsigned int v;
    167	int ret;
    168
    169	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &v);
    170	if (ret)
    171		return ret;
    172
    173	val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
    174
    175	if (v & 0xc0) { /* USB or ADP plugged */
    176		val->intval = rn5t618_decode_status(v);
    177	} else
    178		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
    179
    180	return ret;
    181}
    182
    183static int rn5t618_battery_present(struct rn5t618_power_info *info,
    184				   union power_supply_propval *val)
    185{
    186	unsigned int v;
    187	int ret;
    188
    189	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &v);
    190	if (ret)
    191		return ret;
    192
    193	v &= CHG_STATE_MASK;
    194	if ((v == CHG_STATE_NO_BAT) || (v == CHG_STATE_NO_BAT2))
    195		val->intval = 0;
    196	else
    197		val->intval = 1;
    198
    199	return ret;
    200}
    201
    202static int rn5t618_battery_voltage_now(struct rn5t618_power_info *info,
    203				       union power_supply_propval *val)
    204{
    205	u16 res;
    206	int ret;
    207
    208	ret = rn5t618_battery_read_doublereg(info, RN5T618_VOLTAGE_1, &res);
    209	if (ret)
    210		return ret;
    211
    212	val->intval = res * 2 * 2500 / 4095 * 1000;
    213
    214	return 0;
    215}
    216
    217static int rn5t618_battery_current_now(struct rn5t618_power_info *info,
    218				       union power_supply_propval *val)
    219{
    220	u16 res;
    221	int ret;
    222
    223	ret = rn5t618_battery_read_doublereg(info, RN5T618_CC_AVEREG1, &res);
    224	if (ret)
    225		return ret;
    226
    227	/* current is negative when discharging */
    228	val->intval = sign_extend32(res, 13) * 1000;
    229
    230	return 0;
    231}
    232
    233static int rn5t618_battery_capacity(struct rn5t618_power_info *info,
    234				    union power_supply_propval *val)
    235{
    236	unsigned int v;
    237	int ret;
    238
    239	ret = regmap_read(info->rn5t618->regmap, RN5T618_SOC, &v);
    240	if (ret)
    241		return ret;
    242
    243	val->intval = v;
    244
    245	return 0;
    246}
    247
    248static int rn5t618_battery_temp(struct rn5t618_power_info *info,
    249				union power_supply_propval *val)
    250{
    251	u16 res;
    252	int ret;
    253
    254	ret = rn5t618_battery_read_doublereg(info, RN5T618_TEMP_1, &res);
    255	if (ret)
    256		return ret;
    257
    258	val->intval = sign_extend32(res, 11) * 10 / 16;
    259
    260	return 0;
    261}
    262
    263static int rn5t618_battery_tte(struct rn5t618_power_info *info,
    264			       union power_supply_propval *val)
    265{
    266	u16 res;
    267	int ret;
    268
    269	ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_EMPTY_H, &res);
    270	if (ret)
    271		return ret;
    272
    273	if (res == 65535)
    274		return -ENODATA;
    275
    276	val->intval = res * 60;
    277
    278	return 0;
    279}
    280
    281static int rn5t618_battery_ttf(struct rn5t618_power_info *info,
    282			       union power_supply_propval *val)
    283{
    284	u16 res;
    285	int ret;
    286
    287	ret = rn5t618_battery_read_doublereg(info, RN5T618_TT_FULL_H, &res);
    288	if (ret)
    289		return ret;
    290
    291	if (res == 65535)
    292		return -ENODATA;
    293
    294	val->intval = res * 60;
    295
    296	return 0;
    297}
    298
    299static int rn5t618_battery_set_current_limit(struct rn5t618_power_info *info,
    300				const union power_supply_propval *val)
    301{
    302	if (val->intval < CHG_MIN_CUR)
    303		return -EINVAL;
    304
    305	if (val->intval >= CHG_MAX_CUR)
    306		return -EINVAL;
    307
    308	return regmap_update_bits(info->rn5t618->regmap,
    309				  RN5T618_CHGISET,
    310				  0x1F, TO_CUR_REG(val->intval));
    311}
    312
    313static int rn5t618_battery_get_current_limit(struct rn5t618_power_info *info,
    314					     union power_supply_propval *val)
    315{
    316	unsigned int regval;
    317	int ret;
    318
    319	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGISET,
    320			  &regval);
    321	if (ret < 0)
    322		return ret;
    323
    324	val->intval = FROM_CUR_REG(regval);
    325
    326	return 0;
    327}
    328
    329static int rn5t618_battery_charge_full(struct rn5t618_power_info *info,
    330				       union power_supply_propval *val)
    331{
    332	u16 res;
    333	int ret;
    334
    335	ret = rn5t618_battery_read_doublereg(info, RN5T618_FA_CAP_H, &res);
    336	if (ret)
    337		return ret;
    338
    339	val->intval = res * 1000;
    340
    341	return 0;
    342}
    343
    344static int rn5t618_battery_charge_now(struct rn5t618_power_info *info,
    345				      union power_supply_propval *val)
    346{
    347	u16 res;
    348	int ret;
    349
    350	ret = rn5t618_battery_read_doublereg(info, RN5T618_RE_CAP_H, &res);
    351	if (ret)
    352		return ret;
    353
    354	val->intval = res * 1000;
    355
    356	return 0;
    357}
    358
    359static int rn5t618_battery_get_property(struct power_supply *psy,
    360					enum power_supply_property psp,
    361					union power_supply_propval *val)
    362{
    363	int ret = 0;
    364	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
    365
    366	switch (psp) {
    367	case POWER_SUPPLY_PROP_STATUS:
    368		ret = rn5t618_battery_status(info, val);
    369		break;
    370	case POWER_SUPPLY_PROP_PRESENT:
    371		ret = rn5t618_battery_present(info, val);
    372		break;
    373	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    374		ret = rn5t618_battery_voltage_now(info, val);
    375		break;
    376	case POWER_SUPPLY_PROP_CURRENT_NOW:
    377		ret = rn5t618_battery_current_now(info, val);
    378		break;
    379	case POWER_SUPPLY_PROP_CAPACITY:
    380		ret = rn5t618_battery_capacity(info, val);
    381		break;
    382	case POWER_SUPPLY_PROP_TEMP:
    383		ret = rn5t618_battery_temp(info, val);
    384		break;
    385	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
    386		ret = rn5t618_battery_tte(info, val);
    387		break;
    388	case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
    389		ret = rn5t618_battery_ttf(info, val);
    390		break;
    391	case POWER_SUPPLY_PROP_TECHNOLOGY:
    392		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
    393		break;
    394	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
    395		ret = rn5t618_battery_get_current_limit(info, val);
    396		break;
    397	case POWER_SUPPLY_PROP_CHARGE_FULL:
    398		ret = rn5t618_battery_charge_full(info, val);
    399		break;
    400	case POWER_SUPPLY_PROP_CHARGE_NOW:
    401		ret = rn5t618_battery_charge_now(info, val);
    402		break;
    403	default:
    404		return -EINVAL;
    405	}
    406
    407	return ret;
    408}
    409
    410static int rn5t618_battery_set_property(struct power_supply *psy,
    411					enum power_supply_property psp,
    412					const union power_supply_propval *val)
    413{
    414	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
    415
    416	switch (psp) {
    417	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
    418		return rn5t618_battery_set_current_limit(info, val);
    419	default:
    420		return -EINVAL;
    421	}
    422}
    423
    424static int rn5t618_battery_property_is_writeable(struct power_supply *psy,
    425						enum power_supply_property psp)
    426{
    427	switch (psp) {
    428	case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
    429		return true;
    430	default:
    431		return false;
    432	}
    433}
    434
    435static int rn5t618_adp_get_property(struct power_supply *psy,
    436				    enum power_supply_property psp,
    437				    union power_supply_propval *val)
    438{
    439	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
    440	unsigned int chgstate;
    441	unsigned int regval;
    442	bool online;
    443	int ret;
    444
    445	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &chgstate);
    446	if (ret)
    447		return ret;
    448
    449	online = !!(chgstate & CHG_STATE_ADP_INPUT);
    450
    451	switch (psp) {
    452	case POWER_SUPPLY_PROP_ONLINE:
    453		val->intval = online;
    454		break;
    455	case POWER_SUPPLY_PROP_STATUS:
    456		if (!online) {
    457			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
    458			break;
    459		}
    460		val->intval = rn5t618_decode_status(chgstate);
    461		if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
    462			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
    463
    464		break;
    465	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
    466		ret = regmap_read(info->rn5t618->regmap,
    467				  RN5T618_REGISET1, &regval);
    468		if (ret < 0)
    469			return ret;
    470
    471		val->intval = FROM_CUR_REG(regval);
    472		break;
    473	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    474		if (!info->channel_vadp)
    475			return -ENODATA;
    476
    477		ret = iio_read_channel_processed_scale(info->channel_vadp, &val->intval, 1000);
    478		if (ret < 0)
    479			return ret;
    480
    481		break;
    482	default:
    483		return -EINVAL;
    484	}
    485
    486	return 0;
    487}
    488
    489static int rn5t618_adp_set_property(struct power_supply *psy,
    490				    enum power_supply_property psp,
    491				    const union power_supply_propval *val)
    492{
    493	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
    494	int ret;
    495
    496	switch (psp) {
    497	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
    498		if (val->intval > ADP_MAX_CUR)
    499			return -EINVAL;
    500
    501		if (val->intval < CHG_MIN_CUR)
    502			return -EINVAL;
    503
    504		ret = regmap_write(info->rn5t618->regmap, RN5T618_REGISET1,
    505				   TO_CUR_REG(val->intval));
    506		if (ret < 0)
    507			return ret;
    508
    509		break;
    510	default:
    511		return -EINVAL;
    512	}
    513
    514	return 0;
    515}
    516
    517static int rn5t618_adp_property_is_writeable(struct power_supply *psy,
    518					     enum power_supply_property psp)
    519{
    520	switch (psp) {
    521	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
    522		return true;
    523	default:
    524		return false;
    525	}
    526}
    527
    528static int rc5t619_usb_get_type(struct rn5t618_power_info *info,
    529				union power_supply_propval *val)
    530{
    531	unsigned int regval;
    532	int ret;
    533
    534	ret = regmap_read(info->rn5t618->regmap, RN5T618_GCHGDET, &regval);
    535	if (ret < 0)
    536		return ret;
    537
    538	switch (regval & GCHGDET_TYPE_MASK) {
    539	case GCHGDET_TYPE_SDP:
    540		val->intval = POWER_SUPPLY_USB_TYPE_SDP;
    541		break;
    542	case GCHGDET_TYPE_CDP:
    543		val->intval = POWER_SUPPLY_USB_TYPE_CDP;
    544		break;
    545	case GCHGDET_TYPE_DCP:
    546		val->intval = POWER_SUPPLY_USB_TYPE_DCP;
    547		break;
    548	default:
    549		val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
    550	}
    551
    552	return 0;
    553}
    554
    555static int rn5t618_usb_get_property(struct power_supply *psy,
    556				    enum power_supply_property psp,
    557				    union power_supply_propval *val)
    558{
    559	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
    560	unsigned int chgstate;
    561	unsigned int regval;
    562	bool online;
    563	int ret;
    564
    565	ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGSTATE, &chgstate);
    566	if (ret)
    567		return ret;
    568
    569	online = !!(chgstate & CHG_STATE_USB_INPUT);
    570
    571	switch (psp) {
    572	case POWER_SUPPLY_PROP_ONLINE:
    573		val->intval = online;
    574		break;
    575	case POWER_SUPPLY_PROP_STATUS:
    576		if (!online) {
    577			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
    578			break;
    579		}
    580		val->intval = rn5t618_decode_status(chgstate);
    581		if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
    582			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
    583
    584		break;
    585	case POWER_SUPPLY_PROP_USB_TYPE:
    586		if (!online || (info->rn5t618->variant != RC5T619))
    587			return -ENODATA;
    588
    589		return rc5t619_usb_get_type(info, val);
    590	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
    591		ret = regmap_read(info->rn5t618->regmap, RN5T618_CHGCTL1,
    592				  &regval);
    593		if (ret < 0)
    594			return ret;
    595
    596		val->intval = 0;
    597		if (regval & 2) {
    598			ret = regmap_read(info->rn5t618->regmap,
    599					  RN5T618_REGISET2,
    600					  &regval);
    601			if (ret < 0)
    602				return ret;
    603
    604			val->intval = FROM_CUR_REG(regval);
    605		}
    606		break;
    607	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    608		if (!info->channel_vusb)
    609			return -ENODATA;
    610
    611		ret = iio_read_channel_processed_scale(info->channel_vusb, &val->intval, 1000);
    612		if (ret < 0)
    613			return ret;
    614
    615		break;
    616	default:
    617		return -EINVAL;
    618	}
    619
    620	return 0;
    621}
    622
    623static int rn5t618_usb_set_property(struct power_supply *psy,
    624				    enum power_supply_property psp,
    625				    const union power_supply_propval *val)
    626{
    627	struct rn5t618_power_info *info = power_supply_get_drvdata(psy);
    628	int ret;
    629
    630	switch (psp) {
    631	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
    632		if (val->intval > USB_MAX_CUR)
    633			return -EINVAL;
    634
    635		if (val->intval < CHG_MIN_CUR)
    636			return -EINVAL;
    637
    638		ret = regmap_write(info->rn5t618->regmap, RN5T618_REGISET2,
    639				   0xE0 | TO_CUR_REG(val->intval));
    640		if (ret < 0)
    641			return ret;
    642
    643		break;
    644	default:
    645		return -EINVAL;
    646	}
    647
    648	return 0;
    649}
    650
    651static int rn5t618_usb_property_is_writeable(struct power_supply *psy,
    652					     enum power_supply_property psp)
    653{
    654	switch (psp) {
    655	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
    656		return true;
    657	default:
    658		return false;
    659	}
    660}
    661
    662static const struct power_supply_desc rn5t618_battery_desc = {
    663	.name                   = "rn5t618-battery",
    664	.type                   = POWER_SUPPLY_TYPE_BATTERY,
    665	.properties             = rn5t618_battery_props,
    666	.num_properties         = ARRAY_SIZE(rn5t618_battery_props),
    667	.get_property           = rn5t618_battery_get_property,
    668	.set_property           = rn5t618_battery_set_property,
    669	.property_is_writeable  = rn5t618_battery_property_is_writeable,
    670};
    671
    672static const struct power_supply_desc rn5t618_adp_desc = {
    673	.name                   = "rn5t618-adp",
    674	.type                   = POWER_SUPPLY_TYPE_MAINS,
    675	.properties             = rn5t618_adp_props,
    676	.num_properties         = ARRAY_SIZE(rn5t618_adp_props),
    677	.get_property           = rn5t618_adp_get_property,
    678	.set_property           = rn5t618_adp_set_property,
    679	.property_is_writeable  = rn5t618_adp_property_is_writeable,
    680};
    681
    682static const struct power_supply_desc rn5t618_usb_desc = {
    683	.name                   = "rn5t618-usb",
    684	.type                   = POWER_SUPPLY_TYPE_USB,
    685	.usb_types		= rn5t618_usb_types,
    686	.num_usb_types		= ARRAY_SIZE(rn5t618_usb_types),
    687	.properties             = rn5t618_usb_props,
    688	.num_properties         = ARRAY_SIZE(rn5t618_usb_props),
    689	.get_property           = rn5t618_usb_get_property,
    690	.set_property           = rn5t618_usb_set_property,
    691	.property_is_writeable  = rn5t618_usb_property_is_writeable,
    692};
    693
    694static irqreturn_t rn5t618_charger_irq(int irq, void *data)
    695{
    696	struct device *dev = data;
    697	struct rn5t618_power_info *info = dev_get_drvdata(dev);
    698
    699	unsigned int ctrl, stat1, stat2, err;
    700
    701	regmap_read(info->rn5t618->regmap, RN5T618_CHGERR_IRR, &err);
    702	regmap_read(info->rn5t618->regmap, RN5T618_CHGCTRL_IRR, &ctrl);
    703	regmap_read(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR1, &stat1);
    704	regmap_read(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR2, &stat2);
    705
    706	regmap_write(info->rn5t618->regmap, RN5T618_CHGERR_IRR, 0);
    707	regmap_write(info->rn5t618->regmap, RN5T618_CHGCTRL_IRR, 0);
    708	regmap_write(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR1, 0);
    709	regmap_write(info->rn5t618->regmap, RN5T618_CHGSTAT_IRR2, 0);
    710
    711	dev_dbg(dev, "chgerr: %x chgctrl: %x chgstat: %x chgstat2: %x\n",
    712		err, ctrl, stat1, stat2);
    713
    714	power_supply_changed(info->usb);
    715	power_supply_changed(info->adp);
    716	power_supply_changed(info->battery);
    717
    718	return IRQ_HANDLED;
    719}
    720
    721static int rn5t618_power_probe(struct platform_device *pdev)
    722{
    723	int ret = 0;
    724	unsigned int v;
    725	struct power_supply_config psy_cfg = {};
    726	struct rn5t618_power_info *info;
    727
    728	info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
    729	if (!info)
    730		return -ENOMEM;
    731
    732	info->pdev = pdev;
    733	info->rn5t618 = dev_get_drvdata(pdev->dev.parent);
    734	info->irq = -1;
    735
    736	platform_set_drvdata(pdev, info);
    737
    738	info->channel_vusb = devm_iio_channel_get(&pdev->dev, "vusb");
    739	if (IS_ERR(info->channel_vusb)) {
    740		if (PTR_ERR(info->channel_vusb) == -ENODEV)
    741			return -EPROBE_DEFER;
    742		return PTR_ERR(info->channel_vusb);
    743	}
    744
    745	info->channel_vadp = devm_iio_channel_get(&pdev->dev, "vadp");
    746	if (IS_ERR(info->channel_vadp)) {
    747		if (PTR_ERR(info->channel_vadp) == -ENODEV)
    748			return -EPROBE_DEFER;
    749		return PTR_ERR(info->channel_vadp);
    750	}
    751
    752	ret = regmap_read(info->rn5t618->regmap, RN5T618_CONTROL, &v);
    753	if (ret)
    754		return ret;
    755
    756	if (!(v & FG_ENABLE)) {
    757		/* E.g. the vendor kernels of various Kobo and Tolino Ebook
    758		 * readers disable the fuel gauge on shutdown. If a kernel
    759		 * without fuel gauge support is booted after that, the fuel
    760		 * gauge will get decalibrated.
    761		 */
    762		dev_info(&pdev->dev, "Fuel gauge not enabled, enabling now\n");
    763		dev_info(&pdev->dev, "Expect imprecise results\n");
    764		regmap_update_bits(info->rn5t618->regmap, RN5T618_CONTROL,
    765				   FG_ENABLE, FG_ENABLE);
    766	}
    767
    768	psy_cfg.drv_data = info;
    769	info->battery = devm_power_supply_register(&pdev->dev,
    770						   &rn5t618_battery_desc,
    771						   &psy_cfg);
    772	if (IS_ERR(info->battery)) {
    773		ret = PTR_ERR(info->battery);
    774		dev_err(&pdev->dev, "failed to register battery: %d\n", ret);
    775		return ret;
    776	}
    777
    778	info->adp = devm_power_supply_register(&pdev->dev,
    779					       &rn5t618_adp_desc,
    780					       &psy_cfg);
    781	if (IS_ERR(info->adp)) {
    782		ret = PTR_ERR(info->adp);
    783		dev_err(&pdev->dev, "failed to register adp: %d\n", ret);
    784		return ret;
    785	}
    786
    787	info->usb = devm_power_supply_register(&pdev->dev,
    788					       &rn5t618_usb_desc,
    789					       &psy_cfg);
    790	if (IS_ERR(info->usb)) {
    791		ret = PTR_ERR(info->usb);
    792		dev_err(&pdev->dev, "failed to register usb: %d\n", ret);
    793		return ret;
    794	}
    795
    796	if (info->rn5t618->irq_data)
    797		info->irq = regmap_irq_get_virq(info->rn5t618->irq_data,
    798						RN5T618_IRQ_CHG);
    799
    800	if (info->irq < 0)
    801		info->irq = -1;
    802	else {
    803		ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
    804						rn5t618_charger_irq,
    805						IRQF_ONESHOT,
    806						"rn5t618_power",
    807						&pdev->dev);
    808
    809		if (ret < 0) {
    810			dev_err(&pdev->dev, "request IRQ:%d fail\n",
    811				info->irq);
    812			info->irq = -1;
    813		}
    814	}
    815
    816	return 0;
    817}
    818
    819static struct platform_driver rn5t618_power_driver = {
    820	.driver = {
    821		.name   = "rn5t618-power",
    822	},
    823	.probe = rn5t618_power_probe,
    824};
    825
    826module_platform_driver(rn5t618_power_driver);
    827MODULE_ALIAS("platform:rn5t618-power");
    828MODULE_DESCRIPTION("Power supply driver for RICOH RN5T618");
    829MODULE_LICENSE("GPL");