1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Intel BXT Whiskey Cove PMIC TMU driver 4 * 5 * Copyright (C) 2016 Intel Corporation. All rights reserved. 6 * 7 * This driver adds TMU (Time Management Unit) support for Intel BXT platform. 8 * It enables the alarm wake-up functionality in the TMU unit of Whiskey Cove 9 * PMIC. 10 */ 11 12 #include <linux/module.h> 13 #include <linux/mod_devicetable.h> 14 #include <linux/interrupt.h> 15 #include <linux/platform_device.h> 16 #include <linux/mfd/intel_soc_pmic.h> 17 18 #define BXTWC_TMUIRQ 0x4fb6 19 #define BXTWC_MIRQLVL1 0x4e0e 20 #define BXTWC_MTMUIRQ_REG 0x4fb7 21 #define BXTWC_MIRQLVL1_MTMU BIT(1) 22 #define BXTWC_TMU_WK_ALRM BIT(1) 23 #define BXTWC_TMU_SYS_ALRM BIT(2) 24 #define BXTWC_TMU_ALRM_MASK (BXTWC_TMU_WK_ALRM | BXTWC_TMU_SYS_ALRM) 25 #define BXTWC_TMU_ALRM_IRQ (BXTWC_TMU_WK_ALRM | BXTWC_TMU_SYS_ALRM) 26 27 struct wcove_tmu { 28 int irq; 29 struct device *dev; 30 struct regmap *regmap; 31 }; 32 33 static irqreturn_t bxt_wcove_tmu_irq_handler(int irq, void *data) 34 { 35 struct wcove_tmu *wctmu = data; 36 unsigned int tmu_irq; 37 38 /* Read TMU interrupt reg */ 39 regmap_read(wctmu->regmap, BXTWC_TMUIRQ, &tmu_irq); 40 if (tmu_irq & BXTWC_TMU_ALRM_IRQ) { 41 /* clear TMU irq */ 42 regmap_write(wctmu->regmap, BXTWC_TMUIRQ, tmu_irq); 43 return IRQ_HANDLED; 44 } 45 return IRQ_NONE; 46 } 47 48 static int bxt_wcove_tmu_probe(struct platform_device *pdev) 49 { 50 struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); 51 struct wcove_tmu *wctmu; 52 int ret; 53 54 wctmu = devm_kzalloc(&pdev->dev, sizeof(*wctmu), GFP_KERNEL); 55 if (!wctmu) 56 return -ENOMEM; 57 58 wctmu->dev = &pdev->dev; 59 wctmu->regmap = pmic->regmap; 60 61 wctmu->irq = platform_get_irq(pdev, 0); 62 if (wctmu->irq < 0) 63 return wctmu->irq; 64 65 ret = devm_request_threaded_irq(&pdev->dev, wctmu->irq, 66 NULL, bxt_wcove_tmu_irq_handler, 67 IRQF_ONESHOT, "bxt_wcove_tmu", wctmu); 68 if (ret) { 69 dev_err(&pdev->dev, "request irq failed: %d,virq: %d\n", 70 ret, wctmu->irq); 71 return ret; 72 } 73 74 /* Unmask TMU second level Wake & System alarm */ 75 regmap_update_bits(wctmu->regmap, BXTWC_MTMUIRQ_REG, 76 BXTWC_TMU_ALRM_MASK, 0); 77 78 platform_set_drvdata(pdev, wctmu); 79 return 0; 80 } 81 82 static void bxt_wcove_tmu_remove(struct platform_device *pdev) 83 { 84 struct wcove_tmu *wctmu = platform_get_drvdata(pdev); 85 unsigned int val; 86 87 /* Mask TMU interrupts */ 88 regmap_read(wctmu->regmap, BXTWC_MIRQLVL1, &val); 89 regmap_write(wctmu->regmap, BXTWC_MIRQLVL1, 90 val | BXTWC_MIRQLVL1_MTMU); 91 regmap_read(wctmu->regmap, BXTWC_MTMUIRQ_REG, &val); 92 regmap_write(wctmu->regmap, BXTWC_MTMUIRQ_REG, 93 val | BXTWC_TMU_ALRM_MASK); 94 } 95 96 #ifdef CONFIG_PM_SLEEP 97 static int bxtwc_tmu_suspend(struct device *dev) 98 { 99 struct wcove_tmu *wctmu = dev_get_drvdata(dev); 100 101 enable_irq_wake(wctmu->irq); 102 return 0; 103 } 104 105 static int bxtwc_tmu_resume(struct device *dev) 106 { 107 struct wcove_tmu *wctmu = dev_get_drvdata(dev); 108 109 disable_irq_wake(wctmu->irq); 110 return 0; 111 } 112 #endif 113 114 static SIMPLE_DEV_PM_OPS(bxtwc_tmu_pm_ops, bxtwc_tmu_suspend, bxtwc_tmu_resume); 115 116 static const struct platform_device_id bxt_wcove_tmu_id_table[] = { 117 { .name = "bxt_wcove_tmu" }, 118 {}, 119 }; 120 MODULE_DEVICE_TABLE(platform, bxt_wcove_tmu_id_table); 121 122 static struct platform_driver bxt_wcove_tmu_driver = { 123 .probe = bxt_wcove_tmu_probe, 124 .remove = bxt_wcove_tmu_remove, 125 .driver = { 126 .name = "bxt_wcove_tmu", 127 .pm = &bxtwc_tmu_pm_ops, 128 }, 129 .id_table = bxt_wcove_tmu_id_table, 130 }; 131 132 module_platform_driver(bxt_wcove_tmu_driver); 133 134 MODULE_LICENSE("GPL v2"); 135 MODULE_AUTHOR("Nilesh Bacchewar <nilesh.bacchewar@intel.com>"); 136 MODULE_DESCRIPTION("BXT Whiskey Cove TMU Driver"); 137