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