linux/drivers/mfd/rn5t618.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * MFD core driver for Ricoh RN5T618 PMIC
   4 *
   5 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
   6 * Copyright (C) 2016 Toradex AG
   7 */
   8
   9#include <linux/delay.h>
  10#include <linux/i2c.h>
  11#include <linux/interrupt.h>
  12#include <linux/irq.h>
  13#include <linux/mfd/core.h>
  14#include <linux/mfd/rn5t618.h>
  15#include <linux/module.h>
  16#include <linux/of_device.h>
  17#include <linux/platform_device.h>
  18#include <linux/reboot.h>
  19#include <linux/regmap.h>
  20
  21static const struct mfd_cell rn5t618_cells[] = {
  22        { .name = "rn5t618-regulator" },
  23        { .name = "rn5t618-wdt" },
  24};
  25
  26static const struct mfd_cell rc5t619_cells[] = {
  27        { .name = "rn5t618-adc" },
  28        { .name = "rn5t618-regulator" },
  29        { .name = "rc5t619-rtc" },
  30        { .name = "rn5t618-wdt" },
  31};
  32
  33static bool rn5t618_volatile_reg(struct device *dev, unsigned int reg)
  34{
  35        switch (reg) {
  36        case RN5T618_WATCHDOGCNT:
  37        case RN5T618_DCIRQ:
  38        case RN5T618_ILIMDATAH ... RN5T618_AIN0DATAL:
  39        case RN5T618_ADCCNT3:
  40        case RN5T618_IR_ADC1 ... RN5T618_IR_ADC3:
  41        case RN5T618_IR_GPR:
  42        case RN5T618_IR_GPF:
  43        case RN5T618_MON_IOIN:
  44        case RN5T618_INTMON:
  45        case RN5T618_RTC_CTRL1 ... RN5T618_RTC_CTRL2:
  46        case RN5T618_RTC_SECONDS ... RN5T618_RTC_YEAR:
  47        case RN5T618_CHGSTATE:
  48        case RN5T618_CHGCTRL_IRR ... RN5T618_CHGERR_MONI:
  49        case RN5T618_CONTROL ... RN5T618_CC_AVEREG0:
  50                return true;
  51        default:
  52                return false;
  53        }
  54}
  55
  56static const struct regmap_config rn5t618_regmap_config = {
  57        .reg_bits       = 8,
  58        .val_bits       = 8,
  59        .volatile_reg   = rn5t618_volatile_reg,
  60        .max_register   = RN5T618_MAX_REG,
  61        .cache_type     = REGCACHE_RBTREE,
  62};
  63
  64static const struct regmap_irq rc5t619_irqs[] = {
  65        REGMAP_IRQ_REG(RN5T618_IRQ_SYS, 0, BIT(0)),
  66        REGMAP_IRQ_REG(RN5T618_IRQ_DCDC, 0, BIT(1)),
  67        REGMAP_IRQ_REG(RN5T618_IRQ_RTC, 0, BIT(2)),
  68        REGMAP_IRQ_REG(RN5T618_IRQ_ADC, 0, BIT(3)),
  69        REGMAP_IRQ_REG(RN5T618_IRQ_GPIO, 0, BIT(4)),
  70        REGMAP_IRQ_REG(RN5T618_IRQ_CHG, 0, BIT(6)),
  71};
  72
  73static const struct regmap_irq_chip rc5t619_irq_chip = {
  74        .name = "rc5t619",
  75        .irqs = rc5t619_irqs,
  76        .num_irqs = ARRAY_SIZE(rc5t619_irqs),
  77        .num_regs = 1,
  78        .status_base = RN5T618_INTMON,
  79        .mask_base = RN5T618_INTEN,
  80        .mask_invert = true,
  81};
  82
  83static struct i2c_client *rn5t618_pm_power_off;
  84static struct notifier_block rn5t618_restart_handler;
  85
  86static int rn5t618_irq_init(struct rn5t618 *rn5t618)
  87{
  88        const struct regmap_irq_chip *irq_chip = NULL;
  89        int ret;
  90
  91        if (!rn5t618->irq)
  92                return 0;
  93
  94        switch (rn5t618->variant) {
  95        case RC5T619:
  96                irq_chip = &rc5t619_irq_chip;
  97                break;
  98        default:
  99                dev_err(rn5t618->dev, "Currently no IRQ support for variant %d\n",
 100                        (int)rn5t618->variant);
 101                return -ENOENT;
 102        }
 103
 104        ret = devm_regmap_add_irq_chip(rn5t618->dev, rn5t618->regmap,
 105                                       rn5t618->irq,
 106                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
 107                                       0, irq_chip, &rn5t618->irq_data);
 108        if (ret)
 109                dev_err(rn5t618->dev, "Failed to register IRQ chip\n");
 110
 111        return ret;
 112}
 113
 114static void rn5t618_trigger_poweroff_sequence(bool repower)
 115{
 116        int ret;
 117
 118        /* disable automatic repower-on */
 119        ret = i2c_smbus_read_byte_data(rn5t618_pm_power_off, RN5T618_REPCNT);
 120        if (ret < 0)
 121                goto err;
 122
 123        ret &= ~RN5T618_REPCNT_REPWRON;
 124        if (repower)
 125                ret |= RN5T618_REPCNT_REPWRON;
 126
 127        ret = i2c_smbus_write_byte_data(rn5t618_pm_power_off,
 128                                        RN5T618_REPCNT, (u8)ret);
 129        if (ret < 0)
 130                goto err;
 131
 132        /* start power-off sequence */
 133        ret = i2c_smbus_read_byte_data(rn5t618_pm_power_off, RN5T618_SLPCNT);
 134        if (ret < 0)
 135                goto err;
 136
 137        ret |= RN5T618_SLPCNT_SWPWROFF;
 138
 139        ret = i2c_smbus_write_byte_data(rn5t618_pm_power_off,
 140                                        RN5T618_SLPCNT, (u8)ret);
 141        if (ret < 0)
 142                goto err;
 143
 144        return;
 145
 146err:
 147        dev_alert(&rn5t618_pm_power_off->dev, "Failed to shutdown (err = %d)\n", ret);
 148}
 149
 150static void rn5t618_power_off(void)
 151{
 152        rn5t618_trigger_poweroff_sequence(false);
 153}
 154
 155static int rn5t618_restart(struct notifier_block *this,
 156                            unsigned long mode, void *cmd)
 157{
 158        rn5t618_trigger_poweroff_sequence(true);
 159
 160        /*
 161         * Re-power factor detection on PMIC side is not instant. 1ms
 162         * proved to be enough time until reset takes effect.
 163         */
 164        mdelay(1);
 165
 166        return NOTIFY_DONE;
 167}
 168
 169static const struct of_device_id rn5t618_of_match[] = {
 170        { .compatible = "ricoh,rn5t567", .data = (void *)RN5T567 },
 171        { .compatible = "ricoh,rn5t618", .data = (void *)RN5T618 },
 172        { .compatible = "ricoh,rc5t619", .data = (void *)RC5T619 },
 173        { }
 174};
 175MODULE_DEVICE_TABLE(of, rn5t618_of_match);
 176
 177static int rn5t618_i2c_probe(struct i2c_client *i2c)
 178{
 179        const struct of_device_id *of_id;
 180        struct rn5t618 *priv;
 181        int ret;
 182
 183        of_id = of_match_device(rn5t618_of_match, &i2c->dev);
 184        if (!of_id) {
 185                dev_err(&i2c->dev, "Failed to find matching DT ID\n");
 186                return -EINVAL;
 187        }
 188
 189        priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
 190        if (!priv)
 191                return -ENOMEM;
 192
 193        i2c_set_clientdata(i2c, priv);
 194        priv->variant = (long)of_id->data;
 195        priv->irq = i2c->irq;
 196        priv->dev = &i2c->dev;
 197
 198        priv->regmap = devm_regmap_init_i2c(i2c, &rn5t618_regmap_config);
 199        if (IS_ERR(priv->regmap)) {
 200                ret = PTR_ERR(priv->regmap);
 201                dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
 202                return ret;
 203        }
 204
 205        if (priv->variant == RC5T619)
 206                ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_NONE,
 207                                           rc5t619_cells,
 208                                           ARRAY_SIZE(rc5t619_cells),
 209                                           NULL, 0, NULL);
 210        else
 211                ret = devm_mfd_add_devices(&i2c->dev, PLATFORM_DEVID_NONE,
 212                                           rn5t618_cells,
 213                                           ARRAY_SIZE(rn5t618_cells),
 214                                           NULL, 0, NULL);
 215        if (ret) {
 216                dev_err(&i2c->dev, "failed to add sub-devices: %d\n", ret);
 217                return ret;
 218        }
 219
 220        rn5t618_pm_power_off = i2c;
 221        if (of_device_is_system_power_controller(i2c->dev.of_node)) {
 222                if (!pm_power_off)
 223                        pm_power_off = rn5t618_power_off;
 224                else
 225                        dev_warn(&i2c->dev, "Poweroff callback already assigned\n");
 226        }
 227
 228        rn5t618_restart_handler.notifier_call = rn5t618_restart;
 229        rn5t618_restart_handler.priority = 192;
 230
 231        ret = register_restart_handler(&rn5t618_restart_handler);
 232        if (ret) {
 233                dev_err(&i2c->dev, "cannot register restart handler, %d\n", ret);
 234                return ret;
 235        }
 236
 237        return rn5t618_irq_init(priv);
 238}
 239
 240static int rn5t618_i2c_remove(struct i2c_client *i2c)
 241{
 242        if (i2c == rn5t618_pm_power_off) {
 243                rn5t618_pm_power_off = NULL;
 244                pm_power_off = NULL;
 245        }
 246
 247        unregister_restart_handler(&rn5t618_restart_handler);
 248
 249        return 0;
 250}
 251
 252static int __maybe_unused rn5t618_i2c_suspend(struct device *dev)
 253{
 254        struct rn5t618 *priv = dev_get_drvdata(dev);
 255
 256        if (priv->irq)
 257                disable_irq(priv->irq);
 258
 259        return 0;
 260}
 261
 262static int __maybe_unused rn5t618_i2c_resume(struct device *dev)
 263{
 264        struct rn5t618 *priv = dev_get_drvdata(dev);
 265
 266        if (priv->irq)
 267                enable_irq(priv->irq);
 268
 269        return 0;
 270}
 271
 272static SIMPLE_DEV_PM_OPS(rn5t618_i2c_dev_pm_ops,
 273                        rn5t618_i2c_suspend,
 274                        rn5t618_i2c_resume);
 275
 276static struct i2c_driver rn5t618_i2c_driver = {
 277        .driver = {
 278                .name = "rn5t618",
 279                .of_match_table = of_match_ptr(rn5t618_of_match),
 280                .pm = &rn5t618_i2c_dev_pm_ops,
 281        },
 282        .probe_new = rn5t618_i2c_probe,
 283        .remove = rn5t618_i2c_remove,
 284};
 285
 286module_i2c_driver(rn5t618_i2c_driver);
 287
 288MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
 289MODULE_DESCRIPTION("Ricoh RN5T567/618 MFD driver");
 290MODULE_LICENSE("GPL v2");
 291