18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * TX4939 irq routines
38c2ecf20Sopenharmony_ci * Based on linux/arch/mips/kernel/irq_txx9.c,
48c2ecf20Sopenharmony_ci *	    and RBTX49xx patch from CELF patch archive.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright 2001, 2003-2005 MontaVista Software Inc.
78c2ecf20Sopenharmony_ci * Author: MontaVista Software, Inc.
88c2ecf20Sopenharmony_ci *	   ahennessy@mvista.com
98c2ecf20Sopenharmony_ci *	   source@mvista.com
108c2ecf20Sopenharmony_ci * Copyright (C) 2000-2001,2005-2007 Toshiba Corporation
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
138c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
148c2ecf20Sopenharmony_ci * for more details.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * TX4939 defines 64 IRQs.
188c2ecf20Sopenharmony_ci * Similer to irq_txx9.c but different register layouts.
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci#include <linux/init.h>
218c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
228c2ecf20Sopenharmony_ci#include <linux/irq.h>
238c2ecf20Sopenharmony_ci#include <linux/types.h>
248c2ecf20Sopenharmony_ci#include <asm/irq_cpu.h>
258c2ecf20Sopenharmony_ci#include <asm/txx9irq.h>
268c2ecf20Sopenharmony_ci#include <asm/txx9/tx4939.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* IRCER : Int. Control Enable */
298c2ecf20Sopenharmony_ci#define TXx9_IRCER_ICE	0x00000001
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* IRCR : Int. Control */
328c2ecf20Sopenharmony_ci#define TXx9_IRCR_LOW	0x00000000
338c2ecf20Sopenharmony_ci#define TXx9_IRCR_HIGH	0x00000001
348c2ecf20Sopenharmony_ci#define TXx9_IRCR_DOWN	0x00000002
358c2ecf20Sopenharmony_ci#define TXx9_IRCR_UP	0x00000003
368c2ecf20Sopenharmony_ci#define TXx9_IRCR_EDGE(cr)	((cr) & 0x00000002)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* IRSCR : Int. Status Control */
398c2ecf20Sopenharmony_ci#define TXx9_IRSCR_EIClrE	0x00000100
408c2ecf20Sopenharmony_ci#define TXx9_IRSCR_EIClr_MASK	0x0000000f
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* IRCSR : Int. Current Status */
438c2ecf20Sopenharmony_ci#define TXx9_IRCSR_IF	0x00010000
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define irc_dlevel	0
468c2ecf20Sopenharmony_ci#define irc_elevel	1
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic struct {
498c2ecf20Sopenharmony_ci	unsigned char level;
508c2ecf20Sopenharmony_ci	unsigned char mode;
518c2ecf20Sopenharmony_ci} tx4939irq[TX4939_NUM_IR] __read_mostly;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void tx4939_irq_unmask(struct irq_data *d)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	unsigned int irq_nr = d->irq - TXX9_IRQ_BASE;
568c2ecf20Sopenharmony_ci	u32 __iomem *lvlp;
578c2ecf20Sopenharmony_ci	int ofs;
588c2ecf20Sopenharmony_ci	if (irq_nr < 32) {
598c2ecf20Sopenharmony_ci		irq_nr--;
608c2ecf20Sopenharmony_ci		lvlp = &tx4939_ircptr->lvl[(irq_nr % 16) / 2].r;
618c2ecf20Sopenharmony_ci	} else {
628c2ecf20Sopenharmony_ci		irq_nr -= 32;
638c2ecf20Sopenharmony_ci		lvlp = &tx4939_ircptr->lvl[8 + (irq_nr % 16) / 2].r;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci	ofs = (irq_nr & 16) + (irq_nr & 1) * 8;
668c2ecf20Sopenharmony_ci	__raw_writel((__raw_readl(lvlp) & ~(0xff << ofs))
678c2ecf20Sopenharmony_ci		     | (tx4939irq[irq_nr].level << ofs),
688c2ecf20Sopenharmony_ci		     lvlp);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic inline void tx4939_irq_mask(struct irq_data *d)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	unsigned int irq_nr = d->irq - TXX9_IRQ_BASE;
748c2ecf20Sopenharmony_ci	u32 __iomem *lvlp;
758c2ecf20Sopenharmony_ci	int ofs;
768c2ecf20Sopenharmony_ci	if (irq_nr < 32) {
778c2ecf20Sopenharmony_ci		irq_nr--;
788c2ecf20Sopenharmony_ci		lvlp = &tx4939_ircptr->lvl[(irq_nr % 16) / 2].r;
798c2ecf20Sopenharmony_ci	} else {
808c2ecf20Sopenharmony_ci		irq_nr -= 32;
818c2ecf20Sopenharmony_ci		lvlp = &tx4939_ircptr->lvl[8 + (irq_nr % 16) / 2].r;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci	ofs = (irq_nr & 16) + (irq_nr & 1) * 8;
848c2ecf20Sopenharmony_ci	__raw_writel((__raw_readl(lvlp) & ~(0xff << ofs))
858c2ecf20Sopenharmony_ci		     | (irc_dlevel << ofs),
868c2ecf20Sopenharmony_ci		     lvlp);
878c2ecf20Sopenharmony_ci	mmiowb();
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void tx4939_irq_mask_ack(struct irq_data *d)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	unsigned int irq_nr = d->irq - TXX9_IRQ_BASE;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	tx4939_irq_mask(d);
958c2ecf20Sopenharmony_ci	if (TXx9_IRCR_EDGE(tx4939irq[irq_nr].mode)) {
968c2ecf20Sopenharmony_ci		irq_nr--;
978c2ecf20Sopenharmony_ci		/* clear edge detection */
988c2ecf20Sopenharmony_ci		__raw_writel((TXx9_IRSCR_EIClrE | (irq_nr & 0xf))
998c2ecf20Sopenharmony_ci			     << (irq_nr & 0x10),
1008c2ecf20Sopenharmony_ci			     &tx4939_ircptr->edc.r);
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int tx4939_irq_set_type(struct irq_data *d, unsigned int flow_type)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	unsigned int irq_nr = d->irq - TXX9_IRQ_BASE;
1078c2ecf20Sopenharmony_ci	u32 cr;
1088c2ecf20Sopenharmony_ci	u32 __iomem *crp;
1098c2ecf20Sopenharmony_ci	int ofs;
1108c2ecf20Sopenharmony_ci	int mode;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (flow_type & IRQF_TRIGGER_PROBE)
1138c2ecf20Sopenharmony_ci		return 0;
1148c2ecf20Sopenharmony_ci	switch (flow_type & IRQF_TRIGGER_MASK) {
1158c2ecf20Sopenharmony_ci	case IRQF_TRIGGER_RISING:
1168c2ecf20Sopenharmony_ci		mode = TXx9_IRCR_UP;
1178c2ecf20Sopenharmony_ci		break;
1188c2ecf20Sopenharmony_ci	case IRQF_TRIGGER_FALLING:
1198c2ecf20Sopenharmony_ci		mode = TXx9_IRCR_DOWN;
1208c2ecf20Sopenharmony_ci		break;
1218c2ecf20Sopenharmony_ci	case IRQF_TRIGGER_HIGH:
1228c2ecf20Sopenharmony_ci		mode = TXx9_IRCR_HIGH;
1238c2ecf20Sopenharmony_ci		break;
1248c2ecf20Sopenharmony_ci	case IRQF_TRIGGER_LOW:
1258c2ecf20Sopenharmony_ci		mode = TXx9_IRCR_LOW;
1268c2ecf20Sopenharmony_ci		break;
1278c2ecf20Sopenharmony_ci	default:
1288c2ecf20Sopenharmony_ci		return -EINVAL;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci	if (irq_nr < 32) {
1318c2ecf20Sopenharmony_ci		irq_nr--;
1328c2ecf20Sopenharmony_ci		crp = &tx4939_ircptr->dm[(irq_nr & 8) >> 3].r;
1338c2ecf20Sopenharmony_ci	} else {
1348c2ecf20Sopenharmony_ci		irq_nr -= 32;
1358c2ecf20Sopenharmony_ci		crp = &tx4939_ircptr->dm2[((irq_nr & 8) >> 3)].r;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci	ofs = (((irq_nr & 16) >> 1) | (irq_nr & (8 - 1))) * 2;
1388c2ecf20Sopenharmony_ci	cr = __raw_readl(crp);
1398c2ecf20Sopenharmony_ci	cr &= ~(0x3 << ofs);
1408c2ecf20Sopenharmony_ci	cr |= (mode & 0x3) << ofs;
1418c2ecf20Sopenharmony_ci	__raw_writel(cr, crp);
1428c2ecf20Sopenharmony_ci	tx4939irq[irq_nr].mode = mode;
1438c2ecf20Sopenharmony_ci	return 0;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic struct irq_chip tx4939_irq_chip = {
1478c2ecf20Sopenharmony_ci	.name		= "TX4939",
1488c2ecf20Sopenharmony_ci	.irq_ack	= tx4939_irq_mask_ack,
1498c2ecf20Sopenharmony_ci	.irq_mask	= tx4939_irq_mask,
1508c2ecf20Sopenharmony_ci	.irq_mask_ack	= tx4939_irq_mask_ack,
1518c2ecf20Sopenharmony_ci	.irq_unmask	= tx4939_irq_unmask,
1528c2ecf20Sopenharmony_ci	.irq_set_type	= tx4939_irq_set_type,
1538c2ecf20Sopenharmony_ci};
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int tx4939_irq_set_pri(int irc_irq, int new_pri)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	int old_pri;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if ((unsigned int)irc_irq >= TX4939_NUM_IR)
1608c2ecf20Sopenharmony_ci		return 0;
1618c2ecf20Sopenharmony_ci	old_pri = tx4939irq[irc_irq].level;
1628c2ecf20Sopenharmony_ci	tx4939irq[irc_irq].level = new_pri;
1638c2ecf20Sopenharmony_ci	return old_pri;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_civoid __init tx4939_irq_init(void)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	int i;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	mips_cpu_irq_init();
1718c2ecf20Sopenharmony_ci	/* disable interrupt control */
1728c2ecf20Sopenharmony_ci	__raw_writel(0, &tx4939_ircptr->den.r);
1738c2ecf20Sopenharmony_ci	__raw_writel(0, &tx4939_ircptr->maskint.r);
1748c2ecf20Sopenharmony_ci	__raw_writel(0, &tx4939_ircptr->maskext.r);
1758c2ecf20Sopenharmony_ci	/* irq_base + 0 is not used */
1768c2ecf20Sopenharmony_ci	for (i = 1; i < TX4939_NUM_IR; i++) {
1778c2ecf20Sopenharmony_ci		tx4939irq[i].level = 4; /* middle level */
1788c2ecf20Sopenharmony_ci		tx4939irq[i].mode = TXx9_IRCR_LOW;
1798c2ecf20Sopenharmony_ci		irq_set_chip_and_handler(TXX9_IRQ_BASE + i, &tx4939_irq_chip,
1808c2ecf20Sopenharmony_ci					 handle_level_irq);
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* mask all IRC interrupts */
1848c2ecf20Sopenharmony_ci	__raw_writel(0, &tx4939_ircptr->msk.r);
1858c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++)
1868c2ecf20Sopenharmony_ci		__raw_writel(0, &tx4939_ircptr->lvl[i].r);
1878c2ecf20Sopenharmony_ci	/* setup IRC interrupt mode (Low Active) */
1888c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++)
1898c2ecf20Sopenharmony_ci		__raw_writel(0, &tx4939_ircptr->dm[i].r);
1908c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++)
1918c2ecf20Sopenharmony_ci		__raw_writel(0, &tx4939_ircptr->dm2[i].r);
1928c2ecf20Sopenharmony_ci	/* enable interrupt control */
1938c2ecf20Sopenharmony_ci	__raw_writel(TXx9_IRCER_ICE, &tx4939_ircptr->den.r);
1948c2ecf20Sopenharmony_ci	__raw_writel(irc_elevel, &tx4939_ircptr->msk.r);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	irq_set_chained_handler(MIPS_CPU_IRQ_BASE + TX4939_IRC_INT,
1978c2ecf20Sopenharmony_ci				handle_simple_irq);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	/* raise priority for errors, timers, sio */
2008c2ecf20Sopenharmony_ci	tx4939_irq_set_pri(TX4939_IR_WTOERR, 7);
2018c2ecf20Sopenharmony_ci	tx4939_irq_set_pri(TX4939_IR_PCIERR, 7);
2028c2ecf20Sopenharmony_ci	tx4939_irq_set_pri(TX4939_IR_PCIPME, 7);
2038c2ecf20Sopenharmony_ci	for (i = 0; i < TX4939_NUM_IR_TMR; i++)
2048c2ecf20Sopenharmony_ci		tx4939_irq_set_pri(TX4939_IR_TMR(i), 6);
2058c2ecf20Sopenharmony_ci	for (i = 0; i < TX4939_NUM_IR_SIO; i++)
2068c2ecf20Sopenharmony_ci		tx4939_irq_set_pri(TX4939_IR_SIO(i), 5);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ciint tx4939_irq(void)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	u32 csr = __raw_readl(&tx4939_ircptr->cs.r);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (likely(!(csr & TXx9_IRCSR_IF)))
2148c2ecf20Sopenharmony_ci		return TXX9_IRQ_BASE + (csr & (TX4939_NUM_IR - 1));
2158c2ecf20Sopenharmony_ci	return -1;
2168c2ecf20Sopenharmony_ci}
217