Home | History | Annotate | Line # | Download | only in mpc5200
      1 /*	$NetBSD: pic_mpc5200.c,v 1.1 2026/06/27 13:28:35 rkujawa Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2026 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Radoslaw Kujawa and Robert Swindells.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * MPC5200B SIU interrupt controller.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 __KERNEL_RCSID(0, "$NetBSD: pic_mpc5200.c,v 1.1 2026/06/27 13:28:35 rkujawa Exp $");
     38 
     39 #include <sys/param.h>
     40 #include <sys/systm.h>
     41 #include <sys/device.h>
     42 #include <sys/bus.h>
     43 
     44 #include <dev/ofw/openfirm.h>
     45 
     46 #include <machine/intr.h>
     47 
     48 #include <powerpc/pic/picvar.h>
     49 
     50 #include <powerpc/mpc5200/obiovar.h>
     51 #include <powerpc/mpc5200/pic_mpc5200var.h>
     52 
     53 /* SIU interrupt-controller registers (offsets from the controller base). */
     54 #define	SIU_PER_MASK	0x00	/* peripheral interrupt mask		*/
     55 #define	SIU_PER_PRI1	0x04	/* peripheral priority / HI-LO select 1	*/
     56 #define	SIU_PER_PRI2	0x08	/* peripheral priority / HI-LO select 2	*/
     57 #define	SIU_PER_PRI3	0x0c	/* peripheral priority / HI-LO select 3	*/
     58 #define	SIU_CTRL	0x10	/* external enable & type / critical enable */
     59 #define	SIU_MAIN_MASK	0x14	/* critical priority + main interrupt mask */
     60 #define	SIU_MAIN_PRI1	0x18	/* main priority 1			*/
     61 #define	SIU_MAIN_PRI2	0x1c	/* main priority 2 (0x1Cf in the draft -- typo) */
     62 #define	SIU_ENC_STATUS	0x24	/* encoded highest-priority pending source */
     63 #define	SIU_CRIT_STATUS	0x28	/* critical interrupt status		*/
     64 #define	SIU_MAIN_STATUS	0x2c	/* main interrupt status		*/
     65 #define	SIU_PER_STATUS	0x30	/* peripheral interrupt status		*/
     66 
     67 #define	SIU_REG_SIZE	0x40	/* register window if OF omits a size	*/
     68 
     69 /* Fields of the encoded-status register (SIU_ENC_STATUS). */
     70 #define	SIU_ENC_CRIT_VALID	0x00000400
     71 #define	SIU_ENC_CRIT_MASK	0x00000300
     72 #define	SIU_ENC_CRIT_SHIFT	8
     73 #define	SIU_ENC_MAIN_VALID	0x00200000
     74 #define	SIU_ENC_MAIN_MASK	0x001f0000
     75 #define	SIU_ENC_MAIN_SHIFT	16
     76 #define	SIU_ENC_PER_VALID	0x20000000
     77 #define	SIU_ENC_PER_MASK	0x1f000000
     78 #define	SIU_ENC_PER_SHIFT	24
     79 
     80 #define	SIU_ENC_CRIT_IS_CASCADE	2	/* critical "2" -> read peripheral */
     81 #define	SIU_ENC_MAIN_IS_CASCADE	4	/* main "4"     -> read peripheral */
     82 
     83 /* Reset state programmed at attach time. */
     84 #define	SIU_MAIN_MASK_INIT	0x0001efff /* mask all main but src 4 (per. cascade) */
     85 #define	SIU_PER_MASK_INIT	0xffffff00 /* mask all peripheral sources	*/
     86 
     87 #define	SIU_CTRL_INIT		0x0fc01001
     88 
     89 /* Number of sources owned by this (primary) PIC. */
     90 #define	MPC5200_NIRQ		96
     91 
     92 /* Band membership tests / source extraction for a flat IRQ number. */
     93 #define	MPC5200_IRQ_MAIN_BIT	0x20	/* flat 32..  -> main band	*/
     94 #define	MPC5200_IRQ_PER_BIT	0x40	/* flat 64..  -> peripheral band	*/
     95 #define	MPC5200_IRQ_SOURCE(irq)	((irq) & 0x1f)
     96 
     97 static int	mpc5200pic_match(device_t, cfdata_t, void *);
     98 static void	mpc5200pic_attach(device_t, device_t, void *);
     99 
    100 CFATTACH_DECL_NEW(mpc5200pic, sizeof(struct mpc5200pic_softc),
    101     mpc5200pic_match, mpc5200pic_attach, NULL, NULL);
    102 
    103 static void	mpc5200_enable_irq(struct pic_ops *, int, int);
    104 static void	mpc5200_disable_irq(struct pic_ops *, int);
    105 static int	mpc5200_get_irq(struct pic_ops *, int);
    106 static void	mpc5200_ack_irq(struct pic_ops *, int);
    107 static void	mpc5200_establish_irq(struct pic_ops *, int, int, int);
    108 
    109 static inline uint32_t
    110 siu_read(struct mpc5200_ops *m, bus_size_t off)
    111 {
    112 	return bus_space_read_4(m->bst, m->bsh, off);
    113 }
    114 
    115 static inline void
    116 siu_write(struct mpc5200_ops *m, bus_size_t off, uint32_t val)
    117 {
    118 	bus_space_write_4(m->bst, m->bsh, off, val);
    119 }
    120 
    121 static bool
    122 mpc5200pic_is_pic(int node, const char *name)
    123 {
    124 	char compat[32];
    125 
    126 	if (OF_getprop(node, "compatible", compat, sizeof(compat)) > 0 &&
    127 	    (strcmp(compat, "mpc5200-pic") == 0 ||
    128 	     strcmp(compat, "mpc5200b-pic") == 0))
    129 		return true;
    130 
    131 	return strcmp(name, "pic") == 0;
    132 }
    133 
    134 static int
    135 mpc5200pic_match(device_t parent, cfdata_t cf, void *aux)
    136 {
    137 	struct obio_attach_args *oba = aux;
    138 
    139 	return mpc5200pic_is_pic(oba->obio_node, oba->obio_name) ? 1 : 0;
    140 }
    141 
    142 static void
    143 mpc5200pic_attach(device_t parent, device_t self, void *aux)
    144 {
    145 	struct mpc5200pic_softc *sc = device_private(self);
    146 	struct obio_attach_args *oba = aux;
    147 	struct mpc5200_ops *m = &sc->sc_pic;
    148 	struct pic_ops *pic = &m->pic;
    149 	bus_size_t size;
    150 
    151 	sc->sc_dev = self;
    152 
    153 	size = oba->obio_size != 0 ? oba->obio_size : SIU_REG_SIZE;
    154 	m->bst = oba->obio_bst;
    155 	if (bus_space_map(m->bst, oba->obio_addr, size, 0, &m->bsh) != 0) {
    156 		aprint_error(": can't map registers\n");
    157 		return;
    158 	}
    159 
    160 	pic->pic_cookie = m;
    161 	pic->pic_numintrs = MPC5200_NIRQ;
    162 	pic->pic_enable_irq = mpc5200_enable_irq;
    163 	pic->pic_reenable_irq = mpc5200_enable_irq;
    164 	pic->pic_disable_irq = mpc5200_disable_irq;
    165 	pic->pic_get_irq = mpc5200_get_irq;
    166 	pic->pic_ack_irq = mpc5200_ack_irq;
    167 	pic->pic_establish_irq = mpc5200_establish_irq;
    168 	pic->pic_finish_setup = NULL;
    169 	strlcpy(pic->pic_name, device_xname(self), sizeof(pic->pic_name));
    170 
    171 	m->main_mask = SIU_MAIN_MASK_INIT;
    172 	m->per_mask = SIU_PER_MASK_INIT;
    173 	m->ctrl = SIU_CTRL_INIT;
    174 
    175 	pic_add(pic);
    176 
    177 	siu_write(m, SIU_MAIN_MASK, m->main_mask);
    178 	siu_write(m, SIU_PER_MASK, m->per_mask);
    179 	siu_write(m, SIU_CTRL, m->ctrl);
    180 
    181 	/* Flat priorities: dispatch order comes from the encoded register. */
    182 	siu_write(m, SIU_PER_PRI1, 0);
    183 	siu_write(m, SIU_PER_PRI2, 0);
    184 	siu_write(m, SIU_PER_PRI3, 0);
    185 	siu_write(m, SIU_MAIN_PRI1, 0);
    186 	siu_write(m, SIU_MAIN_PRI2, 0);
    187 
    188 	aprint_normal(": MPC5200 SIU interrupt controller, %d sources\n",
    189 	    MPC5200_NIRQ);
    190 	aprint_verbose_dev(self, "main_mask=0x%08x per_mask=0x%08x\n",
    191 	    siu_read(m, SIU_MAIN_MASK), siu_read(m, SIU_PER_MASK));
    192 }
    193 
    194 static void
    195 mpc5200_enable_irq(struct pic_ops *pic, int irq, int type)
    196 {
    197 	struct mpc5200_ops *m = pic->pic_cookie;
    198 	uint32_t bit;
    199 
    200 	if (irq & MPC5200_IRQ_MAIN_BIT) {		/* main */
    201 		if (MPC5200_IRQ_SOURCE(irq) > 16)
    202 			return;
    203 		bit = 1u << (16 - MPC5200_IRQ_SOURCE(irq));
    204 		m->main_mask &= ~bit;
    205 		siu_write(m, SIU_MAIN_MASK, m->main_mask);
    206 	} else if (irq & MPC5200_IRQ_PER_BIT) {		/* peripheral */
    207 		if (MPC5200_IRQ_SOURCE(irq) > 23)
    208 			return;
    209 		bit = 1u << (31 - MPC5200_IRQ_SOURCE(irq));
    210 		m->per_mask &= ~bit;
    211 		siu_write(m, SIU_PER_MASK, m->per_mask);
    212 	} else {					/* critical */
    213 		if (MPC5200_IRQ_SOURCE(irq) > 3)
    214 			return;
    215 		bit = (1u << (11 - irq)) | ((uint32_t)type << (22 - irq * 2));
    216 		m->ctrl |= bit;
    217 		siu_write(m, SIU_CTRL, m->ctrl);
    218 	}
    219 }
    220 
    221 static void
    222 mpc5200_disable_irq(struct pic_ops *pic, int irq)
    223 {
    224 	struct mpc5200_ops *m = pic->pic_cookie;
    225 	uint32_t bit;
    226 
    227 	/*
    228 	 * Flat 36 == main source 4 is the peripheral cascade
    229 	 */
    230 	if (irq == (32 + 4))
    231 		return;
    232 
    233 	if (irq & MPC5200_IRQ_MAIN_BIT) {		/* main */
    234 		if (MPC5200_IRQ_SOURCE(irq) > 16)
    235 			return;
    236 		bit = 1u << (16 - MPC5200_IRQ_SOURCE(irq));
    237 		m->main_mask |= bit;
    238 		siu_write(m, SIU_MAIN_MASK, m->main_mask);
    239 	} else if (irq & MPC5200_IRQ_PER_BIT) {		/* peripheral */
    240 		if (MPC5200_IRQ_SOURCE(irq) > 23)
    241 			return;
    242 		bit = 1u << (31 - MPC5200_IRQ_SOURCE(irq));
    243 		m->per_mask |= bit;
    244 		siu_write(m, SIU_PER_MASK, m->per_mask);
    245 	} else {					/* critical */
    246 		if (MPC5200_IRQ_SOURCE(irq) > 3)
    247 			return;
    248 		bit = 1u << (11 - MPC5200_IRQ_SOURCE(irq));
    249 		m->ctrl &= ~bit;
    250 		siu_write(m, SIU_CTRL, m->ctrl);
    251 	}
    252 }
    253 
    254 static int
    255 mpc5200_get_irq(struct pic_ops *pic, int mode)
    256 {
    257 	struct mpc5200_ops *m = pic->pic_cookie;
    258 	uint32_t enc;
    259 	int source;
    260 
    261 	enc = siu_read(m, SIU_ENC_STATUS);
    262 
    263 	if (enc & SIU_ENC_CRIT_VALID) {
    264 		source = (enc & SIU_ENC_CRIT_MASK) >> SIU_ENC_CRIT_SHIFT;
    265 		if (source != SIU_ENC_CRIT_IS_CASCADE)
    266 			return source;			/* flat 0..3 */
    267 		goto peripheral;
    268 	}
    269 	if (enc & SIU_ENC_MAIN_VALID) {
    270 		source = (enc & SIU_ENC_MAIN_MASK) >> SIU_ENC_MAIN_SHIFT;
    271 		if (source != SIU_ENC_MAIN_IS_CASCADE)
    272 			return source + 32;		/* flat 32..48 */
    273 		goto peripheral;
    274 	}
    275 	if (enc & SIU_ENC_PER_VALID) {
    276  peripheral:
    277 		source = (enc & SIU_ENC_PER_MASK) >> SIU_ENC_PER_SHIFT;
    278 		return source + 64;			/* flat 64..87 */
    279 	}
    280 
    281 	return 255;	/* no source pending: treated as spurious */
    282 }
    283 
    284 static void
    285 mpc5200_ack_irq(struct pic_ops *pic, int irq)
    286 {
    287 	/*
    288 	 * The SIU has no end-of-interrupt register
    289 	 */
    290 }
    291 
    292 static void
    293 mpc5200_establish_irq(struct pic_ops *pic, int irq, int type, int maxlevel)
    294 {
    295 	/*
    296 	 * Nothing to program per source
    297 	 */
    298 }
    299 
    300 /*
    301  * Translate the index'th entry of an OF "interrupts" property into a flat IRQ.
    302  */
    303 static int
    304 mpc5200_sense_to_ist(uint32_t sense)
    305 {
    306 	/*
    307 	 * The sense cell is only meaningful for the external (critical) IRQ
    308 	 */
    309 	switch (sense) {
    310 	case 1:
    311 		return IST_EDGE_RISING;
    312 	case 2:
    313 		return IST_EDGE_FALLING;
    314 	default:
    315 		return IST_LEVEL;
    316 	}
    317 }
    318 
    319 int
    320 obio_decode_interrupt(int node, int index, int *irqp, int *typep)
    321 {
    322 	uint32_t intr[3 * 16];	/* up to 16 interrupts, 3 cells each */
    323 	uint32_t tier, source;
    324 	int len, count, base;
    325 
    326 	len = OF_getprop(node, "interrupts", intr, sizeof(intr));
    327 	if (len < (int)(3 * sizeof(uint32_t)))
    328 		return 0;
    329 	count = len / (int)(3 * sizeof(uint32_t));
    330 	if (index < 0 || index >= count)
    331 		return 0;
    332 
    333 	tier   = intr[index * 3 + 0];
    334 	source = intr[index * 3 + 1];
    335 
    336 	switch (tier) {
    337 	case 0:
    338 		base = 0;		/* critical   -> flat  0.. */
    339 		break;
    340 	case 1:
    341 		base = 32;		/* main       -> flat 32.. */
    342 		break;
    343 	case 2:
    344 		base = 64;		/* peripheral -> flat 64.. */
    345 		break;
    346 	case 3:
    347 		base = 96;		/* BestComm   -> flat 96.. (secondary PIC) */
    348 		break;
    349 	default:
    350 		return 0;
    351 	}
    352 
    353 	*irqp = base + source;
    354 	*typep = mpc5200_sense_to_ist(intr[index * 3 + 2]);
    355 	return 1;
    356 }
    357