1 /* 2 * Copyright (C) 2017 Marvell 3 * 4 * Hanna Hawa <hannah@marvell.com> 5 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 6 * 7 * This file is licensed under the terms of the GNU General Public 8 * License version 2. This program is licensed "as is" without any 9 * warranty of any kind, whether express or implied. 10 */ 11 12 #include <linux/interrupt.h> 13 #include <linux/irq.h> 14 #include <linux/irqchip.h> 15 #include <linux/irqdomain.h> 16 #include <linux/kernel.h> 17 #include <linux/msi.h> 18 #include <linux/of_irq.h> 19 #include <linux/of_platform.h> 20 #include <linux/platform_device.h> 21 22 #include <dt-bindings/interrupt-controller/mvebu-icu.h> 23 24 /* ICU registers */ 25 #define ICU_SETSPI_NSR_AL 0x10 26 #define ICU_SETSPI_NSR_AH 0x14 27 #define ICU_CLRSPI_NSR_AL 0x18 28 #define ICU_CLRSPI_NSR_AH 0x1c 29 #define ICU_INT_CFG(x) (0x100 + 4 * (x)) 30 #define ICU_INT_ENABLE BIT(24) 31 #define ICU_IS_EDGE BIT(28) 32 #define ICU_GROUP_SHIFT 29 33 34 /* ICU definitions */ 35 #define ICU_MAX_IRQS 207 36 #define ICU_SATA0_ICU_ID 109 37 #define ICU_SATA1_ICU_ID 107 38 39 struct mvebu_icu { 40 struct irq_chip irq_chip; 41 void __iomem *base; 42 struct device *dev; 43 atomic_t initialized; 44 }; 45 46 struct mvebu_icu_irq_data { 47 struct mvebu_icu *icu; 48 unsigned int icu_group; 49 unsigned int type; 50 }; 51 52 static void mvebu_icu_init(struct mvebu_icu *icu, struct msi_msg *msg) 53 { 54 if (atomic_cmpxchg(&icu->initialized, false, true)) 55 return; 56 57 /* Set Clear/Set ICU SPI message address in AP */ 58 writel_relaxed(msg[0].address_hi, icu->base + ICU_SETSPI_NSR_AH); 59 writel_relaxed(msg[0].address_lo, icu->base + ICU_SETSPI_NSR_AL); 60 writel_relaxed(msg[1].address_hi, icu->base + ICU_CLRSPI_NSR_AH); 61 writel_relaxed(msg[1].address_lo, icu->base + ICU_CLRSPI_NSR_AL); 62 } 63 64 static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg) 65 { 66 struct irq_data *d = irq_get_irq_data(desc->irq); 67 struct mvebu_icu_irq_data *icu_irqd = d->chip_data; 68 struct mvebu_icu *icu = icu_irqd->icu; 69 unsigned int icu_int; 70 71 if (msg->address_lo || msg->address_hi) { 72 /* One off initialization */ 73 mvebu_icu_init(icu, msg); 74 /* Configure the ICU with irq number & type */ 75 icu_int = msg->data | ICU_INT_ENABLE; 76 if (icu_irqd->type & IRQ_TYPE_EDGE_RISING) 77 icu_int |= ICU_IS_EDGE; 78 icu_int |= icu_irqd->icu_group << ICU_GROUP_SHIFT; 79 } else { 80 /* De-configure the ICU */ 81 icu_int = 0; 82 } 83 84 writel_relaxed(icu_int, icu->base + ICU_INT_CFG(d->hwirq)); 85 86 /* 87 * The SATA unit has 2 ports, and a dedicated ICU entry per 88 * port. The ahci sata driver supports only one irq interrupt 89 * per SATA unit. To solve this conflict, we configure the 2 90 * SATA wired interrupts in the south bridge into 1 GIC 91 * interrupt in the north bridge. Even if only a single port 92 * is enabled, if sata node is enabled, both interrupts are 93 * configured (regardless of which port is actually in use). 94 */ 95 if (d->hwirq == ICU_SATA0_ICU_ID || d->hwirq == ICU_SATA1_ICU_ID) { 96 writel_relaxed(icu_int, 97 icu->base + ICU_INT_CFG(ICU_SATA0_ICU_ID)); 98 writel_relaxed(icu_int, 99 icu->base + ICU_INT_CFG(ICU_SATA1_ICU_ID)); 100 } 101 } 102 103 static int 104 mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, 105 unsigned long *hwirq, unsigned int *type) 106 { 107 struct mvebu_icu *icu = platform_msi_get_host_data(d); 108 unsigned int icu_group; 109 110 /* Check the count of the parameters in dt */ 111 if (WARN_ON(fwspec->param_count < 3)) { 112 dev_err(icu->dev, "wrong ICU parameter count %d\n", 113 fwspec->param_count); 114 return -EINVAL; 115 } 116 117 /* Only ICU group type is handled */ 118 icu_group = fwspec->param[0]; 119 if (icu_group != ICU_GRP_NSR && icu_group != ICU_GRP_SR && 120 icu_group != ICU_GRP_SEI && icu_group != ICU_GRP_REI) { 121 dev_err(icu->dev, "wrong ICU group type %x\n", icu_group); 122 return -EINVAL; 123 } 124 125 *hwirq = fwspec->param[1]; 126 if (*hwirq >= ICU_MAX_IRQS) { 127 dev_err(icu->dev, "invalid interrupt number %ld\n", *hwirq); 128 return -EINVAL; 129 } 130 131 /* Mask the type to prevent wrong DT configuration */ 132 *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; 133 134 return 0; 135 } 136 137 static int 138 mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, 139 unsigned int nr_irqs, void *args) 140 { 141 int err; 142 unsigned long hwirq; 143 struct irq_fwspec *fwspec = args; 144 struct mvebu_icu *icu = platform_msi_get_host_data(domain); 145 struct mvebu_icu_irq_data *icu_irqd; 146 147 icu_irqd = kmalloc(sizeof(*icu_irqd), GFP_KERNEL); 148 if (!icu_irqd) 149 return -ENOMEM; 150 151 err = mvebu_icu_irq_domain_translate(domain, fwspec, &hwirq, 152 &icu_irqd->type); 153 if (err) { 154 dev_err(icu->dev, "failed to translate ICU parameters\n"); 155 goto free_irqd; 156 } 157 158 icu_irqd->icu_group = fwspec->param[0]; 159 icu_irqd->icu = icu; 160 161 err = platform_msi_domain_alloc(domain, virq, nr_irqs); 162 if (err) { 163 dev_err(icu->dev, "failed to allocate ICU interrupt in parent domain\n"); 164 goto free_irqd; 165 } 166 167 /* Make sure there is no interrupt left pending by the firmware */ 168 err = irq_set_irqchip_state(virq, IRQCHIP_STATE_PENDING, false); 169 if (err) 170 goto free_msi; 171 172 err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, 173 &icu->irq_chip, icu_irqd); 174 if (err) { 175 dev_err(icu->dev, "failed to set the data to IRQ domain\n"); 176 goto free_msi; 177 } 178 179 return 0; 180 181 free_msi: 182 platform_msi_domain_free(domain, virq, nr_irqs); 183 free_irqd: 184 kfree(icu_irqd); 185 return err; 186 } 187 188 static void 189 mvebu_icu_irq_domain_free(struct irq_domain *domain, unsigned int virq, 190 unsigned int nr_irqs) 191 { 192 struct irq_data *d = irq_get_irq_data(virq); 193 struct mvebu_icu_irq_data *icu_irqd = d->chip_data; 194 195 kfree(icu_irqd); 196 197 platform_msi_domain_free(domain, virq, nr_irqs); 198 } 199 200 static const struct irq_domain_ops mvebu_icu_domain_ops = { 201 .translate = mvebu_icu_irq_domain_translate, 202 .alloc = mvebu_icu_irq_domain_alloc, 203 .free = mvebu_icu_irq_domain_free, 204 }; 205 206 static int mvebu_icu_subset_probe(struct platform_device *pdev) 207 { 208 struct device_node *msi_parent_dn; 209 struct device *dev = &pdev->dev; 210 struct irq_domain *irq_domain; 211 struct mvebu_icu *icu; 212 213 icu = dev_get_drvdata(dev); 214 215 dev->msi_domain = of_msi_get_domain(dev, dev->of_node, 216 DOMAIN_BUS_PLATFORM_MSI); 217 if (!dev->msi_domain) 218 return -EPROBE_DEFER; 219 220 msi_parent_dn = irq_domain_get_of_node(dev->msi_domain); 221 if (!msi_parent_dn) 222 return -ENODEV; 223 224 irq_domain = platform_msi_create_device_tree_domain(dev, ICU_MAX_IRQS, 225 mvebu_icu_write_msg, 226 &mvebu_icu_domain_ops, 227 icu); 228 if (!irq_domain) { 229 dev_err(dev, "Failed to create ICU MSI domain\n"); 230 return -ENOMEM; 231 } 232 233 return 0; 234 } 235 236 static int mvebu_icu_probe(struct platform_device *pdev) 237 { 238 struct mvebu_icu *icu; 239 struct resource *res; 240 int i; 241 242 icu = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_icu), 243 GFP_KERNEL); 244 if (!icu) 245 return -ENOMEM; 246 247 icu->dev = &pdev->dev; 248 249 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 250 icu->base = devm_ioremap_resource(&pdev->dev, res); 251 if (IS_ERR(icu->base)) { 252 dev_err(&pdev->dev, "Failed to map icu base address.\n"); 253 return PTR_ERR(icu->base); 254 } 255 256 icu->irq_chip.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, 257 "ICU.%x", 258 (unsigned int)res->start); 259 if (!icu->irq_chip.name) 260 return -ENOMEM; 261 262 icu->irq_chip.irq_mask = irq_chip_mask_parent; 263 icu->irq_chip.irq_unmask = irq_chip_unmask_parent; 264 icu->irq_chip.irq_eoi = irq_chip_eoi_parent; 265 icu->irq_chip.irq_set_type = irq_chip_set_type_parent; 266 #ifdef CONFIG_SMP 267 icu->irq_chip.irq_set_affinity = irq_chip_set_affinity_parent; 268 #endif 269 270 /* 271 * Clean all ICU interrupts with type SPI_NSR, required to 272 * avoid unpredictable SPI assignments done by firmware. 273 */ 274 for (i = 0 ; i < ICU_MAX_IRQS ; i++) { 275 u32 icu_int, icu_grp; 276 277 icu_int = readl_relaxed(icu->base + ICU_INT_CFG(i)); 278 icu_grp = icu_int >> ICU_GROUP_SHIFT; 279 280 if (icu_grp == ICU_GRP_NSR) 281 writel_relaxed(0x0, icu->base + ICU_INT_CFG(i)); 282 } 283 284 platform_set_drvdata(pdev, icu); 285 286 return mvebu_icu_subset_probe(pdev); 287 } 288 289 static const struct of_device_id mvebu_icu_of_match[] = { 290 { .compatible = "marvell,cp110-icu", }, 291 {}, 292 }; 293 294 static struct platform_driver mvebu_icu_driver = { 295 .probe = mvebu_icu_probe, 296 .driver = { 297 .name = "mvebu-icu", 298 .of_match_table = mvebu_icu_of_match, 299 }, 300 }; 301 builtin_platform_driver(mvebu_icu_driver); 302