root/drivers/mfd/adp5520.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. __adp5520_read
  2. __adp5520_write
  3. __adp5520_ack_bits
  4. adp5520_write
  5. adp5520_read
  6. adp5520_set_bits
  7. adp5520_clr_bits
  8. adp5520_register_notifier
  9. adp5520_unregister_notifier
  10. adp5520_irq_thread
  11. __remove_subdev
  12. adp5520_remove_subdevs
  13. adp5520_probe
  14. adp5520_suspend
  15. adp5520_resume

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Base driver for Analog Devices ADP5520/ADP5501 MFD PMICs
   4  * LCD Backlight: drivers/video/backlight/adp5520_bl
   5  * LEDs         : drivers/led/leds-adp5520
   6  * GPIO         : drivers/gpio/adp5520-gpio (ADP5520 only)
   7  * Keys         : drivers/input/keyboard/adp5520-keys (ADP5520 only)
   8  *
   9  * Copyright 2009 Analog Devices Inc.
  10  *
  11  * Author: Michael Hennerich <michael.hennerich@analog.com>
  12  *
  13  * Derived from da903x:
  14  * Copyright (C) 2008 Compulab, Ltd.
  15  *      Mike Rapoport <mike@compulab.co.il>
  16  *
  17  * Copyright (C) 2006-2008 Marvell International Ltd.
  18  *      Eric Miao <eric.miao@marvell.com>
  19  */
  20 
  21 #include <linux/kernel.h>
  22 #include <linux/init.h>
  23 #include <linux/platform_device.h>
  24 #include <linux/slab.h>
  25 #include <linux/interrupt.h>
  26 #include <linux/irq.h>
  27 #include <linux/err.h>
  28 #include <linux/i2c.h>
  29 
  30 #include <linux/mfd/adp5520.h>
  31 
  32 struct adp5520_chip {
  33         struct i2c_client *client;
  34         struct device *dev;
  35         struct mutex lock;
  36         struct blocking_notifier_head notifier_list;
  37         int irq;
  38         unsigned long id;
  39         uint8_t mode;
  40 };
  41 
  42 static int __adp5520_read(struct i2c_client *client,
  43                                 int reg, uint8_t *val)
  44 {
  45         int ret;
  46 
  47         ret = i2c_smbus_read_byte_data(client, reg);
  48         if (ret < 0) {
  49                 dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
  50                 return ret;
  51         }
  52 
  53         *val = (uint8_t)ret;
  54         return 0;
  55 }
  56 
  57 static int __adp5520_write(struct i2c_client *client,
  58                                  int reg, uint8_t val)
  59 {
  60         int ret;
  61 
  62         ret = i2c_smbus_write_byte_data(client, reg, val);
  63         if (ret < 0) {
  64                 dev_err(&client->dev, "failed writing 0x%02x to 0x%02x\n",
  65                                 val, reg);
  66                 return ret;
  67         }
  68         return 0;
  69 }
  70 
  71 static int __adp5520_ack_bits(struct i2c_client *client, int reg,
  72                               uint8_t bit_mask)
  73 {
  74         struct adp5520_chip *chip = i2c_get_clientdata(client);
  75         uint8_t reg_val;
  76         int ret;
  77 
  78         mutex_lock(&chip->lock);
  79 
  80         ret = __adp5520_read(client, reg, &reg_val);
  81 
  82         if (!ret) {
  83                 reg_val |= bit_mask;
  84                 ret = __adp5520_write(client, reg, reg_val);
  85         }
  86 
  87         mutex_unlock(&chip->lock);
  88         return ret;
  89 }
  90 
  91 int adp5520_write(struct device *dev, int reg, uint8_t val)
  92 {
  93         return __adp5520_write(to_i2c_client(dev), reg, val);
  94 }
  95 EXPORT_SYMBOL_GPL(adp5520_write);
  96 
  97 int adp5520_read(struct device *dev, int reg, uint8_t *val)
  98 {
  99         return __adp5520_read(to_i2c_client(dev), reg, val);
 100 }
 101 EXPORT_SYMBOL_GPL(adp5520_read);
 102 
 103 int adp5520_set_bits(struct device *dev, int reg, uint8_t bit_mask)
 104 {
 105         struct adp5520_chip *chip = dev_get_drvdata(dev);
 106         uint8_t reg_val;
 107         int ret;
 108 
 109         mutex_lock(&chip->lock);
 110 
 111         ret = __adp5520_read(chip->client, reg, &reg_val);
 112 
 113         if (!ret && ((reg_val & bit_mask) != bit_mask)) {
 114                 reg_val |= bit_mask;
 115                 ret = __adp5520_write(chip->client, reg, reg_val);
 116         }
 117 
 118         mutex_unlock(&chip->lock);
 119         return ret;
 120 }
 121 EXPORT_SYMBOL_GPL(adp5520_set_bits);
 122 
 123 int adp5520_clr_bits(struct device *dev, int reg, uint8_t bit_mask)
 124 {
 125         struct adp5520_chip *chip = dev_get_drvdata(dev);
 126         uint8_t reg_val;
 127         int ret;
 128 
 129         mutex_lock(&chip->lock);
 130 
 131         ret = __adp5520_read(chip->client, reg, &reg_val);
 132 
 133         if (!ret && (reg_val & bit_mask)) {
 134                 reg_val &= ~bit_mask;
 135                 ret = __adp5520_write(chip->client, reg, reg_val);
 136         }
 137 
 138         mutex_unlock(&chip->lock);
 139         return ret;
 140 }
 141 EXPORT_SYMBOL_GPL(adp5520_clr_bits);
 142 
 143 int adp5520_register_notifier(struct device *dev, struct notifier_block *nb,
 144                                 unsigned int events)
 145 {
 146         struct adp5520_chip *chip = dev_get_drvdata(dev);
 147 
 148         if (chip->irq) {
 149                 adp5520_set_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
 150                         events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
 151                         ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
 152 
 153                 return blocking_notifier_chain_register(&chip->notifier_list,
 154                          nb);
 155         }
 156 
 157         return -ENODEV;
 158 }
 159 EXPORT_SYMBOL_GPL(adp5520_register_notifier);
 160 
 161 int adp5520_unregister_notifier(struct device *dev, struct notifier_block *nb,
 162                                 unsigned int events)
 163 {
 164         struct adp5520_chip *chip = dev_get_drvdata(dev);
 165 
 166         adp5520_clr_bits(chip->dev, ADP5520_INTERRUPT_ENABLE,
 167                 events & (ADP5520_KP_IEN | ADP5520_KR_IEN |
 168                 ADP5520_OVP_IEN | ADP5520_CMPR_IEN));
 169 
 170         return blocking_notifier_chain_unregister(&chip->notifier_list, nb);
 171 }
 172 EXPORT_SYMBOL_GPL(adp5520_unregister_notifier);
 173 
 174 static irqreturn_t adp5520_irq_thread(int irq, void *data)
 175 {
 176         struct adp5520_chip *chip = data;
 177         unsigned int events;
 178         uint8_t reg_val;
 179         int ret;
 180 
 181         ret = __adp5520_read(chip->client, ADP5520_MODE_STATUS, &reg_val);
 182         if (ret)
 183                 goto out;
 184 
 185         events =  reg_val & (ADP5520_OVP_INT | ADP5520_CMPR_INT |
 186                 ADP5520_GPI_INT | ADP5520_KR_INT | ADP5520_KP_INT);
 187 
 188         blocking_notifier_call_chain(&chip->notifier_list, events, NULL);
 189         /* ACK, Sticky bits are W1C */
 190         __adp5520_ack_bits(chip->client, ADP5520_MODE_STATUS, events);
 191 
 192 out:
 193         return IRQ_HANDLED;
 194 }
 195 
 196 static int __remove_subdev(struct device *dev, void *unused)
 197 {
 198         platform_device_unregister(to_platform_device(dev));
 199         return 0;
 200 }
 201 
 202 static int adp5520_remove_subdevs(struct adp5520_chip *chip)
 203 {
 204         return device_for_each_child(chip->dev, NULL, __remove_subdev);
 205 }
 206 
 207 static int adp5520_probe(struct i2c_client *client,
 208                                         const struct i2c_device_id *id)
 209 {
 210         struct adp5520_platform_data *pdata = dev_get_platdata(&client->dev);
 211         struct platform_device *pdev;
 212         struct adp5520_chip *chip;
 213         int ret;
 214 
 215         if (!i2c_check_functionality(client->adapter,
 216                                         I2C_FUNC_SMBUS_BYTE_DATA)) {
 217                 dev_err(&client->dev, "SMBUS Word Data not Supported\n");
 218                 return -EIO;
 219         }
 220 
 221         if (pdata == NULL) {
 222                 dev_err(&client->dev, "missing platform data\n");
 223                 return -ENODEV;
 224         }
 225 
 226         chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
 227         if (!chip)
 228                 return -ENOMEM;
 229 
 230         i2c_set_clientdata(client, chip);
 231         chip->client = client;
 232 
 233         chip->dev = &client->dev;
 234         chip->irq = client->irq;
 235         chip->id = id->driver_data;
 236         mutex_init(&chip->lock);
 237 
 238         if (chip->irq) {
 239                 BLOCKING_INIT_NOTIFIER_HEAD(&chip->notifier_list);
 240 
 241                 ret = request_threaded_irq(chip->irq, NULL, adp5520_irq_thread,
 242                                 IRQF_TRIGGER_LOW | IRQF_ONESHOT,
 243                                 "adp5520", chip);
 244                 if (ret) {
 245                         dev_err(&client->dev, "failed to request irq %d\n",
 246                                         chip->irq);
 247                         return ret;
 248                 }
 249         }
 250 
 251         ret = adp5520_write(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY);
 252         if (ret) {
 253                 dev_err(&client->dev, "failed to write\n");
 254                 goto out_free_irq;
 255         }
 256 
 257         if (pdata->keys) {
 258                 pdev = platform_device_register_data(chip->dev, "adp5520-keys",
 259                                 chip->id, pdata->keys, sizeof(*pdata->keys));
 260                 if (IS_ERR(pdev)) {
 261                         ret = PTR_ERR(pdev);
 262                         goto out_remove_subdevs;
 263                 }
 264         }
 265 
 266         if (pdata->gpio) {
 267                 pdev = platform_device_register_data(chip->dev, "adp5520-gpio",
 268                                 chip->id, pdata->gpio, sizeof(*pdata->gpio));
 269                 if (IS_ERR(pdev)) {
 270                         ret = PTR_ERR(pdev);
 271                         goto out_remove_subdevs;
 272                 }
 273         }
 274 
 275         if (pdata->leds) {
 276                 pdev = platform_device_register_data(chip->dev, "adp5520-led",
 277                                 chip->id, pdata->leds, sizeof(*pdata->leds));
 278                 if (IS_ERR(pdev)) {
 279                         ret = PTR_ERR(pdev);
 280                         goto out_remove_subdevs;
 281                 }
 282         }
 283 
 284         if (pdata->backlight) {
 285                 pdev = platform_device_register_data(chip->dev,
 286                                                 "adp5520-backlight",
 287                                                 chip->id,
 288                                                 pdata->backlight,
 289                                                 sizeof(*pdata->backlight));
 290                 if (IS_ERR(pdev)) {
 291                         ret = PTR_ERR(pdev);
 292                         goto out_remove_subdevs;
 293                 }
 294         }
 295 
 296         return 0;
 297 
 298 out_remove_subdevs:
 299         adp5520_remove_subdevs(chip);
 300 
 301 out_free_irq:
 302         if (chip->irq)
 303                 free_irq(chip->irq, chip);
 304 
 305         return ret;
 306 }
 307 
 308 #ifdef CONFIG_PM_SLEEP
 309 static int adp5520_suspend(struct device *dev)
 310 {
 311         struct i2c_client *client = to_i2c_client(dev);
 312         struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
 313 
 314         adp5520_read(chip->dev, ADP5520_MODE_STATUS, &chip->mode);
 315         /* All other bits are W1C */
 316         chip->mode &= ADP5520_BL_EN | ADP5520_DIM_EN | ADP5520_nSTNBY;
 317         adp5520_write(chip->dev, ADP5520_MODE_STATUS, 0);
 318         return 0;
 319 }
 320 
 321 static int adp5520_resume(struct device *dev)
 322 {
 323         struct i2c_client *client = to_i2c_client(dev);
 324         struct adp5520_chip *chip = dev_get_drvdata(&client->dev);
 325 
 326         adp5520_write(chip->dev, ADP5520_MODE_STATUS, chip->mode);
 327         return 0;
 328 }
 329 #endif
 330 
 331 static SIMPLE_DEV_PM_OPS(adp5520_pm, adp5520_suspend, adp5520_resume);
 332 
 333 static const struct i2c_device_id adp5520_id[] = {
 334         { "pmic-adp5520", ID_ADP5520 },
 335         { "pmic-adp5501", ID_ADP5501 },
 336         { }
 337 };
 338 
 339 static struct i2c_driver adp5520_driver = {
 340         .driver = {
 341                 .name                   = "adp5520",
 342                 .pm                     = &adp5520_pm,
 343                 .suppress_bind_attrs    = true,
 344         },
 345         .probe          = adp5520_probe,
 346         .id_table       = adp5520_id,
 347 };
 348 builtin_i2c_driver(adp5520_driver);

/* [<][>][^][v][top][bottom][index][help] */