remarkable-linux/drivers/mfd/max77693.c
Linus Torvalds 8bd8fd0a29 - New Device Support
- New Clocksource driver from ST
    - New MFD/ACPI/DMA drivers for Intel's Sunrisepoint PCH based platforms
    - Add support for Arizona WM8998 and WM1814
    - Add support for Dialog Semi DA9062 and DA9063
    - Add support for Kontron COMe-bBL6 and COMe-cBW6
    - Add support for X-Powers AXP152
    - Add support for Atmel, many
    - Add support for STMPE, many
    - Add support for USB in X-Powers AXP22X
 
  - Core Frameworks
    - New Base API to traverse devices and their children in reverse order
 
  - Bug Fixes
    - Fix race between runtime-suspend and IRQs
    - Obtain platform data form more reliable source
 
  - Fix-ups
    - Constifying things
    - Variable signage changes
    - Kconfig depends|selects changes
    - Make use of BIT() macro
    - Do not supply .owner attribute in *_driver structures
    - MAINTAINERS entries
    - Stop using set_irq_flags()
    - Start using irq_set_chained_handler_and_data()
    - Export DT device ID structures
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJV6FCRAAoJEFGvii+H/HdhO2MP/1GxcbbywMXU4goj40gJaYfx
 kk0zH0S7i8+A8hD7SoCIQNkWN5o7i6sNYUA6sCTnPqixbyrkduWCyid1XsATu+41
 iiKEGyiCRyEHhCwnwCXvaFhpAZBzDi7FKj6hhf6nnRMHSEqwrs2aBqWgzNrOZTs0
 u66i/JHccnDdfHHm9Y7XcKMA8pWVqRMnwwaHreuYTFqfrEB0UGCYpmEeEBynGVKh
 MUGC0lCUrEKp59aOexZRtBUla/5BeALJd//vMQtf/+D0YPvE8lppDNwkgCe4buXN
 ZlNHDQooIWIiZfTj7wbHaTWjrBK7MsOEHWBUjNsk2nyDvDOJoGhTrSdJwPeyhUSh
 d2eUyW6sPEQY21XPwuD0DhfRKYKLOzVRhIcxvjlRAq9QHDWVXKyIlf3M70fculK8
 5FN1Wb6Sc2h0OvMC5RemPpxMwZSq6Ks3XANa718Ju802TGK/xk6iRqhZrEut/qrN
 rLYsU84TLUz6YindozTiI5FrGo+zSp9OlUU4z7HUh+4t3H5/opdsRjRp0ICwgIbY
 NxAmsk2d/vJ7xX7FAAjwMY2rPIC0zIksbGEe1AJweWV455EcDMaBM1/e9zDzHciI
 TXVxbzs3DFBadtQWlLv/VkwZmt43MTI8g6ozTTJJkPQNCKThtz4bvDSu8rQWqFua
 bkbRyQroraX5fM0Q3HIs
 =2blS
 -----END PGP SIGNATURE-----

Merge tag 'mfd-for-linus-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd

Pull MFD updates from Lee Jones:
 "New Device Support:
   - New Clocksource driver from ST
   - New MFD/ACPI/DMA drivers for Intel's Sunrisepoint PCH based platforms
   - Add support for Arizona WM8998 and WM1814
   - Add support for Dialog Semi DA9062 and DA9063
   - Add support for Kontron COMe-bBL6 and COMe-cBW6
   - Add support for X-Powers AXP152
   - Add support for Atmel, many
   - Add support for STMPE, many
   - Add support for USB in X-Powers AXP22X

  Core Frameworks:
   - New Base API to traverse devices and their children in reverse order

  Bug Fixes:
   - Fix race between runtime-suspend and IRQs
   - Obtain platform data form more reliable source

  Fix-ups:
   - Constifying things
   - Variable signage changes
   - Kconfig depends|selects changes
   - Make use of BIT() macro
   - Do not supply .owner attribute in *_driver structures
   - MAINTAINERS entries
   - Stop using set_irq_flags()
   - Start using irq_set_chained_handler_and_data()
   - Export DT device ID structures"

* tag 'mfd-for-linus-4.3' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (69 commits)
  mfd: jz4740-adc: Init mask cache in generic IRQ chip
  mfd: cros_ec: spi: Add OF match table
  mfd: stmpe: Add OF match table
  mfd: max77686: Split out regulator part from the DT binding
  mfd: Add DT binding for Maxim MAX77802 IC
  mfd: max77686: Use a generic name for the PMIC node in the example
  mfd: max77686: Don't suggest in binding to use a deprecated property
  mfd: Add MFD_CROS_EC dependencies
  mfd: cros_ec: Remove CROS_EC_PROTO dependency for SPI and I2C drivers
  mfd: axp20x: Add a cell for the usb power_supply part of the axp20x PMICs
  mfd: axp20x: Add missing registers, and mark more registers volatile
  mfd: arizona: Fixup some formatting/white space errors
  mfd: wm8994: Fix NULL pointer exception on missing pdata
  of: Add vendor prefix for Nuvoton
  mfd: mt6397: Implement wake handler and suspend/resume to handle wake up event
  mfd: atmel-hlcdc: Add support for new SoCs
  mfd: Export OF module alias information in missing drivers
  mfd: stw481x: Export I2C module alias information
  mfd: da9062: Support for the DA9063 OnKey in the DA9062 core
  mfd: max899x: Avoid redundant irq_data lookup
  ...
2015-09-04 11:35:03 -07:00

400 lines
11 KiB
C

/*
* max77693.c - mfd core driver for the MAX 77693
*
* Copyright (C) 2012 Samsung Electronics
* SangYoung Son <hello.son@smasung.com>
*
* This program is not provided / owned by Maxim Integrated Products.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* This driver is based on max8997.c
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/mutex.h>
#include <linux/mfd/core.h>
#include <linux/mfd/max77693.h>
#include <linux/mfd/max77693-common.h>
#include <linux/mfd/max77693-private.h>
#include <linux/regulator/machine.h>
#include <linux/regmap.h>
#define I2C_ADDR_PMIC (0xCC >> 1) /* Charger, Flash LED */
#define I2C_ADDR_MUIC (0x4A >> 1)
#define I2C_ADDR_HAPTIC (0x90 >> 1)
static const struct mfd_cell max77693_devs[] = {
{ .name = "max77693-pmic", },
{
.name = "max77693-charger",
.of_compatible = "maxim,max77693-charger",
},
{ .name = "max77693-muic", },
{
.name = "max77693-haptic",
.of_compatible = "maxim,max77693-haptic",
},
{
.name = "max77693-led",
.of_compatible = "maxim,max77693-led",
},
};
static const struct regmap_config max77693_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX77693_PMIC_REG_END,
};
static const struct regmap_irq max77693_led_irqs[] = {
{ .mask = LED_IRQ_FLED2_OPEN, },
{ .mask = LED_IRQ_FLED2_SHORT, },
{ .mask = LED_IRQ_FLED1_OPEN, },
{ .mask = LED_IRQ_FLED1_SHORT, },
{ .mask = LED_IRQ_MAX_FLASH, },
};
static const struct regmap_irq_chip max77693_led_irq_chip = {
.name = "max77693-led",
.status_base = MAX77693_LED_REG_FLASH_INT,
.mask_base = MAX77693_LED_REG_FLASH_INT_MASK,
.mask_invert = false,
.num_regs = 1,
.irqs = max77693_led_irqs,
.num_irqs = ARRAY_SIZE(max77693_led_irqs),
};
static const struct regmap_irq max77693_topsys_irqs[] = {
{ .mask = TOPSYS_IRQ_T120C_INT, },
{ .mask = TOPSYS_IRQ_T140C_INT, },
{ .mask = TOPSYS_IRQ_LOWSYS_INT, },
};
static const struct regmap_irq_chip max77693_topsys_irq_chip = {
.name = "max77693-topsys",
.status_base = MAX77693_PMIC_REG_TOPSYS_INT,
.mask_base = MAX77693_PMIC_REG_TOPSYS_INT_MASK,
.mask_invert = false,
.num_regs = 1,
.irqs = max77693_topsys_irqs,
.num_irqs = ARRAY_SIZE(max77693_topsys_irqs),
};
static const struct regmap_irq max77693_charger_irqs[] = {
{ .mask = CHG_IRQ_BYP_I, },
{ .mask = CHG_IRQ_THM_I, },
{ .mask = CHG_IRQ_BAT_I, },
{ .mask = CHG_IRQ_CHG_I, },
{ .mask = CHG_IRQ_CHGIN_I, },
};
static const struct regmap_irq_chip max77693_charger_irq_chip = {
.name = "max77693-charger",
.status_base = MAX77693_CHG_REG_CHG_INT,
.mask_base = MAX77693_CHG_REG_CHG_INT_MASK,
.mask_invert = false,
.num_regs = 1,
.irqs = max77693_charger_irqs,
.num_irqs = ARRAY_SIZE(max77693_charger_irqs),
};
static const struct regmap_config max77693_regmap_muic_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX77693_MUIC_REG_END,
};
static const struct regmap_irq max77693_muic_irqs[] = {
{ .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC, },
{ .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC_LOW, },
{ .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC_ERR, },
{ .reg_offset = 0, .mask = MUIC_IRQ_INT1_ADC1K, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_CHGTYP, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_CHGDETREUN, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_DCDTMR, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_DXOVP, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_VBVOLT, },
{ .reg_offset = 1, .mask = MUIC_IRQ_INT2_VIDRM, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_EOC, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_CGMBC, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_OVP, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_MBCCHG_ERR, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_CHG_ENABLED, },
{ .reg_offset = 2, .mask = MUIC_IRQ_INT3_BAT_DET, },
};
static const struct regmap_irq_chip max77693_muic_irq_chip = {
.name = "max77693-muic",
.status_base = MAX77693_MUIC_REG_INT1,
.mask_base = MAX77693_MUIC_REG_INTMASK1,
.mask_invert = true,
.num_regs = 3,
.irqs = max77693_muic_irqs,
.num_irqs = ARRAY_SIZE(max77693_muic_irqs),
};
static const struct regmap_config max77693_regmap_haptic_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX77693_HAPTIC_REG_END,
};
static int max77693_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max77693_dev *max77693;
unsigned int reg_data;
int ret = 0;
max77693 = devm_kzalloc(&i2c->dev,
sizeof(struct max77693_dev), GFP_KERNEL);
if (max77693 == NULL)
return -ENOMEM;
i2c_set_clientdata(i2c, max77693);
max77693->dev = &i2c->dev;
max77693->i2c = i2c;
max77693->irq = i2c->irq;
max77693->type = id->driver_data;
max77693->regmap = devm_regmap_init_i2c(i2c, &max77693_regmap_config);
if (IS_ERR(max77693->regmap)) {
ret = PTR_ERR(max77693->regmap);
dev_err(max77693->dev, "failed to allocate register map: %d\n",
ret);
return ret;
}
ret = regmap_read(max77693->regmap, MAX77693_PMIC_REG_PMIC_ID2,
&reg_data);
if (ret < 0) {
dev_err(max77693->dev, "device not found on this channel\n");
return ret;
} else
dev_info(max77693->dev, "device ID: 0x%x\n", reg_data);
max77693->i2c_muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC);
if (!max77693->i2c_muic) {
dev_err(max77693->dev, "Failed to allocate I2C device for MUIC\n");
return -ENODEV;
}
i2c_set_clientdata(max77693->i2c_muic, max77693);
max77693->i2c_haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC);
if (!max77693->i2c_haptic) {
dev_err(max77693->dev, "Failed to allocate I2C device for Haptic\n");
ret = -ENODEV;
goto err_i2c_haptic;
}
i2c_set_clientdata(max77693->i2c_haptic, max77693);
max77693->regmap_haptic = devm_regmap_init_i2c(max77693->i2c_haptic,
&max77693_regmap_haptic_config);
if (IS_ERR(max77693->regmap_haptic)) {
ret = PTR_ERR(max77693->regmap_haptic);
dev_err(max77693->dev,
"failed to initialize haptic register map: %d\n", ret);
goto err_regmap;
}
/*
* Initialize register map for MUIC device because use regmap-muic
* instance of MUIC device when irq of max77693 is initialized
* before call max77693-muic probe() function.
*/
max77693->regmap_muic = devm_regmap_init_i2c(max77693->i2c_muic,
&max77693_regmap_muic_config);
if (IS_ERR(max77693->regmap_muic)) {
ret = PTR_ERR(max77693->regmap_muic);
dev_err(max77693->dev,
"failed to allocate register map: %d\n", ret);
goto err_regmap;
}
ret = regmap_add_irq_chip(max77693->regmap, max77693->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&max77693_led_irq_chip,
&max77693->irq_data_led);
if (ret) {
dev_err(max77693->dev, "failed to add irq chip: %d\n", ret);
goto err_regmap;
}
ret = regmap_add_irq_chip(max77693->regmap, max77693->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&max77693_topsys_irq_chip,
&max77693->irq_data_topsys);
if (ret) {
dev_err(max77693->dev, "failed to add irq chip: %d\n", ret);
goto err_irq_topsys;
}
ret = regmap_add_irq_chip(max77693->regmap, max77693->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&max77693_charger_irq_chip,
&max77693->irq_data_chg);
if (ret) {
dev_err(max77693->dev, "failed to add irq chip: %d\n", ret);
goto err_irq_charger;
}
ret = regmap_add_irq_chip(max77693->regmap_muic, max77693->irq,
IRQF_ONESHOT | IRQF_SHARED |
IRQF_TRIGGER_FALLING, 0,
&max77693_muic_irq_chip,
&max77693->irq_data_muic);
if (ret) {
dev_err(max77693->dev, "failed to add irq chip: %d\n", ret);
goto err_irq_muic;
}
/* Unmask interrupts from all blocks in interrupt source register */
ret = regmap_update_bits(max77693->regmap,
MAX77693_PMIC_REG_INTSRC_MASK,
SRC_IRQ_ALL, (unsigned int)~SRC_IRQ_ALL);
if (ret < 0) {
dev_err(max77693->dev,
"Could not unmask interrupts in INTSRC: %d\n",
ret);
goto err_intsrc;
}
pm_runtime_set_active(max77693->dev);
ret = mfd_add_devices(max77693->dev, -1, max77693_devs,
ARRAY_SIZE(max77693_devs), NULL, 0, NULL);
if (ret < 0)
goto err_mfd;
return ret;
err_mfd:
mfd_remove_devices(max77693->dev);
err_intsrc:
regmap_del_irq_chip(max77693->irq, max77693->irq_data_muic);
err_irq_muic:
regmap_del_irq_chip(max77693->irq, max77693->irq_data_chg);
err_irq_charger:
regmap_del_irq_chip(max77693->irq, max77693->irq_data_topsys);
err_irq_topsys:
regmap_del_irq_chip(max77693->irq, max77693->irq_data_led);
err_regmap:
i2c_unregister_device(max77693->i2c_haptic);
err_i2c_haptic:
i2c_unregister_device(max77693->i2c_muic);
return ret;
}
static int max77693_i2c_remove(struct i2c_client *i2c)
{
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
mfd_remove_devices(max77693->dev);
regmap_del_irq_chip(max77693->irq, max77693->irq_data_muic);
regmap_del_irq_chip(max77693->irq, max77693->irq_data_chg);
regmap_del_irq_chip(max77693->irq, max77693->irq_data_topsys);
regmap_del_irq_chip(max77693->irq, max77693->irq_data_led);
i2c_unregister_device(max77693->i2c_muic);
i2c_unregister_device(max77693->i2c_haptic);
return 0;
}
static const struct i2c_device_id max77693_i2c_id[] = {
{ "max77693", TYPE_MAX77693 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max77693_i2c_id);
static int max77693_suspend(struct device *dev)
{
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev)) {
enable_irq_wake(max77693->irq);
disable_irq(max77693->irq);
}
return 0;
}
static int max77693_resume(struct device *dev)
{
struct i2c_client *i2c = container_of(dev, struct i2c_client, dev);
struct max77693_dev *max77693 = i2c_get_clientdata(i2c);
if (device_may_wakeup(dev)) {
disable_irq_wake(max77693->irq);
enable_irq(max77693->irq);
}
return 0;
}
static const struct dev_pm_ops max77693_pm = {
.suspend = max77693_suspend,
.resume = max77693_resume,
};
#ifdef CONFIG_OF
static const struct of_device_id max77693_dt_match[] = {
{ .compatible = "maxim,max77693" },
{},
};
#endif
static struct i2c_driver max77693_i2c_driver = {
.driver = {
.name = "max77693",
.pm = &max77693_pm,
.of_match_table = of_match_ptr(max77693_dt_match),
},
.probe = max77693_i2c_probe,
.remove = max77693_i2c_remove,
.id_table = max77693_i2c_id,
};
static int __init max77693_i2c_init(void)
{
return i2c_add_driver(&max77693_i2c_driver);
}
/* init early so consumer devices can complete system boot */
subsys_initcall(max77693_i2c_init);
static void __exit max77693_i2c_exit(void)
{
i2c_del_driver(&max77693_i2c_driver);
}
module_exit(max77693_i2c_exit);
MODULE_DESCRIPTION("MAXIM 77693 multi-function core driver");
MODULE_AUTHOR("SangYoung, Son <hello.son@samsung.com>");
MODULE_LICENSE("GPL");