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