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/mt6397/registers.h> 23 24 static const struct mfd_cell mt6397_devs[] = { 25 { 26 .name = "mt6397-rtc", 27 .of_compatible = "mediatek,mt6397-rtc", 28 }, { 29 .name = "mt6397-regulator", 30 .of_compatible = "mediatek,mt6397-regulator", 31 }, { 32 .name = "mt6397-codec", 33 .of_compatible = "mediatek,mt6397-codec", 34 }, { 35 .name = "mt6397-clk", 36 .of_compatible = "mediatek,mt6397-clk", 37 }, { 38 .name = "mt6397-pinctrl", 39 .of_compatible = "mediatek,mt6397-pinctrl", 40 }, 41 }; 42 43 static void mt6397_irq_lock(struct irq_data *data) 44 { 45 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); 46 47 mutex_lock(&mt6397->irqlock); 48 } 49 50 static void mt6397_irq_sync_unlock(struct irq_data *data) 51 { 52 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); 53 54 regmap_write(mt6397->regmap, MT6397_INT_CON0, mt6397->irq_masks_cur[0]); 55 regmap_write(mt6397->regmap, MT6397_INT_CON1, mt6397->irq_masks_cur[1]); 56 57 mutex_unlock(&mt6397->irqlock); 58 } 59 60 static void mt6397_irq_disable(struct irq_data *data) 61 { 62 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); 63 int shift = data->hwirq & 0xf; 64 int reg = data->hwirq >> 4; 65 66 mt6397->irq_masks_cur[reg] &= ~BIT(shift); 67 } 68 69 static void mt6397_irq_enable(struct irq_data *data) 70 { 71 struct mt6397_chip *mt6397 = irq_get_chip_data(data->irq); 72 int shift = data->hwirq & 0xf; 73 int reg = data->hwirq >> 4; 74 75 mt6397->irq_masks_cur[reg] |= BIT(shift); 76 } 77 78 static struct irq_chip mt6397_irq_chip = { 79 .name = "mt6397-irq", 80 .irq_bus_lock = mt6397_irq_lock, 81 .irq_bus_sync_unlock = mt6397_irq_sync_unlock, 82 .irq_enable = mt6397_irq_enable, 83 .irq_disable = mt6397_irq_disable, 84 }; 85 86 static void mt6397_irq_handle_reg(struct mt6397_chip *mt6397, int reg, 87 int irqbase) 88 { 89 unsigned int status; 90 int i, irq, ret; 91 92 ret = regmap_read(mt6397->regmap, reg, &status); 93 if (ret) { 94 dev_err(mt6397->dev, "Failed to read irq status: %d\n", ret); 95 return; 96 } 97 98 for (i = 0; i < 16; i++) { 99 if (status & BIT(i)) { 100 irq = irq_find_mapping(mt6397->irq_domain, irqbase + i); 101 if (irq) 102 handle_nested_irq(irq); 103 } 104 } 105 106 regmap_write(mt6397->regmap, reg, status); 107 } 108 109 static irqreturn_t mt6397_irq_thread(int irq, void *data) 110 { 111 struct mt6397_chip *mt6397 = data; 112 113 mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS0, 0); 114 mt6397_irq_handle_reg(mt6397, MT6397_INT_STATUS1, 16); 115 116 return IRQ_HANDLED; 117 } 118 119 static int mt6397_irq_domain_map(struct irq_domain *d, unsigned int irq, 120 irq_hw_number_t hw) 121 { 122 struct mt6397_chip *mt6397 = d->host_data; 123 124 irq_set_chip_data(irq, mt6397); 125 irq_set_chip_and_handler(irq, &mt6397_irq_chip, handle_level_irq); 126 irq_set_nested_thread(irq, 1); 127 #ifdef CONFIG_ARM 128 set_irq_flags(irq, IRQF_VALID); 129 #else 130 irq_set_noprobe(irq); 131 #endif 132 133 return 0; 134 } 135 136 static const struct irq_domain_ops mt6397_irq_domain_ops = { 137 .map = mt6397_irq_domain_map, 138 }; 139 140 static int mt6397_irq_init(struct mt6397_chip *mt6397) 141 { 142 int ret; 143 144 mutex_init(&mt6397->irqlock); 145 146 /* Mask all interrupt sources */ 147 regmap_write(mt6397->regmap, MT6397_INT_CON0, 0x0); 148 regmap_write(mt6397->regmap, MT6397_INT_CON1, 0x0); 149 150 mt6397->irq_domain = irq_domain_add_linear(mt6397->dev->of_node, 151 MT6397_IRQ_NR, &mt6397_irq_domain_ops, mt6397); 152 if (!mt6397->irq_domain) { 153 dev_err(mt6397->dev, "could not create irq domain\n"); 154 return -ENOMEM; 155 } 156 157 ret = devm_request_threaded_irq(mt6397->dev, mt6397->irq, NULL, 158 mt6397_irq_thread, IRQF_ONESHOT, "mt6397-pmic", mt6397); 159 if (ret) { 160 dev_err(mt6397->dev, "failed to register irq=%d; err: %d\n", 161 mt6397->irq, ret); 162 return ret; 163 } 164 165 return 0; 166 } 167 168 static int mt6397_probe(struct platform_device *pdev) 169 { 170 int ret; 171 struct mt6397_chip *mt6397; 172 173 mt6397 = devm_kzalloc(&pdev->dev, sizeof(*mt6397), GFP_KERNEL); 174 if (!mt6397) 175 return -ENOMEM; 176 177 mt6397->dev = &pdev->dev; 178 /* 179 * mt6397 MFD is child device of soc pmic wrapper. 180 * Regmap is set from its parent. 181 */ 182 mt6397->regmap = dev_get_regmap(pdev->dev.parent, NULL); 183 if (!mt6397->regmap) 184 return -ENODEV; 185 186 platform_set_drvdata(pdev, mt6397); 187 188 mt6397->irq = platform_get_irq(pdev, 0); 189 if (mt6397->irq > 0) { 190 ret = mt6397_irq_init(mt6397); 191 if (ret) 192 return ret; 193 } 194 195 ret = mfd_add_devices(&pdev->dev, -1, mt6397_devs, 196 ARRAY_SIZE(mt6397_devs), NULL, 0, NULL); 197 if (ret) 198 dev_err(&pdev->dev, "failed to add child devices: %d\n", ret); 199 200 return ret; 201 } 202 203 static int mt6397_remove(struct platform_device *pdev) 204 { 205 mfd_remove_devices(&pdev->dev); 206 207 return 0; 208 } 209 210 static const struct of_device_id mt6397_of_match[] = { 211 { .compatible = "mediatek,mt6397" }, 212 { } 213 }; 214 MODULE_DEVICE_TABLE(of, mt6397_of_match); 215 216 static struct platform_driver mt6397_driver = { 217 .probe = mt6397_probe, 218 .remove = mt6397_remove, 219 .driver = { 220 .name = "mt6397", 221 .of_match_table = of_match_ptr(mt6397_of_match), 222 }, 223 }; 224 225 module_platform_driver(mt6397_driver); 226 227 MODULE_AUTHOR("Flora Fu, MediaTek"); 228 MODULE_DESCRIPTION("Driver for MediaTek MT6397 PMIC"); 229 MODULE_LICENSE("GPL"); 230 MODULE_ALIAS("platform:mt6397"); 231