|  | /* | 
|  | *  cmu.c, Clock Mask Unit routines for the NEC VR4100 series. | 
|  | * | 
|  | *  Copyright (C) 2001-2002  MontaVista Software Inc. | 
|  | *    Author: Yoichi Yuasa <source@mvista.com> | 
|  | *  Copuright (C) 2003-2005  Yoichi Yuasa <yuasa@linux-mips.org> | 
|  | * | 
|  | *  This program is free software; you can redistribute it and/or modify | 
|  | *  it under the terms of the GNU General Public License as published by | 
|  | *  the Free Software Foundation; either version 2 of the License, or | 
|  | *  (at your option) any later version. | 
|  | * | 
|  | *  This program is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | *  GNU General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU General Public License | 
|  | *  along with this program; if not, write to the Free Software | 
|  | *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  | /* | 
|  | * Changes: | 
|  | *  MontaVista Software Inc. <source@mvista.com> | 
|  | *  - New creation, NEC VR4122 and VR4131 are supported. | 
|  | *  - Added support for NEC VR4111 and VR4121. | 
|  | * | 
|  | *  Yoichi Yuasa <yuasa@linux-mips.org> | 
|  | *  - Added support for NEC VR4133. | 
|  | */ | 
|  | #include <linux/init.h> | 
|  | #include <linux/ioport.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/smp.h> | 
|  | #include <linux/spinlock.h> | 
|  | #include <linux/types.h> | 
|  |  | 
|  | #include <asm/cpu.h> | 
|  | #include <asm/io.h> | 
|  | #include <asm/vr41xx/vr41xx.h> | 
|  |  | 
|  | #define CMU_TYPE1_BASE	0x0b000060UL | 
|  | #define CMU_TYPE1_SIZE	0x4 | 
|  |  | 
|  | #define CMU_TYPE2_BASE	0x0f000060UL | 
|  | #define CMU_TYPE2_SIZE	0x4 | 
|  |  | 
|  | #define CMU_TYPE3_BASE	0x0f000060UL | 
|  | #define CMU_TYPE3_SIZE	0x8 | 
|  |  | 
|  | #define CMUCLKMSK	0x0 | 
|  | #define MSKPIU		0x0001 | 
|  | #define MSKSIU		0x0002 | 
|  | #define MSKAIU		0x0004 | 
|  | #define MSKKIU		0x0008 | 
|  | #define MSKFIR		0x0010 | 
|  | #define MSKDSIU	0x0820 | 
|  | #define MSKCSI		0x0040 | 
|  | #define MSKPCIU	0x0080 | 
|  | #define MSKSSIU	0x0100 | 
|  | #define MSKSHSP	0x0200 | 
|  | #define MSKFFIR	0x0400 | 
|  | #define MSKSCSI	0x1000 | 
|  | #define MSKPPCIU	0x2000 | 
|  | #define CMUCLKMSK2	0x4 | 
|  | #define MSKCEU		0x0001 | 
|  | #define MSKMAC0	0x0002 | 
|  | #define MSKMAC1	0x0004 | 
|  |  | 
|  | static void __iomem *cmu_base; | 
|  | static uint16_t cmuclkmsk, cmuclkmsk2; | 
|  | static DEFINE_SPINLOCK(cmu_lock); | 
|  |  | 
|  | #define cmu_read(offset)		readw(cmu_base + (offset)) | 
|  | #define cmu_write(offset, value)	writew((value), cmu_base + (offset)) | 
|  |  | 
|  | void vr41xx_supply_clock(vr41xx_clock_t clock) | 
|  | { | 
|  | spin_lock_irq(&cmu_lock); | 
|  |  | 
|  | switch (clock) { | 
|  | case PIU_CLOCK: | 
|  | cmuclkmsk |= MSKPIU; | 
|  | break; | 
|  | case SIU_CLOCK: | 
|  | cmuclkmsk |= MSKSIU | MSKSSIU; | 
|  | break; | 
|  | case AIU_CLOCK: | 
|  | cmuclkmsk |= MSKAIU; | 
|  | break; | 
|  | case KIU_CLOCK: | 
|  | cmuclkmsk |= MSKKIU; | 
|  | break; | 
|  | case FIR_CLOCK: | 
|  | cmuclkmsk |= MSKFIR | MSKFFIR; | 
|  | break; | 
|  | case DSIU_CLOCK: | 
|  | if (current_cpu_type() == CPU_VR4111 || | 
|  | current_cpu_type() == CPU_VR4121) | 
|  | cmuclkmsk |= MSKDSIU; | 
|  | else | 
|  | cmuclkmsk |= MSKSIU | MSKDSIU; | 
|  | break; | 
|  | case CSI_CLOCK: | 
|  | cmuclkmsk |= MSKCSI | MSKSCSI; | 
|  | break; | 
|  | case PCIU_CLOCK: | 
|  | cmuclkmsk |= MSKPCIU; | 
|  | break; | 
|  | case HSP_CLOCK: | 
|  | cmuclkmsk |= MSKSHSP; | 
|  | break; | 
|  | case PCI_CLOCK: | 
|  | cmuclkmsk |= MSKPPCIU; | 
|  | break; | 
|  | case CEU_CLOCK: | 
|  | cmuclkmsk2 |= MSKCEU; | 
|  | break; | 
|  | case ETHER0_CLOCK: | 
|  | cmuclkmsk2 |= MSKMAC0; | 
|  | break; | 
|  | case ETHER1_CLOCK: | 
|  | cmuclkmsk2 |= MSKMAC1; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (clock == CEU_CLOCK || clock == ETHER0_CLOCK || | 
|  | clock == ETHER1_CLOCK) | 
|  | cmu_write(CMUCLKMSK2, cmuclkmsk2); | 
|  | else | 
|  | cmu_write(CMUCLKMSK, cmuclkmsk); | 
|  |  | 
|  | spin_unlock_irq(&cmu_lock); | 
|  | } | 
|  |  | 
|  | EXPORT_SYMBOL_GPL(vr41xx_supply_clock); | 
|  |  | 
|  | void vr41xx_mask_clock(vr41xx_clock_t clock) | 
|  | { | 
|  | spin_lock_irq(&cmu_lock); | 
|  |  | 
|  | switch (clock) { | 
|  | case PIU_CLOCK: | 
|  | cmuclkmsk &= ~MSKPIU; | 
|  | break; | 
|  | case SIU_CLOCK: | 
|  | if (current_cpu_type() == CPU_VR4111 || | 
|  | current_cpu_type() == CPU_VR4121) { | 
|  | cmuclkmsk &= ~(MSKSIU | MSKSSIU); | 
|  | } else { | 
|  | if (cmuclkmsk & MSKDSIU) | 
|  | cmuclkmsk &= ~MSKSSIU; | 
|  | else | 
|  | cmuclkmsk &= ~(MSKSIU | MSKSSIU); | 
|  | } | 
|  | break; | 
|  | case AIU_CLOCK: | 
|  | cmuclkmsk &= ~MSKAIU; | 
|  | break; | 
|  | case KIU_CLOCK: | 
|  | cmuclkmsk &= ~MSKKIU; | 
|  | break; | 
|  | case FIR_CLOCK: | 
|  | cmuclkmsk &= ~(MSKFIR | MSKFFIR); | 
|  | break; | 
|  | case DSIU_CLOCK: | 
|  | if (current_cpu_type() == CPU_VR4111 || | 
|  | current_cpu_type() == CPU_VR4121) { | 
|  | cmuclkmsk &= ~MSKDSIU; | 
|  | } else { | 
|  | if (cmuclkmsk & MSKSSIU) | 
|  | cmuclkmsk &= ~MSKDSIU; | 
|  | else | 
|  | cmuclkmsk &= ~(MSKSIU | MSKDSIU); | 
|  | } | 
|  | break; | 
|  | case CSI_CLOCK: | 
|  | cmuclkmsk &= ~(MSKCSI | MSKSCSI); | 
|  | break; | 
|  | case PCIU_CLOCK: | 
|  | cmuclkmsk &= ~MSKPCIU; | 
|  | break; | 
|  | case HSP_CLOCK: | 
|  | cmuclkmsk &= ~MSKSHSP; | 
|  | break; | 
|  | case PCI_CLOCK: | 
|  | cmuclkmsk &= ~MSKPPCIU; | 
|  | break; | 
|  | case CEU_CLOCK: | 
|  | cmuclkmsk2 &= ~MSKCEU; | 
|  | break; | 
|  | case ETHER0_CLOCK: | 
|  | cmuclkmsk2 &= ~MSKMAC0; | 
|  | break; | 
|  | case ETHER1_CLOCK: | 
|  | cmuclkmsk2 &= ~MSKMAC1; | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (clock == CEU_CLOCK || clock == ETHER0_CLOCK || | 
|  | clock == ETHER1_CLOCK) | 
|  | cmu_write(CMUCLKMSK2, cmuclkmsk2); | 
|  | else | 
|  | cmu_write(CMUCLKMSK, cmuclkmsk); | 
|  |  | 
|  | spin_unlock_irq(&cmu_lock); | 
|  | } | 
|  |  | 
|  | EXPORT_SYMBOL_GPL(vr41xx_mask_clock); | 
|  |  | 
|  | static int __init vr41xx_cmu_init(void) | 
|  | { | 
|  | unsigned long start, size; | 
|  |  | 
|  | switch (current_cpu_type()) { | 
|  | case CPU_VR4111: | 
|  | case CPU_VR4121: | 
|  | start = CMU_TYPE1_BASE; | 
|  | size = CMU_TYPE1_SIZE; | 
|  | break; | 
|  | case CPU_VR4122: | 
|  | case CPU_VR4131: | 
|  | start = CMU_TYPE2_BASE; | 
|  | size = CMU_TYPE2_SIZE; | 
|  | break; | 
|  | case CPU_VR4133: | 
|  | start = CMU_TYPE3_BASE; | 
|  | size = CMU_TYPE3_SIZE; | 
|  | break; | 
|  | default: | 
|  | panic("Unexpected CPU of NEC VR4100 series"); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (request_mem_region(start, size, "CMU") == NULL) | 
|  | return -EBUSY; | 
|  |  | 
|  | cmu_base = ioremap(start, size); | 
|  | if (cmu_base == NULL) { | 
|  | release_mem_region(start, size); | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | cmuclkmsk = cmu_read(CMUCLKMSK); | 
|  | if (current_cpu_type() == CPU_VR4133) | 
|  | cmuclkmsk2 = cmu_read(CMUCLKMSK2); | 
|  |  | 
|  | spin_lock_init(&cmu_lock); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | core_initcall(vr41xx_cmu_init); |