diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-05 12:01:30 +0900 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-05 12:01:30 +0900 |
commit | 578f1ef91aa92beb571bfb9af8f4d18f405f3b9e (patch) | |
tree | 8ff59e772d09180b7e7f952a8c90a1bcf25e1d19 /drivers/mfd/max8907.c | |
parent | ecefbd94b834fa32559d854646d777c56749ef1c (diff) | |
parent | 74d8378159de16a0a1d1975d4778120d263d6000 (diff) |
Merge tag 'mfd-3.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6
Pull MFD changes from Samuel Ortiz:
"MFD bits for the 3.7 merge window.
As usual we have a few new drivers:
- TI LP8788
- TI OMAP USB TLL
- Maxim MAX8907
- SMSC ECE1099
- Dialog Semiconductor DA9055
- A simpler syscon driver that allow us to get rid of the anatop one.
Drivers are also gradually getting Device Tree and IRQ domain support.
The following drivers got DT support:
- palmas, 88pm860x, tc3589x and twl4030-audio
And those ones now use the IRQ domain APIs:
- 88pm860x, tc3589x, db8500_prcmu
Also some other interesting changes:
- Intel's ICH LPC now supports Lynx Point
- TI's twl4030-audio added a GPO child
- tps6527 enabled its backlight subdevice
- The twl6030 pwm driver moved to the new PWM subsystem
And finally a bunch of cleanup and casual fixes for mc13xxx, 88pm860x,
palmas, ab8500, wm8994, wm5110, max8907 and the tps65xxx family."
Fix up various annoying conflicts: the DT and IRQ domain support came in
twice and was already in 3.6. And then it was apparently rebased.
Guys, DON'T REBASE!
* tag 'mfd-3.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (89 commits)
ARM: dts: Enable 88pm860x pmic
mfd: 88pm860x: Move gpadc init into touch
mfd: 88pm860x: Device tree support
mfd: 88pm860x: Use irqdomain
mfd: smsc: Add support for smsc gpio io/keypad driver
backlight: tps65217_bl: Add missing platform_set_drvdata in tps65217_bl_probe
mfd: DA9055 core driver
mfd: tps65910: Add alarm interrupt of TPS65910 RTC to mfd device list
mfd: wm5110: Add register patches for revision B
mfd: wm5110: Disable control interface error report for WM5110 rev B
mfd: max8907: Remove regulator-compatible from DT docs
backlight: Add TPS65217 WLED driver
mfd: Add backlight as subdevice to the tps65217
mfd: Provide the PRCMU with its own IRQ domain
mfd: Fix max8907 sparse warning
mfd: Add lp8788 mfd driver
mfd: dbx500: Provide a more accurate smp_twd clock
mfd: rc5t583: Fix warning messages
regulator: palmas: Add DT support
mfd: palmas: Change regulator defns to better suite DT
...
Diffstat (limited to 'drivers/mfd/max8907.c')
-rw-r--r-- | drivers/mfd/max8907.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/drivers/mfd/max8907.c b/drivers/mfd/max8907.c new file mode 100644 index 000000000000..17f2593d82b8 --- /dev/null +++ b/drivers/mfd/max8907.c @@ -0,0 +1,351 @@ +/* + * max8907.c - mfd driver for MAX8907 + * + * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.com> + * Copyright (C) 2010-2012, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max8907.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +static struct mfd_cell max8907_cells[] = { + { .name = "max8907-regulator", }, + { .name = "max8907-rtc", }, +}; + +static bool max8907_gen_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX8907_REG_ON_OFF_IRQ1: + case MAX8907_REG_ON_OFF_STAT: + case MAX8907_REG_ON_OFF_IRQ2: + case MAX8907_REG_CHG_IRQ1: + case MAX8907_REG_CHG_IRQ2: + case MAX8907_REG_CHG_STAT: + return true; + default: + return false; + } +} + +static bool max8907_gen_is_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX8907_REG_ON_OFF_IRQ1: + case MAX8907_REG_ON_OFF_IRQ2: + case MAX8907_REG_CHG_IRQ1: + case MAX8907_REG_CHG_IRQ2: + return true; + default: + return false; + } +} + +static bool max8907_gen_is_writeable_reg(struct device *dev, unsigned int reg) +{ + return !max8907_gen_is_volatile_reg(dev, reg); +} + +static const struct regmap_config max8907_regmap_gen_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = max8907_gen_is_volatile_reg, + .precious_reg = max8907_gen_is_precious_reg, + .writeable_reg = max8907_gen_is_writeable_reg, + .max_register = MAX8907_REG_LDO20VOUT, + .cache_type = REGCACHE_RBTREE, +}; + +static bool max8907_rtc_is_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg <= MAX8907_REG_RTC_YEAR2) + return true; + + switch (reg) { + case MAX8907_REG_RTC_STATUS: + case MAX8907_REG_RTC_IRQ: + return true; + default: + return false; + } +} + +static bool max8907_rtc_is_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX8907_REG_RTC_IRQ: + return true; + default: + return false; + } +} + +static bool max8907_rtc_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX8907_REG_RTC_STATUS: + case MAX8907_REG_RTC_IRQ: + return false; + default: + return true; + } +} + +static const struct regmap_config max8907_regmap_rtc_config = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = max8907_rtc_is_volatile_reg, + .precious_reg = max8907_rtc_is_precious_reg, + .writeable_reg = max8907_rtc_is_writeable_reg, + .max_register = MAX8907_REG_MPL_CNTL, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct regmap_irq max8907_chg_irqs[] = { + { .reg_offset = 0, .mask = 1 << 0, }, + { .reg_offset = 0, .mask = 1 << 1, }, + { .reg_offset = 0, .mask = 1 << 2, }, + { .reg_offset = 1, .mask = 1 << 0, }, + { .reg_offset = 1, .mask = 1 << 1, }, + { .reg_offset = 1, .mask = 1 << 2, }, + { .reg_offset = 1, .mask = 1 << 3, }, + { .reg_offset = 1, .mask = 1 << 4, }, + { .reg_offset = 1, .mask = 1 << 5, }, + { .reg_offset = 1, .mask = 1 << 6, }, + { .reg_offset = 1, .mask = 1 << 7, }, +}; + +static const struct regmap_irq_chip max8907_chg_irq_chip = { + .name = "max8907 chg", + .status_base = MAX8907_REG_CHG_IRQ1, + .mask_base = MAX8907_REG_CHG_IRQ1_MASK, + .wake_base = MAX8907_REG_CHG_IRQ1_MASK, + .irq_reg_stride = MAX8907_REG_CHG_IRQ2 - MAX8907_REG_CHG_IRQ1, + .num_regs = 2, + .irqs = max8907_chg_irqs, + .num_irqs = ARRAY_SIZE(max8907_chg_irqs), +}; + +static const struct regmap_irq max8907_on_off_irqs[] = { + { .reg_offset = 0, .mask = 1 << 0, }, + { .reg_offset = 0, .mask = 1 << 1, }, + { .reg_offset = 0, .mask = 1 << 2, }, + { .reg_offset = 0, .mask = 1 << 3, }, + { .reg_offset = 0, .mask = 1 << 4, }, + { .reg_offset = 0, .mask = 1 << 5, }, + { .reg_offset = 0, .mask = 1 << 6, }, + { .reg_offset = 0, .mask = 1 << 7, }, + { .reg_offset = 1, .mask = 1 << 0, }, + { .reg_offset = 1, .mask = 1 << 1, }, +}; + +static const struct regmap_irq_chip max8907_on_off_irq_chip = { + .name = "max8907 on_off", + .status_base = MAX8907_REG_ON_OFF_IRQ1, + .mask_base = MAX8907_REG_ON_OFF_IRQ1_MASK, + .irq_reg_stride = MAX8907_REG_ON_OFF_IRQ2 - MAX8907_REG_ON_OFF_IRQ1, + .num_regs = 2, + .irqs = max8907_on_off_irqs, + .num_irqs = ARRAY_SIZE(max8907_on_off_irqs), +}; + +static const struct regmap_irq max8907_rtc_irqs[] = { + { .reg_offset = 0, .mask = 1 << 2, }, + { .reg_offset = 0, .mask = 1 << 3, }, +}; + +static const struct regmap_irq_chip max8907_rtc_irq_chip = { + .name = "max8907 rtc", + .status_base = MAX8907_REG_RTC_IRQ, + .mask_base = MAX8907_REG_RTC_IRQ_MASK, + .num_regs = 1, + .irqs = max8907_rtc_irqs, + .num_irqs = ARRAY_SIZE(max8907_rtc_irqs), +}; + +static struct max8907 *max8907_pm_off; +static void max8907_power_off(void) +{ + regmap_update_bits(max8907_pm_off->regmap_gen, MAX8907_REG_RESET_CNFG, + MAX8907_MASK_POWER_OFF, MAX8907_MASK_POWER_OFF); +} + +static __devinit int max8907_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max8907 *max8907; + int ret; + struct max8907_platform_data *pdata = dev_get_platdata(&i2c->dev); + bool pm_off = false; + + if (pdata) + pm_off = pdata->pm_off; + else if (i2c->dev.of_node) + pm_off = of_property_read_bool(i2c->dev.of_node, + "maxim,system-power-controller"); + + max8907 = devm_kzalloc(&i2c->dev, sizeof(struct max8907), GFP_KERNEL); + if (!max8907) { + ret = -ENOMEM; + goto err_alloc_drvdata; + } + + max8907->dev = &i2c->dev; + dev_set_drvdata(max8907->dev, max8907); + + max8907->i2c_gen = i2c; + i2c_set_clientdata(i2c, max8907); + max8907->regmap_gen = devm_regmap_init_i2c(i2c, + &max8907_regmap_gen_config); + if (IS_ERR(max8907->regmap_gen)) { + ret = PTR_ERR(max8907->regmap_gen); + dev_err(&i2c->dev, "gen regmap init failed: %d\n", ret); + goto err_regmap_gen; + } + + max8907->i2c_rtc = i2c_new_dummy(i2c->adapter, MAX8907_RTC_I2C_ADDR); + if (!max8907->i2c_rtc) { + ret = -ENOMEM; + goto err_dummy_rtc; + } + i2c_set_clientdata(max8907->i2c_rtc, max8907); + max8907->regmap_rtc = devm_regmap_init_i2c(max8907->i2c_rtc, + &max8907_regmap_rtc_config); + if (IS_ERR(max8907->regmap_rtc)) { + ret = PTR_ERR(max8907->regmap_rtc); + dev_err(&i2c->dev, "rtc regmap init failed: %d\n", ret); + goto err_regmap_rtc; + } + + irq_set_status_flags(max8907->i2c_gen->irq, IRQ_NOAUTOEN); + + ret = regmap_add_irq_chip(max8907->regmap_gen, max8907->i2c_gen->irq, + IRQF_ONESHOT | IRQF_SHARED, -1, + &max8907_chg_irq_chip, + &max8907->irqc_chg); + if (ret != 0) { + dev_err(&i2c->dev, "failed to add chg irq chip: %d\n", ret); + goto err_irqc_chg; + } + ret = regmap_add_irq_chip(max8907->regmap_gen, max8907->i2c_gen->irq, + IRQF_ONESHOT | IRQF_SHARED, -1, + &max8907_on_off_irq_chip, + &max8907->irqc_on_off); + if (ret != 0) { + dev_err(&i2c->dev, "failed to add on off irq chip: %d\n", ret); + goto err_irqc_on_off; + } + ret = regmap_add_irq_chip(max8907->regmap_rtc, max8907->i2c_gen->irq, + IRQF_ONESHOT | IRQF_SHARED, -1, + &max8907_rtc_irq_chip, + &max8907->irqc_rtc); + if (ret != 0) { + dev_err(&i2c->dev, "failed to add rtc irq chip: %d\n", ret); + goto err_irqc_rtc; + } + + enable_irq(max8907->i2c_gen->irq); + + ret = mfd_add_devices(max8907->dev, -1, max8907_cells, + ARRAY_SIZE(max8907_cells), NULL, 0, NULL); + if (ret != 0) { + dev_err(&i2c->dev, "failed to add MFD devices %d\n", ret); + goto err_add_devices; + } + + if (pm_off && !pm_power_off) { + max8907_pm_off = max8907; + pm_power_off = max8907_power_off; + } + + return 0; + +err_add_devices: + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_rtc); +err_irqc_rtc: + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_on_off); +err_irqc_on_off: + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_chg); +err_irqc_chg: +err_regmap_rtc: + i2c_unregister_device(max8907->i2c_rtc); +err_dummy_rtc: +err_regmap_gen: +err_alloc_drvdata: + return ret; +} + +static __devexit int max8907_i2c_remove(struct i2c_client *i2c) +{ + struct max8907 *max8907 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max8907->dev); + + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_rtc); + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_on_off); + regmap_del_irq_chip(max8907->i2c_gen->irq, max8907->irqc_chg); + + i2c_unregister_device(max8907->i2c_rtc); + + return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id max8907_of_match[] = { + { .compatible = "maxim,max8907" }, + { }, +}; +MODULE_DEVICE_TABLE(of, max8907_of_match); +#endif + +static const struct i2c_device_id max8907_i2c_id[] = { + {"max8907", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, max8907_i2c_id); + +static struct i2c_driver max8907_i2c_driver = { + .driver = { + .name = "max8907", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max8907_of_match), + }, + .probe = max8907_i2c_probe, + .remove = max8907_i2c_remove, + .id_table = max8907_i2c_id, +}; + +static int __init max8907_i2c_init(void) +{ + int ret = -ENODEV; + + ret = i2c_add_driver(&max8907_i2c_driver); + if (ret != 0) + pr_err("Failed to register I2C driver: %d\n", ret); + + return ret; +} +subsys_initcall(max8907_i2c_init); + +static void __exit max8907_i2c_exit(void) +{ + i2c_del_driver(&max8907_i2c_driver); +} +module_exit(max8907_i2c_exit); + +MODULE_DESCRIPTION("MAX8907 multi-function core driver"); +MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>"); +MODULE_LICENSE("GPL v2"); |