Home | History | Annotate | Download | only in cpu
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright (C) 2015, Bin Meng <bmeng.cn (at) gmail.com>
      4  */
      5 
      6 #include <common.h>
      7 #include <dm.h>
      8 #include <errno.h>
      9 #include <fdtdec.h>
     10 #include <malloc.h>
     11 #include <asm/io.h>
     12 #include <asm/irq.h>
     13 #include <asm/pci.h>
     14 #include <asm/pirq_routing.h>
     15 #include <asm/tables.h>
     16 
     17 DECLARE_GLOBAL_DATA_PTR;
     18 
     19 /**
     20  * pirq_reg_to_linkno() - Convert a PIRQ routing register offset to link number
     21  *
     22  * @priv:	IRQ router driver's priv data
     23  * @reg:	PIRQ routing register offset from the base address
     24  * @return:	PIRQ link number (0 for PIRQA, 1 for PIRQB, etc)
     25  */
     26 static inline int pirq_reg_to_linkno(struct irq_router *priv, int reg)
     27 {
     28 	int linkno = 0;
     29 
     30 	if (priv->has_regmap) {
     31 		struct pirq_regmap *map = priv->regmap;
     32 		int i;
     33 
     34 		for (i = 0; i < priv->link_num; i++) {
     35 			if (reg - priv->link_base == map->offset) {
     36 				linkno = map->link;
     37 				break;
     38 			}
     39 			map++;
     40 		}
     41 	} else {
     42 		linkno = reg - priv->link_base;
     43 	}
     44 
     45 	return linkno;
     46 }
     47 
     48 /**
     49  * pirq_linkno_to_reg() - Convert a PIRQ link number to routing register offset
     50  *
     51  * @priv:	IRQ router driver's priv data
     52  * @linkno:	PIRQ link number (0 for PIRQA, 1 for PIRQB, etc)
     53  * @return:	PIRQ routing register offset from the base address
     54  */
     55 static inline int pirq_linkno_to_reg(struct irq_router *priv, int linkno)
     56 {
     57 	int reg = 0;
     58 
     59 	if (priv->has_regmap) {
     60 		struct pirq_regmap *map = priv->regmap;
     61 		int i;
     62 
     63 		for (i = 0; i < priv->link_num; i++) {
     64 			if (linkno == map->link) {
     65 				reg = map->offset + priv->link_base;
     66 				break;
     67 			}
     68 			map++;
     69 		}
     70 	} else {
     71 		reg = linkno + priv->link_base;
     72 	}
     73 
     74 	return reg;
     75 }
     76 
     77 bool pirq_check_irq_routed(struct udevice *dev, int link, u8 irq)
     78 {
     79 	struct irq_router *priv = dev_get_priv(dev);
     80 	u8 pirq;
     81 
     82 	if (priv->config == PIRQ_VIA_PCI)
     83 		dm_pci_read_config8(dev->parent,
     84 				    pirq_linkno_to_reg(priv, link), &pirq);
     85 	else
     86 		pirq = readb((uintptr_t)priv->ibase +
     87 			     pirq_linkno_to_reg(priv, link));
     88 
     89 	pirq &= 0xf;
     90 
     91 	/* IRQ# 0/1/2/8/13 are reserved */
     92 	if (pirq < 3 || pirq == 8 || pirq == 13)
     93 		return false;
     94 
     95 	return pirq == irq ? true : false;
     96 }
     97 
     98 int pirq_translate_link(struct udevice *dev, int link)
     99 {
    100 	struct irq_router *priv = dev_get_priv(dev);
    101 
    102 	return pirq_reg_to_linkno(priv, link);
    103 }
    104 
    105 void pirq_assign_irq(struct udevice *dev, int link, u8 irq)
    106 {
    107 	struct irq_router *priv = dev_get_priv(dev);
    108 
    109 	/* IRQ# 0/1/2/8/13 are reserved */
    110 	if (irq < 3 || irq == 8 || irq == 13)
    111 		return;
    112 
    113 	if (priv->config == PIRQ_VIA_PCI)
    114 		dm_pci_write_config8(dev->parent,
    115 				     pirq_linkno_to_reg(priv, link), irq);
    116 	else
    117 		writeb(irq, (uintptr_t)priv->ibase +
    118 		       pirq_linkno_to_reg(priv, link));
    119 }
    120 
    121 static struct irq_info *check_dup_entry(struct irq_info *slot_base,
    122 					int entry_num, int bus, int device)
    123 {
    124 	struct irq_info *slot = slot_base;
    125 	int i;
    126 
    127 	for (i = 0; i < entry_num; i++) {
    128 		if (slot->bus == bus && slot->devfn == (device << 3))
    129 			break;
    130 		slot++;
    131 	}
    132 
    133 	return (i == entry_num) ? NULL : slot;
    134 }
    135 
    136 static inline void fill_irq_info(struct irq_router *priv, struct irq_info *slot,
    137 				 int bus, int device, int pin, int pirq)
    138 {
    139 	slot->bus = bus;
    140 	slot->devfn = (device << 3) | 0;
    141 	slot->irq[pin - 1].link = pirq_linkno_to_reg(priv, pirq);
    142 	slot->irq[pin - 1].bitmap = priv->irq_mask;
    143 }
    144 
    145 static int create_pirq_routing_table(struct udevice *dev)
    146 {
    147 	struct irq_router *priv = dev_get_priv(dev);
    148 	const void *blob = gd->fdt_blob;
    149 	int node;
    150 	int len, count;
    151 	const u32 *cell;
    152 	struct pirq_regmap *map;
    153 	struct irq_routing_table *rt;
    154 	struct irq_info *slot, *slot_base;
    155 	int irq_entries = 0;
    156 	int i;
    157 	int ret;
    158 
    159 	node = dev_of_offset(dev);
    160 
    161 	/* extract the bdf from fdt_pci_addr */
    162 	priv->bdf = dm_pci_get_bdf(dev->parent);
    163 
    164 	ret = fdt_stringlist_search(blob, node, "intel,pirq-config", "pci");
    165 	if (!ret) {
    166 		priv->config = PIRQ_VIA_PCI;
    167 	} else {
    168 		ret = fdt_stringlist_search(blob, node, "intel,pirq-config",
    169 					    "ibase");
    170 		if (!ret)
    171 			priv->config = PIRQ_VIA_IBASE;
    172 		else
    173 			return -EINVAL;
    174 	}
    175 
    176 	cell = fdt_getprop(blob, node, "intel,pirq-link", &len);
    177 	if (!cell || len != 8)
    178 		return -EINVAL;
    179 	priv->link_base = fdt_addr_to_cpu(cell[0]);
    180 	priv->link_num = fdt_addr_to_cpu(cell[1]);
    181 	if (priv->link_num > CONFIG_MAX_PIRQ_LINKS) {
    182 		debug("Limiting supported PIRQ link number from %d to %d\n",
    183 		      priv->link_num, CONFIG_MAX_PIRQ_LINKS);
    184 		priv->link_num = CONFIG_MAX_PIRQ_LINKS;
    185 	}
    186 
    187 	cell = fdt_getprop(blob, node, "intel,pirq-regmap", &len);
    188 	if (cell) {
    189 		if (len % sizeof(struct pirq_regmap))
    190 			return -EINVAL;
    191 
    192 		count = len / sizeof(struct pirq_regmap);
    193 		if (count < priv->link_num) {
    194 			printf("Number of pirq-regmap entires is wrong\n");
    195 			return -EINVAL;
    196 		}
    197 
    198 		count = priv->link_num;
    199 		priv->regmap = calloc(count, sizeof(struct pirq_regmap));
    200 		if (!priv->regmap)
    201 			return -ENOMEM;
    202 
    203 		priv->has_regmap = true;
    204 		map = priv->regmap;
    205 		for (i = 0; i < count; i++) {
    206 			map->link = fdt_addr_to_cpu(cell[0]);
    207 			map->offset = fdt_addr_to_cpu(cell[1]);
    208 
    209 			cell += sizeof(struct pirq_regmap) / sizeof(u32);
    210 			map++;
    211 		}
    212 	}
    213 
    214 	priv->irq_mask = fdtdec_get_int(blob, node,
    215 					"intel,pirq-mask", PIRQ_BITMAP);
    216 
    217 	if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE)) {
    218 		/* Reserve IRQ9 for SCI */
    219 		priv->irq_mask &= ~(1 << 9);
    220 	}
    221 
    222 	if (priv->config == PIRQ_VIA_IBASE) {
    223 		int ibase_off;
    224 
    225 		ibase_off = fdtdec_get_int(blob, node, "intel,ibase-offset", 0);
    226 		if (!ibase_off)
    227 			return -EINVAL;
    228 
    229 		/*
    230 		 * Here we assume that the IBASE register has already been
    231 		 * properly configured by U-Boot before.
    232 		 *
    233 		 * By 'valid' we mean:
    234 		 *   1) a valid memory space carved within system memory space
    235 		 *      assigned to IBASE register block.
    236 		 *   2) memory range decoding is enabled.
    237 		 * Hence we don't do any santify test here.
    238 		 */
    239 		dm_pci_read_config32(dev->parent, ibase_off, &priv->ibase);
    240 		priv->ibase &= ~0xf;
    241 	}
    242 
    243 	priv->actl_8bit = fdtdec_get_bool(blob, node, "intel,actl-8bit");
    244 	priv->actl_addr = fdtdec_get_int(blob, node, "intel,actl-addr", 0);
    245 
    246 	cell = fdt_getprop(blob, node, "intel,pirq-routing", &len);
    247 	if (!cell || len % sizeof(struct pirq_routing))
    248 		return -EINVAL;
    249 	count = len / sizeof(struct pirq_routing);
    250 
    251 	rt = calloc(1, sizeof(struct irq_routing_table));
    252 	if (!rt)
    253 		return -ENOMEM;
    254 
    255 	/* Populate the PIRQ table fields */
    256 	rt->signature = PIRQ_SIGNATURE;
    257 	rt->version = PIRQ_VERSION;
    258 	rt->rtr_bus = PCI_BUS(priv->bdf);
    259 	rt->rtr_devfn = (PCI_DEV(priv->bdf) << 3) | PCI_FUNC(priv->bdf);
    260 	rt->rtr_vendor = PCI_VENDOR_ID_INTEL;
    261 	rt->rtr_device = PCI_DEVICE_ID_INTEL_ICH7_31;
    262 
    263 	slot_base = rt->slots;
    264 
    265 	/* Now fill in the irq_info entries in the PIRQ table */
    266 	for (i = 0; i < count;
    267 	     i++, cell += sizeof(struct pirq_routing) / sizeof(u32)) {
    268 		struct pirq_routing pr;
    269 
    270 		pr.bdf = fdt_addr_to_cpu(cell[0]);
    271 		pr.pin = fdt_addr_to_cpu(cell[1]);
    272 		pr.pirq = fdt_addr_to_cpu(cell[2]);
    273 
    274 		debug("irq_info %d: b.d.f %x.%x.%x INT%c PIRQ%c\n",
    275 		      i, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
    276 		      PCI_FUNC(pr.bdf), 'A' + pr.pin - 1,
    277 		      'A' + pr.pirq);
    278 
    279 		slot = check_dup_entry(slot_base, irq_entries,
    280 				       PCI_BUS(pr.bdf), PCI_DEV(pr.bdf));
    281 		if (slot) {
    282 			debug("found entry for bus %d device %d, ",
    283 			      PCI_BUS(pr.bdf), PCI_DEV(pr.bdf));
    284 
    285 			if (slot->irq[pr.pin - 1].link) {
    286 				debug("skipping\n");
    287 
    288 				/*
    289 				 * Sanity test on the routed PIRQ pin
    290 				 *
    291 				 * If they don't match, show a warning to tell
    292 				 * there might be something wrong with the PIRQ
    293 				 * routing information in the device tree.
    294 				 */
    295 				if (slot->irq[pr.pin - 1].link !=
    296 				    pirq_linkno_to_reg(priv, pr.pirq))
    297 					debug("WARNING: Inconsistent PIRQ routing information\n");
    298 				continue;
    299 			}
    300 		} else {
    301 			slot = slot_base + irq_entries++;
    302 		}
    303 		debug("writing INT%c\n", 'A' + pr.pin - 1);
    304 		fill_irq_info(priv, slot, PCI_BUS(pr.bdf), PCI_DEV(pr.bdf),
    305 			      pr.pin, pr.pirq);
    306 	}
    307 
    308 	rt->size = irq_entries * sizeof(struct irq_info) + 32;
    309 
    310 	/* Fix up the table checksum */
    311 	rt->checksum = table_compute_checksum(rt, rt->size);
    312 
    313 	gd->arch.pirq_routing_table = rt;
    314 
    315 	return 0;
    316 }
    317 
    318 static void irq_enable_sci(struct udevice *dev)
    319 {
    320 	struct irq_router *priv = dev_get_priv(dev);
    321 
    322 	if (priv->actl_8bit) {
    323 		/* Bit7 must be turned on to enable ACPI */
    324 		dm_pci_write_config8(dev->parent, priv->actl_addr, 0x80);
    325 	} else {
    326 		/* Write 0 to enable SCI on IRQ9 */
    327 		if (priv->config == PIRQ_VIA_PCI)
    328 			dm_pci_write_config32(dev->parent, priv->actl_addr, 0);
    329 		else
    330 			writel(0, (uintptr_t)priv->ibase + priv->actl_addr);
    331 	}
    332 }
    333 
    334 int irq_router_probe(struct udevice *dev)
    335 {
    336 	int ret;
    337 
    338 	ret = create_pirq_routing_table(dev);
    339 	if (ret) {
    340 		debug("Failed to create pirq routing table\n");
    341 		return ret;
    342 	}
    343 	/* Route PIRQ */
    344 	pirq_route_irqs(dev, gd->arch.pirq_routing_table->slots,
    345 			get_irq_slot_count(gd->arch.pirq_routing_table));
    346 
    347 	if (IS_ENABLED(CONFIG_GENERATE_ACPI_TABLE))
    348 		irq_enable_sci(dev);
    349 
    350 	return 0;
    351 }
    352 
    353 ulong write_pirq_routing_table(ulong addr)
    354 {
    355 	if (!gd->arch.pirq_routing_table)
    356 		return addr;
    357 
    358 	return copy_pirq_routing_table(addr, gd->arch.pirq_routing_table);
    359 }
    360 
    361 static const struct udevice_id irq_router_ids[] = {
    362 	{ .compatible = "intel,irq-router" },
    363 	{ }
    364 };
    365 
    366 U_BOOT_DRIVER(irq_router_drv) = {
    367 	.name		= "intel_irq",
    368 	.id		= UCLASS_IRQ,
    369 	.of_match	= irq_router_ids,
    370 	.probe		= irq_router_probe,
    371 	.priv_auto_alloc_size = sizeof(struct irq_router),
    372 };
    373 
    374 UCLASS_DRIVER(irq) = {
    375 	.id		= UCLASS_IRQ,
    376 	.name		= "irq",
    377 };
    378