2019-05-29 01:10:21 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2007-05-04 04:27:45 +08:00
|
|
|
/*
|
|
|
|
* Universal power supply monitor class
|
|
|
|
*
|
|
|
|
* Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
|
|
|
|
* Copyright © 2004 Szabolcs Gyurko
|
|
|
|
* Copyright © 2003 Ian Molton <spyro@f2s.com>
|
|
|
|
*
|
|
|
|
* Modified: 2004, Oct Szabolcs Gyurko
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/init.h>
|
2010-05-19 03:49:51 +08:00
|
|
|
#include <linux/slab.h>
|
2018-06-25 15:51:48 +08:00
|
|
|
#include <linux/delay.h>
|
2007-05-04 04:27:45 +08:00
|
|
|
#include <linux/device.h>
|
2013-11-19 18:18:03 +08:00
|
|
|
#include <linux/notifier.h>
|
2007-05-04 04:27:45 +08:00
|
|
|
#include <linux/err.h>
|
2017-06-08 02:37:51 +08:00
|
|
|
#include <linux/of.h>
|
2007-05-04 04:27:45 +08:00
|
|
|
#include <linux/power_supply.h>
|
2018-05-22 23:16:23 +08:00
|
|
|
#include <linux/property.h>
|
2012-05-09 23:06:47 +08:00
|
|
|
#include <linux/thermal.h>
|
2007-05-04 04:27:45 +08:00
|
|
|
#include "power_supply.h"
|
|
|
|
|
2009-07-30 21:42:31 +08:00
|
|
|
/* exported for the APM Power driver, APM emulation */
|
2007-05-04 04:27:45 +08:00
|
|
|
struct class *power_supply_class;
|
2009-07-30 21:42:31 +08:00
|
|
|
EXPORT_SYMBOL_GPL(power_supply_class);
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2013-11-19 18:18:03 +08:00
|
|
|
ATOMIC_NOTIFIER_HEAD(power_supply_notifier);
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_notifier);
|
|
|
|
|
2010-05-19 03:49:51 +08:00
|
|
|
static struct device_type power_supply_dev_type;
|
|
|
|
|
2015-05-19 15:13:02 +08:00
|
|
|
#define POWER_SUPPLY_DEFERRED_REGISTER_TIME msecs_to_jiffies(10)
|
|
|
|
|
2013-04-02 05:45:54 +08:00
|
|
|
static bool __power_supply_is_supplied_by(struct power_supply *supplier,
|
|
|
|
struct power_supply *supply)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!supply->supplied_from && !supplier->supplied_to)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Support both supplied_to and supplied_from modes */
|
|
|
|
if (supply->supplied_from) {
|
2015-03-12 15:44:11 +08:00
|
|
|
if (!supplier->desc->name)
|
2013-04-02 05:45:54 +08:00
|
|
|
return false;
|
|
|
|
for (i = 0; i < supply->num_supplies; i++)
|
2015-03-12 15:44:11 +08:00
|
|
|
if (!strcmp(supplier->desc->name, supply->supplied_from[i]))
|
2013-04-02 05:45:54 +08:00
|
|
|
return true;
|
|
|
|
} else {
|
2015-03-12 15:44:11 +08:00
|
|
|
if (!supply->desc->name)
|
2013-04-02 05:45:54 +08:00
|
|
|
return false;
|
|
|
|
for (i = 0; i < supplier->num_supplicants; i++)
|
2015-03-12 15:44:11 +08:00
|
|
|
if (!strcmp(supplier->supplied_to[i], supply->desc->name))
|
2013-04-02 05:45:54 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-01-22 13:58:22 +08:00
|
|
|
static int __power_supply_changed_work(struct device *dev, void *data)
|
|
|
|
{
|
2014-09-04 20:01:26 +08:00
|
|
|
struct power_supply *psy = data;
|
2008-01-22 13:58:22 +08:00
|
|
|
struct power_supply *pst = dev_get_drvdata(dev);
|
|
|
|
|
2013-04-02 05:45:54 +08:00
|
|
|
if (__power_supply_is_supplied_by(psy, pst)) {
|
2015-03-12 15:44:11 +08:00
|
|
|
if (pst->desc->external_power_changed)
|
|
|
|
pst->desc->external_power_changed(pst);
|
2013-04-02 05:45:54 +08:00
|
|
|
}
|
|
|
|
|
2008-01-22 13:58:22 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-05-04 04:27:45 +08:00
|
|
|
static void power_supply_changed_work(struct work_struct *work)
|
|
|
|
{
|
2013-08-03 04:38:02 +08:00
|
|
|
unsigned long flags;
|
2007-05-04 04:27:45 +08:00
|
|
|
struct power_supply *psy = container_of(work, struct power_supply,
|
|
|
|
changed_work);
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
dev_dbg(&psy->dev, "%s\n", __func__);
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2013-08-03 04:38:02 +08:00
|
|
|
spin_lock_irqsave(&psy->changed_lock, flags);
|
2014-09-04 20:01:32 +08:00
|
|
|
/*
|
|
|
|
* Check 'changed' here to avoid issues due to race between
|
|
|
|
* power_supply_changed() and this routine. In worst case
|
|
|
|
* power_supply_changed() can be called again just before we take above
|
|
|
|
* lock. During the first call of this routine we will mark 'changed' as
|
|
|
|
* false and it will stay false for the next call as well.
|
|
|
|
*/
|
|
|
|
if (likely(psy->changed)) {
|
2013-08-03 04:38:02 +08:00
|
|
|
psy->changed = false;
|
|
|
|
spin_unlock_irqrestore(&psy->changed_lock, flags);
|
|
|
|
class_for_each_device(power_supply_class, NULL, psy,
|
|
|
|
__power_supply_changed_work);
|
|
|
|
power_supply_update_leds(psy);
|
2013-11-19 18:18:03 +08:00
|
|
|
atomic_notifier_call_chain(&power_supply_notifier,
|
|
|
|
PSY_EVENT_PROP_CHANGED, psy);
|
2015-03-12 15:44:11 +08:00
|
|
|
kobject_uevent(&psy->dev.kobj, KOBJ_CHANGE);
|
2013-08-03 04:38:02 +08:00
|
|
|
spin_lock_irqsave(&psy->changed_lock, flags);
|
|
|
|
}
|
2014-09-04 20:01:32 +08:00
|
|
|
|
2013-08-03 04:38:02 +08:00
|
|
|
/*
|
2014-09-04 20:01:32 +08:00
|
|
|
* Hold the wakeup_source until all events are processed.
|
|
|
|
* power_supply_changed() might have called again and have set 'changed'
|
|
|
|
* to true.
|
2013-08-03 04:38:02 +08:00
|
|
|
*/
|
2014-09-04 20:01:32 +08:00
|
|
|
if (likely(!psy->changed))
|
2015-03-12 15:44:11 +08:00
|
|
|
pm_relax(&psy->dev);
|
2013-08-03 04:38:02 +08:00
|
|
|
spin_unlock_irqrestore(&psy->changed_lock, flags);
|
2007-05-04 04:27:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void power_supply_changed(struct power_supply *psy)
|
|
|
|
{
|
2013-08-03 04:38:02 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
dev_dbg(&psy->dev, "%s\n", __func__);
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2013-08-03 04:38:02 +08:00
|
|
|
spin_lock_irqsave(&psy->changed_lock, flags);
|
|
|
|
psy->changed = true;
|
2015-03-12 15:44:11 +08:00
|
|
|
pm_stay_awake(&psy->dev);
|
2013-08-03 04:38:02 +08:00
|
|
|
spin_unlock_irqrestore(&psy->changed_lock, flags);
|
2007-05-04 04:27:45 +08:00
|
|
|
schedule_work(&psy->changed_work);
|
|
|
|
}
|
2009-07-30 21:42:31 +08:00
|
|
|
EXPORT_SYMBOL_GPL(power_supply_changed);
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2015-05-19 15:13:02 +08:00
|
|
|
/*
|
|
|
|
* Notify that power supply was registered after parent finished the probing.
|
|
|
|
*
|
|
|
|
* Often power supply is registered from driver's probe function. However
|
|
|
|
* calling power_supply_changed() directly from power_supply_register()
|
|
|
|
* would lead to execution of get_property() function provided by the driver
|
|
|
|
* too early - before the probe ends.
|
|
|
|
*
|
|
|
|
* Avoid that by waiting on parent's mutex.
|
|
|
|
*/
|
|
|
|
static void power_supply_deferred_register_work(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct power_supply *psy = container_of(work, struct power_supply,
|
|
|
|
deferred_register_work.work);
|
|
|
|
|
2018-06-25 15:51:48 +08:00
|
|
|
if (psy->dev.parent) {
|
|
|
|
while (!mutex_trylock(&psy->dev.parent->mutex)) {
|
|
|
|
if (psy->removing)
|
|
|
|
return;
|
|
|
|
msleep(10);
|
|
|
|
}
|
|
|
|
}
|
2015-05-19 15:13:02 +08:00
|
|
|
|
|
|
|
power_supply_changed(psy);
|
|
|
|
|
|
|
|
if (psy->dev.parent)
|
|
|
|
mutex_unlock(&psy->dev.parent->mutex);
|
|
|
|
}
|
|
|
|
|
2013-04-02 05:45:55 +08:00
|
|
|
#ifdef CONFIG_OF
|
|
|
|
static int __power_supply_populate_supplied_from(struct device *dev,
|
|
|
|
void *data)
|
|
|
|
{
|
2014-09-04 20:01:26 +08:00
|
|
|
struct power_supply *psy = data;
|
2013-04-02 05:45:55 +08:00
|
|
|
struct power_supply *epsy = dev_get_drvdata(dev);
|
|
|
|
struct device_node *np;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
np = of_parse_phandle(psy->of_node, "power-supplies", i++);
|
|
|
|
if (!np)
|
2014-09-04 20:01:27 +08:00
|
|
|
break;
|
2013-04-02 05:45:55 +08:00
|
|
|
|
|
|
|
if (np == epsy->of_node) {
|
2015-03-12 15:44:11 +08:00
|
|
|
dev_info(&psy->dev, "%s: Found supply : %s\n",
|
|
|
|
psy->desc->name, epsy->desc->name);
|
|
|
|
psy->supplied_from[i-1] = (char *)epsy->desc->name;
|
2013-04-02 05:45:55 +08:00
|
|
|
psy->num_supplies++;
|
2013-06-11 05:26:39 +08:00
|
|
|
of_node_put(np);
|
2013-04-02 05:45:55 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-06-11 05:26:39 +08:00
|
|
|
of_node_put(np);
|
2013-04-02 05:45:55 +08:00
|
|
|
} while (np);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int power_supply_populate_supplied_from(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = class_for_each_device(power_supply_class, NULL, psy,
|
|
|
|
__power_supply_populate_supplied_from);
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
dev_dbg(&psy->dev, "%s %d\n", __func__, error);
|
2013-04-02 05:45:55 +08:00
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __power_supply_find_supply_from_node(struct device *dev,
|
|
|
|
void *data)
|
|
|
|
{
|
2014-09-04 20:01:26 +08:00
|
|
|
struct device_node *np = data;
|
2013-04-02 05:45:55 +08:00
|
|
|
struct power_supply *epsy = dev_get_drvdata(dev);
|
|
|
|
|
2014-09-04 20:01:30 +08:00
|
|
|
/* returning non-zero breaks out of class_for_each_device loop */
|
2013-04-02 05:45:55 +08:00
|
|
|
if (epsy->of_node == np)
|
2014-09-04 20:01:30 +08:00
|
|
|
return 1;
|
2013-04-02 05:45:55 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int power_supply_find_supply_from_node(struct device_node *supply_node)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
/*
|
2014-09-04 20:01:30 +08:00
|
|
|
* class_for_each_device() either returns its own errors or values
|
|
|
|
* returned by __power_supply_find_supply_from_node().
|
|
|
|
*
|
|
|
|
* __power_supply_find_supply_from_node() will return 0 (no match)
|
|
|
|
* or 1 (match).
|
|
|
|
*
|
|
|
|
* We return 0 if class_for_each_device() returned 1, -EPROBE_DEFER if
|
|
|
|
* it returned 0, or error as returned by it.
|
2013-04-02 05:45:55 +08:00
|
|
|
*/
|
|
|
|
error = class_for_each_device(power_supply_class, NULL, supply_node,
|
|
|
|
__power_supply_find_supply_from_node);
|
|
|
|
|
2014-09-04 20:01:30 +08:00
|
|
|
return error ? (error == 1 ? 0 : error) : -EPROBE_DEFER;
|
2013-04-02 05:45:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int power_supply_check_supplies(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
struct device_node *np;
|
|
|
|
int cnt = 0;
|
|
|
|
|
|
|
|
/* If there is already a list honor it */
|
|
|
|
if (psy->supplied_from && psy->num_supplies > 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* No device node found, nothing to do */
|
|
|
|
if (!psy->of_node)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
np = of_parse_phandle(psy->of_node, "power-supplies", cnt++);
|
|
|
|
if (!np)
|
2014-09-04 20:01:27 +08:00
|
|
|
break;
|
2013-04-02 05:45:55 +08:00
|
|
|
|
|
|
|
ret = power_supply_find_supply_from_node(np);
|
2014-09-04 20:01:28 +08:00
|
|
|
of_node_put(np);
|
|
|
|
|
2013-04-02 05:45:55 +08:00
|
|
|
if (ret) {
|
2015-03-12 15:44:11 +08:00
|
|
|
dev_dbg(&psy->dev, "Failed to find supply!\n");
|
2014-09-04 20:01:29 +08:00
|
|
|
return ret;
|
2013-04-02 05:45:55 +08:00
|
|
|
}
|
|
|
|
} while (np);
|
|
|
|
|
2014-09-04 20:01:23 +08:00
|
|
|
/* Missing valid "power-supplies" entries */
|
|
|
|
if (cnt == 1)
|
|
|
|
return 0;
|
|
|
|
|
2013-04-02 05:45:55 +08:00
|
|
|
/* All supplies found, allocate char ** array for filling */
|
2015-03-12 15:44:11 +08:00
|
|
|
psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(psy->supplied_from),
|
2013-04-02 05:45:55 +08:00
|
|
|
GFP_KERNEL);
|
2017-08-27 01:22:32 +08:00
|
|
|
if (!psy->supplied_from)
|
2013-04-02 05:45:55 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 05:07:58 +08:00
|
|
|
*psy->supplied_from = devm_kcalloc(&psy->dev,
|
|
|
|
cnt - 1, sizeof(char *),
|
2013-04-02 05:45:55 +08:00
|
|
|
GFP_KERNEL);
|
2017-08-27 01:22:32 +08:00
|
|
|
if (!*psy->supplied_from)
|
2013-04-02 05:45:55 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
return power_supply_populate_supplied_from(psy);
|
|
|
|
}
|
|
|
|
#else
|
2017-05-08 23:13:54 +08:00
|
|
|
static int power_supply_check_supplies(struct power_supply *psy)
|
2013-04-02 05:45:55 +08:00
|
|
|
{
|
2017-05-08 23:13:54 +08:00
|
|
|
int nval, ret;
|
|
|
|
|
|
|
|
if (!psy->dev.parent)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nval = device_property_read_string_array(psy->dev.parent,
|
|
|
|
"supplied-from", NULL, 0);
|
|
|
|
if (nval <= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
psy->supplied_from = devm_kmalloc_array(&psy->dev, nval,
|
|
|
|
sizeof(char *), GFP_KERNEL);
|
|
|
|
if (!psy->supplied_from)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = device_property_read_string_array(psy->dev.parent,
|
|
|
|
"supplied-from", (const char **)psy->supplied_from, nval);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
psy->num_supplies = nval;
|
|
|
|
|
2013-04-02 05:45:55 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-04-16 23:30:31 +08:00
|
|
|
struct psy_am_i_supplied_data {
|
|
|
|
struct power_supply *psy;
|
|
|
|
unsigned int count;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __power_supply_am_i_supplied(struct device *dev, void *_data)
|
2007-05-04 04:27:45 +08:00
|
|
|
{
|
|
|
|
union power_supply_propval ret = {0,};
|
2008-01-22 13:58:22 +08:00
|
|
|
struct power_supply *epsy = dev_get_drvdata(dev);
|
2017-04-16 23:30:31 +08:00
|
|
|
struct psy_am_i_supplied_data *data = _data;
|
2008-01-22 13:58:22 +08:00
|
|
|
|
2017-08-07 00:23:52 +08:00
|
|
|
if (__power_supply_is_supplied_by(epsy, data->psy)) {
|
|
|
|
data->count++;
|
2015-03-12 15:44:11 +08:00
|
|
|
if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE,
|
|
|
|
&ret))
|
2014-09-04 20:01:31 +08:00
|
|
|
return ret.intval;
|
2017-08-07 00:23:52 +08:00
|
|
|
}
|
2013-04-02 05:45:54 +08:00
|
|
|
|
2008-01-22 13:58:22 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int power_supply_am_i_supplied(struct power_supply *psy)
|
|
|
|
{
|
2017-04-16 23:30:31 +08:00
|
|
|
struct psy_am_i_supplied_data data = { psy, 0 };
|
2008-01-22 13:58:22 +08:00
|
|
|
int error;
|
|
|
|
|
2017-04-16 23:30:31 +08:00
|
|
|
error = class_for_each_device(power_supply_class, NULL, &data,
|
2008-01-22 13:58:22 +08:00
|
|
|
__power_supply_am_i_supplied);
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2017-04-16 23:30:31 +08:00
|
|
|
dev_dbg(&psy->dev, "%s count %u err %d\n", __func__, data.count, error);
|
|
|
|
|
|
|
|
if (data.count == 0)
|
|
|
|
return -ENODEV;
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2008-01-22 13:58:22 +08:00
|
|
|
return error;
|
2007-05-04 04:27:45 +08:00
|
|
|
}
|
2009-07-30 21:42:31 +08:00
|
|
|
EXPORT_SYMBOL_GPL(power_supply_am_i_supplied);
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2008-08-27 04:09:59 +08:00
|
|
|
static int __power_supply_is_system_supplied(struct device *dev, void *data)
|
|
|
|
{
|
|
|
|
union power_supply_propval ret = {0,};
|
|
|
|
struct power_supply *psy = dev_get_drvdata(dev);
|
2011-12-11 05:53:36 +08:00
|
|
|
unsigned int *count = data;
|
2008-08-27 04:09:59 +08:00
|
|
|
|
2011-12-11 05:53:36 +08:00
|
|
|
(*count)++;
|
2015-03-12 15:44:11 +08:00
|
|
|
if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY)
|
|
|
|
if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE,
|
|
|
|
&ret))
|
2008-08-27 04:09:59 +08:00
|
|
|
return ret.intval;
|
2014-09-04 20:01:31 +08:00
|
|
|
|
2008-08-27 04:09:59 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int power_supply_is_system_supplied(void)
|
|
|
|
{
|
|
|
|
int error;
|
2011-12-11 05:53:36 +08:00
|
|
|
unsigned int count = 0;
|
2008-08-27 04:09:59 +08:00
|
|
|
|
2011-12-11 05:53:36 +08:00
|
|
|
error = class_for_each_device(power_supply_class, NULL, &count,
|
2008-08-27 04:09:59 +08:00
|
|
|
__power_supply_is_system_supplied);
|
|
|
|
|
2011-12-11 05:53:36 +08:00
|
|
|
/*
|
|
|
|
* If no power class device was found at all, most probably we are
|
|
|
|
* running on a desktop system, so assume we are on mains power.
|
|
|
|
*/
|
|
|
|
if (count == 0)
|
|
|
|
return 1;
|
|
|
|
|
2008-08-27 04:09:59 +08:00
|
|
|
return error;
|
|
|
|
}
|
2009-07-30 21:42:31 +08:00
|
|
|
EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
|
2008-08-27 04:09:59 +08:00
|
|
|
|
2017-08-16 04:04:56 +08:00
|
|
|
static int __power_supply_get_supplier_max_current(struct device *dev,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
union power_supply_propval ret = {0,};
|
|
|
|
struct power_supply *epsy = dev_get_drvdata(dev);
|
|
|
|
struct power_supply *psy = data;
|
|
|
|
|
|
|
|
if (__power_supply_is_supplied_by(epsy, psy))
|
|
|
|
if (!epsy->desc->get_property(epsy,
|
|
|
|
POWER_SUPPLY_PROP_CURRENT_MAX,
|
|
|
|
&ret))
|
|
|
|
return ret.intval;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int power_supply_set_input_current_limit_from_supplier(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
union power_supply_propval val = {0,};
|
|
|
|
int curr;
|
|
|
|
|
|
|
|
if (!psy->desc->set_property)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This function is not intended for use with a supply with multiple
|
|
|
|
* suppliers, we simply pick the first supply to report a non 0
|
|
|
|
* max-current.
|
|
|
|
*/
|
|
|
|
curr = class_for_each_device(power_supply_class, NULL, psy,
|
|
|
|
__power_supply_get_supplier_max_current);
|
|
|
|
if (curr <= 0)
|
|
|
|
return (curr == 0) ? -ENODEV : curr;
|
|
|
|
|
|
|
|
val.intval = curr;
|
|
|
|
|
|
|
|
return psy->desc->set_property(psy,
|
|
|
|
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_set_input_current_limit_from_supplier);
|
|
|
|
|
2009-07-24 02:35:53 +08:00
|
|
|
int power_supply_set_battery_charged(struct power_supply *psy)
|
|
|
|
{
|
2015-03-12 15:44:03 +08:00
|
|
|
if (atomic_read(&psy->use_cnt) >= 0 &&
|
2015-03-12 15:44:11 +08:00
|
|
|
psy->desc->type == POWER_SUPPLY_TYPE_BATTERY &&
|
|
|
|
psy->desc->set_charged) {
|
|
|
|
psy->desc->set_charged(psy);
|
2009-07-24 02:35:53 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_set_battery_charged);
|
|
|
|
|
2013-02-02 03:40:17 +08:00
|
|
|
static int power_supply_match_device_by_name(struct device *dev, const void *data)
|
2009-07-24 02:35:53 +08:00
|
|
|
{
|
|
|
|
const char *name = data;
|
|
|
|
struct power_supply *psy = dev_get_drvdata(dev);
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
return strcmp(psy->desc->name, name) == 0;
|
2009-07-24 02:35:53 +08:00
|
|
|
}
|
|
|
|
|
2015-03-12 15:44:12 +08:00
|
|
|
/**
|
|
|
|
* power_supply_get_by_name() - Search for a power supply and returns its ref
|
|
|
|
* @name: Power supply name to fetch
|
|
|
|
*
|
|
|
|
* If power supply was found, it increases reference count for the
|
|
|
|
* internal power supply's device. The user should power_supply_put()
|
|
|
|
* after usage.
|
|
|
|
*
|
|
|
|
* Return: On success returns a reference to a power supply with
|
|
|
|
* matching name equals to @name, a NULL otherwise.
|
|
|
|
*/
|
2013-02-02 03:40:17 +08:00
|
|
|
struct power_supply *power_supply_get_by_name(const char *name)
|
2009-07-24 02:35:53 +08:00
|
|
|
{
|
2015-03-12 15:44:13 +08:00
|
|
|
struct power_supply *psy = NULL;
|
2009-07-24 02:35:53 +08:00
|
|
|
struct device *dev = class_find_device(power_supply_class, NULL, name,
|
|
|
|
power_supply_match_device_by_name);
|
|
|
|
|
2015-03-12 15:44:13 +08:00
|
|
|
if (dev) {
|
|
|
|
psy = dev_get_drvdata(dev);
|
|
|
|
atomic_inc(&psy->use_cnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
return psy;
|
2009-07-24 02:35:53 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_get_by_name);
|
|
|
|
|
2015-03-12 15:44:12 +08:00
|
|
|
/**
|
|
|
|
* power_supply_put() - Drop reference obtained with power_supply_get_by_name
|
|
|
|
* @psy: Reference to put
|
|
|
|
*
|
|
|
|
* The reference to power supply should be put before unregistering
|
|
|
|
* the power supply.
|
|
|
|
*/
|
|
|
|
void power_supply_put(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
might_sleep();
|
|
|
|
|
2015-03-12 15:44:13 +08:00
|
|
|
atomic_dec(&psy->use_cnt);
|
2015-03-12 15:44:12 +08:00
|
|
|
put_device(&psy->dev);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_put);
|
|
|
|
|
2013-11-25 00:49:29 +08:00
|
|
|
#ifdef CONFIG_OF
|
|
|
|
static int power_supply_match_device_node(struct device *dev, const void *data)
|
|
|
|
{
|
|
|
|
return dev->parent && dev->parent->of_node == data;
|
|
|
|
}
|
|
|
|
|
2015-03-12 15:44:12 +08:00
|
|
|
/**
|
|
|
|
* power_supply_get_by_phandle() - Search for a power supply and returns its ref
|
|
|
|
* @np: Pointer to device node holding phandle property
|
2016-10-02 03:46:25 +08:00
|
|
|
* @property: Name of property holding a power supply name
|
2015-03-12 15:44:12 +08:00
|
|
|
*
|
|
|
|
* If power supply was found, it increases reference count for the
|
|
|
|
* internal power supply's device. The user should power_supply_put()
|
|
|
|
* after usage.
|
|
|
|
*
|
|
|
|
* Return: On success returns a reference to a power supply with
|
|
|
|
* matching name equals to value under @property, NULL or ERR_PTR otherwise.
|
|
|
|
*/
|
2013-11-25 00:49:29 +08:00
|
|
|
struct power_supply *power_supply_get_by_phandle(struct device_node *np,
|
|
|
|
const char *property)
|
|
|
|
{
|
|
|
|
struct device_node *power_supply_np;
|
2015-03-12 15:44:13 +08:00
|
|
|
struct power_supply *psy = NULL;
|
2013-11-25 00:49:29 +08:00
|
|
|
struct device *dev;
|
|
|
|
|
|
|
|
power_supply_np = of_parse_phandle(np, property, 0);
|
|
|
|
if (!power_supply_np)
|
|
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
|
|
|
|
dev = class_find_device(power_supply_class, NULL, power_supply_np,
|
|
|
|
power_supply_match_device_node);
|
|
|
|
|
|
|
|
of_node_put(power_supply_np);
|
|
|
|
|
2015-03-12 15:44:13 +08:00
|
|
|
if (dev) {
|
|
|
|
psy = dev_get_drvdata(dev);
|
|
|
|
atomic_inc(&psy->use_cnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
return psy;
|
2013-11-25 00:49:29 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_get_by_phandle);
|
2015-06-10 05:37:56 +08:00
|
|
|
|
|
|
|
static void devm_power_supply_put(struct device *dev, void *res)
|
|
|
|
{
|
|
|
|
struct power_supply **psy = res;
|
|
|
|
|
|
|
|
power_supply_put(*psy);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* devm_power_supply_get_by_phandle() - Resource managed version of
|
|
|
|
* power_supply_get_by_phandle()
|
|
|
|
* @dev: Pointer to device holding phandle property
|
2016-10-02 03:46:25 +08:00
|
|
|
* @property: Name of property holding a power supply phandle
|
2015-06-10 05:37:56 +08:00
|
|
|
*
|
|
|
|
* Return: On success returns a reference to a power supply with
|
|
|
|
* matching name equals to value under @property, NULL or ERR_PTR otherwise.
|
|
|
|
*/
|
|
|
|
struct power_supply *devm_power_supply_get_by_phandle(struct device *dev,
|
|
|
|
const char *property)
|
|
|
|
{
|
|
|
|
struct power_supply **ptr, *psy;
|
|
|
|
|
|
|
|
if (!dev->of_node)
|
|
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
|
|
|
|
ptr = devres_alloc(devm_power_supply_put, sizeof(*ptr), GFP_KERNEL);
|
|
|
|
if (!ptr)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
psy = power_supply_get_by_phandle(dev->of_node, property);
|
|
|
|
if (IS_ERR_OR_NULL(psy)) {
|
|
|
|
devres_free(ptr);
|
|
|
|
} else {
|
|
|
|
*ptr = psy;
|
|
|
|
devres_add(dev, ptr);
|
|
|
|
}
|
|
|
|
return psy;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle);
|
2013-11-25 00:49:29 +08:00
|
|
|
#endif /* CONFIG_OF */
|
|
|
|
|
2017-06-08 02:37:51 +08:00
|
|
|
int power_supply_get_battery_info(struct power_supply *psy,
|
|
|
|
struct power_supply_battery_info *info)
|
|
|
|
{
|
2019-12-09 11:56:22 +08:00
|
|
|
struct power_supply_resistance_temp_table *resist_table;
|
2017-06-08 02:37:51 +08:00
|
|
|
struct device_node *battery_np;
|
|
|
|
const char *value;
|
2018-11-05 15:39:09 +08:00
|
|
|
int err, len, index;
|
2019-12-09 11:56:22 +08:00
|
|
|
const __be32 *list;
|
2017-06-08 02:37:51 +08:00
|
|
|
|
|
|
|
info->energy_full_design_uwh = -EINVAL;
|
|
|
|
info->charge_full_design_uah = -EINVAL;
|
|
|
|
info->voltage_min_design_uv = -EINVAL;
|
2019-02-17 22:29:11 +08:00
|
|
|
info->voltage_max_design_uv = -EINVAL;
|
2017-06-08 02:37:51 +08:00
|
|
|
info->precharge_current_ua = -EINVAL;
|
|
|
|
info->charge_term_current_ua = -EINVAL;
|
|
|
|
info->constant_charge_current_max_ua = -EINVAL;
|
|
|
|
info->constant_charge_voltage_max_uv = -EINVAL;
|
2018-11-05 15:39:07 +08:00
|
|
|
info->factory_internal_resistance_uohm = -EINVAL;
|
2019-12-09 11:56:22 +08:00
|
|
|
info->resist_table = NULL;
|
2017-06-08 02:37:51 +08:00
|
|
|
|
2018-11-05 15:39:09 +08:00
|
|
|
for (index = 0; index < POWER_SUPPLY_OCV_TEMP_MAX; index++) {
|
|
|
|
info->ocv_table[index] = NULL;
|
|
|
|
info->ocv_temp[index] = -EINVAL;
|
|
|
|
info->ocv_table_size[index] = -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-06-08 02:37:51 +08:00
|
|
|
if (!psy->of_node) {
|
|
|
|
dev_warn(&psy->dev, "%s currently only supports devicetree\n",
|
|
|
|
__func__);
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
battery_np = of_parse_phandle(psy->of_node, "monitored-battery", 0);
|
|
|
|
if (!battery_np)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
err = of_property_read_string(battery_np, "compatible", &value);
|
|
|
|
if (err)
|
2019-04-17 10:43:03 +08:00
|
|
|
goto out_put_node;
|
2017-06-08 02:37:51 +08:00
|
|
|
|
2019-04-17 10:43:03 +08:00
|
|
|
if (strcmp("simple-battery", value)) {
|
|
|
|
err = -ENODEV;
|
|
|
|
goto out_put_node;
|
|
|
|
}
|
2017-06-08 02:37:51 +08:00
|
|
|
|
|
|
|
/* The property and field names below must correspond to elements
|
|
|
|
* in enum power_supply_property. For reasoning, see
|
2019-06-13 18:10:36 +08:00
|
|
|
* Documentation/power/power_supply_class.rst.
|
2017-06-08 02:37:51 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
of_property_read_u32(battery_np, "energy-full-design-microwatt-hours",
|
|
|
|
&info->energy_full_design_uwh);
|
|
|
|
of_property_read_u32(battery_np, "charge-full-design-microamp-hours",
|
|
|
|
&info->charge_full_design_uah);
|
|
|
|
of_property_read_u32(battery_np, "voltage-min-design-microvolt",
|
|
|
|
&info->voltage_min_design_uv);
|
2019-02-17 22:29:11 +08:00
|
|
|
of_property_read_u32(battery_np, "voltage-max-design-microvolt",
|
|
|
|
&info->voltage_max_design_uv);
|
2020-05-08 23:47:20 +08:00
|
|
|
of_property_read_u32(battery_np, "trickle-charge-current-microamp",
|
|
|
|
&info->tricklecharge_current_ua);
|
2017-06-08 02:37:51 +08:00
|
|
|
of_property_read_u32(battery_np, "precharge-current-microamp",
|
|
|
|
&info->precharge_current_ua);
|
2020-05-08 23:47:20 +08:00
|
|
|
of_property_read_u32(battery_np, "precharge-upper-limit-microvolt",
|
|
|
|
&info->precharge_voltage_max_uv);
|
2017-06-08 02:37:51 +08:00
|
|
|
of_property_read_u32(battery_np, "charge-term-current-microamp",
|
|
|
|
&info->charge_term_current_ua);
|
2020-05-08 23:47:20 +08:00
|
|
|
of_property_read_u32(battery_np, "re-charge-voltage-microvolt",
|
|
|
|
&info->charge_restart_voltage_uv);
|
|
|
|
of_property_read_u32(battery_np, "over-voltage-threshold-microvolt",
|
|
|
|
&info->overvoltage_limit_uv);
|
2019-03-18 21:36:29 +08:00
|
|
|
of_property_read_u32(battery_np, "constant-charge-current-max-microamp",
|
2017-06-08 02:37:51 +08:00
|
|
|
&info->constant_charge_current_max_ua);
|
2019-03-18 21:36:29 +08:00
|
|
|
of_property_read_u32(battery_np, "constant-charge-voltage-max-microvolt",
|
2017-06-08 02:37:51 +08:00
|
|
|
&info->constant_charge_voltage_max_uv);
|
2018-11-05 15:39:07 +08:00
|
|
|
of_property_read_u32(battery_np, "factory-internal-resistance-micro-ohms",
|
|
|
|
&info->factory_internal_resistance_uohm);
|
2017-06-08 02:37:51 +08:00
|
|
|
|
2018-11-05 15:39:09 +08:00
|
|
|
len = of_property_count_u32_elems(battery_np, "ocv-capacity-celsius");
|
|
|
|
if (len < 0 && len != -EINVAL) {
|
2019-04-17 10:43:03 +08:00
|
|
|
err = len;
|
|
|
|
goto out_put_node;
|
2018-11-05 15:39:09 +08:00
|
|
|
} else if (len > POWER_SUPPLY_OCV_TEMP_MAX) {
|
|
|
|
dev_err(&psy->dev, "Too many temperature values\n");
|
2019-04-17 10:43:03 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out_put_node;
|
2018-11-05 15:39:09 +08:00
|
|
|
} else if (len > 0) {
|
|
|
|
of_property_read_u32_array(battery_np, "ocv-capacity-celsius",
|
|
|
|
info->ocv_temp, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (index = 0; index < len; index++) {
|
|
|
|
struct power_supply_battery_ocv_table *table;
|
|
|
|
char *propname;
|
|
|
|
int i, tab_len, size;
|
|
|
|
|
|
|
|
propname = kasprintf(GFP_KERNEL, "ocv-capacity-table-%d", index);
|
|
|
|
list = of_get_property(battery_np, propname, &size);
|
|
|
|
if (!list || !size) {
|
|
|
|
dev_err(&psy->dev, "failed to get %s\n", propname);
|
|
|
|
kfree(propname);
|
|
|
|
power_supply_put_battery_info(psy, info);
|
2019-04-17 10:43:03 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out_put_node;
|
2018-11-05 15:39:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
kfree(propname);
|
|
|
|
tab_len = size / (2 * sizeof(__be32));
|
|
|
|
info->ocv_table_size[index] = tab_len;
|
|
|
|
|
|
|
|
table = info->ocv_table[index] =
|
|
|
|
devm_kcalloc(&psy->dev, tab_len, sizeof(*table), GFP_KERNEL);
|
|
|
|
if (!info->ocv_table[index]) {
|
|
|
|
power_supply_put_battery_info(psy, info);
|
2019-04-17 10:43:03 +08:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto out_put_node;
|
2018-11-05 15:39:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < tab_len; i++) {
|
2019-05-02 09:27:06 +08:00
|
|
|
table[i].ocv = be32_to_cpu(*list);
|
|
|
|
list++;
|
|
|
|
table[i].capacity = be32_to_cpu(*list);
|
|
|
|
list++;
|
2018-11-05 15:39:09 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-09 11:56:22 +08:00
|
|
|
list = of_get_property(battery_np, "resistance-temp-table", &len);
|
|
|
|
if (!list || !len)
|
|
|
|
goto out_put_node;
|
|
|
|
|
|
|
|
info->resist_table_size = len / (2 * sizeof(__be32));
|
|
|
|
resist_table = info->resist_table = devm_kcalloc(&psy->dev,
|
|
|
|
info->resist_table_size,
|
|
|
|
sizeof(*resist_table),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!info->resist_table) {
|
|
|
|
power_supply_put_battery_info(psy, info);
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out_put_node;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (index = 0; index < info->resist_table_size; index++) {
|
|
|
|
resist_table[index].temp = be32_to_cpu(*list++);
|
|
|
|
resist_table[index].resistance = be32_to_cpu(*list++);
|
|
|
|
}
|
|
|
|
|
2019-04-17 10:43:03 +08:00
|
|
|
out_put_node:
|
|
|
|
of_node_put(battery_np);
|
|
|
|
return err;
|
2017-06-08 02:37:51 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_get_battery_info);
|
|
|
|
|
2018-11-05 15:39:09 +08:00
|
|
|
void power_supply_put_battery_info(struct power_supply *psy,
|
|
|
|
struct power_supply_battery_info *info)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < POWER_SUPPLY_OCV_TEMP_MAX; i++) {
|
|
|
|
if (info->ocv_table[i])
|
|
|
|
devm_kfree(&psy->dev, info->ocv_table[i]);
|
|
|
|
}
|
2019-12-09 11:56:22 +08:00
|
|
|
|
|
|
|
if (info->resist_table)
|
|
|
|
devm_kfree(&psy->dev, info->resist_table);
|
2018-11-05 15:39:09 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_put_battery_info);
|
|
|
|
|
2019-12-09 11:56:22 +08:00
|
|
|
/**
|
|
|
|
* power_supply_temp2resist_simple() - find the battery internal resistance
|
|
|
|
* percent
|
|
|
|
* @table: Pointer to battery resistance temperature table
|
|
|
|
* @table_len: The table length
|
2020-07-29 15:43:48 +08:00
|
|
|
* @temp: Current temperature
|
2019-12-09 11:56:22 +08:00
|
|
|
*
|
|
|
|
* This helper function is used to look up battery internal resistance percent
|
|
|
|
* according to current temperature value from the resistance temperature table,
|
|
|
|
* and the table must be ordered descending. Then the actual battery internal
|
|
|
|
* resistance = the ideal battery internal resistance * percent / 100.
|
|
|
|
*
|
|
|
|
* Return: the battery internal resistance percent
|
|
|
|
*/
|
|
|
|
int power_supply_temp2resist_simple(struct power_supply_resistance_temp_table *table,
|
|
|
|
int table_len, int temp)
|
|
|
|
{
|
|
|
|
int i, resist;
|
|
|
|
|
|
|
|
for (i = 0; i < table_len; i++)
|
|
|
|
if (temp > table[i].temp)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (i > 0 && i < table_len) {
|
|
|
|
int tmp;
|
|
|
|
|
|
|
|
tmp = (table[i - 1].resistance - table[i].resistance) *
|
|
|
|
(temp - table[i].temp);
|
|
|
|
tmp /= table[i - 1].temp - table[i].temp;
|
|
|
|
resist = tmp + table[i].resistance;
|
|
|
|
} else if (i == 0) {
|
|
|
|
resist = table[0].resistance;
|
|
|
|
} else {
|
|
|
|
resist = table[table_len - 1].resistance;
|
|
|
|
}
|
|
|
|
|
|
|
|
return resist;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_temp2resist_simple);
|
|
|
|
|
2018-11-05 15:39:09 +08:00
|
|
|
/**
|
|
|
|
* power_supply_ocv2cap_simple() - find the battery capacity
|
|
|
|
* @table: Pointer to battery OCV lookup table
|
|
|
|
* @table_len: OCV table length
|
|
|
|
* @ocv: Current OCV value
|
|
|
|
*
|
|
|
|
* This helper function is used to look up battery capacity according to
|
|
|
|
* current OCV value from one OCV table, and the OCV table must be ordered
|
|
|
|
* descending.
|
|
|
|
*
|
|
|
|
* Return: the battery capacity.
|
|
|
|
*/
|
|
|
|
int power_supply_ocv2cap_simple(struct power_supply_battery_ocv_table *table,
|
|
|
|
int table_len, int ocv)
|
|
|
|
{
|
|
|
|
int i, cap, tmp;
|
|
|
|
|
|
|
|
for (i = 0; i < table_len; i++)
|
|
|
|
if (ocv > table[i].ocv)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (i > 0 && i < table_len) {
|
|
|
|
tmp = (table[i - 1].capacity - table[i].capacity) *
|
|
|
|
(ocv - table[i].ocv);
|
|
|
|
tmp /= table[i - 1].ocv - table[i].ocv;
|
|
|
|
cap = tmp + table[i].capacity;
|
|
|
|
} else if (i == 0) {
|
|
|
|
cap = table[0].capacity;
|
|
|
|
} else {
|
|
|
|
cap = table[table_len - 1].capacity;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cap;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_ocv2cap_simple);
|
|
|
|
|
|
|
|
struct power_supply_battery_ocv_table *
|
|
|
|
power_supply_find_ocv2cap_table(struct power_supply_battery_info *info,
|
|
|
|
int temp, int *table_len)
|
|
|
|
{
|
|
|
|
int best_temp_diff = INT_MAX, temp_diff;
|
|
|
|
u8 i, best_index = 0;
|
|
|
|
|
|
|
|
if (!info->ocv_table[0])
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < POWER_SUPPLY_OCV_TEMP_MAX; i++) {
|
|
|
|
temp_diff = abs(info->ocv_temp[i] - temp);
|
|
|
|
|
|
|
|
if (temp_diff < best_temp_diff) {
|
|
|
|
best_temp_diff = temp_diff;
|
|
|
|
best_index = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*table_len = info->ocv_table_size[best_index];
|
|
|
|
return info->ocv_table[best_index];
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_find_ocv2cap_table);
|
|
|
|
|
|
|
|
int power_supply_batinfo_ocv2cap(struct power_supply_battery_info *info,
|
|
|
|
int ocv, int temp)
|
|
|
|
{
|
|
|
|
struct power_supply_battery_ocv_table *table;
|
|
|
|
int table_len;
|
|
|
|
|
|
|
|
table = power_supply_find_ocv2cap_table(info, temp, &table_len);
|
|
|
|
if (!table)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return power_supply_ocv2cap_simple(table, table_len, ocv);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_batinfo_ocv2cap);
|
|
|
|
|
2015-03-12 15:44:03 +08:00
|
|
|
int power_supply_get_property(struct power_supply *psy,
|
|
|
|
enum power_supply_property psp,
|
|
|
|
union power_supply_propval *val)
|
|
|
|
{
|
2016-06-22 23:45:52 +08:00
|
|
|
if (atomic_read(&psy->use_cnt) <= 0) {
|
|
|
|
if (!psy->initialized)
|
|
|
|
return -EAGAIN;
|
2015-03-12 15:44:03 +08:00
|
|
|
return -ENODEV;
|
2016-06-22 23:45:52 +08:00
|
|
|
}
|
2015-03-12 15:44:03 +08:00
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
return psy->desc->get_property(psy, psp, val);
|
2015-03-12 15:44:03 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_get_property);
|
|
|
|
|
|
|
|
int power_supply_set_property(struct power_supply *psy,
|
|
|
|
enum power_supply_property psp,
|
|
|
|
const union power_supply_propval *val)
|
|
|
|
{
|
2015-03-12 15:44:11 +08:00
|
|
|
if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->set_property)
|
2015-03-12 15:44:03 +08:00
|
|
|
return -ENODEV;
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
return psy->desc->set_property(psy, psp, val);
|
2015-03-12 15:44:03 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_set_property);
|
|
|
|
|
|
|
|
int power_supply_property_is_writeable(struct power_supply *psy,
|
|
|
|
enum power_supply_property psp)
|
|
|
|
{
|
2015-03-12 15:44:11 +08:00
|
|
|
if (atomic_read(&psy->use_cnt) <= 0 ||
|
|
|
|
!psy->desc->property_is_writeable)
|
2015-03-12 15:44:03 +08:00
|
|
|
return -ENODEV;
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
return psy->desc->property_is_writeable(psy, psp);
|
2015-03-12 15:44:03 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_property_is_writeable);
|
|
|
|
|
|
|
|
void power_supply_external_power_changed(struct power_supply *psy)
|
|
|
|
{
|
2015-03-12 15:44:11 +08:00
|
|
|
if (atomic_read(&psy->use_cnt) <= 0 ||
|
|
|
|
!psy->desc->external_power_changed)
|
2015-03-12 15:44:03 +08:00
|
|
|
return;
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
psy->desc->external_power_changed(psy);
|
2015-03-12 15:44:03 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_external_power_changed);
|
|
|
|
|
2011-12-08 01:15:45 +08:00
|
|
|
int power_supply_powers(struct power_supply *psy, struct device *dev)
|
|
|
|
{
|
2015-03-12 15:44:11 +08:00
|
|
|
return sysfs_create_link(&psy->dev.kobj, &dev->kobj, "powers");
|
2011-12-08 01:15:45 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_powers);
|
|
|
|
|
2010-05-19 03:49:51 +08:00
|
|
|
static void power_supply_dev_release(struct device *dev)
|
|
|
|
{
|
2018-02-07 22:58:27 +08:00
|
|
|
struct power_supply *psy = to_power_supply(dev);
|
2017-10-10 22:39:18 +08:00
|
|
|
dev_dbg(dev, "%s\n", __func__);
|
2015-03-12 15:44:11 +08:00
|
|
|
kfree(psy);
|
2010-05-19 03:49:51 +08:00
|
|
|
}
|
|
|
|
|
2013-11-19 18:18:03 +08:00
|
|
|
int power_supply_reg_notifier(struct notifier_block *nb)
|
|
|
|
{
|
|
|
|
return atomic_notifier_chain_register(&power_supply_notifier, nb);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_reg_notifier);
|
|
|
|
|
|
|
|
void power_supply_unreg_notifier(struct notifier_block *nb)
|
|
|
|
{
|
|
|
|
atomic_notifier_chain_unregister(&power_supply_notifier, nb);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_unreg_notifier);
|
|
|
|
|
2012-05-09 23:06:47 +08:00
|
|
|
#ifdef CONFIG_THERMAL
|
|
|
|
static int power_supply_read_temp(struct thermal_zone_device *tzd,
|
2015-07-24 14:12:54 +08:00
|
|
|
int *temp)
|
2012-05-09 23:06:47 +08:00
|
|
|
{
|
|
|
|
struct power_supply *psy;
|
|
|
|
union power_supply_propval val;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
WARN_ON(tzd == NULL);
|
|
|
|
psy = tzd->devdata;
|
2016-06-10 05:28:39 +08:00
|
|
|
ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &val);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2012-05-09 23:06:47 +08:00
|
|
|
|
|
|
|
/* Convert tenths of degree Celsius to milli degree Celsius. */
|
2016-06-10 05:28:39 +08:00
|
|
|
*temp = val.intval * 100;
|
2012-05-09 23:06:47 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct thermal_zone_device_ops psy_tzd_ops = {
|
|
|
|
.get_temp = power_supply_read_temp,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int psy_register_thermal(struct power_supply *psy)
|
|
|
|
{
|
2020-06-29 20:29:22 +08:00
|
|
|
int i, ret;
|
2012-05-09 23:06:47 +08:00
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
if (psy->desc->no_thermal)
|
2014-10-07 23:47:36 +08:00
|
|
|
return 0;
|
|
|
|
|
2012-05-09 23:06:47 +08:00
|
|
|
/* Register battery zone device psy reports temperature */
|
2015-03-12 15:44:11 +08:00
|
|
|
for (i = 0; i < psy->desc->num_properties; i++) {
|
|
|
|
if (psy->desc->properties[i] == POWER_SUPPLY_PROP_TEMP) {
|
|
|
|
psy->tzd = thermal_zone_device_register(psy->desc->name,
|
|
|
|
0, 0, psy, &psy_tzd_ops, NULL, 0, 0);
|
2020-06-29 20:29:22 +08:00
|
|
|
if (IS_ERR(psy->tzd))
|
|
|
|
return PTR_ERR(psy->tzd);
|
|
|
|
ret = thermal_zone_device_enable(psy->tzd);
|
|
|
|
if (ret)
|
|
|
|
thermal_zone_device_unregister(psy->tzd);
|
|
|
|
return ret;
|
2012-05-09 23:06:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void psy_unregister_thermal(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
if (IS_ERR_OR_NULL(psy->tzd))
|
|
|
|
return;
|
|
|
|
thermal_zone_device_unregister(psy->tzd);
|
|
|
|
}
|
2012-10-10 00:55:59 +08:00
|
|
|
|
|
|
|
/* thermal cooling device callbacks */
|
|
|
|
static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd,
|
|
|
|
unsigned long *state)
|
|
|
|
{
|
|
|
|
struct power_supply *psy;
|
|
|
|
union power_supply_propval val;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
psy = tcd->devdata;
|
2016-06-10 05:28:39 +08:00
|
|
|
ret = power_supply_get_property(psy,
|
|
|
|
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*state = val.intval;
|
2012-10-10 00:55:59 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-04-15 22:29:18 +08:00
|
|
|
static int ps_get_cur_charge_cntl_limit(struct thermal_cooling_device *tcd,
|
2012-10-10 00:55:59 +08:00
|
|
|
unsigned long *state)
|
|
|
|
{
|
|
|
|
struct power_supply *psy;
|
|
|
|
union power_supply_propval val;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
psy = tcd->devdata;
|
2016-06-10 05:28:39 +08:00
|
|
|
ret = power_supply_get_property(psy,
|
|
|
|
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
*state = val.intval;
|
2012-10-10 00:55:59 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd,
|
|
|
|
unsigned long state)
|
|
|
|
{
|
|
|
|
struct power_supply *psy;
|
|
|
|
union power_supply_propval val;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
psy = tcd->devdata;
|
|
|
|
val.intval = state;
|
2015-03-12 15:44:11 +08:00
|
|
|
ret = psy->desc->set_property(psy,
|
2012-10-10 00:55:59 +08:00
|
|
|
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-13 13:09:43 +08:00
|
|
|
static const struct thermal_cooling_device_ops psy_tcd_ops = {
|
2012-10-10 00:55:59 +08:00
|
|
|
.get_max_state = ps_get_max_charge_cntl_limit,
|
2019-04-15 22:29:18 +08:00
|
|
|
.get_cur_state = ps_get_cur_charge_cntl_limit,
|
2012-10-10 00:55:59 +08:00
|
|
|
.set_cur_state = ps_set_cur_charge_cntl_limit,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int psy_register_cooler(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Register for cooling device if psy can control charging */
|
2015-03-12 15:44:11 +08:00
|
|
|
for (i = 0; i < psy->desc->num_properties; i++) {
|
|
|
|
if (psy->desc->properties[i] ==
|
2012-10-10 00:55:59 +08:00
|
|
|
POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT) {
|
|
|
|
psy->tcd = thermal_cooling_device_register(
|
2015-03-12 15:44:11 +08:00
|
|
|
(char *)psy->desc->name,
|
2012-10-10 00:55:59 +08:00
|
|
|
psy, &psy_tcd_ops);
|
2014-09-04 20:01:33 +08:00
|
|
|
return PTR_ERR_OR_ZERO(psy->tcd);
|
2012-10-10 00:55:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void psy_unregister_cooler(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
if (IS_ERR_OR_NULL(psy->tcd))
|
|
|
|
return;
|
|
|
|
thermal_cooling_device_unregister(psy->tcd);
|
|
|
|
}
|
2012-05-09 23:06:47 +08:00
|
|
|
#else
|
|
|
|
static int psy_register_thermal(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void psy_unregister_thermal(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
}
|
2012-10-10 00:55:59 +08:00
|
|
|
|
|
|
|
static int psy_register_cooler(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void psy_unregister_cooler(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
}
|
2012-05-09 23:06:47 +08:00
|
|
|
#endif
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
static struct power_supply *__must_check
|
|
|
|
__power_supply_register(struct device *parent,
|
|
|
|
const struct power_supply_desc *desc,
|
2015-03-12 15:44:02 +08:00
|
|
|
const struct power_supply_config *cfg,
|
|
|
|
bool ws)
|
2007-05-04 04:27:45 +08:00
|
|
|
{
|
2010-05-19 03:49:51 +08:00
|
|
|
struct device *dev;
|
2015-03-12 15:44:11 +08:00
|
|
|
struct power_supply *psy;
|
power: supply: Add 'usb_type' property and supporting code
This commit adds the 'usb_type' property to represent USB supplies
which can report a number of different types based on a connection
event.
Examples of this already exist in drivers whereby the existing 'type'
property is updated, based on an event, to represent what was
connected (e.g. USB, USB_DCP, USB_ACA, ...). Current implementations
however don't show all supported connectable types, so this knowledge
has to be exlicitly known for each driver that supports this.
The 'usb_type' property is intended to fill this void and show users
all possible USB types supported by a driver. The property, when read,
shows all available types for the driver, and the one currently chosen
is highlighted/bracketed. It is expected that the 'type' property
would then just show the top-level type 'USB', and this would be
static.
Currently the 'usb_type' enum contains all of the USB variant types
that exist for the 'type' enum at this time, and in addition has
SDP and PPS types. The mirroring is intentional so as to not impact
existing usage of the 'type' property.
Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-04-23 22:10:59 +08:00
|
|
|
int i, rc;
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2015-05-19 15:13:02 +08:00
|
|
|
if (!parent)
|
|
|
|
pr_warn("%s: Expected proper parent device for '%s'\n",
|
|
|
|
__func__, desc->name);
|
|
|
|
|
2018-04-23 22:10:58 +08:00
|
|
|
if (!desc || !desc->name || !desc->properties || !desc->num_properties)
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
|
power: supply: Add 'usb_type' property and supporting code
This commit adds the 'usb_type' property to represent USB supplies
which can report a number of different types based on a connection
event.
Examples of this already exist in drivers whereby the existing 'type'
property is updated, based on an event, to represent what was
connected (e.g. USB, USB_DCP, USB_ACA, ...). Current implementations
however don't show all supported connectable types, so this knowledge
has to be exlicitly known for each driver that supports this.
The 'usb_type' property is intended to fill this void and show users
all possible USB types supported by a driver. The property, when read,
shows all available types for the driver, and the one currently chosen
is highlighted/bracketed. It is expected that the 'type' property
would then just show the top-level type 'USB', and this would be
static.
Currently the 'usb_type' enum contains all of the USB variant types
that exist for the 'type' enum at this time, and in addition has
SDP and PPS types. The mirroring is intentional so as to not impact
existing usage of the 'type' property.
Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2018-04-23 22:10:59 +08:00
|
|
|
for (i = 0; i < desc->num_properties; ++i) {
|
|
|
|
if ((desc->properties[i] == POWER_SUPPLY_PROP_USB_TYPE) &&
|
|
|
|
(!desc->usb_types || !desc->num_usb_types))
|
|
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
}
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
psy = kzalloc(sizeof(*psy), GFP_KERNEL);
|
|
|
|
if (!psy)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
dev = &psy->dev;
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2010-05-19 03:49:51 +08:00
|
|
|
device_initialize(dev);
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2010-05-19 03:49:51 +08:00
|
|
|
dev->class = power_supply_class;
|
|
|
|
dev->type = &power_supply_dev_type;
|
|
|
|
dev->parent = parent;
|
|
|
|
dev->release = power_supply_dev_release;
|
|
|
|
dev_set_drvdata(dev, psy);
|
2015-03-12 15:44:11 +08:00
|
|
|
psy->desc = desc;
|
2015-03-12 15:44:02 +08:00
|
|
|
if (cfg) {
|
2018-09-27 21:46:03 +08:00
|
|
|
dev->groups = cfg->attr_grp;
|
2015-03-12 15:44:02 +08:00
|
|
|
psy->drv_data = cfg->drv_data;
|
2018-05-22 23:16:23 +08:00
|
|
|
psy->of_node =
|
|
|
|
cfg->fwnode ? to_of_node(cfg->fwnode) : cfg->of_node;
|
2015-03-12 15:44:02 +08:00
|
|
|
psy->supplied_to = cfg->supplied_to;
|
|
|
|
psy->num_supplicants = cfg->num_supplicants;
|
|
|
|
}
|
2010-05-19 03:49:51 +08:00
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
rc = dev_set_name(dev, "%s", desc->name);
|
2013-11-23 01:54:28 +08:00
|
|
|
if (rc)
|
|
|
|
goto dev_set_name_failed;
|
|
|
|
|
2011-02-21 22:34:19 +08:00
|
|
|
INIT_WORK(&psy->changed_work, power_supply_changed_work);
|
2015-05-19 15:13:02 +08:00
|
|
|
INIT_DELAYED_WORK(&psy->deferred_register_work,
|
|
|
|
power_supply_deferred_register_work);
|
2011-02-21 22:34:19 +08:00
|
|
|
|
2013-04-02 05:45:55 +08:00
|
|
|
rc = power_supply_check_supplies(psy);
|
|
|
|
if (rc) {
|
|
|
|
dev_info(dev, "Not all required supplies found, defer probe\n");
|
|
|
|
goto check_supplies_failed;
|
|
|
|
}
|
|
|
|
|
2013-08-03 04:38:02 +08:00
|
|
|
spin_lock_init(&psy->changed_lock);
|
2010-05-19 03:49:51 +08:00
|
|
|
rc = device_add(dev);
|
2007-05-04 04:27:45 +08:00
|
|
|
if (rc)
|
2010-05-19 03:49:51 +08:00
|
|
|
goto device_add_failed;
|
|
|
|
|
2019-08-02 05:33:30 +08:00
|
|
|
rc = device_init_wakeup(dev, ws);
|
|
|
|
if (rc)
|
|
|
|
goto wakeup_init_failed;
|
|
|
|
|
2012-05-09 23:06:47 +08:00
|
|
|
rc = psy_register_thermal(psy);
|
|
|
|
if (rc)
|
|
|
|
goto register_thermal_failed;
|
|
|
|
|
2012-10-10 00:55:59 +08:00
|
|
|
rc = psy_register_cooler(psy);
|
|
|
|
if (rc)
|
|
|
|
goto register_cooler_failed;
|
|
|
|
|
2007-05-04 04:27:45 +08:00
|
|
|
rc = power_supply_create_triggers(psy);
|
|
|
|
if (rc)
|
|
|
|
goto create_triggers_failed;
|
|
|
|
|
2019-06-12 16:44:04 +08:00
|
|
|
rc = power_supply_add_hwmon_sysfs(psy);
|
|
|
|
if (rc)
|
|
|
|
goto add_hwmon_sysfs_failed;
|
|
|
|
|
2015-05-19 15:13:01 +08:00
|
|
|
/*
|
|
|
|
* Update use_cnt after any uevents (most notably from device_add()).
|
|
|
|
* We are here still during driver's probe but
|
|
|
|
* the power_supply_uevent() calls back driver's get_property
|
|
|
|
* method so:
|
|
|
|
* 1. Driver did not assigned the returned struct power_supply,
|
|
|
|
* 2. Driver could not finish initialization (anything in its probe
|
|
|
|
* after calling power_supply_register()).
|
|
|
|
*/
|
|
|
|
atomic_inc(&psy->use_cnt);
|
2016-06-22 23:45:52 +08:00
|
|
|
psy->initialized = true;
|
2015-05-19 15:13:02 +08:00
|
|
|
|
|
|
|
queue_delayed_work(system_power_efficient_wq,
|
|
|
|
&psy->deferred_register_work,
|
|
|
|
POWER_SUPPLY_DEFERRED_REGISTER_TIME);
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
return psy;
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2019-06-12 16:44:04 +08:00
|
|
|
add_hwmon_sysfs_failed:
|
|
|
|
power_supply_remove_triggers(psy);
|
2007-05-04 04:27:45 +08:00
|
|
|
create_triggers_failed:
|
2012-10-10 00:55:59 +08:00
|
|
|
psy_unregister_cooler(psy);
|
|
|
|
register_cooler_failed:
|
2012-05-09 23:06:47 +08:00
|
|
|
psy_unregister_thermal(psy);
|
|
|
|
register_thermal_failed:
|
2010-11-20 02:41:58 +08:00
|
|
|
device_del(dev);
|
2013-11-23 01:54:28 +08:00
|
|
|
wakeup_init_failed:
|
2019-08-02 05:33:30 +08:00
|
|
|
device_add_failed:
|
2013-04-02 05:45:55 +08:00
|
|
|
check_supplies_failed:
|
2013-11-23 01:54:28 +08:00
|
|
|
dev_set_name_failed:
|
2010-11-20 02:41:58 +08:00
|
|
|
put_device(dev);
|
2015-03-12 15:44:11 +08:00
|
|
|
return ERR_PTR(rc);
|
2007-05-04 04:27:45 +08:00
|
|
|
}
|
2014-05-28 15:23:37 +08:00
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
/**
|
|
|
|
* power_supply_register() - Register new power supply
|
2015-05-19 15:13:02 +08:00
|
|
|
* @parent: Device to be a parent of power supply's device, usually
|
|
|
|
* the device which probe function calls this
|
2015-03-12 15:44:11 +08:00
|
|
|
* @desc: Description of power supply, must be valid through whole
|
|
|
|
* lifetime of this power supply
|
|
|
|
* @cfg: Run-time specific configuration accessed during registering,
|
|
|
|
* may be NULL
|
|
|
|
*
|
|
|
|
* Return: A pointer to newly allocated power_supply on success
|
|
|
|
* or ERR_PTR otherwise.
|
|
|
|
* Use power_supply_unregister() on returned power_supply pointer to release
|
|
|
|
* resources.
|
|
|
|
*/
|
|
|
|
struct power_supply *__must_check power_supply_register(struct device *parent,
|
|
|
|
const struct power_supply_desc *desc,
|
2015-03-12 15:44:02 +08:00
|
|
|
const struct power_supply_config *cfg)
|
2014-05-28 15:23:37 +08:00
|
|
|
{
|
2015-03-12 15:44:11 +08:00
|
|
|
return __power_supply_register(parent, desc, cfg, true);
|
2014-05-28 15:23:37 +08:00
|
|
|
}
|
2009-07-30 21:42:31 +08:00
|
|
|
EXPORT_SYMBOL_GPL(power_supply_register);
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
/**
|
2015-06-19 03:35:26 +08:00
|
|
|
* power_supply_register_no_ws() - Register new non-waking-source power supply
|
2015-05-19 15:13:02 +08:00
|
|
|
* @parent: Device to be a parent of power supply's device, usually
|
|
|
|
* the device which probe function calls this
|
2015-03-12 15:44:11 +08:00
|
|
|
* @desc: Description of power supply, must be valid through whole
|
|
|
|
* lifetime of this power supply
|
|
|
|
* @cfg: Run-time specific configuration accessed during registering,
|
|
|
|
* may be NULL
|
|
|
|
*
|
|
|
|
* Return: A pointer to newly allocated power_supply on success
|
|
|
|
* or ERR_PTR otherwise.
|
|
|
|
* Use power_supply_unregister() on returned power_supply pointer to release
|
|
|
|
* resources.
|
|
|
|
*/
|
|
|
|
struct power_supply *__must_check
|
|
|
|
power_supply_register_no_ws(struct device *parent,
|
|
|
|
const struct power_supply_desc *desc,
|
2015-03-12 15:44:02 +08:00
|
|
|
const struct power_supply_config *cfg)
|
2014-05-28 15:23:37 +08:00
|
|
|
{
|
2015-03-12 15:44:11 +08:00
|
|
|
return __power_supply_register(parent, desc, cfg, false);
|
2014-05-28 15:23:37 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_register_no_ws);
|
|
|
|
|
2015-02-24 12:33:50 +08:00
|
|
|
static void devm_power_supply_release(struct device *dev, void *res)
|
|
|
|
{
|
|
|
|
struct power_supply **psy = res;
|
|
|
|
|
|
|
|
power_supply_unregister(*psy);
|
|
|
|
}
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
/**
|
2015-06-19 03:35:26 +08:00
|
|
|
* devm_power_supply_register() - Register managed power supply
|
2015-05-19 15:13:02 +08:00
|
|
|
* @parent: Device to be a parent of power supply's device, usually
|
|
|
|
* the device which probe function calls this
|
2015-03-12 15:44:11 +08:00
|
|
|
* @desc: Description of power supply, must be valid through whole
|
|
|
|
* lifetime of this power supply
|
|
|
|
* @cfg: Run-time specific configuration accessed during registering,
|
|
|
|
* may be NULL
|
|
|
|
*
|
|
|
|
* Return: A pointer to newly allocated power_supply on success
|
|
|
|
* or ERR_PTR otherwise.
|
|
|
|
* The returned power_supply pointer will be automatically unregistered
|
|
|
|
* on driver detach.
|
|
|
|
*/
|
|
|
|
struct power_supply *__must_check
|
|
|
|
devm_power_supply_register(struct device *parent,
|
|
|
|
const struct power_supply_desc *desc,
|
2015-03-12 15:44:02 +08:00
|
|
|
const struct power_supply_config *cfg)
|
2015-02-24 12:33:50 +08:00
|
|
|
{
|
2015-03-12 15:44:11 +08:00
|
|
|
struct power_supply **ptr, *psy;
|
|
|
|
|
|
|
|
ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL);
|
2015-02-24 12:33:50 +08:00
|
|
|
|
|
|
|
if (!ptr)
|
2015-03-12 15:44:11 +08:00
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
psy = __power_supply_register(parent, desc, cfg, true);
|
|
|
|
if (IS_ERR(psy)) {
|
2015-02-24 12:33:50 +08:00
|
|
|
devres_free(ptr);
|
2015-03-12 15:44:11 +08:00
|
|
|
} else {
|
2015-02-24 12:33:50 +08:00
|
|
|
*ptr = psy;
|
|
|
|
devres_add(parent, ptr);
|
|
|
|
}
|
2015-03-12 15:44:11 +08:00
|
|
|
return psy;
|
2015-02-24 12:33:50 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(devm_power_supply_register);
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
/**
|
2015-06-19 03:35:26 +08:00
|
|
|
* devm_power_supply_register_no_ws() - Register managed non-waking-source power supply
|
2015-05-19 15:13:02 +08:00
|
|
|
* @parent: Device to be a parent of power supply's device, usually
|
|
|
|
* the device which probe function calls this
|
2015-03-12 15:44:11 +08:00
|
|
|
* @desc: Description of power supply, must be valid through whole
|
|
|
|
* lifetime of this power supply
|
|
|
|
* @cfg: Run-time specific configuration accessed during registering,
|
|
|
|
* may be NULL
|
|
|
|
*
|
|
|
|
* Return: A pointer to newly allocated power_supply on success
|
|
|
|
* or ERR_PTR otherwise.
|
|
|
|
* The returned power_supply pointer will be automatically unregistered
|
|
|
|
* on driver detach.
|
|
|
|
*/
|
|
|
|
struct power_supply *__must_check
|
|
|
|
devm_power_supply_register_no_ws(struct device *parent,
|
|
|
|
const struct power_supply_desc *desc,
|
2015-03-12 15:44:02 +08:00
|
|
|
const struct power_supply_config *cfg)
|
2015-02-24 12:33:50 +08:00
|
|
|
{
|
2015-03-12 15:44:11 +08:00
|
|
|
struct power_supply **ptr, *psy;
|
|
|
|
|
|
|
|
ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL);
|
2015-02-24 12:33:50 +08:00
|
|
|
|
|
|
|
if (!ptr)
|
2015-03-12 15:44:11 +08:00
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
psy = __power_supply_register(parent, desc, cfg, false);
|
|
|
|
if (IS_ERR(psy)) {
|
2015-02-24 12:33:50 +08:00
|
|
|
devres_free(ptr);
|
2015-03-12 15:44:11 +08:00
|
|
|
} else {
|
2015-02-24 12:33:50 +08:00
|
|
|
*ptr = psy;
|
|
|
|
devres_add(parent, ptr);
|
|
|
|
}
|
2015-03-12 15:44:11 +08:00
|
|
|
return psy;
|
2015-02-24 12:33:50 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(devm_power_supply_register_no_ws);
|
|
|
|
|
2015-03-12 15:44:11 +08:00
|
|
|
/**
|
|
|
|
* power_supply_unregister() - Remove this power supply from system
|
|
|
|
* @psy: Pointer to power supply to unregister
|
|
|
|
*
|
|
|
|
* Remove this power supply from the system. The resources of power supply
|
|
|
|
* will be freed here or on last power_supply_put() call.
|
|
|
|
*/
|
2007-05-04 04:27:45 +08:00
|
|
|
void power_supply_unregister(struct power_supply *psy)
|
|
|
|
{
|
2015-03-12 15:44:03 +08:00
|
|
|
WARN_ON(atomic_dec_return(&psy->use_cnt));
|
2018-06-25 15:51:48 +08:00
|
|
|
psy->removing = true;
|
2010-12-12 00:51:45 +08:00
|
|
|
cancel_work_sync(&psy->changed_work);
|
2015-05-19 15:13:02 +08:00
|
|
|
cancel_delayed_work_sync(&psy->deferred_register_work);
|
2015-03-12 15:44:11 +08:00
|
|
|
sysfs_remove_link(&psy->dev.kobj, "powers");
|
2019-06-12 16:44:04 +08:00
|
|
|
power_supply_remove_hwmon_sysfs(psy);
|
2007-05-04 04:27:45 +08:00
|
|
|
power_supply_remove_triggers(psy);
|
2012-10-10 00:55:59 +08:00
|
|
|
psy_unregister_cooler(psy);
|
2012-05-09 23:06:47 +08:00
|
|
|
psy_unregister_thermal(psy);
|
2015-03-12 15:44:11 +08:00
|
|
|
device_init_wakeup(&psy->dev, false);
|
|
|
|
device_unregister(&psy->dev);
|
2007-05-04 04:27:45 +08:00
|
|
|
}
|
2009-07-30 21:42:31 +08:00
|
|
|
EXPORT_SYMBOL_GPL(power_supply_unregister);
|
2007-05-04 04:27:45 +08:00
|
|
|
|
2015-03-12 15:44:01 +08:00
|
|
|
void *power_supply_get_drvdata(struct power_supply *psy)
|
|
|
|
{
|
|
|
|
return psy->drv_data;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(power_supply_get_drvdata);
|
|
|
|
|
2007-05-04 04:27:45 +08:00
|
|
|
static int __init power_supply_class_init(void)
|
|
|
|
{
|
|
|
|
power_supply_class = class_create(THIS_MODULE, "power_supply");
|
|
|
|
|
|
|
|
if (IS_ERR(power_supply_class))
|
|
|
|
return PTR_ERR(power_supply_class);
|
|
|
|
|
|
|
|
power_supply_class->dev_uevent = power_supply_uevent;
|
2010-05-19 03:49:51 +08:00
|
|
|
power_supply_init_attrs(&power_supply_dev_type);
|
2007-05-04 04:27:45 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit power_supply_class_exit(void)
|
|
|
|
{
|
|
|
|
class_destroy(power_supply_class);
|
|
|
|
}
|
|
|
|
|
|
|
|
subsys_initcall(power_supply_class_init);
|
|
|
|
module_exit(power_supply_class_exit);
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("Universal power supply monitor class");
|
|
|
|
MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "
|
|
|
|
"Szabolcs Gyurko, "
|
|
|
|
"Anton Vorontsov <cbou@mail.ru>");
|
|
|
|
MODULE_LICENSE("GPL");
|