Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.18-rc2 282 lines 7.4 kB view raw
1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright 2024 NXP 4 */ 5 6#include <linux/clk.h> 7#include <linux/interrupt.h> 8#include <linux/irq.h> 9#include <linux/irqchip/chained_irq.h> 10#include <linux/irqdomain.h> 11#include <linux/of.h> 12#include <linux/of_irq.h> 13#include <linux/platform_device.h> 14#include <linux/pm_runtime.h> 15#include <linux/regmap.h> 16 17#define USERINTERRUPTMASK(n) (0x8 + 4 * (n)) 18#define INTERRUPTENABLE(n) (0x10 + 4 * (n)) 19#define INTERRUPTPRESET(n) (0x18 + 4 * (n)) 20#define INTERRUPTCLEAR(n) (0x20 + 4 * (n)) 21#define INTERRUPTSTATUS(n) (0x28 + 4 * (n)) 22#define USERINTERRUPTENABLE(n) (0x40 + 4 * (n)) 23#define USERINTERRUPTPRESET(n) (0x48 + 4 * (n)) 24#define USERINTERRUPTCLEAR(n) (0x50 + 4 * (n)) 25#define USERINTERRUPTSTATUS(n) (0x58 + 4 * (n)) 26 27#define IRQ_COUNT 49 28#define IRQ_RESERVED 35 29#define REG_NUM 2 30 31struct dc_ic_data { 32 struct regmap *regs; 33 struct clk *clk_axi; 34 int irq[IRQ_COUNT]; 35 struct irq_domain *domain; 36}; 37 38struct dc_ic_entry { 39 struct dc_ic_data *data; 40 int irq; 41}; 42 43static const struct regmap_range dc_ic_regmap_write_ranges[] = { 44 regmap_reg_range(USERINTERRUPTMASK(0), INTERRUPTCLEAR(1)), 45 regmap_reg_range(USERINTERRUPTENABLE(0), USERINTERRUPTCLEAR(1)), 46}; 47 48static const struct regmap_access_table dc_ic_regmap_write_table = { 49 .yes_ranges = dc_ic_regmap_write_ranges, 50 .n_yes_ranges = ARRAY_SIZE(dc_ic_regmap_write_ranges), 51}; 52 53static const struct regmap_range dc_ic_regmap_read_ranges[] = { 54 regmap_reg_range(USERINTERRUPTMASK(0), INTERRUPTENABLE(1)), 55 regmap_reg_range(INTERRUPTSTATUS(0), INTERRUPTSTATUS(1)), 56 regmap_reg_range(USERINTERRUPTENABLE(0), USERINTERRUPTENABLE(1)), 57 regmap_reg_range(USERINTERRUPTSTATUS(0), USERINTERRUPTSTATUS(1)), 58}; 59 60static const struct regmap_access_table dc_ic_regmap_read_table = { 61 .yes_ranges = dc_ic_regmap_read_ranges, 62 .n_yes_ranges = ARRAY_SIZE(dc_ic_regmap_read_ranges), 63}; 64 65static const struct regmap_range dc_ic_regmap_volatile_ranges[] = { 66 regmap_reg_range(INTERRUPTPRESET(0), INTERRUPTCLEAR(1)), 67 regmap_reg_range(USERINTERRUPTPRESET(0), USERINTERRUPTCLEAR(1)), 68}; 69 70static const struct regmap_access_table dc_ic_regmap_volatile_table = { 71 .yes_ranges = dc_ic_regmap_volatile_ranges, 72 .n_yes_ranges = ARRAY_SIZE(dc_ic_regmap_volatile_ranges), 73}; 74 75static const struct regmap_config dc_ic_regmap_config = { 76 .reg_bits = 32, 77 .reg_stride = 4, 78 .val_bits = 32, 79 .fast_io = true, 80 .wr_table = &dc_ic_regmap_write_table, 81 .rd_table = &dc_ic_regmap_read_table, 82 .volatile_table = &dc_ic_regmap_volatile_table, 83 .max_register = USERINTERRUPTSTATUS(1), 84}; 85 86static void dc_ic_irq_handler(struct irq_desc *desc) 87{ 88 struct dc_ic_entry *entry = irq_desc_get_handler_data(desc); 89 struct dc_ic_data *data = entry->data; 90 unsigned int status, enable; 91 unsigned int virq; 92 93 chained_irq_enter(irq_desc_get_chip(desc), desc); 94 95 regmap_read(data->regs, USERINTERRUPTSTATUS(entry->irq / 32), &status); 96 regmap_read(data->regs, USERINTERRUPTENABLE(entry->irq / 32), &enable); 97 98 status &= enable; 99 100 if (status & BIT(entry->irq % 32)) { 101 virq = irq_find_mapping(data->domain, entry->irq); 102 if (virq) 103 generic_handle_irq(virq); 104 } 105 106 chained_irq_exit(irq_desc_get_chip(desc), desc); 107} 108 109static const unsigned long unused_irq[REG_NUM] = {0x00000000, 0xfffe0008}; 110 111static int dc_ic_probe(struct platform_device *pdev) 112{ 113 struct device *dev = &pdev->dev; 114 struct irq_chip_generic *gc; 115 struct dc_ic_entry *entry; 116 struct irq_chip_type *ct; 117 struct dc_ic_data *data; 118 void __iomem *base; 119 int i, ret; 120 121 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 122 if (!data) 123 return -ENOMEM; 124 125 entry = devm_kcalloc(dev, IRQ_COUNT, sizeof(*entry), GFP_KERNEL); 126 if (!entry) 127 return -ENOMEM; 128 129 base = devm_platform_ioremap_resource(pdev, 0); 130 if (IS_ERR(base)) { 131 dev_err(dev, "failed to initialize reg\n"); 132 return PTR_ERR(base); 133 } 134 135 data->regs = devm_regmap_init_mmio(dev, base, &dc_ic_regmap_config); 136 if (IS_ERR(data->regs)) 137 return PTR_ERR(data->regs); 138 139 data->clk_axi = devm_clk_get(dev, NULL); 140 if (IS_ERR(data->clk_axi)) 141 return dev_err_probe(dev, PTR_ERR(data->clk_axi), 142 "failed to get AXI clock\n"); 143 144 for (i = 0; i < IRQ_COUNT; i++) { 145 /* skip the reserved IRQ */ 146 if (i == IRQ_RESERVED) 147 continue; 148 149 ret = platform_get_irq(pdev, i); 150 if (ret < 0) 151 return ret; 152 } 153 154 dev_set_drvdata(dev, data); 155 156 ret = devm_pm_runtime_enable(dev); 157 if (ret) 158 return ret; 159 160 ret = pm_runtime_resume_and_get(dev); 161 if (ret < 0) { 162 dev_err(dev, "failed to get runtime PM sync: %d\n", ret); 163 return ret; 164 } 165 166 for (i = 0; i < REG_NUM; i++) { 167 /* mask and clear all interrupts */ 168 regmap_write(data->regs, USERINTERRUPTENABLE(i), 0x0); 169 regmap_write(data->regs, INTERRUPTENABLE(i), 0x0); 170 regmap_write(data->regs, USERINTERRUPTCLEAR(i), 0xffffffff); 171 regmap_write(data->regs, INTERRUPTCLEAR(i), 0xffffffff); 172 173 /* set all interrupts to user mode */ 174 regmap_write(data->regs, USERINTERRUPTMASK(i), 0xffffffff); 175 } 176 177 data->domain = irq_domain_add_linear(dev->of_node, IRQ_COUNT, 178 &irq_generic_chip_ops, data); 179 if (!data->domain) { 180 dev_err(dev, "failed to create IRQ domain\n"); 181 pm_runtime_put(dev); 182 return -ENOMEM; 183 } 184 irq_domain_set_pm_device(data->domain, dev); 185 186 ret = irq_alloc_domain_generic_chips(data->domain, 32, 1, "DC", 187 handle_level_irq, 0, 0, 0); 188 if (ret) { 189 dev_err(dev, "failed to alloc generic IRQ chips: %d\n", ret); 190 irq_domain_remove(data->domain); 191 pm_runtime_put(dev); 192 return ret; 193 } 194 195 for (i = 0; i < IRQ_COUNT; i += 32) { 196 gc = irq_get_domain_generic_chip(data->domain, i); 197 gc->reg_base = base; 198 gc->unused = unused_irq[i / 32]; 199 ct = gc->chip_types; 200 ct->chip.irq_ack = irq_gc_ack_set_bit; 201 ct->chip.irq_mask = irq_gc_mask_clr_bit; 202 ct->chip.irq_unmask = irq_gc_mask_set_bit; 203 ct->regs.ack = USERINTERRUPTCLEAR(i / 32); 204 ct->regs.mask = USERINTERRUPTENABLE(i / 32); 205 } 206 207 for (i = 0; i < IRQ_COUNT; i++) { 208 /* skip the reserved IRQ */ 209 if (i == IRQ_RESERVED) 210 continue; 211 212 data->irq[i] = irq_of_parse_and_map(dev->of_node, i); 213 214 entry[i].data = data; 215 entry[i].irq = i; 216 217 irq_set_chained_handler_and_data(data->irq[i], 218 dc_ic_irq_handler, &entry[i]); 219 } 220 221 return 0; 222} 223 224static void dc_ic_remove(struct platform_device *pdev) 225{ 226 struct dc_ic_data *data = dev_get_drvdata(&pdev->dev); 227 int i; 228 229 for (i = 0; i < IRQ_COUNT; i++) { 230 if (i == IRQ_RESERVED) 231 continue; 232 233 irq_set_chained_handler_and_data(data->irq[i], NULL, NULL); 234 } 235 236 irq_domain_remove(data->domain); 237 238 pm_runtime_put_sync(&pdev->dev); 239} 240 241static int dc_ic_runtime_suspend(struct device *dev) 242{ 243 struct dc_ic_data *data = dev_get_drvdata(dev); 244 245 clk_disable_unprepare(data->clk_axi); 246 247 return 0; 248} 249 250static int dc_ic_runtime_resume(struct device *dev) 251{ 252 struct dc_ic_data *data = dev_get_drvdata(dev); 253 int ret; 254 255 ret = clk_prepare_enable(data->clk_axi); 256 if (ret) 257 dev_err(dev, "failed to enable AXI clock: %d\n", ret); 258 259 return ret; 260} 261 262static const struct dev_pm_ops dc_ic_pm_ops = { 263 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 264 pm_runtime_force_resume) 265 RUNTIME_PM_OPS(dc_ic_runtime_suspend, dc_ic_runtime_resume, NULL) 266}; 267 268static const struct of_device_id dc_ic_dt_ids[] = { 269 { .compatible = "fsl,imx8qxp-dc-intc", }, 270 { /* sentinel */ } 271}; 272 273struct platform_driver dc_ic_driver = { 274 .probe = dc_ic_probe, 275 .remove = dc_ic_remove, 276 .driver = { 277 .name = "imx8-dc-intc", 278 .suppress_bind_attrs = true, 279 .of_match_table = dc_ic_dt_ids, 280 .pm = pm_sleep_ptr(&dc_ic_pm_ops), 281 }, 282};