blob: 15f66d037861ac2789ca904391a6c7987725a774 [file] [log] [blame]
/*
* battery_notifier.c
* Samsung Mobile Battery Notifier Driver
*
* Copyright (C) 2017 Samsung Electronics, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <linux/device.h>
#include <linux/notifier.h>
#include <linux/battery/battery_notifier.h>
#include <linux/sec_sysfs.h>
#define DEBUG
#define SET_BATTERY_NOTIFIER_BLOCK(nb, fn, dev) do { \
(nb)->notifier_call = (fn); \
(nb)->priority = (dev); \
} while (0)
#define DESTROY_BATTERY_NOTIFIER_BLOCK(nb) \
SET_BATTERY_NOTIFIER_BLOCK(nb, NULL, -1)
static struct charger_notifier_struct charger_notifier;
static struct pdic_notifier_struct pdic_notifier;
struct device *charger_device;
struct device *pdic_device;
int charger_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
charger_notifier_device_t listener)
{
int ret = 0;
pr_info("%s: listener=%d register\n", __func__, listener);
/* Check if CHARGER Notifier is ready. */
if (!charger_device) {
pr_err("%s: Not Initialized...\n", __func__);
return -1;
}
SET_BATTERY_NOTIFIER_BLOCK(nb, notifier, listener);
ret = blocking_notifier_chain_register(&(charger_notifier.notifier_call_chain), nb);
if (ret < 0)
pr_err("%s: blocking_notifier_chain_register error(%d)\n",
__func__, ret);
/* current charger's attached_device status notify */
nb->notifier_call(nb, charger_notifier.event,
&(charger_notifier));
return ret;
}
int charger_notifier_unregister(struct notifier_block *nb)
{
int ret = 0;
pr_info("%s: listener=%d unregister\n", __func__, nb->priority);
ret = blocking_notifier_chain_unregister(&(charger_notifier.notifier_call_chain), nb);
if (ret < 0)
pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
__func__, ret);
DESTROY_BATTERY_NOTIFIER_BLOCK(nb);
return ret;
}
int pdic_notifier_register(struct notifier_block *nb, notifier_fn_t notifier,
pdic_notifier_device_t listener)
{
int ret = 0;
pr_info("%s: listener=%d register\n", __func__, listener);
/* Check if CHARGER Notifier is ready. */
if (!pdic_device) {
pr_err("%s: Not Initialized...\n", __func__);
return -1;
}
SET_BATTERY_NOTIFIER_BLOCK(nb, notifier, listener);
ret = blocking_notifier_chain_register(&(pdic_notifier.notifier_call_chain), nb);
if (ret < 0)
pr_err("%s: blocking_notifier_chain_register error(%d)\n",
__func__, ret);
/* current pdic's attached_device status notify */
nb->notifier_call(nb, pdic_notifier.event,
&(pdic_notifier));
return ret;
}
int pdic_notifier_unregister(struct notifier_block *nb)
{
int ret = 0;
pr_info("%s: listener=%d unregister\n", __func__, nb->priority);
ret = blocking_notifier_chain_unregister(&(pdic_notifier.notifier_call_chain), nb);
if (ret < 0)
pr_err("%s: blocking_notifier_chain_unregister error(%d)\n",
__func__, ret);
DESTROY_BATTERY_NOTIFIER_BLOCK(nb);
return ret;
}
static int battery_notifier_notify(int type)
{
int ret = 0;
switch (type) {
case CHARGER_NOTIFY:
ret = blocking_notifier_call_chain(&(charger_notifier.notifier_call_chain),
charger_notifier.event, &(charger_notifier));
break;
case PDIC_NOTIFY:
ret = blocking_notifier_call_chain(&(pdic_notifier.notifier_call_chain),
pdic_notifier.event, &(pdic_notifier));
break;
default:
pr_info("%s: notify status unknown(0x%x)\n", __func__, ret);
break;
}
switch (ret) {
case NOTIFY_STOP_MASK:
case NOTIFY_BAD:
pr_err("%s: notify error occur(0x%x)\n", __func__, ret);
break;
case NOTIFY_DONE:
case NOTIFY_OK:
pr_info("%s: notify done(0x%x)\n", __func__, ret);
break;
default:
pr_info("%s: notify status unknown(0x%x)\n", __func__, ret);
break;
}
return ret;
}
static void charger_notifier_set_property(struct charger_notifier_struct * value)
{
charger_notifier.event = value->event;
switch(value->event) {
case CHARGER_NOTIFY_EVENT_AICL:
charger_notifier.aicl_status.input_current = value->aicl_status.input_current;
break;
default:
break;
}
}
void charger_notifier_call(struct charger_notifier_struct *value)
{
/* charger's event broadcast */
pr_info("%s: CHARGER_NOTIFY_EVENT :%d\n", __func__, value->event);
charger_notifier_set_property(value);
battery_notifier_notify(CHARGER_NOTIFY);
}
static void pdic_notifier_set_property(struct pdic_notifier_struct *value)
{
pdic_notifier.event = value->event;
switch(value->event) {
case PDIC_NOTIFY_EVENT_PD_SINK:
pdic_notifier.sink_status = value->sink_status;
break;
default:
break;
}
}
void pdic_notifier_call(struct pdic_notifier_struct *value)
{
/* pdic's event broadcast */
pdic_notifier_set_property(value);
battery_notifier_notify(PDIC_NOTIFY);
}
int battery_notifier_init(void)
{
int ret = 0;
pr_info("%s\n", __func__);
charger_device = sec_device_create(NULL, "charger_notifier");
pdic_device = sec_device_create(NULL, "pdic_notifier");
if (IS_ERR(charger_device)) {
pr_err("%s Failed to create device(charger_notifier)!\n", __func__);
ret = -ENODEV;
goto out;
}
if (IS_ERR(pdic_device)) {
pr_err("%s Failed to create device(pdic_notifier)!\n", __func__);
ret = -ENODEV;
goto out;
}
BLOCKING_INIT_NOTIFIER_HEAD(&(charger_notifier.notifier_call_chain));
BLOCKING_INIT_NOTIFIER_HEAD(&(pdic_notifier.notifier_call_chain));
out:
return ret;
}
device_initcall(battery_notifier_init);