linux/drivers/platform/x86/intel_bxtwc_tmu.c
<<
>>
Prefs
   1/*
   2 * intel_bxtwc_tmu.c - Intel BXT Whiskey Cove PMIC TMU driver
   3 *
   4 * Copyright (C) 2016 Intel Corporation. All rights reserved.
   5 *
   6 * This driver adds TMU (Time Management Unit) support for Intel BXT platform.
   7 * It enables the alarm wake-up functionality in the TMU unit of Whiskey Cove
   8 * PMIC.
   9 *
  10 * This program is free software; you can redistribute it and/or
  11 * modify it under the terms of the GNU General Public License version
  12 * 2 as published by the Free Software Foundation.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 *
  19 */
  20
  21#include <linux/module.h>
  22#include <linux/interrupt.h>
  23#include <linux/platform_device.h>
  24#include <linux/mfd/intel_soc_pmic.h>
  25
  26#define BXTWC_TMUIRQ            0x4fb6
  27#define BXTWC_MIRQLVL1          0x4e0e
  28#define BXTWC_MTMUIRQ_REG       0x4fb7
  29#define BXTWC_MIRQLVL1_MTMU     BIT(1)
  30#define BXTWC_TMU_WK_ALRM       BIT(1)
  31#define BXTWC_TMU_SYS_ALRM      BIT(2)
  32#define BXTWC_TMU_ALRM_MASK     (BXTWC_TMU_WK_ALRM | BXTWC_TMU_SYS_ALRM)
  33#define BXTWC_TMU_ALRM_IRQ      (BXTWC_TMU_WK_ALRM | BXTWC_TMU_SYS_ALRM)
  34
  35struct wcove_tmu {
  36        int irq;
  37        struct device *dev;
  38        struct regmap *regmap;
  39};
  40
  41static irqreturn_t bxt_wcove_tmu_irq_handler(int irq, void *data)
  42{
  43        struct wcove_tmu *wctmu = data;
  44        unsigned int tmu_irq;
  45
  46        /* Read TMU interrupt reg */
  47        regmap_read(wctmu->regmap, BXTWC_TMUIRQ, &tmu_irq);
  48        if (tmu_irq & BXTWC_TMU_ALRM_IRQ) {
  49                /* clear TMU irq */
  50                regmap_write(wctmu->regmap, BXTWC_TMUIRQ, tmu_irq);
  51                return IRQ_HANDLED;
  52        }
  53        return IRQ_NONE;
  54}
  55
  56static int bxt_wcove_tmu_probe(struct platform_device *pdev)
  57{
  58        struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
  59        struct regmap_irq_chip_data *regmap_irq_chip;
  60        struct wcove_tmu *wctmu;
  61        int ret, virq, irq;
  62
  63        wctmu = devm_kzalloc(&pdev->dev, sizeof(*wctmu), GFP_KERNEL);
  64        if (!wctmu)
  65                return -ENOMEM;
  66
  67        wctmu->dev = &pdev->dev;
  68        wctmu->regmap = pmic->regmap;
  69
  70        irq = platform_get_irq(pdev, 0);
  71
  72        if (irq < 0) {
  73                dev_err(&pdev->dev, "invalid irq %d\n", irq);
  74                return irq;
  75        }
  76
  77        regmap_irq_chip = pmic->irq_chip_data_tmu;
  78        virq = regmap_irq_get_virq(regmap_irq_chip, irq);
  79        if (virq < 0) {
  80                dev_err(&pdev->dev,
  81                        "failed to get virtual interrupt=%d\n", irq);
  82                return virq;
  83        }
  84
  85        ret = devm_request_threaded_irq(&pdev->dev, virq,
  86                                        NULL, bxt_wcove_tmu_irq_handler,
  87                                        IRQF_ONESHOT, "bxt_wcove_tmu", wctmu);
  88        if (ret) {
  89                dev_err(&pdev->dev, "request irq failed: %d,virq: %d\n",
  90                                                        ret, virq);
  91                return ret;
  92        }
  93        wctmu->irq = virq;
  94
  95        /* Enable TMU interrupts */
  96        regmap_update_bits(wctmu->regmap, BXTWC_MIRQLVL1,
  97                                  BXTWC_MIRQLVL1_MTMU, 0);
  98
  99        /* Unmask TMU second level Wake & System alarm */
 100        regmap_update_bits(wctmu->regmap, BXTWC_MTMUIRQ_REG,
 101                                  BXTWC_TMU_ALRM_MASK, 0);
 102
 103        platform_set_drvdata(pdev, wctmu);
 104        return 0;
 105}
 106
 107static int bxt_wcove_tmu_remove(struct platform_device *pdev)
 108{
 109        struct wcove_tmu *wctmu = platform_get_drvdata(pdev);
 110        unsigned int val;
 111
 112        /* Mask TMU interrupts */
 113        regmap_read(wctmu->regmap, BXTWC_MIRQLVL1, &val);
 114        regmap_write(wctmu->regmap, BXTWC_MIRQLVL1,
 115                        val | BXTWC_MIRQLVL1_MTMU);
 116        regmap_read(wctmu->regmap, BXTWC_MTMUIRQ_REG, &val);
 117        regmap_write(wctmu->regmap, BXTWC_MTMUIRQ_REG,
 118                        val | BXTWC_TMU_ALRM_MASK);
 119        return 0;
 120}
 121
 122#ifdef CONFIG_PM_SLEEP
 123static int bxtwc_tmu_suspend(struct device *dev)
 124{
 125        struct wcove_tmu *wctmu = dev_get_drvdata(dev);
 126
 127        enable_irq_wake(wctmu->irq);
 128        return 0;
 129}
 130
 131static int bxtwc_tmu_resume(struct device *dev)
 132{
 133        struct wcove_tmu *wctmu = dev_get_drvdata(dev);
 134
 135        disable_irq_wake(wctmu->irq);
 136        return 0;
 137}
 138#endif
 139
 140static SIMPLE_DEV_PM_OPS(bxtwc_tmu_pm_ops, bxtwc_tmu_suspend, bxtwc_tmu_resume);
 141
 142static const struct platform_device_id bxt_wcove_tmu_id_table[] = {
 143        { .name = "bxt_wcove_tmu" },
 144        {},
 145};
 146MODULE_DEVICE_TABLE(platform, bxt_wcove_tmu_id_table);
 147
 148static struct platform_driver bxt_wcove_tmu_driver = {
 149        .probe = bxt_wcove_tmu_probe,
 150        .remove = bxt_wcove_tmu_remove,
 151        .driver = {
 152                .name = "bxt_wcove_tmu",
 153                .pm     = &bxtwc_tmu_pm_ops,
 154        },
 155        .id_table = bxt_wcove_tmu_id_table,
 156};
 157
 158module_platform_driver(bxt_wcove_tmu_driver);
 159
 160MODULE_LICENSE("GPL v2");
 161MODULE_AUTHOR("Nilesh Bacchewar <nilesh.bacchewar@intel.com>");
 162MODULE_DESCRIPTION("BXT Whiskey Cove TMU Driver");
 163