bq2415x_charger: Use power_supply notifier for automode
authorPali Rohár <pali.rohar@gmail.com>
Tue, 19 Nov 2013 10:18:04 +0000 (11:18 +0100)
committerAnton Vorontsov <anton@enomsg.org>
Tue, 24 Dec 2013 01:58:03 +0000 (17:58 -0800)
This patch removing set_mode_hook function from board data and replacing
it with new string variable of notifier power supply device. After this
change it is possible to add DT support because driver does not need
specific board function anymore. Only static data and name of power supply
device is required.

Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
Reviewed-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Anton Vorontsov <anton@enomsg.org>
drivers/power/bq2415x_charger.c
include/linux/power/bq2415x_charger.h

index df893dd1447d1534695ace9405e4783ec49fd79d..0bece7026056fb8bfc636f0b9565560d6c228f1d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * bq2415x charger driver
  *
- * Copyright (C) 2011-2012  Pali Rohár <pali.rohar@gmail.com>
+ * Copyright (C) 2011-2013  Pali Rohár <pali.rohar@gmail.com>
  *
  * 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
@@ -170,6 +170,7 @@ struct bq2415x_device {
        struct bq2415x_platform_data init_data;
        struct power_supply charger;
        struct delayed_work work;
+       struct notifier_block nb;
        enum bq2415x_mode reported_mode;/* mode reported by hook function */
        enum bq2415x_mode mode;         /* current configured mode */
        enum bq2415x_chip chip;
@@ -795,24 +796,53 @@ static int bq2415x_set_mode(struct bq2415x_device *bq, enum bq2415x_mode mode)
 
 }
 
-/* hook function called by other driver which set reported mode */
-static void bq2415x_hook_function(enum bq2415x_mode mode, void *data)
+static int bq2415x_notifier_call(struct notifier_block *nb,
+               unsigned long val, void *v)
 {
-       struct bq2415x_device *bq = data;
+       struct bq2415x_device *bq =
+               container_of(nb, struct bq2415x_device, nb);
+       struct power_supply *psy = v;
+       enum bq2415x_mode mode;
+       union power_supply_propval prop;
+       int ret;
+       int mA;
 
-       if (!bq)
-               return;
+       if (val != PSY_EVENT_PROP_CHANGED)
+               return NOTIFY_OK;
+
+       if (strcmp(psy->name, bq->init_data.notify_device) != 0)
+               return NOTIFY_OK;
+
+       dev_dbg(bq->dev, "notifier call was called\n");
+
+       ret = psy->get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
+       if (ret != 0)
+               return NOTIFY_OK;
+
+       mA = prop.intval;
+
+       if (mA == 0)
+               mode = BQ2415X_MODE_OFF;
+       else if (mA < 500)
+               mode = BQ2415X_MODE_NONE;
+       else if (mA < 1800)
+               mode = BQ2415X_MODE_HOST_CHARGER;
+       else
+               mode = BQ2415X_MODE_DEDICATED_CHARGER;
+
+       if (bq->reported_mode == mode)
+               return NOTIFY_OK;
 
-       dev_dbg(bq->dev, "hook function was called\n");
        bq->reported_mode = mode;
 
        /* if automode is not enabled do not tell about reported_mode */
        if (bq->automode < 1)
-               return;
+               return NOTIFY_OK;
 
        sysfs_notify(&bq->charger.dev->kobj, NULL, "reported_mode");
        bq2415x_set_mode(bq, bq->reported_mode);
 
+       return NOTIFY_OK;
 }
 
 /**** timer functions ****/
@@ -1512,6 +1542,7 @@ static int bq2415x_probe(struct i2c_client *client,
        int num;
        char *name;
        struct bq2415x_device *bq;
+       struct power_supply *psy;
 
        if (!client->dev.platform_data) {
                dev_err(&client->dev, "platform data not set\n");
@@ -1573,16 +1604,27 @@ static int bq2415x_probe(struct i2c_client *client,
                goto error_4;
        }
 
-       if (bq->init_data.set_mode_hook) {
-               if (bq->init_data.set_mode_hook(
-                               bq2415x_hook_function, bq)) {
-                       bq->automode = 1;
+       if (bq->init_data.notify_device) {
+               bq->nb.notifier_call = bq2415x_notifier_call;
+               ret = power_supply_reg_notifier(&bq->nb);
+               if (ret) {
+                       dev_err(bq->dev, "failed to reg notifier: %d\n", ret);
+                       goto error_5;
+               }
+               psy = power_supply_get_by_name(bq->init_data.notify_device);
+               if (psy) {
+                       /* Query for initial reported_mode and set it */
+                       bq2415x_notifier_call(&bq->nb,
+                                       PSY_EVENT_PROP_CHANGED, psy);
                        bq2415x_set_mode(bq, bq->reported_mode);
-                       dev_info(bq->dev, "automode enabled\n");
                } else {
-                       bq->automode = -1;
-                       dev_info(bq->dev, "automode failed\n");
+                       dev_info(bq->dev, "notifier power supply device (%s) "
+                               "for automode is not registred yet... "
+                               "automode will not work without that device\n",
+                               bq->init_data.notify_device);
                }
+               bq->automode = 1;
+               dev_info(bq->dev, "automode enabled\n");
        } else {
                bq->automode = -1;
                dev_info(bq->dev, "automode not supported\n");
@@ -1594,6 +1636,7 @@ static int bq2415x_probe(struct i2c_client *client,
        dev_info(bq->dev, "driver registered\n");
        return 0;
 
+error_5:
 error_4:
        bq2415x_sysfs_exit(bq);
 error_3:
@@ -1614,8 +1657,8 @@ static int bq2415x_remove(struct i2c_client *client)
 {
        struct bq2415x_device *bq = i2c_get_clientdata(client);
 
-       if (bq->init_data.set_mode_hook)
-               bq->init_data.set_mode_hook(NULL, NULL);
+       if (bq->init_data.notify_device)
+               power_supply_unreg_notifier(&bq->nb);
 
        bq2415x_sysfs_exit(bq);
        bq2415x_power_supply_exit(bq);
index 8dcc0f46fc0ab44d5d4d34731745a73f42b5eceb..50762af8b834716812d4ef1748c5b20ad05d4a63 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * bq2415x charger driver
  *
- * Copyright (C) 2011-2012  Pali Rohár <pali.rohar@gmail.com>
+ * Copyright (C) 2011-2013  Pali Rohár <pali.rohar@gmail.com>
  *
  * 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
  * termination current. It it is less or equal to zero, configuring charge
  * and termination current will not be possible.
  *
- * Function set_mode_hook is needed for automode (setting correct current
- * limit when charger is connected/disconnected or setting boost mode).
- * When is NULL, automode function is disabled. When is not NULL, it must
- * have this prototype:
- *
- *    int (*set_mode_hook)(
- *      void (*hook)(enum bq2415x_mode mode, void *data),
- *      void *data)
- *
- * hook is hook function (see below) and data is pointer to driver private
- * data
- *
- * bq2415x driver will call it as:
- *
- *    platform_data->set_mode_hook(bq2415x_hook_function, bq2415x_device);
- *
- * Board/platform function set_mode_hook return non zero value when hook
- * function was successful registered. Platform code should call that hook
- * function (which get from pointer, with data) every time when charger
- * was connected/disconnected or require to enable boost mode. bq2415x
- * driver then will set correct current limit, enable/disable charger or
- * boost mode.
- *
- * Hook function has this prototype:
- *
- *    void hook(enum bq2415x_mode mode, void *data);
- *
- * mode is bq2415x mode (charger or boost)
- * data is pointer to driver private data (which get from
- * set_charger_type_hook)
- *
- * When bq driver is being unloaded, it call function:
- *
- *    platform_data->set_mode_hook(NULL, NULL);
- *
- * (hook function and driver private data are NULL)
- *
- * After that board/platform code must not call driver hook function! It
- * is possible that pointer to hook function will not be valid and calling
- * will cause undefined result.
+ * For automode support is needed to provide name of power supply device
+ * in value notify_device. Device driver must immediately report property
+ * POWER_SUPPLY_PROP_CURRENT_MAX when current changed.
  */
 
 /* Supported modes with maximal current limit */
@@ -89,8 +52,7 @@ struct bq2415x_platform_data {
        int charge_current;             /* mA */
        int termination_current;        /* mA */
        int resistor_sense;             /* m ohm */
-       int (*set_mode_hook)(void (*hook)(enum bq2415x_mode mode, void *data),
-                            void *data);
+       const char *notify_device;      /* name */
 };
 
 #endif