linux/drivers/mfd/mt6397-core.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2014 MediaTek Inc.
   3 * Author: Flora Fu, MediaTek
   4 *
   5 * This program is free software; you can redistribute it and/or modify
   6 * it under the terms of the GNU General Public License version 2 as
   7 * published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope that it will be useful,
  10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12 * GNU General Public License for more details.
  13 */
  14
  15#include <linux/interrupt.h>
  16#include <linux/module.h>
  17#include <linux/of_device.h>
  18#include <linux/of_irq.h>
  19#include <linux/regmap.h>
  20#include <linux/mfd/core.h>
  21#include <linux/mfd/mt6397/core.h>
  22#include <linux/mfd/mt6397/registers.h>
  23
  24#define MT6397_RTC_BASE         0xe000
  25#define MT6397_RTC_SIZE         0x3e
  26
  27static const struct resource mt6397_rtc_resources[] = {
  28        {
  29                .start = MT6397_RTC_BASE,
  30                .end   = MT6397_RTC_BASE + MT6397_RTC_SIZE,
  31                .flags = IORESOURCE_MEM,
  32        },
  33        {
  34                .start = MT6397_IRQ_RTC,
  35                .end   = MT6397_IRQ_RTC,
  36                .flags = IORESOURCE_IRQ,
  37        },
  38};
  39
  40static const struct mfd_cell mt6397_devs[] = {
  41        {
  42                .name = "mt6397-rtc",
  43                .num_resources = ARRAY_SIZE(mt6397_rtc_resources),
  44                .resources = mt6397_rtc_resources,
  45                .of_compatible = "mediatek,mt6397-rtc",
  46        }, {
  47                .name = "mt6397-regulator",
  48                .of_compatible = "mediatek,mt6397-regulator",
  49        }, {
  50                .name = "mt6397-codec",
  51                .of_compatible = "mediatek,mt6397-codec",
  52        }, {
  53                .name = "mt6397-clk",
  54                .of_compatible = "mediatek,mt6397-clk",
  55        }, {
  56                .name = "mt6397-pinctrl",
  57                .of_compatible = "mediatek,mt6397-pinctrl",
  58        },
  59};
  60
  61static void mt6397_irq_lock(struct irq_data *data)
  62{
  63        struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
  64
  65        mutex_lock(&mt6397->irqlock);
  66}
  67
  68static void mt6397_irq_sync_unlock(struct irq_data *data)
  69{
  70        struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
  71
  72        regmap_write(mt6397->regmap, MT6397_INT_CON0, mt6397->irq_masks_cur[0]);
  73        regmap_write(mt6397->regmap, MT6397_INT_CON1, mt6397->irq_masks_cur[1]);
  74
  75        mutex_unlock(&mt6397->irqlock);
  76}
  77
  78static void mt6397_irq_disable(struct irq_data *data)
  79{
  80        struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
  81        int shift = data->hwirq & 0xf;
  82        int reg = data->hwirq >> 4;
  83
  84        mt6397->irq_masks_cur[reg] &= ~BIT(shift);
  85}
  86
  87static void mt6397_irq_enable(struct irq_data *data)
  88{
  89        struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
  90        int shift = data->hwirq & 0xf;
  91        int reg = data->hwirq >> 4;
  92
  93        mt6397->irq_masks_cur[reg] |= BIT(shift);
  94}
  95
  96#ifdef CONFIG_PM_SLEEP
  97static int mt6397_irq_set_wake(struct irq_data *irq_data, unsigned int on)
  98{
  99        struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(irq_data);
 100        int shift = irq_data->hwirq & 0xf;
 101        int reg = irq_data->hwirq >> 4;
 102
 103        if (on)
 104                mt6397->wake_mask[reg] |= BIT(shift);
 105        else
 106                mt6397->wake_mask[reg] &= ~BIT(shift);
 107
 108        return 0;
 109}
 110#else
 111#define mt6397_irq_set_wake NULL
 112#endif
 113
 114static struct irq_chip mt6397_irq_chip = {
 115        .name = "mt6397-irq",
 116        .irq_bus_lock = mt6397_irq_lock,
 117        .irq_bus_sync_unlock = mt6397_irq_sync_unlock,
 118        .irq_enable = mt6397_irq_enable,
 119        .irq_disable = mt6397_irq_disable,
 120        .irq_set_wake = mt6397_irq_set_wake,
 121};
 122
 123static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg,
 124                int irqbase)
 125{
 126        unsigned int status;
 127        int i, irq, ret;
 128
 129        ret = regmap_read(mt6397->regmap, reg, &status);
 130        if (ret) {
 131                dev_err(mt6397->dev, "Failed to read irq status: %d\n", ret);
 132                return;
 133        }
 134
 135        for (i = 0; i < 16; i++) {
 136                if (status & BIT(i)) {
 137                        irq = irq_find_mapping(mt6397->irq_domain, irqbase + i);
 138                        if (irq)
 139                                handle_nested_irq(irq);
 140                }
 141        }
 142
 143        regmap_write(mt6397->regmap, reg, status);
 144}
 145
 146static irqreturn_t mt6397_irq_thread(int irq, void *data)
 147{
 148        struct mt6397_chip *mt6397 = data;
 149
 150        mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS0, 0);
 151        mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS1, 16);
 152
 153        return IRQ_HANDLED;
 154}
 155
 156static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq,
 157                                        irq_hw_number_t hw)
 158{
 159        struct mt6397_chip *mt6397 = d->host_data;
 160
 161        irq_set_chip_data(irq, mt6397);
 162        irq_set_chip_and_handler(irq, &mt6397_irq_chip, handle_level_irq);
 163        irq_set_nested_thread(irq, 1);
 164        irq_set_noprobe(irq);
 165
 166        return 0;
 167}
 168
 169static const struct irq_domain_ops mt6397_irq_domain_ops = {
 170        .map = mt6397_irq_domain_map,
 171};
 172
 173static int mt6397_irq_init(struct mt6397_chip *mt6397)
 174{
 175        int ret;
 176
 177        mutex_init(&mt6397->irqlock);
 178
 179        /* Mask all interrupt sources */
 180        regmap_write(mt6397->regmap, MT6397_INT_CON0, 0x0);
 181        regmap_write(mt6397->regmap, MT6397_INT_CON1, 0x0);
 182
 183        mt6397->irq_domain = irq_domain_add_linear(mt6397->dev->of_node,
 184                MT6397_IRQ_NR, &mt6397_irq_domain_ops, mt6397);
 185        if (!mt6397->irq_domain) {
 186                dev_err(mt6397->dev, "could not create irq domain\n");
 187                return -ENOMEM;
 188        }
 189
 190        ret = devm_request_threaded_irq(mt6397->dev, mt6397->irq, NULL,
 191                mt6397_irq_thread, IRQF_ONESHOT, "mt6397-pmic", mt6397);
 192        if (ret) {
 193                dev_err(mt6397->dev, "failed to register irq=%d; err: %d\n",
 194                        mt6397->irq, ret);
 195                return ret;
 196        }
 197
 198        return 0;
 199}
 200
 201#ifdef CONFIG_PM_SLEEP
 202static int mt6397_irq_suspend(struct device *dev)
 203{
 204        struct mt6397_chip *chip = dev_get_drvdata(dev);
 205
 206        regmap_write(chip->regmap, MT6397_INT_CON0, chip->wake_mask[0]);
 207        regmap_write(chip->regmap, MT6397_INT_CON1, chip->wake_mask[1]);
 208
 209        enable_irq_wake(chip->irq);
 210
 211        return 0;
 212}
 213
 214static int mt6397_irq_resume(struct device *dev)
 215{
 216        struct mt6397_chip *chip = dev_get_drvdata(dev);
 217
 218        regmap_write(chip->regmap, MT6397_INT_CON0, chip->irq_masks_cur[0]);
 219        regmap_write(chip->regmap, MT6397_INT_CON1, chip->irq_masks_cur[1]);
 220
 221        disable_irq_wake(chip->irq);
 222
 223        return 0;
 224}
 225#endif
 226
 227static SIMPLE_DEV_PM_OPS(mt6397_pm_ops, mt6397_irq_suspend,
 228                        mt6397_irq_resume);
 229
 230static int mt6397_probe(struct platform_device *pdev)
 231{
 232        int ret;
 233        struct mt6397_chip *mt6397;
 234
 235        mt6397 = devm_kzalloc(&pdev->dev, sizeof(*mt6397), GFP_KERNEL);
 236        if (!mt6397)
 237                return -ENOMEM;
 238
 239        mt6397->dev = &pdev->dev;
 240        /*
 241         * mt6397 MFD is child device of soc pmic wrapper.
 242         * Regmap is set from its parent.
 243         */
 244        mt6397->regmap = dev_get_regmap(pdev->dev.parent, NULL);
 245        if (!mt6397->regmap)
 246                return -ENODEV;
 247
 248        platform_set_drvdata(pdev, mt6397);
 249
 250        mt6397->irq = platform_get_irq(pdev, 0);
 251        if (mt6397->irq > 0) {
 252                ret = mt6397_irq_init(mt6397);
 253                if (ret)
 254                        return ret;
 255        }
 256
 257        ret = mfd_add_devices(&pdev->dev, -1, mt6397_devs,
 258                        ARRAY_SIZE(mt6397_devs), NULL, 0, NULL);
 259        if (ret)
 260                dev_err(&pdev->dev, "failed to add child devices: %d\n", ret);
 261
 262        return ret;
 263}
 264
 265static int mt6397_remove(struct platform_device *pdev)
 266{
 267        mfd_remove_devices(&pdev->dev);
 268
 269        return 0;
 270}
 271
 272static const struct of_device_id mt6397_of_match[] = {
 273        { .compatible = "mediatek,mt6397" },
 274        { }
 275};
 276MODULE_DEVICE_TABLE(of, mt6397_of_match);
 277
 278static struct platform_driver mt6397_driver = {
 279        .probe = mt6397_probe,
 280        .remove = mt6397_remove,
 281        .driver = {
 282                .name = "mt6397",
 283                .of_match_table = of_match_ptr(mt6397_of_match),
 284                .pm = &mt6397_pm_ops,
 285        },
 286};
 287
 288module_platform_driver(mt6397_driver);
 289
 290MODULE_AUTHOR("Flora Fu, MediaTek");
 291MODULE_DESCRIPTION("Driver for MediaTek MT6397 PMIC");
 292MODULE_LICENSE("GPL");
 293MODULE_ALIAS("platform:mt6397");
 294