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/mt6323/core.h>
  23#include <linux/mfd/mt6397/registers.h>
  24#include <linux/mfd/mt6323/registers.h>
  25
  26#define MT6397_RTC_BASE         0xe000
  27#define MT6397_RTC_SIZE         0x3e
  28
  29#define MT6323_CID_CODE         0x23
  30#define MT6391_CID_CODE         0x91
  31#define MT6397_CID_CODE         0x97
  32
  33static const struct resource mt6397_rtc_resources[] = {
  34        {
  35                .start = MT6397_RTC_BASE,
  36                .end   = MT6397_RTC_BASE + MT6397_RTC_SIZE,
  37                .flags = IORESOURCE_MEM,
  38        },
  39        {
  40                .start = MT6397_IRQ_RTC,
  41                .end   = MT6397_IRQ_RTC,
  42                .flags = IORESOURCE_IRQ,
  43        },
  44};
  45
  46static const struct mfd_cell mt6323_devs[] = {
  47        {
  48                .name = "mt6323-regulator",
  49                .of_compatible = "mediatek,mt6323-regulator"
  50        }, {
  51                .name = "mt6323-led",
  52                .of_compatible = "mediatek,mt6323-led"
  53        },
  54};
  55
  56static const struct mfd_cell mt6397_devs[] = {
  57        {
  58                .name = "mt6397-rtc",
  59                .num_resources = ARRAY_SIZE(mt6397_rtc_resources),
  60                .resources = mt6397_rtc_resources,
  61                .of_compatible = "mediatek,mt6397-rtc",
  62        }, {
  63                .name = "mt6397-regulator",
  64                .of_compatible = "mediatek,mt6397-regulator",
  65        }, {
  66                .name = "mt6397-codec",
  67                .of_compatible = "mediatek,mt6397-codec",
  68        }, {
  69                .name = "mt6397-clk",
  70                .of_compatible = "mediatek,mt6397-clk",
  71        }, {
  72                .name = "mt6397-pinctrl",
  73                .of_compatible = "mediatek,mt6397-pinctrl",
  74        },
  75};
  76
  77static void mt6397_irq_lock(struct irq_data *data)
  78{
  79        struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
  80
  81        mutex_lock(&mt6397->irqlock);
  82}
  83
  84static void mt6397_irq_sync_unlock(struct irq_data *data)
  85{
  86        struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
  87
  88        regmap_write(mt6397->regmap, mt6397->int_con[0],
  89                     mt6397->irq_masks_cur[0]);
  90        regmap_write(mt6397->regmap, mt6397->int_con[1],
  91                     mt6397->irq_masks_cur[1]);
  92
  93        mutex_unlock(&mt6397->irqlock);
  94}
  95
  96static void mt6397_irq_disable(struct irq_data *data)
  97{
  98        struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
  99        int shift = data->hwirq & 0xf;
 100        int reg = data->hwirq >> 4;
 101
 102        mt6397->irq_masks_cur[reg] &= ~BIT(shift);
 103}
 104
 105static void mt6397_irq_enable(struct irq_data *data)
 106{
 107        struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
 108        int shift = data->hwirq & 0xf;
 109        int reg = data->hwirq >> 4;
 110
 111        mt6397->irq_masks_cur[reg] |= BIT(shift);
 112}
 113
 114#ifdef CONFIG_PM_SLEEP
 115static int mt6397_irq_set_wake(struct irq_data *irq_data, unsigned int on)
 116{
 117        struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(irq_data);
 118        int shift = irq_data->hwirq & 0xf;
 119        int reg = irq_data->hwirq >> 4;
 120
 121        if (on)
 122                mt6397->wake_mask[reg] |= BIT(shift);
 123        else
 124                mt6397->wake_mask[reg] &= ~BIT(shift);
 125
 126        return 0;
 127}
 128#else
 129#define mt6397_irq_set_wake NULL
 130#endif
 131
 132static struct irq_chip mt6397_irq_chip = {
 133        .name = "mt6397-irq",
 134        .irq_bus_lock = mt6397_irq_lock,
 135        .irq_bus_sync_unlock = mt6397_irq_sync_unlock,
 136        .irq_enable = mt6397_irq_enable,
 137        .irq_disable = mt6397_irq_disable,
 138        .irq_set_wake = mt6397_irq_set_wake,
 139};
 140
 141static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg,
 142                int irqbase)
 143{
 144        unsigned int status;
 145        int i, irq, ret;
 146
 147        ret = regmap_read(mt6397->regmap, reg, &status);
 148        if (ret) {
 149                dev_err(mt6397->dev, "Failed to read irq status: %d\n", ret);
 150                return;
 151        }
 152
 153        for (i = 0; i < 16; i++) {
 154                if (status & BIT(i)) {
 155                        irq = irq_find_mapping(mt6397->irq_domain, irqbase + i);
 156                        if (irq)
 157                                handle_nested_irq(irq);
 158                }
 159        }
 160
 161        regmap_write(mt6397->regmap, reg, status);
 162}
 163
 164static irqreturn_t mt6397_irq_thread(int irq, void *data)
 165{
 166        struct mt6397_chip *mt6397 = data;
 167
 168        mt6397_irq_handle_reg(mt6397, mt6397->int_status[0], 0);
 169        mt6397_irq_handle_reg(mt6397, mt6397->int_status[1], 16);
 170
 171        return IRQ_HANDLED;
 172}
 173
 174static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq,
 175                                        irq_hw_number_t hw)
 176{
 177        struct mt6397_chip *mt6397 = d->host_data;
 178
 179        irq_set_chip_data(irq, mt6397);
 180        irq_set_chip_and_handler(irq, &mt6397_irq_chip, handle_level_irq);
 181        irq_set_nested_thread(irq, 1);
 182        irq_set_noprobe(irq);
 183
 184        return 0;
 185}
 186
 187static const struct irq_domain_ops mt6397_irq_domain_ops = {
 188        .map = mt6397_irq_domain_map,
 189};
 190
 191static int mt6397_irq_init(struct mt6397_chip *mt6397)
 192{
 193        int ret;
 194
 195        mutex_init(&mt6397->irqlock);
 196
 197        /* Mask all interrupt sources */
 198        regmap_write(mt6397->regmap, mt6397->int_con[0], 0x0);
 199        regmap_write(mt6397->regmap, mt6397->int_con[1], 0x0);
 200
 201        mt6397->irq_domain = irq_domain_add_linear(mt6397->dev->of_node,
 202                MT6397_IRQ_NR, &mt6397_irq_domain_ops, mt6397);
 203        if (!mt6397->irq_domain) {
 204                dev_err(mt6397->dev, "could not create irq domain\n");
 205                return -ENOMEM;
 206        }
 207
 208        ret = devm_request_threaded_irq(mt6397->dev, mt6397->irq, NULL,
 209                mt6397_irq_thread, IRQF_ONESHOT, "mt6397-pmic", mt6397);
 210        if (ret) {
 211                dev_err(mt6397->dev, "failed to register irq=%d; err: %d\n",
 212                        mt6397->irq, ret);
 213                return ret;
 214        }
 215
 216        return 0;
 217}
 218
 219#ifdef CONFIG_PM_SLEEP
 220static int mt6397_irq_suspend(struct device *dev)
 221{
 222        struct mt6397_chip *chip = dev_get_drvdata(dev);
 223
 224        regmap_write(chip->regmap, chip->int_con[0], chip->wake_mask[0]);
 225        regmap_write(chip->regmap, chip->int_con[1], chip->wake_mask[1]);
 226
 227        enable_irq_wake(chip->irq);
 228
 229        return 0;
 230}
 231
 232static int mt6397_irq_resume(struct device *dev)
 233{
 234        struct mt6397_chip *chip = dev_get_drvdata(dev);
 235
 236        regmap_write(chip->regmap, chip->int_con[0], chip->irq_masks_cur[0]);
 237        regmap_write(chip->regmap, chip->int_con[1], chip->irq_masks_cur[1]);
 238
 239        disable_irq_wake(chip->irq);
 240
 241        return 0;
 242}
 243#endif
 244
 245static SIMPLE_DEV_PM_OPS(mt6397_pm_ops, mt6397_irq_suspend,
 246                        mt6397_irq_resume);
 247
 248static int mt6397_probe(struct platform_device *pdev)
 249{
 250        int ret;
 251        unsigned int id;
 252        struct mt6397_chip *pmic;
 253
 254        pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
 255        if (!pmic)
 256                return -ENOMEM;
 257
 258        pmic->dev = &pdev->dev;
 259
 260        /*
 261         * mt6397 MFD is child device of soc pmic wrapper.
 262         * Regmap is set from its parent.
 263         */
 264        pmic->regmap = dev_get_regmap(pdev->dev.parent, NULL);
 265        if (!pmic->regmap)
 266                return -ENODEV;
 267
 268        platform_set_drvdata(pdev, pmic);
 269
 270        ret = regmap_read(pmic->regmap, MT6397_CID, &id);
 271        if (ret) {
 272                dev_err(pmic->dev, "Failed to read chip id: %d\n", ret);
 273                return ret;
 274        }
 275
 276        pmic->irq = platform_get_irq(pdev, 0);
 277        if (pmic->irq <= 0)
 278                return pmic->irq;
 279
 280        switch (id & 0xff) {
 281        case MT6323_CID_CODE:
 282                pmic->int_con[0] = MT6323_INT_CON0;
 283                pmic->int_con[1] = MT6323_INT_CON1;
 284                pmic->int_status[0] = MT6323_INT_STATUS0;
 285                pmic->int_status[1] = MT6323_INT_STATUS1;
 286                ret = mt6397_irq_init(pmic);
 287                if (ret)
 288                        return ret;
 289
 290                ret = devm_mfd_add_devices(&pdev->dev, -1, mt6323_devs,
 291                                           ARRAY_SIZE(mt6323_devs), NULL,
 292                                           0, NULL);
 293                break;
 294
 295        case MT6397_CID_CODE:
 296        case MT6391_CID_CODE:
 297                pmic->int_con[0] = MT6397_INT_CON0;
 298                pmic->int_con[1] = MT6397_INT_CON1;
 299                pmic->int_status[0] = MT6397_INT_STATUS0;
 300                pmic->int_status[1] = MT6397_INT_STATUS1;
 301                ret = mt6397_irq_init(pmic);
 302                if (ret)
 303                        return ret;
 304
 305                ret = devm_mfd_add_devices(&pdev->dev, -1, mt6397_devs,
 306                                           ARRAY_SIZE(mt6397_devs), NULL,
 307                                           0, NULL);
 308                break;
 309
 310        default:
 311                dev_err(&pdev->dev, "unsupported chip: %d\n", id);
 312                ret = -ENODEV;
 313                break;
 314        }
 315
 316        if (ret) {
 317                irq_domain_remove(pmic->irq_domain);
 318                dev_err(&pdev->dev, "failed to add child devices: %d\n", ret);
 319        }
 320
 321        return ret;
 322}
 323
 324static const struct of_device_id mt6397_of_match[] = {
 325        { .compatible = "mediatek,mt6397" },
 326        { .compatible = "mediatek,mt6323" },
 327        { }
 328};
 329MODULE_DEVICE_TABLE(of, mt6397_of_match);
 330
 331static const struct platform_device_id mt6397_id[] = {
 332        { "mt6397", 0 },
 333        { },
 334};
 335MODULE_DEVICE_TABLE(platform, mt6397_id);
 336
 337static struct platform_driver mt6397_driver = {
 338        .probe = mt6397_probe,
 339        .driver = {
 340                .name = "mt6397",
 341                .of_match_table = of_match_ptr(mt6397_of_match),
 342                .pm = &mt6397_pm_ops,
 343        },
 344        .id_table = mt6397_id,
 345};
 346
 347module_platform_driver(mt6397_driver);
 348
 349MODULE_AUTHOR("Flora Fu, MediaTek");
 350MODULE_DESCRIPTION("Driver for MediaTek MT6397 PMIC");
 351MODULE_LICENSE("GPL");
 352