From c6a09196bab3bc9e515b713193d61e3e87c720f7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 5 Jan 2006 14:35:42 +0000 Subject: [PATCH] Add sh_bus_type probe and remove methods Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- arch/sh/kernel/cpu/bus.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'arch/sh/kernel/cpu') diff --git a/arch/sh/kernel/cpu/bus.c b/arch/sh/kernel/cpu/bus.c index d4fee2a79373..3278d234bb1b 100644 --- a/arch/sh/kernel/cpu/bus.c +++ b/arch/sh/kernel/cpu/bus.c @@ -53,21 +53,6 @@ static int sh_bus_resume(struct device *dev) return 0; } -static struct device sh_bus_devices[SH_NR_BUSES] = { - { - .bus_id = SH_BUS_NAME_VIRT, - }, -}; - -struct bus_type sh_bus_types[SH_NR_BUSES] = { - { - .name = SH_BUS_NAME_VIRT, - .match = sh_bus_match, - .suspend = sh_bus_suspend, - .resume = sh_bus_resume, - }, -}; - static int sh_device_probe(struct device *dev) { struct sh_dev *shdev = to_sh_dev(dev); @@ -90,6 +75,23 @@ static int sh_device_remove(struct device *dev) return 0; } +static struct device sh_bus_devices[SH_NR_BUSES] = { + { + .bus_id = SH_BUS_NAME_VIRT, + }, +}; + +struct bus_type sh_bus_types[SH_NR_BUSES] = { + { + .name = SH_BUS_NAME_VIRT, + .match = sh_bus_match, + .probe = sh_bus_probe, + .remove = sh_bus_remove, + .suspend = sh_bus_suspend, + .resume = sh_bus_resume, + }, +}; + int sh_device_register(struct sh_dev *dev) { if (!dev) @@ -133,8 +135,6 @@ int sh_driver_register(struct sh_driver *drv) return -EINVAL; } - drv->drv.probe = sh_device_probe; - drv->drv.remove = sh_device_remove; drv->drv.bus = &sh_bus_types[drv->bus_id]; return driver_register(&drv->drv); -- cgit v1.2.2 From 0d831770b154a057562236e8cf50905c8f1ae1b0 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 16 Jan 2006 22:14:09 -0800 Subject: [PATCH] sh: DMA updates This extends the current SH DMA API somewhat to support a proper virtual channel abstraction, and also works to represent this through the driver model by giving each DMAC its own platform device. There's also a few other minor changes to support a few new CPU subtypes, and make TEI generation for the SH DMAC configurable. Signed-off-by: Paul Mundt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sh/kernel/cpu/bus.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/sh/kernel/cpu') diff --git a/arch/sh/kernel/cpu/bus.c b/arch/sh/kernel/cpu/bus.c index 3278d234bb1b..fc6c4bd40c65 100644 --- a/arch/sh/kernel/cpu/bus.c +++ b/arch/sh/kernel/cpu/bus.c @@ -109,6 +109,8 @@ int sh_device_register(struct sh_dev *dev) /* This is needed for USB OHCI to work */ if (dev->dma_mask) dev->dev.dma_mask = dev->dma_mask; + if (dev->coherent_dma_mask) + dev->dev.coherent_dma_mask = dev->coherent_dma_mask; snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s%u", dev->name, dev->dev_id); -- cgit v1.2.2 From bf3a00f88c926635932c91afd90b4a0907dfbe78 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 16 Jan 2006 22:14:14 -0800 Subject: [PATCH] sh: IRQ handler updates This moves the various IRQ controller drivers into a new subdirectory, and also extends the INTC2 IRQ handler to also deal with SH7760 and SH7780 interrupts, rather than just ST-40. The old CONFIG_SH_GENERIC has also been removed from the IRQ definitions, as new ports are expected to be based off of CONFIG_SH_UNKNOWN. Since there are plenty of incompatible machvecs, CONFIG_SH_GENERIC doesn't make sense anymore. Signed-off-by: Paul Mundt Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sh/kernel/cpu/Makefile | 9 +- arch/sh/kernel/cpu/irq/Makefile | 7 + arch/sh/kernel/cpu/irq/imask.c | 110 ++++++++++++ arch/sh/kernel/cpu/irq/intc2.c | 284 +++++++++++++++++++++++++++++++ arch/sh/kernel/cpu/irq/ipr.c | 206 ++++++++++++++++++++++ arch/sh/kernel/cpu/irq/pint.c | 169 ++++++++++++++++++ arch/sh/kernel/cpu/irq_imask.c | 116 ------------- arch/sh/kernel/cpu/irq_ipr.c | 339 ------------------------------------- arch/sh/kernel/cpu/sh4/irq_intc2.c | 222 ------------------------ 9 files changed, 779 insertions(+), 683 deletions(-) create mode 100644 arch/sh/kernel/cpu/irq/Makefile create mode 100644 arch/sh/kernel/cpu/irq/imask.c create mode 100644 arch/sh/kernel/cpu/irq/intc2.c create mode 100644 arch/sh/kernel/cpu/irq/ipr.c create mode 100644 arch/sh/kernel/cpu/irq/pint.c delete mode 100644 arch/sh/kernel/cpu/irq_imask.c delete mode 100644 arch/sh/kernel/cpu/irq_ipr.c delete mode 100644 arch/sh/kernel/cpu/sh4/irq_intc2.c (limited to 'arch/sh/kernel/cpu') diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile index cd43714df61a..5bfc33bec5d0 100644 --- a/arch/sh/kernel/cpu/Makefile +++ b/arch/sh/kernel/cpu/Makefile @@ -2,15 +2,12 @@ # Makefile for the Linux/SuperH CPU-specifc backends. # -obj-y := irq_ipr.o irq_imask.o init.o bus.o +obj-y += irq/ init.o bus.o clock.o obj-$(CONFIG_CPU_SH2) += sh2/ obj-$(CONFIG_CPU_SH3) += sh3/ obj-$(CONFIG_CPU_SH4) += sh4/ -obj-$(CONFIG_SH_RTC) += rtc.o +obj-$(CONFIG_SH_RTC) += rtc.o obj-$(CONFIG_UBC_WAKEUP) += ubc.o -obj-$(CONFIG_SH_ADC) += adc.o - -USE_STANDARD_AS_RULE := true - +obj-$(CONFIG_SH_ADC) += adc.o diff --git a/arch/sh/kernel/cpu/irq/Makefile b/arch/sh/kernel/cpu/irq/Makefile new file mode 100644 index 000000000000..e3cccea15e1d --- /dev/null +++ b/arch/sh/kernel/cpu/irq/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux/SuperH CPU-specifc IRQ handlers. +# +obj-y += ipr.o imask.o + +obj-$(CONFIG_CPU_HAS_PINT_IRQ) += pint.o +obj-$(CONFIG_CPU_HAS_INTC2_IRQ) += intc2.o diff --git a/arch/sh/kernel/cpu/irq/imask.c b/arch/sh/kernel/cpu/irq/imask.c new file mode 100644 index 000000000000..baed9a550d39 --- /dev/null +++ b/arch/sh/kernel/cpu/irq/imask.c @@ -0,0 +1,110 @@ +/* + * arch/sh/kernel/cpu/irq/imask.c + * + * Copyright (C) 1999, 2000 Niibe Yutaka + * + * Simple interrupt handling using IMASK of SR register. + * + */ +/* NOTE: Will not work on level 15 */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Bitmap of IRQ masked */ +static unsigned long imask_mask = 0x7fff; +static int interrupt_priority = 0; + +static void enable_imask_irq(unsigned int irq); +static void disable_imask_irq(unsigned int irq); +static void shutdown_imask_irq(unsigned int irq); +static void mask_and_ack_imask(unsigned int); +static void end_imask_irq(unsigned int irq); + +#define IMASK_PRIORITY 15 + +static unsigned int startup_imask_irq(unsigned int irq) +{ + /* Nothing to do */ + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type imask_irq_type = { + .typename = "SR.IMASK", + .startup = startup_imask_irq, + .shutdown = shutdown_imask_irq, + .enable = enable_imask_irq, + .disable = disable_imask_irq, + .ack = mask_and_ack_imask, + .end = end_imask_irq +}; + +void static inline set_interrupt_registers(int ip) +{ + unsigned long __dummy; + + asm volatile("ldc %2, r6_bank\n\t" + "stc sr, %0\n\t" + "and #0xf0, %0\n\t" + "shlr2 %0\n\t" + "cmp/eq #0x3c, %0\n\t" + "bt/s 1f ! CLI-ed\n\t" + " stc sr, %0\n\t" + "and %1, %0\n\t" + "or %2, %0\n\t" + "ldc %0, sr\n" + "1:" + : "=&z" (__dummy) + : "r" (~0xf0), "r" (ip << 4) + : "t"); +} + +static void disable_imask_irq(unsigned int irq) +{ + clear_bit(irq, &imask_mask); + if (interrupt_priority < IMASK_PRIORITY - irq) + interrupt_priority = IMASK_PRIORITY - irq; + + set_interrupt_registers(interrupt_priority); +} + +static void enable_imask_irq(unsigned int irq) +{ + set_bit(irq, &imask_mask); + interrupt_priority = IMASK_PRIORITY - ffz(imask_mask); + + set_interrupt_registers(interrupt_priority); +} + +static void mask_and_ack_imask(unsigned int irq) +{ + disable_imask_irq(irq); +} + +static void end_imask_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_imask_irq(irq); +} + +static void shutdown_imask_irq(unsigned int irq) +{ + /* Nothing to do */ +} + +void make_imask_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + irq_desc[irq].handler = &imask_irq_type; + enable_irq(irq); +} diff --git a/arch/sh/kernel/cpu/irq/intc2.c b/arch/sh/kernel/cpu/irq/intc2.c new file mode 100644 index 000000000000..06e8afab32e4 --- /dev/null +++ b/arch/sh/kernel/cpu/irq/intc2.c @@ -0,0 +1,284 @@ +/* + * Interrupt handling for INTC2-based IRQ. + * + * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) + * Copyright (C) 2005, 2006 Paul Mundt (lethal@linux-sh.org) + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * These are the "new Hitachi style" interrupts, as present on the + * Hitachi 7751, the STM ST40 STB1, SH7760, and SH7780. + */ + +#include +#include +#include +#include +#include +#include + +struct intc2_data { + unsigned char msk_offset; + unsigned char msk_shift; + + int (*clear_irq) (int); +}; + +static struct intc2_data intc2_data[NR_INTC2_IRQS]; + +static void enable_intc2_irq(unsigned int irq); +static void disable_intc2_irq(unsigned int irq); + +/* shutdown is same as "disable" */ +#define shutdown_intc2_irq disable_intc2_irq + +static void mask_and_ack_intc2(unsigned int); +static void end_intc2_irq(unsigned int irq); + +static unsigned int startup_intc2_irq(unsigned int irq) +{ + enable_intc2_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type intc2_irq_type = { + .typename = "INTC2-IRQ", + .startup = startup_intc2_irq, + .shutdown = shutdown_intc2_irq, + .enable = enable_intc2_irq, + .disable = disable_intc2_irq, + .ack = mask_and_ack_intc2, + .end = end_intc2_irq +}; + +static void disable_intc2_irq(unsigned int irq) +{ + int irq_offset = irq - INTC2_FIRST_IRQ; + int msk_shift, msk_offset; + + /* Sanity check */ + if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS)) + return; + + msk_shift = intc2_data[irq_offset].msk_shift; + msk_offset = intc2_data[irq_offset].msk_offset; + + ctrl_outl(1 << msk_shift, + INTC2_BASE + INTC2_INTMSK_OFFSET + msk_offset); +} + +static void enable_intc2_irq(unsigned int irq) +{ + int irq_offset = irq - INTC2_FIRST_IRQ; + int msk_shift, msk_offset; + + /* Sanity check */ + if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS)) + return; + + msk_shift = intc2_data[irq_offset].msk_shift; + msk_offset = intc2_data[irq_offset].msk_offset; + + ctrl_outl(1 << msk_shift, + INTC2_BASE + INTC2_INTMSKCLR_OFFSET + msk_offset); +} + +static void mask_and_ack_intc2(unsigned int irq) +{ + disable_intc2_irq(irq); +} + +static void end_intc2_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_intc2_irq(irq); + + if (unlikely(intc2_data[irq - INTC2_FIRST_IRQ].clear_irq)) + intc2_data[irq - INTC2_FIRST_IRQ].clear_irq(irq); +} + +/* + * Setup an INTC2 style interrupt. + * NOTE: Unlike IPR interrupts, parameters are not shifted by this code, + * allowing the use of the numbers straight out of the datasheet. + * For example: + * PIO1 which is INTPRI00[19,16] and INTMSK00[13] + * would be: ^ ^ ^ ^ + * | | | | + * make_intc2_irq(84, 0, 16, 0, 13); + */ +void make_intc2_irq(unsigned int irq, + unsigned int ipr_offset, unsigned int ipr_shift, + unsigned int msk_offset, unsigned int msk_shift, + unsigned int priority) +{ + int irq_offset = irq - INTC2_FIRST_IRQ; + unsigned int flags; + unsigned long ipr; + + if (unlikely(irq_offset < 0 || irq_offset >= NR_INTC2_IRQS)) + return; + + disable_irq_nosync(irq); + + /* Fill the data we need */ + intc2_data[irq_offset].msk_offset = msk_offset; + intc2_data[irq_offset].msk_shift = msk_shift; + intc2_data[irq_offset].clear_irq = NULL; + + /* Set the priority level */ + local_irq_save(flags); + + ipr = ctrl_inl(INTC2_BASE + INTC2_INTPRI_OFFSET + ipr_offset); + ipr &= ~(0xf << ipr_shift); + ipr |= priority << ipr_shift; + ctrl_outl(ipr, INTC2_BASE + INTC2_INTPRI_OFFSET + ipr_offset); + + local_irq_restore(flags); + + irq_desc[irq].handler = &intc2_irq_type; + + disable_intc2_irq(irq); +} + +static struct intc2_init { + unsigned short irq; + unsigned char ipr_offset, ipr_shift; + unsigned char msk_offset, msk_shift; + unsigned char priority; +} intc2_init_data[] __initdata = { +#if defined(CONFIG_CPU_SUBTYPE_ST40) + {64, 0, 0, 0, 0, 13}, /* PCI serr */ + {65, 0, 4, 0, 1, 13}, /* PCI err */ + {66, 0, 4, 0, 2, 13}, /* PCI ad */ + {67, 0, 4, 0, 3, 13}, /* PCI pwd down */ + {72, 0, 8, 0, 5, 13}, /* DMAC INT0 */ + {73, 0, 8, 0, 6, 13}, /* DMAC INT1 */ + {74, 0, 8, 0, 7, 13}, /* DMAC INT2 */ + {75, 0, 8, 0, 8, 13}, /* DMAC INT3 */ + {76, 0, 8, 0, 9, 13}, /* DMAC INT4 */ + {78, 0, 8, 0, 11, 13}, /* DMAC ERR */ + {80, 0, 12, 0, 12, 13}, /* PIO0 */ + {84, 0, 16, 0, 13, 13}, /* PIO1 */ + {88, 0, 20, 0, 14, 13}, /* PIO2 */ + {112, 4, 0, 4, 0, 13}, /* Mailbox */ + #ifdef CONFIG_CPU_SUBTYPE_ST40GX1 + {116, 4, 4, 4, 4, 13}, /* SSC0 */ + {120, 4, 8, 4, 8, 13}, /* IR Blaster */ + {124, 4, 12, 4, 12, 13}, /* USB host */ + {128, 4, 16, 4, 16, 13}, /* Video processor BLITTER */ + {132, 4, 20, 4, 20, 13}, /* UART0 */ + {134, 4, 20, 4, 22, 13}, /* UART2 */ + {136, 4, 24, 4, 24, 13}, /* IO_PIO0 */ + {140, 4, 28, 4, 28, 13}, /* EMPI */ + {144, 8, 0, 8, 0, 13}, /* MAFE */ + {148, 8, 4, 8, 4, 13}, /* PWM */ + {152, 8, 8, 8, 8, 13}, /* SSC1 */ + {156, 8, 12, 8, 12, 13}, /* IO_PIO1 */ + {160, 8, 16, 8, 16, 13}, /* USB target */ + {164, 8, 20, 8, 20, 13}, /* UART1 */ + {168, 8, 24, 8, 24, 13}, /* Teletext */ + {172, 8, 28, 8, 28, 13}, /* VideoSync VTG */ + {173, 8, 28, 8, 29, 13}, /* VideoSync DVP0 */ + {174, 8, 28, 8, 30, 13}, /* VideoSync DVP1 */ +#endif +#elif defined(CONFIG_CPU_SUBTYPE_SH7760) +/* + * SH7760 INTC2-Style interrupts, vectors IRQ48-111 INTEVT 0x800-0xFE0 + */ + /* INTPRIO0 | INTMSK0 */ + {48, 0, 28, 0, 31, 3}, /* IRQ 4 */ + {49, 0, 24, 0, 30, 3}, /* IRQ 3 */ + {50, 0, 20, 0, 29, 3}, /* IRQ 2 */ + {51, 0, 16, 0, 28, 3}, /* IRQ 1 */ + /* 52-55 (INTEVT 0x880-0x8E0) unused/reserved */ + /* INTPRIO4 | INTMSK0 */ + {56, 4, 28, 0, 25, 3}, /* HCAN2_CHAN0 */ + {57, 4, 24, 0, 24, 3}, /* HCAN2_CHAN1 */ + {58, 4, 20, 0, 23, 3}, /* I2S_CHAN0 */ + {59, 4, 16, 0, 22, 3}, /* I2S_CHAN1 */ + {60, 4, 12, 0, 21, 3}, /* AC97_CHAN0 */ + {61, 4, 8, 0, 20, 3}, /* AC97_CHAN1 */ + {62, 4, 4, 0, 19, 3}, /* I2C_CHAN0 */ + {63, 4, 0, 0, 18, 3}, /* I2C_CHAN1 */ + /* INTPRIO8 | INTMSK0 */ + {52, 8, 16, 0, 11, 3}, /* SCIF0_ERI_IRQ */ + {53, 8, 16, 0, 10, 3}, /* SCIF0_RXI_IRQ */ + {54, 8, 16, 0, 9, 3}, /* SCIF0_BRI_IRQ */ + {55, 8, 16, 0, 8, 3}, /* SCIF0_TXI_IRQ */ + {64, 8, 28, 0, 17, 3}, /* USBHI_IRQ */ + {65, 8, 24, 0, 16, 3}, /* LCDC */ + /* 66, 67 unused */ + {68, 8, 20, 0, 14, 13}, /* DMABRGI0_IRQ */ + {69, 8, 20, 0, 13, 13}, /* DMABRGI1_IRQ */ + {70, 8, 20, 0, 12, 13}, /* DMABRGI2_IRQ */ + /* 71 unused */ + {72, 8, 12, 0, 7, 3}, /* SCIF1_ERI_IRQ */ + {73, 8, 12, 0, 6, 3}, /* SCIF1_RXI_IRQ */ + {74, 8, 12, 0, 5, 3}, /* SCIF1_BRI_IRQ */ + {75, 8, 12, 0, 4, 3}, /* SCIF1_TXI_IRQ */ + {76, 8, 8, 0, 3, 3}, /* SCIF2_ERI_IRQ */ + {77, 8, 8, 0, 2, 3}, /* SCIF2_RXI_IRQ */ + {78, 8, 8, 0, 1, 3}, /* SCIF2_BRI_IRQ */ + {79, 8, 8, 0, 0, 3}, /* SCIF2_TXI_IRQ */ + /* | INTMSK4 */ + {80, 8, 4, 4, 23, 3}, /* SIM_ERI */ + {81, 8, 4, 4, 22, 3}, /* SIM_RXI */ + {82, 8, 4, 4, 21, 3}, /* SIM_TXI */ + {83, 8, 4, 4, 20, 3}, /* SIM_TEI */ + {84, 8, 0, 4, 19, 3}, /* HSPII */ + /* INTPRIOC | INTMSK4 */ + /* 85-87 unused/reserved */ + {88, 12, 20, 4, 18, 3}, /* MMCI0 */ + {89, 12, 20, 4, 17, 3}, /* MMCI1 */ + {90, 12, 20, 4, 16, 3}, /* MMCI2 */ + {91, 12, 20, 4, 15, 3}, /* MMCI3 */ + {92, 12, 12, 4, 6, 3}, /* MFI (unsure, bug? in my 7760 manual*/ + /* 93-107 reserved/undocumented */ + {108,12, 4, 4, 1, 3}, /* ADC */ + {109,12, 0, 4, 0, 3}, /* CMTI */ + /* 110-111 reserved/unused */ +#elif defined(CONFIG_CPU_SUBTYPE_SH7780) + { TIMER_IRQ, 0, 24, 0, INTC_TMU0_MSK, 2}, +#ifdef CONFIG_SH_RTC + { RTC_IRQ, 4, 0, 0, INTC_RTC_MSK, TIMER_PRIORITY }, +#endif + { SCIF0_ERI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY }, + { SCIF0_RXI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY }, + { SCIF0_BRI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY }, + { SCIF0_TXI_IRQ, 8, 24, 0, INTC_SCIF0_MSK, SCIF0_PRIORITY }, + + { SCIF1_ERI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY }, + { SCIF1_RXI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY }, + { SCIF1_BRI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY }, + { SCIF1_TXI_IRQ, 8, 16, 0, INTC_SCIF1_MSK, SCIF1_PRIORITY }, + + { PCIC0_IRQ, 0x10, 8, 0, INTC_PCIC0_MSK, PCIC0_PRIORITY }, + { PCIC1_IRQ, 0x10, 0, 0, INTC_PCIC1_MSK, PCIC1_PRIORITY }, + { PCIC2_IRQ, 0x14, 24, 0, INTC_PCIC2_MSK, PCIC2_PRIORITY }, + { PCIC3_IRQ, 0x14, 16, 0, INTC_PCIC3_MSK, PCIC3_PRIORITY }, + { PCIC4_IRQ, 0x14, 8, 0, INTC_PCIC4_MSK, PCIC4_PRIORITY }, +#endif +}; + +void __init init_IRQ_intc2(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(intc2_init_data); i++) { + struct intc2_init *p = intc2_init_data + i; + make_intc2_irq(p->irq, p->ipr_offset, p->ipr_shift, + p-> msk_offset, p->msk_shift, p->priority); + } +} + +/* Adds a termination callback to the interrupt */ +void intc2_add_clear_irq(int irq, int (*fn)(int)) +{ + if (unlikely(irq < INTC2_FIRST_IRQ)) + return; + + intc2_data[irq - INTC2_FIRST_IRQ].clear_irq = fn; +} + diff --git a/arch/sh/kernel/cpu/irq/ipr.c b/arch/sh/kernel/cpu/irq/ipr.c new file mode 100644 index 000000000000..fdbd718ae5c6 --- /dev/null +++ b/arch/sh/kernel/cpu/irq/ipr.c @@ -0,0 +1,206 @@ +/* + * arch/sh/kernel/cpu/irq/ipr.c + * + * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi + * Copyright (C) 2000 Kazumoto Kojima + * Copyright (C) 2003 Takashi Kusuda + * + * Interrupt handling for IPR-based IRQ. + * + * Supported system: + * On-chip supporting modules (TMU, RTC, etc.). + * On-chip supporting modules for SH7709/SH7709A/SH7729/SH7300. + * Hitachi SolutionEngine external I/O: + * MS7709SE01, MS7709ASE01, and MS7750SE01 + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +struct ipr_data { + unsigned int addr; /* Address of Interrupt Priority Register */ + int shift; /* Shifts of the 16-bit data */ + int priority; /* The priority */ +}; +static struct ipr_data ipr_data[NR_IRQS]; + +static void enable_ipr_irq(unsigned int irq); +static void disable_ipr_irq(unsigned int irq); + +/* shutdown is same as "disable" */ +#define shutdown_ipr_irq disable_ipr_irq + +static void mask_and_ack_ipr(unsigned int); +static void end_ipr_irq(unsigned int irq); + +static unsigned int startup_ipr_irq(unsigned int irq) +{ + enable_ipr_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type ipr_irq_type = { + .typename = "IPR-IRQ", + .startup = startup_ipr_irq, + .shutdown = shutdown_ipr_irq, + .enable = enable_ipr_irq, + .disable = disable_ipr_irq, + .ack = mask_and_ack_ipr, + .end = end_ipr_irq +}; + +static void disable_ipr_irq(unsigned int irq) +{ + unsigned long val, flags; + unsigned int addr = ipr_data[irq].addr; + unsigned short mask = 0xffff ^ (0x0f << ipr_data[irq].shift); + + /* Set the priority in IPR to 0 */ + local_irq_save(flags); + val = ctrl_inw(addr); + val &= mask; + ctrl_outw(val, addr); + local_irq_restore(flags); +} + +static void enable_ipr_irq(unsigned int irq) +{ + unsigned long val, flags; + unsigned int addr = ipr_data[irq].addr; + int priority = ipr_data[irq].priority; + unsigned short value = (priority << ipr_data[irq].shift); + + /* Set priority in IPR back to original value */ + local_irq_save(flags); + val = ctrl_inw(addr); + val |= value; + ctrl_outw(val, addr); + local_irq_restore(flags); +} + +static void mask_and_ack_ipr(unsigned int irq) +{ + disable_ipr_irq(irq); + +#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ + defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) + /* This is needed when we use edge triggered setting */ + /* XXX: Is it really needed? */ + if (IRQ0_IRQ <= irq && irq <= IRQ5_IRQ) { + /* Clear external interrupt request */ + int a = ctrl_inb(INTC_IRR0); + a &= ~(1 << (irq - IRQ0_IRQ)); + ctrl_outb(a, INTC_IRR0); + } +#endif +} + +static void end_ipr_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_ipr_irq(irq); +} + +void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, + int priority, int maskpos) +{ + disable_irq_nosync(irq); + ipr_data[irq].addr = addr; + ipr_data[irq].shift = pos*4; /* POSition (0-3) x 4 means shift */ + ipr_data[irq].priority = priority; + + irq_desc[irq].handler = &ipr_irq_type; + disable_ipr_irq(irq); +} + +void __init init_IRQ(void) +{ +#ifndef CONFIG_CPU_SUBTYPE_SH7780 + make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY, 0); + make_ipr_irq(TIMER1_IRQ, TIMER1_IPR_ADDR, TIMER1_IPR_POS, TIMER1_PRIORITY, 0); +#if defined(CONFIG_SH_RTC) + make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY, 0); +#endif + +#ifdef SCI_ERI_IRQ + make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0); + make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0); + make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY, 0); +#endif + +#ifdef SCIF1_ERI_IRQ + make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0); + make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0); + make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0); + make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY, 0); +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7300) + make_ipr_irq(SCIF0_IRQ, SCIF0_IPR_ADDR, SCIF0_IPR_POS, SCIF0_PRIORITY, 0); + make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY, 0); + make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY, 0); + make_ipr_irq(VIO_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY, 0); +#endif + +#ifdef SCIF_ERI_IRQ + make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0); + make_ipr_irq(SCIF_RXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0); + make_ipr_irq(SCIF_BRI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0); + make_ipr_irq(SCIF_TXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY, 0); +#endif + +#ifdef IRDA_ERI_IRQ + make_ipr_irq(IRDA_ERI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0); + make_ipr_irq(IRDA_RXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0); + make_ipr_irq(IRDA_BRI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0); + make_ipr_irq(IRDA_TXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY, 0); +#endif + +#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ + defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) + /* + * Initialize the Interrupt Controller (INTC) + * registers to their power on values + */ + + /* + * Enable external irq (INTC IRQ mode). + * You should set corresponding bits of PFC to "00" + * to enable these interrupts. + */ + make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY, 0); + make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY, 0); + make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY, 0); + make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY, 0); + make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY, 0); + make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY, 0); +#endif +#endif + +#ifdef CONFIG_CPU_HAS_PINT_IRQ + init_IRQ_pint(); +#endif + +#ifdef CONFIG_CPU_HAS_INTC2_IRQ + init_IRQ_intc2(); +#endif + /* Perform the machine specific initialisation */ + if (sh_mv.mv_init_irq != NULL) + sh_mv.mv_init_irq(); +} + +#if !defined(CONFIG_CPU_HAS_PINT_IRQ) +int ipr_irq_demux(int irq) +{ + return irq; +} +#endif + +EXPORT_SYMBOL(make_ipr_irq); diff --git a/arch/sh/kernel/cpu/irq/pint.c b/arch/sh/kernel/cpu/irq/pint.c new file mode 100644 index 000000000000..95d6024fe1ae --- /dev/null +++ b/arch/sh/kernel/cpu/irq/pint.c @@ -0,0 +1,169 @@ +/* + * arch/sh/kernel/cpu/irq/pint.c - Interrupt handling for PINT-based IRQs. + * + * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi + * Copyright (C) 2000 Kazumoto Kojima + * Copyright (C) 2003 Takashi Kusuda + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include + +#include +#include +#include + +static unsigned char pint_map[256]; +static unsigned long portcr_mask; + +static void enable_pint_irq(unsigned int irq); +static void disable_pint_irq(unsigned int irq); + +/* shutdown is same as "disable" */ +#define shutdown_pint_irq disable_pint_irq + +static void mask_and_ack_pint(unsigned int); +static void end_pint_irq(unsigned int irq); + +static unsigned int startup_pint_irq(unsigned int irq) +{ + enable_pint_irq(irq); + return 0; /* never anything pending */ +} + +static struct hw_interrupt_type pint_irq_type = { + .typename = "PINT-IRQ", + .startup = startup_pint_irq, + .shutdown = shutdown_pint_irq, + .enable = enable_pint_irq, + .disable = disable_pint_irq, + .ack = mask_and_ack_pint, + .end = end_pint_irq +}; + +static void disable_pint_irq(unsigned int irq) +{ + unsigned long val, flags; + + local_irq_save(flags); + val = ctrl_inw(INTC_INTER); + val &= ~(1 << (irq - PINT_IRQ_BASE)); + ctrl_outw(val, INTC_INTER); /* disable PINTn */ + portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2); + local_irq_restore(flags); +} + +static void enable_pint_irq(unsigned int irq) +{ + unsigned long val, flags; + + local_irq_save(flags); + val = ctrl_inw(INTC_INTER); + val |= 1 << (irq - PINT_IRQ_BASE); + ctrl_outw(val, INTC_INTER); /* enable PINTn */ + portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2; + local_irq_restore(flags); +} + +static void mask_and_ack_pint(unsigned int irq) +{ + disable_pint_irq(irq); +} + +static void end_pint_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_pint_irq(irq); +} + +void make_pint_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + irq_desc[irq].handler = &pint_irq_type; + disable_pint_irq(irq); +} + +void __init init_IRQ_pint(void) +{ + int i; + + make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY); + make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY); + + enable_irq(PINT0_IRQ); + enable_irq(PINT8_IRQ); + + for(i = 0; i < 16; i++) + make_pint_irq(PINT_IRQ_BASE + i); + + for(i = 0; i < 256; i++) { + if (i & 1) + pint_map[i] = 0; + else if (i & 2) + pint_map[i] = 1; + else if (i & 4) + pint_map[i] = 2; + else if (i & 8) + pint_map[i] = 3; + else if (i & 0x10) + pint_map[i] = 4; + else if (i & 0x20) + pint_map[i] = 5; + else if (i & 0x40) + pint_map[i] = 6; + else if (i & 0x80) + pint_map[i] = 7; + } +} + +int ipr_irq_demux(int irq) +{ + unsigned long creg, dreg, d, sav; + + if (irq == PINT0_IRQ) { +#if defined(CONFIG_CPU_SUBTYPE_SH7707) + creg = PORT_PACR; + dreg = PORT_PADR; +#else + creg = PORT_PCCR; + dreg = PORT_PCDR; +#endif + sav = ctrl_inw(creg); + ctrl_outw(sav | portcr_mask, creg); + d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) & + ctrl_inw(INTC_INTER) & 0xff; + ctrl_outw(sav, creg); + + if (d == 0) + return irq; + + return PINT_IRQ_BASE + pint_map[d]; + } else if (irq == PINT8_IRQ) { +#if defined(CONFIG_CPU_SUBTYPE_SH7707) + creg = PORT_PBCR; + dreg = PORT_PBDR; +#else + creg = PORT_PFCR; + dreg = PORT_PFDR; +#endif + sav = ctrl_inw(creg); + ctrl_outw(sav | (portcr_mask >> 16), creg); + d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) & + (ctrl_inw(INTC_INTER) >> 8) & 0xff; + ctrl_outw(sav, creg); + + if (d == 0) + return irq; + + return PINT_IRQ_BASE + 8 + pint_map[d]; + } + + return irq; +} + diff --git a/arch/sh/kernel/cpu/irq_imask.c b/arch/sh/kernel/cpu/irq_imask.c deleted file mode 100644 index a963d00a971e..000000000000 --- a/arch/sh/kernel/cpu/irq_imask.c +++ /dev/null @@ -1,116 +0,0 @@ -/* $Id: irq_imask.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $ - * - * linux/arch/sh/kernel/irq_imask.c - * - * Copyright (C) 1999, 2000 Niibe Yutaka - * - * Simple interrupt handling using IMASK of SR register. - * - */ - -/* NOTE: Will not work on level 15 */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -/* Bitmap of IRQ masked */ -static unsigned long imask_mask = 0x7fff; -static int interrupt_priority = 0; - -static void enable_imask_irq(unsigned int irq); -static void disable_imask_irq(unsigned int irq); -static void shutdown_imask_irq(unsigned int irq); -static void mask_and_ack_imask(unsigned int); -static void end_imask_irq(unsigned int irq); - -#define IMASK_PRIORITY 15 - -static unsigned int startup_imask_irq(unsigned int irq) -{ - /* Nothing to do */ - return 0; /* never anything pending */ -} - -static struct hw_interrupt_type imask_irq_type = { - .typename = "SR.IMASK", - .startup = startup_imask_irq, - .shutdown = shutdown_imask_irq, - .enable = enable_imask_irq, - .disable = disable_imask_irq, - .ack = mask_and_ack_imask, - .end = end_imask_irq -}; - -void static inline set_interrupt_registers(int ip) -{ - unsigned long __dummy; - - asm volatile("ldc %2, r6_bank\n\t" - "stc sr, %0\n\t" - "and #0xf0, %0\n\t" - "shlr2 %0\n\t" - "cmp/eq #0x3c, %0\n\t" - "bt/s 1f ! CLI-ed\n\t" - " stc sr, %0\n\t" - "and %1, %0\n\t" - "or %2, %0\n\t" - "ldc %0, sr\n" - "1:" - : "=&z" (__dummy) - : "r" (~0xf0), "r" (ip << 4) - : "t"); -} - -static void disable_imask_irq(unsigned int irq) -{ - clear_bit(irq, &imask_mask); - if (interrupt_priority < IMASK_PRIORITY - irq) - interrupt_priority = IMASK_PRIORITY - irq; - - set_interrupt_registers(interrupt_priority); -} - -static void enable_imask_irq(unsigned int irq) -{ - set_bit(irq, &imask_mask); - interrupt_priority = IMASK_PRIORITY - ffz(imask_mask); - - set_interrupt_registers(interrupt_priority); -} - -static void mask_and_ack_imask(unsigned int irq) -{ - disable_imask_irq(irq); -} - -static void end_imask_irq(unsigned int irq) -{ - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) - enable_imask_irq(irq); -} - -static void shutdown_imask_irq(unsigned int irq) -{ - /* Nothing to do */ -} - -void make_imask_irq(unsigned int irq) -{ - disable_irq_nosync(irq); - irq_desc[irq].handler = &imask_irq_type; - enable_irq(irq); -} diff --git a/arch/sh/kernel/cpu/irq_ipr.c b/arch/sh/kernel/cpu/irq_ipr.c deleted file mode 100644 index 71f92096132b..000000000000 --- a/arch/sh/kernel/cpu/irq_ipr.c +++ /dev/null @@ -1,339 +0,0 @@ -/* $Id: irq_ipr.c,v 1.1.2.1 2002/11/17 10:53:43 mrbrown Exp $ - * - * linux/arch/sh/kernel/irq_ipr.c - * - * Copyright (C) 1999 Niibe Yutaka & Takeshi Yaegashi - * Copyright (C) 2000 Kazumoto Kojima - * Copyright (C) 2003 Takashi Kusuda - * - * Interrupt handling for IPR-based IRQ. - * - * Supported system: - * On-chip supporting modules (TMU, RTC, etc.). - * On-chip supporting modules for SH7709/SH7709A/SH7729/SH7300. - * Hitachi SolutionEngine external I/O: - * MS7709SE01, MS7709ASE01, and MS7750SE01 - * - */ - -#include -#include -#include -#include - -#include -#include -#include - -struct ipr_data { - unsigned int addr; /* Address of Interrupt Priority Register */ - int shift; /* Shifts of the 16-bit data */ - int priority; /* The priority */ -}; -static struct ipr_data ipr_data[NR_IRQS]; - -static void enable_ipr_irq(unsigned int irq); -static void disable_ipr_irq(unsigned int irq); - -/* shutdown is same as "disable" */ -#define shutdown_ipr_irq disable_ipr_irq - -static void mask_and_ack_ipr(unsigned int); -static void end_ipr_irq(unsigned int irq); - -static unsigned int startup_ipr_irq(unsigned int irq) -{ - enable_ipr_irq(irq); - return 0; /* never anything pending */ -} - -static struct hw_interrupt_type ipr_irq_type = { - .typename = "IPR-IRQ", - .startup = startup_ipr_irq, - .shutdown = shutdown_ipr_irq, - .enable = enable_ipr_irq, - .disable = disable_ipr_irq, - .ack = mask_and_ack_ipr, - .end = end_ipr_irq -}; - -static void disable_ipr_irq(unsigned int irq) -{ - unsigned long val, flags; - unsigned int addr = ipr_data[irq].addr; - unsigned short mask = 0xffff ^ (0x0f << ipr_data[irq].shift); - - /* Set the priority in IPR to 0 */ - local_irq_save(flags); - val = ctrl_inw(addr); - val &= mask; - ctrl_outw(val, addr); - local_irq_restore(flags); -} - -static void enable_ipr_irq(unsigned int irq) -{ - unsigned long val, flags; - unsigned int addr = ipr_data[irq].addr; - int priority = ipr_data[irq].priority; - unsigned short value = (priority << ipr_data[irq].shift); - - /* Set priority in IPR back to original value */ - local_irq_save(flags); - val = ctrl_inw(addr); - val |= value; - ctrl_outw(val, addr); - local_irq_restore(flags); -} - -static void mask_and_ack_ipr(unsigned int irq) -{ - disable_ipr_irq(irq); - -#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ - defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) - /* This is needed when we use edge triggered setting */ - /* XXX: Is it really needed? */ - if (IRQ0_IRQ <= irq && irq <= IRQ5_IRQ) { - /* Clear external interrupt request */ - int a = ctrl_inb(INTC_IRR0); - a &= ~(1 << (irq - IRQ0_IRQ)); - ctrl_outb(a, INTC_IRR0); - } -#endif -} - -static void end_ipr_irq(unsigned int irq) -{ - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) - enable_ipr_irq(irq); -} - -void make_ipr_irq(unsigned int irq, unsigned int addr, int pos, int priority) -{ - disable_irq_nosync(irq); - ipr_data[irq].addr = addr; - ipr_data[irq].shift = pos*4; /* POSition (0-3) x 4 means shift */ - ipr_data[irq].priority = priority; - - irq_desc[irq].handler = &ipr_irq_type; - disable_ipr_irq(irq); -} - -#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7707) || \ - defined(CONFIG_CPU_SUBTYPE_SH7709) -static unsigned char pint_map[256]; -static unsigned long portcr_mask = 0; - -static void enable_pint_irq(unsigned int irq); -static void disable_pint_irq(unsigned int irq); - -/* shutdown is same as "disable" */ -#define shutdown_pint_irq disable_pint_irq - -static void mask_and_ack_pint(unsigned int); -static void end_pint_irq(unsigned int irq); - -static unsigned int startup_pint_irq(unsigned int irq) -{ - enable_pint_irq(irq); - return 0; /* never anything pending */ -} - -static struct hw_interrupt_type pint_irq_type = { - .typename = "PINT-IRQ", - .startup = startup_pint_irq, - .shutdown = shutdown_pint_irq, - .enable = enable_pint_irq, - .disable = disable_pint_irq, - .ack = mask_and_ack_pint, - .end = end_pint_irq -}; - -static void disable_pint_irq(unsigned int irq) -{ - unsigned long val, flags; - - local_irq_save(flags); - val = ctrl_inw(INTC_INTER); - val &= ~(1 << (irq - PINT_IRQ_BASE)); - ctrl_outw(val, INTC_INTER); /* disable PINTn */ - portcr_mask &= ~(3 << (irq - PINT_IRQ_BASE)*2); - local_irq_restore(flags); -} - -static void enable_pint_irq(unsigned int irq) -{ - unsigned long val, flags; - - local_irq_save(flags); - val = ctrl_inw(INTC_INTER); - val |= 1 << (irq - PINT_IRQ_BASE); - ctrl_outw(val, INTC_INTER); /* enable PINTn */ - portcr_mask |= 3 << (irq - PINT_IRQ_BASE)*2; - local_irq_restore(flags); -} - -static void mask_and_ack_pint(unsigned int irq) -{ - disable_pint_irq(irq); -} - -static void end_pint_irq(unsigned int irq) -{ - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) - enable_pint_irq(irq); -} - -void make_pint_irq(unsigned int irq) -{ - disable_irq_nosync(irq); - irq_desc[irq].handler = &pint_irq_type; - disable_pint_irq(irq); -} -#endif - -void __init init_IRQ(void) -{ -#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ - defined(CONFIG_CPU_SUBTYPE_SH7707) || \ - defined(CONFIG_CPU_SUBTYPE_SH7709) - int i; -#endif - - make_ipr_irq(TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY); - make_ipr_irq(TIMER1_IRQ, TIMER1_IPR_ADDR, TIMER1_IPR_POS, TIMER1_PRIORITY); -#if defined(CONFIG_SH_RTC) - make_ipr_irq(RTC_IRQ, RTC_IPR_ADDR, RTC_IPR_POS, RTC_PRIORITY); -#endif - -#ifdef SCI_ERI_IRQ - make_ipr_irq(SCI_ERI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); - make_ipr_irq(SCI_RXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); - make_ipr_irq(SCI_TXI_IRQ, SCI_IPR_ADDR, SCI_IPR_POS, SCI_PRIORITY); -#endif - -#ifdef SCIF1_ERI_IRQ - make_ipr_irq(SCIF1_ERI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); - make_ipr_irq(SCIF1_RXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); - make_ipr_irq(SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); - make_ipr_irq(SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY); -#endif - -#if defined(CONFIG_CPU_SUBTYPE_SH7300) - make_ipr_irq(SCIF0_IRQ, SCIF0_IPR_ADDR, SCIF0_IPR_POS, SCIF0_PRIORITY); - make_ipr_irq(DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY); - make_ipr_irq(DMTE3_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY); - make_ipr_irq(VIO_IRQ, VIO_IPR_ADDR, VIO_IPR_POS, VIO_PRIORITY); -#endif - -#ifdef SCIF_ERI_IRQ - make_ipr_irq(SCIF_ERI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); - make_ipr_irq(SCIF_RXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); - make_ipr_irq(SCIF_BRI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); - make_ipr_irq(SCIF_TXI_IRQ, SCIF_IPR_ADDR, SCIF_IPR_POS, SCIF_PRIORITY); -#endif - -#ifdef IRDA_ERI_IRQ - make_ipr_irq(IRDA_ERI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); - make_ipr_irq(IRDA_RXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); - make_ipr_irq(IRDA_BRI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); - make_ipr_irq(IRDA_TXI_IRQ, IRDA_IPR_ADDR, IRDA_IPR_POS, IRDA_PRIORITY); -#endif - -#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ - defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) - /* - * Initialize the Interrupt Controller (INTC) - * registers to their power on values - */ - - /* - * Enable external irq (INTC IRQ mode). - * You should set corresponding bits of PFC to "00" - * to enable these interrupts. - */ - make_ipr_irq(IRQ0_IRQ, IRQ0_IPR_ADDR, IRQ0_IPR_POS, IRQ0_PRIORITY); - make_ipr_irq(IRQ1_IRQ, IRQ1_IPR_ADDR, IRQ1_IPR_POS, IRQ1_PRIORITY); - make_ipr_irq(IRQ2_IRQ, IRQ2_IPR_ADDR, IRQ2_IPR_POS, IRQ2_PRIORITY); - make_ipr_irq(IRQ3_IRQ, IRQ3_IPR_ADDR, IRQ3_IPR_POS, IRQ3_PRIORITY); - make_ipr_irq(IRQ4_IRQ, IRQ4_IPR_ADDR, IRQ4_IPR_POS, IRQ4_PRIORITY); - make_ipr_irq(IRQ5_IRQ, IRQ5_IPR_ADDR, IRQ5_IPR_POS, IRQ5_PRIORITY); -#if !defined(CONFIG_CPU_SUBTYPE_SH7300) - make_ipr_irq(PINT0_IRQ, PINT0_IPR_ADDR, PINT0_IPR_POS, PINT0_PRIORITY); - make_ipr_irq(PINT8_IRQ, PINT8_IPR_ADDR, PINT8_IPR_POS, PINT8_PRIORITY); - enable_ipr_irq(PINT0_IRQ); - enable_ipr_irq(PINT8_IRQ); - - for(i = 0; i < 16; i++) - make_pint_irq(PINT_IRQ_BASE + i); - for(i = 0; i < 256; i++) - { - if(i & 1) pint_map[i] = 0; - else if(i & 2) pint_map[i] = 1; - else if(i & 4) pint_map[i] = 2; - else if(i & 8) pint_map[i] = 3; - else if(i & 0x10) pint_map[i] = 4; - else if(i & 0x20) pint_map[i] = 5; - else if(i & 0x40) pint_map[i] = 6; - else if(i & 0x80) pint_map[i] = 7; - } -#endif /* !CONFIG_CPU_SUBTYPE_SH7300 */ -#endif /* CONFIG_CPU_SUBTYPE_SH7707 || CONFIG_CPU_SUBTYPE_SH7709 || CONFIG_CPU_SUBTYPE_SH7300*/ - -#ifdef CONFIG_CPU_SUBTYPE_ST40 - init_IRQ_intc2(); -#endif - - /* Perform the machine specific initialisation */ - if (sh_mv.mv_init_irq != NULL) { - sh_mv.mv_init_irq(); - } -} -#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709) || \ - defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705) -int ipr_irq_demux(int irq) -{ -#if !defined(CONFIG_CPU_SUBTYPE_SH7300) - unsigned long creg, dreg, d, sav; - - if(irq == PINT0_IRQ) - { -#if defined(CONFIG_CPU_SUBTYPE_SH7707) - creg = PORT_PACR; - dreg = PORT_PADR; -#else - creg = PORT_PCCR; - dreg = PORT_PCDR; -#endif - sav = ctrl_inw(creg); - ctrl_outw(sav | portcr_mask, creg); - d = (~ctrl_inb(dreg) ^ ctrl_inw(INTC_ICR2)) & ctrl_inw(INTC_INTER) & 0xff; - ctrl_outw(sav, creg); - if(d == 0) return irq; - return PINT_IRQ_BASE + pint_map[d]; - } - else if(irq == PINT8_IRQ) - { -#if defined(CONFIG_CPU_SUBTYPE_SH7707) - creg = PORT_PBCR; - dreg = PORT_PBDR; -#else - creg = PORT_PFCR; - dreg = PORT_PFDR; -#endif - sav = ctrl_inw(creg); - ctrl_outw(sav | (portcr_mask >> 16), creg); - d = (~ctrl_inb(dreg) ^ (ctrl_inw(INTC_ICR2) >> 8)) & (ctrl_inw(INTC_INTER) >> 8) & 0xff; - ctrl_outw(sav, creg); - if(d == 0) return irq; - return PINT_IRQ_BASE + 8 + pint_map[d]; - } -#endif - return irq; -} -#endif - -EXPORT_SYMBOL(make_ipr_irq); - diff --git a/arch/sh/kernel/cpu/sh4/irq_intc2.c b/arch/sh/kernel/cpu/sh4/irq_intc2.c deleted file mode 100644 index f6b16ba01932..000000000000 --- a/arch/sh/kernel/cpu/sh4/irq_intc2.c +++ /dev/null @@ -1,222 +0,0 @@ -/* - * linux/arch/sh/kernel/irq_intc2.c - * - * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) - * - * May be copied or modified under the terms of the GNU General Public - * License. See linux/COPYING for more information. - * - * Interrupt handling for INTC2-based IRQ. - * - * These are the "new Hitachi style" interrupts, as present on the - * Hitachi 7751 and the STM ST40 STB1. - */ - -#include -#include -#include - -#include -#include -#include - - -struct intc2_data { - unsigned char msk_offset; - unsigned char msk_shift; -#ifdef CONFIG_CPU_SUBTYPE_ST40 - int (*clear_irq) (int); -#endif -}; - - -static struct intc2_data intc2_data[NR_INTC2_IRQS]; - -static void enable_intc2_irq(unsigned int irq); -static void disable_intc2_irq(unsigned int irq); - -/* shutdown is same as "disable" */ -#define shutdown_intc2_irq disable_intc2_irq - -static void mask_and_ack_intc2(unsigned int); -static void end_intc2_irq(unsigned int irq); - -static unsigned int startup_intc2_irq(unsigned int irq) -{ - enable_intc2_irq(irq); - return 0; /* never anything pending */ -} - -static struct hw_interrupt_type intc2_irq_type = { - .typename = "INTC2-IRQ", - .startup = startup_intc2_irq, - .shutdown = shutdown_intc2_irq, - .enable = enable_intc2_irq, - .disable = disable_intc2_irq, - .ack = mask_and_ack_intc2, - .end = end_intc2_irq -}; - -static void disable_intc2_irq(unsigned int irq) -{ - int irq_offset = irq - INTC2_FIRST_IRQ; - int msk_shift, msk_offset; - - // Sanity check - if((irq_offset<0) || (irq_offset>=NR_INTC2_IRQS)) - return; - - msk_shift = intc2_data[irq_offset].msk_shift; - msk_offset = intc2_data[irq_offset].msk_offset; - - ctrl_outl(1<=NR_INTC2_IRQS)) - return; - - msk_shift = intc2_data[irq_offset].msk_shift; - msk_offset = intc2_data[irq_offset].msk_offset; - - ctrl_outl(1<=NR_INTC2_IRQS)) - return; - - disable_irq_nosync(irq); - - /* Fill the data we need */ - intc2_data[irq_offset].msk_offset = msk_offset; - intc2_data[irq_offset].msk_shift = msk_shift; -#ifdef CONFIG_CPU_SUBTYPE_ST40 - intc2_data[irq_offset].clear_irq = NULL; -#endif - - /* Set the priority level */ - local_irq_save(flags); - - ipr=ctrl_inl(INTC2_BASE+INTC2_INTPRI_OFFSET+ipr_offset); - ipr&=~(0xf<irq, p->ipr_offset, p->ipr_shift, - p-> msk_offset, p->msk_shift, 13); - } -} - -/* Adds a termination callback to the interrupt */ -void intc2_add_clear_irq(int irq, int (*fn)(int)) -{ - if (irq < INTC2_FIRST_IRQ) - return; - - intc2_data[irq - INTC2_FIRST_IRQ].clear_irq = fn; -} - -#endif /* CONFIG_CPU_SUBTYPE_ST40 */ -- cgit v1.2.2 From 36ddf31b689a8c11d424e43565d2aa440b77bbf4 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 16 Jan 2006 22:14:17 -0800 Subject: [PATCH] sh: Simplistic clock framework This adds a relatively simplistic clock framework for sh. The initial goal behind this is to clean up the arch/sh/kernel/time.c mess and to get the CPU subtype-specific frequency setting and calculation code moved somewhere more sensible. This only deals with the core clocks at the moment, though it's trivial for other drivers to define their own clocks as desired. Signed-off-by: Paul Mundt Cc: john stultz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sh/kernel/cpu/clock.c | 287 +++++++++++++++++++++++++++++++++ arch/sh/kernel/cpu/sh3/Makefile | 7 + arch/sh/kernel/cpu/sh3/clock-sh3.c | 89 ++++++++++ arch/sh/kernel/cpu/sh3/clock-sh7300.c | 78 +++++++++ arch/sh/kernel/cpu/sh3/clock-sh7705.c | 84 ++++++++++ arch/sh/kernel/cpu/sh3/clock-sh7709.c | 96 +++++++++++ arch/sh/kernel/cpu/sh4/Makefile | 11 +- arch/sh/kernel/cpu/sh4/clock-sh4-202.c | 179 ++++++++++++++++++++ arch/sh/kernel/cpu/sh4/clock-sh4.c | 80 +++++++++ arch/sh/kernel/cpu/sh4/clock-sh73180.c | 81 ++++++++++ arch/sh/kernel/cpu/sh4/clock-sh7770.c | 73 +++++++++ arch/sh/kernel/cpu/sh4/clock-sh7780.c | 126 +++++++++++++++ 12 files changed, 1190 insertions(+), 1 deletion(-) create mode 100644 arch/sh/kernel/cpu/clock.c create mode 100644 arch/sh/kernel/cpu/sh3/clock-sh3.c create mode 100644 arch/sh/kernel/cpu/sh3/clock-sh7300.c create mode 100644 arch/sh/kernel/cpu/sh3/clock-sh7705.c create mode 100644 arch/sh/kernel/cpu/sh3/clock-sh7709.c create mode 100644 arch/sh/kernel/cpu/sh4/clock-sh4-202.c create mode 100644 arch/sh/kernel/cpu/sh4/clock-sh4.c create mode 100644 arch/sh/kernel/cpu/sh4/clock-sh73180.c create mode 100644 arch/sh/kernel/cpu/sh4/clock-sh7770.c create mode 100644 arch/sh/kernel/cpu/sh4/clock-sh7780.c (limited to 'arch/sh/kernel/cpu') diff --git a/arch/sh/kernel/cpu/clock.c b/arch/sh/kernel/cpu/clock.c new file mode 100644 index 000000000000..989e7fdd524d --- /dev/null +++ b/arch/sh/kernel/cpu/clock.c @@ -0,0 +1,287 @@ +/* + * arch/sh/kernel/cpu/clock.c - SuperH clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * This clock framework is derived from the OMAP version by: + * + * Copyright (C) 2004 Nokia Corporation + * Written by Tuukka Tikkanen + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(clock_list); +static DEFINE_SPINLOCK(clock_lock); +static DECLARE_MUTEX(clock_list_sem); + +/* + * Each subtype is expected to define the init routines for these clocks, + * as each subtype (or processor family) will have these clocks at the + * very least. These are all provided through the CPG, which even some of + * the more quirky parts (such as ST40, SH4-202, etc.) still have. + * + * The processor-specific code is expected to register any additional + * clock sources that are of interest. + */ +static struct clk master_clk = { + .name = "master_clk", + .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, +#ifdef CONFIG_SH_PCLK_FREQ_BOOL + .rate = CONFIG_SH_PCLK_FREQ, +#endif +}; + +static struct clk module_clk = { + .name = "module_clk", + .parent = &master_clk, + .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, +}; + +static struct clk bus_clk = { + .name = "bus_clk", + .parent = &master_clk, + .flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES, +}; + +static struct clk cpu_clk = { + .name = "cpu_clk", + .parent = &master_clk, + .flags = CLK_ALWAYS_ENABLED, +}; + +/* + * The ordering of these clocks matters, do not change it. + */ +static struct clk *onchip_clocks[] = { + &master_clk, + &module_clk, + &bus_clk, + &cpu_clk, +}; + +static void propagate_rate(struct clk *clk) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &clock_list, node) { + if (likely(clkp->parent != clk)) + continue; + if (likely(clkp->ops && clkp->ops->recalc)) + clkp->ops->recalc(clkp); + } +} + +int __clk_enable(struct clk *clk) +{ + /* + * See if this is the first time we're enabling the clock, some + * clocks that are always enabled still require "special" + * initialization. This is especially true if the clock mode + * changes and the clock needs to hunt for the proper set of + * divisors to use before it can effectively recalc. + */ + if (unlikely(atomic_read(&clk->kref.refcount) == 1)) + if (clk->ops && clk->ops->init) + clk->ops->init(clk); + + if (clk->flags & CLK_ALWAYS_ENABLED) + return 0; + + if (likely(clk->ops && clk->ops->enable)) + clk->ops->enable(clk); + + kref_get(&clk->kref); + return 0; +} + +int clk_enable(struct clk *clk) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&clock_lock, flags); + ret = __clk_enable(clk); + spin_unlock_irqrestore(&clock_lock, flags); + + return ret; +} + +static void clk_kref_release(struct kref *kref) +{ + /* Nothing to do */ +} + +void __clk_disable(struct clk *clk) +{ + if (clk->flags & CLK_ALWAYS_ENABLED) + return; + + kref_put(&clk->kref, clk_kref_release); +} + +void clk_disable(struct clk *clk) +{ + unsigned long flags; + + spin_lock_irqsave(&clock_lock, flags); + __clk_disable(clk); + spin_unlock_irqrestore(&clock_lock, flags); +} + +int clk_register(struct clk *clk) +{ + down(&clock_list_sem); + + list_add(&clk->node, &clock_list); + kref_init(&clk->kref); + + up(&clock_list_sem); + + return 0; +} + +void clk_unregister(struct clk *clk) +{ + down(&clock_list_sem); + list_del(&clk->node); + up(&clock_list_sem); +} + +inline unsigned long clk_get_rate(struct clk *clk) +{ + return clk->rate; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + int ret = -EOPNOTSUPP; + + if (likely(clk->ops && clk->ops->set_rate)) { + unsigned long flags; + + spin_lock_irqsave(&clock_lock, flags); + ret = clk->ops->set_rate(clk, rate); + spin_unlock_irqrestore(&clock_lock, flags); + } + + if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) + propagate_rate(clk); + + return ret; +} + +void clk_recalc_rate(struct clk *clk) +{ + if (likely(clk->ops && clk->ops->recalc)) { + unsigned long flags; + + spin_lock_irqsave(&clock_lock, flags); + clk->ops->recalc(clk); + spin_unlock_irqrestore(&clock_lock, flags); + } + + if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) + propagate_rate(clk); +} + +struct clk *clk_get(const char *id) +{ + struct clk *p, *clk = ERR_PTR(-ENOENT); + + down(&clock_list_sem); + list_for_each_entry(p, &clock_list, node) { + if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { + clk = p; + break; + } + } + up(&clock_list_sem); + + return clk; +} + +void clk_put(struct clk *clk) +{ + if (clk && !IS_ERR(clk)) + module_put(clk->owner); +} + +void __init __attribute__ ((weak)) +arch_init_clk_ops(struct clk_ops **ops, int type) +{ +} + +int __init clk_init(void) +{ + int i, ret = 0; + + if (unlikely(!master_clk.rate)) + /* + * NOTE: This will break if the default divisor has been + * changed. + * + * No one should be changing the default on us however, + * expect that a sane value for CONFIG_SH_PCLK_FREQ will + * be defined in the event of a different divisor. + */ + master_clk.rate = get_timer_frequency() * 4; + + for (i = 0; i < ARRAY_SIZE(onchip_clocks); i++) { + struct clk *clk = onchip_clocks[i]; + + arch_init_clk_ops(&clk->ops, i); + ret |= clk_register(clk); + clk_enable(clk); + } + + /* Kick the child clocks.. */ + propagate_rate(&master_clk); + propagate_rate(&bus_clk); + + return ret; +} + +int show_clocks(struct seq_file *m) +{ + struct clk *clk; + + list_for_each_entry_reverse(clk, &clock_list, node) { + unsigned long rate = clk_get_rate(clk); + + /* + * Don't bother listing dummy clocks with no ancestry + * that only support enable and disable ops. + */ + if (unlikely(!rate && !clk->parent)) + continue; + + seq_printf(m, "%-12s\t: %ld.%02ldMHz\n", clk->name, + rate / 1000000, (rate % 1000000) / 10000); + } + + return 0; +} + +EXPORT_SYMBOL_GPL(clk_register); +EXPORT_SYMBOL_GPL(clk_unregister); +EXPORT_SYMBOL_GPL(clk_get); +EXPORT_SYMBOL_GPL(clk_put); +EXPORT_SYMBOL_GPL(clk_enable); +EXPORT_SYMBOL_GPL(clk_disable); +EXPORT_SYMBOL_GPL(__clk_enable); +EXPORT_SYMBOL_GPL(__clk_disable); +EXPORT_SYMBOL_GPL(clk_get_rate); +EXPORT_SYMBOL_GPL(clk_set_rate); +EXPORT_SYMBOL_GPL(clk_recalc_rate); diff --git a/arch/sh/kernel/cpu/sh3/Makefile b/arch/sh/kernel/cpu/sh3/Makefile index a64532e4dc63..b54dbb9a0c86 100644 --- a/arch/sh/kernel/cpu/sh3/Makefile +++ b/arch/sh/kernel/cpu/sh3/Makefile @@ -4,3 +4,10 @@ obj-y := ex.o probe.o +clock-$(CONFIG_CPU_SH3) := clock-sh3.o +clock-$(CONFIG_CPU_SUBTYPE_SH7300) := clock-sh7300.o +clock-$(CONFIG_CPU_SUBTYPE_SH7705) := clock-sh7705.o +clock-$(CONFIG_CPU_SUBTYPE_SH7709) := clock-sh7709.o + +obj-y += $(clock-y) + diff --git a/arch/sh/kernel/cpu/sh3/clock-sh3.c b/arch/sh/kernel/cpu/sh3/clock-sh3.c new file mode 100644 index 000000000000..c3c945958baf --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh3.c @@ -0,0 +1,89 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh3.c + * + * Generic SH-3 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * FRQCR parsing hacked out of arch/sh/kernel/time.c + * + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * Copyright (C) 2000 Philipp Rumpf + * Copyright (C) 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2002 M. R. Brown + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include + +static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; +static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 }; +static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; + +static void master_clk_init(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate *= pfc_divisors[idx]; +} + +static struct clk_ops sh3_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate = clk->parent->rate / pfc_divisors[idx]; +} + +static struct clk_ops sh3_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4); + + clk->rate = clk->parent->rate / stc_multipliers[idx]; +} + +static struct clk_ops sh3_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2); + + clk->rate = clk->parent->rate / ifc_divisors[idx]; +} + +static struct clk_ops sh3_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh3_clk_ops[] = { + &sh3_master_clk_ops, + &sh3_module_clk_ops, + &sh3_bus_clk_ops, + &sh3_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh3_clk_ops)) + *ops = sh3_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7300.c b/arch/sh/kernel/cpu/sh3/clock-sh7300.c new file mode 100644 index 000000000000..e804174b9625 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7300.c @@ -0,0 +1,78 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh7300.c + * + * SH7300 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * FRQCR parsing hacked out of arch/sh/kernel/time.c + * + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * Copyright (C) 2000 Philipp Rumpf + * Copyright (C) 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2002 M. R. Brown + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include + +static int md_table[] = { 1, 2, 3, 4, 6, 8, 12 }; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= md_table[ctrl_inw(FRQCR) & 0x0007]; +} + +static struct clk_ops sh7300_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) & 0x0007); + clk->rate = clk->parent->rate / md_table[idx]; +} + +static struct clk_ops sh7300_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) & 0x0700) >> 8; + clk->rate = clk->parent->rate / md_table[idx]; +} + +static struct clk_ops sh7300_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) & 0x0070) >> 4; + clk->rate = clk->parent->rate / md_table[idx]; +} + +static struct clk_ops sh7300_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh7300_clk_ops[] = { + &sh7300_master_clk_ops, + &sh7300_module_clk_ops, + &sh7300_bus_clk_ops, + &sh7300_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7300_clk_ops)) + *ops = sh7300_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7705.c b/arch/sh/kernel/cpu/sh3/clock-sh7705.c new file mode 100644 index 000000000000..dfdbf3277fd7 --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7705.c @@ -0,0 +1,84 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh7705.c + * + * SH7705 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * FRQCR parsing hacked out of arch/sh/kernel/time.c + * + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * Copyright (C) 2000 Philipp Rumpf + * Copyright (C) 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2002 M. R. Brown + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include + +/* + * SH7705 uses the same divisors as the generic SH-3 case, it's just the + * FRQCR layout that is a bit different.. + */ +static int stc_multipliers[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; +static int ifc_divisors[] = { 1, 2, 3, 4, 1, 1, 1, 1 }; +static int pfc_divisors[] = { 1, 2, 3, 4, 6, 1, 1, 1 }; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= pfc_divisors[ctrl_inw(FRQCR) & 0x0003]; +} + +static struct clk_ops sh7705_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int idx = ctrl_inw(FRQCR) & 0x0003; + clk->rate = clk->parent->rate / pfc_divisors[idx]; +} + +static struct clk_ops sh7705_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) & 0x0300) >> 8; + clk->rate = clk->parent->rate / stc_multipliers[idx]; +} + +static struct clk_ops sh7705_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) & 0x0030) >> 4; + clk->rate = clk->parent->rate / ifc_divisors[idx]; +} + +static struct clk_ops sh7705_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh7705_clk_ops[] = { + &sh7705_master_clk_ops, + &sh7705_module_clk_ops, + &sh7705_bus_clk_ops, + &sh7705_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7705_clk_ops)) + *ops = sh7705_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh3/clock-sh7709.c b/arch/sh/kernel/cpu/sh3/clock-sh7709.c new file mode 100644 index 000000000000..10461a745e5f --- /dev/null +++ b/arch/sh/kernel/cpu/sh3/clock-sh7709.c @@ -0,0 +1,96 @@ +/* + * arch/sh/kernel/cpu/sh3/clock-sh7709.c + * + * SH7709 support for the clock framework + * + * Copyright (C) 2005 Andriy Skulysh + * + * Based on arch/sh/kernel/cpu/sh3/clock-sh7705.c + * Copyright (C) 2005 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include + +static int stc_multipliers[] = { 1, 2, 4, 8, 3, 6, 1, 1 }; +static int ifc_divisors[] = { 1, 2, 4, 1, 3, 1, 1, 1 }; +static int pfc_divisors[] = { 1, 2, 4, 1, 3, 6, 1, 1 }; + +static void set_bus_parent(struct clk *clk) +{ + struct clk *bus_clk = clk_get("bus_clk"); + clk->parent = bus_clk; + clk_put(bus_clk); +} + +static void master_clk_init(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate *= pfc_divisors[idx]; +} + +static struct clk_ops sh7709_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x2000) >> 11) | (frqcr & 0x0003); + + clk->rate = clk->parent->rate / pfc_divisors[idx]; +} + +static struct clk_ops sh7709_module_clk_ops = { +#ifdef CLOCK_MODE_0_1_2_7 + .init = set_bus_parent, +#endif + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = (frqcr & 0x0080) ? + ((frqcr & 0x8000) >> 13) | ((frqcr & 0x0030) >> 4) : 1; + + clk->rate = clk->parent->rate * stc_multipliers[idx]; +} + +static struct clk_ops sh7709_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int frqcr = ctrl_inw(FRQCR); + int idx = ((frqcr & 0x4000) >> 12) | ((frqcr & 0x000c) >> 2); + + clk->rate = clk->parent->rate / ifc_divisors[idx]; +} + +static struct clk_ops sh7709_cpu_clk_ops = { + .init = set_bus_parent, + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh7709_clk_ops[] = { + &sh7709_master_clk_ops, + &sh7709_module_clk_ops, + &sh7709_bus_clk_ops, + &sh7709_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7709_clk_ops)) + *ops = sh7709_clk_ops[idx]; +} diff --git a/arch/sh/kernel/cpu/sh4/Makefile b/arch/sh/kernel/cpu/sh4/Makefile index ead1071eac73..3d5cafc71ae3 100644 --- a/arch/sh/kernel/cpu/sh4/Makefile +++ b/arch/sh/kernel/cpu/sh4/Makefile @@ -5,6 +5,15 @@ obj-y := ex.o probe.o obj-$(CONFIG_SH_FPU) += fpu.o -obj-$(CONFIG_CPU_SUBTYPE_ST40STB1) += irq_intc2.o obj-$(CONFIG_SH_STORE_QUEUES) += sq.o +# Primary on-chip clocks (common) +clock-$(CONFIG_CPU_SH4) := clock-sh4.o +clock-$(CONFIG_CPU_SUBTYPE_SH73180) := clock-sh73180.o +clock-$(CONFIG_CPU_SUBTYPE_SH7770) := clock-sh7770.o +clock-$(CONFIG_CPU_SUBTYPE_SH7780) := clock-sh7780.o + +# Additional clocks by subtype +clock-$(CONFIG_CPU_SUBTYPE_SH4_202) += clock-sh4-202.o + +obj-y += $(clock-y) diff --git a/arch/sh/kernel/cpu/sh4/clock-sh4-202.c b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c new file mode 100644 index 000000000000..bfdf5fe8d948 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh4-202.c @@ -0,0 +1,179 @@ +/* + * arch/sh/kernel/cpu/sh4/clock-sh4-202.c + * + * Additional SH4-202 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include + +#define CPG2_FRQCR3 0xfe0a0018 + +static int frqcr3_divisors[] = { 1, 2, 3, 4, 6, 8, 16 }; +static int frqcr3_values[] = { 0, 1, 2, 3, 4, 5, 6 }; + +static void emi_clk_recalc(struct clk *clk) +{ + int idx = ctrl_inl(CPG2_FRQCR3) & 0x0007; + clk->rate = clk->parent->rate / frqcr3_divisors[idx]; +} + +static inline int frqcr3_lookup(struct clk *clk, unsigned long rate) +{ + int divisor = clk->parent->rate / rate; + int i; + + for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++) + if (frqcr3_divisors[i] == divisor) + return frqcr3_values[i]; + + /* Safe fallback */ + return 5; +} + +static struct clk_ops sh4202_emi_clk_ops = { + .recalc = emi_clk_recalc, +}; + +static struct clk sh4202_emi_clk = { + .name = "emi_clk", + .flags = CLK_ALWAYS_ENABLED, + .ops = &sh4202_emi_clk_ops, +}; + +static void femi_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inl(CPG2_FRQCR3) >> 3) & 0x0007; + clk->rate = clk->parent->rate / frqcr3_divisors[idx]; +} + +static struct clk_ops sh4202_femi_clk_ops = { + .recalc = femi_clk_recalc, +}; + +static struct clk sh4202_femi_clk = { + .name = "femi_clk", + .flags = CLK_ALWAYS_ENABLED, + .ops = &sh4202_femi_clk_ops, +}; + +static void shoc_clk_init(struct clk *clk) +{ + int i; + + /* + * For some reason, the shoc_clk seems to be set to some really + * insane value at boot (values outside of the allowable frequency + * range for instance). We deal with this by scaling it back down + * to something sensible just in case. + * + * Start scaling from the high end down until we find something + * that passes rate verification.. + */ + for (i = 0; i < ARRAY_SIZE(frqcr3_divisors); i++) { + int divisor = frqcr3_divisors[i]; + + if (clk->ops->set_rate(clk, clk->parent->rate / divisor) == 0) + break; + } + + WARN_ON(i == ARRAY_SIZE(frqcr3_divisors)); /* Undefined clock */ +} + +static void shoc_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inl(CPG2_FRQCR3) >> 6) & 0x0007; + clk->rate = clk->parent->rate / frqcr3_divisors[idx]; +} + +static int shoc_clk_verify_rate(struct clk *clk, unsigned long rate) +{ + struct clk *bclk = clk_get("bus_clk"); + unsigned long bclk_rate = clk_get_rate(bclk); + + clk_put(bclk); + + if (rate > bclk_rate) + return 1; + if (rate > 66000000) + return 1; + + return 0; +} + +static int shoc_clk_set_rate(struct clk *clk, unsigned long rate) +{ + unsigned long frqcr3; + unsigned int tmp; + + /* Make sure we have something sensible to switch to */ + if (shoc_clk_verify_rate(clk, rate) != 0) + return -EINVAL; + + tmp = frqcr3_lookup(clk, rate); + + frqcr3 = ctrl_inl(CPG2_FRQCR3); + frqcr3 &= ~(0x0007 << 6); + frqcr3 |= tmp << 6; + ctrl_outl(frqcr3, CPG2_FRQCR3); + + clk->rate = clk->parent->rate / frqcr3_divisors[tmp]; + + return 0; +} + +static struct clk_ops sh4202_shoc_clk_ops = { + .init = shoc_clk_init, + .recalc = shoc_clk_recalc, + .set_rate = shoc_clk_set_rate, +}; + +static struct clk sh4202_shoc_clk = { + .name = "shoc_clk", + .flags = CLK_ALWAYS_ENABLED, + .ops = &sh4202_shoc_clk_ops, +}; + +static struct clk *sh4202_onchip_clocks[] = { + &sh4202_emi_clk, + &sh4202_femi_clk, + &sh4202_shoc_clk, +}; + +static int __init sh4202_clk_init(void) +{ + struct clk *clk = clk_get("master_clk"); + int i; + + for (i = 0; i < ARRAY_SIZE(sh4202_onchip_clocks); i++) { + struct clk *clkp = sh4202_onchip_clocks[i]; + + clkp->parent = clk; + clk_register(clkp); + clk_enable(clkp); + } + + /* + * Now that we have the rest of the clocks registered, we need to + * force the parent clock to propagate so that these clocks will + * automatically figure out their rate. We cheat by handing the + * parent clock its current rate and forcing child propagation. + */ + clk_set_rate(clk, clk_get_rate(clk)); + + clk_put(clk); + + return 0; +} + +arch_initcall(sh4202_clk_init); + diff --git a/arch/sh/kernel/cpu/sh4/clock-sh4.c b/arch/sh/kernel/cpu/sh4/clock-sh4.c new file mode 100644 index 000000000000..dca9f87a12d6 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh4.c @@ -0,0 +1,80 @@ +/* + * arch/sh/kernel/cpu/sh4/clock-sh4.c + * + * Generic SH-4 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * FRQCR parsing hacked out of arch/sh/kernel/time.c + * + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * Copyright (C) 2000 Philipp Rumpf + * Copyright (C) 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2002 M. R. Brown + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include + +static int ifc_divisors[] = { 1, 2, 3, 4, 6, 8, 1, 1 }; +#define bfc_divisors ifc_divisors /* Same */ +static int pfc_divisors[] = { 2, 3, 4, 6, 8, 2, 2, 2 }; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= pfc_divisors[ctrl_inw(FRQCR) & 0x0007]; +} + +static struct clk_ops sh4_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) & 0x0007); + clk->rate = clk->parent->rate / pfc_divisors[idx]; +} + +static struct clk_ops sh4_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) >> 3) & 0x0007; + clk->rate = clk->parent->rate / bfc_divisors[idx]; +} + +static struct clk_ops sh4_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inw(FRQCR) >> 6) & 0x0007; + clk->rate = clk->parent->rate / ifc_divisors[idx]; +} + +static struct clk_ops sh4_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh4_clk_ops[] = { + &sh4_master_clk_ops, + &sh4_module_clk_ops, + &sh4_bus_clk_ops, + &sh4_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh4_clk_ops)) + *ops = sh4_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh4/clock-sh73180.c b/arch/sh/kernel/cpu/sh4/clock-sh73180.c new file mode 100644 index 000000000000..2fa5cb2ae68d --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh73180.c @@ -0,0 +1,81 @@ +/* + * arch/sh/kernel/cpu/sh4/clock-sh73180.c + * + * SH73180 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * FRQCR parsing hacked out of arch/sh/kernel/time.c + * + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * Copyright (C) 2000 Philipp Rumpf + * Copyright (C) 2002, 2003, 2004 Paul Mundt + * Copyright (C) 2002 M. R. Brown + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include + +/* + * SH73180 uses a common set of divisors, so this is quite simple.. + */ +static int divisors[] = { 1, 2, 3, 4, 6, 8, 12, 16 }; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= divisors[ctrl_inl(FRQCR) & 0x0007]; +} + +static struct clk_ops sh73180_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inl(FRQCR) & 0x0007); + clk->rate = clk->parent->rate / divisors[idx]; +} + +static struct clk_ops sh73180_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inl(FRQCR) >> 12) & 0x0007; + clk->rate = clk->parent->rate / divisors[idx]; +} + +static struct clk_ops sh73180_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inl(FRQCR) >> 20) & 0x0007; + clk->rate = clk->parent->rate / divisors[idx]; +} + +static struct clk_ops sh73180_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh73180_clk_ops[] = { + &sh73180_master_clk_ops, + &sh73180_module_clk_ops, + &sh73180_bus_clk_ops, + &sh73180_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh73180_clk_ops)) + *ops = sh73180_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh4/clock-sh7770.c b/arch/sh/kernel/cpu/sh4/clock-sh7770.c new file mode 100644 index 000000000000..c8694bac6477 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh7770.c @@ -0,0 +1,73 @@ +/* + * arch/sh/kernel/cpu/sh4/clock-sh7770.c + * + * SH7770 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include + +static int ifc_divisors[] = { 1, 1, 1, 1, 1, 1, 1, 1 }; +static int bfc_divisors[] = { 1, 1, 1, 1, 1, 8,12, 1 }; +static int pfc_divisors[] = { 1, 8, 1,10,12,16, 1, 1 }; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= pfc_divisors[(ctrl_inl(FRQCR) >> 28) & 0x000f]; +} + +static struct clk_ops sh7770_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int idx = ((ctrl_inl(FRQCR) >> 28) & 0x000f); + clk->rate = clk->parent->rate / pfc_divisors[idx]; +} + +static struct clk_ops sh7770_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inl(FRQCR) & 0x000f); + clk->rate = clk->parent->rate / bfc_divisors[idx]; +} + +static struct clk_ops sh7770_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int idx = ((ctrl_inl(FRQCR) >> 24) & 0x000f); + clk->rate = clk->parent->rate / ifc_divisors[idx]; +} + +static struct clk_ops sh7770_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh7770_clk_ops[] = { + &sh7770_master_clk_ops, + &sh7770_module_clk_ops, + &sh7770_bus_clk_ops, + &sh7770_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7770_clk_ops)) + *ops = sh7770_clk_ops[idx]; +} + diff --git a/arch/sh/kernel/cpu/sh4/clock-sh7780.c b/arch/sh/kernel/cpu/sh4/clock-sh7780.c new file mode 100644 index 000000000000..93ad367342c9 --- /dev/null +++ b/arch/sh/kernel/cpu/sh4/clock-sh7780.c @@ -0,0 +1,126 @@ +/* + * arch/sh/kernel/cpu/sh4/clock-sh7780.c + * + * SH7780 support for the clock framework + * + * Copyright (C) 2005 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include + +static int ifc_divisors[] = { 2, 4 }; +static int bfc_divisors[] = { 1, 1, 1, 8, 12, 16, 24, 1 }; +static int pfc_divisors[] = { 1, 24, 24, 1 }; +static int cfc_divisors[] = { 1, 1, 4, 1, 6, 1, 1, 1 }; + +static void master_clk_init(struct clk *clk) +{ + clk->rate *= pfc_divisors[ctrl_inl(FRQCR) & 0x0003]; +} + +static struct clk_ops sh7780_master_clk_ops = { + .init = master_clk_init, +}; + +static void module_clk_recalc(struct clk *clk) +{ + int idx = (ctrl_inl(FRQCR) & 0x0003); + clk->rate = clk->parent->rate / pfc_divisors[idx]; +} + +static struct clk_ops sh7780_module_clk_ops = { + .recalc = module_clk_recalc, +}; + +static void bus_clk_recalc(struct clk *clk) +{ + int idx = ((ctrl_inl(FRQCR) >> 16) & 0x0007); + clk->rate = clk->parent->rate / bfc_divisors[idx]; +} + +static struct clk_ops sh7780_bus_clk_ops = { + .recalc = bus_clk_recalc, +}; + +static void cpu_clk_recalc(struct clk *clk) +{ + int idx = ((ctrl_inl(FRQCR) >> 24) & 0x0001); + clk->rate = clk->parent->rate / ifc_divisors[idx]; +} + +static struct clk_ops sh7780_cpu_clk_ops = { + .recalc = cpu_clk_recalc, +}; + +static struct clk_ops *sh7780_clk_ops[] = { + &sh7780_master_clk_ops, + &sh7780_module_clk_ops, + &sh7780_bus_clk_ops, + &sh7780_cpu_clk_ops, +}; + +void __init arch_init_clk_ops(struct clk_ops **ops, int idx) +{ + if (idx < ARRAY_SIZE(sh7780_clk_ops)) + *ops = sh7780_clk_ops[idx]; +} + +static void shyway_clk_recalc(struct clk *clk) +{ + int idx = ((ctrl_inl(FRQCR) >> 20) & 0x0007); + clk->rate = clk->parent->rate / cfc_divisors[idx]; +} + +static struct clk_ops sh7780_shyway_clk_ops = { + .recalc = shyway_clk_recalc, +}; + +static struct clk sh7780_shyway_clk = { + .name = "shyway_clk", + .flags = CLK_ALWAYS_ENABLED, + .ops = &sh7780_shyway_clk_ops, +}; + +/* + * Additional SH7780-specific on-chip clocks that aren't already part of the + * clock framework + */ +static struct clk *sh7780_onchip_clocks[] = { + &sh7780_shyway_clk, +}; + +static int __init sh7780_clk_init(void) +{ + struct clk *clk = clk_get("master_clk"); + int i; + + for (i = 0; i < ARRAY_SIZE(sh7780_onchip_clocks); i++) { + struct clk *clkp = sh7780_onchip_clocks[i]; + + clkp->parent = clk; + clk_register(clkp); + clk_enable(clkp); + } + + /* + * Now that we have the rest of the clocks registered, we need to + * force the parent clock to propagate so that these clocks will + * automatically figure out their rate. We cheat by handing the + * parent clock its current rate and forcing child propagation. + */ + clk_set_rate(clk, clk_get_rate(clk)); + + clk_put(clk); + + return 0; +} + +arch_initcall(sh7780_clk_init); + -- cgit v1.2.2