162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Intel BXT Whiskey Cove PMIC TMU driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016 Intel Corporation. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This driver adds TMU (Time Management Unit) support for Intel BXT platform. 862306a36Sopenharmony_ci * It enables the alarm wake-up functionality in the TMU unit of Whiskey Cove 962306a36Sopenharmony_ci * PMIC. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/mfd/intel_soc_pmic.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define BXTWC_TMUIRQ 0x4fb6 1962306a36Sopenharmony_ci#define BXTWC_MIRQLVL1 0x4e0e 2062306a36Sopenharmony_ci#define BXTWC_MTMUIRQ_REG 0x4fb7 2162306a36Sopenharmony_ci#define BXTWC_MIRQLVL1_MTMU BIT(1) 2262306a36Sopenharmony_ci#define BXTWC_TMU_WK_ALRM BIT(1) 2362306a36Sopenharmony_ci#define BXTWC_TMU_SYS_ALRM BIT(2) 2462306a36Sopenharmony_ci#define BXTWC_TMU_ALRM_MASK (BXTWC_TMU_WK_ALRM | BXTWC_TMU_SYS_ALRM) 2562306a36Sopenharmony_ci#define BXTWC_TMU_ALRM_IRQ (BXTWC_TMU_WK_ALRM | BXTWC_TMU_SYS_ALRM) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct wcove_tmu { 2862306a36Sopenharmony_ci int irq; 2962306a36Sopenharmony_ci struct device *dev; 3062306a36Sopenharmony_ci struct regmap *regmap; 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic irqreturn_t bxt_wcove_tmu_irq_handler(int irq, void *data) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct wcove_tmu *wctmu = data; 3662306a36Sopenharmony_ci unsigned int tmu_irq; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* Read TMU interrupt reg */ 3962306a36Sopenharmony_ci regmap_read(wctmu->regmap, BXTWC_TMUIRQ, &tmu_irq); 4062306a36Sopenharmony_ci if (tmu_irq & BXTWC_TMU_ALRM_IRQ) { 4162306a36Sopenharmony_ci /* clear TMU irq */ 4262306a36Sopenharmony_ci regmap_write(wctmu->regmap, BXTWC_TMUIRQ, tmu_irq); 4362306a36Sopenharmony_ci return IRQ_HANDLED; 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci return IRQ_NONE; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic int bxt_wcove_tmu_probe(struct platform_device *pdev) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); 5162306a36Sopenharmony_ci struct regmap_irq_chip_data *regmap_irq_chip; 5262306a36Sopenharmony_ci struct wcove_tmu *wctmu; 5362306a36Sopenharmony_ci int ret, virq, irq; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci wctmu = devm_kzalloc(&pdev->dev, sizeof(*wctmu), GFP_KERNEL); 5662306a36Sopenharmony_ci if (!wctmu) 5762306a36Sopenharmony_ci return -ENOMEM; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci wctmu->dev = &pdev->dev; 6062306a36Sopenharmony_ci wctmu->regmap = pmic->regmap; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 6362306a36Sopenharmony_ci if (irq < 0) 6462306a36Sopenharmony_ci return irq; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci regmap_irq_chip = pmic->irq_chip_data_tmu; 6762306a36Sopenharmony_ci virq = regmap_irq_get_virq(regmap_irq_chip, irq); 6862306a36Sopenharmony_ci if (virq < 0) { 6962306a36Sopenharmony_ci dev_err(&pdev->dev, 7062306a36Sopenharmony_ci "failed to get virtual interrupt=%d\n", irq); 7162306a36Sopenharmony_ci return virq; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci ret = devm_request_threaded_irq(&pdev->dev, virq, 7562306a36Sopenharmony_ci NULL, bxt_wcove_tmu_irq_handler, 7662306a36Sopenharmony_ci IRQF_ONESHOT, "bxt_wcove_tmu", wctmu); 7762306a36Sopenharmony_ci if (ret) { 7862306a36Sopenharmony_ci dev_err(&pdev->dev, "request irq failed: %d,virq: %d\n", 7962306a36Sopenharmony_ci ret, virq); 8062306a36Sopenharmony_ci return ret; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci wctmu->irq = virq; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Unmask TMU second level Wake & System alarm */ 8562306a36Sopenharmony_ci regmap_update_bits(wctmu->regmap, BXTWC_MTMUIRQ_REG, 8662306a36Sopenharmony_ci BXTWC_TMU_ALRM_MASK, 0); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci platform_set_drvdata(pdev, wctmu); 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void bxt_wcove_tmu_remove(struct platform_device *pdev) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct wcove_tmu *wctmu = platform_get_drvdata(pdev); 9562306a36Sopenharmony_ci unsigned int val; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* Mask TMU interrupts */ 9862306a36Sopenharmony_ci regmap_read(wctmu->regmap, BXTWC_MIRQLVL1, &val); 9962306a36Sopenharmony_ci regmap_write(wctmu->regmap, BXTWC_MIRQLVL1, 10062306a36Sopenharmony_ci val | BXTWC_MIRQLVL1_MTMU); 10162306a36Sopenharmony_ci regmap_read(wctmu->regmap, BXTWC_MTMUIRQ_REG, &val); 10262306a36Sopenharmony_ci regmap_write(wctmu->regmap, BXTWC_MTMUIRQ_REG, 10362306a36Sopenharmony_ci val | BXTWC_TMU_ALRM_MASK); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 10762306a36Sopenharmony_cistatic int bxtwc_tmu_suspend(struct device *dev) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct wcove_tmu *wctmu = dev_get_drvdata(dev); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci enable_irq_wake(wctmu->irq); 11262306a36Sopenharmony_ci return 0; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic int bxtwc_tmu_resume(struct device *dev) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct wcove_tmu *wctmu = dev_get_drvdata(dev); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci disable_irq_wake(wctmu->irq); 12062306a36Sopenharmony_ci return 0; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci#endif 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(bxtwc_tmu_pm_ops, bxtwc_tmu_suspend, bxtwc_tmu_resume); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic const struct platform_device_id bxt_wcove_tmu_id_table[] = { 12762306a36Sopenharmony_ci { .name = "bxt_wcove_tmu" }, 12862306a36Sopenharmony_ci {}, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, bxt_wcove_tmu_id_table); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic struct platform_driver bxt_wcove_tmu_driver = { 13362306a36Sopenharmony_ci .probe = bxt_wcove_tmu_probe, 13462306a36Sopenharmony_ci .remove_new = bxt_wcove_tmu_remove, 13562306a36Sopenharmony_ci .driver = { 13662306a36Sopenharmony_ci .name = "bxt_wcove_tmu", 13762306a36Sopenharmony_ci .pm = &bxtwc_tmu_pm_ops, 13862306a36Sopenharmony_ci }, 13962306a36Sopenharmony_ci .id_table = bxt_wcove_tmu_id_table, 14062306a36Sopenharmony_ci}; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cimodule_platform_driver(bxt_wcove_tmu_driver); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 14562306a36Sopenharmony_ciMODULE_AUTHOR("Nilesh Bacchewar <nilesh.bacchewar@intel.com>"); 14662306a36Sopenharmony_ciMODULE_DESCRIPTION("BXT Whiskey Cove TMU Driver"); 147