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