1d0f60334SBartosz Golaszewski // SPDX-License-Identifier: GPL-2.0 2d0f60334SBartosz Golaszewski // 3d0f60334SBartosz Golaszewski // Copyright (C) 2018 BayLibre SAS 4d0f60334SBartosz Golaszewski // Author: Bartosz Golaszewski <bgolaszewski@baylibre.com> 5d0f60334SBartosz Golaszewski // 6d0f60334SBartosz Golaszewski // Core MFD driver for MAXIM 77650/77651 charger/power-supply. 7d0f60334SBartosz Golaszewski // Programming manual: https://pdfserv.maximintegrated.com/en/an/AN6428.pdf 8d0f60334SBartosz Golaszewski 9d0f60334SBartosz Golaszewski #include <linux/i2c.h> 10d0f60334SBartosz Golaszewski #include <linux/interrupt.h> 11d0f60334SBartosz Golaszewski #include <linux/irq.h> 12d0f60334SBartosz Golaszewski #include <linux/mfd/core.h> 13d0f60334SBartosz Golaszewski #include <linux/mfd/max77650.h> 14d0f60334SBartosz Golaszewski #include <linux/module.h> 15d0f60334SBartosz Golaszewski #include <linux/of.h> 16d0f60334SBartosz Golaszewski #include <linux/regmap.h> 17d0f60334SBartosz Golaszewski 18d0f60334SBartosz Golaszewski #define MAX77650_INT_GPI_F_MSK BIT(0) 19d0f60334SBartosz Golaszewski #define MAX77650_INT_GPI_R_MSK BIT(1) 20d0f60334SBartosz Golaszewski #define MAX77650_INT_GPI_MSK \ 21d0f60334SBartosz Golaszewski (MAX77650_INT_GPI_F_MSK | MAX77650_INT_GPI_R_MSK) 22d0f60334SBartosz Golaszewski #define MAX77650_INT_nEN_F_MSK BIT(2) 23d0f60334SBartosz Golaszewski #define MAX77650_INT_nEN_R_MSK BIT(3) 24d0f60334SBartosz Golaszewski #define MAX77650_INT_TJAL1_R_MSK BIT(4) 25d0f60334SBartosz Golaszewski #define MAX77650_INT_TJAL2_R_MSK BIT(5) 26d0f60334SBartosz Golaszewski #define MAX77650_INT_DOD_R_MSK BIT(6) 27d0f60334SBartosz Golaszewski 28d0f60334SBartosz Golaszewski #define MAX77650_INT_THM_MSK BIT(0) 29d0f60334SBartosz Golaszewski #define MAX77650_INT_CHG_MSK BIT(1) 30d0f60334SBartosz Golaszewski #define MAX77650_INT_CHGIN_MSK BIT(2) 31d0f60334SBartosz Golaszewski #define MAX77650_INT_TJ_REG_MSK BIT(3) 32d0f60334SBartosz Golaszewski #define MAX77650_INT_CHGIN_CTRL_MSK BIT(4) 33d0f60334SBartosz Golaszewski #define MAX77650_INT_SYS_CTRL_MSK BIT(5) 34d0f60334SBartosz Golaszewski #define MAX77650_INT_SYS_CNFG_MSK BIT(6) 35d0f60334SBartosz Golaszewski 36d0f60334SBartosz Golaszewski #define MAX77650_INT_GLBL_OFFSET 0 37d0f60334SBartosz Golaszewski #define MAX77650_INT_CHG_OFFSET 1 38d0f60334SBartosz Golaszewski 39d0f60334SBartosz Golaszewski #define MAX77650_SBIA_LPM_MASK BIT(5) 40d0f60334SBartosz Golaszewski #define MAX77650_SBIA_LPM_DISABLED 0x00 41d0f60334SBartosz Golaszewski 42d0f60334SBartosz Golaszewski enum { 43d0f60334SBartosz Golaszewski MAX77650_INT_GPI, 44d0f60334SBartosz Golaszewski MAX77650_INT_nEN_F, 45d0f60334SBartosz Golaszewski MAX77650_INT_nEN_R, 46d0f60334SBartosz Golaszewski MAX77650_INT_TJAL1_R, 47d0f60334SBartosz Golaszewski MAX77650_INT_TJAL2_R, 48d0f60334SBartosz Golaszewski MAX77650_INT_DOD_R, 49d0f60334SBartosz Golaszewski MAX77650_INT_THM, 50d0f60334SBartosz Golaszewski MAX77650_INT_CHG, 51d0f60334SBartosz Golaszewski MAX77650_INT_CHGIN, 52d0f60334SBartosz Golaszewski MAX77650_INT_TJ_REG, 53d0f60334SBartosz Golaszewski MAX77650_INT_CHGIN_CTRL, 54d0f60334SBartosz Golaszewski MAX77650_INT_SYS_CTRL, 55d0f60334SBartosz Golaszewski MAX77650_INT_SYS_CNFG, 56d0f60334SBartosz Golaszewski }; 57d0f60334SBartosz Golaszewski 58d0f60334SBartosz Golaszewski static const struct resource max77650_charger_resources[] = { 59d0f60334SBartosz Golaszewski DEFINE_RES_IRQ_NAMED(MAX77650_INT_CHG, "CHG"), 60d0f60334SBartosz Golaszewski DEFINE_RES_IRQ_NAMED(MAX77650_INT_CHGIN, "CHGIN"), 61d0f60334SBartosz Golaszewski }; 62d0f60334SBartosz Golaszewski 63d0f60334SBartosz Golaszewski static const struct resource max77650_gpio_resources[] = { 64d0f60334SBartosz Golaszewski DEFINE_RES_IRQ_NAMED(MAX77650_INT_GPI, "GPI"), 65d0f60334SBartosz Golaszewski }; 66d0f60334SBartosz Golaszewski 67d0f60334SBartosz Golaszewski static const struct resource max77650_onkey_resources[] = { 68d0f60334SBartosz Golaszewski DEFINE_RES_IRQ_NAMED(MAX77650_INT_nEN_F, "nEN_F"), 69d0f60334SBartosz Golaszewski DEFINE_RES_IRQ_NAMED(MAX77650_INT_nEN_R, "nEN_R"), 70d0f60334SBartosz Golaszewski }; 71d0f60334SBartosz Golaszewski 72d0f60334SBartosz Golaszewski static const struct mfd_cell max77650_cells[] = { 73d0f60334SBartosz Golaszewski { 74d0f60334SBartosz Golaszewski .name = "max77650-regulator", 75d0f60334SBartosz Golaszewski .of_compatible = "maxim,max77650-regulator", 76d0f60334SBartosz Golaszewski }, { 77d0f60334SBartosz Golaszewski .name = "max77650-charger", 78d0f60334SBartosz Golaszewski .of_compatible = "maxim,max77650-charger", 79d0f60334SBartosz Golaszewski .resources = max77650_charger_resources, 80d0f60334SBartosz Golaszewski .num_resources = ARRAY_SIZE(max77650_charger_resources), 81d0f60334SBartosz Golaszewski }, { 82d0f60334SBartosz Golaszewski .name = "max77650-gpio", 83d0f60334SBartosz Golaszewski .of_compatible = "maxim,max77650-gpio", 84d0f60334SBartosz Golaszewski .resources = max77650_gpio_resources, 85d0f60334SBartosz Golaszewski .num_resources = ARRAY_SIZE(max77650_gpio_resources), 86d0f60334SBartosz Golaszewski }, { 87d0f60334SBartosz Golaszewski .name = "max77650-led", 88d0f60334SBartosz Golaszewski .of_compatible = "maxim,max77650-led", 89d0f60334SBartosz Golaszewski }, { 90d0f60334SBartosz Golaszewski .name = "max77650-onkey", 91d0f60334SBartosz Golaszewski .of_compatible = "maxim,max77650-onkey", 92d0f60334SBartosz Golaszewski .resources = max77650_onkey_resources, 93d0f60334SBartosz Golaszewski .num_resources = ARRAY_SIZE(max77650_onkey_resources), 94d0f60334SBartosz Golaszewski }, 95d0f60334SBartosz Golaszewski }; 96d0f60334SBartosz Golaszewski 97d0f60334SBartosz Golaszewski static const struct regmap_irq max77650_irqs[] = { 98d0f60334SBartosz Golaszewski [MAX77650_INT_GPI] = { 99d0f60334SBartosz Golaszewski .reg_offset = MAX77650_INT_GLBL_OFFSET, 100d0f60334SBartosz Golaszewski .mask = MAX77650_INT_GPI_MSK, 101d0f60334SBartosz Golaszewski .type = { 102d0f60334SBartosz Golaszewski .type_falling_val = MAX77650_INT_GPI_F_MSK, 103d0f60334SBartosz Golaszewski .type_rising_val = MAX77650_INT_GPI_R_MSK, 104d0f60334SBartosz Golaszewski .types_supported = IRQ_TYPE_EDGE_BOTH, 105d0f60334SBartosz Golaszewski }, 106d0f60334SBartosz Golaszewski }, 107d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_nEN_F, 108d0f60334SBartosz Golaszewski MAX77650_INT_GLBL_OFFSET, MAX77650_INT_nEN_F_MSK), 109d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_nEN_R, 110d0f60334SBartosz Golaszewski MAX77650_INT_GLBL_OFFSET, MAX77650_INT_nEN_R_MSK), 111d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_TJAL1_R, 112d0f60334SBartosz Golaszewski MAX77650_INT_GLBL_OFFSET, MAX77650_INT_TJAL1_R_MSK), 113d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_TJAL2_R, 114d0f60334SBartosz Golaszewski MAX77650_INT_GLBL_OFFSET, MAX77650_INT_TJAL2_R_MSK), 115d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_DOD_R, 116d0f60334SBartosz Golaszewski MAX77650_INT_GLBL_OFFSET, MAX77650_INT_DOD_R_MSK), 117d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_THM, 118d0f60334SBartosz Golaszewski MAX77650_INT_CHG_OFFSET, MAX77650_INT_THM_MSK), 119d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_CHG, 120d0f60334SBartosz Golaszewski MAX77650_INT_CHG_OFFSET, MAX77650_INT_CHG_MSK), 121d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_CHGIN, 122d0f60334SBartosz Golaszewski MAX77650_INT_CHG_OFFSET, MAX77650_INT_CHGIN_MSK), 123d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_TJ_REG, 124d0f60334SBartosz Golaszewski MAX77650_INT_CHG_OFFSET, MAX77650_INT_TJ_REG_MSK), 125d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_CHGIN_CTRL, 126d0f60334SBartosz Golaszewski MAX77650_INT_CHG_OFFSET, MAX77650_INT_CHGIN_CTRL_MSK), 127d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_SYS_CTRL, 128d0f60334SBartosz Golaszewski MAX77650_INT_CHG_OFFSET, MAX77650_INT_SYS_CTRL_MSK), 129d0f60334SBartosz Golaszewski REGMAP_IRQ_REG(MAX77650_INT_SYS_CNFG, 130d0f60334SBartosz Golaszewski MAX77650_INT_CHG_OFFSET, MAX77650_INT_SYS_CNFG_MSK), 131d0f60334SBartosz Golaszewski }; 132d0f60334SBartosz Golaszewski 133d0f60334SBartosz Golaszewski static const struct regmap_irq_chip max77650_irq_chip = { 134d0f60334SBartosz Golaszewski .name = "max77650-irq", 135d0f60334SBartosz Golaszewski .irqs = max77650_irqs, 136d0f60334SBartosz Golaszewski .num_irqs = ARRAY_SIZE(max77650_irqs), 137d0f60334SBartosz Golaszewski .num_regs = 2, 138d0f60334SBartosz Golaszewski .status_base = MAX77650_REG_INT_GLBL, 139d0f60334SBartosz Golaszewski .mask_base = MAX77650_REG_INTM_GLBL, 140d0f60334SBartosz Golaszewski .type_in_mask = true, 141d0f60334SBartosz Golaszewski .type_invert = true, 142d0f60334SBartosz Golaszewski .init_ack_masked = true, 143d0f60334SBartosz Golaszewski .clear_on_unmask = true, 144d0f60334SBartosz Golaszewski }; 145d0f60334SBartosz Golaszewski 146d0f60334SBartosz Golaszewski static const struct regmap_config max77650_regmap_config = { 147d0f60334SBartosz Golaszewski .name = "max77650", 148d0f60334SBartosz Golaszewski .reg_bits = 8, 149d0f60334SBartosz Golaszewski .val_bits = 8, 150d0f60334SBartosz Golaszewski }; 151d0f60334SBartosz Golaszewski 152d0f60334SBartosz Golaszewski static int max77650_i2c_probe(struct i2c_client *i2c) 153d0f60334SBartosz Golaszewski { 154d0f60334SBartosz Golaszewski struct regmap_irq_chip_data *irq_data; 155d0f60334SBartosz Golaszewski struct device *dev = &i2c->dev; 156d0f60334SBartosz Golaszewski struct irq_domain *domain; 157d0f60334SBartosz Golaszewski struct regmap *map; 158d0f60334SBartosz Golaszewski unsigned int val; 159d0f60334SBartosz Golaszewski int rv, id; 160d0f60334SBartosz Golaszewski 161d0f60334SBartosz Golaszewski map = devm_regmap_init_i2c(i2c, &max77650_regmap_config); 162d0f60334SBartosz Golaszewski if (IS_ERR(map)) { 163d0f60334SBartosz Golaszewski dev_err(dev, "Unable to initialise I2C Regmap\n"); 164d0f60334SBartosz Golaszewski return PTR_ERR(map); 165d0f60334SBartosz Golaszewski } 166d0f60334SBartosz Golaszewski 167d0f60334SBartosz Golaszewski rv = regmap_read(map, MAX77650_REG_CID, &val); 168d0f60334SBartosz Golaszewski if (rv) { 169d0f60334SBartosz Golaszewski dev_err(dev, "Unable to read Chip ID\n"); 170d0f60334SBartosz Golaszewski return rv; 171d0f60334SBartosz Golaszewski } 172d0f60334SBartosz Golaszewski 173d0f60334SBartosz Golaszewski id = MAX77650_CID_BITS(val); 174d0f60334SBartosz Golaszewski switch (id) { 175d0f60334SBartosz Golaszewski case MAX77650_CID_77650A: 176d0f60334SBartosz Golaszewski case MAX77650_CID_77650C: 177d0f60334SBartosz Golaszewski case MAX77650_CID_77651A: 178d0f60334SBartosz Golaszewski case MAX77650_CID_77651B: 179d0f60334SBartosz Golaszewski break; 180d0f60334SBartosz Golaszewski default: 181d0f60334SBartosz Golaszewski dev_err(dev, "Chip not supported - ID: 0x%02x\n", id); 182d0f60334SBartosz Golaszewski return -ENODEV; 183d0f60334SBartosz Golaszewski } 184d0f60334SBartosz Golaszewski 185d0f60334SBartosz Golaszewski /* 186d0f60334SBartosz Golaszewski * This IC has a low-power mode which reduces the quiescent current 187d0f60334SBartosz Golaszewski * consumption to ~5.6uA but is only suitable for systems consuming 188d0f60334SBartosz Golaszewski * less than ~2mA. Since this is not likely the case even on 189d0f60334SBartosz Golaszewski * linux-based wearables - keep the chip in normal power mode. 190d0f60334SBartosz Golaszewski */ 191d0f60334SBartosz Golaszewski rv = regmap_update_bits(map, 192d0f60334SBartosz Golaszewski MAX77650_REG_CNFG_GLBL, 193d0f60334SBartosz Golaszewski MAX77650_SBIA_LPM_MASK, 194d0f60334SBartosz Golaszewski MAX77650_SBIA_LPM_DISABLED); 195d0f60334SBartosz Golaszewski if (rv) { 196d0f60334SBartosz Golaszewski dev_err(dev, "Unable to change the power mode\n"); 197d0f60334SBartosz Golaszewski return rv; 198d0f60334SBartosz Golaszewski } 199d0f60334SBartosz Golaszewski 200d0f60334SBartosz Golaszewski rv = devm_regmap_add_irq_chip(dev, map, i2c->irq, 201d0f60334SBartosz Golaszewski IRQF_ONESHOT | IRQF_SHARED, 0, 202d0f60334SBartosz Golaszewski &max77650_irq_chip, &irq_data); 203d0f60334SBartosz Golaszewski if (rv) { 204d0f60334SBartosz Golaszewski dev_err(dev, "Unable to add Regmap IRQ chip\n"); 205d0f60334SBartosz Golaszewski return rv; 206d0f60334SBartosz Golaszewski } 207d0f60334SBartosz Golaszewski 208d0f60334SBartosz Golaszewski domain = regmap_irq_get_domain(irq_data); 209d0f60334SBartosz Golaszewski 210d0f60334SBartosz Golaszewski return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 211d0f60334SBartosz Golaszewski max77650_cells, ARRAY_SIZE(max77650_cells), 212d0f60334SBartosz Golaszewski NULL, 0, domain); 213d0f60334SBartosz Golaszewski } 214d0f60334SBartosz Golaszewski 215d0f60334SBartosz Golaszewski static const struct of_device_id max77650_of_match[] = { 216d0f60334SBartosz Golaszewski { .compatible = "maxim,max77650" }, 217d0f60334SBartosz Golaszewski { } 218d0f60334SBartosz Golaszewski }; 219d0f60334SBartosz Golaszewski MODULE_DEVICE_TABLE(of, max77650_of_match); 220d0f60334SBartosz Golaszewski 221d0f60334SBartosz Golaszewski static struct i2c_driver max77650_i2c_driver = { 222d0f60334SBartosz Golaszewski .driver = { 223d0f60334SBartosz Golaszewski .name = "max77650", 224*4dfdc9a8SKrzysztof Kozlowski .of_match_table = max77650_of_match, 225d0f60334SBartosz Golaszewski }, 226d0f60334SBartosz Golaszewski .probe_new = max77650_i2c_probe, 227d0f60334SBartosz Golaszewski }; 228d0f60334SBartosz Golaszewski module_i2c_driver(max77650_i2c_driver); 229d0f60334SBartosz Golaszewski 230d0f60334SBartosz Golaszewski MODULE_DESCRIPTION("MAXIM 77650/77651 multi-function core driver"); 231d0f60334SBartosz Golaszewski MODULE_AUTHOR("Bartosz Golaszewski <bgolaszewski@baylibre.com>"); 232d0f60334SBartosz Golaszewski MODULE_LICENSE("GPL v2"); 233