1/* 2 * bcu.c, Bus Control Unit routines for the NEC VR4100 series. 3 * 4 * Copyright (C) 2002 MontaVista Software Inc. 5 * Author: Yoichi Yuasa <source@mvista.com> 6 * Copyright (C) 2003-2005 Yoichi Yuasa <yuasa@linux-mips.org> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22/* 23 * Changes: 24 * MontaVista Software Inc. <source@mvista.com> 25 * - New creation, NEC VR4122 and VR4131 are supported. 26 * - Added support for NEC VR4111 and VR4121. 27 * 28 * Yoichi Yuasa <yuasa@linux-mips.org> 29 * - Added support for NEC VR4133. 30 */ 31#include <linux/kernel.h> 32#include <linux/module.h> 33#include <linux/smp.h> 34#include <linux/types.h> 35 36#include <asm/cpu.h> 37#include <asm/io.h> 38 39#define CLKSPEEDREG_TYPE1 (void __iomem *)KSEG1ADDR(0x0b000014) 40#define CLKSPEEDREG_TYPE2 (void __iomem *)KSEG1ADDR(0x0f000014) 41 #define CLKSP(x) ((x) & 0x001f) 42 #define CLKSP_VR4133(x) ((x) & 0x0007) 43 44 #define DIV2B 0x8000 45 #define DIV3B 0x4000 46 #define DIV4B 0x2000 47 48 #define DIVT(x) (((x) & 0xf000) >> 12) 49 #define DIVVT(x) (((x) & 0x0f00) >> 8) 50 51 #define TDIVMODE(x) (2 << (((x) & 0x1000) >> 12)) 52 #define VTDIVMODE(x) (((x) & 0x0700) >> 8) 53 54static unsigned long vr41xx_vtclock; 55static unsigned long vr41xx_tclock; 56 57unsigned long vr41xx_get_vtclock_frequency(void) 58{ 59 return vr41xx_vtclock; 60} 61 62EXPORT_SYMBOL_GPL(vr41xx_get_vtclock_frequency); 63 64unsigned long vr41xx_get_tclock_frequency(void) 65{ 66 return vr41xx_tclock; 67} 68 69EXPORT_SYMBOL_GPL(vr41xx_get_tclock_frequency); 70 71static inline uint16_t read_clkspeed(void) 72{ 73 switch (current_cpu_type()) { 74 case CPU_VR4111: 75 case CPU_VR4121: return readw(CLKSPEEDREG_TYPE1); 76 case CPU_VR4122: 77 case CPU_VR4131: 78 case CPU_VR4133: return readw(CLKSPEEDREG_TYPE2); 79 default: 80 printk(KERN_INFO "Unexpected CPU of NEC VR4100 series\n"); 81 break; 82 } 83 84 return 0; 85} 86 87static inline unsigned long calculate_pclock(uint16_t clkspeed) 88{ 89 unsigned long pclock = 0; 90 91 switch (current_cpu_type()) { 92 case CPU_VR4111: 93 case CPU_VR4121: 94 pclock = 18432000 * 64; 95 pclock /= CLKSP(clkspeed); 96 break; 97 case CPU_VR4122: 98 pclock = 18432000 * 98; 99 pclock /= CLKSP(clkspeed); 100 break; 101 case CPU_VR4131: 102 pclock = 18432000 * 108; 103 pclock /= CLKSP(clkspeed); 104 break; 105 case CPU_VR4133: 106 switch (CLKSP_VR4133(clkspeed)) { 107 case 0: 108 pclock = 133000000; 109 break; 110 case 1: 111 pclock = 149000000; 112 break; 113 case 2: 114 pclock = 165900000; 115 break; 116 case 3: 117 pclock = 199100000; 118 break; 119 case 4: 120 pclock = 265900000; 121 break; 122 default: 123 printk(KERN_INFO "Unknown PClock speed for NEC VR4133\n"); 124 break; 125 } 126 break; 127 default: 128 printk(KERN_INFO "Unexpected CPU of NEC VR4100 series\n"); 129 break; 130 } 131 132 printk(KERN_INFO "PClock: %ldHz\n", pclock); 133 134 return pclock; 135} 136 137static inline unsigned long calculate_vtclock(uint16_t clkspeed, unsigned long pclock) 138{ 139 unsigned long vtclock = 0; 140 141 switch (current_cpu_type()) { 142 case CPU_VR4111: 143 /* The NEC VR4111 doesn't have the VTClock. */ 144 break; 145 case CPU_VR4121: 146 vtclock = pclock; 147 /* DIVVT == 9 Divide by 1.5 . VTClock = (PClock * 6) / 9 */ 148 if (DIVVT(clkspeed) == 9) 149 vtclock = pclock * 6; 150 /* DIVVT == 10 Divide by 2.5 . VTClock = (PClock * 4) / 10 */ 151 else if (DIVVT(clkspeed) == 10) 152 vtclock = pclock * 4; 153 vtclock /= DIVVT(clkspeed); 154 printk(KERN_INFO "VTClock: %ldHz\n", vtclock); 155 break; 156 case CPU_VR4122: 157 if(VTDIVMODE(clkspeed) == 7) 158 vtclock = pclock / 1; 159 else if(VTDIVMODE(clkspeed) == 1) 160 vtclock = pclock / 2; 161 else 162 vtclock = pclock / VTDIVMODE(clkspeed); 163 printk(KERN_INFO "VTClock: %ldHz\n", vtclock); 164 break; 165 case CPU_VR4131: 166 case CPU_VR4133: 167 vtclock = pclock / VTDIVMODE(clkspeed); 168 printk(KERN_INFO "VTClock: %ldHz\n", vtclock); 169 break; 170 default: 171 printk(KERN_INFO "Unexpected CPU of NEC VR4100 series\n"); 172 break; 173 } 174 175 return vtclock; 176} 177 178static inline unsigned long calculate_tclock(uint16_t clkspeed, unsigned long pclock, 179 unsigned long vtclock) 180{ 181 unsigned long tclock = 0; 182 183 switch (current_cpu_type()) { 184 case CPU_VR4111: 185 if (!(clkspeed & DIV2B)) 186 tclock = pclock / 2; 187 else if (!(clkspeed & DIV3B)) 188 tclock = pclock / 3; 189 else if (!(clkspeed & DIV4B)) 190 tclock = pclock / 4; 191 break; 192 case CPU_VR4121: 193 tclock = pclock / DIVT(clkspeed); 194 break; 195 case CPU_VR4122: 196 case CPU_VR4131: 197 case CPU_VR4133: 198 tclock = vtclock / TDIVMODE(clkspeed); 199 break; 200 default: 201 printk(KERN_INFO "Unexpected CPU of NEC VR4100 series\n"); 202 break; 203 } 204 205 printk(KERN_INFO "TClock: %ldHz\n", tclock); 206 207 return tclock; 208} 209 210void vr41xx_calculate_clock_frequency(void) 211{ 212 unsigned long pclock; 213 uint16_t clkspeed; 214 215 clkspeed = read_clkspeed(); 216 217 pclock = calculate_pclock(clkspeed); 218 vr41xx_vtclock = calculate_vtclock(clkspeed, pclock); 219 vr41xx_tclock = calculate_tclock(clkspeed, pclock, vr41xx_vtclock); 220} 221 222EXPORT_SYMBOL_GPL(vr41xx_calculate_clock_frequency); 223